From ebda622c2cf06da9727454837b6d8211de945eca Mon Sep 17 00:00:00 2001 From: yugecin Date: Sat, 7 Jan 2017 14:02:33 +0100 Subject: [PATCH 01/92] guicy new way of running the app --- pom.xml | 10 +++ src/yugecin/opsudance/OpsuDance.java | 44 ++++++++++++ .../opsudance/PreStartupInitializer.java | 67 +++++++++++++++++++ src/yugecin/opsudance/core/Container.java | 67 +++++++++++++++++++ .../opsudance/core/ContainerWrapper.java | 23 +++++++ src/yugecin/opsudance/core/Demux.java | 65 ++++++++++++++++++ src/yugecin/opsudance/kernel/Entrypoint.java | 28 ++++++++ .../opsudance/kernel/InstanceContainer.java | 24 +++++++ .../kernel/InstanceContainerImpl.java | 47 +++++++++++++ .../opsudance/kernel/InstanceResolver.java | 28 ++++++++ .../opsudance/kernel/OpsuDanceModule.java | 34 ++++++++++ src/yugecin/opsudance/states/EmptyState.java | 50 ++++++++++++++ src/yugecin/opsudance/states/GameState.java | 29 ++++++++ 13 files changed, 516 insertions(+) create mode 100644 src/yugecin/opsudance/OpsuDance.java create mode 100644 src/yugecin/opsudance/PreStartupInitializer.java create mode 100644 src/yugecin/opsudance/core/Container.java create mode 100644 src/yugecin/opsudance/core/ContainerWrapper.java create mode 100644 src/yugecin/opsudance/core/Demux.java create mode 100644 src/yugecin/opsudance/kernel/Entrypoint.java create mode 100644 src/yugecin/opsudance/kernel/InstanceContainer.java create mode 100644 src/yugecin/opsudance/kernel/InstanceContainerImpl.java create mode 100644 src/yugecin/opsudance/kernel/InstanceResolver.java create mode 100644 src/yugecin/opsudance/kernel/OpsuDanceModule.java create mode 100644 src/yugecin/opsudance/states/EmptyState.java create mode 100644 src/yugecin/opsudance/states/GameState.java diff --git a/pom.xml b/pom.xml index 4388cf9d..18108bde 100644 --- a/pom.xml +++ b/pom.xml @@ -144,6 +144,16 @@ + + com.google.inject + guice + 4.1.0 + + + com.google.inject.extensions + guice-assistedinject + 4.1.0 + org.jcraft jorbis diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java new file mode 100644 index 00000000..1ce075c4 --- /dev/null +++ b/src/yugecin/opsudance/OpsuDance.java @@ -0,0 +1,44 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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; + +import com.google.inject.Inject; +import org.newdawn.slick.SlickException; +import yugecin.opsudance.core.Container; +import yugecin.opsudance.core.Demux; + +public class OpsuDance { + + private final Demux stateDemultiplexer; + private final Container container; + + @Inject + public OpsuDance(Demux stateDemultiplexer, Container container) { + this.stateDemultiplexer = stateDemultiplexer; + this.container = container; + } + + public void start() { + try { + container.start(); + } catch (SlickException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/yugecin/opsudance/PreStartupInitializer.java b/src/yugecin/opsudance/PreStartupInitializer.java new file mode 100644 index 00000000..e9326e4b --- /dev/null +++ b/src/yugecin/opsudance/PreStartupInitializer.java @@ -0,0 +1,67 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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; + +import com.google.inject.Inject; +import itdelatrisu.opsu.NativeLoader; +import itdelatrisu.opsu.Options; +import itdelatrisu.opsu.Utils; +import org.newdawn.slick.util.FileSystemLocation; +import org.newdawn.slick.util.Log; +import org.newdawn.slick.util.ResourceLoader; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; + +public class PreStartupInitializer { + + @Inject + public PreStartupInitializer() { + // load natives + File nativeDir; + if (!Utils.isJarRunning() && ( + (nativeDir = new File("./target/natives/")).isDirectory() || + (nativeDir = new File("./build/natives/")).isDirectory())) + ; + else { + nativeDir = Options.NATIVE_DIR; + try { + new NativeLoader(nativeDir).loadNatives(); + } catch (IOException e) { + Log.error("Error loading natives.", e); + } + } + System.setProperty("org.lwjgl.librarypath", nativeDir.getAbsolutePath()); + System.setProperty("java.library.path", nativeDir.getAbsolutePath()); + try { + // Workaround for "java.library.path" property being read-only. + // http://stackoverflow.com/a/24988095 + Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths"); + fieldSysPath.setAccessible(true); + fieldSysPath.set(null, null); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + Log.warn("Failed to set 'sys_paths' field.", e); + } + + // set the resource paths + ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/"))); + + } + +} diff --git a/src/yugecin/opsudance/core/Container.java b/src/yugecin/opsudance/core/Container.java new file mode 100644 index 00000000..8057d3ab --- /dev/null +++ b/src/yugecin/opsudance/core/Container.java @@ -0,0 +1,67 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core; + +import com.google.inject.Inject; +import org.lwjgl.opengl.Display; +import org.newdawn.slick.AppGameContainer; +import org.newdawn.slick.SlickException; + +/** + * based on itdelatrisu.opsu.Container + */ +public class Container extends AppGameContainer { + + @Inject + public Container(Demux demux) throws SlickException { + super(demux); + } + + @Override + public void start() throws SlickException { + try { + setup(); + getDelta(); + while (running()) + gameLoop(); + } catch (Exception e) { + } + destroy(); + } + + @Override + protected void gameLoop() throws SlickException { + int delta = getDelta(); + if (!Display.isVisible() && updateOnlyOnVisible) { + try { Thread.sleep(100); } catch (Exception e) {} + } else { + try { + updateAndRender(delta); + } catch (SlickException e) { + running = false; + return; + } + } + updateFPS(); + Display.update(); + if (Display.isCloseRequested()) { + if (game.closeRequested()) + running = false; + } + } +} diff --git a/src/yugecin/opsudance/core/ContainerWrapper.java b/src/yugecin/opsudance/core/ContainerWrapper.java new file mode 100644 index 00000000..2f2dc7f3 --- /dev/null +++ b/src/yugecin/opsudance/core/ContainerWrapper.java @@ -0,0 +1,23 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core; + +public class ContainerWrapper { + + +} diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java new file mode 100644 index 00000000..e00db535 --- /dev/null +++ b/src/yugecin/opsudance/core/Demux.java @@ -0,0 +1,65 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core; + +import com.google.inject.Inject; +import org.newdawn.slick.Game; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.SlickException; +import yugecin.opsudance.kernel.InstanceContainer; +import yugecin.opsudance.states.EmptyState; +import yugecin.opsudance.states.GameState; + +public class Demux implements Game { + + private final InstanceContainer instanceContainer; + + private GameState currentState; + + @Inject + public Demux(InstanceContainer instanceContainer) { + this.instanceContainer = instanceContainer; + } + + @Override + public void init(GameContainer container) throws SlickException { + currentState = instanceContainer.provide(EmptyState.class); + } + + @Override + public void update(GameContainer container, int delta) throws SlickException { + currentState.update(delta); + } + + @Override + public void render(GameContainer container, Graphics g) throws SlickException { + currentState.render(g); + } + + @Override + public boolean closeRequested() { + return false; + } + + @Override + public String getTitle() { + return "opsu!dance"; + } + +} diff --git a/src/yugecin/opsudance/kernel/Entrypoint.java b/src/yugecin/opsudance/kernel/Entrypoint.java new file mode 100644 index 00000000..66ea43b5 --- /dev/null +++ b/src/yugecin/opsudance/kernel/Entrypoint.java @@ -0,0 +1,28 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.kernel; + +import yugecin.opsudance.OpsuDance; + +public class Entrypoint { + + public static void main(String[] args) { + InstanceContainerImpl.initialize().provide(OpsuDance.class).start(); + } + +} diff --git a/src/yugecin/opsudance/kernel/InstanceContainer.java b/src/yugecin/opsudance/kernel/InstanceContainer.java new file mode 100644 index 00000000..b82251be --- /dev/null +++ b/src/yugecin/opsudance/kernel/InstanceContainer.java @@ -0,0 +1,24 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.kernel; + +public interface InstanceContainer { + + T provide(Class type); + +} diff --git a/src/yugecin/opsudance/kernel/InstanceContainerImpl.java b/src/yugecin/opsudance/kernel/InstanceContainerImpl.java new file mode 100644 index 00000000..ed1f7dcc --- /dev/null +++ b/src/yugecin/opsudance/kernel/InstanceContainerImpl.java @@ -0,0 +1,47 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.kernel; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class InstanceContainerImpl implements InstanceContainer { + + private static InstanceContainer instance; + + private Injector injector; + + private InstanceContainerImpl() { + injector = Guice.createInjector(new OpsuDanceModule()); + } + + public static InstanceContainer initialize() { + return instance = new InstanceContainerImpl(); + } + + @Deprecated + public static InstanceContainer get() { + return instance; + } + + @Override + public T provide(Class type) { + return injector.getInstance(type); + } + +} diff --git a/src/yugecin/opsudance/kernel/InstanceResolver.java b/src/yugecin/opsudance/kernel/InstanceResolver.java new file mode 100644 index 00000000..ed6780cd --- /dev/null +++ b/src/yugecin/opsudance/kernel/InstanceResolver.java @@ -0,0 +1,28 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.kernel; + +@SuppressWarnings("deprecation") +public class InstanceResolver implements InstanceContainer { + + @Override + public T provide(Class type) { + return InstanceContainerImpl.get().provide(type); + } + +} diff --git a/src/yugecin/opsudance/kernel/OpsuDanceModule.java b/src/yugecin/opsudance/kernel/OpsuDanceModule.java new file mode 100644 index 00000000..884c8d98 --- /dev/null +++ b/src/yugecin/opsudance/kernel/OpsuDanceModule.java @@ -0,0 +1,34 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.kernel; + +import com.google.inject.AbstractModule; +import yugecin.opsudance.PreStartupInitializer; +import yugecin.opsudance.core.Container; +import yugecin.opsudance.core.Demux; + +public class OpsuDanceModule extends AbstractModule { + + protected void configure() { + bind(InstanceContainer.class).to(InstanceResolver.class); + bind(PreStartupInitializer.class).asEagerSingleton(); + bind(Demux.class).asEagerSingleton(); + bind(Container.class).asEagerSingleton(); + } + +} diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java new file mode 100644 index 00000000..2bc4d825 --- /dev/null +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -0,0 +1,50 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.states; + +import com.google.inject.Inject; +import org.newdawn.slick.Graphics; + +public class EmptyState implements GameState { + + @Inject + public EmptyState() { + + } + + @Override + public void update(int delta) { + System.out.println("updatin' " + delta); + } + + @Override + public void render(Graphics g) { + System.out.println("renderin'"); + } + + @Override + public void enter() { + System.out.println("entered"); + } + + @Override + public void leave() { + System.out.println("left"); + } + +} diff --git a/src/yugecin/opsudance/states/GameState.java b/src/yugecin/opsudance/states/GameState.java new file mode 100644 index 00000000..ea679cf1 --- /dev/null +++ b/src/yugecin/opsudance/states/GameState.java @@ -0,0 +1,29 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.states; + +import org.newdawn.slick.Graphics; + +public interface GameState { + + void update(int delta); + void render(Graphics g); + void enter(); + void leave(); + +} From 6d8f48a03fb9dea857b39422cb2cbb3409fdf96a Mon Sep 17 00:00:00 2001 From: yugecin Date: Sat, 7 Jan 2017 23:14:10 +0100 Subject: [PATCH 02/92] switching states using fade transitions --- src/yugecin/opsudance/core/Container.java | 1 + src/yugecin/opsudance/core/Demux.java | 46 +++++++++--- .../opsudance/kernel/OpsuDanceModule.java | 8 ++ .../opsudance/states/EmptyRedState.java | 62 ++++++++++++++++ src/yugecin/opsudance/states/EmptyState.java | 24 ++++-- .../transitions/FadeInTransitionState.java} | 31 +++++++- .../transitions/FadeOutTransitionState.java | 47 ++++++++++++ .../states/transitions/TransitionState.java | 74 +++++++++++++++++++ 8 files changed, 276 insertions(+), 17 deletions(-) create mode 100644 src/yugecin/opsudance/states/EmptyRedState.java rename src/yugecin/opsudance/{core/ContainerWrapper.java => states/transitions/FadeInTransitionState.java} (53%) create mode 100644 src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java create mode 100644 src/yugecin/opsudance/states/transitions/TransitionState.java diff --git a/src/yugecin/opsudance/core/Container.java b/src/yugecin/opsudance/core/Container.java index 8057d3ab..847e087e 100644 --- a/src/yugecin/opsudance/core/Container.java +++ b/src/yugecin/opsudance/core/Container.java @@ -30,6 +30,7 @@ public class Container extends AppGameContainer { @Inject public Container(Demux demux) throws SlickException { super(demux); + setShowFPS(false); } @Override diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index e00db535..464daf34 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -18,19 +18,22 @@ package yugecin.opsudance.core; import com.google.inject.Inject; -import org.newdawn.slick.Game; -import org.newdawn.slick.GameContainer; -import org.newdawn.slick.Graphics; -import org.newdawn.slick.SlickException; +import org.newdawn.slick.*; import yugecin.opsudance.kernel.InstanceContainer; import yugecin.opsudance.states.EmptyState; import yugecin.opsudance.states.GameState; +import yugecin.opsudance.states.transitions.FadeInTransitionState; +import yugecin.opsudance.states.transitions.FadeOutTransitionState; +import yugecin.opsudance.states.transitions.TransitionState; public class Demux implements Game { private final InstanceContainer instanceContainer; - private GameState currentState; + private TransitionState fadeOutTransitionState; + private TransitionState fadeInTransitionState; + + private GameState state; @Inject public Demux(InstanceContainer instanceContainer) { @@ -39,22 +42,26 @@ public class Demux implements Game { @Override public void init(GameContainer container) throws SlickException { - currentState = instanceContainer.provide(EmptyState.class); + fadeOutTransitionState = instanceContainer.provide(FadeOutTransitionState.class); + fadeInTransitionState = instanceContainer.provide(FadeInTransitionState.class); + state = instanceContainer.provide(EmptyState.class); + state.enter(); } @Override public void update(GameContainer container, int delta) throws SlickException { - currentState.update(delta); + state.update(delta); } @Override public void render(GameContainer container, Graphics g) throws SlickException { - currentState.render(g); + state.render(g); } @Override public boolean closeRequested() { - return false; + // TODO for what is this used + return !isTransitioning(); } @Override @@ -62,4 +69,25 @@ public class Demux implements Game { return "opsu!dance"; } + public boolean isTransitioning() { + return state == fadeInTransitionState || state == fadeOutTransitionState; + } + + public void switchState(GameState newState) { + if (isTransitioning()) { + return; + } + fadeOutTransitionState.setApplicableState(state); + fadeInTransitionState.setApplicableState(newState); + state = fadeOutTransitionState; + state.enter(); + } + + public void switchStateNow(GameState newState) { + if (!isTransitioning()) { + return; + } + state = newState; + } + } diff --git a/src/yugecin/opsudance/kernel/OpsuDanceModule.java b/src/yugecin/opsudance/kernel/OpsuDanceModule.java index 884c8d98..71a473b8 100644 --- a/src/yugecin/opsudance/kernel/OpsuDanceModule.java +++ b/src/yugecin/opsudance/kernel/OpsuDanceModule.java @@ -21,6 +21,10 @@ import com.google.inject.AbstractModule; import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.Container; import yugecin.opsudance.core.Demux; +import yugecin.opsudance.states.EmptyRedState; +import yugecin.opsudance.states.EmptyState; +import yugecin.opsudance.states.transitions.FadeInTransitionState; +import yugecin.opsudance.states.transitions.FadeOutTransitionState; public class OpsuDanceModule extends AbstractModule { @@ -29,6 +33,10 @@ public class OpsuDanceModule extends AbstractModule { bind(PreStartupInitializer.class).asEagerSingleton(); bind(Demux.class).asEagerSingleton(); bind(Container.class).asEagerSingleton(); + bind(FadeInTransitionState.class).asEagerSingleton(); + bind(FadeOutTransitionState.class).asEagerSingleton(); + bind(EmptyRedState.class).asEagerSingleton(); + bind(EmptyState.class).asEagerSingleton(); } } diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java new file mode 100644 index 00000000..b1a15524 --- /dev/null +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -0,0 +1,62 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.states; + +import com.google.inject.Inject; +import org.newdawn.slick.Color; +import org.newdawn.slick.Graphics; +import yugecin.opsudance.core.Demux; +import yugecin.opsudance.kernel.InstanceContainer; + +public class EmptyRedState implements GameState { + + private int counter; + + private final Demux demux; + private final InstanceContainer instanceContainer; + + @Inject + public EmptyRedState(InstanceContainer instanceContainer, Demux demux) { + this.instanceContainer = instanceContainer; + this.demux = demux; + } + + @Override + public void update(int delta) { + counter -= delta; + if (counter < 0) { + demux.switchState(instanceContainer.provide(EmptyState.class)); + } + } + + @Override + public void render(Graphics g) { + g.setColor(Color.red); + g.fillRect(0, 0, 100, 100); + } + + @Override + public void enter() { + counter = 5000; + } + + @Override + public void leave() { + } + +} diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index 2bc4d825..503f727b 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -18,33 +18,45 @@ package yugecin.opsudance.states; import com.google.inject.Inject; +import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; +import yugecin.opsudance.core.Demux; +import yugecin.opsudance.kernel.InstanceContainer; public class EmptyState implements GameState { - @Inject - public EmptyState() { + private int counter; + private final InstanceContainer instanceContainer; + private final Demux demux; + + @Inject + public EmptyState(InstanceContainer instanceContainer, Demux demux) { + this.instanceContainer = instanceContainer; + this.demux = demux; } @Override public void update(int delta) { - System.out.println("updatin' " + delta); + counter -= delta; + if (counter < 0) { + demux.switchState(instanceContainer.provide(EmptyRedState.class)); + } } @Override public void render(Graphics g) { - System.out.println("renderin'"); + g.setColor(Color.green); + g.fillRect(0, 0, 100, 100); } @Override public void enter() { - System.out.println("entered"); + counter = 2000; } @Override public void leave() { - System.out.println("left"); } } diff --git a/src/yugecin/opsudance/core/ContainerWrapper.java b/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java similarity index 53% rename from src/yugecin/opsudance/core/ContainerWrapper.java rename to src/yugecin/opsudance/states/transitions/FadeInTransitionState.java index 2f2dc7f3..b935d74f 100644 --- a/src/yugecin/opsudance/core/ContainerWrapper.java +++ b/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java @@ -15,9 +15,36 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.core; +package yugecin.opsudance.states.transitions; -public class ContainerWrapper { +import com.google.inject.Inject; +import yugecin.opsudance.core.Container; +import yugecin.opsudance.core.Demux; +public class FadeInTransitionState extends TransitionState { + + private final Demux demux; + + @Inject + public FadeInTransitionState(Container container, Demux demux) { + super(container, 300); + this.demux = demux; + } + + @Override + protected float getMaskAlphaLevel() { + return 1f - (float) fadeTime / fadeTargetTime; + } + + @Override + public void enter() { + super.enter(); + applicableState.enter(); + } + + @Override + protected void onFadeFinished() { + demux.switchStateNow(applicableState); + } } diff --git a/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java b/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java new file mode 100644 index 00000000..2fd966a6 --- /dev/null +++ b/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java @@ -0,0 +1,47 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.states.transitions; + +import com.google.inject.Inject; +import yugecin.opsudance.core.Container; +import yugecin.opsudance.core.Demux; + +public class FadeOutTransitionState extends TransitionState { + + private final Demux demux; + private final FadeInTransitionState fadeInTransitionState; + + @Inject + public FadeOutTransitionState(Container container, Demux demux, FadeInTransitionState fadeInTransitionState) { + super(container, 200); + this.demux = demux; + this.fadeInTransitionState = fadeInTransitionState; + } + + @Override + protected float getMaskAlphaLevel() { + return (float) fadeTime / fadeTargetTime; + } + + @Override + protected void onFadeFinished() { + applicableState.leave(); + demux.switchStateNow(fadeInTransitionState); + fadeInTransitionState.enter(); + } +} diff --git a/src/yugecin/opsudance/states/transitions/TransitionState.java b/src/yugecin/opsudance/states/transitions/TransitionState.java new file mode 100644 index 00000000..dd90b5ce --- /dev/null +++ b/src/yugecin/opsudance/states/transitions/TransitionState.java @@ -0,0 +1,74 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.states.transitions; + +import org.newdawn.slick.Color; +import org.newdawn.slick.Graphics; +import yugecin.opsudance.core.Container; +import yugecin.opsudance.states.GameState; + +public abstract class TransitionState implements GameState { + + protected GameState applicableState; + + private final Container container; + + protected final int fadeTargetTime; + protected int fadeTime; + + private final Color black; + + public TransitionState(Container container, int fadeTargetTime) { + this.container = container; + this.fadeTargetTime = fadeTargetTime; + black = new Color(Color.black); + } + + public void setApplicableState(GameState applicableState) { + this.applicableState = applicableState; + } + + @Override + public void update(int delta) { + applicableState.update(delta); + fadeTime += delta; + if (fadeTime >= fadeTargetTime) { + onFadeFinished(); + } + } + + @Override + public void render(Graphics g) { + applicableState.render(g); + black.a = getMaskAlphaLevel(); + g.setColor(black); + g.fillRect(0, 0, container.getWidth(), container.getHeight()); + } + + @Override + public void enter() { + fadeTime = 0; + } + + @Override + public void leave() { } + + protected abstract float getMaskAlphaLevel(); + protected abstract void onFadeFinished(); + +} From 31a25b297f55551983c7af27f30098a92ad93025 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 8 Jan 2017 11:23:06 +0100 Subject: [PATCH 03/92] more oop shit for state transitions --- .../transitions/FadeInTransitionState.java | 8 +- .../transitions/FadeOutTransitionState.java | 9 ++- .../transitions/FadeTransitionState.java | 74 +++++++++++++++++++ .../states/transitions/TransitionState.java | 30 +++----- 4 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 src/yugecin/opsudance/states/transitions/FadeTransitionState.java diff --git a/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java b/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java index b935d74f..d832e414 100644 --- a/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java +++ b/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java @@ -21,7 +21,7 @@ import com.google.inject.Inject; import yugecin.opsudance.core.Container; import yugecin.opsudance.core.Demux; -public class FadeInTransitionState extends TransitionState { +public class FadeInTransitionState extends FadeTransitionState { private final Demux demux; @@ -32,8 +32,8 @@ public class FadeInTransitionState extends TransitionState { } @Override - protected float getMaskAlphaLevel() { - return 1f - (float) fadeTime / fadeTargetTime; + protected float getMaskAlphaLevel(float fadeProgress) { + return 1f - fadeProgress; } @Override @@ -43,7 +43,7 @@ public class FadeInTransitionState extends TransitionState { } @Override - protected void onFadeFinished() { + protected void onTransitionFinished() { demux.switchStateNow(applicableState); } diff --git a/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java b/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java index 2fd966a6..6d83e4d9 100644 --- a/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java +++ b/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java @@ -21,7 +21,7 @@ import com.google.inject.Inject; import yugecin.opsudance.core.Container; import yugecin.opsudance.core.Demux; -public class FadeOutTransitionState extends TransitionState { +public class FadeOutTransitionState extends FadeTransitionState { private final Demux demux; private final FadeInTransitionState fadeInTransitionState; @@ -34,14 +34,15 @@ public class FadeOutTransitionState extends TransitionState { } @Override - protected float getMaskAlphaLevel() { - return (float) fadeTime / fadeTargetTime; + protected float getMaskAlphaLevel(float fadeProgress) { + return fadeProgress; } @Override - protected void onFadeFinished() { + protected void onTransitionFinished() { applicableState.leave(); demux.switchStateNow(fadeInTransitionState); fadeInTransitionState.enter(); } + } diff --git a/src/yugecin/opsudance/states/transitions/FadeTransitionState.java b/src/yugecin/opsudance/states/transitions/FadeTransitionState.java new file mode 100644 index 00000000..3870b0b3 --- /dev/null +++ b/src/yugecin/opsudance/states/transitions/FadeTransitionState.java @@ -0,0 +1,74 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.states.transitions; + +import org.newdawn.slick.Color; +import org.newdawn.slick.Graphics; +import yugecin.opsudance.core.Container; +import yugecin.opsudance.states.GameState; + +public abstract class FadeTransitionState extends TransitionState { + + protected GameState applicableState; + + private final Container container; + + protected final int fadeTargetTime; + protected int fadeTime; + + private final Color black; + + public FadeTransitionState(Container container, int fadeTargetTime) { + super(fadeTargetTime); + this.container = container; + this.fadeTargetTime = fadeTargetTime; + black = new Color(Color.black); + } + + public void setApplicableState(GameState applicableState) { + this.applicableState = applicableState; + } + + @Override + public void update(int delta) { + applicableState.update(delta); + fadeTime += delta; + if (fadeTime >= fadeTargetTime) { + onTransitionFinished(); + } + } + + @Override + public void render(Graphics g) { + applicableState.render(g); + black.a = getMaskAlphaLevel((float) fadeTime / fadeTargetTime); + g.setColor(black); + g.fillRect(0, 0, container.getWidth(), container.getHeight()); + } + + @Override + public void enter() { + fadeTime = 0; + } + + @Override + public void leave() { } + + protected abstract float getMaskAlphaLevel(float fadeProgress); + +} diff --git a/src/yugecin/opsudance/states/transitions/TransitionState.java b/src/yugecin/opsudance/states/transitions/TransitionState.java index dd90b5ce..98828fb8 100644 --- a/src/yugecin/opsudance/states/transitions/TransitionState.java +++ b/src/yugecin/opsudance/states/transitions/TransitionState.java @@ -17,26 +17,18 @@ */ package yugecin.opsudance.states.transitions; -import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; -import yugecin.opsudance.core.Container; import yugecin.opsudance.states.GameState; public abstract class TransitionState implements GameState { protected GameState applicableState; - private final Container container; + protected final int transitionTargetTime; + protected int transitionTime; - protected final int fadeTargetTime; - protected int fadeTime; - - private final Color black; - - public TransitionState(Container container, int fadeTargetTime) { - this.container = container; - this.fadeTargetTime = fadeTargetTime; - black = new Color(Color.black); + public TransitionState(int transitionTargetTime) { + this.transitionTargetTime = transitionTargetTime; } public void setApplicableState(GameState applicableState) { @@ -46,29 +38,25 @@ public abstract class TransitionState implements GameState { @Override public void update(int delta) { applicableState.update(delta); - fadeTime += delta; - if (fadeTime >= fadeTargetTime) { - onFadeFinished(); + transitionTime += delta; + if (transitionTime >= transitionTargetTime) { + onTransitionFinished(); } } @Override public void render(Graphics g) { applicableState.render(g); - black.a = getMaskAlphaLevel(); - g.setColor(black); - g.fillRect(0, 0, container.getWidth(), container.getHeight()); } @Override public void enter() { - fadeTime = 0; + transitionTime = 0; } @Override public void leave() { } - protected abstract float getMaskAlphaLevel(); - protected abstract void onFadeFinished(); + protected abstract void onTransitionFinished(); } From d5796e5a1eab1afaf9d56810f97dee36adad16c1 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 8 Jan 2017 11:23:23 +0100 Subject: [PATCH 04/92] cleanup the prestartupinitializer --- .../opsudance/PreStartupInitializer.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/yugecin/opsudance/PreStartupInitializer.java b/src/yugecin/opsudance/PreStartupInitializer.java index e9326e4b..d54bdc51 100644 --- a/src/yugecin/opsudance/PreStartupInitializer.java +++ b/src/yugecin/opsudance/PreStartupInitializer.java @@ -20,7 +20,6 @@ package yugecin.opsudance; import com.google.inject.Inject; import itdelatrisu.opsu.NativeLoader; import itdelatrisu.opsu.Options; -import itdelatrisu.opsu.Utils; import org.newdawn.slick.util.FileSystemLocation; import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; @@ -33,22 +32,16 @@ public class PreStartupInitializer { @Inject public PreStartupInitializer() { - // load natives - File nativeDir; - if (!Utils.isJarRunning() && ( - (nativeDir = new File("./target/natives/")).isDirectory() || - (nativeDir = new File("./build/natives/")).isDirectory())) - ; - else { - nativeDir = Options.NATIVE_DIR; - try { - new NativeLoader(nativeDir).loadNatives(); - } catch (IOException e) { - Log.error("Error loading natives.", e); - } - } + loadNatives(); + setResourcePath(); + } + + private void loadNatives() { + File nativeDir = loadNativesUsingOptionsPath(); + System.setProperty("org.lwjgl.librarypath", nativeDir.getAbsolutePath()); System.setProperty("java.library.path", nativeDir.getAbsolutePath()); + try { // Workaround for "java.library.path" property being read-only. // http://stackoverflow.com/a/24988095 @@ -58,10 +51,20 @@ public class PreStartupInitializer { } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { Log.warn("Failed to set 'sys_paths' field.", e); } + } - // set the resource paths + private File loadNativesUsingOptionsPath() { + File nativeDir = Options.NATIVE_DIR; + try { + new NativeLoader(nativeDir).loadNatives(); + } catch (IOException e) { + Log.error("Error loading natives.", e); + } + return nativeDir; + } + + private void setResourcePath() { ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/"))); - } } From e8cac712fc51d507e3279f53c34510d9957d53f9 Mon Sep 17 00:00:00 2001 From: yugecin Date: Mon, 9 Jan 2017 22:42:59 +0100 Subject: [PATCH 05/92] getting rid of the slick (App)GameContainer --- src/yugecin/opsudance/OpsuDance.java | 16 +- src/yugecin/opsudance/core/Container.java | 68 ------ src/yugecin/opsudance/core/Demux.java | 29 +-- .../opsudance/core/DisplayContainer.java | 209 ++++++++++++++++++ .../core/ResolutionChangeListener.java | 24 ++ .../opsudance/kernel/OpsuDanceModule.java | 6 +- .../transitions/FadeInTransitionState.java | 4 +- .../transitions/FadeOutTransitionState.java | 4 +- .../transitions/FadeTransitionState.java | 8 +- src/yugecin/opsudance/utils/GLHelper.java | 53 +++++ 10 files changed, 315 insertions(+), 106 deletions(-) delete mode 100644 src/yugecin/opsudance/core/Container.java create mode 100644 src/yugecin/opsudance/core/DisplayContainer.java create mode 100644 src/yugecin/opsudance/core/ResolutionChangeListener.java create mode 100644 src/yugecin/opsudance/utils/GLHelper.java diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 1ce075c4..3ae341ea 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -18,25 +18,23 @@ package yugecin.opsudance; import com.google.inject.Inject; -import org.newdawn.slick.SlickException; -import yugecin.opsudance.core.Container; -import yugecin.opsudance.core.Demux; +import org.lwjgl.LWJGLException; +import yugecin.opsudance.core.DisplayContainer; public class OpsuDance { - private final Demux stateDemultiplexer; - private final Container container; + private final DisplayContainer container; @Inject - public OpsuDance(Demux stateDemultiplexer, Container container) { - this.stateDemultiplexer = stateDemultiplexer; + public OpsuDance(DisplayContainer container) { this.container = container; } public void start() { try { - container.start(); - } catch (SlickException e) { + container.setIcons(new String[] { "icon16.png", "icon32.png" }); + container.run(); + } catch (LWJGLException e) { e.printStackTrace(); } } diff --git a/src/yugecin/opsudance/core/Container.java b/src/yugecin/opsudance/core/Container.java deleted file mode 100644 index 847e087e..00000000 --- a/src/yugecin/opsudance/core/Container.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 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.core; - -import com.google.inject.Inject; -import org.lwjgl.opengl.Display; -import org.newdawn.slick.AppGameContainer; -import org.newdawn.slick.SlickException; - -/** - * based on itdelatrisu.opsu.Container - */ -public class Container extends AppGameContainer { - - @Inject - public Container(Demux demux) throws SlickException { - super(demux); - setShowFPS(false); - } - - @Override - public void start() throws SlickException { - try { - setup(); - getDelta(); - while (running()) - gameLoop(); - } catch (Exception e) { - } - destroy(); - } - - @Override - protected void gameLoop() throws SlickException { - int delta = getDelta(); - if (!Display.isVisible() && updateOnlyOnVisible) { - try { Thread.sleep(100); } catch (Exception e) {} - } else { - try { - updateAndRender(delta); - } catch (SlickException e) { - running = false; - return; - } - } - updateFPS(); - Display.update(); - if (Display.isCloseRequested()) { - if (game.closeRequested()) - running = false; - } - } -} diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index 464daf34..2bd1c65e 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -18,7 +18,7 @@ package yugecin.opsudance.core; import com.google.inject.Inject; -import org.newdawn.slick.*; +import org.newdawn.slick.Graphics; import yugecin.opsudance.kernel.InstanceContainer; import yugecin.opsudance.states.EmptyState; import yugecin.opsudance.states.GameState; @@ -26,7 +26,10 @@ import yugecin.opsudance.states.transitions.FadeInTransitionState; import yugecin.opsudance.states.transitions.FadeOutTransitionState; import yugecin.opsudance.states.transitions.TransitionState; -public class Demux implements Game { +/** + * state demultiplexer, sends events to current state + */ +public class Demux { private final InstanceContainer instanceContainer; @@ -40,35 +43,25 @@ public class Demux implements Game { this.instanceContainer = instanceContainer; } - @Override - public void init(GameContainer container) throws SlickException { + // cannot do this in constructor, would cause circular dependency + public void init() { + state = instanceContainer.provide(EmptyState.class); fadeOutTransitionState = instanceContainer.provide(FadeOutTransitionState.class); fadeInTransitionState = instanceContainer.provide(FadeInTransitionState.class); - state = instanceContainer.provide(EmptyState.class); - state.enter(); } - @Override - public void update(GameContainer container, int delta) throws SlickException { + public void update(int delta) { state.update(delta); } - @Override - public void render(GameContainer container, Graphics g) throws SlickException { + public void render(Graphics g) { state.render(g); } - @Override - public boolean closeRequested() { - // TODO for what is this used + public boolean onCloseRequest() { return !isTransitioning(); } - @Override - public String getTitle() { - return "opsu!dance"; - } - public boolean isTransitioning() { return state == fadeInTransitionState || state == fadeOutTransitionState; } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java new file mode 100644 index 00000000..215eea1d --- /dev/null +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -0,0 +1,209 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core; + +import com.google.inject.Inject; +import org.lwjgl.LWJGLException; +import org.lwjgl.Sys; +import org.lwjgl.openal.AL; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.DisplayMode; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.opengl.ImageIOImageData; +import org.newdawn.slick.opengl.InternalTextureLoader; +import org.newdawn.slick.opengl.LoadableImageData; +import org.newdawn.slick.opengl.TGAImageData; +import org.newdawn.slick.opengl.renderer.Renderer; +import org.newdawn.slick.opengl.renderer.SGL; +import org.newdawn.slick.util.Log; +import org.newdawn.slick.util.ResourceLoader; +import yugecin.opsudance.states.EmptyRedState; +import yugecin.opsudance.utils.GLHelper; + +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; + +/** + * based on org.newdawn.slick.AppGameContainer + */ +public class DisplayContainer { + + private static SGL GL = Renderer.get(); + + private final Demux demux; + private final DisplayMode nativeDisplayMode; + private final List resolutionChangeListeners; + + private Graphics graphics; + + public int width; + public int height; + + private long lastFrame; + + @Inject + public DisplayContainer(Demux demux) { + this.demux = demux; + this.nativeDisplayMode = Display.getDisplayMode(); + this.resolutionChangeListeners = new LinkedList<>(); + lastFrame = getTime(); + } + + public void addResolutionChangeListener(ResolutionChangeListener listener) { + resolutionChangeListeners.add(listener); + } + + public void run() throws LWJGLException { + demux.init(); + demux.switchStateNow(new EmptyRedState(null, null)); + setup(); + while(!(Display.isCloseRequested() && demux.onCloseRequest())) { + // TODO: lower fps when not visible Display.isVisible + int delta = getDelta(); + GL.glClear(SGL.GL_COLOR_BUFFER_BIT); + /* + graphics.resetTransform(); + graphics.resetFont(); + graphics.resetLineWidth(); + graphics.resetTransform(); + */ + demux.update(delta); + demux.render(graphics); + Display.update(true); + Display.sync(60); + } + teardown(); + } + + private void setup() { + Display.setTitle("opsu!dance"); + try { + // temp displaymode to not flash the screen with a 1ms black window + Display.setDisplayMode(new DisplayMode(100, 100)); + Display.create(); + setDisplayMode(640, 480, false); + } catch (LWJGLException e) { + e.printStackTrace(); + // TODO errorhandler dialog here + Log.error("could not initialize GL", e); + } + } + + private void teardown() { + Display.destroy(); + AL.destroy(); + } + + public void setDisplayMode(int width, int height, boolean fullscreen) throws LWJGLException { + if (this.width == width && this.height == height) { + Display.setFullscreen(fullscreen); + return; + } + + DisplayMode displayMode = null; + if (fullscreen) { + displayMode = GLHelper.findFullscreenDisplayMode(nativeDisplayMode.getBitsPerPixel(), nativeDisplayMode.getFrequency(), width, height); + } + + if (displayMode == null) { + displayMode = new DisplayMode(width,height); + if (fullscreen) { + fullscreen = false; + Log.warn("could not find fullscreen displaymode for " + width + "x" + height); + } + } + + this.width = displayMode.getWidth(); + this.height = displayMode.getHeight(); + + Display.setDisplayMode(displayMode); + Display.setFullscreen(fullscreen); + + initGL(); + + for (ResolutionChangeListener resolutionChangeListener : resolutionChangeListeners) { + resolutionChangeListener.onDisplayResolutionChanged(width, height); + } + + if (displayMode.getBitsPerPixel() == 16) { + InternalTextureLoader.get().set16BitMode(); + } + + getDelta(); + } + + private void initGL() { + GL.initDisplay(width, height); + GL.enterOrtho(width, height); + + graphics = new Graphics(width, height); + graphics.setAntiAlias(false); + + /* + if (input == null) { + input = new Input(height); + } + input.init(height); + // no need to remove listeners? + //input.removeAllListeners(); + if (game instanceof InputListener) { + input.removeListener((InputListener) game); + input.addListener((InputListener) game); + } + */ + + } + + private int getDelta() { + long time = getTime(); + int delta = (int) (time - lastFrame); + lastFrame = time; + return delta; + } + + public long getTime() { + return (Sys.getTime() * 1000) / Sys.getTimerResolution(); + } + + public void setIcons(String[] refs) { + ByteBuffer[] bufs = new ByteBuffer[refs.length]; + + for (int i = 0; i < refs.length; i++) { + LoadableImageData data; + boolean flip = true; + + if (refs[i].endsWith(".tga")) { + data = new TGAImageData(); + } else { + flip = false; + data = new ImageIOImageData(); + } + + try { + bufs[i] = data.loadImage(ResourceLoader.getResourceAsStream(refs[i]), flip, false, null); + } catch (Exception e) { + Log.error("failed to set the icon", e); + return; + } + } + + Display.setIcon(bufs); + } + +} diff --git a/src/yugecin/opsudance/core/ResolutionChangeListener.java b/src/yugecin/opsudance/core/ResolutionChangeListener.java new file mode 100644 index 00000000..9c9b30f8 --- /dev/null +++ b/src/yugecin/opsudance/core/ResolutionChangeListener.java @@ -0,0 +1,24 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core; + +public interface ResolutionChangeListener { + + void onDisplayResolutionChanged(int width, int height); + +} diff --git a/src/yugecin/opsudance/kernel/OpsuDanceModule.java b/src/yugecin/opsudance/kernel/OpsuDanceModule.java index 71a473b8..c7962d66 100644 --- a/src/yugecin/opsudance/kernel/OpsuDanceModule.java +++ b/src/yugecin/opsudance/kernel/OpsuDanceModule.java @@ -19,7 +19,7 @@ package yugecin.opsudance.kernel; import com.google.inject.AbstractModule; import yugecin.opsudance.PreStartupInitializer; -import yugecin.opsudance.core.Container; +import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.Demux; import yugecin.opsudance.states.EmptyRedState; import yugecin.opsudance.states.EmptyState; @@ -31,12 +31,12 @@ public class OpsuDanceModule extends AbstractModule { protected void configure() { bind(InstanceContainer.class).to(InstanceResolver.class); bind(PreStartupInitializer.class).asEagerSingleton(); - bind(Demux.class).asEagerSingleton(); - bind(Container.class).asEagerSingleton(); + bind(DisplayContainer.class).asEagerSingleton(); bind(FadeInTransitionState.class).asEagerSingleton(); bind(FadeOutTransitionState.class).asEagerSingleton(); bind(EmptyRedState.class).asEagerSingleton(); bind(EmptyState.class).asEagerSingleton(); + bind(Demux.class).asEagerSingleton(); } } diff --git a/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java b/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java index d832e414..ce71cfe5 100644 --- a/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java +++ b/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java @@ -18,15 +18,15 @@ package yugecin.opsudance.states.transitions; import com.google.inject.Inject; -import yugecin.opsudance.core.Container; import yugecin.opsudance.core.Demux; +import yugecin.opsudance.core.DisplayContainer; public class FadeInTransitionState extends FadeTransitionState { private final Demux demux; @Inject - public FadeInTransitionState(Container container, Demux demux) { + public FadeInTransitionState(DisplayContainer container, Demux demux) { super(container, 300); this.demux = demux; } diff --git a/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java b/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java index 6d83e4d9..5303d18c 100644 --- a/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java +++ b/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java @@ -18,8 +18,8 @@ package yugecin.opsudance.states.transitions; import com.google.inject.Inject; -import yugecin.opsudance.core.Container; import yugecin.opsudance.core.Demux; +import yugecin.opsudance.core.DisplayContainer; public class FadeOutTransitionState extends FadeTransitionState { @@ -27,7 +27,7 @@ public class FadeOutTransitionState extends FadeTransitionState { private final FadeInTransitionState fadeInTransitionState; @Inject - public FadeOutTransitionState(Container container, Demux demux, FadeInTransitionState fadeInTransitionState) { + public FadeOutTransitionState(DisplayContainer container, Demux demux, FadeInTransitionState fadeInTransitionState) { super(container, 200); this.demux = demux; this.fadeInTransitionState = fadeInTransitionState; diff --git a/src/yugecin/opsudance/states/transitions/FadeTransitionState.java b/src/yugecin/opsudance/states/transitions/FadeTransitionState.java index 3870b0b3..918ed4eb 100644 --- a/src/yugecin/opsudance/states/transitions/FadeTransitionState.java +++ b/src/yugecin/opsudance/states/transitions/FadeTransitionState.java @@ -19,21 +19,21 @@ package yugecin.opsudance.states.transitions; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; -import yugecin.opsudance.core.Container; +import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.states.GameState; public abstract class FadeTransitionState extends TransitionState { protected GameState applicableState; - private final Container container; + private final DisplayContainer container; protected final int fadeTargetTime; protected int fadeTime; private final Color black; - public FadeTransitionState(Container container, int fadeTargetTime) { + public FadeTransitionState(DisplayContainer container, int fadeTargetTime) { super(fadeTargetTime); this.container = container; this.fadeTargetTime = fadeTargetTime; @@ -58,7 +58,7 @@ public abstract class FadeTransitionState extends TransitionState { applicableState.render(g); black.a = getMaskAlphaLevel((float) fadeTime / fadeTargetTime); g.setColor(black); - g.fillRect(0, 0, container.getWidth(), container.getHeight()); + g.fillRect(0, 0, container.width, container.height); } @Override diff --git a/src/yugecin/opsudance/utils/GLHelper.java b/src/yugecin/opsudance/utils/GLHelper.java new file mode 100644 index 00000000..e0c48f46 --- /dev/null +++ b/src/yugecin/opsudance/utils/GLHelper.java @@ -0,0 +1,53 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.utils; + +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.DisplayMode; + +public class GLHelper { + + /** + * from org.newdawn.slick.AppGameContainer#setDisplayMode + */ + public static DisplayMode findFullscreenDisplayMode(int targetBPP, int targetFrequency, int width, int height) throws LWJGLException { + DisplayMode[] modes = Display.getAvailableDisplayModes(); + DisplayMode foundMode = null; + int freq = 0; + int bpp = 0; + + for (DisplayMode current : modes) { + if (current.getWidth() != width || current.getHeight() != height) { + continue; + } + + if (current.getBitsPerPixel() == targetBPP && current.getFrequency() == targetFrequency) { + return current; + } + + if (current.getFrequency() >= freq && (foundMode == null || current.getBitsPerPixel() >= bpp)) { + foundMode = current; + freq = foundMode.getFrequency(); + bpp = foundMode.getBitsPerPixel(); + } + } + return foundMode; + } + +} From 9c0eaca4eecc4bfb25d173feef65a9a01ec31bd9 Mon Sep 17 00:00:00 2001 From: yugecin Date: Mon, 9 Jan 2017 22:45:52 +0100 Subject: [PATCH 06/92] moving stuff around --- src/yugecin/opsudance/OpsuDance.java | 1 - .../opsudance/core/DisplayContainer.java | 31 +--------------- src/yugecin/opsudance/utils/GLHelper.java | 35 +++++++++++++++++++ 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 3ae341ea..6c2408a5 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -32,7 +32,6 @@ public class OpsuDance { public void start() { try { - container.setIcons(new String[] { "icon16.png", "icon32.png" }); container.run(); } catch (LWJGLException e) { e.printStackTrace(); diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 215eea1d..34810f07 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -24,18 +24,13 @@ import org.lwjgl.openal.AL; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; import org.newdawn.slick.Graphics; -import org.newdawn.slick.opengl.ImageIOImageData; import org.newdawn.slick.opengl.InternalTextureLoader; -import org.newdawn.slick.opengl.LoadableImageData; -import org.newdawn.slick.opengl.TGAImageData; import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.util.Log; -import org.newdawn.slick.util.ResourceLoader; import yugecin.opsudance.states.EmptyRedState; import yugecin.opsudance.utils.GLHelper; -import java.nio.ByteBuffer; import java.util.LinkedList; import java.util.List; @@ -97,6 +92,7 @@ public class DisplayContainer { // temp displaymode to not flash the screen with a 1ms black window Display.setDisplayMode(new DisplayMode(100, 100)); Display.create(); + GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" }); setDisplayMode(640, 480, false); } catch (LWJGLException e) { e.printStackTrace(); @@ -181,29 +177,4 @@ public class DisplayContainer { return (Sys.getTime() * 1000) / Sys.getTimerResolution(); } - public void setIcons(String[] refs) { - ByteBuffer[] bufs = new ByteBuffer[refs.length]; - - for (int i = 0; i < refs.length; i++) { - LoadableImageData data; - boolean flip = true; - - if (refs[i].endsWith(".tga")) { - data = new TGAImageData(); - } else { - flip = false; - data = new ImageIOImageData(); - } - - try { - bufs[i] = data.loadImage(ResourceLoader.getResourceAsStream(refs[i]), flip, false, null); - } catch (Exception e) { - Log.error("failed to set the icon", e); - return; - } - } - - Display.setIcon(bufs); - } - } diff --git a/src/yugecin/opsudance/utils/GLHelper.java b/src/yugecin/opsudance/utils/GLHelper.java index e0c48f46..b1d4a965 100644 --- a/src/yugecin/opsudance/utils/GLHelper.java +++ b/src/yugecin/opsudance/utils/GLHelper.java @@ -20,6 +20,13 @@ package yugecin.opsudance.utils; import org.lwjgl.LWJGLException; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; +import org.newdawn.slick.opengl.ImageIOImageData; +import org.newdawn.slick.opengl.LoadableImageData; +import org.newdawn.slick.opengl.TGAImageData; +import org.newdawn.slick.util.Log; +import org.newdawn.slick.util.ResourceLoader; + +import java.nio.ByteBuffer; public class GLHelper { @@ -50,4 +57,32 @@ public class GLHelper { return foundMode; } + /** + * from org.newdawn.slick.AppGameContainer#setDisplayMode + */ + public static void setIcons(String[] refs) { + ByteBuffer[] bufs = new ByteBuffer[refs.length]; + + for (int i = 0; i < refs.length; i++) { + LoadableImageData data; + boolean flip = true; + + if (refs[i].endsWith(".tga")) { + data = new TGAImageData(); + } else { + flip = false; + data = new ImageIOImageData(); + } + + try { + bufs[i] = data.loadImage(ResourceLoader.getResourceAsStream(refs[i]), flip, false, null); + } catch (Exception e) { + Log.error("failed to set the icon", e); + return; + } + } + + Display.setIcon(bufs); + } + } From 0ceff03bdd8ae573f9b253c33f28427babc4c28c Mon Sep 17 00:00:00 2001 From: yugecin Date: Mon, 9 Jan 2017 23:10:51 +0100 Subject: [PATCH 07/92] log start time --- src/yugecin/opsudance/OpsuDance.java | 3 +++ src/yugecin/opsudance/core/DisplayContainer.java | 3 +++ src/yugecin/opsudance/kernel/Entrypoint.java | 12 ++++++++++++ 3 files changed, 18 insertions(+) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 6c2408a5..d5e32ff1 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -21,6 +21,8 @@ import com.google.inject.Inject; import org.lwjgl.LWJGLException; import yugecin.opsudance.core.DisplayContainer; +import static yugecin.opsudance.kernel.Entrypoint.log; + public class OpsuDance { private final DisplayContainer container; @@ -31,6 +33,7 @@ public class OpsuDance { } public void start() { + log("initialized"); try { container.run(); } catch (LWJGLException e) { diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 34810f07..ef996416 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -34,6 +34,8 @@ import yugecin.opsudance.utils.GLHelper; import java.util.LinkedList; import java.util.List; +import static yugecin.opsudance.kernel.Entrypoint.log; + /** * based on org.newdawn.slick.AppGameContainer */ @@ -68,6 +70,7 @@ public class DisplayContainer { demux.init(); demux.switchStateNow(new EmptyRedState(null, null)); setup(); + log("ready"); while(!(Display.isCloseRequested() && demux.onCloseRequest())) { // TODO: lower fps when not visible Display.isVisible int delta = getDelta(); diff --git a/src/yugecin/opsudance/kernel/Entrypoint.java b/src/yugecin/opsudance/kernel/Entrypoint.java index 66ea43b5..45f65dde 100644 --- a/src/yugecin/opsudance/kernel/Entrypoint.java +++ b/src/yugecin/opsudance/kernel/Entrypoint.java @@ -21,8 +21,20 @@ import yugecin.opsudance.OpsuDance; public class Entrypoint { + public static final long startTime = System.currentTimeMillis(); + public static final boolean isJarRunning = Entrypoint.class.getResource(String.format("%s.class", Entrypoint.class.getSimpleName())).toString().startsWith("jar:"); + public static void main(String[] args) { + log("launched"); InstanceContainerImpl.initialize().provide(OpsuDance.class).start(); } + public static long runtime() { + return System.currentTimeMillis() - startTime; + } + + public static void log(String message) { + System.out.println(String.format("[%7d] %s", runtime(), message)); + } + } From e05b675285ce696c642201c625cc2a8af5864130 Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 10 Jan 2017 12:07:24 +0100 Subject: [PATCH 08/92] moving stuff around again --- src/yugecin/opsudance/core/Demux.java | 14 +++++++------- src/yugecin/opsudance/core/DisplayContainer.java | 2 -- .../GameState.java => core/state/OpsuState.java} | 4 ++-- .../state}/transitions/FadeInTransitionState.java | 2 +- .../state}/transitions/FadeOutTransitionState.java | 2 +- .../state}/transitions/FadeTransitionState.java | 8 ++++---- .../state}/transitions/TransitionState.java | 10 +++++----- src/yugecin/opsudance/kernel/Entrypoint.java | 1 - src/yugecin/opsudance/kernel/OpsuDanceModule.java | 4 ++-- src/yugecin/opsudance/states/EmptyRedState.java | 3 ++- src/yugecin/opsudance/states/EmptyState.java | 3 ++- 11 files changed, 26 insertions(+), 27 deletions(-) rename src/yugecin/opsudance/{states/GameState.java => core/state/OpsuState.java} (92%) rename src/yugecin/opsudance/{states => core/state}/transitions/FadeInTransitionState.java (96%) rename src/yugecin/opsudance/{states => core/state}/transitions/FadeOutTransitionState.java (96%) rename src/yugecin/opsudance/{states => core/state}/transitions/FadeTransitionState.java (90%) rename src/yugecin/opsudance/{states => core/state}/transitions/TransitionState.java (84%) diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index 2bd1c65e..fdc1661a 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -21,10 +21,10 @@ import com.google.inject.Inject; import org.newdawn.slick.Graphics; import yugecin.opsudance.kernel.InstanceContainer; import yugecin.opsudance.states.EmptyState; -import yugecin.opsudance.states.GameState; -import yugecin.opsudance.states.transitions.FadeInTransitionState; -import yugecin.opsudance.states.transitions.FadeOutTransitionState; -import yugecin.opsudance.states.transitions.TransitionState; +import yugecin.opsudance.core.state.OpsuState; +import yugecin.opsudance.core.state.transitions.FadeInTransitionState; +import yugecin.opsudance.core.state.transitions.FadeOutTransitionState; +import yugecin.opsudance.core.state.transitions.TransitionState; /** * state demultiplexer, sends events to current state @@ -36,7 +36,7 @@ public class Demux { private TransitionState fadeOutTransitionState; private TransitionState fadeInTransitionState; - private GameState state; + private OpsuState state; @Inject public Demux(InstanceContainer instanceContainer) { @@ -66,7 +66,7 @@ public class Demux { return state == fadeInTransitionState || state == fadeOutTransitionState; } - public void switchState(GameState newState) { + public void switchState(OpsuState newState) { if (isTransitioning()) { return; } @@ -76,7 +76,7 @@ public class Demux { state.enter(); } - public void switchStateNow(GameState newState) { + public void switchStateNow(OpsuState newState) { if (!isTransitioning()) { return; } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index ef996416..1b4d0a01 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -143,8 +143,6 @@ public class DisplayContainer { if (displayMode.getBitsPerPixel() == 16) { InternalTextureLoader.get().set16BitMode(); } - - getDelta(); } private void initGL() { diff --git a/src/yugecin/opsudance/states/GameState.java b/src/yugecin/opsudance/core/state/OpsuState.java similarity index 92% rename from src/yugecin/opsudance/states/GameState.java rename to src/yugecin/opsudance/core/state/OpsuState.java index ea679cf1..9238b598 100644 --- a/src/yugecin/opsudance/states/GameState.java +++ b/src/yugecin/opsudance/core/state/OpsuState.java @@ -15,11 +15,11 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.states; +package yugecin.opsudance.core.state; import org.newdawn.slick.Graphics; -public interface GameState { +public interface OpsuState { void update(int delta); void render(Graphics g); diff --git a/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java b/src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java similarity index 96% rename from src/yugecin/opsudance/states/transitions/FadeInTransitionState.java rename to src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java index ce71cfe5..d5b1f15a 100644 --- a/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.states.transitions; +package yugecin.opsudance.core.state.transitions; import com.google.inject.Inject; import yugecin.opsudance.core.Demux; diff --git a/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java b/src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java similarity index 96% rename from src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java rename to src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java index 5303d18c..d45e4d79 100644 --- a/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.states.transitions; +package yugecin.opsudance.core.state.transitions; import com.google.inject.Inject; import yugecin.opsudance.core.Demux; diff --git a/src/yugecin/opsudance/states/transitions/FadeTransitionState.java b/src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java similarity index 90% rename from src/yugecin/opsudance/states/transitions/FadeTransitionState.java rename to src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java index 918ed4eb..dac1f279 100644 --- a/src/yugecin/opsudance/states/transitions/FadeTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java @@ -15,16 +15,16 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.states.transitions; +package yugecin.opsudance.core.state.transitions; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.states.GameState; +import yugecin.opsudance.core.state.OpsuState; public abstract class FadeTransitionState extends TransitionState { - protected GameState applicableState; + protected OpsuState applicableState; private final DisplayContainer container; @@ -40,7 +40,7 @@ public abstract class FadeTransitionState extends TransitionState { black = new Color(Color.black); } - public void setApplicableState(GameState applicableState) { + public void setApplicableState(OpsuState applicableState) { this.applicableState = applicableState; } diff --git a/src/yugecin/opsudance/states/transitions/TransitionState.java b/src/yugecin/opsudance/core/state/transitions/TransitionState.java similarity index 84% rename from src/yugecin/opsudance/states/transitions/TransitionState.java rename to src/yugecin/opsudance/core/state/transitions/TransitionState.java index 98828fb8..5b38399f 100644 --- a/src/yugecin/opsudance/states/transitions/TransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/TransitionState.java @@ -15,14 +15,14 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.states.transitions; +package yugecin.opsudance.core.state.transitions; import org.newdawn.slick.Graphics; -import yugecin.opsudance.states.GameState; +import yugecin.opsudance.core.state.OpsuState; -public abstract class TransitionState implements GameState { +public abstract class TransitionState implements OpsuState { - protected GameState applicableState; + protected OpsuState applicableState; protected final int transitionTargetTime; protected int transitionTime; @@ -31,7 +31,7 @@ public abstract class TransitionState implements GameState { this.transitionTargetTime = transitionTargetTime; } - public void setApplicableState(GameState applicableState) { + public void setApplicableState(OpsuState applicableState) { this.applicableState = applicableState; } diff --git a/src/yugecin/opsudance/kernel/Entrypoint.java b/src/yugecin/opsudance/kernel/Entrypoint.java index 45f65dde..c2f61523 100644 --- a/src/yugecin/opsudance/kernel/Entrypoint.java +++ b/src/yugecin/opsudance/kernel/Entrypoint.java @@ -22,7 +22,6 @@ import yugecin.opsudance.OpsuDance; public class Entrypoint { public static final long startTime = System.currentTimeMillis(); - public static final boolean isJarRunning = Entrypoint.class.getResource(String.format("%s.class", Entrypoint.class.getSimpleName())).toString().startsWith("jar:"); public static void main(String[] args) { log("launched"); diff --git a/src/yugecin/opsudance/kernel/OpsuDanceModule.java b/src/yugecin/opsudance/kernel/OpsuDanceModule.java index c7962d66..59a15e1d 100644 --- a/src/yugecin/opsudance/kernel/OpsuDanceModule.java +++ b/src/yugecin/opsudance/kernel/OpsuDanceModule.java @@ -23,8 +23,8 @@ import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.Demux; import yugecin.opsudance.states.EmptyRedState; import yugecin.opsudance.states.EmptyState; -import yugecin.opsudance.states.transitions.FadeInTransitionState; -import yugecin.opsudance.states.transitions.FadeOutTransitionState; +import yugecin.opsudance.core.state.transitions.FadeInTransitionState; +import yugecin.opsudance.core.state.transitions.FadeOutTransitionState; public class OpsuDanceModule extends AbstractModule { diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index b1a15524..0f7a3b5c 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -21,9 +21,10 @@ import com.google.inject.Inject; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import yugecin.opsudance.core.Demux; +import yugecin.opsudance.core.state.OpsuState; import yugecin.opsudance.kernel.InstanceContainer; -public class EmptyRedState implements GameState { +public class EmptyRedState implements OpsuState { private int counter; diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index 503f727b..c4cb7f49 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -21,9 +21,10 @@ import com.google.inject.Inject; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import yugecin.opsudance.core.Demux; +import yugecin.opsudance.core.state.OpsuState; import yugecin.opsudance.kernel.InstanceContainer; -public class EmptyState implements GameState { +public class EmptyState implements OpsuState { private int counter; From 6857393315db9cf6f976279a6712b7470ac3120a Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 10 Jan 2017 12:43:05 +0100 Subject: [PATCH 09/92] input events --- src/yugecin/opsudance/core/Demux.java | 90 ++++++++++++++++--- .../opsudance/core/DisplayContainer.java | 32 +++---- .../opsudance/core/state/BaseOpsuState.java | 47 ++++++++++ .../opsudance/core/state/OpsuState.java | 25 ++++++ .../state/transitions/TransitionState.java | 3 +- .../opsudance/states/EmptyRedState.java | 38 ++++++-- src/yugecin/opsudance/states/EmptyState.java | 38 ++++++-- 7 files changed, 226 insertions(+), 47 deletions(-) create mode 100644 src/yugecin/opsudance/core/state/BaseOpsuState.java diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index fdc1661a..ac47b35b 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -19,6 +19,9 @@ package yugecin.opsudance.core; import com.google.inject.Inject; import org.newdawn.slick.Graphics; +import org.newdawn.slick.Input; +import org.newdawn.slick.KeyListener; +import org.newdawn.slick.MouseListener; import yugecin.opsudance.kernel.InstanceContainer; import yugecin.opsudance.states.EmptyState; import yugecin.opsudance.core.state.OpsuState; @@ -29,7 +32,7 @@ import yugecin.opsudance.core.state.transitions.TransitionState; /** * state demultiplexer, sends events to current state */ -public class Demux { +public class Demux implements KeyListener, MouseListener { private final InstanceContainer instanceContainer; @@ -49,23 +52,14 @@ public class Demux { fadeOutTransitionState = instanceContainer.provide(FadeOutTransitionState.class); fadeInTransitionState = instanceContainer.provide(FadeInTransitionState.class); } - - public void update(int delta) { - state.update(delta); - } - - public void render(Graphics g) { - state.render(g); - } - - public boolean onCloseRequest() { - return !isTransitioning(); - } - public boolean isTransitioning() { return state == fadeInTransitionState || state == fadeOutTransitionState; } + public void switchState(Class newState) { + switchState(instanceContainer.provide(newState)); + } + public void switchState(OpsuState newState) { if (isTransitioning()) { return; @@ -83,4 +77,72 @@ public class Demux { state = newState; } + /* + * demux stuff below + */ + + public void update(int delta) { + state.update(delta); + } + + public void render(Graphics g) { + state.render(g); + } + + public boolean onCloseRequest() { + return !isTransitioning(); + } + + /* + * input events below, see org.newdawn.slick.KeyListener & org.newdawn.slick.MouseListener + */ + + @Override + public void keyPressed(int key, char c) { + state.keyPressed(key, c); + } + + @Override + public void keyReleased(int key, char c) { + state.keyReleased(key, c); + } + + @Override + public void mouseWheelMoved(int change) { + state.mouseWheelMoved(change); + } + + @Override + public void mouseClicked(int button, int x, int y, int clickCount) { } + + @Override + public void mousePressed(int button, int x, int y) { + state.mousePressed(button, x, y); + } + + @Override + public void mouseReleased(int button, int x, int y) { + state.mouseReleased(button, x, y); + } + + @Override + public void mouseMoved(int oldx, int oldy, int newx, int newy) { } + + @Override + public void mouseDragged(int oldx, int oldy, int newx, int newy) { } + + @Override + public void setInput(Input input) { } + + @Override + public boolean isAcceptingInput() { + return true; + } + + @Override + public void inputEnded() { } + + @Override + public void inputStarted() { } + } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 1b4d0a01..02ffaa92 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -24,11 +24,12 @@ import org.lwjgl.openal.AL; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; import org.newdawn.slick.Graphics; +import org.newdawn.slick.Input; import org.newdawn.slick.opengl.InternalTextureLoader; import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.util.Log; -import yugecin.opsudance.states.EmptyRedState; +import yugecin.opsudance.core.state.OpsuState; import yugecin.opsudance.utils.GLHelper; import java.util.LinkedList; @@ -48,6 +49,7 @@ public class DisplayContainer { private final List resolutionChangeListeners; private Graphics graphics; + private Input input; public int width; public int height; @@ -66,14 +68,22 @@ public class DisplayContainer { resolutionChangeListeners.add(listener); } + public void switchState(OpsuState newState) { + demux.switchState(newState); + } + + public void switchState(Class newState) { + demux.switchState(newState); + } + public void run() throws LWJGLException { demux.init(); - demux.switchStateNow(new EmptyRedState(null, null)); setup(); - log("ready"); + log("GL ready"); while(!(Display.isCloseRequested() && demux.onCloseRequest())) { // TODO: lower fps when not visible Display.isVisible int delta = getDelta(); + input.poll(width, height); GL.glClear(SGL.GL_COLOR_BUFFER_BIT); /* graphics.resetTransform(); @@ -152,19 +162,9 @@ public class DisplayContainer { graphics = new Graphics(width, height); graphics.setAntiAlias(false); - /* - if (input == null) { - input = new Input(height); - } - input.init(height); - // no need to remove listeners? - //input.removeAllListeners(); - if (game instanceof InputListener) { - input.removeListener((InputListener) game); - input.addListener((InputListener) game); - } - */ - + input = new Input(height); + input.addKeyListener(demux); + input.addMouseListener(demux); } private int getDelta() { diff --git a/src/yugecin/opsudance/core/state/BaseOpsuState.java b/src/yugecin/opsudance/core/state/BaseOpsuState.java new file mode 100644 index 00000000..512387e1 --- /dev/null +++ b/src/yugecin/opsudance/core/state/BaseOpsuState.java @@ -0,0 +1,47 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.state; + +public abstract class BaseOpsuState implements OpsuState { + + @Override + public boolean keyPressed(int key, char c) { + return false; + } + + @Override + public boolean keyReleased(int key, char c) { + return false; + } + + @Override + public boolean mouseWheelMoved(int delta) { + return false; + } + + @Override + public boolean mousePressed(int button, int x, int y) { + return false; + } + + @Override + public boolean mouseReleased(int button, int x, int y) { + return false; + } + +} diff --git a/src/yugecin/opsudance/core/state/OpsuState.java b/src/yugecin/opsudance/core/state/OpsuState.java index 9238b598..9620284a 100644 --- a/src/yugecin/opsudance/core/state/OpsuState.java +++ b/src/yugecin/opsudance/core/state/OpsuState.java @@ -26,4 +26,29 @@ public interface OpsuState { void enter(); void leave(); + /** + * @return false to stop event bubbling + */ + boolean keyPressed(int key, char c); + + /** + * @return false to stop event bubbling + */ + boolean keyReleased(int key, char c); + + /** + * @return false to stop event bubbling + */ + boolean mouseWheelMoved(int delta); + + /** + * @return false to stop event bubbling + */ + boolean mousePressed(int button, int x, int y); + + /** + * @return false to stop event bubbling + */ + boolean mouseReleased(int button, int x, int y); + } diff --git a/src/yugecin/opsudance/core/state/transitions/TransitionState.java b/src/yugecin/opsudance/core/state/transitions/TransitionState.java index 5b38399f..1658a57b 100644 --- a/src/yugecin/opsudance/core/state/transitions/TransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/TransitionState.java @@ -18,9 +18,10 @@ package yugecin.opsudance.core.state.transitions; import org.newdawn.slick.Graphics; +import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.OpsuState; -public abstract class TransitionState implements OpsuState { +public abstract class TransitionState extends BaseOpsuState { protected OpsuState applicableState; diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index 0f7a3b5c..d9cf5ccd 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -20,28 +20,25 @@ package yugecin.opsudance.states; import com.google.inject.Inject; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; -import yugecin.opsudance.core.Demux; +import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.state.OpsuState; -import yugecin.opsudance.kernel.InstanceContainer; public class EmptyRedState implements OpsuState { private int counter; - private final Demux demux; - private final InstanceContainer instanceContainer; + private final DisplayContainer displayContainer; @Inject - public EmptyRedState(InstanceContainer instanceContainer, Demux demux) { - this.instanceContainer = instanceContainer; - this.demux = demux; + public EmptyRedState(DisplayContainer displayContainer) { + this.displayContainer = displayContainer; } @Override public void update(int delta) { counter -= delta; if (counter < 0) { - demux.switchState(instanceContainer.provide(EmptyState.class)); + displayContainer.switchState(EmptyState.class); } } @@ -60,4 +57,29 @@ public class EmptyRedState implements OpsuState { public void leave() { } + @Override + public boolean keyPressed(int key, char c) { + return false; + } + + @Override + public boolean keyReleased(int key, char c) { + return false; + } + + @Override + public boolean mouseWheelMoved(int delta) { + return false; + } + + @Override + public boolean mousePressed(int button, int x, int y) { + return false; + } + + @Override + public boolean mouseReleased(int button, int x, int y) { + return false; + } + } diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index c4cb7f49..958e6071 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -20,28 +20,25 @@ package yugecin.opsudance.states; import com.google.inject.Inject; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; -import yugecin.opsudance.core.Demux; +import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.state.OpsuState; -import yugecin.opsudance.kernel.InstanceContainer; public class EmptyState implements OpsuState { private int counter; - private final InstanceContainer instanceContainer; - private final Demux demux; + private final DisplayContainer displayContainer; @Inject - public EmptyState(InstanceContainer instanceContainer, Demux demux) { - this.instanceContainer = instanceContainer; - this.demux = demux; + public EmptyState(DisplayContainer displayContainer) { + this.displayContainer = displayContainer; } @Override public void update(int delta) { counter -= delta; if (counter < 0) { - demux.switchState(instanceContainer.provide(EmptyRedState.class)); + displayContainer.switchState(EmptyRedState.class); } } @@ -60,4 +57,29 @@ public class EmptyState implements OpsuState { public void leave() { } + @Override + public boolean keyPressed(int key, char c) { + return false; + } + + @Override + public boolean keyReleased(int key, char c) { + return false; + } + + @Override + public boolean mouseWheelMoved(int delta) { + return false; + } + + @Override + public boolean mousePressed(int button, int x, int y) { + return false; + } + + @Override + public boolean mouseReleased(int button, int x, int y) { + return false; + } + } From 7162f8bc7d47bf7947ef561abf6b73478d33f8e9 Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 10 Jan 2017 13:02:40 +0100 Subject: [PATCH 10/92] handle input at higher rate than render rate --- src/yugecin/opsudance/core/Demux.java | 4 ++ .../opsudance/core/DisplayContainer.java | 56 ++++++++++++++----- .../opsudance/core/state/OpsuState.java | 1 + .../state/transitions/TransitionState.java | 4 ++ .../opsudance/states/EmptyRedState.java | 5 ++ src/yugecin/opsudance/states/EmptyState.java | 4 ++ 6 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index ac47b35b..0b3014ae 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -85,6 +85,10 @@ public class Demux implements KeyListener, MouseListener { state.update(delta); } + public void preRenderUpdate(int delta) { + state.update(delta); + } + public void render(Graphics g) { state.render(g); } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 02ffaa92..bbbc247e 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -54,6 +54,14 @@ public class DisplayContainer { public int width; public int height; + public int targetRenderInterval; + public int targetBackgroundRenderInterval; + + public int realRenderInterval; + public int delta; + + public int timeSinceLastRender; + private long lastFrame; @Inject @@ -61,6 +69,8 @@ public class DisplayContainer { this.demux = demux; this.nativeDisplayMode = Display.getDisplayMode(); this.resolutionChangeListeners = new LinkedList<>(); + targetRenderInterval = 16; // ~60 fps + targetBackgroundRenderInterval = 41; // ~24 fps lastFrame = getTime(); } @@ -81,20 +91,40 @@ public class DisplayContainer { setup(); log("GL ready"); while(!(Display.isCloseRequested() && demux.onCloseRequest())) { - // TODO: lower fps when not visible Display.isVisible - int delta = getDelta(); + delta = getDelta(); + + timeSinceLastRender += delta; + input.poll(width, height); - GL.glClear(SGL.GL_COLOR_BUFFER_BIT); - /* - graphics.resetTransform(); - graphics.resetFont(); - graphics.resetLineWidth(); - graphics.resetTransform(); - */ - demux.update(delta); - demux.render(graphics); - Display.update(true); - Display.sync(60); + + int maxRenderInterval; + if (Display.isVisible() && Display.isActive()) { + maxRenderInterval = targetRenderInterval; + } else { + maxRenderInterval = targetBackgroundRenderInterval; + } + + if (timeSinceLastRender >= maxRenderInterval) { + GL.glClear(SGL.GL_COLOR_BUFFER_BIT); + + /* + graphics.resetTransform(); + graphics.resetFont(); + graphics.resetLineWidth(); + graphics.resetTransform(); + */ + + demux.update(timeSinceLastRender); + demux.render(graphics); + + realRenderInterval = timeSinceLastRender; + timeSinceLastRender = 0; + + Display.update(false); + } + + Display.processMessages(); + Display.sync(1000); } teardown(); } diff --git a/src/yugecin/opsudance/core/state/OpsuState.java b/src/yugecin/opsudance/core/state/OpsuState.java index 9620284a..b2e070fb 100644 --- a/src/yugecin/opsudance/core/state/OpsuState.java +++ b/src/yugecin/opsudance/core/state/OpsuState.java @@ -22,6 +22,7 @@ import org.newdawn.slick.Graphics; public interface OpsuState { void update(int delta); + void preRenderUpdate(int delta); void render(Graphics g); void enter(); void leave(); diff --git a/src/yugecin/opsudance/core/state/transitions/TransitionState.java b/src/yugecin/opsudance/core/state/transitions/TransitionState.java index 1658a57b..e90310f7 100644 --- a/src/yugecin/opsudance/core/state/transitions/TransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/TransitionState.java @@ -45,6 +45,10 @@ public abstract class TransitionState extends BaseOpsuState { } } + @Override + public void preRenderUpdate(int delta) { + } + @Override public void render(Graphics g) { applicableState.render(g); diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index d9cf5ccd..f1073d0c 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -42,6 +42,10 @@ public class EmptyRedState implements OpsuState { } } + @Override + public void preRenderUpdate(int delta) { + } + @Override public void render(Graphics g) { g.setColor(Color.red); @@ -69,6 +73,7 @@ public class EmptyRedState implements OpsuState { @Override public boolean mouseWheelMoved(int delta) { + System.out.println("moved"); return false; } diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index 958e6071..484795ac 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -42,6 +42,10 @@ public class EmptyState implements OpsuState { } } + @Override + public void preRenderUpdate(int delta) { + } + @Override public void render(Graphics g) { g.setColor(Color.green); From 1248d0a29fac6aed6cb94918e41b05f39c054739 Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 10 Jan 2017 13:16:57 +0100 Subject: [PATCH 11/92] disable controllers --- src/yugecin/opsudance/core/DisplayContainer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index bbbc247e..80d94085 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -130,6 +130,7 @@ public class DisplayContainer { } private void setup() { + Input.disableControllers(); Display.setTitle("opsu!dance"); try { // temp displaymode to not flash the screen with a 1ms black window From 6aefa559f4715ffe586d0fcb21dadbc3d22eb15c Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 10 Jan 2017 13:17:13 +0100 Subject: [PATCH 12/92] remove assistedinject dependency for now --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 18108bde..89b1aabc 100644 --- a/pom.xml +++ b/pom.xml @@ -149,11 +149,13 @@ guice 4.1.0 + org.jcraft jorbis From 90684c084ac75b84baeca4d11cf4e9bc47b8a041 Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 10 Jan 2017 14:11:15 +0100 Subject: [PATCH 13/92] fix incorrect calling of update method --- src/yugecin/opsudance/core/Demux.java | 2 +- src/yugecin/opsudance/core/DisplayContainer.java | 3 ++- src/yugecin/opsudance/states/EmptyRedState.java | 6 +++++- src/yugecin/opsudance/states/EmptyState.java | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index 0b3014ae..14cf80fd 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -86,7 +86,7 @@ public class Demux implements KeyListener, MouseListener { } public void preRenderUpdate(int delta) { - state.update(delta); + state.preRenderUpdate(delta); } public void render(Graphics g) { diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 80d94085..b09bdb24 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -96,6 +96,7 @@ public class DisplayContainer { timeSinceLastRender += delta; input.poll(width, height); + demux.update(delta); int maxRenderInterval; if (Display.isVisible() && Display.isActive()) { @@ -114,7 +115,7 @@ public class DisplayContainer { graphics.resetTransform(); */ - demux.update(timeSinceLastRender); + demux.preRenderUpdate(timeSinceLastRender); demux.render(graphics); realRenderInterval = timeSinceLastRender; diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index f1073d0c..b055876b 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -26,6 +26,7 @@ import yugecin.opsudance.core.state.OpsuState; public class EmptyRedState implements OpsuState { private int counter; + private long start; private final DisplayContainer displayContainer; @@ -38,6 +39,8 @@ public class EmptyRedState implements OpsuState { public void update(int delta) { counter -= delta; if (counter < 0) { + counter = 10000; // to prevent more calls to switch, as this will keep rendingering untill state transitioned + System.out.println(System.currentTimeMillis() - start); displayContainer.switchState(EmptyState.class); } } @@ -55,6 +58,7 @@ public class EmptyRedState implements OpsuState { @Override public void enter() { counter = 5000; + start = System.currentTimeMillis(); } @Override @@ -63,6 +67,7 @@ public class EmptyRedState implements OpsuState { @Override public boolean keyPressed(int key, char c) { + System.out.println("pressed"); return false; } @@ -73,7 +78,6 @@ public class EmptyRedState implements OpsuState { @Override public boolean mouseWheelMoved(int delta) { - System.out.println("moved"); return false; } diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index 484795ac..02a1a443 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -38,6 +38,7 @@ public class EmptyState implements OpsuState { public void update(int delta) { counter -= delta; if (counter < 0) { + counter = 10000; // to prevent more calls to switch, as this will keep rendingering untill state transitioned displayContainer.switchState(EmptyRedState.class); } } From 68ac7f3d101fb48976ded10d892ce3af79911dd4 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 11 Jan 2017 20:41:13 +0100 Subject: [PATCH 14/92] ErrorHandler --- src/itdelatrisu/opsu/Utils.java | 2 + src/yugecin/opsudance/OpsuDance.java | 18 +- .../opsudance/core/DisplayContainer.java | 45 ++-- .../errorhandling/ErrorDumpable.java | 26 ++ .../opsudance/errorhandling/ErrorHandler.java | 224 ++++++++++++++++++ .../opsudance/utils/CachedVariable.java | 41 ++++ src/yugecin/opsudance/utils/MiscUtils.java | 42 ++++ 7 files changed, 379 insertions(+), 19 deletions(-) create mode 100644 src/yugecin/opsudance/errorhandling/ErrorDumpable.java create mode 100644 src/yugecin/opsudance/errorhandling/ErrorHandler.java create mode 100644 src/yugecin/opsudance/utils/CachedVariable.java create mode 100644 src/yugecin/opsudance/utils/MiscUtils.java diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index 57bd11e1..930b4eac 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -590,6 +590,8 @@ public class Utils { if (isJarRunning()) return null; File f = new File(".git/refs/remotes/origin/master"); + if (!f.isFile()) + f = new File("../.git/refs/remotes/origin/master"); if (!f.isFile()) return null; try (BufferedReader in = new BufferedReader(new FileReader(f))) { diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index d5e32ff1..4894d1b1 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -20,6 +20,7 @@ package yugecin.opsudance; import com.google.inject.Inject; import org.lwjgl.LWJGLException; import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.errorhandling.ErrorHandler; import static yugecin.opsudance.kernel.Entrypoint.log; @@ -34,11 +35,24 @@ public class OpsuDance { public void start() { log("initialized"); + container.init(); + while (rungame()); + } + + private boolean rungame() { + try { + container.setup(); + } catch (LWJGLException e) { + ErrorHandler.error("could not initialize GL", e, container).showAndExit(); + } + Exception caughtException = null; try { container.run(); - } catch (LWJGLException e) { - e.printStackTrace(); + } catch (Exception e) { + caughtException = e; } + container.teardown(); + return caughtException != null && ErrorHandler.error("update/render error", caughtException, container).show().shouldIgnoreAndContinue(); } } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index b09bdb24..b1551e65 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -23,6 +23,7 @@ import org.lwjgl.Sys; import org.lwjgl.openal.AL; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; +import org.lwjgl.opengl.GL11; import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; import org.newdawn.slick.opengl.InternalTextureLoader; @@ -30,8 +31,10 @@ import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.state.OpsuState; +import yugecin.opsudance.errorhandling.ErrorDumpable; import yugecin.opsudance.utils.GLHelper; +import java.io.StringWriter; import java.util.LinkedList; import java.util.List; @@ -40,7 +43,7 @@ import static yugecin.opsudance.kernel.Entrypoint.log; /** * based on org.newdawn.slick.AppGameContainer */ -public class DisplayContainer { +public class DisplayContainer implements ErrorDumpable { private static SGL GL = Renderer.get(); @@ -64,6 +67,9 @@ public class DisplayContainer { private long lastFrame; + private String glVersion; + private String glVendor; + @Inject public DisplayContainer(Demux demux) { this.demux = demux; @@ -86,10 +92,11 @@ public class DisplayContainer { demux.switchState(newState); } - public void run() throws LWJGLException { + public void init() { demux.init(); - setup(); - log("GL ready"); + } + + public void run() throws LWJGLException { while(!(Display.isCloseRequested() && demux.onCloseRequest())) { delta = getDelta(); @@ -130,23 +137,21 @@ public class DisplayContainer { teardown(); } - private void setup() { + public void setup() throws LWJGLException { + width = height = -1; Input.disableControllers(); Display.setTitle("opsu!dance"); - try { - // temp displaymode to not flash the screen with a 1ms black window - Display.setDisplayMode(new DisplayMode(100, 100)); - Display.create(); - GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" }); - setDisplayMode(640, 480, false); - } catch (LWJGLException e) { - e.printStackTrace(); - // TODO errorhandler dialog here - Log.error("could not initialize GL", e); - } + // temp displaymode to not flash the screen with a 1ms black window + Display.setDisplayMode(new DisplayMode(100, 100)); + Display.create(); + GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" }); + setDisplayMode(640, 480, false); + log("GL ready"); + glVersion = GL11.glGetString(GL11.GL_VERSION); + glVendor = GL11.glGetString(GL11.GL_VENDOR); } - private void teardown() { + public void teardown() { Display.destroy(); AL.destroy(); } @@ -210,4 +215,10 @@ public class DisplayContainer { return (Sys.getTime() * 1000) / Sys.getTimerResolution(); } + @Override + public void writeErrorDump(StringWriter dump) { + dump.append("> DisplayContainer dump").append('\n'); + dump.append("OpenGL version: ").append(glVersion).append( "(").append(glVendor).append(")").append('\n'); + } + } diff --git a/src/yugecin/opsudance/errorhandling/ErrorDumpable.java b/src/yugecin/opsudance/errorhandling/ErrorDumpable.java new file mode 100644 index 00000000..b822a735 --- /dev/null +++ b/src/yugecin/opsudance/errorhandling/ErrorDumpable.java @@ -0,0 +1,26 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.errorhandling; + +import java.io.StringWriter; + +public interface ErrorDumpable { + + void writeErrorDump(StringWriter dump); + +} diff --git a/src/yugecin/opsudance/errorhandling/ErrorHandler.java b/src/yugecin/opsudance/errorhandling/ErrorHandler.java new file mode 100644 index 00000000..6c900efb --- /dev/null +++ b/src/yugecin/opsudance/errorhandling/ErrorHandler.java @@ -0,0 +1,224 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.errorhandling; + +import itdelatrisu.opsu.Options; +import itdelatrisu.opsu.Utils; +import org.newdawn.slick.util.Log; +import yugecin.opsudance.utils.MiscUtils; + +import javax.swing.*; +import java.awt.Desktop; +import java.awt.Cursor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; + +/** + * based on itdelatrisu.opsu.ErrorHandler + */ +public class ErrorHandler { + + private final String customMessage; + private final Throwable cause; + private final String errorDump; + private final String messageBody; + + private boolean preventContinue; + private boolean preventReport; + private boolean ignoreAndContinue; + + private ErrorHandler(String customMessage, Throwable cause, ErrorDumpable[] errorInfoProviders) { + this.customMessage = customMessage; + this.cause = cause; + + StringWriter dump = new StringWriter(); + for (ErrorDumpable infoProvider : errorInfoProviders) { + try { + infoProvider.writeErrorDump(dump); + } catch (Exception e) { + dump + .append("### ") + .append(e.getClass().getSimpleName()) + .append(" while creating errordump for ") + .append(infoProvider.getClass().getSimpleName()); + e.printStackTrace(new PrintWriter(dump)); + } + } + errorDump = dump.toString(); + + dump = new StringWriter(); + dump.append(customMessage).append("\n"); + dump.append("unhandled ").append(cause.getClass().getSimpleName()).append("\n\n"); + cause.printStackTrace(new PrintWriter(dump)); + dump.append("\n\n").append(errorDump); + messageBody = dump.toString(); + Log.error(messageBody); + } + + public static ErrorHandler error(String message, Throwable cause, ErrorDumpable... errorInfoProviders) { + return new ErrorHandler(message, cause, errorInfoProviders); + } + + public ErrorHandler preventReport() { + preventReport = true; + return this; + } + + public ErrorHandler preventContinue() { + preventContinue = true; + return this; + } + + public ErrorHandler show() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + Log.warn("Unable to set look and feel for error dialog"); + } + + String title = "opsu!dance error - " + customMessage; + + String messageText = "opsu!dance has encountered an error."; + if (!preventReport) { + messageText += " Please report this!"; + } + JLabel message = new JLabel(messageText); + + JTextArea textArea = new JTextArea(15, 100); + textArea.setEditable(false); + textArea.setBackground(UIManager.getColor("Panel.background")); + textArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + textArea.setTabSize(2); + textArea.setLineWrap(false); + textArea.setWrapStyleWord(true); + textArea.setText(messageBody); + + Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(), createReportButton() }; + + String[] buttons; + if (preventContinue) { + buttons = new String[] { "Terminate" }; + } else { + buttons = new String[] { "Terminate", "Ignore & continue" }; + } + + JFrame frame = new JFrame(title); + frame.setUndecorated(true); + frame.setVisible(true); + frame.setLocationRelativeTo(null); + int result = JOptionPane.showOptionDialog(frame, + messageComponents, + title, + JOptionPane.DEFAULT_OPTION, + JOptionPane.ERROR_MESSAGE, + null, + buttons, + buttons[buttons.length - 1]); + ignoreAndContinue = result == 1; + frame.dispose(); + + return this; + } + + private JComponent createViewLogButton() { + return createButton("View log", Desktop.Action.OPEN, new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + try { + Desktop.getDesktop().open(Options.LOG_FILE); + } catch (IOException e) { + Log.warn("Could not open log file", e); + JOptionPane.showMessageDialog(null, "whoops could not open log file", "errorception", JOptionPane.ERROR_MESSAGE); + } + } + }); + } + + private JComponent createReportButton() { + if (preventReport) { + return new JLabel(); + } + return createButton("Report error", Desktop.Action.BROWSE, new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + try { + Desktop.getDesktop().browse(createGithubIssueUrl()); + } catch (IOException e) { + Log.warn("Could not open browser to report issue", e); + JOptionPane.showMessageDialog(null, "whoops could not launch a browser", "errorception", JOptionPane.ERROR_MESSAGE); + } + } + }); + } + + private JButton createButton(String buttonText, Desktop.Action action, ActionListener listener) { + JButton button = new JButton(buttonText); + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(action)) { + button.addActionListener(listener); + return button; + } + button.setEnabled(false); + return button; + } + + private URI createGithubIssueUrl() { + StringWriter dump = new StringWriter(); + + dump.append("**opsu!dance version:** ").append(MiscUtils.buildProperties.get().getProperty("version")).append('\n'); + String gitHash = Utils.getGitHash(); + if (gitHash != null) { + dump.append("**git hash:** ").append(gitHash.substring(0, 12)).append('\n'); + } + + dump.append("**os:** ").append(System.getProperty("os.name")) + .append(" (").append(System.getProperty("os.arch")).append(")\n"); + dump.append("**jre:** ").append(System.getProperty("java.version")).append('\n'); + dump.append("**info dump:**").append('\n'); + dump.append("```\n").append(errorDump).append("```").append("\n\n"); + + dump.append("**trace:**").append("\n```\n"); + cause.printStackTrace(new PrintWriter(dump)); + dump.append("```"); + + String issueTitle = ""; + String issueBody = ""; + try { + issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + " " + customMessage, "UTF-8"); + issueBody = URLEncoder.encode(dump.toString(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e); + } + return URI.create(String.format(Options.ISSUES_URL, issueTitle, issueBody)); + } + + public boolean shouldIgnoreAndContinue() { + return ignoreAndContinue; + } + + public void showAndExit() { + show(); + System.exit(1); + } + +} diff --git a/src/yugecin/opsudance/utils/CachedVariable.java b/src/yugecin/opsudance/utils/CachedVariable.java new file mode 100644 index 00000000..1173a661 --- /dev/null +++ b/src/yugecin/opsudance/utils/CachedVariable.java @@ -0,0 +1,41 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.utils; + +public class CachedVariable { + + private T value; + private Getter getter; + + public CachedVariable(Getter getter) { + this.getter = getter; + } + + public T get() { + if (getter != null) { + value = getter.get(); + getter = null; + } + return value; + } + + public interface Getter { + T get(); + } + +} diff --git a/src/yugecin/opsudance/utils/MiscUtils.java b/src/yugecin/opsudance/utils/MiscUtils.java new file mode 100644 index 00000000..c66e5801 --- /dev/null +++ b/src/yugecin/opsudance/utils/MiscUtils.java @@ -0,0 +1,42 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.utils; + +import itdelatrisu.opsu.Options; +import org.newdawn.slick.util.Log; +import org.newdawn.slick.util.ResourceLoader; + +import java.io.IOException; +import java.util.Properties; + +public class MiscUtils { + + public static final CachedVariable buildProperties = new CachedVariable<>(new CachedVariable.Getter() { + @Override + public Properties get() { + Properties props = new Properties(); + try { + props.load(ResourceLoader.getResourceAsStream(Options.VERSION_FILE)); + } catch (IOException e) { + Log.error("Could not read version file", e); + } + return props; + } + }); + +} From cb92d45ae8fb2a09f2d52b4100aba59d38b4af2b Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 11 Jan 2017 20:46:10 +0100 Subject: [PATCH 15/92] rename log method to sout --- src/yugecin/opsudance/OpsuDance.java | 4 ++-- src/yugecin/opsudance/core/DisplayContainer.java | 4 ++-- src/yugecin/opsudance/kernel/Entrypoint.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 4894d1b1..5b3651fa 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -22,7 +22,7 @@ import org.lwjgl.LWJGLException; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.errorhandling.ErrorHandler; -import static yugecin.opsudance.kernel.Entrypoint.log; +import static yugecin.opsudance.kernel.Entrypoint.sout; public class OpsuDance { @@ -34,7 +34,7 @@ public class OpsuDance { } public void start() { - log("initialized"); + sout("initialized"); container.init(); while (rungame()); } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index b1551e65..b9fd70b1 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -38,7 +38,7 @@ import java.io.StringWriter; import java.util.LinkedList; import java.util.List; -import static yugecin.opsudance.kernel.Entrypoint.log; +import static yugecin.opsudance.kernel.Entrypoint.sout; /** * based on org.newdawn.slick.AppGameContainer @@ -146,7 +146,7 @@ public class DisplayContainer implements ErrorDumpable { Display.create(); GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" }); setDisplayMode(640, 480, false); - log("GL ready"); + sout("GL ready"); glVersion = GL11.glGetString(GL11.GL_VERSION); glVendor = GL11.glGetString(GL11.GL_VENDOR); } diff --git a/src/yugecin/opsudance/kernel/Entrypoint.java b/src/yugecin/opsudance/kernel/Entrypoint.java index c2f61523..d330666a 100644 --- a/src/yugecin/opsudance/kernel/Entrypoint.java +++ b/src/yugecin/opsudance/kernel/Entrypoint.java @@ -24,7 +24,7 @@ public class Entrypoint { public static final long startTime = System.currentTimeMillis(); public static void main(String[] args) { - log("launched"); + sout("launched"); InstanceContainerImpl.initialize().provide(OpsuDance.class).start(); } @@ -32,7 +32,7 @@ public class Entrypoint { return System.currentTimeMillis() - startTime; } - public static void log(String message) { + public static void sout(String message) { System.out.println(String.format("[%7d] %s", runtime(), message)); } From e9120652a158ee57ed1f05e3e4cd14cb9b0b0382 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 11 Jan 2017 23:02:12 +0100 Subject: [PATCH 16/92] improve transition base --- src/yugecin/opsudance/OpsuDance.java | 3 +- src/yugecin/opsudance/core/Demux.java | 78 ++++++++++++------- .../opsudance/core/DisplayContainer.java | 14 ++-- .../transitions/EmptyTransitionState.java | 27 +++++++ .../transitions/FadeInTransitionState.java | 19 +---- .../transitions/FadeOutTransitionState.java | 17 +--- .../transitions/FadeTransitionState.java | 33 +------- .../TransitionFinishedListener.java | 24 ++++++ .../state/transitions/TransitionState.java | 21 +++-- .../opsudance/kernel/OpsuDanceModule.java | 6 +- 10 files changed, 138 insertions(+), 104 deletions(-) create mode 100644 src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java create mode 100644 src/yugecin/opsudance/core/state/transitions/TransitionFinishedListener.java diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 5b3651fa..1022158f 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -21,6 +21,7 @@ import com.google.inject.Inject; import org.lwjgl.LWJGLException; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.errorhandling.ErrorHandler; +import yugecin.opsudance.states.EmptyState; import static yugecin.opsudance.kernel.Entrypoint.sout; @@ -35,7 +36,7 @@ public class OpsuDance { public void start() { sout("initialized"); - container.init(); + container.switchStateNow(EmptyState.class); while (rungame()); } diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index 14cf80fd..a4361ca2 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -22,12 +22,10 @@ import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; import org.newdawn.slick.KeyListener; import org.newdawn.slick.MouseListener; +import yugecin.opsudance.core.state.BaseOpsuState; +import yugecin.opsudance.core.state.transitions.*; import yugecin.opsudance.kernel.InstanceContainer; -import yugecin.opsudance.states.EmptyState; import yugecin.opsudance.core.state.OpsuState; -import yugecin.opsudance.core.state.transitions.FadeInTransitionState; -import yugecin.opsudance.core.state.transitions.FadeOutTransitionState; -import yugecin.opsudance.core.state.transitions.TransitionState; /** * state demultiplexer, sends events to current state @@ -36,47 +34,75 @@ public class Demux implements KeyListener, MouseListener { private final InstanceContainer instanceContainer; - private TransitionState fadeOutTransitionState; - private TransitionState fadeInTransitionState; + private TransitionState outTransitionState; + private TransitionState inTransitionState; + + private final TransitionFinishedListener outTransitionListener; + private final TransitionFinishedListener inTransitionListener; private OpsuState state; @Inject - public Demux(InstanceContainer instanceContainer) { + public Demux(final InstanceContainer instanceContainer) { this.instanceContainer = instanceContainer; + + state = new BaseOpsuState() { + + @Override + public void update(int delta) { } + + @Override + public void preRenderUpdate(int delta) { } + + @Override + public void render(Graphics g) { } + + @Override + public void enter() { } + + @Override + public void leave() { } + + }; + + outTransitionListener = new TransitionFinishedListener() { + @Override + public void onFinish() { + state.leave(); + outTransitionState.getApplicableState().leave(); + state = inTransitionState; + state.enter(); + inTransitionState.getApplicableState().enter(); + } + }; + + inTransitionListener = new TransitionFinishedListener() { + @Override + public void onFinish() { + state.leave(); + state = inTransitionState.getApplicableState(); + } + }; } - // cannot do this in constructor, would cause circular dependency - public void init() { - state = instanceContainer.provide(EmptyState.class); - fadeOutTransitionState = instanceContainer.provide(FadeOutTransitionState.class); - fadeInTransitionState = instanceContainer.provide(FadeInTransitionState.class); - } public boolean isTransitioning() { - return state == fadeInTransitionState || state == fadeOutTransitionState; + return state instanceof TransitionState; } public void switchState(Class newState) { - switchState(instanceContainer.provide(newState)); + switchState(newState, FadeOutTransitionState.class, 200, FadeInTransitionState.class, 300); } - public void switchState(OpsuState newState) { + public void switchState(Class newState, Class outTransition, int outTime, Class inTransition, int inTime) { if (isTransitioning()) { return; } - fadeOutTransitionState.setApplicableState(state); - fadeInTransitionState.setApplicableState(newState); - state = fadeOutTransitionState; + outTransitionState = instanceContainer.provide(outTransition).set(state, outTime, outTransitionListener); + inTransitionState = instanceContainer.provide(inTransition).set(instanceContainer.provide(newState), inTime, inTransitionListener); + state = outTransitionState; state.enter(); } - public void switchStateNow(OpsuState newState) { - if (!isTransitioning()) { - return; - } - state = newState; - } - /* * demux stuff below */ diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index b9fd70b1..77368bd3 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -31,6 +31,8 @@ import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.state.OpsuState; +import yugecin.opsudance.core.state.transitions.EmptyTransitionState; +import yugecin.opsudance.core.state.transitions.TransitionState; import yugecin.opsudance.errorhandling.ErrorDumpable; import yugecin.opsudance.utils.GLHelper; @@ -84,16 +86,16 @@ public class DisplayContainer implements ErrorDumpable { resolutionChangeListeners.add(listener); } - public void switchState(OpsuState newState) { - demux.switchState(newState); - } - public void switchState(Class newState) { demux.switchState(newState); } - public void init() { - demux.init(); + public void switchStateNow(Class newState) { + demux.switchState(newState, EmptyTransitionState.class, 0, EmptyTransitionState.class, 0); + } + + public void switchState(Class newState, Class outTransition, int outTime, Class inTransition, int inTime) { + demux.switchState(newState, outTransition, outTime, inTransition, inTime); } public void run() throws LWJGLException { diff --git a/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java b/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java new file mode 100644 index 00000000..fec4c716 --- /dev/null +++ b/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java @@ -0,0 +1,27 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.state.transitions; + +public class EmptyTransitionState extends TransitionState { + + @Override + public void enter() { + finish(); + } + +} diff --git a/src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java b/src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java index d5b1f15a..aa78f646 100644 --- a/src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java @@ -18,17 +18,13 @@ package yugecin.opsudance.core.state.transitions; import com.google.inject.Inject; -import yugecin.opsudance.core.Demux; import yugecin.opsudance.core.DisplayContainer; public class FadeInTransitionState extends FadeTransitionState { - private final Demux demux; - @Inject - public FadeInTransitionState(DisplayContainer container, Demux demux) { - super(container, 300); - this.demux = demux; + public FadeInTransitionState(DisplayContainer container) { + super(container); } @Override @@ -36,15 +32,4 @@ public class FadeInTransitionState extends FadeTransitionState { return 1f - fadeProgress; } - @Override - public void enter() { - super.enter(); - applicableState.enter(); - } - - @Override - protected void onTransitionFinished() { - demux.switchStateNow(applicableState); - } - } diff --git a/src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java b/src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java index d45e4d79..95cde1d7 100644 --- a/src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java @@ -18,19 +18,13 @@ package yugecin.opsudance.core.state.transitions; import com.google.inject.Inject; -import yugecin.opsudance.core.Demux; import yugecin.opsudance.core.DisplayContainer; public class FadeOutTransitionState extends FadeTransitionState { - private final Demux demux; - private final FadeInTransitionState fadeInTransitionState; - @Inject - public FadeOutTransitionState(DisplayContainer container, Demux demux, FadeInTransitionState fadeInTransitionState) { - super(container, 200); - this.demux = demux; - this.fadeInTransitionState = fadeInTransitionState; + public FadeOutTransitionState(DisplayContainer container) { + super(container); } @Override @@ -38,11 +32,4 @@ public class FadeOutTransitionState extends FadeTransitionState { return fadeProgress; } - @Override - protected void onTransitionFinished() { - applicableState.leave(); - demux.switchStateNow(fadeInTransitionState); - fadeInTransitionState.enter(); - } - } diff --git a/src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java b/src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java index dac1f279..8c516c83 100644 --- a/src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java @@ -20,55 +20,26 @@ package yugecin.opsudance.core.state.transitions; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.core.state.OpsuState; public abstract class FadeTransitionState extends TransitionState { - protected OpsuState applicableState; - private final DisplayContainer container; - protected final int fadeTargetTime; - protected int fadeTime; - private final Color black; - public FadeTransitionState(DisplayContainer container, int fadeTargetTime) { - super(fadeTargetTime); + public FadeTransitionState(DisplayContainer container) { this.container = container; - this.fadeTargetTime = fadeTargetTime; black = new Color(Color.black); } - public void setApplicableState(OpsuState applicableState) { - this.applicableState = applicableState; - } - - @Override - public void update(int delta) { - applicableState.update(delta); - fadeTime += delta; - if (fadeTime >= fadeTargetTime) { - onTransitionFinished(); - } - } - @Override public void render(Graphics g) { applicableState.render(g); - black.a = getMaskAlphaLevel((float) fadeTime / fadeTargetTime); + black.a = getMaskAlphaLevel((float) transitionTime / transitionTargetTime); g.setColor(black); g.fillRect(0, 0, container.width, container.height); } - @Override - public void enter() { - fadeTime = 0; - } - - @Override - public void leave() { } - protected abstract float getMaskAlphaLevel(float fadeProgress); } diff --git a/src/yugecin/opsudance/core/state/transitions/TransitionFinishedListener.java b/src/yugecin/opsudance/core/state/transitions/TransitionFinishedListener.java new file mode 100644 index 00000000..3838884b --- /dev/null +++ b/src/yugecin/opsudance/core/state/transitions/TransitionFinishedListener.java @@ -0,0 +1,24 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.state.transitions; + +public interface TransitionFinishedListener { + + void onFinish(); + +} diff --git a/src/yugecin/opsudance/core/state/transitions/TransitionState.java b/src/yugecin/opsudance/core/state/transitions/TransitionState.java index e90310f7..8b839b0d 100644 --- a/src/yugecin/opsudance/core/state/transitions/TransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/TransitionState.java @@ -25,15 +25,20 @@ public abstract class TransitionState extends BaseOpsuState { protected OpsuState applicableState; - protected final int transitionTargetTime; + protected int transitionTargetTime; protected int transitionTime; - public TransitionState(int transitionTargetTime) { - this.transitionTargetTime = transitionTargetTime; + private TransitionFinishedListener listener; + + public final TransitionState set(OpsuState applicableState, int targetTime, TransitionFinishedListener listener) { + this.applicableState = applicableState; + this.transitionTargetTime = targetTime; + this.listener = listener; + return this; } - public void setApplicableState(OpsuState applicableState) { - this.applicableState = applicableState; + public final OpsuState getApplicableState() { + return applicableState; } @Override @@ -41,7 +46,7 @@ public abstract class TransitionState extends BaseOpsuState { applicableState.update(delta); transitionTime += delta; if (transitionTime >= transitionTargetTime) { - onTransitionFinished(); + finish(); } } @@ -62,6 +67,8 @@ public abstract class TransitionState extends BaseOpsuState { @Override public void leave() { } - protected abstract void onTransitionFinished(); + protected final void finish() { + listener.onFinish(); + } } diff --git a/src/yugecin/opsudance/kernel/OpsuDanceModule.java b/src/yugecin/opsudance/kernel/OpsuDanceModule.java index 59a15e1d..0708d3f1 100644 --- a/src/yugecin/opsudance/kernel/OpsuDanceModule.java +++ b/src/yugecin/opsudance/kernel/OpsuDanceModule.java @@ -21,6 +21,7 @@ import com.google.inject.AbstractModule; import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.Demux; +import yugecin.opsudance.core.state.transitions.EmptyTransitionState; import yugecin.opsudance.states.EmptyRedState; import yugecin.opsudance.states.EmptyState; import yugecin.opsudance.core.state.transitions.FadeInTransitionState; @@ -31,12 +32,15 @@ public class OpsuDanceModule extends AbstractModule { protected void configure() { bind(InstanceContainer.class).to(InstanceResolver.class); bind(PreStartupInitializer.class).asEagerSingleton(); + bind(Demux.class).asEagerSingleton(); bind(DisplayContainer.class).asEagerSingleton(); + + bind(EmptyTransitionState.class).asEagerSingleton(); bind(FadeInTransitionState.class).asEagerSingleton(); bind(FadeOutTransitionState.class).asEagerSingleton(); + bind(EmptyRedState.class).asEagerSingleton(); bind(EmptyState.class).asEagerSingleton(); - bind(Demux.class).asEagerSingleton(); } } From 4ba32c44f259da3ad0faffbacf0a84e4ee05acb4 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 11 Jan 2017 23:02:17 +0100 Subject: [PATCH 17/92] typos --- src/yugecin/opsudance/states/EmptyRedState.java | 2 +- src/yugecin/opsudance/states/EmptyState.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index b055876b..e00eeaaf 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -39,7 +39,7 @@ public class EmptyRedState implements OpsuState { public void update(int delta) { counter -= delta; if (counter < 0) { - counter = 10000; // to prevent more calls to switch, as this will keep rendingering untill state transitioned + counter = 10000; // to prevent more calls to switch, as this will keep rendering until state transitioned System.out.println(System.currentTimeMillis() - start); displayContainer.switchState(EmptyState.class); } diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index 02a1a443..8e189941 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -38,7 +38,7 @@ public class EmptyState implements OpsuState { public void update(int delta) { counter -= delta; if (counter < 0) { - counter = 10000; // to prevent more calls to switch, as this will keep rendingering untill state transitioned + counter = 10000; // to prevent more calls to switch, as this will keep rending until state transitioned displayContainer.switchState(EmptyRedState.class); } } From 66ee2a09790d08a83e73629dac73448ff2a95fc6 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 11 Jan 2017 23:04:50 +0100 Subject: [PATCH 18/92] handle close request per state --- src/yugecin/opsudance/core/Demux.java | 2 +- src/yugecin/opsudance/core/state/BaseOpsuState.java | 5 +++++ src/yugecin/opsudance/core/state/OpsuState.java | 5 +++++ .../opsudance/core/state/transitions/TransitionState.java | 5 +++++ src/yugecin/opsudance/states/EmptyRedState.java | 5 +++++ src/yugecin/opsudance/states/EmptyState.java | 5 +++++ 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index a4361ca2..4d3d5c52 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -120,7 +120,7 @@ public class Demux implements KeyListener, MouseListener { } public boolean onCloseRequest() { - return !isTransitioning(); + return state.onCloseRequest(); } /* diff --git a/src/yugecin/opsudance/core/state/BaseOpsuState.java b/src/yugecin/opsudance/core/state/BaseOpsuState.java index 512387e1..faeef07b 100644 --- a/src/yugecin/opsudance/core/state/BaseOpsuState.java +++ b/src/yugecin/opsudance/core/state/BaseOpsuState.java @@ -19,6 +19,11 @@ package yugecin.opsudance.core.state; public abstract class BaseOpsuState implements OpsuState { + @Override + public boolean onCloseRequest() { + return true; + } + @Override public boolean keyPressed(int key, char c) { return false; diff --git a/src/yugecin/opsudance/core/state/OpsuState.java b/src/yugecin/opsudance/core/state/OpsuState.java index b2e070fb..22611efa 100644 --- a/src/yugecin/opsudance/core/state/OpsuState.java +++ b/src/yugecin/opsudance/core/state/OpsuState.java @@ -27,6 +27,11 @@ public interface OpsuState { void enter(); void leave(); + /** + * @return true if closing is allowed + */ + boolean onCloseRequest(); + /** * @return false to stop event bubbling */ diff --git a/src/yugecin/opsudance/core/state/transitions/TransitionState.java b/src/yugecin/opsudance/core/state/transitions/TransitionState.java index 8b839b0d..3af5a749 100644 --- a/src/yugecin/opsudance/core/state/transitions/TransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/TransitionState.java @@ -71,4 +71,9 @@ public abstract class TransitionState extends BaseOpsuState { listener.onFinish(); } + @Override + public boolean onCloseRequest() { + return false; + } + } diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index e00eeaaf..eb40cdc9 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -65,6 +65,11 @@ public class EmptyRedState implements OpsuState { public void leave() { } + @Override + public boolean onCloseRequest() { + return true; + } + @Override public boolean keyPressed(int key, char c) { System.out.println("pressed"); diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index 8e189941..d161f1cb 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -62,6 +62,11 @@ public class EmptyState implements OpsuState { public void leave() { } + @Override + public boolean onCloseRequest() { + return true; + } + @Override public boolean keyPressed(int key, char c) { return false; From cb8eab0023ee854c743cdfce55c96fafd9e26700 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 11 Jan 2017 23:13:37 +0100 Subject: [PATCH 19/92] truncate error body when posting github issue --- .../opsudance/errorhandling/ErrorHandler.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/yugecin/opsudance/errorhandling/ErrorHandler.java b/src/yugecin/opsudance/errorhandling/ErrorHandler.java index 6c900efb..e6d42e21 100644 --- a/src/yugecin/opsudance/errorhandling/ErrorHandler.java +++ b/src/yugecin/opsudance/errorhandling/ErrorHandler.java @@ -73,7 +73,10 @@ public class ErrorHandler { cause.printStackTrace(new PrintWriter(dump)); dump.append("\n\n").append(errorDump); messageBody = dump.toString(); + + Log.error("====== start unhandled exception dump"); Log.error(messageBody); + Log.error("====== end unhandled exception dump"); } public static ErrorHandler error(String message, Throwable cause, ErrorDumpable... errorInfoProviders) { @@ -205,13 +208,21 @@ public class ErrorHandler { String issueBody = ""; try { issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + " " + customMessage, "UTF-8"); - issueBody = URLEncoder.encode(dump.toString(), "UTF-8"); + issueBody = URLEncoder.encode(truncateGithubIssueBody(dump.toString()), "UTF-8"); } catch (UnsupportedEncodingException e) { Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e); } return URI.create(String.format(Options.ISSUES_URL, issueTitle, issueBody)); } + private String truncateGithubIssueBody(String body) { + if (body.length() < 1750) { + return body; + } + Log.warn("error dump too long to fit into github issue url, truncating"); + return body.substring(0, 1740) + "** TRUNCATED **"; + } + public boolean shouldIgnoreAndContinue() { return ignoreAndContinue; } From a647ea206d24ac84794ee60bda849454c38e4603 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 11 Jan 2017 23:20:03 +0100 Subject: [PATCH 20/92] improve errorhandler result text --- .../opsudance/errorhandling/ErrorHandler.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/yugecin/opsudance/errorhandling/ErrorHandler.java b/src/yugecin/opsudance/errorhandling/ErrorHandler.java index e6d42e21..6b396c19 100644 --- a/src/yugecin/opsudance/errorhandling/ErrorHandler.java +++ b/src/yugecin/opsudance/errorhandling/ErrorHandler.java @@ -69,9 +69,8 @@ public class ErrorHandler { dump = new StringWriter(); dump.append(customMessage).append("\n"); - dump.append("unhandled ").append(cause.getClass().getSimpleName()).append("\n\n"); cause.printStackTrace(new PrintWriter(dump)); - dump.append("\n\n").append(errorDump); + dump.append("\n").append(errorDump); messageBody = dump.toString(); Log.error("====== start unhandled exception dump"); @@ -188,21 +187,24 @@ public class ErrorHandler { private URI createGithubIssueUrl() { StringWriter dump = new StringWriter(); - dump.append("**opsu!dance version:** ").append(MiscUtils.buildProperties.get().getProperty("version")).append('\n'); + dump.append(customMessage).append("\n"); + + dump.append("**ver** ").append(MiscUtils.buildProperties.get().getProperty("version")).append('\n'); String gitHash = Utils.getGitHash(); if (gitHash != null) { - dump.append("**git hash:** ").append(gitHash.substring(0, 12)).append('\n'); + dump.append("**git hash** ").append(gitHash.substring(0, 12)).append('\n'); } - dump.append("**os:** ").append(System.getProperty("os.name")) + dump.append("**os** ").append(System.getProperty("os.name")) .append(" (").append(System.getProperty("os.arch")).append(")\n"); - dump.append("**jre:** ").append(System.getProperty("java.version")).append('\n'); - dump.append("**info dump:**").append('\n'); - dump.append("```\n").append(errorDump).append("```").append("\n\n"); + dump.append("**jre** ").append(System.getProperty("java.version")).append('\n'); - dump.append("**trace:**").append("\n```\n"); + dump.append("**trace**").append("\n```\n"); cause.printStackTrace(new PrintWriter(dump)); - dump.append("```"); + dump.append("\n```\n"); + + dump.append("**info dump**").append('\n'); + dump.append("```\n").append(errorDump).append("\n```\n\n"); String issueTitle = ""; String issueBody = ""; @@ -216,11 +218,11 @@ public class ErrorHandler { } private String truncateGithubIssueBody(String body) { - if (body.length() < 1750) { + if (body.replaceAll("[^a-zA-Z+-]", "").length() < 1750) { return body; } Log.warn("error dump too long to fit into github issue url, truncating"); - return body.substring(0, 1740) + "** TRUNCATED **"; + return body.substring(0, 1640) + "** TRUNCATED **\n```"; } public boolean shouldIgnoreAndContinue() { From 7fee3aa0055f2ac28ca5676d575eccde5190bfe3 Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 12 Jan 2017 11:31:58 +0100 Subject: [PATCH 21/92] remove exit code from errorhandler --- src/yugecin/opsudance/OpsuDance.java | 3 ++- src/yugecin/opsudance/errorhandling/ErrorHandler.java | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 1022158f..369ba2e9 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -44,7 +44,8 @@ public class OpsuDance { try { container.setup(); } catch (LWJGLException e) { - ErrorHandler.error("could not initialize GL", e, container).showAndExit(); + ErrorHandler.error("could not initialize GL", e, container).preventContinue().show(); + System.exit(1); } Exception caughtException = null; try { diff --git a/src/yugecin/opsudance/errorhandling/ErrorHandler.java b/src/yugecin/opsudance/errorhandling/ErrorHandler.java index 6b396c19..411afa0c 100644 --- a/src/yugecin/opsudance/errorhandling/ErrorHandler.java +++ b/src/yugecin/opsudance/errorhandling/ErrorHandler.java @@ -229,9 +229,4 @@ public class ErrorHandler { return ignoreAndContinue; } - public void showAndExit() { - show(); - System.exit(1); - } - } From b56007347a4c9c61478b7ef242b4cd440d80c6ed Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 12 Jan 2017 12:45:08 +0100 Subject: [PATCH 22/92] more startup stuff --- src/yugecin/opsudance/OpsuDance.java | 114 ++++++++++++++++++- src/yugecin/opsudance/kernel/Entrypoint.java | 2 +- 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 369ba2e9..c476dad0 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -18,26 +18,58 @@ package yugecin.opsudance; import com.google.inject.Inject; +import itdelatrisu.opsu.Options; +import itdelatrisu.opsu.Utils; +import itdelatrisu.opsu.db.DBController; +import itdelatrisu.opsu.downloads.DownloadList; +import itdelatrisu.opsu.downloads.Updater; import org.lwjgl.LWJGLException; +import org.newdawn.slick.util.Log; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.errorhandling.ErrorHandler; import yugecin.opsudance.states.EmptyState; +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.UnknownHostException; + import static yugecin.opsudance.kernel.Entrypoint.sout; +/* + * loosely based on itdelatrisu.opsu.Opsu + */ public class OpsuDance { private final DisplayContainer container; + private ServerSocket singleInstanceSocket; + @Inject public OpsuDance(DisplayContainer container) { this.container = container; } - public void start() { + public void start(String[] args) { sout("initialized"); + + checkRunningDirectory(); + Options.parseOptions(); + ensureSingleInstance(); + sout("prechecks done and options parsed"); + + initDatabase(); + initUpdater(args); + sout("database & updater initialized"); + container.switchStateNow(EmptyState.class); + while (rungame()); + + closeSingleInstanceSocket(); + DBController.closeConnections(); + DownloadList.get().cancelAllDownloads(); } private boolean rungame() { @@ -57,4 +89,84 @@ public class OpsuDance { return caughtException != null && ErrorHandler.error("update/render error", caughtException, container).show().shouldIgnoreAndContinue(); } + private void initDatabase() { + try { + DBController.init(); + } catch (UnsatisfiedLinkError e) { + errorAndExit("Could not initialize database.", e); + } + } + + private void initUpdater(String[] args) { + // check if just updated + if (args.length >= 2) + Updater.get().setUpdateInfo(args[0], args[1]); + + // check for updates + if (Options.isUpdaterDisabled()) { + return; + } + new Thread() { + @Override + public void run() { + try { + Updater.get().checkForUpdates(); + } catch (IOException e) { + Log.warn("updatecheck failed.", e); + } + } + }.start(); + } + + private void checkRunningDirectory() { + if (!Utils.isJarRunning()) { + return; + } + File runningDir = Utils.getRunningDirectory(); + if (runningDir == null) { + return; + } + if (runningDir.getAbsolutePath().indexOf('!') == -1) { + return; + } + errorAndExit("Cannot run from a path that contains a '!'. Please move or rename the jar and try again."); + } + + private void ensureSingleInstance() { + if (Options.noSingleInstance()) { + return; + } + try { + singleInstanceSocket = new ServerSocket(Options.getPort(), 1, InetAddress.getLocalHost()); + } catch (UnknownHostException e) { + // shouldn't happen + } catch (IOException e) { + errorAndExit(String.format( + "Could not launch. Either opsu! is already running or a different program uses port %d.\n" + + "You can change the port opsu! uses by editing the 'Port' field in the .opsu.cfg configuration file.\n" + + "If that still does not resolve the problem, you can set 'NoSingleInstance' to 'true', but this is not recommended.", Options.getPort()), e); + } + } + + private void closeSingleInstanceSocket() { + if (singleInstanceSocket == null) { + return; + } + try { + singleInstanceSocket.close(); + } catch (IOException e) { + Log.error("Single instance socket was not closed!", e); + } + } + + private void errorAndExit(String errstr) { + ErrorHandler.error(errstr, new Throwable()).preventContinue().show(); + System.exit(1); + } + + private void errorAndExit(String errstr, Throwable cause) { + ErrorHandler.error(errstr, cause).preventContinue().show(); + System.exit(1); + } + } diff --git a/src/yugecin/opsudance/kernel/Entrypoint.java b/src/yugecin/opsudance/kernel/Entrypoint.java index d330666a..13914a6f 100644 --- a/src/yugecin/opsudance/kernel/Entrypoint.java +++ b/src/yugecin/opsudance/kernel/Entrypoint.java @@ -25,7 +25,7 @@ public class Entrypoint { public static void main(String[] args) { sout("launched"); - InstanceContainerImpl.initialize().provide(OpsuDance.class).start(); + InstanceContainerImpl.initialize().provide(OpsuDance.class).start(args); } public static long runtime() { From e4d4e93f0a3a47fe58e0e0b77b33bd62074bedff Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 12 Jan 2017 12:45:14 +0100 Subject: [PATCH 23/92] added todo --- src/yugecin/opsudance/core/DisplayContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 77368bd3..c298b392 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -134,7 +134,7 @@ public class DisplayContainer implements ErrorDumpable { } Display.processMessages(); - Display.sync(1000); + Display.sync(1000); // TODO add option to change this, to not eat CPUs } teardown(); } From c459c7b6bce6957f22ee287e053e172947c4f08c Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 12 Jan 2017 13:07:00 +0100 Subject: [PATCH 24/92] delete gradle once again --- build.gradle | 109 --------------- gradle/wrapper/gradle-wrapper.jar | Bin 52141 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 6 - gradlew | 164 ----------------------- gradlew.bat | 90 ------------- settings.gradle | 1 - 6 files changed, 370 deletions(-) delete mode 100644 build.gradle delete mode 100644 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100644 gradlew delete mode 100644 gradlew.bat delete mode 100644 settings.gradle diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 7f931f5b..00000000 --- a/build.gradle +++ /dev/null @@ -1,109 +0,0 @@ -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'eclipse' -apply plugin: 'idea' -apply plugin: 'application' - -import org.apache.tools.ant.filters.* - -group = 'itdelatrisu' -version = '0.12.1' - -mainClassName = 'itdelatrisu.opsu.Opsu' -buildDir = new File(rootProject.projectDir, "build/") - -def useXDG = 'false' -if (hasProperty('XDG')) { - useXDG = XDG -} - -sourceCompatibility = 1.7 -targetCompatibility = 1.7 - -sourceSets { - main { - java { - srcDir 'src' - } - } -} - -repositories { - mavenCentral() -} - -dependencies { - compile('org.lwjgl.lwjgl:lwjgl:2.9.3') { - exclude group: 'net.java.jinput', module: 'jinput' - } - compile('org.slick2d:slick2d-core:1.0.1') { - exclude group: 'org.lwjgl.lwjgl', module: 'lwjgl' - } - compile 'org.jcraft:jorbis:0.0.17' - compile 'net.lingala.zip4j:zip4j:1.3.2' - compile 'com.googlecode.soundlibs:jlayer:1.0.1-1' - compile 'com.googlecode.soundlibs:mp3spi:1.9.5-1' - compile 'com.googlecode.soundlibs:tritonus-share:0.3.7-2' - compile 'org.xerial:sqlite-jdbc:3.8.10.2' - compile 'org.json:json:20140107' - compile 'net.java.dev.jna:jna:4.1.0' - compile 'net.java.dev.jna:jna-platform:4.1.0' - compile 'org.apache.maven:maven-artifact:3.3.3' - compile 'org.apache.commons:commons-compress:1.9' - compile 'org.tukaani:xz:1.5' - compile 'com.github.jponge:lzma-java:1.3' -} - -def nativePlatforms = ['windows', 'linux', 'osx'] -nativePlatforms.each { platform -> //noinspection GroovyAssignabilityCheck - task "${platform}Natives" { - def outputDir = "${buildDir}/natives/" - inputs.files(configurations.compile) - outputs.dir(outputDir) - doLast { - copy { - def artifacts = configurations.compile.resolvedConfiguration.resolvedArtifacts - .findAll { it.classifier == "natives-$platform" } - artifacts.each { - from zipTree(it.file) - } - into outputDir - } - } - } -} - -processResources { - from 'res' - exclude '**/Thumbs.db' - - filesMatching('version') { - expand(version: project.version, timestamp: new Date().format("yyyy-MM-dd HH:mm")) - } -} - -task unpackNatives { - description "Copies native libraries to the build directory." - dependsOn nativePlatforms.collect { "${it}Natives" }.findAll { tasks[it] } -} - -jar { - manifest { - attributes 'Implementation-Title': 'opsu!', - 'Implementation-Version': version, - 'Main-Class': mainClassName, - 'Use-XDG': useXDG - } - - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - baseName = "opsu" - - from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } - exclude '**/Thumbs.db' - - outputs.upToDateWhen { false } -} - -run { - dependsOn 'unpackNatives' -} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 085a1cdc27db1185342f15a00441734e74fe3735..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52141 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
)Mf(9O=$zt#xQMm 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 \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 8a0b282a..00000000 --- a/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 0dcd0cb3..00000000 --- a/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'opsu' From 1f89c6fcce2d4f0453e899c0bb7f25784d513a18 Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 12 Jan 2017 13:27:52 +0100 Subject: [PATCH 25/92] update readme --- README.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8748a75e..56784b16 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,22 @@ #opsu!dance -[example video](https://www.youtube.com/watch?v=QvXWRMg1gwE) +[example video](https://www.youtube.com/watch?v=tqZqn7nx8N0) -fork of [opsu!](https://github.com/itdelatrisu/opsu) with cursordancing auto. -I make cursordancing video's -[example](https://www.youtube.com/watch?v=1oFH58X_lTY)-, but I have too many requests and this way I can give people the opportunity to just run it and see the result instead of waiting for me to make a video. Original bot is written in C# and videos are made on osu!, but I don't want to distribute that program, because I don't want to endorse cheating in any way. The sources found in this repo are very representative of the ones of my bot (most files are the exact same, except for C#/java changes). +Originally started as a fork of [opsu!](https://github.com/itdelatrisu/opsu) with cursordance stuff. I made a cursordancing bot in C# for osu!, and by adding it into this clone, it allows me to do even more stuff with it. This way I can also provide this client to other players so they can play with it too, as I will not give my bot to people because I don't want to endorse cheating in any way. + +As of 2017 some major changes were made in this fork which changed the inner workings of opsu. This was done in an attempt to make the code more organised (for me, my subjective opinion) and allowed a.o. changing resolution and skin at runtime without the need to restart the whole system. This fork was pretty even to opsu! before this change, but now there are way more differences. + +My goal is to to add cool cursordancing things to this fork, but also make it possible to play the normal way. +###Downloads +Click on the releases link (scroll up) to go to the downloadpage with prebuilt jars. + +###Building You can find general (run/build) instructions in the original [opsu! README](README-OPSU.md). - -###Making videos -You can make videos under following conditions: - -* You will wait a reasonable amount of time between making videos, making too much too fast means it won't be entertaining anymore. -* You will not deny you used opsu!dance to make your video -* You may provide a link to this repository. This is always very appreciated. Please do keep in mind I only made some adjustements/fixes and added the dancing stuff. opsu! was made by [@itdelatrisu](https://github.com/itdelatrisu) (see credits below) -* Asking for beatmap requests is discouraged. After all, everyone can download this and see it for themselves. -* __YOU WILL NOT PRETEND LIKE YOU MADE/OWN THIS SOFTWARE__ +Please note that I am only using maven and gradle scripts are not being updated. ###Credits opsu! was made by Jeffrey Han ([@itdelatrisu](https://github.com/itdelatrisu)). All game concepts and designs are based on work by osu! developer Dean Herbert. Other opsu! credits can be found [here](CREDITS.md). -opsu!dance (everything in the src package yugecin.opsudance) was made by me ([@yugecin](https://github.com/yugecin)). Originally in C#, now ported to java. Some edits were made in the opsu! sources. +opsu!dance (everything in the src package yugecin.opsudance) was made by me ([@yugecin](https://github.com/yugecin)). Eits were made in the opsu! sources, too. ###License **This software is licensed under GNU GPL version 3.** From a1e5c3d30fa08f2df80c51afb9b364bf0b2c2142 Mon Sep 17 00:00:00 2001 From: yugecin Date: Fri, 13 Jan 2017 16:28:36 +0100 Subject: [PATCH 26/92] getting rid of delegation methods --- src/yugecin/opsudance/OpsuDance.java | 2 +- src/yugecin/opsudance/core/Demux.java | 4 ++++ .../opsudance/core/DisplayContainer.java | 17 +---------------- src/yugecin/opsudance/states/EmptyRedState.java | 2 +- src/yugecin/opsudance/states/EmptyState.java | 2 +- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index c476dad0..293019bd 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -63,7 +63,7 @@ public class OpsuDance { initUpdater(args); sout("database & updater initialized"); - container.switchStateNow(EmptyState.class); + container.demux.switchStateNow(EmptyState.class); while (rungame()); diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index 4d3d5c52..17be4f6f 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -93,6 +93,10 @@ public class Demux implements KeyListener, MouseListener { switchState(newState, FadeOutTransitionState.class, 200, FadeInTransitionState.class, 300); } + public void switchStateNow(Class newState) { + switchState(newState, EmptyTransitionState.class, 0, EmptyTransitionState.class, 0); + } + public void switchState(Class newState, Class outTransition, int outTime, Class inTransition, int inTime) { if (isTransitioning()) { return; diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index c298b392..93d3bc1b 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -30,9 +30,6 @@ import org.newdawn.slick.opengl.InternalTextureLoader; import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.util.Log; -import yugecin.opsudance.core.state.OpsuState; -import yugecin.opsudance.core.state.transitions.EmptyTransitionState; -import yugecin.opsudance.core.state.transitions.TransitionState; import yugecin.opsudance.errorhandling.ErrorDumpable; import yugecin.opsudance.utils.GLHelper; @@ -49,7 +46,7 @@ public class DisplayContainer implements ErrorDumpable { private static SGL GL = Renderer.get(); - private final Demux demux; + public final Demux demux; private final DisplayMode nativeDisplayMode; private final List resolutionChangeListeners; @@ -86,18 +83,6 @@ public class DisplayContainer implements ErrorDumpable { resolutionChangeListeners.add(listener); } - public void switchState(Class newState) { - demux.switchState(newState); - } - - public void switchStateNow(Class newState) { - demux.switchState(newState, EmptyTransitionState.class, 0, EmptyTransitionState.class, 0); - } - - public void switchState(Class newState, Class outTransition, int outTime, Class inTransition, int inTime) { - demux.switchState(newState, outTransition, outTime, inTransition, inTime); - } - public void run() throws LWJGLException { while(!(Display.isCloseRequested() && demux.onCloseRequest())) { delta = getDelta(); diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index eb40cdc9..2e8cbc16 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -41,7 +41,7 @@ public class EmptyRedState implements OpsuState { if (counter < 0) { counter = 10000; // to prevent more calls to switch, as this will keep rendering until state transitioned System.out.println(System.currentTimeMillis() - start); - displayContainer.switchState(EmptyState.class); + displayContainer.demux.switchState(EmptyState.class); } } diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index d161f1cb..2f0d6013 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -39,7 +39,7 @@ public class EmptyState implements OpsuState { counter -= delta; if (counter < 0) { counter = 10000; // to prevent more calls to switch, as this will keep rending until state transitioned - displayContainer.switchState(EmptyRedState.class); + displayContainer.demux.switchState(EmptyRedState.class); } } From c467e9b34ef5692c3273e6cea9c900b8e458bf07 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sat, 14 Jan 2017 23:49:10 +0100 Subject: [PATCH 27/92] let the base state listen to the resolution change events --- src/yugecin/opsudance/core/Demux.java | 27 ++++-------- .../opsudance/core/state/BaseOpsuState.java | 44 ++++++++++++++++++- .../transitions/EmptyTransitionState.java | 8 ++++ .../transitions/FadeTransitionState.java | 8 ++-- .../state/transitions/TransitionState.java | 9 ++-- 5 files changed, 69 insertions(+), 27 deletions(-) diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index 17be4f6f..cb248bca 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -22,11 +22,14 @@ import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; import org.newdawn.slick.KeyListener; import org.newdawn.slick.MouseListener; -import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.transitions.*; import yugecin.opsudance.kernel.InstanceContainer; import yugecin.opsudance.core.state.OpsuState; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + /** * state demultiplexer, sends events to current state */ @@ -46,24 +49,12 @@ public class Demux implements KeyListener, MouseListener { public Demux(final InstanceContainer instanceContainer) { this.instanceContainer = instanceContainer; - state = new BaseOpsuState() { - + state = (OpsuState) Proxy.newProxyInstance(OpsuState.class.getClassLoader(), new Class[]{OpsuState.class}, new InvocationHandler() { @Override - public void update(int delta) { } - - @Override - public void preRenderUpdate(int delta) { } - - @Override - public void render(Graphics g) { } - - @Override - public void enter() { } - - @Override - public void leave() { } - - }; + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return false; + } + }); outTransitionListener = new TransitionFinishedListener() { @Override diff --git a/src/yugecin/opsudance/core/state/BaseOpsuState.java b/src/yugecin/opsudance/core/state/BaseOpsuState.java index faeef07b..07a7f173 100644 --- a/src/yugecin/opsudance/core/state/BaseOpsuState.java +++ b/src/yugecin/opsudance/core/state/BaseOpsuState.java @@ -17,7 +17,49 @@ */ package yugecin.opsudance.core.state; -public abstract class BaseOpsuState implements OpsuState { +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.ResolutionChangeListener; + +public abstract class BaseOpsuState implements OpsuState, ResolutionChangeListener { + + protected final DisplayContainer displayContainer; + + /** + * state is dirty when resolution or skin changed but hasn't rendered yet + */ + private boolean isDirty; + private boolean isCurrentState; + + public BaseOpsuState(DisplayContainer displayContainer) { + this.displayContainer = displayContainer; + displayContainer.addResolutionChangeListener(this); + } + + protected void revalidate() { + } + + @Override + public void onDisplayResolutionChanged(int width, int height) { + if (isCurrentState) { + revalidate(); + return; + } + isDirty = true; + } + + @Override + public void enter() { + isCurrentState = true; + if (isDirty) { + revalidate(); + isDirty = false; + } + } + + @Override + public void leave() { + isCurrentState = false; + } @Override public boolean onCloseRequest() { diff --git a/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java b/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java index fec4c716..8153e8f6 100644 --- a/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java @@ -17,8 +17,16 @@ */ package yugecin.opsudance.core.state.transitions; +import com.google.inject.Inject; +import yugecin.opsudance.core.DisplayContainer; + public class EmptyTransitionState extends TransitionState { + @Inject + public EmptyTransitionState(DisplayContainer displayContainer) { + super(displayContainer); + } + @Override public void enter() { finish(); diff --git a/src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java b/src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java index 8c516c83..15202558 100644 --- a/src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/FadeTransitionState.java @@ -23,12 +23,10 @@ import yugecin.opsudance.core.DisplayContainer; public abstract class FadeTransitionState extends TransitionState { - private final DisplayContainer container; - private final Color black; - public FadeTransitionState(DisplayContainer container) { - this.container = container; + public FadeTransitionState(DisplayContainer displayContainer) { + super(displayContainer); black = new Color(Color.black); } @@ -37,7 +35,7 @@ public abstract class FadeTransitionState extends TransitionState { applicableState.render(g); black.a = getMaskAlphaLevel((float) transitionTime / transitionTargetTime); g.setColor(black); - g.fillRect(0, 0, container.width, container.height); + g.fillRect(0, 0, displayContainer.width, displayContainer.height); } protected abstract float getMaskAlphaLevel(float fadeProgress); diff --git a/src/yugecin/opsudance/core/state/transitions/TransitionState.java b/src/yugecin/opsudance/core/state/transitions/TransitionState.java index 3af5a749..5956da7d 100644 --- a/src/yugecin/opsudance/core/state/transitions/TransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/TransitionState.java @@ -18,6 +18,7 @@ package yugecin.opsudance.core.state.transitions; import org.newdawn.slick.Graphics; +import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.OpsuState; @@ -30,6 +31,10 @@ public abstract class TransitionState extends BaseOpsuState { private TransitionFinishedListener listener; + public TransitionState(DisplayContainer displayContainer) { + super(displayContainer); + } + public final TransitionState set(OpsuState applicableState, int targetTime, TransitionFinishedListener listener) { this.applicableState = applicableState; this.transitionTargetTime = targetTime; @@ -61,12 +66,10 @@ public abstract class TransitionState extends BaseOpsuState { @Override public void enter() { + super.enter(); transitionTime = 0; } - @Override - public void leave() { } - protected final void finish() { listener.onFinish(); } From 24c6c7da772c1a10107a53e08565dafa4b0b2060 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 15 Jan 2017 00:11:52 +0100 Subject: [PATCH 28/92] make more stuff ErrorDumpable --- src/yugecin/opsudance/core/Demux.java | 20 ++++++++++++++++++- .../opsudance/core/DisplayContainer.java | 5 +++-- .../opsudance/core/state/BaseOpsuState.java | 8 ++++++++ .../opsudance/core/state/OpsuState.java | 3 ++- .../state/transitions/TransitionState.java | 15 ++++++++++++++ .../opsudance/states/EmptyRedState.java | 8 ++++++++ src/yugecin/opsudance/states/EmptyState.java | 8 ++++++++ 7 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index cb248bca..c9f2972e 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -23,9 +23,11 @@ import org.newdawn.slick.Input; import org.newdawn.slick.KeyListener; import org.newdawn.slick.MouseListener; import yugecin.opsudance.core.state.transitions.*; +import yugecin.opsudance.errorhandling.ErrorDumpable; import yugecin.opsudance.kernel.InstanceContainer; import yugecin.opsudance.core.state.OpsuState; +import java.io.StringWriter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -33,7 +35,7 @@ import java.lang.reflect.Proxy; /** * state demultiplexer, sends events to current state */ -public class Demux implements KeyListener, MouseListener { +public class Demux implements ErrorDumpable, KeyListener, MouseListener { private final InstanceContainer instanceContainer; @@ -98,6 +100,22 @@ public class Demux implements KeyListener, MouseListener { state.enter(); } + @Override + public void writeErrorDump(StringWriter dump) { + dump.append("> Demux dump\n"); + if (isTransitioning()) { + dump.append("doing a transition\n"); + dump.append("using out transition ").append(outTransitionState.getClass().getSimpleName()).append('\n'); + dump.append("using in transition ").append(inTransitionState.getClass().getSimpleName()).append('\n'); + if (state == inTransitionState) { + dump.append("currently doing the in transition\n"); + } else { + dump.append("currently doing the out transition\n"); + } + } + state.writeErrorDump(dump); + } + /* * demux stuff below */ diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 93d3bc1b..7a49b91d 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -204,8 +204,9 @@ public class DisplayContainer implements ErrorDumpable { @Override public void writeErrorDump(StringWriter dump) { - dump.append("> DisplayContainer dump").append('\n'); - dump.append("OpenGL version: ").append(glVersion).append( "(").append(glVendor).append(")").append('\n'); + dump.append("> DisplayContainer dump\n"); + dump.append("OpenGL version: ").append(glVersion).append( "(").append(glVendor).append(")\n"); + demux.writeErrorDump(dump); } } diff --git a/src/yugecin/opsudance/core/state/BaseOpsuState.java b/src/yugecin/opsudance/core/state/BaseOpsuState.java index 07a7f173..2d6e7755 100644 --- a/src/yugecin/opsudance/core/state/BaseOpsuState.java +++ b/src/yugecin/opsudance/core/state/BaseOpsuState.java @@ -20,6 +20,8 @@ package yugecin.opsudance.core.state; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.ResolutionChangeListener; +import java.io.StringWriter; + public abstract class BaseOpsuState implements OpsuState, ResolutionChangeListener { protected final DisplayContainer displayContainer; @@ -91,4 +93,10 @@ public abstract class BaseOpsuState implements OpsuState, ResolutionChangeListen return false; } + @Override + public void writeErrorDump(StringWriter dump) { + dump.append("> BaseOpsuState dump\n"); + dump.append("isDirty: ").append(String.valueOf(isDirty)).append('\n'); + } + } diff --git a/src/yugecin/opsudance/core/state/OpsuState.java b/src/yugecin/opsudance/core/state/OpsuState.java index 22611efa..36d2e581 100644 --- a/src/yugecin/opsudance/core/state/OpsuState.java +++ b/src/yugecin/opsudance/core/state/OpsuState.java @@ -18,8 +18,9 @@ package yugecin.opsudance.core.state; import org.newdawn.slick.Graphics; +import yugecin.opsudance.errorhandling.ErrorDumpable; -public interface OpsuState { +public interface OpsuState extends ErrorDumpable { void update(int delta); void preRenderUpdate(int delta); diff --git a/src/yugecin/opsudance/core/state/transitions/TransitionState.java b/src/yugecin/opsudance/core/state/transitions/TransitionState.java index 5956da7d..11bcad13 100644 --- a/src/yugecin/opsudance/core/state/transitions/TransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/TransitionState.java @@ -22,6 +22,8 @@ import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.OpsuState; +import java.io.StringWriter; + public abstract class TransitionState extends BaseOpsuState { protected OpsuState applicableState; @@ -79,4 +81,17 @@ public abstract class TransitionState extends BaseOpsuState { return false; } + @Override + public void writeErrorDump(StringWriter dump) { + dump.append("> TransitionState dump\n"); + dump.append("progress: ").append(String.valueOf(transitionTime)).append("/").append(String.valueOf(transitionTargetTime)).append('\n'); + dump.append("applicable state: "); + if (applicableState == null) { + dump.append("IS NULL"); + return; + } + dump.append(applicableState.getClass().getSimpleName()).append('\n'); + applicableState.writeErrorDump(dump); + } + } diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index 2e8cbc16..5f98b8f3 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -23,6 +23,8 @@ import org.newdawn.slick.Graphics; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.state.OpsuState; +import java.io.StringWriter; + public class EmptyRedState implements OpsuState { private int counter; @@ -96,4 +98,10 @@ public class EmptyRedState implements OpsuState { return false; } + @Override + public void writeErrorDump(StringWriter dump) { + dump.append("> EmptyRedState dump\n"); + dump.append("its red\n"); + } + } diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index 2f0d6013..faf95e5c 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -23,6 +23,8 @@ import org.newdawn.slick.Graphics; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.state.OpsuState; +import java.io.StringWriter; + public class EmptyState implements OpsuState { private int counter; @@ -92,4 +94,10 @@ public class EmptyState implements OpsuState { return false; } + @Override + public void writeErrorDump(StringWriter dump) { + dump.append("> EmptyState dump\n"); + dump.append("its green\n"); + } + } From 4f1057732144ec9747ab9477aa3306d2a8223b5f Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 15 Jan 2017 00:20:20 +0100 Subject: [PATCH 29/92] make the error handler a single instance and always dump the displaycontainer --- src/yugecin/opsudance/OpsuDance.java | 4 +- .../opsudance/errorhandling/ErrorHandler.java | 46 +++++++++++-------- .../opsudance/kernel/OpsuDanceModule.java | 3 ++ 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 293019bd..2d4ed702 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -76,7 +76,7 @@ public class OpsuDance { try { container.setup(); } catch (LWJGLException e) { - ErrorHandler.error("could not initialize GL", e, container).preventContinue().show(); + ErrorHandler.error("could not initialize GL", e).preventContinue().show(); System.exit(1); } Exception caughtException = null; @@ -86,7 +86,7 @@ public class OpsuDance { caughtException = e; } container.teardown(); - return caughtException != null && ErrorHandler.error("update/render error", caughtException, container).show().shouldIgnoreAndContinue(); + return caughtException != null && ErrorHandler.error("update/render error", caughtException).show().shouldIgnoreAndContinue(); } private void initDatabase() { diff --git a/src/yugecin/opsudance/errorhandling/ErrorHandler.java b/src/yugecin/opsudance/errorhandling/ErrorHandler.java index 411afa0c..9592de0c 100644 --- a/src/yugecin/opsudance/errorhandling/ErrorHandler.java +++ b/src/yugecin/opsudance/errorhandling/ErrorHandler.java @@ -17,9 +17,11 @@ */ package yugecin.opsudance.errorhandling; +import com.google.inject.Inject; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.utils.MiscUtils; import javax.swing.*; @@ -39,31 +41,38 @@ import java.net.URLEncoder; */ public class ErrorHandler { - private final String customMessage; - private final Throwable cause; - private final String errorDump; - private final String messageBody; + private static ErrorHandler instance; + + private final DisplayContainer displayContainer; + + private String customMessage; + private Throwable cause; + private String errorDump; + private String messageBody; private boolean preventContinue; private boolean preventReport; private boolean ignoreAndContinue; - private ErrorHandler(String customMessage, Throwable cause, ErrorDumpable[] errorInfoProviders) { + @Inject + public ErrorHandler(DisplayContainer displayContainer) { + this.displayContainer = displayContainer; + instance = this; + } + + private ErrorHandler init(String customMessage, Throwable cause) { this.customMessage = customMessage; this.cause = cause; StringWriter dump = new StringWriter(); - for (ErrorDumpable infoProvider : errorInfoProviders) { - try { - infoProvider.writeErrorDump(dump); - } catch (Exception e) { - dump - .append("### ") - .append(e.getClass().getSimpleName()) - .append(" while creating errordump for ") - .append(infoProvider.getClass().getSimpleName()); - e.printStackTrace(new PrintWriter(dump)); - } + try { + displayContainer.writeErrorDump(dump); + } catch (Exception e) { + dump + .append("### ") + .append(e.getClass().getSimpleName()) + .append(" while creating errordump"); + e.printStackTrace(new PrintWriter(dump)); } errorDump = dump.toString(); @@ -76,10 +85,11 @@ public class ErrorHandler { Log.error("====== start unhandled exception dump"); Log.error(messageBody); Log.error("====== end unhandled exception dump"); + return this; } - public static ErrorHandler error(String message, Throwable cause, ErrorDumpable... errorInfoProviders) { - return new ErrorHandler(message, cause, errorInfoProviders); + public static ErrorHandler error(String message, Throwable cause) { + return instance.init(message, cause); } public ErrorHandler preventReport() { diff --git a/src/yugecin/opsudance/kernel/OpsuDanceModule.java b/src/yugecin/opsudance/kernel/OpsuDanceModule.java index 0708d3f1..19897f19 100644 --- a/src/yugecin/opsudance/kernel/OpsuDanceModule.java +++ b/src/yugecin/opsudance/kernel/OpsuDanceModule.java @@ -22,6 +22,7 @@ import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.Demux; import yugecin.opsudance.core.state.transitions.EmptyTransitionState; +import yugecin.opsudance.errorhandling.ErrorHandler; import yugecin.opsudance.states.EmptyRedState; import yugecin.opsudance.states.EmptyState; import yugecin.opsudance.core.state.transitions.FadeInTransitionState; @@ -35,6 +36,8 @@ public class OpsuDanceModule extends AbstractModule { bind(Demux.class).asEagerSingleton(); bind(DisplayContainer.class).asEagerSingleton(); + bind(ErrorHandler.class).asEagerSingleton(); + bind(EmptyTransitionState.class).asEagerSingleton(); bind(FadeInTransitionState.class).asEagerSingleton(); bind(FadeOutTransitionState.class).asEagerSingleton(); From 88676f2c36063c67570d8bd0f0d7624fb29f3bbc Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 15 Jan 2017 00:21:52 +0100 Subject: [PATCH 30/92] also catch startup exceptions --- src/yugecin/opsudance/OpsuDance.java | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 2d4ed702..0a96abe7 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -23,7 +23,6 @@ import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.db.DBController; import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.Updater; -import org.lwjgl.LWJGLException; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.errorhandling.ErrorHandler; @@ -52,18 +51,22 @@ public class OpsuDance { } public void start(String[] args) { - sout("initialized"); + try { + sout("initialized"); - checkRunningDirectory(); - Options.parseOptions(); - ensureSingleInstance(); - sout("prechecks done and options parsed"); + checkRunningDirectory(); + Options.parseOptions(); + ensureSingleInstance(); + sout("prechecks done and options parsed"); - initDatabase(); - initUpdater(args); - sout("database & updater initialized"); + initDatabase(); + initUpdater(args); + sout("database & updater initialized"); - container.demux.switchStateNow(EmptyState.class); + container.demux.switchStateNow(EmptyState.class); + } catch (Exception e) { + errorAndExit("startup failure", e); + } while (rungame()); @@ -75,9 +78,8 @@ public class OpsuDance { private boolean rungame() { try { container.setup(); - } catch (LWJGLException e) { - ErrorHandler.error("could not initialize GL", e).preventContinue().show(); - System.exit(1); + } catch (Exception e) { + errorAndExit("could not initialize GL", e); } Exception caughtException = null; try { From ec39392689496e04efa7ae70d5c08f700e2a3b28 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 15 Jan 2017 22:37:59 +0100 Subject: [PATCH 31/92] eventbus --- .../opsudance/core/events/EventBus.java | 58 +++++++++++++++++++ .../opsudance/core/events/EventListener.java | 24 ++++++++ 2 files changed, 82 insertions(+) create mode 100644 src/yugecin/opsudance/core/events/EventBus.java create mode 100644 src/yugecin/opsudance/core/events/EventListener.java diff --git a/src/yugecin/opsudance/core/events/EventBus.java b/src/yugecin/opsudance/core/events/EventBus.java new file mode 100644 index 00000000..4b203b73 --- /dev/null +++ b/src/yugecin/opsudance/core/events/EventBus.java @@ -0,0 +1,58 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.events; + +import com.google.inject.Inject; + +import java.util.*; + +@SuppressWarnings("unchecked") +public class EventBus { + + private final List subscribers; + + @Inject + public EventBus() { + subscribers = new LinkedList<>(); + } + + public void subscribe(Class eventType, EventListener eventListener) { + subscribers.add(new Subscriber<>(eventType, eventListener)); + } + + public void post(Object event) { + for (Subscriber s : subscribers) { + if (s.eventType.isInstance(event)) { + s.listener.onEvent(event); + } + } + } + + private class Subscriber { + + private final Class eventType; + private final EventListener listener; + + private Subscriber(Class eventType, EventListener listener) { + this.eventType = eventType; + this.listener = listener; + } + + } + +} diff --git a/src/yugecin/opsudance/core/events/EventListener.java b/src/yugecin/opsudance/core/events/EventListener.java new file mode 100644 index 00000000..e4249347 --- /dev/null +++ b/src/yugecin/opsudance/core/events/EventListener.java @@ -0,0 +1,24 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.events; + +public interface EventListener { + + void onEvent(T event); + +} From 54b1b3bb1cc3e10343ad2163050f24b8ebaa9ce9 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 15 Jan 2017 22:46:09 +0100 Subject: [PATCH 32/92] use EventBus instead of ResolutionChangeListener --- .../opsudance/core/DisplayContainer.java | 19 +++++++------------ .../opsudance/core/state/BaseOpsuState.java | 9 +++++---- .../ResolutionChangedEvent.java} | 12 +++++++++--- .../opsudance/kernel/OpsuDanceModule.java | 3 +++ 4 files changed, 24 insertions(+), 19 deletions(-) rename src/yugecin/opsudance/{core/ResolutionChangeListener.java => events/ResolutionChangedEvent.java} (76%) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 7a49b91d..78b030ee 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -30,12 +30,12 @@ import org.newdawn.slick.opengl.InternalTextureLoader; import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.errorhandling.ErrorDumpable; +import yugecin.opsudance.events.ResolutionChangedEvent; import yugecin.opsudance.utils.GLHelper; import java.io.StringWriter; -import java.util.LinkedList; -import java.util.List; import static yugecin.opsudance.kernel.Entrypoint.sout; @@ -46,9 +46,10 @@ public class DisplayContainer implements ErrorDumpable { private static SGL GL = Renderer.get(); + public final EventBus eventBus; public final Demux demux; + private final DisplayMode nativeDisplayMode; - private final List resolutionChangeListeners; private Graphics graphics; private Input input; @@ -70,19 +71,15 @@ public class DisplayContainer implements ErrorDumpable { private String glVendor; @Inject - public DisplayContainer(Demux demux) { + public DisplayContainer(Demux demux, EventBus eventBus) { this.demux = demux; + this.eventBus = eventBus; this.nativeDisplayMode = Display.getDisplayMode(); - this.resolutionChangeListeners = new LinkedList<>(); targetRenderInterval = 16; // ~60 fps targetBackgroundRenderInterval = 41; // ~24 fps lastFrame = getTime(); } - public void addResolutionChangeListener(ResolutionChangeListener listener) { - resolutionChangeListeners.add(listener); - } - public void run() throws LWJGLException { while(!(Display.isCloseRequested() && demux.onCloseRequest())) { delta = getDelta(); @@ -170,9 +167,7 @@ public class DisplayContainer implements ErrorDumpable { initGL(); - for (ResolutionChangeListener resolutionChangeListener : resolutionChangeListeners) { - resolutionChangeListener.onDisplayResolutionChanged(width, height); - } + eventBus.post(new ResolutionChangedEvent(this.width, this.height)); if (displayMode.getBitsPerPixel() == 16) { InternalTextureLoader.get().set16BitMode(); diff --git a/src/yugecin/opsudance/core/state/BaseOpsuState.java b/src/yugecin/opsudance/core/state/BaseOpsuState.java index 2d6e7755..3777ba3e 100644 --- a/src/yugecin/opsudance/core/state/BaseOpsuState.java +++ b/src/yugecin/opsudance/core/state/BaseOpsuState.java @@ -18,11 +18,12 @@ package yugecin.opsudance.core.state; import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.core.ResolutionChangeListener; +import yugecin.opsudance.core.events.EventListener; +import yugecin.opsudance.events.ResolutionChangedEvent; import java.io.StringWriter; -public abstract class BaseOpsuState implements OpsuState, ResolutionChangeListener { +public abstract class BaseOpsuState implements OpsuState, EventListener { protected final DisplayContainer displayContainer; @@ -34,14 +35,14 @@ public abstract class BaseOpsuState implements OpsuState, ResolutionChangeListen public BaseOpsuState(DisplayContainer displayContainer) { this.displayContainer = displayContainer; - displayContainer.addResolutionChangeListener(this); + displayContainer.eventBus.subscribe(ResolutionChangedEvent.class, this); } protected void revalidate() { } @Override - public void onDisplayResolutionChanged(int width, int height) { + public void onEvent(ResolutionChangedEvent event) { if (isCurrentState) { revalidate(); return; diff --git a/src/yugecin/opsudance/core/ResolutionChangeListener.java b/src/yugecin/opsudance/events/ResolutionChangedEvent.java similarity index 76% rename from src/yugecin/opsudance/core/ResolutionChangeListener.java rename to src/yugecin/opsudance/events/ResolutionChangedEvent.java index 9c9b30f8..6aa91f23 100644 --- a/src/yugecin/opsudance/core/ResolutionChangeListener.java +++ b/src/yugecin/opsudance/events/ResolutionChangedEvent.java @@ -15,10 +15,16 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.core; +package yugecin.opsudance.events; -public interface ResolutionChangeListener { +public class ResolutionChangedEvent { - void onDisplayResolutionChanged(int width, int height); + public final int width; + public final int height; + + public ResolutionChangedEvent(int width, int height) { + this.width = width; + this.height = height; + } } diff --git a/src/yugecin/opsudance/kernel/OpsuDanceModule.java b/src/yugecin/opsudance/kernel/OpsuDanceModule.java index 19897f19..ce3950d7 100644 --- a/src/yugecin/opsudance/kernel/OpsuDanceModule.java +++ b/src/yugecin/opsudance/kernel/OpsuDanceModule.java @@ -21,6 +21,7 @@ import com.google.inject.AbstractModule; import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.Demux; +import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.state.transitions.EmptyTransitionState; import yugecin.opsudance.errorhandling.ErrorHandler; import yugecin.opsudance.states.EmptyRedState; @@ -31,6 +32,8 @@ import yugecin.opsudance.core.state.transitions.FadeOutTransitionState; public class OpsuDanceModule extends AbstractModule { protected void configure() { + bind(EventBus.class).asEagerSingleton(); + bind(InstanceContainer.class).to(InstanceResolver.class); bind(PreStartupInitializer.class).asEagerSingleton(); bind(Demux.class).asEagerSingleton(); From 7b1d052de776ff1e3968974005623ae06b3c90fa Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 15 Jan 2017 22:54:42 +0100 Subject: [PATCH 33/92] use explicit init with state in demux and remove the mocked state --- src/yugecin/opsudance/OpsuDance.java | 2 +- src/yugecin/opsudance/core/Demux.java | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 0a96abe7..c3eda1c8 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -63,7 +63,7 @@ public class OpsuDance { initUpdater(args); sout("database & updater initialized"); - container.demux.switchStateNow(EmptyState.class); + container.demux.init(EmptyState.class); } catch (Exception e) { errorAndExit("startup failure", e); } diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index c9f2972e..8ed07ba2 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -28,9 +28,6 @@ import yugecin.opsudance.kernel.InstanceContainer; import yugecin.opsudance.core.state.OpsuState; import java.io.StringWriter; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; /** * state demultiplexer, sends events to current state @@ -51,13 +48,6 @@ public class Demux implements ErrorDumpable, KeyListener, MouseListener { public Demux(final InstanceContainer instanceContainer) { this.instanceContainer = instanceContainer; - state = (OpsuState) Proxy.newProxyInstance(OpsuState.class.getClassLoader(), new Class[]{OpsuState.class}, new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return false; - } - }); - outTransitionListener = new TransitionFinishedListener() { @Override public void onFinish() { @@ -78,6 +68,11 @@ public class Demux implements ErrorDumpable, KeyListener, MouseListener { }; } + public void init(Class startingState) { + state = instanceContainer.provide(startingState); + state.enter(); + } + public boolean isTransitioning() { return state instanceof TransitionState; } From 7c79deac65b113c761bdde174d51b0916271466d Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 15 Jan 2017 23:36:21 +0100 Subject: [PATCH 34/92] Initialize fonts in GL setup phase --- src/yugecin/opsudance/core/DisplayContainer.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 78b030ee..eb3ee1b7 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -18,6 +18,8 @@ package yugecin.opsudance.core; import com.google.inject.Inject; +import itdelatrisu.opsu.GameImage; +import itdelatrisu.opsu.ui.Fonts; import org.lwjgl.LWJGLException; import org.lwjgl.Sys; import org.lwjgl.openal.AL; @@ -121,7 +123,7 @@ public class DisplayContainer implements ErrorDumpable { teardown(); } - public void setup() throws LWJGLException { + public void setup() throws Exception { width = height = -1; Input.disableControllers(); Display.setTitle("opsu!dance"); @@ -129,7 +131,7 @@ public class DisplayContainer implements ErrorDumpable { Display.setDisplayMode(new DisplayMode(100, 100)); Display.create(); GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" }); - setDisplayMode(640, 480, false); + setDisplayMode(800, 600, false); sout("GL ready"); glVersion = GL11.glGetString(GL11.GL_VERSION); glVendor = GL11.glGetString(GL11.GL_VENDOR); @@ -140,7 +142,7 @@ public class DisplayContainer implements ErrorDumpable { AL.destroy(); } - public void setDisplayMode(int width, int height, boolean fullscreen) throws LWJGLException { + public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception { if (this.width == width && this.height == height) { Display.setFullscreen(fullscreen); return; @@ -174,7 +176,7 @@ public class DisplayContainer implements ErrorDumpable { } } - private void initGL() { + private void initGL() throws Exception { GL.initDisplay(width, height); GL.enterOrtho(width, height); @@ -184,6 +186,9 @@ public class DisplayContainer implements ErrorDumpable { input = new Input(height); input.addKeyListener(demux); input.addMouseListener(demux); + + GameImage.init(width, height); + Fonts.init(); } private int getDelta() { From 07d15eeb569e4adda2c4f978fc86b452b1688fa6 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 15 Jan 2017 23:38:20 +0100 Subject: [PATCH 35/92] draw fps --- src/yugecin/opsudance/core/Demux.java | 6 ++ .../opsudance/core/DisplayContainer.java | 2 + .../state/specialstates/FpsRenderState.java | 63 +++++++++++++++++++ .../opsudance/kernel/OpsuDanceModule.java | 3 + 4 files changed, 74 insertions(+) create mode 100644 src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index 8ed07ba2..07c9b037 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -22,6 +22,7 @@ import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; import org.newdawn.slick.KeyListener; import org.newdawn.slick.MouseListener; +import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.transitions.*; import yugecin.opsudance.errorhandling.ErrorDumpable; import yugecin.opsudance.kernel.InstanceContainer; @@ -36,6 +37,8 @@ public class Demux implements ErrorDumpable, KeyListener, MouseListener { private final InstanceContainer instanceContainer; + private FpsRenderState fpsState; + private TransitionState outTransitionState; private TransitionState inTransitionState; @@ -71,6 +74,8 @@ public class Demux implements ErrorDumpable, KeyListener, MouseListener { public void init(Class startingState) { state = instanceContainer.provide(startingState); state.enter(); + + fpsState = instanceContainer.provide(FpsRenderState.class); } public boolean isTransitioning() { @@ -125,6 +130,7 @@ public class Demux implements ErrorDumpable, KeyListener, MouseListener { public void render(Graphics g) { state.render(g); + fpsState.render(g); } public boolean onCloseRequest() { diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index eb3ee1b7..bbfa9c0b 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -80,6 +80,8 @@ public class DisplayContainer implements ErrorDumpable { targetRenderInterval = 16; // ~60 fps targetBackgroundRenderInterval = 41; // ~24 fps lastFrame = getTime(); + delta = 1; + realRenderInterval = 1; } public void run() throws LWJGLException { diff --git a/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java b/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java new file mode 100644 index 00000000..5f8eaabf --- /dev/null +++ b/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java @@ -0,0 +1,63 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.state.specialstates; + +import com.google.inject.Inject; +import itdelatrisu.opsu.ui.Fonts; +import org.newdawn.slick.Color; +import org.newdawn.slick.Graphics; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.events.EventListener; +import yugecin.opsudance.events.ResolutionChangedEvent; + +public class FpsRenderState implements EventListener { + + private final DisplayContainer displayContainer; + + private int x; + private int y; + private int singleHeight; + + @Inject + public FpsRenderState(DisplayContainer displayContainer) { + this.displayContainer = displayContainer; + displayContainer.eventBus.subscribe(ResolutionChangedEvent.class, this); + } + + public void render(Graphics g) { + int x = this.x; + x = drawText(g, (1000 / displayContainer.realRenderInterval) + " fps", x, this.y); + drawText(g, (1000 / displayContainer.delta) + " ups", x, this.y); + } + + private int drawText(Graphics g, String text, int x, int y) { + int width = Fonts.SMALL.getWidth(text) + 10; + g.setColor(new Color(0, 0x80, 0)); + g.fillRoundRect(x - width, y, width, singleHeight + 6, 2); + Fonts.SMALL.drawString(x - width + 3, y + 3, text, Color.white); + return x - width - 6; + } + + @Override + public void onEvent(ResolutionChangedEvent event) { + singleHeight = Fonts.SMALL.getLineHeight(); + x = event.width - 3; + y = event.height - 3 - singleHeight - 10; + } + +} diff --git a/src/yugecin/opsudance/kernel/OpsuDanceModule.java b/src/yugecin/opsudance/kernel/OpsuDanceModule.java index ce3950d7..ff61f773 100644 --- a/src/yugecin/opsudance/kernel/OpsuDanceModule.java +++ b/src/yugecin/opsudance/kernel/OpsuDanceModule.java @@ -22,6 +22,7 @@ import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.Demux; import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.transitions.EmptyTransitionState; import yugecin.opsudance.errorhandling.ErrorHandler; import yugecin.opsudance.states.EmptyRedState; @@ -41,6 +42,8 @@ public class OpsuDanceModule extends AbstractModule { bind(ErrorHandler.class).asEagerSingleton(); + bind(FpsRenderState.class).asEagerSingleton(); + bind(EmptyTransitionState.class).asEagerSingleton(); bind(FadeInTransitionState.class).asEagerSingleton(); bind(FadeOutTransitionState.class).asEagerSingleton(); From 7c1be80d0cf53f36c61e8a306a0755b328cd7a08 Mon Sep 17 00:00:00 2001 From: yugecin Date: Mon, 16 Jan 2017 11:55:01 +0100 Subject: [PATCH 36/92] change pom version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 89b1aabc..109111f4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 yugecin opsu-dance - 0.4.2 + 0.5.0-SNAPSHOT ${project.version} ${maven.build.timestamp} From d62d2503e6eae350ae83ba727903419d360d7c12 Mon Sep 17 00:00:00 2001 From: yugecin Date: Mon, 16 Jan 2017 12:10:07 +0100 Subject: [PATCH 37/92] change main class --- pom.xml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 109111f4..a513be66 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ ${project.version} ${maven.build.timestamp} yyyy-MM-dd HH:mm - itdelatrisu.opsu.Opsu + yugecin.opsudance.kernel.Entrypoint false @@ -148,6 +148,16 @@ com.google.inject guice 4.1.0 + + + org.ow2.asm + asm + + + cglib + cglib + + org.jcraft jorbis diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index c3eda1c8..e53fe306 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -17,7 +17,6 @@ */ package yugecin.opsudance; -import com.google.inject.Inject; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.db.DBController; @@ -45,7 +44,6 @@ public class OpsuDance { private ServerSocket singleInstanceSocket; - @Inject public OpsuDance(DisplayContainer container) { this.container = container; } diff --git a/src/yugecin/opsudance/PreStartupInitializer.java b/src/yugecin/opsudance/PreStartupInitializer.java index d54bdc51..b7b671bf 100644 --- a/src/yugecin/opsudance/PreStartupInitializer.java +++ b/src/yugecin/opsudance/PreStartupInitializer.java @@ -17,7 +17,6 @@ */ package yugecin.opsudance; -import com.google.inject.Inject; import itdelatrisu.opsu.NativeLoader; import itdelatrisu.opsu.Options; import org.newdawn.slick.util.FileSystemLocation; @@ -30,7 +29,6 @@ import java.lang.reflect.Field; public class PreStartupInitializer { - @Inject public PreStartupInitializer() { loadNatives(); setResourcePath(); diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index 07c9b037..8d1f095d 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -17,7 +17,6 @@ */ package yugecin.opsudance.core; -import com.google.inject.Inject; import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; import org.newdawn.slick.KeyListener; @@ -47,7 +46,6 @@ public class Demux implements ErrorDumpable, KeyListener, MouseListener { private OpsuState state; - @Inject public Demux(final InstanceContainer instanceContainer) { this.instanceContainer = instanceContainer; diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index bbfa9c0b..fbb13391 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -17,7 +17,6 @@ */ package yugecin.opsudance.core; -import com.google.inject.Inject; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.ui.Fonts; import org.lwjgl.LWJGLException; @@ -72,7 +71,6 @@ public class DisplayContainer implements ErrorDumpable { private String glVersion; private String glVendor; - @Inject public DisplayContainer(Demux demux, EventBus eventBus) { this.demux = demux; this.eventBus = eventBus; diff --git a/src/yugecin/opsudance/core/events/EventBus.java b/src/yugecin/opsudance/core/events/EventBus.java index 4b203b73..51f142a3 100644 --- a/src/yugecin/opsudance/core/events/EventBus.java +++ b/src/yugecin/opsudance/core/events/EventBus.java @@ -17,8 +17,6 @@ */ package yugecin.opsudance.core.events; -import com.google.inject.Inject; - import java.util.*; @SuppressWarnings("unchecked") @@ -26,7 +24,6 @@ public class EventBus { private final List subscribers; - @Inject public EventBus() { subscribers = new LinkedList<>(); } diff --git a/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java b/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java index 5f8eaabf..bdcc84b5 100644 --- a/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java +++ b/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java @@ -17,7 +17,6 @@ */ package yugecin.opsudance.core.state.specialstates; -import com.google.inject.Inject; import itdelatrisu.opsu.ui.Fonts; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; @@ -33,7 +32,6 @@ public class FpsRenderState implements EventListener { private int y; private int singleHeight; - @Inject public FpsRenderState(DisplayContainer displayContainer) { this.displayContainer = displayContainer; displayContainer.eventBus.subscribe(ResolutionChangedEvent.class, this); diff --git a/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java b/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java index 8153e8f6..9119c60c 100644 --- a/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/EmptyTransitionState.java @@ -17,12 +17,10 @@ */ package yugecin.opsudance.core.state.transitions; -import com.google.inject.Inject; import yugecin.opsudance.core.DisplayContainer; public class EmptyTransitionState extends TransitionState { - @Inject public EmptyTransitionState(DisplayContainer displayContainer) { super(displayContainer); } diff --git a/src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java b/src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java index aa78f646..11c471e3 100644 --- a/src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/FadeInTransitionState.java @@ -17,12 +17,10 @@ */ package yugecin.opsudance.core.state.transitions; -import com.google.inject.Inject; import yugecin.opsudance.core.DisplayContainer; public class FadeInTransitionState extends FadeTransitionState { - @Inject public FadeInTransitionState(DisplayContainer container) { super(container); } diff --git a/src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java b/src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java index 95cde1d7..3a5944ab 100644 --- a/src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/FadeOutTransitionState.java @@ -17,12 +17,10 @@ */ package yugecin.opsudance.core.state.transitions; -import com.google.inject.Inject; import yugecin.opsudance.core.DisplayContainer; public class FadeOutTransitionState extends FadeTransitionState { - @Inject public FadeOutTransitionState(DisplayContainer container) { super(container); } diff --git a/src/yugecin/opsudance/errorhandling/ErrorHandler.java b/src/yugecin/opsudance/errorhandling/ErrorHandler.java index 9592de0c..46f7dc93 100644 --- a/src/yugecin/opsudance/errorhandling/ErrorHandler.java +++ b/src/yugecin/opsudance/errorhandling/ErrorHandler.java @@ -17,7 +17,6 @@ */ package yugecin.opsudance.errorhandling; -import com.google.inject.Inject; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; import org.newdawn.slick.util.Log; @@ -54,7 +53,6 @@ public class ErrorHandler { private boolean preventReport; private boolean ignoreAndContinue; - @Inject public ErrorHandler(DisplayContainer displayContainer) { this.displayContainer = displayContainer; instance = this; diff --git a/src/yugecin/opsudance/kernel/InstanceContainerImpl.java b/src/yugecin/opsudance/kernel/InstanceContainerImpl.java deleted file mode 100644 index ed1f7dcc..00000000 --- a/src/yugecin/opsudance/kernel/InstanceContainerImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 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.kernel; - -import com.google.inject.Guice; -import com.google.inject.Injector; - -public class InstanceContainerImpl implements InstanceContainer { - - private static InstanceContainer instance; - - private Injector injector; - - private InstanceContainerImpl() { - injector = Guice.createInjector(new OpsuDanceModule()); - } - - public static InstanceContainer initialize() { - return instance = new InstanceContainerImpl(); - } - - @Deprecated - public static InstanceContainer get() { - return instance; - } - - @Override - public T provide(Class type) { - return injector.getInstance(type); - } - -} diff --git a/src/yugecin/opsudance/kernel/InstanceResolver.java b/src/yugecin/opsudance/kernel/InstanceResolver.java deleted file mode 100644 index ed6780cd..00000000 --- a/src/yugecin/opsudance/kernel/InstanceResolver.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 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.kernel; - -@SuppressWarnings("deprecation") -public class InstanceResolver implements InstanceContainer { - - @Override - public T provide(Class type) { - return InstanceContainerImpl.get().provide(type); - } - -} diff --git a/src/yugecin/opsudance/kernel/OpsuDanceModule.java b/src/yugecin/opsudance/kernel/OpsuDanceModule.java deleted file mode 100644 index ff61f773..00000000 --- a/src/yugecin/opsudance/kernel/OpsuDanceModule.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 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.kernel; - -import com.google.inject.AbstractModule; -import yugecin.opsudance.PreStartupInitializer; -import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.core.Demux; -import yugecin.opsudance.core.events.EventBus; -import yugecin.opsudance.core.state.specialstates.FpsRenderState; -import yugecin.opsudance.core.state.transitions.EmptyTransitionState; -import yugecin.opsudance.errorhandling.ErrorHandler; -import yugecin.opsudance.states.EmptyRedState; -import yugecin.opsudance.states.EmptyState; -import yugecin.opsudance.core.state.transitions.FadeInTransitionState; -import yugecin.opsudance.core.state.transitions.FadeOutTransitionState; - -public class OpsuDanceModule extends AbstractModule { - - protected void configure() { - bind(EventBus.class).asEagerSingleton(); - - bind(InstanceContainer.class).to(InstanceResolver.class); - bind(PreStartupInitializer.class).asEagerSingleton(); - bind(Demux.class).asEagerSingleton(); - bind(DisplayContainer.class).asEagerSingleton(); - - bind(ErrorHandler.class).asEagerSingleton(); - - bind(FpsRenderState.class).asEagerSingleton(); - - bind(EmptyTransitionState.class).asEagerSingleton(); - bind(FadeInTransitionState.class).asEagerSingleton(); - bind(FadeOutTransitionState.class).asEagerSingleton(); - - bind(EmptyRedState.class).asEagerSingleton(); - bind(EmptyState.class).asEagerSingleton(); - } - -} diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index 5f98b8f3..18b92963 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -17,7 +17,6 @@ */ package yugecin.opsudance.states; -import com.google.inject.Inject; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import yugecin.opsudance.core.DisplayContainer; @@ -32,7 +31,6 @@ public class EmptyRedState implements OpsuState { private final DisplayContainer displayContainer; - @Inject public EmptyRedState(DisplayContainer displayContainer) { this.displayContainer = displayContainer; } diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index faf95e5c..b0f30507 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -17,7 +17,6 @@ */ package yugecin.opsudance.states; -import com.google.inject.Inject; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import yugecin.opsudance.core.DisplayContainer; @@ -31,7 +30,6 @@ public class EmptyState implements OpsuState { private final DisplayContainer displayContainer; - @Inject public EmptyState(DisplayContainer displayContainer) { this.displayContainer = displayContainer; } From 09dfc68f16e4fddc6309972ec610c31b01d00a6a Mon Sep 17 00:00:00 2001 From: yugecin Date: Mon, 16 Jan 2017 21:53:48 +0100 Subject: [PATCH 40/92] Moving even more stuff around --- pom.xml | 2 +- src/yugecin/opsudance/OpsuDance.java | 4 ++-- src/yugecin/opsudance/core/Demux.java | 4 ++-- src/yugecin/opsudance/core/DisplayContainer.java | 4 ++-- src/yugecin/opsudance/{kernel => core}/Entrypoint.java | 4 ++-- .../opsudance/{ => core}/errorhandling/ErrorDumpable.java | 2 +- .../opsudance/{ => core}/errorhandling/ErrorHandler.java | 2 +- src/yugecin/opsudance/{ => core}/inject/Binder.java | 2 +- src/yugecin/opsudance/{ => core}/inject/Injector.java | 4 +--- .../opsudance/{kernel => core/inject}/InstanceContainer.java | 2 +- .../opsudance/{ => core}/inject/OpsuDanceInjector.java | 4 ++-- src/yugecin/opsudance/core/state/OpsuState.java | 2 +- 12 files changed, 17 insertions(+), 19 deletions(-) rename src/yugecin/opsudance/{kernel => core}/Entrypoint.java (93%) rename src/yugecin/opsudance/{ => core}/errorhandling/ErrorDumpable.java (94%) rename src/yugecin/opsudance/{ => core}/errorhandling/ErrorHandler.java (99%) rename src/yugecin/opsudance/{ => core}/inject/Binder.java (95%) rename src/yugecin/opsudance/{ => core}/inject/Injector.java (97%) rename src/yugecin/opsudance/{kernel => core/inject}/InstanceContainer.java (95%) rename src/yugecin/opsudance/{ => core}/inject/OpsuDanceInjector.java (95%) diff --git a/pom.xml b/pom.xml index 9a5a4cd9..8062f357 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ ${project.version} ${maven.build.timestamp} yyyy-MM-dd HH:mm - yugecin.opsudance.kernel.Entrypoint + yugecin.opsudance.core.Entrypoint false diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index e53fe306..e3962618 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -24,7 +24,7 @@ import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.Updater; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.errorhandling.ErrorHandler; +import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.states.EmptyState; import java.io.File; @@ -33,7 +33,7 @@ import java.net.InetAddress; import java.net.ServerSocket; import java.net.UnknownHostException; -import static yugecin.opsudance.kernel.Entrypoint.sout; +import static yugecin.opsudance.core.Entrypoint.sout; /* * loosely based on itdelatrisu.opsu.Opsu diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index 8d1f095d..78ed0c4b 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -23,8 +23,8 @@ import org.newdawn.slick.KeyListener; import org.newdawn.slick.MouseListener; import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.transitions.*; -import yugecin.opsudance.errorhandling.ErrorDumpable; -import yugecin.opsudance.kernel.InstanceContainer; +import yugecin.opsudance.core.errorhandling.ErrorDumpable; +import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.state.OpsuState; import java.io.StringWriter; diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index fbb13391..2a2e804d 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -32,13 +32,13 @@ import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.events.EventBus; -import yugecin.opsudance.errorhandling.ErrorDumpable; +import yugecin.opsudance.core.errorhandling.ErrorDumpable; import yugecin.opsudance.events.ResolutionChangedEvent; import yugecin.opsudance.utils.GLHelper; import java.io.StringWriter; -import static yugecin.opsudance.kernel.Entrypoint.sout; +import static yugecin.opsudance.core.Entrypoint.sout; /** * based on org.newdawn.slick.AppGameContainer diff --git a/src/yugecin/opsudance/kernel/Entrypoint.java b/src/yugecin/opsudance/core/Entrypoint.java similarity index 93% rename from src/yugecin/opsudance/kernel/Entrypoint.java rename to src/yugecin/opsudance/core/Entrypoint.java index 8676e2a4..88225e8c 100644 --- a/src/yugecin/opsudance/kernel/Entrypoint.java +++ b/src/yugecin/opsudance/core/Entrypoint.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.kernel; +package yugecin.opsudance.core; import yugecin.opsudance.OpsuDance; -import yugecin.opsudance.inject.OpsuDanceInjector; +import yugecin.opsudance.core.inject.OpsuDanceInjector; public class Entrypoint { diff --git a/src/yugecin/opsudance/errorhandling/ErrorDumpable.java b/src/yugecin/opsudance/core/errorhandling/ErrorDumpable.java similarity index 94% rename from src/yugecin/opsudance/errorhandling/ErrorDumpable.java rename to src/yugecin/opsudance/core/errorhandling/ErrorDumpable.java index b822a735..a47f75ae 100644 --- a/src/yugecin/opsudance/errorhandling/ErrorDumpable.java +++ b/src/yugecin/opsudance/core/errorhandling/ErrorDumpable.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.errorhandling; +package yugecin.opsudance.core.errorhandling; import java.io.StringWriter; diff --git a/src/yugecin/opsudance/errorhandling/ErrorHandler.java b/src/yugecin/opsudance/core/errorhandling/ErrorHandler.java similarity index 99% rename from src/yugecin/opsudance/errorhandling/ErrorHandler.java rename to src/yugecin/opsudance/core/errorhandling/ErrorHandler.java index 46f7dc93..163a1940 100644 --- a/src/yugecin/opsudance/errorhandling/ErrorHandler.java +++ b/src/yugecin/opsudance/core/errorhandling/ErrorHandler.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.errorhandling; +package yugecin.opsudance.core.errorhandling; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; diff --git a/src/yugecin/opsudance/inject/Binder.java b/src/yugecin/opsudance/core/inject/Binder.java similarity index 95% rename from src/yugecin/opsudance/inject/Binder.java rename to src/yugecin/opsudance/core/inject/Binder.java index 2df509ba..16f33180 100644 --- a/src/yugecin/opsudance/inject/Binder.java +++ b/src/yugecin/opsudance/core/inject/Binder.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.inject; +package yugecin.opsudance.core.inject; public interface Binder { diff --git a/src/yugecin/opsudance/inject/Injector.java b/src/yugecin/opsudance/core/inject/Injector.java similarity index 97% rename from src/yugecin/opsudance/inject/Injector.java rename to src/yugecin/opsudance/core/inject/Injector.java index 5c301583..fb075533 100644 --- a/src/yugecin/opsudance/inject/Injector.java +++ b/src/yugecin/opsudance/core/inject/Injector.java @@ -15,9 +15,7 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.inject; - -import yugecin.opsudance.kernel.InstanceContainer; +package yugecin.opsudance.core.inject; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; diff --git a/src/yugecin/opsudance/kernel/InstanceContainer.java b/src/yugecin/opsudance/core/inject/InstanceContainer.java similarity index 95% rename from src/yugecin/opsudance/kernel/InstanceContainer.java rename to src/yugecin/opsudance/core/inject/InstanceContainer.java index b82251be..7cc0065d 100644 --- a/src/yugecin/opsudance/kernel/InstanceContainer.java +++ b/src/yugecin/opsudance/core/inject/InstanceContainer.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.kernel; +package yugecin.opsudance.core.inject; public interface InstanceContainer { diff --git a/src/yugecin/opsudance/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java similarity index 95% rename from src/yugecin/opsudance/inject/OpsuDanceInjector.java rename to src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index e82c6d41..2185472c 100644 --- a/src/yugecin/opsudance/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with opsu!dance. If not, see . */ -package yugecin.opsudance.inject; +package yugecin.opsudance.core.inject; import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.Demux; @@ -25,7 +25,7 @@ import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.transitions.EmptyTransitionState; import yugecin.opsudance.core.state.transitions.FadeInTransitionState; import yugecin.opsudance.core.state.transitions.FadeOutTransitionState; -import yugecin.opsudance.errorhandling.ErrorHandler; +import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.states.EmptyRedState; import yugecin.opsudance.states.EmptyState; diff --git a/src/yugecin/opsudance/core/state/OpsuState.java b/src/yugecin/opsudance/core/state/OpsuState.java index 36d2e581..059d8ba2 100644 --- a/src/yugecin/opsudance/core/state/OpsuState.java +++ b/src/yugecin/opsudance/core/state/OpsuState.java @@ -18,7 +18,7 @@ package yugecin.opsudance.core.state; import org.newdawn.slick.Graphics; -import yugecin.opsudance.errorhandling.ErrorDumpable; +import yugecin.opsudance.core.errorhandling.ErrorDumpable; public interface OpsuState extends ErrorDumpable { From 3ba50ebe60002d5bc4a95d8db470d9c609e618f5 Mon Sep 17 00:00:00 2001 From: yugecin Date: Mon, 16 Jan 2017 22:36:50 +0100 Subject: [PATCH 41/92] move demux into displaycontainer --- src/yugecin/opsudance/OpsuDance.java | 2 +- src/yugecin/opsudance/core/Demux.java | 190 ------------------ .../opsudance/core/DisplayContainer.java | 151 +++++++++++++- .../core/inject/OpsuDanceInjector.java | 2 - .../opsudance/states/EmptyRedState.java | 2 +- src/yugecin/opsudance/states/EmptyState.java | 2 +- 6 files changed, 143 insertions(+), 206 deletions(-) delete mode 100644 src/yugecin/opsudance/core/Demux.java diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index e3962618..d0a9da61 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -61,7 +61,7 @@ public class OpsuDance { initUpdater(args); sout("database & updater initialized"); - container.demux.init(EmptyState.class); + container.init(EmptyState.class); } catch (Exception e) { errorAndExit("startup failure", e); } diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java deleted file mode 100644 index 78ed0c4b..00000000 --- a/src/yugecin/opsudance/core/Demux.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 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.core; - -import org.newdawn.slick.Graphics; -import org.newdawn.slick.Input; -import org.newdawn.slick.KeyListener; -import org.newdawn.slick.MouseListener; -import yugecin.opsudance.core.state.specialstates.FpsRenderState; -import yugecin.opsudance.core.state.transitions.*; -import yugecin.opsudance.core.errorhandling.ErrorDumpable; -import yugecin.opsudance.core.inject.InstanceContainer; -import yugecin.opsudance.core.state.OpsuState; - -import java.io.StringWriter; - -/** - * state demultiplexer, sends events to current state - */ -public class Demux implements ErrorDumpable, KeyListener, MouseListener { - - private final InstanceContainer instanceContainer; - - private FpsRenderState fpsState; - - private TransitionState outTransitionState; - private TransitionState inTransitionState; - - private final TransitionFinishedListener outTransitionListener; - private final TransitionFinishedListener inTransitionListener; - - private OpsuState state; - - public Demux(final InstanceContainer instanceContainer) { - this.instanceContainer = instanceContainer; - - outTransitionListener = new TransitionFinishedListener() { - @Override - public void onFinish() { - state.leave(); - outTransitionState.getApplicableState().leave(); - state = inTransitionState; - state.enter(); - inTransitionState.getApplicableState().enter(); - } - }; - - inTransitionListener = new TransitionFinishedListener() { - @Override - public void onFinish() { - state.leave(); - state = inTransitionState.getApplicableState(); - } - }; - } - - public void init(Class startingState) { - state = instanceContainer.provide(startingState); - state.enter(); - - fpsState = instanceContainer.provide(FpsRenderState.class); - } - - public boolean isTransitioning() { - return state instanceof TransitionState; - } - - public void switchState(Class newState) { - switchState(newState, FadeOutTransitionState.class, 200, FadeInTransitionState.class, 300); - } - - public void switchStateNow(Class newState) { - switchState(newState, EmptyTransitionState.class, 0, EmptyTransitionState.class, 0); - } - - public void switchState(Class newState, Class outTransition, int outTime, Class inTransition, int inTime) { - if (isTransitioning()) { - return; - } - outTransitionState = instanceContainer.provide(outTransition).set(state, outTime, outTransitionListener); - inTransitionState = instanceContainer.provide(inTransition).set(instanceContainer.provide(newState), inTime, inTransitionListener); - state = outTransitionState; - state.enter(); - } - - @Override - public void writeErrorDump(StringWriter dump) { - dump.append("> Demux dump\n"); - if (isTransitioning()) { - dump.append("doing a transition\n"); - dump.append("using out transition ").append(outTransitionState.getClass().getSimpleName()).append('\n'); - dump.append("using in transition ").append(inTransitionState.getClass().getSimpleName()).append('\n'); - if (state == inTransitionState) { - dump.append("currently doing the in transition\n"); - } else { - dump.append("currently doing the out transition\n"); - } - } - state.writeErrorDump(dump); - } - - /* - * demux stuff below - */ - - public void update(int delta) { - state.update(delta); - } - - public void preRenderUpdate(int delta) { - state.preRenderUpdate(delta); - } - - public void render(Graphics g) { - state.render(g); - fpsState.render(g); - } - - public boolean onCloseRequest() { - return state.onCloseRequest(); - } - - /* - * input events below, see org.newdawn.slick.KeyListener & org.newdawn.slick.MouseListener - */ - - @Override - public void keyPressed(int key, char c) { - state.keyPressed(key, c); - } - - @Override - public void keyReleased(int key, char c) { - state.keyReleased(key, c); - } - - @Override - public void mouseWheelMoved(int change) { - state.mouseWheelMoved(change); - } - - @Override - public void mouseClicked(int button, int x, int y, int clickCount) { } - - @Override - public void mousePressed(int button, int x, int y) { - state.mousePressed(button, x, y); - } - - @Override - public void mouseReleased(int button, int x, int y) { - state.mouseReleased(button, x, y); - } - - @Override - public void mouseMoved(int oldx, int oldy, int newx, int newy) { } - - @Override - public void mouseDragged(int oldx, int oldy, int newx, int newy) { } - - @Override - public void setInput(Input input) { } - - @Override - public boolean isAcceptingInput() { - return true; - } - - @Override - public void inputEnded() { } - - @Override - public void inputStarted() { } - -} diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 2a2e804d..2cb71121 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -27,12 +27,18 @@ import org.lwjgl.opengl.DisplayMode; import org.lwjgl.opengl.GL11; import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; +import org.newdawn.slick.KeyListener; +import org.newdawn.slick.MouseListener; import org.newdawn.slick.opengl.InternalTextureLoader; import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.errorhandling.ErrorDumpable; +import yugecin.opsudance.core.inject.InstanceContainer; +import yugecin.opsudance.core.state.OpsuState; +import yugecin.opsudance.core.state.specialstates.FpsRenderState; +import yugecin.opsudance.core.state.transitions.*; import yugecin.opsudance.events.ResolutionChangedEvent; import yugecin.opsudance.utils.GLHelper; @@ -43,12 +49,22 @@ import static yugecin.opsudance.core.Entrypoint.sout; /** * based on org.newdawn.slick.AppGameContainer */ -public class DisplayContainer implements ErrorDumpable { +public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListener { private static SGL GL = Renderer.get(); public final EventBus eventBus; - public final Demux demux; + private final InstanceContainer instanceContainer; + + private FpsRenderState fpsState; + + private TransitionState outTransitionState; + private TransitionState inTransitionState; + + private final TransitionFinishedListener outTransitionListener; + private final TransitionFinishedListener inTransitionListener; + + private OpsuState state; private final DisplayMode nativeDisplayMode; @@ -71,9 +87,29 @@ public class DisplayContainer implements ErrorDumpable { private String glVersion; private String glVendor; - public DisplayContainer(Demux demux, EventBus eventBus) { - this.demux = demux; + public DisplayContainer(InstanceContainer instanceContainer, EventBus eventBus) { + this.instanceContainer = instanceContainer; this.eventBus = eventBus; + + outTransitionListener = new TransitionFinishedListener() { + @Override + public void onFinish() { + state.leave(); + outTransitionState.getApplicableState().leave(); + state = inTransitionState; + state.enter(); + inTransitionState.getApplicableState().enter(); + } + }; + + inTransitionListener = new TransitionFinishedListener() { + @Override + public void onFinish() { + state.leave(); + state = inTransitionState.getApplicableState(); + } + }; + this.nativeDisplayMode = Display.getDisplayMode(); targetRenderInterval = 16; // ~60 fps targetBackgroundRenderInterval = 41; // ~24 fps @@ -82,14 +118,22 @@ public class DisplayContainer implements ErrorDumpable { realRenderInterval = 1; } + public void init(Class startingState) { + state = instanceContainer.provide(startingState); + state.enter(); + + fpsState = instanceContainer.provide(FpsRenderState.class); + } + + public void run() throws LWJGLException { - while(!(Display.isCloseRequested() && demux.onCloseRequest())) { + while(!(Display.isCloseRequested() && state.onCloseRequest())) { delta = getDelta(); timeSinceLastRender += delta; input.poll(width, height); - demux.update(delta); + state.update(delta); int maxRenderInterval; if (Display.isVisible() && Display.isActive()) { @@ -108,8 +152,9 @@ public class DisplayContainer implements ErrorDumpable { graphics.resetTransform(); */ - demux.preRenderUpdate(timeSinceLastRender); - demux.render(graphics); + state.preRenderUpdate(timeSinceLastRender); + state.render(graphics); + fpsState.render(graphics); realRenderInterval = timeSinceLastRender; timeSinceLastRender = 0; @@ -184,8 +229,8 @@ public class DisplayContainer implements ErrorDumpable { graphics.setAntiAlias(false); input = new Input(height); - input.addKeyListener(demux); - input.addMouseListener(demux); + input.addKeyListener(this); + input.addMouseListener(this); GameImage.init(width, height); Fonts.init(); @@ -206,7 +251,91 @@ public class DisplayContainer implements ErrorDumpable { public void writeErrorDump(StringWriter dump) { dump.append("> DisplayContainer dump\n"); dump.append("OpenGL version: ").append(glVersion).append( "(").append(glVendor).append(")\n"); - demux.writeErrorDump(dump); + if (isTransitioning()) { + dump.append("doing a transition\n"); + dump.append("using out transition ").append(outTransitionState.getClass().getSimpleName()).append('\n'); + dump.append("using in transition ").append(inTransitionState.getClass().getSimpleName()).append('\n'); + if (state == inTransitionState) { + dump.append("currently doing the in transition\n"); + } else { + dump.append("currently doing the out transition\n"); + } + } + state.writeErrorDump(dump); } + public boolean isTransitioning() { + return state instanceof TransitionState; + } + + public void switchState(Class newState) { + switchState(newState, FadeOutTransitionState.class, 200, FadeInTransitionState.class, 300); + } + + public void switchStateNow(Class newState) { + switchState(newState, EmptyTransitionState.class, 0, EmptyTransitionState.class, 0); + } + + public void switchState(Class newState, Class outTransition, int outTime, Class inTransition, int inTime) { + if (isTransitioning()) { + return; + } + outTransitionState = instanceContainer.provide(outTransition).set(state, outTime, outTransitionListener); + inTransitionState = instanceContainer.provide(inTransition).set(instanceContainer.provide(newState), inTime, inTransitionListener); + state = outTransitionState; + state.enter(); + } + + /* + * input events below, see org.newdawn.slick.KeyListener & org.newdawn.slick.MouseListener + */ + + @Override + public void keyPressed(int key, char c) { + state.keyPressed(key, c); + } + + @Override + public void keyReleased(int key, char c) { + state.keyReleased(key, c); + } + + @Override + public void mouseWheelMoved(int change) { + state.mouseWheelMoved(change); + } + + @Override + public void mouseClicked(int button, int x, int y, int clickCount) { } + + @Override + public void mousePressed(int button, int x, int y) { + state.mousePressed(button, x, y); + } + + @Override + public void mouseReleased(int button, int x, int y) { + state.mouseReleased(button, x, y); + } + + @Override + public void mouseMoved(int oldx, int oldy, int newx, int newy) { } + + @Override + public void mouseDragged(int oldx, int oldy, int newx, int newy) { } + + @Override + public void setInput(Input input) { } + + @Override + public boolean isAcceptingInput() { + return true; + } + + @Override + public void inputEnded() { } + + @Override + public void inputStarted() { } + } diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index 2185472c..525e8fae 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -18,7 +18,6 @@ package yugecin.opsudance.core.inject; import yugecin.opsudance.PreStartupInitializer; -import yugecin.opsudance.core.Demux; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.state.specialstates.FpsRenderState; @@ -35,7 +34,6 @@ public class OpsuDanceInjector extends Injector { bind(EventBus.class).asEagerSingleton(); bind(PreStartupInitializer.class).asEagerSingleton(); - bind(Demux.class).asEagerSingleton(); bind(DisplayContainer.class).asEagerSingleton(); bind(ErrorHandler.class).asEagerSingleton(); diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index 18b92963..2747d09c 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -41,7 +41,7 @@ public class EmptyRedState implements OpsuState { if (counter < 0) { counter = 10000; // to prevent more calls to switch, as this will keep rendering until state transitioned System.out.println(System.currentTimeMillis() - start); - displayContainer.demux.switchState(EmptyState.class); + displayContainer.switchState(EmptyState.class); } } diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index b0f30507..67cef8ea 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -39,7 +39,7 @@ public class EmptyState implements OpsuState { counter -= delta; if (counter < 0) { counter = 10000; // to prevent more calls to switch, as this will keep rending until state transitioned - displayContainer.demux.switchState(EmptyRedState.class); + displayContainer.switchState(EmptyRedState.class); } } From 9da723c50bc585940b99513c2923df9f2840e5c9 Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 17 Jan 2017 11:25:36 +0100 Subject: [PATCH 42/92] simple bar notifications --- .../opsudance/core/DisplayContainer.java | 4 + .../core/inject/OpsuDanceInjector.java | 2 + .../specialstates/BarNotificationState.java | 86 +++++++++++++++++++ .../events/BarNotificationEvent.java | 28 ++++++ .../opsudance/states/EmptyRedState.java | 2 + 5 files changed, 122 insertions(+) create mode 100644 src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java create mode 100644 src/yugecin/opsudance/events/BarNotificationEvent.java diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 2cb71121..1e918fc3 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -37,6 +37,7 @@ import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.errorhandling.ErrorDumpable; import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.state.OpsuState; +import yugecin.opsudance.core.state.specialstates.BarNotificationState; import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.transitions.*; import yugecin.opsudance.events.ResolutionChangedEvent; @@ -57,6 +58,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen private final InstanceContainer instanceContainer; private FpsRenderState fpsState; + private BarNotificationState barNotifState; private TransitionState outTransitionState; private TransitionState inTransitionState; @@ -123,6 +125,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen state.enter(); fpsState = instanceContainer.provide(FpsRenderState.class); + barNotifState = instanceContainer.provide(BarNotificationState.class); } @@ -155,6 +158,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen state.preRenderUpdate(timeSinceLastRender); state.render(graphics); fpsState.render(graphics); + barNotifState.render(graphics, timeSinceLastRender); realRenderInterval = timeSinceLastRender; timeSinceLastRender = 0; diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index 525e8fae..9a772786 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -20,6 +20,7 @@ package yugecin.opsudance.core.inject; import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.core.state.specialstates.BarNotificationState; import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.transitions.EmptyTransitionState; import yugecin.opsudance.core.state.transitions.FadeInTransitionState; @@ -39,6 +40,7 @@ public class OpsuDanceInjector extends Injector { bind(ErrorHandler.class).asEagerSingleton(); bind(FpsRenderState.class).asEagerSingleton(); + bind(BarNotificationState.class).asEagerSingleton(); bind(EmptyTransitionState.class).asEagerSingleton(); bind(FadeInTransitionState.class).asEagerSingleton(); diff --git a/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java b/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java new file mode 100644 index 00000000..4350552a --- /dev/null +++ b/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java @@ -0,0 +1,86 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.state.specialstates; + +import itdelatrisu.opsu.ui.Fonts; +import org.newdawn.slick.Color; +import org.newdawn.slick.Graphics; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.core.events.EventListener; +import yugecin.opsudance.events.BarNotificationEvent; +import yugecin.opsudance.events.ResolutionChangedEvent; + +public class BarNotificationState implements EventListener { + + private final int NOTIFICATION_TIME = 5000; + + private final DisplayContainer displayContainer; + private final Color bgcol; + + private int timeShown; + + private String message; + private int textX; + private int textY; + private int barY; + private int barHeight; + + public BarNotificationState(DisplayContainer displayContainer, EventBus eventBus) { + this.displayContainer = displayContainer; + this.bgcol = new Color(0f, 0f, 0f, 0f); + this.timeShown = NOTIFICATION_TIME; + eventBus.subscribe(BarNotificationEvent.class, this); + eventBus.subscribe(ResolutionChangedEvent.class, new EventListener() { + @Override + public void onEvent(ResolutionChangedEvent event) { + if (timeShown >= NOTIFICATION_TIME) { + return; + } + calculatePosition(); + } + }); + } + + public void render(Graphics g, int delta) { + if (timeShown >= NOTIFICATION_TIME) { + return; + } + timeShown += delta; + g.setColor(bgcol); + g.fillRect(0, barY, displayContainer.width, barHeight); + Fonts.LARGE.drawString(textX, textY, message); + } + + private void calculatePosition() { + int textHeight = Fonts.LARGE.getHeight(message); + int textWidth = Fonts.LARGE.getWidth(message); + textX = (displayContainer.width - textWidth) / 2; + textY = (displayContainer.height - textHeight) / 2; + barY = textY - 5; // TODO uiscale stuff? + barHeight = textHeight + 10; + } + + @Override + public void onEvent(BarNotificationEvent event) { + this.message = event.message; + calculatePosition(); + timeShown = 0; + } + +} diff --git a/src/yugecin/opsudance/events/BarNotificationEvent.java b/src/yugecin/opsudance/events/BarNotificationEvent.java new file mode 100644 index 00000000..f3fc6182 --- /dev/null +++ b/src/yugecin/opsudance/events/BarNotificationEvent.java @@ -0,0 +1,28 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.events; + +public class BarNotificationEvent { + + public final String message; + + public BarNotificationEvent(String message) { + this.message = message; + } + +} diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index 2747d09c..dd132f32 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -21,6 +21,7 @@ import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.state.OpsuState; +import yugecin.opsudance.events.BarNotificationEvent; import java.io.StringWriter; @@ -93,6 +94,7 @@ public class EmptyRedState implements OpsuState { @Override public boolean mouseReleased(int button, int x, int y) { + displayContainer.eventBus.post(new BarNotificationEvent("this is a\nbar notification")); return false; } From 18d579eb628ee1a2a74e54cb80211f9b7109e3dc Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 17 Jan 2017 13:37:47 +0100 Subject: [PATCH 43/92] smallbold font and newlines when wrapping text (itdelatrisu/opsu@21aa72b) --- src/itdelatrisu/opsu/states/ButtonMenu.java | 2 +- src/itdelatrisu/opsu/ui/Fonts.java | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/itdelatrisu/opsu/states/ButtonMenu.java b/src/itdelatrisu/opsu/states/ButtonMenu.java index 969e3e3c..df9e7db1 100644 --- a/src/itdelatrisu/opsu/states/ButtonMenu.java +++ b/src/itdelatrisu/opsu/states/ButtonMenu.java @@ -446,7 +446,7 @@ public class ButtonMenu extends BasicGameState { for (int i = 0; i < title.length; i++) { // wrap text if too long if (Fonts.LARGE.getWidth(title[i]) > maxLineWidth) { - List list = Fonts.wrap(Fonts.LARGE, title[i], maxLineWidth); + List list = Fonts.wrap(Fonts.LARGE, title[i], maxLineWidth, false); actualTitle.addAll(list); } else actualTitle.add(title[i]); diff --git a/src/itdelatrisu/opsu/ui/Fonts.java b/src/itdelatrisu/opsu/ui/Fonts.java index 14ecbdb0..2b1cb6d6 100644 --- a/src/itdelatrisu/opsu/ui/Fonts.java +++ b/src/itdelatrisu/opsu/ui/Fonts.java @@ -40,7 +40,7 @@ import org.newdawn.slick.util.ResourceLoader; * Fonts used for drawing. */ public class Fonts { - public static UnicodeFont DEFAULT, BOLD, XLARGE, LARGE, MEDIUM, MEDIUMBOLD, SMALL; + public static UnicodeFont DEFAULT, BOLD, XLARGE, LARGE, MEDIUM, MEDIUMBOLD, SMALL, SMALLBOLD; /** Set of all Unicode strings already loaded per font. */ private static HashMap> loadedGlyphs = new HashMap>(); @@ -65,6 +65,7 @@ public class Fonts { MEDIUM = new UnicodeFont(font.deriveFont(fontBase * 3 / 2)); MEDIUMBOLD = new UnicodeFont(font.deriveFont(Font.BOLD, fontBase * 3 / 2)); SMALL = new UnicodeFont(font.deriveFont(fontBase)); + SMALLBOLD = new UnicodeFont(font.deriveFont(Font.BOLD, fontBase)); ColorEffect colorEffect = new ColorEffect(); loadFont(DEFAULT, colorEffect); loadFont(BOLD, colorEffect); @@ -73,6 +74,7 @@ public class Fonts { loadFont(MEDIUM, colorEffect); loadFont(MEDIUMBOLD, colorEffect); loadFont(SMALL, colorEffect); + loadFont(SMALLBOLD, colorEffect); } /** @@ -123,7 +125,7 @@ public class Fonts { * @return the list of split strings * @author davedes (http://slick.ninjacave.com/forum/viewtopic.php?t=3778) */ - public static List wrap(org.newdawn.slick.Font font, String text, int width) { + public static List wrap(org.newdawn.slick.Font font, String text, int width, boolean newlines) { List list = new ArrayList(); String str = text; String line = ""; @@ -134,7 +136,7 @@ public class Fonts { if (Character.isWhitespace(c)) lastSpace = i; String append = line + c; - if (font.getWidth(append) > width) { + if (font.getWidth(append) > width || (newlines && c == '\n')) { int split = (lastSpace != -1) ? lastSpace : i; int splitTrimmed = split; if (lastSpace != -1 && split < str.length() - 1) @@ -153,4 +155,5 @@ public class Fonts { list.add(str); return list; } + } From c6464ffb256f658e4f645fdbe5fea67a6b316cca Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 17 Jan 2017 16:11:59 +0100 Subject: [PATCH 44/92] bubble notifications (close #111) --- .../opsudance/core/DisplayContainer.java | 12 + .../core/inject/OpsuDanceInjector.java | 2 + .../BubbleNotificationState.java | 232 ++++++++++++++++++ .../events/BubbleNotificationEvent.java | 37 +++ .../opsudance/states/EmptyRedState.java | 4 +- 5 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java create mode 100644 src/yugecin/opsudance/events/BubbleNotificationEvent.java diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 1e918fc3..01246ed5 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -38,6 +38,7 @@ import yugecin.opsudance.core.errorhandling.ErrorDumpable; import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.state.OpsuState; import yugecin.opsudance.core.state.specialstates.BarNotificationState; +import yugecin.opsudance.core.state.specialstates.BubbleNotificationState; import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.transitions.*; import yugecin.opsudance.events.ResolutionChangedEvent; @@ -59,6 +60,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen private FpsRenderState fpsState; private BarNotificationState barNotifState; + private BubbleNotificationState bubNotifState; private TransitionState outTransitionState; private TransitionState inTransitionState; @@ -76,6 +78,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen public int width; public int height; + public int mouseX; + public int mouseY; + public int targetRenderInterval; public int targetBackgroundRenderInterval; @@ -126,6 +131,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen fpsState = instanceContainer.provide(FpsRenderState.class); barNotifState = instanceContainer.provide(BarNotificationState.class); + bubNotifState = instanceContainer.provide(BubbleNotificationState.class); } @@ -136,6 +142,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen timeSinceLastRender += delta; input.poll(width, height); + mouseX = input.getMouseX(); + mouseY = input.getMouseY(); state.update(delta); int maxRenderInterval; @@ -159,6 +167,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen state.render(graphics); fpsState.render(graphics); barNotifState.render(graphics, timeSinceLastRender); + bubNotifState.render(graphics, timeSinceLastRender); realRenderInterval = timeSinceLastRender; timeSinceLastRender = 0; @@ -319,6 +328,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen @Override public void mouseReleased(int button, int x, int y) { + if (bubNotifState.mouseReleased(x, y)) { + return; + } state.mouseReleased(button, x, y); } diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index 9a772786..0617463c 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -21,6 +21,7 @@ import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.state.specialstates.BarNotificationState; +import yugecin.opsudance.core.state.specialstates.BubbleNotificationState; import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.transitions.EmptyTransitionState; import yugecin.opsudance.core.state.transitions.FadeInTransitionState; @@ -41,6 +42,7 @@ public class OpsuDanceInjector extends Injector { bind(FpsRenderState.class).asEagerSingleton(); bind(BarNotificationState.class).asEagerSingleton(); + bind(BubbleNotificationState.class).asEagerSingleton(); bind(EmptyTransitionState.class).asEagerSingleton(); bind(FadeInTransitionState.class).asEagerSingleton(); diff --git a/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java b/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java new file mode 100644 index 00000000..e6c7f5f3 --- /dev/null +++ b/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java @@ -0,0 +1,232 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.state.specialstates; + +import itdelatrisu.opsu.ui.Fonts; +import itdelatrisu.opsu.ui.animations.AnimationEquation; +import org.newdawn.slick.Color; +import org.newdawn.slick.Graphics; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.core.events.EventListener; +import yugecin.opsudance.events.BubbleNotificationEvent; +import yugecin.opsudance.events.ResolutionChangedEvent; + +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +public class BubbleNotificationState implements EventListener { + + public static final int IN_TIME = 633; + public static final int DISPLAY_TIME = 7000 + IN_TIME; + public static final int OUT_TIME = 433; + public static final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME; + + private final DisplayContainer displayContainer; + private final LinkedList bubbles; + + private int addAnimationTime; + private int addAnimationHeight; + + public BubbleNotificationState(DisplayContainer displayContainer, EventBus eventBus) { + this.displayContainer = displayContainer; + this.bubbles = new LinkedList<>(); + this.addAnimationTime = IN_TIME; + eventBus.subscribe(BubbleNotificationEvent.class, this); + eventBus.subscribe(ResolutionChangedEvent.class, new EventListener() { + @Override + public void onEvent(ResolutionChangedEvent event) { + calculatePositions(); + } + }); + } + + public void render(Graphics g, int delta) { + ListIterator iter = bubbles.listIterator(); + if (!iter.hasNext()) { + return; + } + addAnimationTime += delta; + if (addAnimationTime > IN_TIME) { + finishAddAnimation(); + } + boolean animateUp = false; + do { + Notification next = iter.next(); + if (animateUp && addAnimationTime < IN_TIME) { + next.y = next.baseY - (int) (addAnimationHeight * AnimationEquation.OUT_QUINT.calc((float) addAnimationTime / IN_TIME)); + } + if (next.render(g, displayContainer.mouseX, displayContainer.mouseY, delta)) { + iter.remove(); + } + animateUp = true; + } while (iter.hasNext()); + } + + public boolean mouseReleased(int x, int y) { + if (x < displayContainer.width - Notification.width) { + return false; + } + for (Notification bubble : bubbles) { + if (bubble.mouseReleased(x, y)) { + return true; + } + } + return false; + } + + private void calculatePositions() { + Notification.width = (int) (displayContainer.width * 0.1703125f); + Notification.baseLine = (int) (displayContainer.height * 0.9645f); + Notification.paddingY = (int) (displayContainer.height * 0.0144f); + Notification.finalX = displayContainer.width - Notification.width - (int) (displayContainer.width * 0.01); + Notification.fontPaddingX = (int) (Notification.width * 0.02f); + Notification.fontPaddingY = (int) (Fonts.SMALLBOLD.getLineHeight() / 4f); + Notification.lineHeight = Fonts.SMALLBOLD.getLineHeight(); + if (bubbles.isEmpty()) { + return; + } + finishAddAnimation(); + } + + private void finishAddAnimation() { + if (bubbles.isEmpty()) { + addAnimationHeight = 0; + addAnimationTime = IN_TIME; + return; + } + ListIterator iter = bubbles.listIterator(); + iter.next(); + while (iter.hasNext()) { + Notification bubble = iter.next(); + bubble.y = bubble.baseY - addAnimationHeight; + bubble.baseY = bubble.y; + } + addAnimationHeight = 0; + addAnimationTime = IN_TIME; + } + + @Override + public void onEvent(BubbleNotificationEvent event) { + finishAddAnimation(); + Notification newBubble = new Notification(event.message, event.borderColor); + bubbles.add(0, newBubble); + addAnimationTime = 0; + addAnimationHeight = newBubble.height + Notification.paddingY; + ListIterator iter = bubbles.listIterator(); + iter.next(); + while (iter.hasNext()) { + Notification next = iter.next(); + next.baseY = next.y; + } + } + + private static class Notification { + + private final static int HOVER_ANIM_TIME = 150; + + private static int width; + private static int finalX; + private static int baseLine; + private static int fontPaddingX; + private static int fontPaddingY; + private static int lineHeight; + private static int paddingY; + + private final Color bgcol; + private final Color textColor; + private final Color borderColor; + private final Color targetBorderColor; + + private int timeShown; + private int x; + private int y; + private int baseY; + private int height; + private List lines; + private boolean isFading; + + private int hoverTime; + + private Notification(String message, Color borderColor) { + this.lines = Fonts.wrap(Fonts.SMALLBOLD, message, (int) (width * 0.96f), true); + this.height = (int) (Fonts.SMALLBOLD.getLineHeight() * (lines.size() + 0.5f)); + this.targetBorderColor = borderColor; + this.borderColor = new Color(borderColor); + this.textColor = new Color(Color.white); + this.bgcol = new Color(Color.black); + this.y = baseLine - height; + this.baseY = this.y; + } + + private boolean render(Graphics g, int mouseX, int mouseY, int delta) { + timeShown += delta; + processAnimations(isMouseHovered(mouseX, mouseY), delta); + g.setColor(bgcol); + g.fillRoundRect(x, y, width, height, 6); + g.setLineWidth(2f); + g.setColor(borderColor); + g.drawRoundRect(x, y, width, height, 6); + int y = this.y + fontPaddingY; + for (String line : lines) { + Fonts.SMALLBOLD.drawString(x + fontPaddingX, y, line, textColor); + y += lineHeight; + } + return timeShown > BubbleNotificationState.TOTAL_TIME; + } + + private void processAnimations(boolean mouseHovered, int delta) { + if (mouseHovered) { + hoverTime = Math.min(HOVER_ANIM_TIME, hoverTime + delta); + } else { + hoverTime = Math.max(0, hoverTime - delta); + } + float hoverProgress = (float) hoverTime / HOVER_ANIM_TIME; + borderColor.r = targetBorderColor.r + (0.977f - targetBorderColor.r) * hoverProgress; + borderColor.g = targetBorderColor.g + (0.977f - targetBorderColor.g) * hoverProgress; + borderColor.b = targetBorderColor.b + (0.977f - targetBorderColor.b) * hoverProgress; + if (timeShown < BubbleNotificationState.IN_TIME) { + float progress = (float) timeShown / BubbleNotificationState.IN_TIME; + this.x = finalX + (int) ((1 - AnimationEquation.OUT_BACK.calc(progress)) * width / 2); + textColor.a = borderColor.a = bgcol.a = progress; + return; + } + x = Notification.finalX; + if (timeShown > BubbleNotificationState.DISPLAY_TIME) { + isFading = true; + float progress = (float) (timeShown - BubbleNotificationState.DISPLAY_TIME) / BubbleNotificationState.OUT_TIME; + textColor.a = borderColor.a = bgcol.a = 1f - progress; + } + } + + private boolean mouseReleased(int x, int y) { + if (!isFading && isMouseHovered(x, y)) { + timeShown = BubbleNotificationState.DISPLAY_TIME; + return true; + } + return false; + } + + private boolean isMouseHovered(int x, int y) { + return this.x <= x && x < this.x + width && this.y <= y && y <= this.y + this.height; + } + + } + +} diff --git a/src/yugecin/opsudance/events/BubbleNotificationEvent.java b/src/yugecin/opsudance/events/BubbleNotificationEvent.java new file mode 100644 index 00000000..caf5b736 --- /dev/null +++ b/src/yugecin/opsudance/events/BubbleNotificationEvent.java @@ -0,0 +1,37 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.events; + +import org.newdawn.slick.Color; + +public class BubbleNotificationEvent { + + public static final Color COMMONCOLOR_RED = new Color(138, 72, 51); + public static final Color COMMONCOLOR_GREEN = new Color(98, 131, 59); + public static final Color COMMONCOLOR_WHITE = new Color(220, 220, 220); + public static final Color COMMONCOLOR_PURPLE = new Color(94, 46, 149); + + public final String message; + public final Color borderColor; + + public BubbleNotificationEvent(String message, Color borderColor) { + this.message = message; + this.borderColor = borderColor; + } + +} diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index dd132f32..c6bdd317 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -22,6 +22,7 @@ import org.newdawn.slick.Graphics; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.state.OpsuState; import yugecin.opsudance.events.BarNotificationEvent; +import yugecin.opsudance.events.BubbleNotificationEvent; import java.io.StringWriter; @@ -73,7 +74,7 @@ public class EmptyRedState implements OpsuState { @Override public boolean keyPressed(int key, char c) { - System.out.println("pressed"); + displayContainer.eventBus.post(new BubbleNotificationEvent("this is a bubble notification... bubbly bubbly bubbly linewraaaaaaaaaap", BubbleNotificationEvent.COMMONCOLOR_RED)); return false; } @@ -84,6 +85,7 @@ public class EmptyRedState implements OpsuState { @Override public boolean mouseWheelMoved(int delta) { + displayContainer.eventBus.post(new BubbleNotificationEvent("Life is like a box of chocolates. It's all going to melt by the end of the day.\n-Emily", BubbleNotificationEvent.COMMONCOLOR_PURPLE)); return false; } From 281d888905817be9e4acea51a106f79dd469762e Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 17 Jan 2017 16:15:09 +0100 Subject: [PATCH 45/92] fix bubble notif hitbox --- .../core/state/specialstates/BubbleNotificationState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java b/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java index e6c7f5f3..57f60584 100644 --- a/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java +++ b/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java @@ -80,7 +80,7 @@ public class BubbleNotificationState implements EventListener Date: Tue, 17 Jan 2017 16:22:06 +0100 Subject: [PATCH 46/92] add forgotten recalculations after changing resolution --- .../BubbleNotificationState.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java b/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java index 57f60584..4028dea1 100644 --- a/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java +++ b/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java @@ -103,6 +103,13 @@ public class BubbleNotificationState implements EventListener lines; - private boolean isFading; + private final String message; + private List lines; + + private boolean isFading; private int hoverTime; private Notification(String message, Color borderColor) { - this.lines = Fonts.wrap(Fonts.SMALLBOLD, message, (int) (width * 0.96f), true); - this.height = (int) (Fonts.SMALLBOLD.getLineHeight() * (lines.size() + 0.5f)); + this.message = message; + recalculateDimensions(); this.targetBorderColor = borderColor; this.borderColor = new Color(borderColor); this.textColor = new Color(Color.white); @@ -175,6 +184,11 @@ public class BubbleNotificationState implements EventListener Date: Tue, 17 Jan 2017 17:08:05 +0100 Subject: [PATCH 47/92] animation for the bar notifications --- .../specialstates/BarNotificationState.java | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java b/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java index 4350552a..11ad5f66 100644 --- a/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java +++ b/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java @@ -18,6 +18,7 @@ package yugecin.opsudance.core.state.specialstates; import itdelatrisu.opsu.ui.Fonts; +import itdelatrisu.opsu.ui.animations.AnimationEquation; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import yugecin.opsudance.core.DisplayContainer; @@ -26,30 +27,38 @@ import yugecin.opsudance.core.events.EventListener; import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.ResolutionChangedEvent; +import java.util.List; + public class BarNotificationState implements EventListener { - private final int NOTIFICATION_TIME = 5000; + private final int IN_TIME = 200; + private final int DISPLAY_TIME = 5700 + IN_TIME; + private final int OUT_TIME = 200; + private final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME; private final DisplayContainer displayContainer; private final Color bgcol; + private final Color textCol; private int timeShown; private String message; - private int textX; + private List lines; private int textY; - private int barY; - private int barHeight; + + private int barHalfTargetHeight; + private int barHalfHeight; public BarNotificationState(DisplayContainer displayContainer, EventBus eventBus) { this.displayContainer = displayContainer; - this.bgcol = new Color(0f, 0f, 0f, 0f); - this.timeShown = NOTIFICATION_TIME; + this.bgcol = new Color(Color.black); + this.textCol = new Color(Color.white); + this.timeShown = TOTAL_TIME; eventBus.subscribe(BarNotificationEvent.class, this); eventBus.subscribe(ResolutionChangedEvent.class, new EventListener() { @Override public void onEvent(ResolutionChangedEvent event) { - if (timeShown >= NOTIFICATION_TIME) { + if (timeShown >= TOTAL_TIME) { return; } calculatePosition(); @@ -58,22 +67,44 @@ public class BarNotificationState implements EventListener } public void render(Graphics g, int delta) { - if (timeShown >= NOTIFICATION_TIME) { + if (timeShown >= TOTAL_TIME) { return; } timeShown += delta; + processAnimations(); g.setColor(bgcol); - g.fillRect(0, barY, displayContainer.width, barHeight); - Fonts.LARGE.drawString(textX, textY, message); + g.fillRect(0, displayContainer.height / 2 - barHalfHeight, displayContainer.width, barHalfHeight * 2); + int y = textY; + for (String line : lines) { + Fonts.LARGE.drawString((displayContainer.width - Fonts.LARGE.getWidth(line)) / 2, y, line, textCol); + y += Fonts.LARGE.getLineHeight(); + } + } + + private void processAnimations() { + if (timeShown < IN_TIME) { + float progress = (float) timeShown / IN_TIME; + barHalfHeight = (int) (barHalfTargetHeight * AnimationEquation.OUT_BACK.calc(progress)); + textCol.a = progress; + bgcol.a = 0.4f * progress; + return; + } + if (timeShown > DISPLAY_TIME) { + float progress = 1f - (float) (timeShown - DISPLAY_TIME) / OUT_TIME; + textCol.a = progress; + bgcol.a = 0.4f * progress; + return; + } + barHalfHeight = barHalfTargetHeight; + textCol.a = 1f; + bgcol.a = 0.4f; } private void calculatePosition() { - int textHeight = Fonts.LARGE.getHeight(message); - int textWidth = Fonts.LARGE.getWidth(message); - textX = (displayContainer.width - textWidth) / 2; - textY = (displayContainer.height - textHeight) / 2; - barY = textY - 5; // TODO uiscale stuff? - barHeight = textHeight + 10; + this.lines = Fonts.wrap(Fonts.LARGE, message, (int) (displayContainer.width * 0.96f), true); + int textHeight = (int) (Fonts.LARGE.getLineHeight() * (lines.size() + 0.5f)); + textY = (displayContainer.height - textHeight) / 2 + Fonts.LARGE.getLineHeight() / 8; + barHalfTargetHeight = textHeight / 2 + Fonts.LARGE.getLineHeight() / 8; } @Override From f0883ff1afc1e4b9b97ce81614c9374a5c519f09 Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 17 Jan 2017 17:13:25 +0100 Subject: [PATCH 48/92] draw the bar notification above the bubble notifications --- src/yugecin/opsudance/core/DisplayContainer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 01246ed5..4a91012c 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -130,8 +130,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen state.enter(); fpsState = instanceContainer.provide(FpsRenderState.class); - barNotifState = instanceContainer.provide(BarNotificationState.class); bubNotifState = instanceContainer.provide(BubbleNotificationState.class); + barNotifState = instanceContainer.provide(BarNotificationState.class); } @@ -166,8 +166,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen state.preRenderUpdate(timeSinceLastRender); state.render(graphics); fpsState.render(graphics); - barNotifState.render(graphics, timeSinceLastRender); bubNotifState.render(graphics, timeSinceLastRender); + barNotifState.render(graphics, timeSinceLastRender); realRenderInterval = timeSinceLastRender; timeSinceLastRender = 0; From 580b4142f6b3f8d0fb68659d271a131f76b876fd Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 17 Jan 2017 18:45:17 +0100 Subject: [PATCH 49/92] don't pass deltas as param --- src/yugecin/opsudance/core/DisplayContainer.java | 15 ++++++++------- src/yugecin/opsudance/core/state/OpsuState.java | 4 ++-- .../state/specialstates/BarNotificationState.java | 4 ++-- .../specialstates/BubbleNotificationState.java | 6 +++--- .../core/state/specialstates/FpsRenderState.java | 2 +- .../core/state/transitions/TransitionState.java | 9 +++++---- src/yugecin/opsudance/states/EmptyRedState.java | 6 +++--- src/yugecin/opsudance/states/EmptyState.java | 6 +++--- 8 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 4a91012c..84c38ba9 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -84,7 +84,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen public int targetRenderInterval; public int targetBackgroundRenderInterval; - public int realRenderInterval; + public int renderDelta; public int delta; public int timeSinceLastRender; @@ -122,7 +122,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen targetBackgroundRenderInterval = 41; // ~24 fps lastFrame = getTime(); delta = 1; - realRenderInterval = 1; + renderDelta = 1; } public void init(Class startingState) { @@ -144,7 +144,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen input.poll(width, height); mouseX = input.getMouseX(); mouseY = input.getMouseY(); - state.update(delta); + state.update(); int maxRenderInterval; if (Display.isVisible() && Display.isActive()) { @@ -163,13 +163,14 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen graphics.resetTransform(); */ - state.preRenderUpdate(timeSinceLastRender); + renderDelta = timeSinceLastRender; + + state.preRenderUpdate(); state.render(graphics); fpsState.render(graphics); - bubNotifState.render(graphics, timeSinceLastRender); - barNotifState.render(graphics, timeSinceLastRender); + bubNotifState.render(graphics); + barNotifState.render(graphics); - realRenderInterval = timeSinceLastRender; timeSinceLastRender = 0; Display.update(false); diff --git a/src/yugecin/opsudance/core/state/OpsuState.java b/src/yugecin/opsudance/core/state/OpsuState.java index 059d8ba2..37902c0b 100644 --- a/src/yugecin/opsudance/core/state/OpsuState.java +++ b/src/yugecin/opsudance/core/state/OpsuState.java @@ -22,8 +22,8 @@ import yugecin.opsudance.core.errorhandling.ErrorDumpable; public interface OpsuState extends ErrorDumpable { - void update(int delta); - void preRenderUpdate(int delta); + void update(); + void preRenderUpdate(); void render(Graphics g); void enter(); void leave(); diff --git a/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java b/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java index 11ad5f66..c2a35a36 100644 --- a/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java +++ b/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java @@ -66,11 +66,11 @@ public class BarNotificationState implements EventListener }); } - public void render(Graphics g, int delta) { + public void render(Graphics g) { if (timeShown >= TOTAL_TIME) { return; } - timeShown += delta; + timeShown += displayContainer.renderDelta; processAnimations(); g.setColor(bgcol); g.fillRect(0, displayContainer.height / 2 - barHalfHeight, displayContainer.width, barHalfHeight * 2); diff --git a/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java b/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java index 4028dea1..9e1b5e57 100644 --- a/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java +++ b/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java @@ -57,12 +57,12 @@ public class BubbleNotificationState implements EventListener iter = bubbles.listIterator(); if (!iter.hasNext()) { return; } - addAnimationTime += delta; + addAnimationTime += displayContainer.renderDelta; if (addAnimationTime > IN_TIME) { finishAddAnimation(); } @@ -72,7 +72,7 @@ public class BubbleNotificationState implements EventListener { public void render(Graphics g) { int x = this.x; - x = drawText(g, (1000 / displayContainer.realRenderInterval) + " fps", x, this.y); + x = drawText(g, (1000 / displayContainer.renderDelta) + " fps", x, this.y); drawText(g, (1000 / displayContainer.delta) + " ups", x, this.y); } diff --git a/src/yugecin/opsudance/core/state/transitions/TransitionState.java b/src/yugecin/opsudance/core/state/transitions/TransitionState.java index 11bcad13..6ae186d7 100644 --- a/src/yugecin/opsudance/core/state/transitions/TransitionState.java +++ b/src/yugecin/opsudance/core/state/transitions/TransitionState.java @@ -49,16 +49,17 @@ public abstract class TransitionState extends BaseOpsuState { } @Override - public void update(int delta) { - applicableState.update(delta); - transitionTime += delta; + public void update() { + applicableState.update(); + transitionTime += displayContainer.delta; if (transitionTime >= transitionTargetTime) { finish(); } } @Override - public void preRenderUpdate(int delta) { + public void preRenderUpdate() { + applicableState.preRenderUpdate(); } @Override diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index c6bdd317..fcf52029 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -38,8 +38,8 @@ public class EmptyRedState implements OpsuState { } @Override - public void update(int delta) { - counter -= delta; + public void update() { + counter -= displayContainer.delta; if (counter < 0) { counter = 10000; // to prevent more calls to switch, as this will keep rendering until state transitioned System.out.println(System.currentTimeMillis() - start); @@ -48,7 +48,7 @@ public class EmptyRedState implements OpsuState { } @Override - public void preRenderUpdate(int delta) { + public void preRenderUpdate() { } @Override diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index 67cef8ea..28703a57 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -35,8 +35,8 @@ public class EmptyState implements OpsuState { } @Override - public void update(int delta) { - counter -= delta; + public void update() { + counter -= displayContainer.delta; if (counter < 0) { counter = 10000; // to prevent more calls to switch, as this will keep rending until state transitioned displayContainer.switchState(EmptyRedState.class); @@ -44,7 +44,7 @@ public class EmptyState implements OpsuState { } @Override - public void preRenderUpdate(int delta) { + public void preRenderUpdate() { } @Override From 14c8fba9cb12796d49a843088d3402c45815c9bb Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 17 Jan 2017 21:06:59 +0100 Subject: [PATCH 50/92] colorful fps counters --- .../opsudance/core/DisplayContainer.java | 18 +++++++++++-- .../state/specialstates/FpsRenderState.java | 25 +++++++++++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 84c38ba9..c32d3243 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -81,6 +81,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen public int mouseX; public int mouseY; + private int targetUpdatesPerSecond; + public int targetUpdateInterval; + private int targetRendersPerSecond; public int targetRenderInterval; public int targetBackgroundRenderInterval; @@ -118,13 +121,24 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen }; this.nativeDisplayMode = Display.getDisplayMode(); - targetRenderInterval = 16; // ~60 fps + setUPS(1000); + setFPS(60); targetBackgroundRenderInterval = 41; // ~24 fps lastFrame = getTime(); delta = 1; renderDelta = 1; } + public void setUPS(int ups) { + targetUpdatesPerSecond = ups; + targetUpdateInterval = 1000 / targetUpdatesPerSecond; + } + + public void setFPS(int fps) { + targetRendersPerSecond = fps; + targetRenderInterval = 1000 / targetRendersPerSecond; + } + public void init(Class startingState) { state = instanceContainer.provide(startingState); state.enter(); @@ -177,7 +191,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen } Display.processMessages(); - Display.sync(1000); // TODO add option to change this, to not eat CPUs + Display.sync(targetUpdatesPerSecond); } teardown(); } diff --git a/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java b/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java index 302cd9ab..4e940351 100644 --- a/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java +++ b/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java @@ -28,6 +28,10 @@ public class FpsRenderState implements EventListener { private final DisplayContainer displayContainer; + private final static Color GREEN = new Color(171, 218, 25); + private final static Color ORANGE = new Color(255, 204, 34); + private final static Color DARKORANGE = new Color(255, 149, 24); + private int x; private int y; private int singleHeight; @@ -39,15 +43,26 @@ public class FpsRenderState implements EventListener { public void render(Graphics g) { int x = this.x; - x = drawText(g, (1000 / displayContainer.renderDelta) + " fps", x, this.y); - drawText(g, (1000 / displayContainer.delta) + " ups", x, this.y); + int target = displayContainer.targetRenderInterval - (displayContainer.targetUpdateInterval % displayContainer.targetRenderInterval); + x = drawText(g, getColor(target, displayContainer.renderDelta), (1000 / displayContainer.renderDelta) + " fps", x, this.y); + drawText(g, getColor(displayContainer.targetUpdateInterval, displayContainer.delta), (1000 / displayContainer.delta) + " ups", x, this.y); } - private int drawText(Graphics g, String text, int x, int y) { + private Color getColor(int targetValue, int realValue) { + if (realValue >= targetValue) { + return GREEN; + } + if (realValue >= targetValue * 0.9f) { + return ORANGE; + } + return DARKORANGE; + } + + private int drawText(Graphics g, Color color, String text, int x, int y) { int width = Fonts.SMALL.getWidth(text) + 10; - g.setColor(new Color(0, 0x80, 0)); + g.setColor(color); g.fillRoundRect(x - width, y, width, singleHeight + 6, 2); - Fonts.SMALL.drawString(x - width + 3, y + 3, text, Color.white); + Fonts.SMALL.drawString(x - width + 3, y + 3, text, Color.black); return x - width - 6; } From b270015c14aea1b428a9c7d79e01e880a160adbf Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 17 Jan 2017 22:02:40 +0100 Subject: [PATCH 51/92] javadoc for non-obvious things --- src/yugecin/opsudance/core/DisplayContainer.java | 2 +- .../core/state/specialstates/BubbleNotificationState.java | 3 +++ .../opsudance/core/state/specialstates/FpsRenderState.java | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index c32d3243..f1480557 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -227,7 +227,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen } if (displayMode == null) { - displayMode = new DisplayMode(width,height); + displayMode = new DisplayMode(width, height); if (fullscreen) { fullscreen = false; Log.warn("could not find fullscreen displaymode for " + width + "x" + height); diff --git a/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java b/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java index 9e1b5e57..5327ccad 100644 --- a/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java +++ b/src/yugecin/opsudance/core/state/specialstates/BubbleNotificationState.java @@ -189,6 +189,9 @@ public class BubbleNotificationState implements EventListener { return DARKORANGE; } + /** + * @return x position where the next block can be drawn (right aligned) + */ private int drawText(Graphics g, Color color, String text, int x, int y) { int width = Fonts.SMALL.getWidth(text) + 10; g.setColor(color); From 5e09a1d24a2ccf59d1c331b8e448a1214afa0531 Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 17 Jan 2017 22:26:19 +0100 Subject: [PATCH 52/92] attempt to correct the fps counter target values --- .../opsudance/core/state/specialstates/FpsRenderState.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java b/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java index 89055d22..d8a52f37 100644 --- a/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java +++ b/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java @@ -43,16 +43,16 @@ public class FpsRenderState implements EventListener { public void render(Graphics g) { int x = this.x; - int target = displayContainer.targetRenderInterval - (displayContainer.targetUpdateInterval % displayContainer.targetRenderInterval); + int target = displayContainer.targetRenderInterval + (displayContainer.targetUpdateInterval % displayContainer.targetRenderInterval); x = drawText(g, getColor(target, displayContainer.renderDelta), (1000 / displayContainer.renderDelta) + " fps", x, this.y); drawText(g, getColor(displayContainer.targetUpdateInterval, displayContainer.delta), (1000 / displayContainer.delta) + " ups", x, this.y); } private Color getColor(int targetValue, int realValue) { - if (realValue >= targetValue) { + if (realValue <= targetValue) { return GREEN; } - if (realValue >= targetValue * 0.9f) { + if (realValue <= targetValue * 1.15f) { return ORANGE; } return DARKORANGE; From 8b226f3afc463f68864531476065d4e50bb2f194 Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 17 Jan 2017 23:18:12 +0100 Subject: [PATCH 53/92] working splash state --- src/itdelatrisu/opsu/Opsu.java | 4 +- src/itdelatrisu/opsu/Options.java | 19 ++--- src/itdelatrisu/opsu/Utils.java | 45 ++++-------- .../opsu/audio/MusicController.java | 9 +++ src/itdelatrisu/opsu/states/Splash.java | 51 ++++++------- src/itdelatrisu/opsu/ui/Cursor.java | 21 +++--- src/itdelatrisu/opsu/ui/UI.java | 71 ++++++------------- src/yugecin/opsudance/OpsuDance.java | 4 +- .../opsudance/core/DisplayContainer.java | 7 +- .../core/inject/OpsuDanceInjector.java | 3 + .../opsudance/core/state/BaseOpsuState.java | 13 ++++ 11 files changed, 114 insertions(+), 133 deletions(-) diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index 485210a9..29f7c4ee 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -85,7 +85,7 @@ public class Opsu extends StateBasedGame { @Override public void initStatesList(GameContainer container) throws SlickException { - addState(new Splash(STATE_SPLASH)); + //addState(new Splash(STATE_SPLASH)); addState(new MainMenu(STATE_MAINMENU)); addState(new ButtonMenu(STATE_BUTTONMENU)); addState(new SongMenu(STATE_SONGMENU)); @@ -206,7 +206,7 @@ public class Opsu extends StateBasedGame { Container app = new Container(opsu); // basic game settings - Options.setDisplayMode(app); + //Options.setDisplayMode(app); String[] icons = { "icon16.png", "icon32.png" }; try { app.setIcons(icons); diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index 19db44b0..dcf2da73 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -59,6 +59,8 @@ import com.sun.jna.platform.win32.Advapi32Util; import com.sun.jna.platform.win32.Win32Exception; import com.sun.jna.platform.win32.WinReg; import yugecin.opsudance.*; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.movers.factories.ExgonMoverFactory; import yugecin.opsudance.movers.factories.QuadraticBezierMoverFactory; import yugecin.opsudance.movers.slidermovers.DefaultSliderMoverController; @@ -1294,10 +1296,9 @@ public class Options { /** * Sets the master volume level (if within valid range). - * @param container the game container * @param volume the volume [0, 1] */ - public static void setMasterVolume(GameContainer container, float volume) { + public static void setMasterVolume(float volume) { if (volume >= 0f && volume <= 1f) { GameOption.MASTER_VOLUME.setValue((int) (volume * 100f)); MusicController.setVolume(getMasterVolume() * getMusicVolume()); @@ -1346,11 +1347,10 @@ public class Options { *

* If the configured resolution is larger than the screen size, the smallest * available resolution will be used. - * @param app the game container */ - public static void setDisplayMode(Container app) { - int screenWidth = app.getScreenWidth(); - int screenHeight = app.getScreenHeight(); + public static void setDisplayMode(DisplayContainer container) { + int screenWidth = container.nativeDisplayMode.getWidth(); + int screenHeight = container.nativeDisplayMode.getHeight(); resolutions[0] = screenWidth + "x" + screenHeight; if (resolutionIdx < 0 || resolutionIdx > resolutions.length) { @@ -1370,9 +1370,10 @@ public class Options { } try { - app.setDisplayMode(width, height, isFullscreen()); - } catch (SlickException e) { - ErrorHandler.error("Failed to set display mode.", e, true); + container.setDisplayMode(width, height, isFullscreen()); + } catch (Exception e) { + container.eventBus.post(new BubbleNotificationEvent("Failed to change resolution", BubbleNotificationEvent.COMMONCOLOR_RED)); + Log.error("Failed to set display mode.", e); } if (!isFullscreen()) { diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index 930b4eac..a66e320d 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -18,6 +18,7 @@ package itdelatrisu.opsu; +import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.beatmap.HitObject; @@ -71,6 +72,7 @@ import org.newdawn.slick.state.StateBasedGame; import org.newdawn.slick.util.Log; import com.sun.jna.platform.FileUtils; +import yugecin.opsudance.core.DisplayContainer; /** * Contains miscellaneous utilities. @@ -89,40 +91,18 @@ public class Utils { Arrays.sort(illegalChars); } - // game-related variables - private static Input input; - // This class should not be instantiated. private Utils() {} /** * Initializes game settings and class data. - * @param container the game container - * @param game the game object */ - public static void init(GameContainer container, StateBasedGame game) { - input = container.getInput(); - int width = container.getWidth(); - int height = container.getHeight(); + public static void init(DisplayContainer displayContainer) { + // TODO clean this up // game settings - container.setTargetFrameRate(Options.getTargetFPS()); - container.setVSync(Options.getTargetFPS() == 60); - container.setMusicVolume(Options.getMusicVolume() * Options.getMasterVolume()); - container.setShowFPS(false); - container.getInput().enableKeyRepeat(); - container.setAlwaysRender(true); - container.setUpdateOnlyWhenVisible(false); - - // calculate UI scale - GameImage.init(width, height); - - // create fonts - try { - Fonts.init(); - } catch (Exception e) { - ErrorHandler.error("Failed to load fonts.", e, true); - } + displayContainer.setFPS(Options.getTargetFPS()); // TODO move this elsewhere + MusicController.setMusicVolume(Options.getMusicVolume() * Options.getMasterVolume()); // load skin Options.loadSkin(); @@ -134,19 +114,19 @@ public class Utils { } // initialize game mods - GameMod.init(width, height); + GameMod.init(displayContainer.width, displayContainer.height); // initialize playback buttons - PlaybackSpeed.init(width, height); + PlaybackSpeed.init(displayContainer.width, displayContainer.height); // initialize hit objects - HitObject.init(width, height); + HitObject.init(displayContainer.width, displayContainer.height); // initialize download nodes - DownloadNode.init(width, height); + DownloadNode.init(displayContainer.width, displayContainer.height); // initialize UI components - UI.init(container, game); + UI.init(displayContainer); } /** @@ -246,12 +226,15 @@ public class Utils { * @return true if pressed */ public static boolean isGameKeyPressed() { + /* boolean mouseDown = !Options.isMouseDisabled() && ( input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)); return (mouseDown || input.isKeyDown(Options.getGameKeyLeft()) || input.isKeyDown(Options.getGameKeyRight())); + */ + return true; } /** diff --git a/src/itdelatrisu/opsu/audio/MusicController.java b/src/itdelatrisu/opsu/audio/MusicController.java index c5e24e7b..ba4ec00a 100644 --- a/src/itdelatrisu/opsu/audio/MusicController.java +++ b/src/itdelatrisu/opsu/audio/MusicController.java @@ -578,4 +578,13 @@ public class MusicController { ErrorHandler.error("Failed to destroy OpenAL.", e, true); } } + + /** + * Set the default volume for music + * @param volume the new default value for music volume + */ + public static void setMusicVolume(float volume) { + SoundStore.get().setMusicVolume(volume); + } + } diff --git a/src/itdelatrisu/opsu/states/Splash.java b/src/itdelatrisu/opsu/states/Splash.java index a0ea9963..03154422 100644 --- a/src/itdelatrisu/opsu/states/Splash.java +++ b/src/itdelatrisu/opsu/states/Splash.java @@ -19,7 +19,6 @@ package itdelatrisu.opsu.states; import itdelatrisu.opsu.GameImage; -import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.audio.MusicController; @@ -36,19 +35,18 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation; import java.io.File; import org.newdawn.slick.Color; -import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; -import org.newdawn.slick.SlickException; -import org.newdawn.slick.state.BasicGameState; -import org.newdawn.slick.state.StateBasedGame; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.state.BaseOpsuState; /** * "Splash Screen" state. *

* Loads game resources and enters "Main Menu" state. */ -public class Splash extends BasicGameState { +public class Splash extends BaseOpsuState { + /** Minimum time, in milliseconds, to display the splash screen (and fade in the logo). */ private static final int MIN_SPLASH_TIME = 400; @@ -71,18 +69,15 @@ public class Splash extends BasicGameState { private AnimatedValue logoAlpha; // game-related variables - private final int state; - private GameContainer container; private boolean init = false; - public Splash(int state) { - this.state = state; + public Splash(DisplayContainer displayContainer) { + super(displayContainer); } @Override - public void init(GameContainer container, StateBasedGame game) - throws SlickException { - this.container = container; + protected void revalidate() { + super.revalidate(); // check if skin changed if (Options.getSkin() != null) @@ -92,7 +87,7 @@ public class Splash extends BasicGameState { this.watchServiceChange = Options.isWatchServiceEnabled() && BeatmapWatchService.get() == null; // load Utils class first (needed in other 'init' methods) - Utils.init(container, game); + Utils.init(displayContainer); // fade in logo this.logoAlpha = new AnimatedValue(MIN_SPLASH_TIME, 0f, 1f, AnimationEquation.LINEAR); @@ -100,16 +95,14 @@ public class Splash extends BasicGameState { } @Override - public void render(GameContainer container, StateBasedGame game, Graphics g) - throws SlickException { + public void render(Graphics g) { g.setBackground(Color.black); - GameImage.MENU_LOGO.getImage().drawCentered(container.getWidth() / 2, container.getHeight() / 2); + GameImage.MENU_LOGO.getImage().drawCentered(displayContainer.width / 2, displayContainer.height / 2); UI.drawLoadingProgress(g); } @Override - public void update(GameContainer container, StateBasedGame game, int delta) - throws SlickException { + public void update() { if (!init) { init = true; @@ -165,7 +158,7 @@ public class Splash extends BasicGameState { } // fade in logo - if (logoAlpha.update(delta)) + if (logoAlpha.update(displayContainer.delta)) GameImage.MENU_LOGO.getImage().setAlpha(logoAlpha.getValue()); // change states when loading complete @@ -176,30 +169,32 @@ public class Splash extends BasicGameState { if (Options.isThemeSongEnabled()) MusicController.playThemeSong(); else - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); + //((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); + System.out.println(("todo")); + // TODO } // play the theme song else MusicController.playThemeSong(); - game.enterState(Opsu.STATE_MAINMENU); + //game.enterState(Opsu.STATE_MAINMENU); + } } @Override - public int getID() { return state; } - - @Override - public void keyPressed(int key, char c) { + public boolean keyPressed(int key, char c) { if (key == Input.KEY_ESCAPE) { // close program - if (++escapeCount >= 3) - container.exit(); + if (++escapeCount >= 3) System.out.println("hi"); + //container.exit(); // TODO // stop parsing beatmaps by sending interrupt to BeatmapParser else if (thread != null) thread.interrupt(); + return true; } + return false; } } diff --git a/src/itdelatrisu/opsu/ui/Cursor.java b/src/itdelatrisu/opsu/ui/Cursor.java index 7ad8171d..c3025ab7 100644 --- a/src/itdelatrisu/opsu/ui/Cursor.java +++ b/src/itdelatrisu/opsu/ui/Cursor.java @@ -36,6 +36,7 @@ import org.lwjgl.LWJGLException; import org.newdawn.slick.*; import org.newdawn.slick.state.StateBasedGame; import yugecin.opsudance.Dancer; +import yugecin.opsudance.core.DisplayContainer; /** * Updates and draws the cursor. @@ -68,9 +69,7 @@ public class Cursor { private boolean newStyle; // game-related variables - private static GameContainer container; - private static StateBasedGame game; - private static Input input; + private static DisplayContainer displayContainer; public static Color lastObjColor = Color.white; public static Color lastMirroredObjColor = Color.white; @@ -82,13 +81,9 @@ public class Cursor { /** * Initializes the class. - * @param container the game container - * @param game the game object */ - public static void init(GameContainer container, StateBasedGame game) { - Cursor.container = container; - Cursor.game = game; - Cursor.input = container.getInput(); + public static void init(DisplayContainer displayContainer) { + Cursor.displayContainer = displayContainer; // create empty cursor to simulate hiding the cursor try { @@ -116,12 +111,14 @@ public class Cursor { * Draws the cursor. */ public void draw() { + /* int state = game.getCurrentStateID(); boolean mousePressed = (((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && Utils.isGameKeyPressed()) || ((input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) && !(state == Opsu.STATE_GAME && Options.isMouseDisabled()))); draw(input.getMouseX(), input.getMouseY(), mousePressed); + */ } /** @@ -215,7 +212,7 @@ public class Cursor { public void setCursorPosition(int mouseX, int mouseY) { // TODO: use an image buffer int removeCount = 0; - float FPSmod = Math.max(container.getFPS(), 1) / 30f; + float FPSmod = Math.max(1000 / displayContainer.renderDelta, 1) / 30f; // TODO if (newStyle) { // new style: add all points between cursor movements if ((lastPosition.x == 0 && lastPosition.y == 0) || !addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY)) { @@ -349,11 +346,13 @@ public class Cursor { */ public void hide() { if (emptyCursor != null) { + /* try { container.setMouseCursor(emptyCursor, 0, 0); } catch (SlickException e) { ErrorHandler.error("Failed to hide the cursor.", e, true); } + */ } } @@ -361,6 +360,6 @@ public class Cursor { * Unhides the cursor. */ public void show() { - container.setDefaultMouseCursor(); + //container.setDefaultMouseCursor(); } } diff --git a/src/itdelatrisu/opsu/ui/UI.java b/src/itdelatrisu/opsu/ui/UI.java index b58b7002..6fe0e50b 100644 --- a/src/itdelatrisu/opsu/ui/UI.java +++ b/src/itdelatrisu/opsu/ui/UI.java @@ -39,6 +39,7 @@ import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; import org.newdawn.slick.state.StateBasedGame; +import yugecin.opsudance.core.DisplayContainer; /** * Draws common UI components. @@ -75,7 +76,7 @@ public class UI { private static AnimatedValue tooltipAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR); // game-related variables - private static GameContainer container; + private static DisplayContainer displayContainer; private static Input input; // This class should not be instantiated. @@ -83,24 +84,21 @@ public class UI { /** * Initializes UI data. - * @param container the game container - * @param game the game object */ - public static void init(GameContainer container, StateBasedGame game) { - UI.container = container; - UI.input = container.getInput(); + public static void init(DisplayContainer displayContainer) { + UI.displayContainer = displayContainer; // initialize cursor - Cursor.init(container, game); + Cursor.init(displayContainer); cursor.hide(); // back button if (GameImage.MENU_BACK.getImages() != null) { Animation back = GameImage.MENU_BACK.getAnimation(120); - backButton = new MenuButton(back, back.getWidth() / 2f, container.getHeight() - (back.getHeight() / 2f)); + backButton = new MenuButton(back, back.getWidth() / 2f, displayContainer.height - (back.getHeight() / 2f)); } else { Image back = GameImage.MENU_BACK.getImage(); - backButton = new MenuButton(back, back.getWidth() / 2f, container.getHeight() - (back.getHeight() / 2f)); + backButton = new MenuButton(back, back.getWidth() / 2f, displayContainer.height - (back.getHeight() / 2f)); } backButton.setHoverAnimationDuration(350); backButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK); @@ -125,7 +123,6 @@ public class UI { public static void draw(Graphics g) { drawBarNotification(g); drawVolume(g); - drawFPS(); cursor.draw(); drawTooltip(g); } @@ -140,7 +137,6 @@ public class UI { public static void draw(Graphics g, int mouseX, int mouseY, boolean mousePressed) { drawBarNotification(g); drawVolume(g); - drawFPS(); cursor.draw(mouseX, mouseY, mousePressed); drawTooltip(g); } @@ -189,27 +185,6 @@ public class UI { Fonts.MEDIUM.drawString(tabTextX, tabTextY, text, textColor); } - /** - * Draws the FPS at the bottom-right corner of the game container. - * If the option is not activated, this will do nothing. - */ - public static void drawFPS() { - if (!Options.isFPSCounterEnabled()) - return; - - String fps = String.format("%dFPS", container.getFPS()); - Fonts.BOLD.drawString( - container.getWidth() * 0.997f - Fonts.BOLD.getWidth(fps), - container.getHeight() * 0.997f - Fonts.BOLD.getHeight(fps), - Integer.toString(container.getFPS()), Color.white - ); - Fonts.DEFAULT.drawString( - container.getWidth() * 0.997f - Fonts.BOLD.getWidth("FPS"), - container.getHeight() * 0.997f - Fonts.BOLD.getHeight("FPS"), - "FPS", Color.white - ); - } - /** * Draws the volume bar on the middle right-hand side of the game container. * Only draws if the volume has recently been changed using with {@link #changeVolume(int)}. @@ -219,7 +194,6 @@ public class UI { if (volumeDisplay == -1) return; - int width = container.getWidth(), height = container.getHeight(); Image img = GameImage.VOLUME.getImage(); // move image in/out @@ -230,13 +204,13 @@ public class UI { else if (ratio >= 0.9f) xOffset = img.getWidth() * (1 - ((1 - ratio) * 10f)); - img.drawCentered(width - img.getWidth() / 2f + xOffset, height / 2f); + img.drawCentered(displayContainer.width - img.getWidth() / 2f + xOffset, displayContainer.height / 2f); float barHeight = img.getHeight() * 0.9f; float volume = Options.getMasterVolume(); g.setColor(Color.white); g.fillRoundRect( - width - (img.getWidth() * 0.368f) + xOffset, - (height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)), + displayContainer.width - (img.getWidth() * 0.368f) + xOffset, + (displayContainer.height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)), img.getWidth() * 0.15f, barHeight * volume, 3 ); } @@ -260,7 +234,7 @@ public class UI { */ public static void changeVolume(int units) { final float UNIT_OFFSET = 0.05f; - Options.setMasterVolume(container, Utils.clamp(Options.getMasterVolume() + (UNIT_OFFSET * units), 0f, 1f)); + Options.setMasterVolume(Utils.clamp(Options.getMasterVolume() + (UNIT_OFFSET * units), 0f, 1f)); if (volumeDisplay == -1) volumeDisplay = 0; else if (volumeDisplay >= VOLUME_DISPLAY_TIME / 10) @@ -294,8 +268,8 @@ public class UI { return; // draw loading info - float marginX = container.getWidth() * 0.02f, marginY = container.getHeight() * 0.02f; - float lineY = container.getHeight() - marginY; + float marginX = displayContainer.width * 0.02f, marginY = displayContainer.height * 0.02f; + float lineY = displayContainer.height - marginY; int lineOffsetY = Fonts.MEDIUM.getLineHeight(); if (Options.isLoadVerbose()) { // verbose: display percentages and file names @@ -308,7 +282,7 @@ public class UI { Fonts.MEDIUM.drawString(marginX, lineY - (lineOffsetY * 2), text, Color.white); g.setColor(Color.white); g.fillRoundRect(marginX, lineY - (lineOffsetY / 2f), - (container.getWidth() - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4 + (displayContainer.width - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4 ); } } @@ -332,7 +306,7 @@ public class UI { float unitBaseX, float unitBaseY, float unitWidth, float scrollAreaHeight, Color bgColor, Color scrollbarColor, boolean right ) { - float scrollbarWidth = container.getWidth() * 0.00347f; + float scrollbarWidth = displayContainer.width * 0.00347f; float scrollbarHeight = scrollAreaHeight * lengthShown / totalLength; float offsetY = (scrollAreaHeight - scrollbarHeight) * (position / (totalLength - lengthShown)); float scrollbarX = unitBaseX + unitWidth - ((right) ? scrollbarWidth : 0); @@ -368,8 +342,7 @@ public class UI { if (tooltipAlpha.getTime() == 0 || tooltip == null) return; - int containerWidth = container.getWidth(), containerHeight = container.getHeight(); - int margin = containerWidth / 100, textMarginX = 2; + int margin = displayContainer.width / 100, textMarginX = 2; int offset = GameImage.CURSOR_MIDDLE.getImage().getWidth() / 2; int lineHeight = Fonts.SMALL.getLineHeight(); int textWidth = textMarginX * 2, textHeight = lineHeight; @@ -388,12 +361,12 @@ public class UI { // get drawing coordinates int x = input.getMouseX() + offset, y = input.getMouseY() + offset; - if (x + textWidth > containerWidth - margin) - x = containerWidth - margin - textWidth; + if (x + textWidth > displayContainer.width - margin) + x = displayContainer.width - margin - textWidth; else if (x < margin) x = margin; - if (y + textHeight > containerHeight - margin) - y = containerHeight - margin - textHeight; + if (y + textHeight > displayContainer.height - margin) + y = displayContainer.height - margin - textHeight; else if (y < margin) y = margin; @@ -467,13 +440,13 @@ public class UI { float alpha = 1f; if (barNotifTimer >= BAR_NOTIFICATION_TIME * 0.9f) alpha -= 1 - ((BAR_NOTIFICATION_TIME - barNotifTimer) / (BAR_NOTIFICATION_TIME * 0.1f)); - int midX = container.getWidth() / 2, midY = container.getHeight() / 2; + int midX = displayContainer.width / 2, midY = displayContainer.height / 2; float barHeight = Fonts.LARGE.getLineHeight() * (1f + 0.6f * Math.min(barNotifTimer * 15f / BAR_NOTIFICATION_TIME, 1f)); float oldAlphaB = Colors.BLACK_ALPHA.a, oldAlphaW = Colors.WHITE_ALPHA.a; Colors.BLACK_ALPHA.a *= alpha; Colors.WHITE_ALPHA.a = alpha; g.setColor(Colors.BLACK_ALPHA); - g.fillRect(0, midY - barHeight / 2f, container.getWidth(), barHeight); + g.fillRect(0, midY - barHeight / 2f, displayContainer.width, barHeight); Fonts.LARGE.drawString( midX - Fonts.LARGE.getWidth(barNotif) / 2f, midY - Fonts.LARGE.getLineHeight() / 2.2f, diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index d0a9da61..f8c72dfb 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -22,6 +22,7 @@ import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.db.DBController; import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.Updater; +import itdelatrisu.opsu.states.Splash; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.errorhandling.ErrorHandler; @@ -61,7 +62,8 @@ public class OpsuDance { initUpdater(args); sout("database & updater initialized"); - container.init(EmptyState.class); + //container.init(EmptyState.class); + container.init(Splash.class); } catch (Exception e) { errorAndExit("startup failure", e); } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index f1480557..73941d40 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -18,6 +18,7 @@ package yugecin.opsudance.core; import itdelatrisu.opsu.GameImage; +import itdelatrisu.opsu.Options; import itdelatrisu.opsu.ui.Fonts; import org.lwjgl.LWJGLException; import org.lwjgl.Sys; @@ -29,6 +30,7 @@ import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; import org.newdawn.slick.KeyListener; import org.newdawn.slick.MouseListener; +import org.newdawn.slick.openal.SoundStore; import org.newdawn.slick.opengl.InternalTextureLoader; import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; @@ -70,7 +72,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen private OpsuState state; - private final DisplayMode nativeDisplayMode; + public final DisplayMode nativeDisplayMode; private Graphics graphics; private Input input; @@ -204,7 +206,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen Display.setDisplayMode(new DisplayMode(100, 100)); Display.create(); GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" }); - setDisplayMode(800, 600, false); + Options.setDisplayMode(this); sout("GL ready"); glVersion = GL11.glGetString(GL11.GL_VERSION); glVendor = GL11.glGetString(GL11.GL_VENDOR); @@ -257,6 +259,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen graphics.setAntiAlias(false); input = new Input(height); + input.enableKeyRepeat(); input.addKeyListener(this); input.addMouseListener(this); diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index 0617463c..bcf806bd 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -17,6 +17,7 @@ */ package yugecin.opsudance.core.inject; +import itdelatrisu.opsu.states.Splash; import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.events.EventBus; @@ -50,6 +51,8 @@ public class OpsuDanceInjector extends Injector { bind(EmptyRedState.class).asEagerSingleton(); bind(EmptyState.class).asEagerSingleton(); + + bind(Splash.class).asEagerSingleton(); } } diff --git a/src/yugecin/opsudance/core/state/BaseOpsuState.java b/src/yugecin/opsudance/core/state/BaseOpsuState.java index 3777ba3e..72ebc54b 100644 --- a/src/yugecin/opsudance/core/state/BaseOpsuState.java +++ b/src/yugecin/opsudance/core/state/BaseOpsuState.java @@ -17,6 +17,7 @@ */ package yugecin.opsudance.core.state; +import org.newdawn.slick.Graphics; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.events.EventListener; import yugecin.opsudance.events.ResolutionChangedEvent; @@ -41,6 +42,18 @@ public abstract class BaseOpsuState implements OpsuState, EventListener Date: Tue, 17 Jan 2017 23:44:12 +0100 Subject: [PATCH 54/92] converted mainmenu --- src/itdelatrisu/opsu/Opsu.java | 2 +- src/itdelatrisu/opsu/Options.java | 4 +- src/itdelatrisu/opsu/states/ButtonMenu.java | 3 +- .../opsu/states/DownloadsMenu.java | 3 +- src/itdelatrisu/opsu/states/Game.java | 3 +- .../opsu/states/GamePauseMenu.java | 3 +- src/itdelatrisu/opsu/states/GameRanking.java | 3 +- src/itdelatrisu/opsu/states/MainMenu.java | 191 +++++++++--------- src/itdelatrisu/opsu/states/SongMenu.java | 3 +- src/itdelatrisu/opsu/states/Splash.java | 28 +-- .../core/inject/OpsuDanceInjector.java | 2 + 11 files changed, 125 insertions(+), 120 deletions(-) diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index 29f7c4ee..0b5bbd7c 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -86,7 +86,7 @@ public class Opsu extends StateBasedGame { @Override public void initStatesList(GameContainer container) throws SlickException { //addState(new Splash(STATE_SPLASH)); - addState(new MainMenu(STATE_MAINMENU)); + //addState(new MainMenu(STATE_MAINMENU)); addState(new ButtonMenu(STATE_BUTTONMENU)); addState(new SongMenu(STATE_SONGMENU)); addState(new Game(STATE_GAME)); diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index dcf2da73..2c033336 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -1281,11 +1281,11 @@ public class Options { /** * Sets the target frame rate to the next available option, and sends a * bar notification about the action. - * @param container the game container */ - public static void setNextFPS(GameContainer container) { + public static void setNextFPS(DisplayContainer displayContainer) { GameOption.TARGET_FPS.clickListItem((targetFPSindex + 1) % targetFPS.length); UI.sendBarNotification(String.format("Frame limiter: %s", GameOption.TARGET_FPS.getValueString())); + displayContainer.setFPS(GameOption.TARGET_FPS.val); } /** diff --git a/src/itdelatrisu/opsu/states/ButtonMenu.java b/src/itdelatrisu/opsu/states/ButtonMenu.java index df9e7db1..ff95558c 100644 --- a/src/itdelatrisu/opsu/states/ButtonMenu.java +++ b/src/itdelatrisu/opsu/states/ButtonMenu.java @@ -705,7 +705,8 @@ public class ButtonMenu extends BasicGameState { menuState.leave(container, game); break; case Input.KEY_F7: - Options.setNextFPS(container); + // TODO + //Options.setNextFPS(displayContainer); break; case Input.KEY_F10: Options.toggleMouseDisabled(); diff --git a/src/itdelatrisu/opsu/states/DownloadsMenu.java b/src/itdelatrisu/opsu/states/DownloadsMenu.java index 76fd7376..df4435bc 100644 --- a/src/itdelatrisu/opsu/states/DownloadsMenu.java +++ b/src/itdelatrisu/opsu/states/DownloadsMenu.java @@ -911,7 +911,8 @@ public class DownloadsMenu extends BasicGameState { resetSearchTimer(); break; case Input.KEY_F7: - Options.setNextFPS(container); + // TODO d + //Options.setNextFPS(container); break; case Input.KEY_F10: Options.toggleMouseDisabled(); diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index 0c29abae..49bb6c95 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -1198,7 +1198,8 @@ public class Game extends BasicGameState { UI.changeVolume(-1); break; case Input.KEY_F7: - Options.setNextFPS(container); + // TODO d + //Options.setNextFPS(container); break; case Input.KEY_F10: Options.toggleMouseDisabled(); diff --git a/src/itdelatrisu/opsu/states/GamePauseMenu.java b/src/itdelatrisu/opsu/states/GamePauseMenu.java index c3c9dec1..a9d5a7ad 100644 --- a/src/itdelatrisu/opsu/states/GamePauseMenu.java +++ b/src/itdelatrisu/opsu/states/GamePauseMenu.java @@ -143,7 +143,8 @@ public class GamePauseMenu extends BasicGameState { } break; case Input.KEY_F7: - Options.setNextFPS(container); + // TODO d + //Options.setNextFPS(container); break; case Input.KEY_F10: Options.toggleMouseDisabled(); diff --git a/src/itdelatrisu/opsu/states/GameRanking.java b/src/itdelatrisu/opsu/states/GameRanking.java index fe7450fd..00f1b48f 100644 --- a/src/itdelatrisu/opsu/states/GameRanking.java +++ b/src/itdelatrisu/opsu/states/GameRanking.java @@ -148,7 +148,8 @@ public class GameRanking extends BasicGameState { returnToSongMenu(); break; case Input.KEY_F7: - Options.setNextFPS(container); + // TODO d + //Options.setNextFPS(container); break; case Input.KEY_F10: Options.toggleMouseDisabled(); diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index 93fd15e7..568754f2 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -29,7 +29,6 @@ import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.BeatmapSetList; import itdelatrisu.opsu.beatmap.BeatmapSetNode; -import itdelatrisu.opsu.beatmap.TimingPoint; import itdelatrisu.opsu.downloads.Updater; import itdelatrisu.opsu.states.ButtonMenu.MenuState; import itdelatrisu.opsu.ui.*; @@ -43,23 +42,26 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Stack; +import org.lwjgl.opengl.Display; import org.newdawn.slick.Color; -import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; -import org.newdawn.slick.SlickException; -import org.newdawn.slick.state.BasicGameState; -import org.newdawn.slick.state.StateBasedGame; import org.newdawn.slick.state.transition.EasedFadeOutTransition; import org.newdawn.slick.state.transition.FadeInTransition; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.inject.InstanceContainer; +import yugecin.opsudance.core.state.BaseOpsuState; /** * "Main Menu" state. *

* Players are able to enter the song menu or downloads menu from this state. */ -public class MainMenu extends BasicGameState { +public class MainMenu extends BaseOpsuState { + + private final InstanceContainer instanceContainer; + /** Idle time, in milliseconds, before returning the logo to its original position. */ private static final short LOGO_IDLE_DELAY = 10000; @@ -123,40 +125,27 @@ public class MainMenu extends BasicGameState { /** The star fountain. */ private StarFountain starFountain; - // game-related variables - private GameContainer container; - private StateBasedGame game; - private Input input; - private final int state; - - public MainMenu(int state) { - this.state = state; + public MainMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) { + super(displayContainer); + this.instanceContainer = instanceContainer; } @Override - public void init(GameContainer container, StateBasedGame game) - throws SlickException { - this.container = container; - this.game = game; - this.input = container.getInput(); - + protected void revalidate() { programStartTime = System.currentTimeMillis(); - previous = new Stack(); - - int width = container.getWidth(); - int height = container.getHeight(); + previous = new Stack<>(); // initialize menu buttons Image logoImg = GameImage.MENU_LOGO.getImage(); Image playImg = GameImage.MENU_PLAY.getImage(); Image exitImg = GameImage.MENU_EXIT.getImage(); float exitOffset = (playImg.getWidth() - exitImg.getWidth()) / 3f; - logo = new MenuButton(logoImg, width / 2f, height / 2f); + logo = new MenuButton(logoImg, displayContainer.width / 2f, displayContainer.height / 2f); playButton = new MenuButton(playImg, - width * 0.75f, (height / 2) - (logoImg.getHeight() / 5f) + displayContainer.width * 0.75f, (displayContainer.height / 2) - (logoImg.getHeight() / 5f) ); exitButton = new MenuButton(exitImg, - width * 0.75f - exitOffset, (height / 2) + (exitImg.getHeight() / 2f) + displayContainer.width * 0.75f - exitOffset, (displayContainer.height / 2) + (exitImg.getHeight() / 2f) ); final int logoAnimationDuration = 350; logo.setHoverAnimationDuration(logoAnimationDuration); @@ -174,30 +163,30 @@ public class MainMenu extends BasicGameState { // initialize music buttons int musicWidth = GameImage.MUSIC_PLAY.getImage().getWidth(); int musicHeight = GameImage.MUSIC_PLAY.getImage().getHeight(); - musicPlay = new MenuButton(GameImage.MUSIC_PLAY.getImage(), width - (2 * musicWidth), musicHeight / 1.5f); - musicPause = new MenuButton(GameImage.MUSIC_PAUSE.getImage(), width - (2 * musicWidth), musicHeight / 1.5f); - musicNext = new MenuButton(GameImage.MUSIC_NEXT.getImage(), width - musicWidth, musicHeight / 1.5f); - musicPrevious = new MenuButton(GameImage.MUSIC_PREVIOUS.getImage(), width - (3 * musicWidth), musicHeight / 1.5f); + musicPlay = new MenuButton(GameImage.MUSIC_PLAY.getImage(), displayContainer.width - (2 * musicWidth), musicHeight / 1.5f); + musicPause = new MenuButton(GameImage.MUSIC_PAUSE.getImage(), displayContainer.width - (2 * musicWidth), musicHeight / 1.5f); + musicNext = new MenuButton(GameImage.MUSIC_NEXT.getImage(), displayContainer.width - musicWidth, musicHeight / 1.5f); + musicPrevious = new MenuButton(GameImage.MUSIC_PREVIOUS.getImage(), displayContainer.width - (3 * musicWidth), musicHeight / 1.5f); musicPlay.setHoverExpand(1.5f); musicPause.setHoverExpand(1.5f); musicNext.setHoverExpand(1.5f); musicPrevious.setHoverExpand(1.5f); // initialize music position bar location - musicBarX = width - musicWidth * 3.5f; + musicBarX = displayContainer.width - musicWidth * 3.5f; musicBarY = musicHeight * 1.25f; musicBarWidth = musicWidth * 3f; musicBarHeight = musicHeight * 0.11f; // initialize downloads button Image dlImg = GameImage.DOWNLOADS.getImage(); - downloadsButton = new MenuButton(dlImg, width - dlImg.getWidth() / 2f, height / 2f); + downloadsButton = new MenuButton(dlImg, displayContainer.width - dlImg.getWidth() / 2f, displayContainer.height / 2f); downloadsButton.setHoverAnimationDuration(350); downloadsButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK); downloadsButton.setHoverExpand(1.03f, Expand.LEFT); // initialize repository button - float startX = width * 0.997f, startY = height * 0.997f; + float startX = displayContainer.width * 0.997f, startY = displayContainer.height * 0.997f; if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { // only if a webpage can be opened Image repoImg; repoImg = GameImage.REPOSITORY.getImage(); @@ -217,7 +206,7 @@ public class MainMenu extends BasicGameState { } // initialize update buttons - float updateX = width / 2f, updateY = height * 17 / 18f; + float updateX = displayContainer.width / 2f, updateY = displayContainer.height * 17 / 18f; Image downloadImg = GameImage.DOWNLOAD.getImage(); updateButton = new MenuButton(downloadImg, updateX, updateY); updateButton.setHoverAnimationDuration(400); @@ -230,10 +219,10 @@ public class MainMenu extends BasicGameState { restartButton.setHoverRotate(360); // initialize star fountain - starFountain = new StarFountain(width, height); + starFountain = new StarFountain(displayContainer.width, displayContainer.height); // logo animations - float centerOffsetX = width / 6.5f; + float centerOffsetX = displayContainer.width / 6.5f; logoOpen = new AnimatedValue(100, 0, centerOffsetX, AnimationEquation.OUT_QUAD); logoClose = new AnimatedValue(2200, centerOffsetX, 0, AnimationEquation.OUT_QUAD); logoButtonAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR); @@ -242,10 +231,9 @@ public class MainMenu extends BasicGameState { } @Override - public void render(GameContainer container, StateBasedGame game, Graphics g) - throws SlickException { - int width = container.getWidth(); - int height = container.getHeight(); + public void render(Graphics g) { + int width = displayContainer.width; + int height = displayContainer.height; // draw background Beatmap beatmap = MusicController.getBeatmap(); @@ -305,7 +293,8 @@ public class MainMenu extends BasicGameState { musicPrevious.draw(); // draw music position bar - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); + int mouseX = displayContainer.mouseX; + int mouseY = displayContainer.mouseY; g.setColor((musicPositionBarContains(mouseX, mouseY)) ? Colors.BLACK_BG_HOVER : Colors.BLACK_BG_NORMAL); g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4); g.setColor(Color.white); @@ -366,12 +355,14 @@ public class MainMenu extends BasicGameState { } @Override - public void update(GameContainer container, StateBasedGame game, int delta) - throws SlickException { + public void preRenderUpdate() { + int delta = displayContainer.renderDelta; + UI.update(delta); if (MusicController.trackEnded()) nextTrack(false); // end of track: go to next track - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); + int mouseX = displayContainer.mouseX; + int mouseY = displayContainer.mouseY; logo.hoverUpdate(delta, mouseX, mouseY, 0.25f); playButton.hoverUpdate(delta, mouseX, mouseY, 0.25f); exitButton.hoverUpdate(delta, mouseX, mouseY, 0.25f); @@ -396,7 +387,7 @@ public class MainMenu extends BasicGameState { // window focus change: increase/decrease theme song volume if (MusicController.isThemePlaying() && - MusicController.isTrackDimmed() == container.hasFocus()) + MusicController.isTrackDimmed() == Display.isActive()) MusicController.toggleTrackDimmed(0.33f); // fade in background @@ -413,7 +404,7 @@ public class MainMenu extends BasicGameState { } // buttons - int centerX = container.getWidth() / 2; + int centerX = displayContainer.width / 2; float currentLogoButtonAlpha; switch (logoState) { case DEFAULT: @@ -468,11 +459,8 @@ public class MainMenu extends BasicGameState { } @Override - public int getID() { return state; } - - @Override - public void enter(GameContainer container, StateBasedGame game) - throws SlickException { + public void enter() { + super.enter(); UI.enter(); if (!enterNotification) { if (Updater.get().getStatus() == Updater.Status.UPDATE_AVAILABLE) { @@ -489,7 +477,8 @@ public class MainMenu extends BasicGameState { starFountain.clear(); // reset button hover states if mouse is not currently hovering over the button - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); + int mouseX = displayContainer.mouseX; + int mouseY = displayContainer.mouseY; if (!logo.contains(mouseX, mouseY, 0.25f)) logo.resetHover(); if (!playButton.contains(mouseX, mouseY, 0.25f)) @@ -515,17 +504,17 @@ public class MainMenu extends BasicGameState { } @Override - public void leave(GameContainer container, StateBasedGame game) - throws SlickException { + public void leave() { + super.leave(); if (MusicController.isTrackDimmed()) MusicController.toggleTrackDimmed(1f); } @Override - public void mousePressed(int button, int x, int y) { + public boolean mousePressed(int button, int x, int y) { // check mouse button if (button == Input.MOUSE_MIDDLE_BUTTON) - return; + return false; // music position bar if (MusicController.isPlaying()) { @@ -533,7 +522,7 @@ public class MainMenu extends BasicGameState { lastMeasureProgress = 0f; float pos = (x - musicBarX) / musicBarWidth; MusicController.setPosition((int) (pos * MusicController.getDuration())); - return; + return true; } } @@ -546,29 +535,29 @@ public class MainMenu extends BasicGameState { MusicController.resume(); UI.sendBarNotification("Play"); } - return; + return true; } else if (musicNext.contains(x, y)) { nextTrack(true); UI.sendBarNotification(">> Next"); - return; + return true; } else if (musicPrevious.contains(x, y)) { lastMeasureProgress = 0f; if (!previous.isEmpty()) { - SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU); - menu.setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false); + instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false); if (Options.isDynamicBackgroundEnabled()) bgAlpha.setTime(0); } else MusicController.setPosition(0); UI.sendBarNotification("<< Previous"); - return; + return true; } // downloads button actions if (downloadsButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUHIT); - game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition()); - return; + // TODO //displayContainer.switchState(DownloadsMenu.class); + //game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition()); + return true; } // repository button actions @@ -580,7 +569,7 @@ public class MainMenu extends BasicGameState { } catch (IOException e) { ErrorHandler.error("Could not browse to repository URI.", e, false); } - return; + return true; } if (danceRepoButton != null && danceRepoButton.contains(x, y)) { @@ -591,7 +580,7 @@ public class MainMenu extends BasicGameState { } catch (IOException e) { ErrorHandler.error("Could not browse to repository URI.", e, false); } - return; + return true; } // update button actions @@ -604,13 +593,14 @@ public class MainMenu extends BasicGameState { updateButton.setHoverAnimationDuration(800); updateButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_QUAD); updateButton.setHoverFade(0.6f); - return; + return true; } else if (restartButton.contains(x, y) && status == Updater.Status.UPDATE_DOWNLOADED) { SoundController.playSound(SoundEffect.MENUHIT); Updater.get().prepareUpdate(); - container.setForceExit(false); - container.exit(); - return; + // TODO: exit? + //container.setForceExit(false); + //container.exit(); + return true; } } @@ -623,7 +613,7 @@ public class MainMenu extends BasicGameState { playButton.getImage().setAlpha(0f); exitButton.getImage().setAlpha(0f); SoundController.playSound(SoundEffect.MENUHIT); - return; + return true; } } @@ -632,21 +622,24 @@ public class MainMenu extends BasicGameState { if (logo.contains(x, y, 0.25f) || playButton.contains(x, y, 0.25f)) { SoundController.playSound(SoundEffect.MENUHIT); enterSongMenu(); - return; + return true; } else if (exitButton.contains(x, y, 0.25f)) { - container.exit(); - return; + // TODO exit? + //container.exit(); + return true; } } + return false; } @Override - public void mouseWheelMoved(int newValue) { + public boolean mouseWheelMoved(int newValue) { UI.changeVolume((newValue < 0) ? -1 : 1); + return true; } @Override - public void keyPressed(int key, char c) { + public boolean keyPressed(int key, char c) { switch (key) { case Input.KEY_ESCAPE: case Input.KEY_Q: @@ -656,9 +649,10 @@ public class MainMenu extends BasicGameState { logoTimer = 0; break; } - ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.EXIT); - game.enterState(Opsu.STATE_BUTTONMENU); - break; + // TODO + //((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.EXIT); + //game.enterState(Opsu.STATE_BUTTONMENU); + return true; case Input.KEY_P: SoundController.playSound(SoundEffect.MENUHIT); if (logoState == LogoState.DEFAULT || logoState == LogoState.CLOSING) { @@ -669,30 +663,32 @@ public class MainMenu extends BasicGameState { exitButton.getImage().setAlpha(0f); } else enterSongMenu(); - break; + return true; case Input.KEY_D: - SoundController.playSound(SoundEffect.MENUHIT); - game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition()); - break; + // TODO + //SoundController.playSound(SoundEffect.MENUHIT); + //game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition()); + return true; case Input.KEY_R: nextTrack(true); - break; + return true; case Input.KEY_UP: UI.changeVolume(1); - break; + return true; case Input.KEY_DOWN: UI.changeVolume(-1); - break; + return true; case Input.KEY_F7: - Options.setNextFPS(container); - break; + Options.setNextFPS(displayContainer); + return true; case Input.KEY_F10: Options.toggleMouseDisabled(); - break; + return true; case Input.KEY_F12: Utils.takeScreenShot(); - break; + return true; } + return false; } /** @@ -710,7 +706,7 @@ public class MainMenu extends BasicGameState { */ public void reset() { // reset logo - logo.setX(container.getWidth() / 2); + logo.setX(displayContainer.width / 2); logoOpen.setTime(0); logoClose.setTime(0); logoButtonAlpha.setTime(0); @@ -746,8 +742,7 @@ public class MainMenu extends BasicGameState { MusicController.playAt(0, false); return; } - SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU); - BeatmapSetNode node = menu.setFocus(BeatmapSetList.get().getRandomNode(), -1, true, false); + BeatmapSetNode node = instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, false); boolean sameAudio = false; if (node != null) { sameAudio = MusicController.getBeatmap().audioFilename.equals(node.getBeatmapSet().get(0).audioFilename); @@ -764,9 +759,11 @@ public class MainMenu extends BasicGameState { private void enterSongMenu() { int state = Opsu.STATE_SONGMENU; if (BeatmapSetList.get().getMapSetCount() == 0) { - ((DownloadsMenu) game.getState(Opsu.STATE_DOWNLOADSMENU)).notifyOnLoad("Download some beatmaps to get started!"); - state = Opsu.STATE_DOWNLOADSMENU; + // TODO + //((DownloadsMenu) game.getState(Opsu.STATE_DOWNLOADSMENU)).notifyOnLoad("Download some beatmaps to get started!"); + //state = Opsu.STATE_DOWNLOADSMENU; } - game.enterState(state, new EasedFadeOutTransition(), new FadeInTransition()); + // TODO + //game.enterState(state, new EasedFadeOutTransition(), new FadeInTransition()); } } diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index a0328350..4178065a 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -1120,7 +1120,8 @@ public class SongMenu extends BasicGameState { } break; case Input.KEY_F7: - Options.setNextFPS(container); + // TODO d + //Options.setNextFPS(container); break; case Input.KEY_F10: Options.toggleMouseDisabled(); diff --git a/src/itdelatrisu/opsu/states/Splash.java b/src/itdelatrisu/opsu/states/Splash.java index 03154422..5aa82ee6 100644 --- a/src/itdelatrisu/opsu/states/Splash.java +++ b/src/itdelatrisu/opsu/states/Splash.java @@ -38,6 +38,7 @@ import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.state.BaseOpsuState; /** @@ -47,6 +48,8 @@ import yugecin.opsudance.core.state.BaseOpsuState; */ public class Splash extends BaseOpsuState { + private final InstanceContainer instanceContainer; + /** Minimum time, in milliseconds, to display the splash screen (and fade in the logo). */ private static final int MIN_SPLASH_TIME = 400; @@ -71,8 +74,9 @@ public class Splash extends BaseOpsuState { // game-related variables private boolean init = false; - public Splash(DisplayContainer displayContainer) { + public Splash(DisplayContainer displayContainer, InstanceContainer instanceContainer) { super(displayContainer); + this.instanceContainer = instanceContainer; } @Override @@ -102,7 +106,7 @@ public class Splash extends BaseOpsuState { } @Override - public void update() { + public void preRenderUpdate() { if (!init) { init = true; @@ -158,7 +162,7 @@ public class Splash extends BaseOpsuState { } // fade in logo - if (logoAlpha.update(displayContainer.delta)) + if (logoAlpha.update(displayContainer.renderDelta)) GameImage.MENU_LOGO.getImage().setAlpha(logoAlpha.getValue()); // change states when loading complete @@ -166,20 +170,16 @@ public class Splash extends BaseOpsuState { // initialize song list if (BeatmapSetList.get().size() > 0) { BeatmapSetList.get().init(); - if (Options.isThemeSongEnabled()) + if (Options.isThemeSongEnabled()) { MusicController.playThemeSong(); - else - //((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); + } else { + instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); System.out.println(("todo")); - // TODO - } - - // play the theme song - else + } + } else { MusicController.playThemeSong(); - - //game.enterState(Opsu.STATE_MAINMENU); - + } + displayContainer.switchState(MainMenu.class); } } diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index bcf806bd..bf9145bd 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -17,6 +17,7 @@ */ package yugecin.opsudance.core.inject; +import itdelatrisu.opsu.states.MainMenu; import itdelatrisu.opsu.states.Splash; import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.DisplayContainer; @@ -53,6 +54,7 @@ public class OpsuDanceInjector extends Injector { bind(EmptyState.class).asEagerSingleton(); bind(Splash.class).asEagerSingleton(); + bind(MainMenu.class).asEagerSingleton(); } } From 57b29d7e917c157df6dc632ca6ea7658779cf4ad Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 10:27:40 +0100 Subject: [PATCH 55/92] getting rid of the temp displaymode --- src/yugecin/opsudance/core/DisplayContainer.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 73941d40..4eee9338 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -202,12 +202,12 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen width = height = -1; Input.disableControllers(); Display.setTitle("opsu!dance"); - // temp displaymode to not flash the screen with a 1ms black window - Display.setDisplayMode(new DisplayMode(100, 100)); + Options.setDisplayMode(this); Display.create(); GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" }); - Options.setDisplayMode(this); + initGL(); sout("GL ready"); + eventBus.post(new ResolutionChangedEvent(this.width, this.height)); glVersion = GL11.glGetString(GL11.GL_VERSION); glVendor = GL11.glGetString(GL11.GL_VENDOR); } @@ -242,9 +242,11 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen Display.setDisplayMode(displayMode); Display.setFullscreen(fullscreen); - initGL(); + if (Display.isCreated()) { + initGL(); - eventBus.post(new ResolutionChangedEvent(this.width, this.height)); + eventBus.post(new ResolutionChangedEvent(this.width, this.height)); + } if (displayMode.getBitsPerPixel() == 16) { InternalTextureLoader.get().set16BitMode(); From 605dcec605acb8a42b6a95b040adedaee9561c16 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 11:59:30 +0100 Subject: [PATCH 56/92] bubble notif when fullscreen is not supported + changed bubble colors --- src/itdelatrisu/opsu/Options.java | 2 +- src/yugecin/opsudance/core/DisplayContainer.java | 10 ++++++---- .../opsudance/events/BubbleNotificationEvent.java | 3 ++- src/yugecin/opsudance/states/EmptyRedState.java | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index 2c033336..d810feba 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -1372,7 +1372,7 @@ public class Options { try { container.setDisplayMode(width, height, isFullscreen()); } catch (Exception e) { - container.eventBus.post(new BubbleNotificationEvent("Failed to change resolution", BubbleNotificationEvent.COMMONCOLOR_RED)); + container.eventBus.post(new BubbleNotificationEvent("Failed to change resolution", BubbleNotificationEvent.COLOR_RED)); Log.error("Failed to set display mode.", e); } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 4eee9338..c90aa334 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -43,6 +43,7 @@ import yugecin.opsudance.core.state.specialstates.BarNotificationState; import yugecin.opsudance.core.state.specialstates.BubbleNotificationState; import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.transitions.*; +import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.ResolutionChangedEvent; import yugecin.opsudance.utils.GLHelper; @@ -206,8 +207,6 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen Display.create(); GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" }); initGL(); - sout("GL ready"); - eventBus.post(new ResolutionChangedEvent(this.width, this.height)); glVersion = GL11.glGetString(GL11.GL_VERSION); glVendor = GL11.glGetString(GL11.GL_VENDOR); } @@ -233,6 +232,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen if (fullscreen) { fullscreen = false; Log.warn("could not find fullscreen displaymode for " + width + "x" + height); + eventBus.post(new BubbleNotificationEvent("Fullscreen mode is not supported for " + width + "x" + height, BubbleNotificationEvent.COLOR_ORANGE)); } } @@ -244,8 +244,6 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen if (Display.isCreated()) { initGL(); - - eventBus.post(new ResolutionChangedEvent(this.width, this.height)); } if (displayMode.getBitsPerPixel() == 16) { @@ -265,8 +263,12 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen input.addKeyListener(this); input.addMouseListener(this); + sout("GL ready"); + GameImage.init(width, height); Fonts.init(); + + eventBus.post(new ResolutionChangedEvent(this.width, this.height)); } private int getDelta() { diff --git a/src/yugecin/opsudance/events/BubbleNotificationEvent.java b/src/yugecin/opsudance/events/BubbleNotificationEvent.java index caf5b736..a4abdd51 100644 --- a/src/yugecin/opsudance/events/BubbleNotificationEvent.java +++ b/src/yugecin/opsudance/events/BubbleNotificationEvent.java @@ -21,10 +21,11 @@ import org.newdawn.slick.Color; public class BubbleNotificationEvent { - public static final Color COMMONCOLOR_RED = new Color(138, 72, 51); public static final Color COMMONCOLOR_GREEN = new Color(98, 131, 59); public static final Color COMMONCOLOR_WHITE = new Color(220, 220, 220); public static final Color COMMONCOLOR_PURPLE = new Color(94, 46, 149); + public static final Color COLOR_RED = new Color(178, 62, 41); + public static final Color COLOR_ORANGE = new Color(138, 72, 51); public final String message; public final Color borderColor; diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index fcf52029..a4102ff3 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -74,7 +74,7 @@ public class EmptyRedState implements OpsuState { @Override public boolean keyPressed(int key, char c) { - displayContainer.eventBus.post(new BubbleNotificationEvent("this is a bubble notification... bubbly bubbly bubbly linewraaaaaaaaaap", BubbleNotificationEvent.COMMONCOLOR_RED)); + displayContainer.eventBus.post(new BubbleNotificationEvent("this is a bubble notification... bubbly bubbly bubbly linewraaaaaaaaaap", BubbleNotificationEvent.COLOR_RED)); return false; } From b1ccfe3019a4302a8859ec76896cc0578908140d Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 16:43:35 +0100 Subject: [PATCH 57/92] fix skipping close functions on startup exception --- src/yugecin/opsudance/OpsuDance.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index f8c72dfb..42fa75de 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -79,7 +79,8 @@ public class OpsuDance { try { container.setup(); } catch (Exception e) { - errorAndExit("could not initialize GL", e); + ErrorHandler.error("could not initialize GL", e).preventContinue().show(); + return false; } Exception caughtException = null; try { From 02863de1cf3cc7d7de8e64d1267776f7f3bbefdc Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 16:55:30 +0100 Subject: [PATCH 58/92] add code to exit from code --- src/itdelatrisu/opsu/states/MainMenu.java | 7 ++----- src/itdelatrisu/opsu/states/Splash.java | 18 ++++++++---------- .../opsudance/core/DisplayContainer.java | 5 +++-- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index 568754f2..ab901a3f 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -597,9 +597,7 @@ public class MainMenu extends BaseOpsuState { } else if (restartButton.contains(x, y) && status == Updater.Status.UPDATE_DOWNLOADED) { SoundController.playSound(SoundEffect.MENUHIT); Updater.get().prepareUpdate(); - // TODO: exit? - //container.setForceExit(false); - //container.exit(); + displayContainer.exitRequested = true; return true; } } @@ -624,8 +622,7 @@ public class MainMenu extends BaseOpsuState { enterSongMenu(); return true; } else if (exitButton.contains(x, y, 0.25f)) { - // TODO exit? - //container.exit(); + displayContainer.exitRequested = true; return true; } } diff --git a/src/itdelatrisu/opsu/states/Splash.java b/src/itdelatrisu/opsu/states/Splash.java index 5aa82ee6..35531ea4 100644 --- a/src/itdelatrisu/opsu/states/Splash.java +++ b/src/itdelatrisu/opsu/states/Splash.java @@ -185,16 +185,14 @@ public class Splash extends BaseOpsuState { @Override public boolean keyPressed(int key, char c) { - if (key == Input.KEY_ESCAPE) { - // close program - if (++escapeCount >= 3) System.out.println("hi"); - //container.exit(); // TODO - - // stop parsing beatmaps by sending interrupt to BeatmapParser - else if (thread != null) - thread.interrupt(); - return true; + if (key != Input.KEY_ESCAPE) { + return false; } - return false; + if (++escapeCount >= 3) { + displayContainer.exitRequested = true; + } else if (thread != null) { + thread.interrupt(); + } + return true; } } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index c90aa334..178135ab 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -93,6 +93,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen public int renderDelta; public int delta; + public boolean exitRequested; + public int timeSinceLastRender; private long lastFrame; @@ -153,7 +155,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen public void run() throws LWJGLException { - while(!(Display.isCloseRequested() && state.onCloseRequest())) { + while(!exitRequested && !(Display.isCloseRequested() && state.onCloseRequest())) { delta = getDelta(); timeSinceLastRender += delta; @@ -196,7 +198,6 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen Display.processMessages(); Display.sync(targetUpdatesPerSecond); } - teardown(); } public void setup() throws Exception { From 4541b507f286fcdeee7df4f750eb8af976366d1a Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 16:56:12 +0100 Subject: [PATCH 59/92] remove placeholder println --- src/itdelatrisu/opsu/states/Splash.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/itdelatrisu/opsu/states/Splash.java b/src/itdelatrisu/opsu/states/Splash.java index 35531ea4..73445a75 100644 --- a/src/itdelatrisu/opsu/states/Splash.java +++ b/src/itdelatrisu/opsu/states/Splash.java @@ -174,7 +174,6 @@ public class Splash extends BaseOpsuState { MusicController.playThemeSong(); } else { instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); - System.out.println(("todo")); } } else { MusicController.playThemeSong(); From 7004c9ef4d8c5fb8be4f29c258d47852c4bcaead Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 17:31:29 +0100 Subject: [PATCH 60/92] convert songmenu --- src/itdelatrisu/opsu/Opsu.java | 2 +- src/itdelatrisu/opsu/states/MainMenu.java | 11 +- src/itdelatrisu/opsu/states/SongMenu.java | 334 ++++++++++-------- .../opsudance/core/DisplayContainer.java | 7 +- .../core/inject/OpsuDanceInjector.java | 2 + 5 files changed, 200 insertions(+), 156 deletions(-) diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index 0b5bbd7c..e45b4abe 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -88,7 +88,7 @@ public class Opsu extends StateBasedGame { //addState(new Splash(STATE_SPLASH)); //addState(new MainMenu(STATE_MAINMENU)); addState(new ButtonMenu(STATE_BUTTONMENU)); - addState(new SongMenu(STATE_SONGMENU)); + //addState(new SongMenu(STATE_SONGMENU)); addState(new Game(STATE_GAME)); addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); addState(new GameRanking(STATE_GAMERANKING)); diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index ab901a3f..a2fa86dc 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -52,6 +52,7 @@ import org.newdawn.slick.state.transition.FadeInTransition; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.state.BaseOpsuState; +import yugecin.opsudance.core.state.OpsuState; /** * "Main Menu" state. @@ -754,13 +755,11 @@ public class MainMenu extends BaseOpsuState { * Enters the song menu, or the downloads menu if no beatmaps are loaded. */ private void enterSongMenu() { - int state = Opsu.STATE_SONGMENU; + Class state = SongMenu.class; if (BeatmapSetList.get().getMapSetCount() == 0) { - // TODO - //((DownloadsMenu) game.getState(Opsu.STATE_DOWNLOADSMENU)).notifyOnLoad("Download some beatmaps to get started!"); - //state = Opsu.STATE_DOWNLOADSMENU; + instanceContainer.provide(DownloadsMenu.class).notifyOnLoad("Download some beatmaps to get started!"); + // TODO d state = DownloadsMenu.class; } - // TODO - //game.enterState(state, new EasedFadeOutTransition(), new FadeInTransition()); + displayContainer.switchState(state); } } diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 4178065a..455ac4cd 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -72,11 +72,15 @@ import org.newdawn.slick.Input; import org.newdawn.slick.SlickException; import org.newdawn.slick.SpriteSheet; import org.newdawn.slick.gui.TextField; -import org.newdawn.slick.state.BasicGameState; import org.newdawn.slick.state.StateBasedGame; import org.newdawn.slick.state.transition.EasedFadeOutTransition; import org.newdawn.slick.state.transition.EmptyTransition; import org.newdawn.slick.state.transition.FadeInTransition; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.events.EventListener; +import yugecin.opsudance.core.inject.InstanceContainer; +import yugecin.opsudance.core.state.BaseOpsuState; +import yugecin.opsudance.events.ResolutionChangedEvent; /** * "Song Selection" state. @@ -84,7 +88,10 @@ import org.newdawn.slick.state.transition.FadeInTransition; * Players are able to select a beatmap to play, view previous scores, choose game mods, * manage beatmaps, or change game options from this state. */ -public class SongMenu extends BasicGameState { +public class SongMenu extends BaseOpsuState { + + private final InstanceContainer instanceContainer; + /** The max number of song buttons to be shown on each screen. */ public static final int MAX_SONG_BUTTONS = 6; @@ -169,7 +176,8 @@ public class SongMenu extends BasicGameState { private MenuButton selectModsButton, selectRandomButton, selectMapOptionsButton, selectOptionsButton; /** The search textfield. */ - private TextField search; + //private TextField search; + // TODO d recreate textfield /** * Delay timer, in milliseconds, before running another search. @@ -321,47 +329,39 @@ public class SongMenu extends BasicGameState { private boolean isScrollingToFocusNode = false; /** Sort order dropdown menu. */ - private DropdownMenu sortMenu; + // TODO: d remake dropdownmenu + //private DropdownMenu sortMenu; - // game-related variables - private GameContainer container; - private StateBasedGame game; - private Input input; - private final int state; - - public SongMenu(int state) { - this.state = state; + public SongMenu(final DisplayContainer displayContainer, InstanceContainer instanceContainer) { + super(displayContainer); + this.instanceContainer = instanceContainer; } @Override - public void init(GameContainer container, StateBasedGame game) - throws SlickException { - this.container = container; - this.game = game; - this.input = container.getInput(); - - int width = container.getWidth(); - int height = container.getHeight(); + public void revalidate() { + super.revalidate(); // header/footer coordinates - headerY = height * 0.0075f + GameImage.MENU_MUSICNOTE.getImage().getHeight() + + headerY = displayContainer.height * 0.0075f + GameImage.MENU_MUSICNOTE.getImage().getHeight() + Fonts.BOLD.getLineHeight() + Fonts.DEFAULT.getLineHeight() + Fonts.SMALL.getLineHeight(); - footerY = height - GameImage.SELECTION_MODS.getImage().getHeight(); + footerY = displayContainer.height - GameImage.SELECTION_MODS.getImage().getHeight(); // footer logo coordinates - float footerHeight = height - footerY; + float footerHeight = displayContainer.height - footerY; footerLogoSize = footerHeight * 3.25f; Image logo = GameImage.MENU_LOGO.getImage(); logo = logo.getScaledCopy(footerLogoSize / logo.getWidth()); - footerLogoButton = new MenuButton(logo, width - footerHeight * 0.8f, height - footerHeight * 0.65f); + footerLogoButton = new MenuButton(logo, displayContainer.width - footerHeight * 0.8f, displayContainer.height - footerHeight * 0.65f); footerLogoButton.setHoverAnimationDuration(1); footerLogoButton.setHoverExpand(1.2f); // initialize sorts - int sortWidth = (int) (width * 0.12f); + // TODO d reenable dropdown + /* + int sortWidth = (int) (displayContainer.width * 0.12f); sortMenu = new DropdownMenu(container, BeatmapSortOrder.values(), - width * 0.87f, headerY - GameImage.MENU_TAB.getImage().getHeight() * 2.25f, sortWidth) { + displayContainer.width * 0.87f, headerY - GameImage.MENU_TAB.getImage().getHeight() * 2.25f, sortWidth) { @Override public void itemSelected(int index, BeatmapSortOrder item) { BeatmapSortOrder.set(item); @@ -386,36 +386,40 @@ public class SongMenu extends BasicGameState { sortMenu.setBackgroundColor(Colors.BLACK_BG_HOVER); sortMenu.setBorderColor(Colors.BLUE_DIVIDER); sortMenu.setChevronRightColor(Color.white); + */ // initialize group tabs for (BeatmapGroup group : BeatmapGroup.values()) - group.init(width, headerY - DIVIDER_LINE_WIDTH / 2); + group.init(displayContainer.width, headerY - DIVIDER_LINE_WIDTH / 2); // initialize score data buttons - ScoreData.init(width, headerY + height * 0.01f); + ScoreData.init(displayContainer.width, headerY + displayContainer.height * 0.01f); // song button background & graphics context Image menuBackground = GameImage.MENU_BUTTON_BG.getImage(); // song button coordinates - buttonX = width * 0.6f; + buttonX = displayContainer.width * 0.6f; //buttonY = headerY; buttonWidth = menuBackground.getWidth(); buttonHeight = menuBackground.getHeight(); buttonOffset = (footerY - headerY - DIVIDER_LINE_WIDTH) / MAX_SONG_BUTTONS; // search - int textFieldX = (int) (width * 0.7125f + Fonts.BOLD.getWidth("Search: ")); + // TODO d reenable search box + /* + int textFieldX = (int) (displayContainer.width * 0.7125f + Fonts.BOLD.getWidth("Search: ")); int textFieldY = (int) (headerY + Fonts.BOLD.getLineHeight() / 2); search = new TextField( container, Fonts.BOLD, textFieldX, textFieldY, - (int) (width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight() + (int) (displayContainer.width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight() ); search.setBackgroundColor(Color.transparent); search.setBorderColor(Color.transparent); search.setTextColor(Color.white); search.setConsumeEvents(false); search.setMaxLength(60); + */ // selection buttons Image selectionMods = GameImage.SELECTION_MODS.getImage(); @@ -427,8 +431,8 @@ public class SongMenu extends BasicGameState { if (selectButtonsWidth < 20) { selectButtonsWidth = 100; } - float selectX = width * 0.183f + selectButtonsWidth / 2f; - float selectY = height - selectButtonsHeight / 2f; + float selectX = displayContainer.width * 0.183f + selectButtonsWidth / 2f; + float selectY = displayContainer.height - selectButtonsHeight / 2f; float selectOffset = selectButtonsWidth * 1.05f; selectModsButton = new MenuButton(GameImage.SELECTION_MODS_OVERLAY.getImage(), selectX, selectY); @@ -449,33 +453,37 @@ public class SongMenu extends BasicGameState { loader = new Animation(spr, 50); // beatmap watch service listener - final StateBasedGame game_ = game; BeatmapWatchService.addListener(new BeatmapWatchServiceListener() { @Override public void eventReceived(Kind kind, Path child) { if (!songFolderChanged && kind != StandardWatchEventKinds.ENTRY_MODIFY) { songFolderChanged = true; - if (game_.getCurrentStateID() == Opsu.STATE_SONGMENU) + if (displayContainer.isInState(SongMenu.class)) { UI.sendBarNotification("Changes in Songs folder detected. Hit F5 to refresh."); + } } } }); // star stream - starStream = new StarStream(width, (height - GameImage.STAR.getImage().getHeight()) / 2, -width, 0, MAX_STREAM_STARS); - starStream.setPositionSpread(height / 20f); + starStream = new StarStream(displayContainer.width, (displayContainer.height - GameImage.STAR.getImage().getHeight()) / 2, -displayContainer.width, 0, MAX_STREAM_STARS); + starStream.setPositionSpread(displayContainer.height / 20f); starStream.setDirectionSpread(10f); } @Override - public void render(GameContainer container, StateBasedGame game, Graphics g) - throws SlickException { + public void render(Graphics g) { + super.render(g); + g.setBackground(Color.black); - int width = container.getWidth(); - int height = container.getHeight(); - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); - boolean inDropdownMenu = sortMenu.contains(mouseX, mouseY); + int width = displayContainer.width; + int height = displayContainer.height; + int mouseX = displayContainer.mouseX; + int mouseY = displayContainer.mouseY; + + // TODO d + //boolean inDropdownMenu = sortMenu.contains(mouseX, mouseY); // background if (focusNode != null) { @@ -547,8 +555,11 @@ public class SongMenu extends BasicGameState { g.clearClip(); // scroll bar + // TODO d + /* if (focusScores.length > MAX_SCORE_BUTTONS && ScoreData.areaContains(mouseX, mouseY) && !inDropdownMenu) ScoreData.drawScrollbar(g, startScorePos.getPosition(), focusScores.length * ScoreData.getButtonOffset()); + */ } // top/bottom bars @@ -565,6 +576,8 @@ public class SongMenu extends BasicGameState { Float position = MusicController.getBeatProgress(); if (position == null) // default to 60bpm position = System.currentTimeMillis() % 1000 / 1000f; + // TODO d + /* if (footerLogoButton.contains(mouseX, mouseY, 0.25f) && !inDropdownMenu) { // hovering over logo: stop pulsing footerLogoButton.draw(); @@ -578,6 +591,7 @@ public class SongMenu extends BasicGameState { ghostLogo.drawCentered(footerLogoButton.getX(), footerLogoButton.getY(), Colors.GHOST_LOGO); Colors.GHOST_LOGO.a = oldGhostAlpha; } + */ // header if (focusNode != null) { @@ -658,6 +672,8 @@ public class SongMenu extends BasicGameState { // group tabs BeatmapGroup currentGroup = BeatmapGroup.current(); BeatmapGroup hoverGroup = null; + // TODO d + /* if (!inDropdownMenu) { for (BeatmapGroup group : BeatmapGroup.values()) { if (group.contains(mouseX, mouseY)) { @@ -666,6 +682,7 @@ public class SongMenu extends BasicGameState { } } } + */ for (BeatmapGroup group : BeatmapGroup.VALUES_REVERSED) { if (group != currentGroup) group.draw(false, group == hoverGroup); @@ -673,6 +690,8 @@ public class SongMenu extends BasicGameState { currentGroup.draw(true, false); // search + // TODO d + /* boolean searchEmpty = search.getText().isEmpty(); int searchX = search.getX(), searchY = search.getY(); float searchBaseX = width * 0.7f; @@ -707,6 +726,7 @@ public class SongMenu extends BasicGameState { // sorting options sortMenu.render(container, g); + */ // reloading beatmaps if (reloadThread != null) { @@ -725,8 +745,10 @@ public class SongMenu extends BasicGameState { } @Override - public void update(GameContainer container, StateBasedGame game, int delta) - throws SlickException { + public void preRenderUpdate() { + super.preRenderUpdate(); + + int delta = displayContainer.delta; UI.update(delta); if (reloadThread == null) MusicController.loopTrackIfEnded(true); @@ -742,8 +764,9 @@ public class SongMenu extends BasicGameState { MusicController.playThemeSong(); reloadThread = null; } - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); - boolean inDropdownMenu = sortMenu.contains(mouseX, mouseY); + int mouseX = displayContainer.mouseX; + int mouseY = displayContainer.mouseY; + boolean inDropdownMenu = false; // TODO d sortMenu.contains(mouseX, mouseY); UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); selectModsButton.hoverUpdate(delta, mouseX, mouseY); selectRandomButton.hoverUpdate(delta, mouseX, mouseY); @@ -759,8 +782,8 @@ public class SongMenu extends BasicGameState { if (focusNode != null) { MenuState state = focusNode.getBeatmapSet().isFavorite() ? MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP; - ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(state, focusNode); - game.enterState(Opsu.STATE_BUTTONMENU); + instanceContainer.provide(ButtonMenu.class).setMenuState(state, focusNode); + // TODO d displayContainer.switchState(ButtonMenu.class); } return; } @@ -782,6 +805,8 @@ public class SongMenu extends BasicGameState { starStream.update(delta); // search + // TODO d + /* search.setFocus(true); searchTimer += delta; if (searchTimer >= SEARCH_DELAY && reloadThread == null && beatmapMenuTimer == -1) { @@ -827,6 +852,7 @@ public class SongMenu extends BasicGameState { if (searchTransitionTimer > SEARCH_TRANSITION_TIME) searchTransitionTimer = SEARCH_TRANSITION_TIME; } + */ // scores if (focusScores != null) { @@ -862,9 +888,11 @@ public class SongMenu extends BasicGameState { } // tooltips + // TODO d + /* if (sortMenu.baseContains(mouseX, mouseY)) UI.updateTooltip(delta, "Sort by...", false); - else if (focusScores != null && ScoreData.areaContains(mouseX, mouseY)) { + else */if (focusScores != null && ScoreData.areaContains(mouseX, mouseY)) { int startScore = (int) (startScorePos.getPosition() / ScoreData.getButtonOffset()); int offset = (int) (-startScorePos.getPosition() + startScore * ScoreData.getButtonOffset()); int scoreButtons = Math.min(focusScores.length - startScore, MAX_SCORE_BUTTONS); @@ -880,66 +908,58 @@ public class SongMenu extends BasicGameState { } @Override - public int getID() { return state; } + public boolean mousePressed(int button, int x, int y) { + if (button == Input.MOUSE_MIDDLE_BUTTON) { + return false; + } - @Override - public void mousePressed(int button, int x, int y) { - // check mouse button - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; - - if (isScrollingToFocusNode) - return; + if (isScrollingToFocusNode) { + return true; + } songScrolling.pressed(); startScorePos.pressed(); + return true; } @Override - public void mouseReleased(int button, int x, int y) { - // check mouse button - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; + public boolean mouseReleased(int button, int x, int y) { + if (button == Input.MOUSE_MIDDLE_BUTTON) { + return false; + } - if (isScrollingToFocusNode) - return; + if (isScrollingToFocusNode) { + return true; + } songScrolling.released(); startScorePos.released(); - } - @Override - public void mouseClicked(int button, int x, int y, int clickCount) { - // check mouse button - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; + if (isInputBlocked()) { + return true; + } - // block input - if (isInputBlocked()) - return; - - // back if (UI.getBackButton().contains(x, y)) { SoundController.playSound(SoundEffect.MENUBACK); - ((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); - game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition()); - return; + instanceContainer.provide(MainMenu.class).reset(); // TODO d why is this needed + displayContainer.switchState(MainMenu.class); + return true; } // selection buttons if (selectModsButton.contains(x, y)) { this.keyPressed(Input.KEY_F1, '\0'); - return; + return true; } else if (selectRandomButton.contains(x, y)) { this.keyPressed(Input.KEY_F2, '\0'); - return; + return true; } else if (selectMapOptionsButton.contains(x, y)) { this.keyPressed(Input.KEY_F3, '\0'); - return; + return true; } else if (selectOptionsButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUHIT); - game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition()); - return; + // TODO d displayContainer.switchState(OptionsMenu.class); + return true; } // group tabs @@ -954,7 +974,7 @@ public class SongMenu extends BasicGameState { songInfo = null; scoreMap = null; focusScores = null; - search.setText(""); + // TODO d search.setText(""); searchTimer = SEARCH_DELAY; searchTransitionTimer = SEARCH_TRANSITION_TIME; searchResultString = null; @@ -965,17 +985,18 @@ public class SongMenu extends BasicGameState { if (BeatmapSetList.get().size() < 1 && group.getEmptyMessage() != null) UI.sendBarNotification(group.getEmptyMessage()); } - return; + return true; } } - if (focusNode == null) - return; + if (focusNode == null) { + return false; + } // logo: start game if (footerLogoButton.contains(x, y, 0.25f)) { startGame(); - return; + return true; } // song buttons @@ -1014,7 +1035,7 @@ public class SongMenu extends BasicGameState { if (button == Input.MOUSE_RIGHT_BUTTON) beatmapMenuTimer = (node.index == expandedIndex) ? BEATMAP_MENU_DELAY * 4 / 5 : 0; - return; + return true; } // score buttons @@ -1027,28 +1048,33 @@ public class SongMenu extends BasicGameState { SoundController.playSound(SoundEffect.MENUHIT); if (button != Input.MOUSE_RIGHT_BUTTON) { // view score - GameData data = new GameData(focusScores[rank], container.getWidth(), container.getHeight()); - ((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data); - game.enterState(Opsu.STATE_GAMERANKING, new EasedFadeOutTransition(), new FadeInTransition()); + instanceContainer.provide(GameRanking.class).setGameData(new GameData(focusScores[rank], displayContainer.width, displayContainer.height)); + // TODO d displayContainer.switchState(GameRanking.class); } else { // score management - ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.SCORE, focusScores[rank]); - game.enterState(Opsu.STATE_BUTTONMENU); + instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.SCORE, focusScores[rank]); + // TODO d displayContainer.switchState(ButtonMenu.class); } - return; + return true; } } } + return true; } @Override - public void keyPressed(int key, char c) { + public boolean keyPressed(int key, char c) { // block input - if ((reloadThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) || beatmapMenuTimer > -1 || isScrollingToFocusNode) - return; + if ((reloadThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) || beatmapMenuTimer > -1 || isScrollingToFocusNode) { + return true; + } + + Input input = displayContainer.input; switch (key) { case Input.KEY_ESCAPE: + // TODO d + /* if (reloadThread != null) { // beatmap reloading: stop parsing beatmaps by sending interrupt to BeatmapParser reloadThread.interrupt(); @@ -1064,12 +1090,13 @@ public class SongMenu extends BasicGameState { ((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition()); } - break; + */ + return true; case Input.KEY_F1: SoundController.playSound(SoundEffect.MENUHIT); - ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.MODS); - game.enterState(Opsu.STATE_BUTTONMENU); - break; + instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.MODS); + // TODO d displayContainer.switchState(ButtonMenu.class); + return true; case Input.KEY_F2: if (focusNode == null) break; @@ -1089,25 +1116,25 @@ public class SongMenu extends BasicGameState { randomStack.push(new SongNode(BeatmapSetList.get().getBaseNode(focusNode.index), focusNode.beatmapIndex)); setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); } - break; + return true; case Input.KEY_F3: if (focusNode == null) break; SoundController.playSound(SoundEffect.MENUHIT); MenuState state = focusNode.getBeatmapSet().isFavorite() ? MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP; - ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(state, focusNode); - game.enterState(Opsu.STATE_BUTTONMENU); - break; + instanceContainer.provide(ButtonMenu.class).setMenuState(state, focusNode); + // TODO d displayContainer.switchState(ButtonMenu.class); + return true; case Input.KEY_F5: SoundController.playSound(SoundEffect.MENUHIT); if (songFolderChanged) reloadBeatmaps(false); else { - ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.RELOAD); - game.enterState(Opsu.STATE_BUTTONMENU); + instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.RELOAD); + // TODO d displayContainer.switchState(ButtonMenu.class); } - break; + return true; case Input.KEY_DELETE: if (focusNode == null) break; @@ -1115,31 +1142,31 @@ public class SongMenu extends BasicGameState { SoundController.playSound(SoundEffect.MENUHIT); MenuState ms = (focusNode.beatmapIndex == -1 || focusNode.getBeatmapSet().size() == 1) ? MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT; - ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(ms, focusNode); - game.enterState(Opsu.STATE_BUTTONMENU); + instanceContainer.provide(ButtonMenu.class).setMenuState(ms, focusNode); + // TODO d displayContainer.switchState(ButtonMenu.class); } - break; + return true; case Input.KEY_F7: // TODO d //Options.setNextFPS(container); - break; + return true; case Input.KEY_F10: Options.toggleMouseDisabled(); - break; + return true; case Input.KEY_F12: Utils.takeScreenShot(); - break; + return true; case Input.KEY_ENTER: if (focusNode == null) break; startGame(); - break; + return true; case Input.KEY_DOWN: changeIndex(1); - break; + return true; case Input.KEY_UP: changeIndex(-1); - break; + return true; case Input.KEY_RIGHT: if (focusNode == null) break; @@ -1155,7 +1182,7 @@ public class SongMenu extends BasicGameState { hoverIndex = oldHoverIndex; } } - break; + return true; case Input.KEY_LEFT: if (focusNode == null) break; @@ -1171,24 +1198,26 @@ public class SongMenu extends BasicGameState { hoverIndex = oldHoverIndex; } } - break; + return true; case Input.KEY_NEXT: changeIndex(MAX_SONG_BUTTONS); - break; + return true; case Input.KEY_PRIOR: changeIndex(-MAX_SONG_BUTTONS); - break; + return true; case Input.KEY_O: if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) { - game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition()); + // TODO d displayContainer.switchState(OptionsMenu.class); } - break; + return true; default: // wait for user to finish typing // TODO: accept all characters (current conditions are from TextField class) if ((c > 31 && c < 127) || key == Input.KEY_BACK) { searchTimer = 0; - int textLength = search.getText().length(); + // TODO d + //int textLength = search.getText().length(); + int textLength = 0; if (lastSearchTextLength != textLength) { if (key == Input.KEY_BACK) { if (textLength == 0) @@ -1198,10 +1227,13 @@ public class SongMenu extends BasicGameState { lastSearchTextLength = textLength; } } - break; + return true; } + return true; } + // TODO d + /* @Override public void mouseDragged(int oldx, int oldy, int newx, int newy) { // block input @@ -1229,18 +1261,20 @@ public class SongMenu extends BasicGameState { else songScrolling.dragged(-diff * multiplier); } + */ @Override - public void mouseWheelMoved(int newValue) { - // change volume + public boolean mouseWheelMoved(int newValue) { + Input input = displayContainer.input; + if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) { UI.changeVolume((newValue < 0) ? -1 : 1); - return; + return true; } - // block input - if (isInputBlocked()) - return; + if (isInputBlocked()) { + return true; + } int shift = (newValue < 0) ? 1 : -1; int mouseX = input.getMouseX(), mouseY = input.getMouseY(); @@ -1252,13 +1286,14 @@ public class SongMenu extends BasicGameState { // song buttons else changeIndex(shift); + return false; } @Override - public void enter(GameContainer container, StateBasedGame game) - throws SlickException { + public void enter() { + super.enter(); + UI.enter(); - Display.setTitle(game.getTitle()); selectModsButton.resetHover(); selectRandomButton.resetHover(); selectMapOptionsButton.resetHover(); @@ -1276,8 +1311,9 @@ public class SongMenu extends BasicGameState { songChangeTimer.setTime(songChangeTimer.getDuration()); musicIconBounceTimer.setTime(musicIconBounceTimer.getDuration()); starStream.clear(); - sortMenu.activate(); - sortMenu.reset(); + // TODO d + //sortMenu.activate(); + //sortMenu.reset(); // reset song stack randomStack = new Stack(); @@ -1308,7 +1344,7 @@ public class SongMenu extends BasicGameState { // reset game data if (resetGame) { - ((Game) game.getState(Opsu.STATE_GAME)).resetGameData(); + instanceContainer.provide(Game.class).resetGameData(); // destroy extra Clips MultiClip.destroyExtraClips(); @@ -1441,10 +1477,13 @@ public class SongMenu extends BasicGameState { } @Override - public void leave(GameContainer container, StateBasedGame game) - throws SlickException { + public void leave() { + super.leave(); + // TODO d + /* search.setFocus(false); sortMenu.deactivate(); + */ } /** @@ -1569,9 +1608,9 @@ public class SongMenu extends BasicGameState { // change the focus node if (changeStartNode || (startNode.index == 0 && startNode.beatmapIndex == -1 && startNode.prev == null)) { - if (startNode == null || game.getCurrentStateID() != Opsu.STATE_SONGMENU) + if (startNode == null || displayContainer.isInState(SongMenu.class)) { songScrolling.setPosition((node.index - 1) * buttonOffset); - else { + } else { isScrollingToFocusNode = true; songScrolling.setSpeedMultiplier(2f); songScrolling.released(); @@ -1704,7 +1743,8 @@ public class SongMenu extends BasicGameState { songInfo = null; hoverOffset.setTime(0); hoverIndex = null; - search.setText(""); + // TODO d + //search.setText(""); searchTimer = SEARCH_DELAY; searchTransitionTimer = SEARCH_TRANSITION_TIME; searchResultString = null; @@ -1785,17 +1825,17 @@ public class SongMenu extends BasicGameState { } // turn on "auto" mod if holding "ctrl" key - if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) { + if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) { if (!GameMod.AUTO.isActive()) GameMod.AUTO.toggle(true); } SoundController.playSound(SoundEffect.MENUHIT); MultiClip.destroyExtraClips(); - Game gameState = (Game) game.getState(Opsu.STATE_GAME); + Game gameState = instanceContainer.provide(Game.class); gameState.loadBeatmap(beatmap); gameState.setRestart(Game.Restart.NEW); gameState.setReplay(null); - game.enterState(Opsu.STATE_GAME, new EasedFadeOutTransition(), new FadeInTransition()); + // TODO d displayContainer.switchState(Game.class); } } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 178135ab..a2b70d7f 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -30,7 +30,6 @@ import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; import org.newdawn.slick.KeyListener; import org.newdawn.slick.MouseListener; -import org.newdawn.slick.openal.SoundStore; import org.newdawn.slick.opengl.InternalTextureLoader; import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; @@ -76,7 +75,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen public final DisplayMode nativeDisplayMode; private Graphics graphics; - private Input input; + public Input input; public int width; public int height; @@ -300,6 +299,10 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen state.writeErrorDump(dump); } + public boolean isInState(Class state) { + return state.isInstance(state); + } + public boolean isTransitioning() { return state instanceof TransitionState; } diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index bf9145bd..8fd39377 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -18,6 +18,7 @@ package yugecin.opsudance.core.inject; import itdelatrisu.opsu.states.MainMenu; +import itdelatrisu.opsu.states.SongMenu; import itdelatrisu.opsu.states.Splash; import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.DisplayContainer; @@ -55,6 +56,7 @@ public class OpsuDanceInjector extends Injector { bind(Splash.class).asEagerSingleton(); bind(MainMenu.class).asEagerSingleton(); + bind(SongMenu.class).asEagerSingleton(); } } From 54917091de81eab8b4084ae20739d140ed82d56d Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 17:32:40 +0100 Subject: [PATCH 61/92] fix tooltip causing crash --- src/itdelatrisu/opsu/ui/UI.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/itdelatrisu/opsu/ui/UI.java b/src/itdelatrisu/opsu/ui/UI.java index 6fe0e50b..1afed82e 100644 --- a/src/itdelatrisu/opsu/ui/UI.java +++ b/src/itdelatrisu/opsu/ui/UI.java @@ -77,7 +77,6 @@ public class UI { // game-related variables private static DisplayContainer displayContainer; - private static Input input; // This class should not be instantiated. private UI() {} @@ -360,7 +359,8 @@ public class UI { textWidth += Fonts.SMALL.getWidth(tooltip); // get drawing coordinates - int x = input.getMouseX() + offset, y = input.getMouseY() + offset; + int x = displayContainer.mouseX + offset; + int y = displayContainer.mouseY + offset; if (x + textWidth > displayContainer.width - margin) x = displayContainer.width - margin - textWidth; else if (x < margin) From db6e4f3b55b7709263a5b4ea07e1d4c38ccdc263 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 17:39:46 +0100 Subject: [PATCH 62/92] fix music not playing (whoops) --- src/yugecin/opsudance/core/DisplayContainer.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index a2b70d7f..82b39997 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -26,10 +26,7 @@ import org.lwjgl.openal.AL; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; import org.lwjgl.opengl.GL11; -import org.newdawn.slick.Graphics; -import org.newdawn.slick.Input; -import org.newdawn.slick.KeyListener; -import org.newdawn.slick.MouseListener; +import org.newdawn.slick.*; import org.newdawn.slick.opengl.InternalTextureLoader; import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; @@ -160,6 +157,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen timeSinceLastRender += delta; input.poll(width, height); + Music.poll(delta); mouseX = input.getMouseX(); mouseY = input.getMouseY(); state.update(); From 44164168130a085f4750f3cf881954b600710ada Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 18:20:47 +0100 Subject: [PATCH 63/92] fix songmenu delta --- src/itdelatrisu/opsu/states/SongMenu.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 455ac4cd..4e2557e6 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -748,7 +748,7 @@ public class SongMenu extends BaseOpsuState { public void preRenderUpdate() { super.preRenderUpdate(); - int delta = displayContainer.delta; + int delta = displayContainer.renderDelta; UI.update(delta); if (reloadThread == null) MusicController.loopTrackIfEnded(true); From c03897a47dab980c1118aae640e72c3186c1b231 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 19:06:45 +0100 Subject: [PATCH 64/92] convert buttonmenu --- src/itdelatrisu/opsu/Opsu.java | 2 +- src/itdelatrisu/opsu/states/ButtonMenu.java | 485 ++++++++---------- src/itdelatrisu/opsu/states/MainMenu.java | 8 +- src/itdelatrisu/opsu/states/SongMenu.java | 19 +- .../core/inject/OpsuDanceInjector.java | 2 + 5 files changed, 239 insertions(+), 277 deletions(-) diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index e45b4abe..de11aab1 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -87,7 +87,7 @@ public class Opsu extends StateBasedGame { public void initStatesList(GameContainer container) throws SlickException { //addState(new Splash(STATE_SPLASH)); //addState(new MainMenu(STATE_MAINMENU)); - addState(new ButtonMenu(STATE_BUTTONMENU)); + //addState(new ButtonMenu(STATE_BUTTONMENU)); //addState(new SongMenu(STATE_SONGMENU)); addState(new Game(STATE_GAME)); addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); diff --git a/src/itdelatrisu/opsu/states/ButtonMenu.java b/src/itdelatrisu/opsu/states/ButtonMenu.java index ff95558c..8533dbf9 100644 --- a/src/itdelatrisu/opsu/states/ButtonMenu.java +++ b/src/itdelatrisu/opsu/states/ButtonMenu.java @@ -20,7 +20,6 @@ package itdelatrisu.opsu.states; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameMod; -import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.Utils; @@ -39,114 +38,112 @@ import java.util.ArrayList; import java.util.List; import org.newdawn.slick.Color; -import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; -import org.newdawn.slick.SlickException; -import org.newdawn.slick.state.BasicGameState; -import org.newdawn.slick.state.StateBasedGame; -import org.newdawn.slick.state.transition.EmptyTransition; -import org.newdawn.slick.state.transition.FadeInTransition; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.inject.InstanceContainer; +import yugecin.opsudance.core.state.BaseOpsuState; /** * Generic button menu state. *

* Displays a header and a set of defined options to the player. */ -public class ButtonMenu extends BasicGameState { +public class ButtonMenu extends BaseOpsuState { + /** Menu states. */ public enum MenuState { /** The exit confirmation screen. */ EXIT (new Button[] { Button.YES, Button.NO }) { @Override - public String[] getTitle(GameContainer container, StateBasedGame game) { + public String[] getTitle() { return new String[] { "Are you sure you want to exit opsu!?" }; } @Override - public void leave(GameContainer container, StateBasedGame game) { - Button.NO.click(container, game); + public void leave() { + Button.NO.click(); } }, /** The initial beatmap management screen (for a non-"favorite" beatmap). */ BEATMAP (new Button[] { Button.CLEAR_SCORES, Button.FAVORITE_ADD, Button.DELETE, Button.CANCEL }) { @Override - public String[] getTitle(GameContainer container, StateBasedGame game) { - BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); + public String[] getTitle() { + BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); String beatmapString = (node != null) ? BeatmapSetList.get().getBaseNode(node.index).toString() : ""; return new String[] { beatmapString, "What do you want to do with this beatmap?" }; } @Override - public void leave(GameContainer container, StateBasedGame game) { - Button.CANCEL.click(container, game); + public void leave() { + Button.CANCEL.click(); } @Override - public void scroll(GameContainer container, StateBasedGame game, int newValue) { - Input input = container.getInput(); - if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) - super.scroll(container, game, newValue); + public void mouseWheelMoved(int newValue) { + if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) { + super.mouseWheelMoved(newValue); + } } }, /** The initial beatmap management screen (for a "favorite" beatmap). */ BEATMAP_FAVORITE (new Button[] { Button.CLEAR_SCORES, Button.FAVORITE_REMOVE, Button.DELETE, Button.CANCEL }) { @Override - public String[] getTitle(GameContainer container, StateBasedGame game) { - return BEATMAP.getTitle(container, game); + public String[] getTitle() { + return BEATMAP.getTitle(); } @Override - public void leave(GameContainer container, StateBasedGame game) { - BEATMAP.leave(container, game); + public void leave() { + BEATMAP.leave(); } @Override - public void scroll(GameContainer container, StateBasedGame game, int newValue) { - BEATMAP.scroll(container, game, newValue); + public void mouseWheelMoved(int newValue) { + BEATMAP.mouseWheelMoved(newValue); } }, /** The beatmap deletion screen for a beatmap set with multiple beatmaps. */ BEATMAP_DELETE_SELECT (new Button[] { Button.DELETE_GROUP, Button.DELETE_SONG, Button.CANCEL_DELETE }) { @Override - public String[] getTitle(GameContainer container, StateBasedGame game) { - BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); + public String[] getTitle() { + BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); String beatmapString = (node != null) ? node.toString() : ""; return new String[] { String.format("Are you sure you wish to delete '%s' from disk?", beatmapString) }; } @Override - public void leave(GameContainer container, StateBasedGame game) { - Button.CANCEL_DELETE.click(container, game); + public void leave() { + Button.CANCEL_DELETE.click(); } @Override - public void scroll(GameContainer container, StateBasedGame game, int newValue) { - MenuState.BEATMAP.scroll(container, game, newValue); + public void mouseWheelMoved(int newValue) { + MenuState.BEATMAP.mouseWheelMoved(newValue); } }, /** The beatmap deletion screen for a single beatmap. */ BEATMAP_DELETE_CONFIRM (new Button[] { Button.DELETE_CONFIRM, Button.CANCEL_DELETE }) { @Override - public String[] getTitle(GameContainer container, StateBasedGame game) { - return BEATMAP_DELETE_SELECT.getTitle(container, game); + public String[] getTitle() { + return BEATMAP_DELETE_SELECT.getTitle(); } @Override - public void leave(GameContainer container, StateBasedGame game) { - Button.CANCEL_DELETE.click(container, game); + public void leave() { + Button.CANCEL_DELETE.click(); } @Override - public void scroll(GameContainer container, StateBasedGame game, int newValue) { - MenuState.BEATMAP.scroll(container, game, newValue); + public void mouseWheelMoved(int newValue) { + MenuState.BEATMAP.mouseWheelMoved(newValue); } }, /** The beatmap reloading confirmation screen. */ RELOAD (new Button[] { Button.RELOAD_CONFIRM, Button.RELOAD_CANCEL }) { @Override - public String[] getTitle(GameContainer container, StateBasedGame game) { + public String[] getTitle() { return new String[] { "You have requested a full process of your beatmaps.", "This could take a few minutes.", @@ -155,70 +152,68 @@ public class ButtonMenu extends BasicGameState { } @Override - public void leave(GameContainer container, StateBasedGame game) { - Button.RELOAD_CANCEL.click(container, game); + public void leave() { + Button.RELOAD_CANCEL.click(); } @Override - public void scroll(GameContainer container, StateBasedGame game, int newValue) { - MenuState.BEATMAP.scroll(container, game, newValue); + public void mouseWheelMoved(int newValue) { + MenuState.BEATMAP.mouseWheelMoved(newValue); } }, /** The score management screen. */ SCORE (new Button[] { Button.DELETE_SCORE, Button.CLOSE }) { @Override - public String[] getTitle(GameContainer container, StateBasedGame game) { + public String[] getTitle() { return new String[] { "Score Management" }; } @Override - public void leave(GameContainer container, StateBasedGame game) { - Button.CLOSE.click(container, game); + public void leave() { + Button.CLOSE.click(); } @Override - public void scroll(GameContainer container, StateBasedGame game, int newValue) { - MenuState.BEATMAP.scroll(container, game, newValue); + public void mouseWheelMoved(int newValue) { + MenuState.BEATMAP.mouseWheelMoved(newValue); } }, /** The game mod selection screen. */ MODS (new Button[] { Button.RESET_MODS, Button.CLOSE }) { @Override - public String[] getTitle(GameContainer container, StateBasedGame game) { + public String[] getTitle() { return new String[] { "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun." }; } @Override - protected float getBaseY(GameContainer container, StateBasedGame game) { - return container.getHeight() * 2f / 3; + protected float getBaseY(DisplayContainer displayContainer) { + return displayContainer.height * 2f / 3; } @Override - public void enter(GameContainer container, StateBasedGame game) { - super.enter(container, game); - for (GameMod mod : GameMod.values()) + public void enter() { + super.enter(); + for (GameMod mod : GameMod.values()) { mod.resetHover(); + } } @Override - public void leave(GameContainer container, StateBasedGame game) { - Button.CLOSE.click(container, game); + public void leave() { + Button.CLOSE.click(); } @Override - public void draw(GameContainer container, StateBasedGame game, Graphics g) { - int width = container.getWidth(); - int height = container.getHeight(); - + public void render(Graphics g) { // score multiplier (TODO: fade in color changes) float mult = GameMod.getScoreMultiplier(); String multString = String.format("Score Multiplier: %.2fx", mult); Color multColor = (mult == 1f) ? Color.white : (mult > 1f) ? Color.green : Color.red; - float multY = Fonts.LARGE.getLineHeight() * 2 + height * 0.06f; + float multY = Fonts.LARGE.getLineHeight() * 2 + displayContainer.height * 0.06f; Fonts.LARGE.drawString( - (width - Fonts.LARGE.getWidth(multString)) / 2f, + (displayContainer.width - Fonts.LARGE.getWidth(multString)) / 2f, multY, multString, multColor); // category text @@ -232,27 +227,28 @@ public class ButtonMenu extends BasicGameState { for (GameMod mod : GameMod.values()) mod.draw(); - super.draw(container, game, g); + super.render(g); } @Override - public void update(GameContainer container, int delta, int mouseX, int mouseY) { - super.update(container, delta, mouseX, mouseY); + public void preRenderUpdate() { + super.preRenderUpdate(); GameMod hoverMod = null; for (GameMod mod : GameMod.values()) { - mod.hoverUpdate(delta, mod.isActive()); - if (hoverMod == null && mod.contains(mouseX, mouseY)) + mod.hoverUpdate(displayContainer.renderDelta, mod.isActive()); + if (hoverMod == null && mod.contains(displayContainer.mouseX, displayContainer.mouseY)) hoverMod = mod; } // tooltips - if (hoverMod != null) - UI.updateTooltip(delta, hoverMod.getDescription(), true); + if (hoverMod != null) { + UI.updateTooltip(displayContainer.renderDelta, hoverMod.getDescription(), true); + } } @Override - public void keyPress(GameContainer container, StateBasedGame game, int key, char c) { - super.keyPress(container, game, key, c); + public void keyPressed(int key, char c) { + super.keyPressed(key, c); for (GameMod mod : GameMod.values()) { if (key == mod.getKey()) { mod.toggle(true); @@ -262,8 +258,8 @@ public class ButtonMenu extends BasicGameState { } @Override - public void click(GameContainer container, StateBasedGame game, int cx, int cy) { - super.click(container, game, cx, cy); + public void mousePressed(int cx, int cy) { + super.mousePressed(cx, cy); for (GameMod mod : GameMod.values()) { if (mod.contains(cx, cy)) { boolean prevState = mod.isActive(); @@ -276,11 +272,14 @@ public class ButtonMenu extends BasicGameState { } @Override - public void scroll(GameContainer container, StateBasedGame game, int newValue) { - MenuState.BEATMAP.scroll(container, game, newValue); + public void mouseWheelMoved(int newValue) { + MenuState.BEATMAP.mouseWheelMoved(newValue); } }; + public static DisplayContainer displayContainer; + public static InstanceContainer instanceContainer; + /** The buttons in the state. */ private final Button[] buttons; @@ -306,15 +305,10 @@ public class ButtonMenu extends BasicGameState { /** * Initializes the menu state. - * @param container the game container - * @param game the game - * @param button the center button image - * @param buttonL the left button image - * @param buttonR the right button image */ - public void init(GameContainer container, StateBasedGame game, Image button, Image buttonL, Image buttonR) { - float center = container.getWidth() / 2f; - float baseY = getBaseY(container, game); + public void revalidate(Image button, Image buttonL, Image buttonR) { + float center = displayContainer.width / 2; + float baseY = getBaseY(displayContainer); float offsetY = button.getHeight() * 1.25f; menuButtons = new MenuButton[buttons.length]; @@ -328,25 +322,21 @@ public class ButtonMenu extends BasicGameState { /** * Returns the base Y coordinate for the buttons. - * @param container the game container - * @param game the game */ - protected float getBaseY(GameContainer container, StateBasedGame game) { - float baseY = container.getHeight() * 0.2f; - baseY += ((getTitle(container, game).length - 1) * Fonts.LARGE.getLineHeight()); + protected float getBaseY(DisplayContainer displayContainer) { + float baseY = displayContainer.height * 0.2f; + baseY += ((getTitle().length - 1) * Fonts.LARGE.getLineHeight()); return baseY; } /** * Draws the title and buttons to the graphics context. - * @param container the game container - * @param game the game * @param g the graphics context */ - public void draw(GameContainer container, StateBasedGame game, Graphics g) { + public void render(Graphics g) { // draw title if (actualTitle != null) { - float marginX = container.getWidth() * 0.015f, marginY = container.getHeight() * 0.01f; + float marginX = displayContainer.width * 0.015f, marginY = displayContainer.height * 0.01f; int lineHeight = Fonts.LARGE.getLineHeight(); for (int i = 0, size = actualTitle.size(); i < size; i++) Fonts.LARGE.drawString(marginX, marginY + (i * lineHeight), actualTitle.get(i), Color.white); @@ -361,17 +351,13 @@ public class ButtonMenu extends BasicGameState { /** * Updates the menu state. - * @param container the game container - * @param delta the delta interval - * @param mouseX the mouse x coordinate - * @param mouseY the mouse y coordinate */ - public void update(GameContainer container, int delta, int mouseX, int mouseY) { - float center = container.getWidth() / 2f; - boolean centerOffsetUpdated = centerOffset.update(delta); + public void preRenderUpdate() { + float center = displayContainer.width / 2f; + boolean centerOffsetUpdated = centerOffset.update(displayContainer.renderDelta); float centerOffsetX = centerOffset.getValue(); for (int i = 0; i < buttons.length; i++) { - menuButtons[i].hoverUpdate(delta, mouseX, mouseY); + menuButtons[i].hoverUpdate(displayContainer.renderDelta, displayContainer.mouseX, displayContainer.mouseY); // move button to center if (centerOffsetUpdated) @@ -381,15 +367,11 @@ public class ButtonMenu extends BasicGameState { /** * Processes a mouse click action. - * @param container the game container - * @param game the game - * @param cx the x coordinate - * @param cy the y coordinate */ - public void click(GameContainer container, StateBasedGame game, int cx, int cy) { + public void mousePressed(int x, int y) { for (int i = 0; i < buttons.length; i++) { - if (menuButtons[i].contains(cx, cy)) { - buttons[i].click(container, game); + if (menuButtons[i].contains(x, y)) { + buttons[i].click(); break; } } @@ -397,42 +379,34 @@ public class ButtonMenu extends BasicGameState { /** * Processes a key press action. - * @param container the game container - * @param game the game * @param key the key code that was pressed (see {@link org.newdawn.slick.Input}) * @param c the character of the key that was pressed */ - public void keyPress(GameContainer container, StateBasedGame game, int key, char c) { + public void keyPressed(int key, char c) { int index = Character.getNumericValue(c) - 1; if (index >= 0 && index < buttons.length) - buttons[index].click(container, game); + buttons[index].click(); } /** * Retrieves the title strings for the menu state (via override). - * @param container the game container - * @param game the game */ - public String[] getTitle(GameContainer container, StateBasedGame game) { return new String[0]; } + public String[] getTitle() { return new String[0]; } /** * Processes a mouse wheel movement. - * @param container the game container - * @param game the game * @param newValue the amount that the mouse wheel moved */ - public void scroll(GameContainer container, StateBasedGame game, int newValue) { + public void mouseWheelMoved(int newValue) { UI.changeVolume((newValue < 0) ? -1 : 1); } /** * Processes a state enter request. - * @param container the game container - * @param game the game */ - public void enter(GameContainer container, StateBasedGame game) { - float center = container.getWidth() / 2f; - float centerOffsetX = container.getWidth() * OFFSET_WIDTH_RATIO; + public void enter() { + float center = displayContainer.width / 2f; + float centerOffsetX = displayContainer.width * OFFSET_WIDTH_RATIO; centerOffset = new AnimatedValue(700, centerOffsetX, 0, AnimationEquation.OUT_BOUNCE); for (int i = 0; i < buttons.length; i++) { menuButtons[i].setX(center + ((i % 2 == 0) ? centerOffsetX : centerOffsetX * -1)); @@ -440,150 +414,149 @@ public class ButtonMenu extends BasicGameState { } // create title string list - actualTitle = new ArrayList(); - String[] title = getTitle(container, game); - int maxLineWidth = (int) (container.getWidth() * 0.96f); - for (int i = 0; i < title.length; i++) { + actualTitle = new ArrayList<>(); + String[] title = getTitle(); + int maxLineWidth = (int) (displayContainer.width * 0.96f); + for (String aTitle : title) { // wrap text if too long - if (Fonts.LARGE.getWidth(title[i]) > maxLineWidth) { - List list = Fonts.wrap(Fonts.LARGE, title[i], maxLineWidth, false); + if (Fonts.LARGE.getWidth(aTitle) > maxLineWidth) { + List list = Fonts.wrap(Fonts.LARGE, aTitle, maxLineWidth, false); actualTitle.addAll(list); - } else - actualTitle.add(title[i]); + } else { + actualTitle.add(aTitle); + } } } /** * Processes a state exit request (via override). - * @param container the game container - * @param game the game */ - public void leave(GameContainer container, StateBasedGame game) {} - }; + public void leave() {} + } /** Button types. */ private enum Button { YES ("Yes", Color.green) { @Override - public void click(GameContainer container, StateBasedGame game) { - container.exit(); + public void click() { + displayContainer.exitRequested = true; } }, NO ("No", Color.red) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUBACK); - game.enterState(Opsu.STATE_MAINMENU, new EmptyTransition(), new FadeInTransition()); + displayContainer.switchState(MainMenu.class); } }, CLEAR_SCORES ("Clear local scores", Color.magenta) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUHIT); - BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP, node); - game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); + BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); + instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP, node); + displayContainer.switchState(SongMenu.class); } }, FAVORITE_ADD ("Add to Favorites", Color.blue) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUHIT); - BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); + BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); node.getBeatmapSet().setFavorite(true); - game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); + displayContainer.switchState(SongMenu.class); } }, FAVORITE_REMOVE ("Remove from Favorites", Color.blue) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUHIT); - BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); + BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); node.getBeatmapSet().setFavorite(false); - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_FAVORITE); - game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); + instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_FAVORITE); + displayContainer.switchState(SongMenu.class); } }, DELETE ("Delete...", Color.red) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUHIT); - BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); + BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); MenuState ms = (node.beatmapIndex == -1 || node.getBeatmapSet().size() == 1) ? MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT; - ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(ms, node); - game.enterState(Opsu.STATE_BUTTONMENU); + instanceContainer.provide(ButtonMenu.class).setMenuState(ms, node); + displayContainer.switchState(ButtonMenu.class); } }, CANCEL ("Cancel", Color.gray) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUBACK); - game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); + displayContainer.switchState(SongMenu.class); } }, DELETE_CONFIRM ("Yes, delete this beatmap!", Color.red) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUHIT); - BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_DELETE_CONFIRM, node); - game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); + BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); + instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_DELETE_CONFIRM, node); + displayContainer.switchState(SongMenu.class); } }, DELETE_GROUP ("Yes, delete all difficulties!", Color.red) { @Override - public void click(GameContainer container, StateBasedGame game) { - DELETE_CONFIRM.click(container, game); + public void click() { + DELETE_CONFIRM.click(); } }, DELETE_SONG ("Yes, but only this difficulty", Color.red) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUHIT); - BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_DELETE_SELECT, node); - game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); + BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); + instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_DELETE_SELECT, node); + displayContainer.switchState(SongMenu.class); } }, CANCEL_DELETE ("Nooooo! I didn't mean to!", Color.gray) { @Override - public void click(GameContainer container, StateBasedGame game) { - CANCEL.click(container, game); + public void click() { + CANCEL.click(); } }, RELOAD_CONFIRM ("Let's do it!", Color.green) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUHIT); - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.RELOAD); - game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); + instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.RELOAD); + displayContainer.switchState(SongMenu.class); } }, RELOAD_CANCEL ("Cancel", Color.red) { @Override - public void click(GameContainer container, StateBasedGame game) { - CANCEL.click(container, game); + public void click() { + CANCEL.click(); } }, DELETE_SCORE ("Delete score", Color.green) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUHIT); - ScoreData scoreData = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getScoreData(); - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.SCORE, scoreData); - game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); + ScoreData scoreData = instanceContainer.provide(ButtonMenu.class).getScoreData(); + instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.SCORE, scoreData); + displayContainer.switchState(SongMenu.class); } }, CLOSE ("Close", Color.gray) { @Override - public void click(GameContainer container, StateBasedGame game) { - CANCEL.click(container, game); + public void click() { + CANCEL.click(); } }, RESET_MODS ("Reset All Mods", Color.red) { @Override - public void click(GameContainer container, StateBasedGame game) { + public void click() { SoundController.playSound(SoundEffect.MENUCLICK); for (GameMod mod : GameMod.values()) { if (mod.isActive()) @@ -592,6 +565,9 @@ public class ButtonMenu extends BasicGameState { } }; + public static DisplayContainer displayContainer; + public static InstanceContainer instanceContainer; + /** The text to show on the button. */ private final String text; @@ -620,10 +596,8 @@ public class ButtonMenu extends BasicGameState { /** * Processes a mouse click action (via override). - * @param container the game container - * @param game the game */ - public void click(GameContainer container, StateBasedGame game) {} + public void click() {} } /** The current menu state. */ @@ -635,98 +609,89 @@ public class ButtonMenu extends BasicGameState { /** The score data to process in the state. */ private ScoreData scoreData; - // game-related variables - private GameContainer container; - private StateBasedGame game; - private Input input; - private final int state; - - public ButtonMenu(int state) { - this.state = state; + public ButtonMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) { + super(displayContainer); + Button.displayContainer = MenuState.displayContainer = displayContainer; + Button.instanceContainer = MenuState.instanceContainer = instanceContainer; } @Override - public void init(GameContainer container, StateBasedGame game) - throws SlickException { - this.container = container; - this.game = game; - this.input = container.getInput(); + public void revalidate() { + super.revalidate(); // initialize buttons Image button = GameImage.MENU_BUTTON_MID.getImage(); - button = button.getScaledCopy(container.getWidth() / 2, button.getHeight()); + button = button.getScaledCopy(displayContainer.width / 2, button.getHeight()); Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage(); Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage(); - for (MenuState ms : MenuState.values()) - ms.init(container, game, button, buttonL, buttonR); - } - - @Override - public void render(GameContainer container, StateBasedGame game, Graphics g) - throws SlickException { - g.setBackground(Color.black); - if (menuState != null) - menuState.draw(container, game, g); - } - - @Override - public void update(GameContainer container, StateBasedGame game, int delta) - throws SlickException { - UI.update(delta); - MusicController.loopTrackIfEnded(false); - if (menuState != null) - menuState.update(container, delta, input.getMouseX(), input.getMouseY()); - } - - @Override - public int getID() { return state; } - - @Override - public void mousePressed(int button, int x, int y) { - // check mouse button - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; - - if (menuState != null) - menuState.click(container, game, x, y); - } - - @Override - public void mouseWheelMoved(int newValue) { - if (menuState != null) - menuState.scroll(container, game, newValue); - } - - @Override - public void keyPressed(int key, char c) { - switch (key) { - case Input.KEY_ESCAPE: - if (menuState != null) - menuState.leave(container, game); - break; - case Input.KEY_F7: - // TODO - //Options.setNextFPS(displayContainer); - break; - case Input.KEY_F10: - Options.toggleMouseDisabled(); - break; - case Input.KEY_F12: - Utils.takeScreenShot(); - break; - default: - if (menuState != null) - menuState.keyPress(container, game, key, c); - break; + for (MenuState ms : MenuState.values()) { + ms.revalidate(button, buttonL, buttonR); } } @Override - public void enter(GameContainer container, StateBasedGame game) - throws SlickException { + public void render(Graphics g) { + super.render(g); + + g.setBackground(Color.black); + if (menuState == null) { + return; + } + menuState.render(g); + } + + @Override + public void preRenderUpdate() { + super.preRenderUpdate(); + + UI.update(displayContainer.renderDelta); + MusicController.loopTrackIfEnded(false); + menuState.preRenderUpdate(); + } + + @Override + public boolean mousePressed(int button, int x, int y) { + if (button == Input.MOUSE_MIDDLE_BUTTON) { + return false; + } + + menuState.mousePressed(x, y); + return true; + } + + @Override + public boolean mouseWheelMoved(int newValue) { + menuState.mouseWheelMoved(newValue); + return true; + } + + @Override + public boolean keyPressed(int key, char c) { + switch (key) { + case Input.KEY_ESCAPE: + menuState.leave(); + return true; + case Input.KEY_F7: + // TODO + //Options.setNextFPS(displayContainer); + return true; + case Input.KEY_F10: + Options.toggleMouseDisabled(); + return true; + case Input.KEY_F12: + Utils.takeScreenShot(); + return true; + } + menuState.keyPressed(key, c); + return true; + } + + @Override + public void enter() { + super.enter(); + UI.enter(); - if (menuState != null) - menuState.enter(container, game); + menuState.enter(); } /** diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index a2fa86dc..a91e7fef 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -20,7 +20,6 @@ package itdelatrisu.opsu.states; import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.GameImage; -import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.audio.MusicController; @@ -47,8 +46,6 @@ import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; -import org.newdawn.slick.state.transition.EasedFadeOutTransition; -import org.newdawn.slick.state.transition.FadeInTransition; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.state.BaseOpsuState; @@ -647,9 +644,8 @@ public class MainMenu extends BaseOpsuState { logoTimer = 0; break; } - // TODO - //((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.EXIT); - //game.enterState(Opsu.STATE_BUTTONMENU); + instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.EXIT); + displayContainer.switchState(ButtonMenu.class); return true; case Input.KEY_P: SoundController.playSound(SoundEffect.MENUHIT); diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 4e2557e6..3741aab4 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -783,7 +783,7 @@ public class SongMenu extends BaseOpsuState { MenuState state = focusNode.getBeatmapSet().isFavorite() ? MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP; instanceContainer.provide(ButtonMenu.class).setMenuState(state, focusNode); - // TODO d displayContainer.switchState(ButtonMenu.class); + displayContainer.switchState(ButtonMenu.class); } return; } @@ -1053,7 +1053,7 @@ public class SongMenu extends BaseOpsuState { } else { // score management instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.SCORE, focusScores[rank]); - // TODO d displayContainer.switchState(ButtonMenu.class); + displayContainer.switchState(ButtonMenu.class); } return true; } @@ -1084,18 +1084,17 @@ public class SongMenu extends BaseOpsuState { searchTimer = SEARCH_DELAY; searchTransitionTimer = 0; searchResultString = null; - } else { + } else*/ { // return to main menu SoundController.playSound(SoundEffect.MENUBACK); - ((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); - game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition()); + instanceContainer.provide(MainMenu.class).reset(); // TODO d is this needed + displayContainer.switchState(MainMenu.class); } - */ return true; case Input.KEY_F1: SoundController.playSound(SoundEffect.MENUHIT); instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.MODS); - // TODO d displayContainer.switchState(ButtonMenu.class); + displayContainer.switchState(ButtonMenu.class); return true; case Input.KEY_F2: if (focusNode == null) @@ -1124,7 +1123,7 @@ public class SongMenu extends BaseOpsuState { MenuState state = focusNode.getBeatmapSet().isFavorite() ? MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP; instanceContainer.provide(ButtonMenu.class).setMenuState(state, focusNode); - // TODO d displayContainer.switchState(ButtonMenu.class); + displayContainer.switchState(ButtonMenu.class); return true; case Input.KEY_F5: SoundController.playSound(SoundEffect.MENUHIT); @@ -1132,7 +1131,7 @@ public class SongMenu extends BaseOpsuState { reloadBeatmaps(false); else { instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.RELOAD); - // TODO d displayContainer.switchState(ButtonMenu.class); + displayContainer.switchState(ButtonMenu.class); } return true; case Input.KEY_DELETE: @@ -1143,7 +1142,7 @@ public class SongMenu extends BaseOpsuState { MenuState ms = (focusNode.beatmapIndex == -1 || focusNode.getBeatmapSet().size() == 1) ? MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT; instanceContainer.provide(ButtonMenu.class).setMenuState(ms, focusNode); - // TODO d displayContainer.switchState(ButtonMenu.class); + displayContainer.switchState(ButtonMenu.class); } return true; case Input.KEY_F7: diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index 8fd39377..499d5221 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -17,6 +17,7 @@ */ package yugecin.opsudance.core.inject; +import itdelatrisu.opsu.states.ButtonMenu; import itdelatrisu.opsu.states.MainMenu; import itdelatrisu.opsu.states.SongMenu; import itdelatrisu.opsu.states.Splash; @@ -56,6 +57,7 @@ public class OpsuDanceInjector extends Injector { bind(Splash.class).asEagerSingleton(); bind(MainMenu.class).asEagerSingleton(); + bind(ButtonMenu.class).asEagerSingleton(); bind(SongMenu.class).asEagerSingleton(); } From a02613bb7644c8849230e2246312456d3970722a Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 19:22:14 +0100 Subject: [PATCH 65/92] pause music on exception --- src/yugecin/opsudance/OpsuDance.java | 3 +++ .../opsudance/core/DisplayContainer.java | 22 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 42fa75de..06a2f206 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -69,6 +69,7 @@ public class OpsuDance { } while (rungame()); + container.teardownAL(); closeSingleInstanceSocket(); DBController.closeConnections(); @@ -78,6 +79,7 @@ public class OpsuDance { private boolean rungame() { try { container.setup(); + container.resume(); } catch (Exception e) { ErrorHandler.error("could not initialize GL", e).preventContinue().show(); return false; @@ -89,6 +91,7 @@ public class OpsuDance { caughtException = e; } container.teardown(); + container.pause(); return caughtException != null && ErrorHandler.error("update/render error", caughtException).show().shouldIgnoreAndContinue(); } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 82b39997..ee8912d7 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -19,8 +19,8 @@ package yugecin.opsudance.core; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.Options; +import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.ui.Fonts; -import org.lwjgl.LWJGLException; import org.lwjgl.Sys; import org.lwjgl.openal.AL; import org.lwjgl.opengl.Display; @@ -95,6 +95,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen private long lastFrame; + private boolean wasMusicPlaying; + private String glVersion; private String glVendor; @@ -150,7 +152,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen } - public void run() throws LWJGLException { + public void run() throws Exception { while(!exitRequested && !(Display.isCloseRequested() && state.onCloseRequest())) { delta = getDelta(); @@ -211,9 +213,25 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen public void teardown() { Display.destroy(); + } + + public void teardownAL() { AL.destroy(); } + public void pause() { + wasMusicPlaying = MusicController.isPlaying(); + if (wasMusicPlaying) { + MusicController.pause(); + } + } + + public void resume() { + if (wasMusicPlaying) { + MusicController.resume(); + } + } + public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception { if (this.width == width && this.height == height) { Display.setFullscreen(fullscreen); From 156026bd9b3853275018120277b36483904ca062 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 19:33:58 +0100 Subject: [PATCH 66/92] destroy all images when closing --- src/itdelatrisu/opsu/GameImage.java | 31 +++++++++++++++++++ .../opsudance/core/DisplayContainer.java | 2 ++ 2 files changed, 33 insertions(+) diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index 21d10812..de479156 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -461,6 +461,37 @@ public enum GameImage { } } + public static void destroyAll() { + for (GameImage img : GameImage.values()) { + destroyAll(img.defaultImages); + destroyImage(img.defaultImage); + destroyAll(img.skinImages); + destroyImage(img.skinImage); + img.isSkinned = false; + img.defaultImages = img.skinImages = null; + img.defaultImage = img.skinImage = null; + } + } + + public static void destroyAll(Image[] imgs) { + if (imgs == null) { + return; + } + for (Image i : imgs) { + destroyImage(i); + } + } + + public static void destroyImage(Image image) { + if (image == null) { + return; + } + try { + image.destroy(); + } catch (SlickException ignored) { + } + } + /** * Returns the bitmask image type from a type string. * @param type the type string diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index ee8912d7..4ede5648 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -212,6 +212,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen } public void teardown() { + InternalTextureLoader.get().clear(); + GameImage.destroyAll(); Display.destroy(); } From 0eac32505b2bd7c8ced54a3beb9088b2f0ba2e1e Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 19:40:31 +0100 Subject: [PATCH 67/92] destroy even more images when closing --- src/itdelatrisu/opsu/GameData.java | 12 ++++- src/itdelatrisu/opsu/GameImage.java | 30 +++---------- src/itdelatrisu/opsu/beatmap/Beatmap.java | 9 ++++ .../opsudance/core/DisplayContainer.java | 6 ++- src/yugecin/opsudance/utils/SlickUtil.java | 44 +++++++++++++++++++ 5 files changed, 74 insertions(+), 27 deletions(-) create mode 100644 src/yugecin/opsudance/utils/SlickUtil.java diff --git a/src/itdelatrisu/opsu/GameData.java b/src/itdelatrisu/opsu/GameData.java index e35a079e..9138d725 100644 --- a/src/itdelatrisu/opsu/GameData.java +++ b/src/itdelatrisu/opsu/GameData.java @@ -42,7 +42,7 @@ import org.newdawn.slick.Animation; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; -import yugecin.opsudance.Dancer; +import yugecin.opsudance.utils.SlickUtil; /** * Holds game data and renders all related elements. @@ -101,8 +101,16 @@ public class GameData { * This does NOT destroy images, so be careful of memory leaks! */ public static void clearReferences() { - for (Grade grade : Grade.values()) + for (Grade grade : Grade.values()) { grade.menuImage = null; + } + } + + public static void destroyImages() { + for (Grade grade : Grade.values()) { + SlickUtil.destroyImage(grade.menuImage); + grade.menuImage = null; + } } /** diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index de479156..cd1dd463 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -28,6 +28,7 @@ import org.newdawn.slick.Animation; import org.newdawn.slick.Image; import org.newdawn.slick.SlickException; import org.newdawn.slick.util.ResourceLoader; +import yugecin.opsudance.utils.SlickUtil; /** * Game images. @@ -461,37 +462,18 @@ public enum GameImage { } } - public static void destroyAll() { + public static void destroyImages() { for (GameImage img : GameImage.values()) { - destroyAll(img.defaultImages); - destroyImage(img.defaultImage); - destroyAll(img.skinImages); - destroyImage(img.skinImage); + SlickUtil.destroyImages(img.defaultImages); + SlickUtil.destroyImage(img.defaultImage); + SlickUtil.destroyImages(img.skinImages); + SlickUtil.destroyImage(img.skinImage); img.isSkinned = false; img.defaultImages = img.skinImages = null; img.defaultImage = img.skinImage = null; } } - public static void destroyAll(Image[] imgs) { - if (imgs == null) { - return; - } - for (Image i : imgs) { - destroyImage(i); - } - } - - public static void destroyImage(Image image) { - if (image == null) { - return; - } - try { - image.destroy(); - } catch (SlickException ignored) { - } - } - /** * Returns the bitmask image type from a type string. * @param type the type string diff --git a/src/itdelatrisu/opsu/beatmap/Beatmap.java b/src/itdelatrisu/opsu/beatmap/Beatmap.java index a87cc236..55a34ab5 100644 --- a/src/itdelatrisu/opsu/beatmap/Beatmap.java +++ b/src/itdelatrisu/opsu/beatmap/Beatmap.java @@ -22,6 +22,7 @@ import itdelatrisu.opsu.Options; import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedList; import java.util.Map; @@ -59,6 +60,14 @@ public class Beatmap implements Comparable { */ public static void clearBackgroundImageCache() { bgImageCache.clear(); } + public static void destroyBackgroundImageCache() { + Collection values = bgImageCache.values(); + for (ImageLoader value : values) { + value.destroy(); + } + bgImageCache.clear(); + } + /** The OSU File object associated with this beatmap. */ private File file; diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 4ede5648..6c6242fd 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -17,9 +17,11 @@ */ package yugecin.opsudance.core; +import itdelatrisu.opsu.GameData; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.audio.MusicController; +import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.ui.Fonts; import org.lwjgl.Sys; import org.lwjgl.openal.AL; @@ -213,7 +215,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen public void teardown() { InternalTextureLoader.get().clear(); - GameImage.destroyAll(); + GameImage.destroyImages(); + GameData.Grade.destroyImages(); + Beatmap.destroyBackgroundImageCache(); Display.destroy(); } diff --git a/src/yugecin/opsudance/utils/SlickUtil.java b/src/yugecin/opsudance/utils/SlickUtil.java new file mode 100644 index 00000000..1123caf0 --- /dev/null +++ b/src/yugecin/opsudance/utils/SlickUtil.java @@ -0,0 +1,44 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.utils; + +import org.newdawn.slick.Image; +import org.newdawn.slick.SlickException; + +public class SlickUtil { + + public static void destroyImages(Image[] imgs) { + if (imgs == null) { + return; + } + for (Image i : imgs) { + destroyImage(i); + } + } + + public static void destroyImage(Image image) { + if (image == null) { + return; + } + try { + image.destroy(); + } catch (SlickException ignored) { + } + } + +} From 8d05a8893b76383330e6bfeff992546fb46e4ead Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 19:41:27 +0100 Subject: [PATCH 68/92] save options and delete temp dir on close --- src/yugecin/opsudance/OpsuDance.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 06a2f206..e8b5b28a 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -71,9 +71,11 @@ public class OpsuDance { while (rungame()); container.teardownAL(); + Options.saveOptions(); closeSingleInstanceSocket(); DBController.closeConnections(); DownloadList.get().cancelAllDownloads(); + Utils.deleteDirectory(Options.TEMP_DIR); } private boolean rungame() { From c4560ae9f1f201c40f09b108e1182b80ee314fc4 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 19:44:54 +0100 Subject: [PATCH 69/92] stop loading stuff when close is requested in splash state --- src/itdelatrisu/opsu/states/Splash.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/itdelatrisu/opsu/states/Splash.java b/src/itdelatrisu/opsu/states/Splash.java index 73445a75..c832408f 100644 --- a/src/itdelatrisu/opsu/states/Splash.java +++ b/src/itdelatrisu/opsu/states/Splash.java @@ -37,6 +37,7 @@ import java.io.File; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; +import org.newdawn.slick.util.Log; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.state.BaseOpsuState; @@ -182,6 +183,19 @@ public class Splash extends BaseOpsuState { } } + @Override + public boolean onCloseRequest() { + if (thread != null && thread.isAlive()) { + thread.interrupt(); + try { + thread.join(); + } catch (InterruptedException e) { + Log.warn("InterruptedException while waiting for splash thread to die", e); + } + } + return true; + } + @Override public boolean keyPressed(int key, char c) { if (key != Input.KEY_ESCAPE) { From f21484135934450525ded2686809dc4d736022d8 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 21:48:27 +0100 Subject: [PATCH 70/92] mouseDragged event --- src/yugecin/opsudance/core/DisplayContainer.java | 4 +++- src/yugecin/opsudance/core/state/BaseOpsuState.java | 5 +++++ src/yugecin/opsudance/core/state/OpsuState.java | 5 +++++ src/yugecin/opsudance/states/EmptyRedState.java | 5 +++++ src/yugecin/opsudance/states/EmptyState.java | 5 +++++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 6c6242fd..c827a2e0 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -386,7 +386,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen public void mouseMoved(int oldx, int oldy, int newx, int newy) { } @Override - public void mouseDragged(int oldx, int oldy, int newx, int newy) { } + public void mouseDragged(int oldx, int oldy, int newx, int newy) { + state.mouseDragged(oldx, oldy, newx, newy); + } @Override public void setInput(Input input) { } diff --git a/src/yugecin/opsudance/core/state/BaseOpsuState.java b/src/yugecin/opsudance/core/state/BaseOpsuState.java index 72ebc54b..8c13315d 100644 --- a/src/yugecin/opsudance/core/state/BaseOpsuState.java +++ b/src/yugecin/opsudance/core/state/BaseOpsuState.java @@ -107,6 +107,11 @@ public abstract class BaseOpsuState implements OpsuState, EventListener BaseOpsuState dump\n"); diff --git a/src/yugecin/opsudance/core/state/OpsuState.java b/src/yugecin/opsudance/core/state/OpsuState.java index 37902c0b..0a0a65ca 100644 --- a/src/yugecin/opsudance/core/state/OpsuState.java +++ b/src/yugecin/opsudance/core/state/OpsuState.java @@ -58,4 +58,9 @@ public interface OpsuState extends ErrorDumpable { */ boolean mouseReleased(int button, int x, int y); + /** + * @return false to stop event bubbling + */ + boolean mouseDragged(int oldx, int oldy, int newx, int newy); + } diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index a4102ff3..b8d049ad 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -100,6 +100,11 @@ public class EmptyRedState implements OpsuState { return false; } + @Override + public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { + return false; + } + @Override public void writeErrorDump(StringWriter dump) { dump.append("> EmptyRedState dump\n"); diff --git a/src/yugecin/opsudance/states/EmptyState.java b/src/yugecin/opsudance/states/EmptyState.java index 28703a57..6a75f783 100644 --- a/src/yugecin/opsudance/states/EmptyState.java +++ b/src/yugecin/opsudance/states/EmptyState.java @@ -92,6 +92,11 @@ public class EmptyState implements OpsuState { return false; } + @Override + public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { + return false; + } + @Override public void writeErrorDump(StringWriter dump) { dump.append("> EmptyState dump\n"); From 6f7e4242da2f836cf8ec82dbef551854a049e530 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 21:55:06 +0100 Subject: [PATCH 71/92] correct red color for bubble notifs --- src/itdelatrisu/opsu/Options.java | 2 +- src/yugecin/opsudance/events/BubbleNotificationEvent.java | 2 +- src/yugecin/opsudance/states/EmptyRedState.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index d810feba..2c033336 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -1372,7 +1372,7 @@ public class Options { try { container.setDisplayMode(width, height, isFullscreen()); } catch (Exception e) { - container.eventBus.post(new BubbleNotificationEvent("Failed to change resolution", BubbleNotificationEvent.COLOR_RED)); + container.eventBus.post(new BubbleNotificationEvent("Failed to change resolution", BubbleNotificationEvent.COMMONCOLOR_RED)); Log.error("Failed to set display mode.", e); } diff --git a/src/yugecin/opsudance/events/BubbleNotificationEvent.java b/src/yugecin/opsudance/events/BubbleNotificationEvent.java index a4abdd51..8ae2b1de 100644 --- a/src/yugecin/opsudance/events/BubbleNotificationEvent.java +++ b/src/yugecin/opsudance/events/BubbleNotificationEvent.java @@ -24,7 +24,7 @@ public class BubbleNotificationEvent { public static final Color COMMONCOLOR_GREEN = new Color(98, 131, 59); public static final Color COMMONCOLOR_WHITE = new Color(220, 220, 220); public static final Color COMMONCOLOR_PURPLE = new Color(94, 46, 149); - public static final Color COLOR_RED = new Color(178, 62, 41); + public static final Color COMMONCOLOR_RED = new Color(141, 49, 16); public static final Color COLOR_ORANGE = new Color(138, 72, 51); public final String message; diff --git a/src/yugecin/opsudance/states/EmptyRedState.java b/src/yugecin/opsudance/states/EmptyRedState.java index b8d049ad..95c6427c 100644 --- a/src/yugecin/opsudance/states/EmptyRedState.java +++ b/src/yugecin/opsudance/states/EmptyRedState.java @@ -74,7 +74,7 @@ public class EmptyRedState implements OpsuState { @Override public boolean keyPressed(int key, char c) { - displayContainer.eventBus.post(new BubbleNotificationEvent("this is a bubble notification... bubbly bubbly bubbly linewraaaaaaaaaap", BubbleNotificationEvent.COLOR_RED)); + displayContainer.eventBus.post(new BubbleNotificationEvent("this is a bubble notification... bubbly bubbly bubbly linewraaaaaaaaaap", BubbleNotificationEvent.COMMONCOLOR_RED)); return false; } From 36cfe3813a8a7f07682e8a540c3ed59aa2781793 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 22:35:57 +0100 Subject: [PATCH 72/92] taking over components --- src/org/newdawn/slick/gui/TextField.java | 526 +++++------------- .../core/components/ActionListener.java | 24 + .../opsudance/core/components/Component.java | 60 ++ .../core/state/ComplexOpsuState.java | 124 +++++ 4 files changed, 334 insertions(+), 400 deletions(-) create mode 100644 src/yugecin/opsudance/core/components/ActionListener.java create mode 100644 src/yugecin/opsudance/core/components/Component.java create mode 100644 src/yugecin/opsudance/core/state/ComplexOpsuState.java diff --git a/src/org/newdawn/slick/gui/TextField.java b/src/org/newdawn/slick/gui/TextField.java index 6696ff9f..a989d2c4 100644 --- a/src/org/newdawn/slick/gui/TextField.java +++ b/src/org/newdawn/slick/gui/TextField.java @@ -34,232 +34,70 @@ import org.newdawn.slick.Font; import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; import org.newdawn.slick.geom.Rectangle; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.components.ActionListener; +import yugecin.opsudance.core.components.Component; /** * A single text field supporting text entry * * @author kevin */ -@SuppressWarnings("unused") -public class TextField extends AbstractComponent { - /** The key repeat interval */ +public class TextField extends Component { + private static final int INITIAL_KEY_REPEAT_INTERVAL = 400; - /** The key repeat interval */ private static final int KEY_REPEAT_INTERVAL = 50; + + private final DisplayContainer displayContainer; - /** The width of the field */ - private int width; - - /** The height of the field */ - private int height; - - /** The location in the X coordinate */ - protected int x; - - /** The location in the Y coordinate */ - protected int y; - - /** The maximum number of characters allowed to be input */ + private String value = ""; + private Font font; private int maxCharacter = 10000; - /** The value stored in the text field */ - private String value = ""; + private Color borderCol = Color.white; + private Color textCol = Color.white; + private Color backgroundCol = new Color(0, 0, 0, 0.5f); - /** The font used to render text in the field */ - private Font font; - - /** The border color - null if no border */ - private Color border = Color.white; - - /** The text color */ - private Color text = Color.white; - - /** The background color - null if no background */ - private Color background = new Color(0, 0, 0, 0.5f); - - /** The current cursor position */ private int cursorPos; - - /** True if the cursor should be visible */ - private boolean visibleCursor = true; - - /** The last key pressed */ private int lastKey = -1; - - /** The last character pressed */ private char lastChar = 0; - - /** The time since last key repeat */ private long repeatTimer; - - /** The text before the paste in */ - private String oldText; - - /** The cursor position before the paste */ - private int oldCursorPos; - - /** True if events should be consumed by the field */ - private boolean consume = true; - - /** - * Create a new text field - * - * @param container - * The container rendering this field - * @param font - * The font to use in the text field - * @param x - * The x coordinate of the top left corner of the text field - * @param y - * The y coordinate of the top left corner of the text field - * @param width - * The width of the text field - * @param height - * The height of the text field - * @param listener - * The listener to add to the text field - */ - public TextField(GUIContext container, Font font, int x, int y, int width, - int height, ComponentListener listener) { - this(container,font,x,y,width,height); - addListener(listener); - } - - /** - * Create a new text field - * - * @param container - * The container rendering this field - * @param font - * The font to use in the text field - * @param x - * The x coordinate of the top left corner of the text field - * @param y - * The y coordinate of the top left corner of the text field - * @param width - * The width of the text field - * @param height - * The height of the text field - */ - public TextField(GUIContext container, Font font, int x, int y, int width, - int height) { - super(container); + private ActionListener listener; + + public TextField(DisplayContainer displayContainer, Font font, int x, int y, int width, int height) { + this.displayContainer = displayContainer; this.font = font; - - setLocation(x, y); + this.x = x; + this.y = y; this.width = width; this.height = height; } - /** - * Indicate if the input events should be consumed by this field - * - * @param consume True if events should be consumed by this field - */ - public void setConsumeEvents(boolean consume) { - this.consume = consume; + public void setListener(ActionListener listener) { + this.listener = listener; } - - /** - * Deactivate the key input handling for this field - */ - public void deactivate() { - setFocus(false); + + public void setBorderColor(Color border) { + this.borderCol = border; } - - /** - * Moves the component. - * - * @param x - * X coordinate - * @param y - * Y coordinate - */ + + public void setTextColor(Color text) { + this.textCol = text; + } + + public void setBackgroundColor(Color background) { + this.backgroundCol = background; + } + @Override - public void setLocation(int x, int y) { - this.x = x; - this.y = y; + public boolean isFocusable() { + return true; } - /** - * Returns the position in the X coordinate - * - * @return x - */ - @Override - public int getX() { - return x; - } - - /** - * Returns the position in the Y coordinate - * - * @return y - */ - @Override - public int getY() { - return y; - } - - /** - * Get the width of the component - * - * @return The width of the component - */ - @Override - public int getWidth() { - return width; - } - - /** - * Get the height of the component - * - * @return The height of the component - */ - @Override - public int getHeight() { - return height; - } - - /** - * Set the background color. Set to null to disable the background - * - * @param color - * The color to use for the background - */ - public void setBackgroundColor(Color color) { - background = color; - } - - /** - * Set the border color. Set to null to disable the border - * - * @param color - * The color to use for the border - */ - public void setBorderColor(Color color) { - border = color; - } - - /** - * Set the text color. - * - * @param color - * The color to use for the text - */ - public void setTextColor(Color color) { - text = color; - } - - /** - * @see org.newdawn.slick.gui.AbstractComponent#render(org.newdawn.slick.gui.GUIContext, - * org.newdawn.slick.Graphics) - */ - @Override - public void render(GUIContext container, Graphics g) { + public void render(Graphics g) { if (lastKey != -1) { - if (input.isKeyDown(lastKey)) { + if (displayContainer.input.isKeyDown(lastKey)) { if (repeatTimer < System.currentTimeMillis()) { repeatTimer = System.currentTimeMillis() + KEY_REPEAT_INTERVAL; keyPressed(lastKey, lastChar); @@ -274,11 +112,11 @@ public class TextField extends AbstractComponent { // Someone could have set a color for me to blend... Color clr = g.getColor(); - if (background != null) { - g.setColor(background.multiply(clr)); + if (backgroundCol != null) { + g.setColor(backgroundCol.multiply(clr)); g.fillRect(x, y, width, height); } - g.setColor(text.multiply(clr)); + g.setColor(textCol.multiply(clr)); Font temp = g.getFont(); int cpos = font.getWidth(value.substring(0, cursorPos)); @@ -291,14 +129,14 @@ public class TextField extends AbstractComponent { g.setFont(font); g.drawString(value, x + 1, y + 1); - if (hasFocus() && visibleCursor) { - g.drawString("_", x + 1 + cpos + 2, y + 1); + if (focused) { + g.drawString("|", x + 1 + cpos + 2, y + 1); } g.translate(-tx - 2, 0); - if (border != null) { - g.setColor(border.multiply(clr)); + if (borderCol != null) { + g.setColor(borderCol.multiply(clr)); g.drawRect(x, y, width, height); } g.setColor(clr); @@ -307,21 +145,10 @@ public class TextField extends AbstractComponent { g.setClip(oldClip); } - /** - * Get the value in the text field - * - * @return The value in the text field - */ public String getText() { return value; } - /** - * Set the value to be displayed in the text field - * - * @param value - * The value to be displayed in the text field - */ public void setText(String value) { this.value = value; if (cursorPos > value.length()) { @@ -329,35 +156,6 @@ public class TextField extends AbstractComponent { } } - /** - * Set the position of the cursor - * - * @param pos - * The new position of the cursor - */ - public void setCursorPos(int pos) { - cursorPos = pos; - if (cursorPos > value.length()) { - cursorPos = value.length(); - } - } - - /** - * Indicate whether the mouse cursor should be visible or not - * - * @param visibleCursor - * True if the mouse cursor should be visible - */ - public void setCursorVisible(boolean visibleCursor) { - this.visibleCursor = visibleCursor; - } - - /** - * Set the length of the allowed input - * - * @param length - * The length of the allowed input - */ public void setMaxLength(int length) { maxCharacter = length; if (value.length() > maxCharacter) { @@ -365,173 +163,101 @@ public class TextField extends AbstractComponent { } } - /** - * Do the paste into the field, overrideable for custom behaviour - * - * @param text The text to be pasted in - */ protected void doPaste(String text) { - recordOldPosition(); - for (int i=0;i 0) { + cursorPos--; + } + // Nobody more will be notified + if (consume) { + container.getInput().consumeEvent(); + } + */ } else if (key == Input.KEY_RIGHT) { /* + if (cursorPos < value.length()) { + cursorPos++; + } + // Nobody more will be notified + if (consume) { + container.getInput().consumeEvent(); + } + */ } else if (key == Input.KEY_BACK) { + if ((cursorPos > 0) && (value.length() > 0)) { + if (displayContainer.input.isKeyDown(Input.KEY_LCONTROL) || displayContainer.input.isKeyDown(Input.KEY_RCONTROL)) { + int sp = 0; + boolean startSpace = Character.isWhitespace(value.charAt(cursorPos - 1)); + boolean charSeen = false; + for (int i = cursorPos - 1; i >= 0; i--) { + boolean isSpace = Character.isWhitespace(value.charAt(i)); + if (!startSpace && isSpace) { + sp = i; + break; + } else if (startSpace) { + if (charSeen && isSpace) { + sp = i + 1; + break; + } else if (!charSeen && !isSpace) + charSeen = true; + } + } + if (cursorPos < value.length()) + value = value.substring(0, sp) + value.substring(cursorPos); + else + value = value.substring(0, sp); + cursorPos = sp; + } else { + if (cursorPos < value.length()) { + value = value.substring(0, cursorPos - 1) + + value.substring(cursorPos); + } else { + value = value.substring(0, cursorPos - 1); } - return; - } */ - - // alt and control keys don't come through here - /* if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) { - return; - } */ - if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) { - return; - } - } - - if (lastKey != key) { - lastKey = key; - repeatTimer = System.currentTimeMillis() + INITIAL_KEY_REPEAT_INTERVAL; - } else { - repeatTimer = System.currentTimeMillis() + KEY_REPEAT_INTERVAL; - } - lastChar = c; - - if (key == Input.KEY_LEFT) { /* - if (cursorPos > 0) { cursorPos--; } - // Nobody more will be notified - if (consume) { - container.getInput().consumeEvent(); - } - */ } else if (key == Input.KEY_RIGHT) { /* - if (cursorPos < value.length()) { - cursorPos++; - } - // Nobody more will be notified - if (consume) { - container.getInput().consumeEvent(); - } - */ } else if (key == Input.KEY_BACK) { - if ((cursorPos > 0) && (value.length() > 0)) { - if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) { - int sp = 0; - boolean startSpace = Character.isWhitespace(value.charAt(cursorPos - 1)); - boolean charSeen = false; - for (int i = cursorPos - 1; i >= 0; i--) { - boolean isSpace = Character.isWhitespace(value.charAt(i)); - if (!startSpace && isSpace) { - sp = i; - break; - } else if (startSpace) { - if (charSeen && isSpace) { - sp = i + 1; - break; - } else if (!charSeen && !isSpace) - charSeen = true; - } - } - if (cursorPos < value.length()) - value = value.substring(0, sp) + value.substring(cursorPos); - else - value = value.substring(0, sp); - cursorPos = sp; - } else { - if (cursorPos < value.length()) { - value = value.substring(0, cursorPos - 1) - + value.substring(cursorPos); - } else { - value = value.substring(0, cursorPos - 1); - } - cursorPos--; - } - } - // Nobody more will be notified - if (consume) { - container.getInput().consumeEvent(); - } - } else if (key == Input.KEY_DELETE) { - if (value.length() > cursorPos) { - value = value.substring(0,cursorPos) + value.substring(cursorPos+1); - } - // Nobody more will be notified - if (consume) { - container.getInput().consumeEvent(); - } - } else if ((c < 127) && (c > 31) && (value.length() < maxCharacter)) { - if (cursorPos < value.length()) { - value = value.substring(0, cursorPos) + c - + value.substring(cursorPos); - } else { - value = value.substring(0, cursorPos) + c; - } - cursorPos++; - // Nobody more will be notified - if (consume) { - container.getInput().consumeEvent(); - } - } else if (key == Input.KEY_RETURN) { - notifyListeners(); - // Nobody more will be notified - if (consume) { - container.getInput().consumeEvent(); - } } - + } else if (key == Input.KEY_DELETE) { + if (value.length() > cursorPos) { + value = value.substring(0,cursorPos) + value.substring(cursorPos+1); + } + } else if ((c < 127) && (c > 31) && (value.length() < maxCharacter)) { + if (cursorPos < value.length()) { + value = value.substring(0, cursorPos) + c + + value.substring(cursorPos); + } else { + value = value.substring(0, cursorPos) + c; + } + cursorPos++; + } else if (key == Input.KEY_RETURN) { + if (listener != null) { + listener.onAction(); + } } + } - /** - * @see org.newdawn.slick.gui.AbstractComponent#setFocus(boolean) - */ - @Override - public void setFocus(boolean focus) { - lastKey = -1; - - super.setFocus(focus); - } } diff --git a/src/yugecin/opsudance/core/components/ActionListener.java b/src/yugecin/opsudance/core/components/ActionListener.java new file mode 100644 index 00000000..b1e05733 --- /dev/null +++ b/src/yugecin/opsudance/core/components/ActionListener.java @@ -0,0 +1,24 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.components; + +public interface ActionListener { + + void onAction(); + +} diff --git a/src/yugecin/opsudance/core/components/Component.java b/src/yugecin/opsudance/core/components/Component.java new file mode 100644 index 00000000..20e4f777 --- /dev/null +++ b/src/yugecin/opsudance/core/components/Component.java @@ -0,0 +1,60 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.components; + +import org.newdawn.slick.Graphics; + +public abstract class Component { + + public int width; + public int height; + public int x; + public int y; + + protected boolean focused; + protected boolean hovered; + + public abstract boolean isFocusable(); + + public boolean isHovered() { + return hovered; + } + + public void updateHover(int x, int y) { + this.hovered = this.x <= x && x <= this.x + width && this.y <= y && y <= this.y + height; + } + + public void mouseReleased(int button) { + } + + public void preRenderUpdate() { + } + + public abstract void render(Graphics g); + + public void keyPressed(int key, char c) { + } + + public void keyReleased(int key, char c) { + } + + public void setFocused(boolean focused) { + this.focused = focused; + } + +} diff --git a/src/yugecin/opsudance/core/state/ComplexOpsuState.java b/src/yugecin/opsudance/core/state/ComplexOpsuState.java new file mode 100644 index 00000000..ff3da952 --- /dev/null +++ b/src/yugecin/opsudance/core/state/ComplexOpsuState.java @@ -0,0 +1,124 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.state; + +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Input; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.components.Component; + +import java.util.LinkedList; + +public class ComplexOpsuState extends BaseOpsuState { + + protected final LinkedList components; + + private Component focusedComponent; + + public ComplexOpsuState(DisplayContainer displayContainer) { + super(displayContainer); + this.components = new LinkedList<>(); + } + + public final void focusComponent(Component component) { + if (!component.isFocusable()) { + return; + } + if (focusedComponent != null) { + focusedComponent.setFocused(false); + } + focusedComponent = component; + component.setFocused(true); + } + + public boolean isAnyComponentFocused() { + return focusedComponent != null; + } + + @Override + public boolean mouseReleased(int button, int x, int y) { + if (focusedComponent == null) { + for (Component component : components) { + if (!component.isFocusable()) { + continue; + } + component.updateHover(x, y); + if (component.isHovered()) { + focusedComponent = component; + focusedComponent.setFocused(true); + return true; + } + } + return false; + } + focusedComponent.updateHover(x, y); + if (focusedComponent.isHovered()) { + focusedComponent.mouseReleased(button); + return true; + } + focusedComponent.setFocused(false); + focusedComponent = null; + return false; + } + + @Override + public void preRenderUpdate() { + super.preRenderUpdate(); + for (Component component : components) { + component.updateHover(displayContainer.mouseX, displayContainer.mouseY); + component.preRenderUpdate(); + } + } + + @Override + public void render(Graphics g) { + super.render(g); + for (Component component : components) { + component.render(g); + } + } + + @Override + public boolean keyReleased(int key, char c) { + if (focusedComponent != null) { + if (key == Input.KEY_ESCAPE) { + focusedComponent.setFocused(false); + focusedComponent = null; + return true; + } + focusedComponent.keyReleased(key, c); + return true; + } + return false; + } + + @Override + public boolean keyPressed(int key, char c) { + if (focusedComponent != null) { + if (key == Input.KEY_ESCAPE) { + focusedComponent.setFocused(false); + focusedComponent = null; + return true; + } + focusedComponent.keyPressed(key, c); + return true; + } + return false; + } + +} From 81b71d5703f028f022ba2ac49601b12c20593107 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 22:36:09 +0100 Subject: [PATCH 73/92] convert download state --- src/itdelatrisu/opsu/Opsu.java | 2 +- .../opsu/states/DownloadsMenu.java | 302 +++++++------- src/itdelatrisu/opsu/states/MainMenu.java | 10 +- src/itdelatrisu/opsu/ui/DropdownMenu.java | 372 +++++------------- .../core/inject/OpsuDanceInjector.java | 6 +- 5 files changed, 262 insertions(+), 430 deletions(-) diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index de11aab1..7547cb0d 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -93,7 +93,7 @@ public class Opsu extends StateBasedGame { addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); addState(new GameRanking(STATE_GAMERANKING)); addState(new OptionsMenu(STATE_OPTIONSMENU)); - addState(new DownloadsMenu(STATE_DOWNLOADSMENU)); + //addState(new DownloadsMenu(STATE_DOWNLOADSMENU)); } /** diff --git a/src/itdelatrisu/opsu/states/DownloadsMenu.java b/src/itdelatrisu/opsu/states/DownloadsMenu.java index df4435bc..ed6e47ec 100644 --- a/src/itdelatrisu/opsu/states/DownloadsMenu.java +++ b/src/itdelatrisu/opsu/states/DownloadsMenu.java @@ -51,17 +51,15 @@ import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineListener; import org.newdawn.slick.Color; -import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; import org.newdawn.slick.SlickException; import org.newdawn.slick.gui.TextField; -import org.newdawn.slick.state.BasicGameState; -import org.newdawn.slick.state.StateBasedGame; -import org.newdawn.slick.state.transition.EasedFadeOutTransition; -import org.newdawn.slick.state.transition.FadeInTransition; import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.inject.InstanceContainer; +import yugecin.opsudance.core.state.ComplexOpsuState; /** * Downloads menu. @@ -69,7 +67,10 @@ import org.newdawn.slick.util.Log; * Players are able to download beatmaps off of various servers and import them * from this state. */ -public class DownloadsMenu extends BasicGameState { +public class DownloadsMenu extends ComplexOpsuState { + + private final InstanceContainer instanceContainer; + /** Delay time, in milliseconds, between each search. */ private static final int SEARCH_DELAY = 700; @@ -288,45 +289,42 @@ public class DownloadsMenu extends BasicGameState { } } - // game-related variables - private GameContainer container; - private StateBasedGame game; - private Input input; - private final int state; - - public DownloadsMenu(int state) { - this.state = state; + public DownloadsMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) { + super(displayContainer); + this.instanceContainer = instanceContainer; } @Override - public void init(GameContainer container, StateBasedGame game) - throws SlickException { - this.container = container; - this.game = game; - this.input = container.getInput(); + public void revalidate() { + super.revalidate(); - int width = container.getWidth(); - int height = container.getHeight(); - float baseX = width * 0.024f; - float searchY = (height * 0.04f) + Fonts.LARGE.getLineHeight(); - float searchWidth = width * 0.3f; + components.clear(); + + int width = displayContainer.width; + int height = displayContainer.height; + int baseX = (int) (width * 0.024f); + int searchY = (int) (height * 0.04f + Fonts.LARGE.getLineHeight()); + int searchWidth = (int) (width * 0.3f); // search searchTimer = SEARCH_DELAY; searchResultString = "Loading data from server..."; - search = new TextField( - container, Fonts.DEFAULT, (int) baseX, (int) searchY, - (int) searchWidth, Fonts.MEDIUM.getLineHeight() - ); + search = new TextField(displayContainer, Fonts.DEFAULT, baseX, searchY, searchWidth, Fonts.MEDIUM.getLineHeight()) { + @Override + public boolean isFocusable() { + return false; + } + }; + search.setFocused(true); search.setBackgroundColor(Colors.BLACK_BG_NORMAL); search.setBorderColor(Color.white); search.setTextColor(Color.white); - search.setConsumeEvents(false); search.setMaxLength(255); + components.add(search); // page buttons - float pageButtonY = height * 0.2f; - float pageButtonWidth = width * 0.7f; + int pageButtonY = (int) (height * 0.2f); + int pageButtonWidth = (int) (width * 0.7f); Image prevImg = GameImage.MUSIC_PREVIOUS.getImage(); Image nextImg = GameImage.MUSIC_NEXT.getImage(); prevPage = new MenuButton(prevImg, baseX + prevImg.getWidth() / 2f, @@ -337,25 +335,25 @@ public class DownloadsMenu extends BasicGameState { nextPage.setHoverExpand(1.5f); // buttons - float buttonMarginX = width * 0.004f; - float buttonHeight = height * 0.038f; - float resetWidth = width * 0.085f; - float rankedWidth = width * 0.15f; - float lowerWidth = width * 0.12f; - float topButtonY = searchY + Fonts.MEDIUM.getLineHeight() / 2f; - float lowerButtonY = height * 0.995f - searchY - buttonHeight / 2f; + int buttonMarginX = (int) (width * 0.004f); + int buttonHeight = (int) (height * 0.038f); + int resetWidth = (int) (width * 0.085f); + int rankedWidth = (int) (width * 0.15f); + int lowerWidth = (int) (width * 0.12f); + int topButtonY = (int) (searchY + Fonts.MEDIUM.getLineHeight() / 2f); + int lowerButtonY = (int) (height * 0.995f - searchY - buttonHeight / 2f); Image button = GameImage.MENU_BUTTON_MID.getImage(); Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage(); Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage(); buttonL = buttonL.getScaledCopy(buttonHeight / buttonL.getHeight()); buttonR = buttonR.getScaledCopy(buttonHeight / buttonR.getHeight()); int lrButtonWidth = buttonL.getWidth() + buttonR.getWidth(); - Image resetButtonImage = button.getScaledCopy((int) resetWidth - lrButtonWidth, (int) buttonHeight); - Image rankedButtonImage = button.getScaledCopy((int) rankedWidth - lrButtonWidth, (int) buttonHeight); - Image lowerButtonImage = button.getScaledCopy((int) lowerWidth - lrButtonWidth, (int) buttonHeight); - float resetButtonWidth = resetButtonImage.getWidth() + lrButtonWidth; - float rankedButtonWidth = rankedButtonImage.getWidth() + lrButtonWidth; - float lowerButtonWidth = lowerButtonImage.getWidth() + lrButtonWidth; + Image resetButtonImage = button.getScaledCopy(resetWidth - lrButtonWidth, buttonHeight); + Image rankedButtonImage = button.getScaledCopy(rankedWidth - lrButtonWidth, buttonHeight); + Image lowerButtonImage = button.getScaledCopy(lowerWidth - lrButtonWidth, buttonHeight); + int resetButtonWidth = resetButtonImage.getWidth() + lrButtonWidth; + int rankedButtonWidth = rankedButtonImage.getWidth() + lrButtonWidth; + int lowerButtonWidth = lowerButtonImage.getWidth() + lrButtonWidth; clearButton = new MenuButton(lowerButtonImage, buttonL, buttonR, width * 0.75f + buttonMarginX + lowerButtonWidth / 2f, lowerButtonY); importButton = new MenuButton(lowerButtonImage, buttonL, buttonR, @@ -374,8 +372,8 @@ public class DownloadsMenu extends BasicGameState { // dropdown menu int serverWidth = (int) (width * 0.12f); - serverMenu = new DropdownMenu(container, SERVERS, - baseX + searchWidth + buttonMarginX * 3f + resetButtonWidth + rankedButtonWidth, searchY, serverWidth) { + int x = baseX + searchWidth + buttonMarginX * 3 + resetButtonWidth + rankedButtonWidth; + serverMenu = new DropdownMenu(displayContainer, SERVERS, x, searchY, serverWidth) { @Override public void itemSelected(int index, DownloadServer item) { resultList = null; @@ -388,13 +386,14 @@ public class DownloadsMenu extends BasicGameState { searchResultString = "Loading data from server..."; lastQuery = null; pageDir = Page.RESET; - if (searchQuery != null) + if (searchQuery != null) { searchQuery.interrupt(); + } resetSearchTimer(); } @Override - public boolean menuClicked(int index) { + public boolean canSelect(int index) { // block input during beatmap importing if (importThread != null) return false; @@ -406,28 +405,25 @@ public class DownloadsMenu extends BasicGameState { serverMenu.setBackgroundColor(Colors.BLACK_BG_HOVER); serverMenu.setBorderColor(Color.black); serverMenu.setChevronRightColor(Color.white); + components.add(serverMenu); } @Override - public void render(GameContainer container, StateBasedGame game, Graphics g) - throws SlickException { - int width = container.getWidth(); - int height = container.getHeight(); - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); - boolean inDropdownMenu = serverMenu.contains(mouseX, mouseY); + public void render(Graphics g) { + super.render(g); // background GameImage.SEARCH_BG.getImage().draw(); // title - Fonts.LARGE.drawString(width * 0.024f, height * 0.03f, "Download Beatmaps!", Color.white); + Fonts.LARGE.drawString(displayContainer.width * 0.024f, displayContainer.height * 0.03f, "Download Beatmaps!", Color.white); // search g.setColor(Color.white); g.setLineWidth(2f); - search.render(container, g); + search.render(g); Fonts.BOLD.drawString( - search.getX() + search.getWidth() * 0.01f, search.getY() + search.getHeight() * 1.3f, + search.x + search.width * 0.01f, search.y + search.height * 1.3f, searchResultString, Color.white ); @@ -446,7 +442,7 @@ public class DownloadsMenu extends BasicGameState { if (index >= nodes.length) break; nodes[index].drawResult(g, offset + i * DownloadNode.getButtonOffset(), - DownloadNode.resultContains(mouseX, mouseY - offset, i) && !inDropdownMenu, + DownloadNode.resultContains(displayContainer.mouseX, displayContainer.mouseY - offset, i) && !serverMenu.isHovered(), (index == focusResult), (previewID == nodes[index].getID())); } g.clearClip(); @@ -457,9 +453,9 @@ public class DownloadsMenu extends BasicGameState { // pages if (nodes.length > 0) { - float baseX = width * 0.024f; - float buttonY = height * 0.2f; - float buttonWidth = width * 0.7f; + float baseX = displayContainer.width * 0.024f; + float buttonY = displayContainer.height * 0.2f; + float buttonWidth = displayContainer.width * 0.7f; Fonts.BOLD.drawString( baseX + (buttonWidth - Fonts.BOLD.getWidth("Page 1")) / 2f, buttonY - Fonts.BOLD.getLineHeight() * 1.3f, @@ -473,11 +469,11 @@ public class DownloadsMenu extends BasicGameState { } // downloads - float downloadsX = width * 0.75f, downloadsY = search.getY(); + float downloadsX = displayContainer.width * 0.75f, downloadsY = search.y; g.setColor(Colors.BLACK_BG_NORMAL); g.fillRect(downloadsX, downloadsY, - width * 0.25f, height - downloadsY * 2f); - Fonts.LARGE.drawString(downloadsX + width * 0.015f, downloadsY + height * 0.015f, "Downloads", Color.white); + displayContainer.width * 0.25f, displayContainer.height - downloadsY * 2f); + Fonts.LARGE.drawString(downloadsX + displayContainer.width * 0.015f, downloadsY + displayContainer.height * 0.015f, "Downloads", Color.white); int downloadsSize = DownloadList.get().size(); if (downloadsSize > 0) { int maxDownloadsShown = DownloadNode.maxDownloadsShown(); @@ -493,7 +489,7 @@ public class DownloadsMenu extends BasicGameState { if (node == null) break; node.drawDownload(g, i * DownloadNode.getInfoHeight() + offset, index, - DownloadNode.downloadContains(mouseX, mouseY - offset, i)); + DownloadNode.downloadContains(displayContainer.mouseX, displayContainer.mouseY - offset, i)); } g.clearClip(); @@ -510,13 +506,13 @@ public class DownloadsMenu extends BasicGameState { rankedButton.draw(Color.magenta); // dropdown menu - serverMenu.render(container, g); + serverMenu.render(g); // importing beatmaps if (importThread != null) { // darken the screen g.setColor(Colors.BLACK_ALPHA); - g.fillRect(0, 0, width, height); + g.fillRect(0, 0, displayContainer.width, displayContainer.height); UI.drawLoadingProgress(g); } @@ -529,8 +525,10 @@ public class DownloadsMenu extends BasicGameState { } @Override - public void update(GameContainer container, StateBasedGame game, int delta) - throws SlickException { + public void preRenderUpdate() { + super.preRenderUpdate(); + + int delta = displayContainer.renderDelta; UI.update(delta); if (importThread == null) MusicController.loopTrackIfEnded(false); @@ -547,11 +545,12 @@ public class DownloadsMenu extends BasicGameState { // focus new beatmap // NOTE: This can't be called in another thread because it makes OpenGL calls. - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(importedNode, -1, true, true); + instanceContainer.provide(SongMenu.class).setFocus(importedNode, -1, true, true); } importThread = null; } - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); + int mouseX = displayContainer.mouseX; + int mouseY = displayContainer.mouseY; UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); prevPage.hoverUpdate(delta, mouseX, mouseY); nextPage.hoverUpdate(delta, mouseX, mouseY); @@ -572,7 +571,6 @@ public class DownloadsMenu extends BasicGameState { focusTimer += delta; // search - search.setFocus(true); searchTimer += delta; if (searchTimer >= SEARCH_DELAY && importThread == null) { searchTimer = 0; @@ -608,24 +606,26 @@ public class DownloadsMenu extends BasicGameState { } @Override - public int getID() { return state; } + public boolean mousePressed(int button, int x, int y) { + if (super.mousePressed(button, x, y)) { + return true; + } - @Override - public void mousePressed(int button, int x, int y) { - // check mouse button - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; + if (button == Input.MOUSE_MIDDLE_BUTTON) { + return false; + } // block input during beatmap importing - if (importThread != null) - return; + if (importThread != null) { + return true; + } // back if (UI.getBackButton().contains(x, y)) { SoundController.playSound(SoundEffect.MENUBACK); - ((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); - game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition()); - return; + instanceContainer.provide(MainMenu.class).reset(); + displayContainer.switchState(MainMenu.class); + return true; } // search results @@ -694,11 +694,12 @@ public class DownloadsMenu extends BasicGameState { } }.start(); } - return; + return true; } - if (isLoaded) - return; + if (isLoaded) { + return true; + } SoundController.playSound(SoundEffect.MENUCLICK); if (index == focusResult) { @@ -725,7 +726,7 @@ public class DownloadsMenu extends BasicGameState { break; } } - return; + return true; } // pages @@ -741,7 +742,7 @@ public class DownloadsMenu extends BasicGameState { searchQuery.interrupt(); resetSearchTimer(); } - return; + return true; } if (pageResultTotal < totalResults && nextPage.contains(x, y)) { if (lastQueryDir == Page.NEXT && searchQuery != null && !searchQuery.isComplete()) @@ -753,7 +754,7 @@ public class DownloadsMenu extends BasicGameState { if (searchQuery != null) searchQuery.interrupt(); resetSearchTimer(); - return; + return true; } } } @@ -763,7 +764,7 @@ public class DownloadsMenu extends BasicGameState { if (clearButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUCLICK); DownloadList.get().clearInactiveDownloads(); - return; + return true; } if (importButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUCLICK); @@ -771,7 +772,7 @@ public class DownloadsMenu extends BasicGameState { // import songs in new thread importThread = new BeatmapImportThread(); importThread.start(); - return; + return true; } if (resetButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUCLICK); @@ -781,7 +782,7 @@ public class DownloadsMenu extends BasicGameState { if (searchQuery != null) searchQuery.interrupt(); resetSearchTimer(); - return; + return true; } if (rankedButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUCLICK); @@ -791,7 +792,7 @@ public class DownloadsMenu extends BasicGameState { if (searchQuery != null) searchQuery.interrupt(); resetSearchTimer(); - return; + return true; } // downloads @@ -807,8 +808,9 @@ public class DownloadsMenu extends BasicGameState { if (DownloadNode.downloadIconContains(x, y - offset, i)) { SoundController.playSound(SoundEffect.MENUCLICK); DownloadNode node = DownloadList.get().getNode(index); - if (node == null) - return; + if (node == null) { + return true; + } Download dl = node.getDownload(); switch (dl.getStatus()) { case CANCELLED: @@ -822,62 +824,78 @@ public class DownloadsMenu extends BasicGameState { dl.cancel(); break; } - return; + return true; } } } + return false; } @Override - public void mouseReleased(int button, int x, int y) { - // check mouse button - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; + public boolean mouseReleased(int button, int x, int y) { + if (super.mouseReleased(button, x, y)) { + return true; + } + + if (button == Input.MOUSE_MIDDLE_BUTTON) { + return false; + } startDownloadIndexPos.released(); startResultPos.released(); + return true; } @Override - public void mouseWheelMoved(int newValue) { + public boolean mouseWheelMoved(int newValue) { // change volume - if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) { + if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) { UI.changeVolume((newValue < 0) ? -1 : 1); - return; + return true; } // block input during beatmap importing - if (importThread != null) - return; + if (importThread != null) { + return true; + } int shift = (newValue < 0) ? 1 : -1; - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); - scrollLists(mouseX, mouseY, shift); + scrollLists(displayContainer.mouseX, displayContainer.mouseY, shift); + return true; } @Override - public void mouseDragged(int oldx, int oldy, int newx, int newy) { + public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { // block input during beatmap importing - if (importThread != null) - return; - - // check mouse button - if (!input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON) && - !input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) - return; + if (importThread != null) { + return true; + } int diff = newy - oldy; - if (diff == 0) - return; + if (diff == 0) { + return false; + } + startDownloadIndexPos.dragged(-diff); startResultPos.dragged(-diff); + return true; } @Override - public void keyPressed(int key, char c) { + public boolean keyReleased(int key, char c) { + return super.keyReleased(key, c); + } + + @Override + public boolean keyPressed(int key, char c) { + if (super.keyPressed(key, c)) { + return true; + } + // block input during beatmap importing - if (importThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) - return; + if (importThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) { + return true; + } switch (key) { case Input.KEY_ESCAPE: @@ -892,16 +910,16 @@ public class DownloadsMenu extends BasicGameState { } else { // return to main menu SoundController.playSound(SoundEffect.MENUBACK); - ((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); - game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition()); + instanceContainer.provide(MainMenu.class).reset(); + displayContainer.switchState(MainMenu.class); } - break; + return true; case Input.KEY_ENTER: if (!search.getText().isEmpty()) { pageDir = Page.RESET; resetSearchTimer(); } - break; + return true; case Input.KEY_F5: SoundController.playSound(SoundEffect.MENUCLICK); lastQuery = null; @@ -909,30 +927,31 @@ public class DownloadsMenu extends BasicGameState { if (searchQuery != null) searchQuery.interrupt(); resetSearchTimer(); - break; + return true; case Input.KEY_F7: // TODO d //Options.setNextFPS(container); - break; + return true; case Input.KEY_F10: Options.toggleMouseDisabled(); - break; + return true; case Input.KEY_F12: Utils.takeScreenShot(); - break; - default: - // wait for user to finish typing - if (Character.isLetterOrDigit(c) || key == Input.KEY_BACK) { - searchTimer = 0; - pageDir = Page.RESET; - } - break; + return true; } + // wait for user to finish typing + if (Character.isLetterOrDigit(c) || key == Input.KEY_BACK) { + search.keyPressed(key, c); + searchTimer = 0; + pageDir = Page.RESET; + } + return true; } @Override - public void enter(GameContainer container, StateBasedGame game) - throws SlickException { + public void enter() { + super.enter(); + UI.enter(); prevPage.resetHover(); nextPage.resetHover(); @@ -940,7 +959,6 @@ public class DownloadsMenu extends BasicGameState { importButton.resetHover(); resetButton.resetHover(); rankedButton.resetHover(); - serverMenu.activate(); serverMenu.reset(); focusResult = -1; startResultPos.setPosition(0); @@ -954,10 +972,10 @@ public class DownloadsMenu extends BasicGameState { } @Override - public void leave(GameContainer container, StateBasedGame game) - throws SlickException { - search.setFocus(false); - serverMenu.deactivate(); + public void leave() { + super.leave(); + + focusComponent(search); SoundController.stopTrack(); MusicController.resume(); } diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index a91e7fef..d1f63a9e 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -553,8 +553,7 @@ public class MainMenu extends BaseOpsuState { // downloads button actions if (downloadsButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUHIT); - // TODO //displayContainer.switchState(DownloadsMenu.class); - //game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition()); + displayContainer.switchState(DownloadsMenu.class); return true; } @@ -659,9 +658,8 @@ public class MainMenu extends BaseOpsuState { enterSongMenu(); return true; case Input.KEY_D: - // TODO - //SoundController.playSound(SoundEffect.MENUHIT); - //game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition()); + SoundController.playSound(SoundEffect.MENUHIT); + displayContainer.switchState(DownloadsMenu.class); return true; case Input.KEY_R: nextTrack(true); @@ -754,7 +752,7 @@ public class MainMenu extends BaseOpsuState { Class state = SongMenu.class; if (BeatmapSetList.get().getMapSetCount() == 0) { instanceContainer.provide(DownloadsMenu.class).notifyOnLoad("Download some beatmaps to get started!"); - // TODO d state = DownloadsMenu.class; + state = DownloadsMenu.class; } displayContainer.switchState(state); } diff --git a/src/itdelatrisu/opsu/ui/DropdownMenu.java b/src/itdelatrisu/opsu/ui/DropdownMenu.java index 5204c257..095ead77 100644 --- a/src/itdelatrisu/opsu/ui/DropdownMenu.java +++ b/src/itdelatrisu/opsu/ui/DropdownMenu.java @@ -27,152 +27,62 @@ import org.newdawn.slick.Font; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; -import org.newdawn.slick.SlickException; import org.newdawn.slick.UnicodeFont; -import org.newdawn.slick.gui.AbstractComponent; -import org.newdawn.slick.gui.GUIContext; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.components.Component; + +public class DropdownMenu extends Component { -/** - * Simple dropdown menu. - *

- * Basic usage: - *

    - *
  • Override {@link #menuClicked(int)} to perform actions when the menu is clicked - * (e.g. play a sound effect, block input under certain conditions). - *
  • Override {@link #itemSelected(int, Object)} to perform actions when a new item is selected. - *
  • Call {@link #activate()}/{@link #deactivate()} whenever the component is needed - * (e.g. in a state's {@code enter} and {@code leave} events. - *
- * - * @param the type of the elements in the menu - */ -public class DropdownMenu extends AbstractComponent { - /** Padding ratios for drawing. */ private static final float PADDING_Y = 0.1f, CHEVRON_X = 0.03f; - /** Whether this component is active. */ - private boolean active; + private final DisplayContainer displayContainer; - /** The menu items. */ private E[] items; - - /** The menu item names. */ private String[] itemNames; + private int selectedItemIndex = 0; + private boolean expanded; - /** The index of the selected item. */ - private int itemIndex = 0; - - /** Whether the menu is expanded. */ - private boolean expanded = false; - - /** The expanding animation progress. */ private AnimatedValue expandProgress = new AnimatedValue(300, 0f, 1f, AnimationEquation.LINEAR); - /** The last update time, in milliseconds. */ - private long lastUpdateTime; - - /** The top-left coordinates. */ - private float x, y; - - /** The width and height of the dropdown menu. */ - private int width, height; - - /** The height of the base item. */ private int baseHeight; - - /** The vertical offset between items. */ private float offsetY; - /** The colors to use. */ - private Color - textColor = Color.white, backgroundColor = Color.black, - highlightColor = Colors.BLUE_DIVIDER, borderColor = Colors.BLUE_DIVIDER, - chevronDownColor = textColor, chevronRightColor = backgroundColor; + private Color textColor = Color.white; + private Color backgroundColor = Color.black; + private Color highlightColor = Colors.BLUE_DIVIDER; + private Color borderColor = Colors.BLUE_DIVIDER; + private Color chevronDownColor = textColor; + private Color chevronRightColor = backgroundColor; - /** The fonts to use. */ - private UnicodeFont fontNormal = Fonts.MEDIUM, fontSelected = Fonts.MEDIUMBOLD; + private UnicodeFont fontNormal = Fonts.MEDIUM; + private UnicodeFont fontSelected = Fonts.MEDIUMBOLD; - /** The chevron images. */ - private Image chevronDown, chevronRight; + private Image chevronDown; + private Image chevronRight; - /** Should the next click be blocked? */ - private boolean blockClick = false; - - /** - * Creates a new dropdown menu. - * @param container the container rendering this menu - * @param items the list of items (with names given as their {@code toString()} methods) - * @param x the top-left x coordinate - * @param y the top-left y coordinate - */ - public DropdownMenu(GUIContext container, E[] items, float x, float y) { - this(container, items, x, y, 0); - } - - /** - * Creates a new dropdown menu with the given fonts. - * @param container the container rendering this menu - * @param items the list of items (with names given as their {@code toString()} methods) - * @param x the top-left x coordinate - * @param y the top-left y coordinate - * @param normal the normal font - * @param selected the font for the selected item - */ - public DropdownMenu(GUIContext container, E[] items, float x, float y, UnicodeFont normal, UnicodeFont selected) { - this(container, items, x, y, 0, normal, selected); - } - - /** - * Creates a new dropdown menu with the given width. - * @param container the container rendering this menu - * @param items the list of items (with names given as their {@code toString()} methods) - * @param x the top-left x coordinate - * @param y the top-left y coordinate - * @param width the menu width - */ - public DropdownMenu(GUIContext container, E[] items, float x, float y, int width) { - super(container); + public DropdownMenu(DisplayContainer displayContainer, E[] items, int x, int y, int width) { + this.displayContainer = displayContainer; init(items, x, y, width); } - /** - * Creates a new dropdown menu with the given width and fonts. - * @param container the container rendering this menu - * @param items the list of items (with names given as their {@code toString()} methods) - * @param x the top-left x coordinate - * @param y the top-left y coordinate - * @param width the menu width - * @param normal the normal font - * @param selected the font for the selected item - */ - public DropdownMenu(GUIContext container, E[] items, float x, float y, int width, UnicodeFont normal, UnicodeFont selected) { - super(container); - this.fontNormal = normal; - this.fontSelected = selected; - init(items, x, y, width); - } - - /** - * Returns the maximum item width from the list. - */ private int getMaxItemWidth() { int maxWidth = 0; - for (int i = 0; i < itemNames.length; i++) { - int w = fontSelected.getWidth(itemNames[i]); - if (w > maxWidth) + for (String itemName : itemNames) { + int w = fontSelected.getWidth(itemName); + if (w > maxWidth) { maxWidth = w; + } } return maxWidth; } - /** - * Initializes the component. - */ - private void init(E[] items, float x, float y, int width) { + @SuppressWarnings("SuspiciousNameCombination") + private void init(E[] items, int x, int y, int width) { this.items = items; this.itemNames = new String[items.length]; - for (int i = 0; i < itemNames.length; i++) + for (int i = 0; i < itemNames.length; i++) { itemNames[i] = items[i].toString(); + } this.x = x; this.y = y; this.baseHeight = fontNormal.getLineHeight(); @@ -188,77 +98,27 @@ public class DropdownMenu extends AbstractComponent { } @Override - public void setLocation(int x, int y) { - this.x = x; - this.y = y; + public void updateHover(int x, int y) { + this.hovered = this.x <= x && x <= this.x + width && this.y <= y && y <= this.y + (expanded ? height : baseHeight); + } + + public boolean baseContains(int x, int y) { + return (x > this.x && x < this.x + width && y > this.y && y < this.y + baseHeight); } @Override - public int getX() { return (int) x; } + public void render(Graphics g) { + int delta = displayContainer.renderDelta; - @Override - public int getY() { return (int) y; } - - @Override - public int getWidth() { return width; } - - @Override - public int getHeight() { return (expanded) ? height : baseHeight; } - - /** Activates the component. */ - public void activate() { this.active = true; } - - /** Deactivates the component. */ - public void deactivate() { this.active = false; } - - /** - * Returns whether the dropdown menu is currently open. - * @return true if open, false otherwise - */ - public boolean isOpen() { return expanded; } - - /** - * Opens or closes the dropdown menu. - * @param flag true to open, false to close - */ - public void open(boolean flag) { this.expanded = flag; } - - /** - * Returns true if the coordinates are within the menu bounds. - * @param cx the x coordinate - * @param cy the y coordinate - */ - public boolean contains(float cx, float cy) { - return (cx > x && cx < x + width && ( - (cy > y && cy < y + baseHeight) || - (expanded && cy > y + offsetY && cy < y + height))); - } - - /** - * Returns true if the coordinates are within the base item bounds. - * @param cx the x coordinate - * @param cy the y coordinate - */ - public boolean baseContains(float cx, float cy) { - return (cx > x && cx < x + width && cy > y && cy < y + baseHeight); - } - - @Override - public void render(GUIContext container, Graphics g) throws SlickException { // update animation - long time = container.getTime(); - if (lastUpdateTime > 0) { - int delta = (int) (time - lastUpdateTime); - expandProgress.update((expanded) ? delta : -delta * 2); - } - this.lastUpdateTime = time; + expandProgress.update((expanded) ? delta : -delta * 2); // get parameters - Input input = container.getInput(); - int idx = getIndexAt(input.getMouseX(), input.getMouseY()); + int idx = getIndexAt(displayContainer.mouseY); float t = expandProgress.getValue(); - if (expanded) + if (expanded) { t = AnimationEquation.OUT_CUBIC.calc(t); + } // background and border Color oldGColor = g.getColor(); @@ -293,12 +153,12 @@ public class DropdownMenu extends AbstractComponent { // text chevronDown.draw(x + width - chevronDown.getWidth() - width * CHEVRON_X, y + (baseHeight - chevronDown.getHeight()) / 2f, chevronDownColor); - fontNormal.drawString(x + (width * 0.03f), y + (fontNormal.getPaddingTop() + fontNormal.getPaddingBottom()) / 2f, itemNames[itemIndex], textColor); + fontNormal.drawString(x + (width * 0.03f), y + (fontNormal.getPaddingTop() + fontNormal.getPaddingBottom()) / 2f, itemNames[selectedItemIndex], textColor); float oldTextAlpha = textColor.a; textColor.a *= t; if (expanded || t >= 0.0001) { for (int i = 0; i < itemNames.length; i++) { - Font f = (i == itemIndex) ? fontSelected : fontNormal; + Font f = (i == selectedItemIndex) ? fontSelected : fontNormal; if (i == idx && t >= 0.999) chevronRight.draw(x, y + offsetY + (offsetY * i) + (offsetY - chevronRight.getHeight()) / 2f, chevronRightColor); f.drawString(x + chevronRight.getWidth(), y + offsetY + (offsetY * i * t), itemNames[i], textColor); @@ -310,131 +170,89 @@ public class DropdownMenu extends AbstractComponent { /** * Returns the index of the item at the given location, -1 for the base item, * and -2 if there is no item at the location. - * @param cx the x coordinate - * @param cy the y coordinate + * @param y the y coordinate */ - private int getIndexAt(float cx, float cy) { - if (!contains(cx, cy)) + private int getIndexAt(int y) { + if (!hovered) { return -2; - if (cy <= y + baseHeight) + } + if (y <= this.y + baseHeight) { return -1; - if (!expanded) + } + if (!expanded) { return -2; - return (int) ((cy - (y + offsetY)) / offsetY); + } + return (int) ((y - (this.y + offsetY)) / offsetY); } - /** - * Resets the menu state. - */ public void reset() { this.expanded = false; - this.lastUpdateTime = 0; expandProgress.setTime(0); - blockClick = false; + } + + + @Override + public void setFocused(boolean focused) { + super.setFocused(focused); + expanded = focused; } @Override - public void mousePressed(int button, int x, int y) { - if (!active) - return; + public boolean isFocusable() { + return true; + } - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; + @Override + public void mouseReleased(int button) { + super.mouseReleased(button); - int idx = getIndexAt(x, y); + if (button == Input.MOUSE_MIDDLE_BUTTON) { + return; + } + + int idx = getIndexAt(displayContainer.mouseY); if (idx == -2) { this.expanded = false; return; } - if (!menuClicked(idx)) + if (!canSelect(selectedItemIndex)) { return; - this.expanded = (idx == -1) ? !expanded : false; - if (idx >= 0 && itemIndex != idx) { - this.itemIndex = idx; - itemSelected(idx, items[idx]); } - blockClick = true; - consumeEvent(); - } - - @Override - public void mouseClicked(int button, int x, int y, int clickCount) { - if (!active) - return; - - if (blockClick) { - blockClick = false; - consumeEvent(); + this.expanded = (idx == -1) && !expanded; + if (idx >= 0 && selectedItemIndex != idx) { + this.selectedItemIndex = idx; + itemSelected(idx, items[selectedItemIndex]); } } - /** - * Notification that a new item was selected (via override). - * @param index the index of the item selected - * @param item the item selected - */ - public void itemSelected(int index, E item) {} - - /** - * Notification that the menu was clicked (via override). - * @param index the index of the item clicked, or -1 for the base item - * @return true to process the click, or false to block/intercept it - */ - public boolean menuClicked(int index) { return true; } - - @Override - public void setFocus(boolean focus) { /* does not currently use the "focus" concept */ } - - @Override - public void mouseReleased(int button, int x, int y) { /* does not currently use the "focus" concept */ } - - /** - * Selects the item at the given index. - * @param index the list item index - * @throws IllegalArgumentException if {@code index} is negative or greater than or equal to size - */ - public void setSelectedIndex(int index) { - if (index < 0 || index >= items.length) - throw new IllegalArgumentException(); - this.itemIndex = index; + protected boolean canSelect(int index) { + return true; } - /** - * Returns the index of the selected item. - */ - public int getSelectedIndex() { return itemIndex; } + protected void itemSelected(int index, E item) { + } - /** - * Returns the selected item. - */ - public E getSelectedItem() { return items[itemIndex]; } + public E getSelectedItem() { + return items[selectedItemIndex]; + } - /** - * Returns the item at the given index. - * @param index the list item index - */ - public E getItemAt(int index) { return items[index]; } + public void setBackgroundColor(Color c) { + this.backgroundColor = c; + } - /** - * Returns the number of items in the list. - */ - public int getItemCount() { return items.length; } + public void setHighlightColor(Color c) { + this.highlightColor = c; + } - /** Sets the text color. */ - public void setTextColor(Color c) { this.textColor = c; } + public void setBorderColor(Color c) { + this.borderColor = c; + } - /** Sets the background color. */ - public void setBackgroundColor(Color c) { this.backgroundColor = c; } + public void setChevronDownColor(Color c) { + this.chevronDownColor = c; + } - /** Sets the highlight color. */ - public void setHighlightColor(Color c) { this.highlightColor = c; } - - /** Sets the border color. */ - public void setBorderColor(Color c) { this.borderColor = c; } - - /** Sets the down chevron color. */ - public void setChevronDownColor(Color c) { this.chevronDownColor = c; } - - /** Sets the right chevron color. */ - public void setChevronRightColor(Color c) { this.chevronRightColor = c; } + public void setChevronRightColor(Color c) { + this.chevronRightColor = c; + } } diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index 499d5221..df63f5a9 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -17,10 +17,7 @@ */ package yugecin.opsudance.core.inject; -import itdelatrisu.opsu.states.ButtonMenu; -import itdelatrisu.opsu.states.MainMenu; -import itdelatrisu.opsu.states.SongMenu; -import itdelatrisu.opsu.states.Splash; +import itdelatrisu.opsu.states.*; import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.events.EventBus; @@ -59,6 +56,7 @@ public class OpsuDanceInjector extends Injector { bind(MainMenu.class).asEagerSingleton(); bind(ButtonMenu.class).asEagerSingleton(); bind(SongMenu.class).asEagerSingleton(); + bind(DownloadsMenu.class).asEagerSingleton(); } } From 06a5deb3a158911c39849a51fdcb5ecc8fa4e583 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 22:46:45 +0100 Subject: [PATCH 74/92] process common hotkeys in base state --- src/itdelatrisu/opsu/Options.java | 6 +++--- src/itdelatrisu/opsu/states/ButtonMenu.java | 20 +++++++------------ .../opsu/states/DownloadsMenu.java | 12 +---------- src/itdelatrisu/opsu/states/MainMenu.java | 13 ++++-------- src/itdelatrisu/opsu/states/SongMenu.java | 16 +++++---------- .../opsudance/core/state/BaseOpsuState.java | 15 ++++++++++++++ .../core/state/ComplexOpsuState.java | 3 +++ 7 files changed, 38 insertions(+), 47 deletions(-) diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index 2c033336..272b3057 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -395,8 +395,7 @@ public class Options { @Override public void clickListItem(int index) { targetFPSindex = index; - Container.instance.setTargetFrameRate(targetFPS[index]); - Container.instance.setVSync(targetFPS[index] == 60); + displayContainer.setFPS(targetFPS[index]); } @Override @@ -985,6 +984,7 @@ public class Options { PIPPI_SLIDER_FOLLOW_EXPAND ("Followcircle expand", "PippiFollowExpand", "Increase radius in followcircles", false), PIPPI_PREVENT_WOBBLY_STREAMS ("Prevent wobbly streams", "PippiPreventWobblyStreams", "Force linear mover while doing streams to prevent wobbly pippi", true); + public static DisplayContainer displayContainer; /** Option name. */ private final String name; @@ -1283,9 +1283,9 @@ public class Options { * bar notification about the action. */ public static void setNextFPS(DisplayContainer displayContainer) { + GameOption.displayContainer = displayContainer; // TODO dirty shit GameOption.TARGET_FPS.clickListItem((targetFPSindex + 1) % targetFPS.length); UI.sendBarNotification(String.format("Frame limiter: %s", GameOption.TARGET_FPS.getValueString())); - displayContainer.setFPS(GameOption.TARGET_FPS.val); } /** diff --git a/src/itdelatrisu/opsu/states/ButtonMenu.java b/src/itdelatrisu/opsu/states/ButtonMenu.java index 8533dbf9..ca899a0d 100644 --- a/src/itdelatrisu/opsu/states/ButtonMenu.java +++ b/src/itdelatrisu/opsu/states/ButtonMenu.java @@ -667,21 +667,15 @@ public class ButtonMenu extends BaseOpsuState { @Override public boolean keyPressed(int key, char c) { - switch (key) { - case Input.KEY_ESCAPE: - menuState.leave(); - return true; - case Input.KEY_F7: - // TODO - //Options.setNextFPS(displayContainer); - return true; - case Input.KEY_F10: - Options.toggleMouseDisabled(); - return true; - case Input.KEY_F12: - Utils.takeScreenShot(); + if (super.keyPressed(key, c)) { return true; } + + if (key == Input.KEY_ESCAPE) { + menuState.leave(); + return true; + } + menuState.keyPressed(key, c); return true; } diff --git a/src/itdelatrisu/opsu/states/DownloadsMenu.java b/src/itdelatrisu/opsu/states/DownloadsMenu.java index ed6e47ec..6a2e7c89 100644 --- a/src/itdelatrisu/opsu/states/DownloadsMenu.java +++ b/src/itdelatrisu/opsu/states/DownloadsMenu.java @@ -893,7 +893,7 @@ public class DownloadsMenu extends ComplexOpsuState { } // block input during beatmap importing - if (importThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) { + if (importThread != null && key != Input.KEY_ESCAPE) { return true; } @@ -928,16 +928,6 @@ public class DownloadsMenu extends ComplexOpsuState { searchQuery.interrupt(); resetSearchTimer(); return true; - case Input.KEY_F7: - // TODO d - //Options.setNextFPS(container); - return true; - case Input.KEY_F10: - Options.toggleMouseDisabled(); - return true; - case Input.KEY_F12: - Utils.takeScreenShot(); - return true; } // wait for user to finish typing if (Character.isLetterOrDigit(c) || key == Input.KEY_BACK) { diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index d1f63a9e..8bf821c0 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -634,6 +634,10 @@ public class MainMenu extends BaseOpsuState { @Override public boolean keyPressed(int key, char c) { + if (super.keyPressed(key, c)) { + return true; + } + switch (key) { case Input.KEY_ESCAPE: case Input.KEY_Q: @@ -670,15 +674,6 @@ public class MainMenu extends BaseOpsuState { case Input.KEY_DOWN: UI.changeVolume(-1); return true; - case Input.KEY_F7: - Options.setNextFPS(displayContainer); - return true; - case Input.KEY_F10: - Options.toggleMouseDisabled(); - return true; - case Input.KEY_F12: - Utils.takeScreenShot(); - return true; } return false; } diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 3741aab4..788fb9a8 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -1064,8 +1064,12 @@ public class SongMenu extends BaseOpsuState { @Override public boolean keyPressed(int key, char c) { + if (super.keyPressed(key, c)) { + return true; + } + // block input - if ((reloadThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) || beatmapMenuTimer > -1 || isScrollingToFocusNode) { + if ((reloadThread != null && key != Input.KEY_ESCAPE) || beatmapMenuTimer > -1 || isScrollingToFocusNode) { return true; } @@ -1145,16 +1149,6 @@ public class SongMenu extends BaseOpsuState { displayContainer.switchState(ButtonMenu.class); } return true; - case Input.KEY_F7: - // TODO d - //Options.setNextFPS(container); - return true; - case Input.KEY_F10: - Options.toggleMouseDisabled(); - return true; - case Input.KEY_F12: - Utils.takeScreenShot(); - return true; case Input.KEY_ENTER: if (focusNode == null) break; diff --git a/src/yugecin/opsudance/core/state/BaseOpsuState.java b/src/yugecin/opsudance/core/state/BaseOpsuState.java index 8c13315d..86bdfb9c 100644 --- a/src/yugecin/opsudance/core/state/BaseOpsuState.java +++ b/src/yugecin/opsudance/core/state/BaseOpsuState.java @@ -17,7 +17,10 @@ */ package yugecin.opsudance.core.state; +import itdelatrisu.opsu.Options; +import itdelatrisu.opsu.Utils; import org.newdawn.slick.Graphics; +import org.newdawn.slick.Input; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.events.EventListener; import yugecin.opsudance.events.ResolutionChangedEvent; @@ -89,6 +92,18 @@ public abstract class BaseOpsuState implements OpsuState, EventListener Date: Wed, 18 Jan 2017 22:50:18 +0100 Subject: [PATCH 75/92] remove the reset method from mainmenu --- .../opsu/states/DownloadsMenu.java | 2 - src/itdelatrisu/opsu/states/MainMenu.java | 38 ++++--------------- src/itdelatrisu/opsu/states/SongMenu.java | 2 - 3 files changed, 8 insertions(+), 34 deletions(-) diff --git a/src/itdelatrisu/opsu/states/DownloadsMenu.java b/src/itdelatrisu/opsu/states/DownloadsMenu.java index 6a2e7c89..718be4dd 100644 --- a/src/itdelatrisu/opsu/states/DownloadsMenu.java +++ b/src/itdelatrisu/opsu/states/DownloadsMenu.java @@ -623,7 +623,6 @@ public class DownloadsMenu extends ComplexOpsuState { // back if (UI.getBackButton().contains(x, y)) { SoundController.playSound(SoundEffect.MENUBACK); - instanceContainer.provide(MainMenu.class).reset(); displayContainer.switchState(MainMenu.class); return true; } @@ -910,7 +909,6 @@ public class DownloadsMenu extends ComplexOpsuState { } else { // return to main menu SoundController.playSound(SoundEffect.MENUBACK); - instanceContainer.provide(MainMenu.class).reset(); displayContainer.switchState(MainMenu.class); } return true; diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index 8bf821c0..a56418fe 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -224,8 +224,6 @@ public class MainMenu extends BaseOpsuState { logoOpen = new AnimatedValue(100, 0, centerOffsetX, AnimationEquation.OUT_QUAD); logoClose = new AnimatedValue(2200, centerOffsetX, 0, AnimationEquation.OUT_QUAD); logoButtonAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR); - - reset(); } @Override @@ -459,6 +457,14 @@ public class MainMenu extends BaseOpsuState { @Override public void enter() { super.enter(); + + logo.setX(displayContainer.width / 2); + logoOpen.setTime(0); + logoClose.setTime(0); + logoButtonAlpha.setTime(0); + logoTimer = 0; + logoState = LogoState.DEFAULT; + UI.enter(); if (!enterNotification) { if (Updater.get().getStatus() == Updater.Status.UPDATE_AVAILABLE) { @@ -688,34 +694,6 @@ public class MainMenu extends BaseOpsuState { (cy > musicBarY && cy < musicBarY + musicBarHeight)); } - /** - * Resets the button states. - */ - public void reset() { - // reset logo - logo.setX(displayContainer.width / 2); - logoOpen.setTime(0); - logoClose.setTime(0); - logoButtonAlpha.setTime(0); - logoTimer = 0; - logoState = LogoState.DEFAULT; - - logo.resetHover(); - playButton.resetHover(); - exitButton.resetHover(); - musicPlay.resetHover(); - musicPause.resetHover(); - musicNext.resetHover(); - musicPrevious.resetHover(); - if (repoButton != null) - repoButton.resetHover(); - if (danceRepoButton != null) - danceRepoButton.resetHover(); - updateButton.resetHover(); - restartButton.resetHover(); - downloadsButton.resetHover(); - } - /** * Plays the next track, and adds the previous one to the stack. * @param user {@code true} if this was user-initiated, false otherwise (track end) diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 788fb9a8..af2ca892 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -941,7 +941,6 @@ public class SongMenu extends BaseOpsuState { if (UI.getBackButton().contains(x, y)) { SoundController.playSound(SoundEffect.MENUBACK); - instanceContainer.provide(MainMenu.class).reset(); // TODO d why is this needed displayContainer.switchState(MainMenu.class); return true; } @@ -1091,7 +1090,6 @@ public class SongMenu extends BaseOpsuState { } else*/ { // return to main menu SoundController.playSound(SoundEffect.MENUBACK); - instanceContainer.provide(MainMenu.class).reset(); // TODO d is this needed displayContainer.switchState(MainMenu.class); } return true; From 04449abe62ca769bc8d3d5544254cfea895b5da3 Mon Sep 17 00:00:00 2001 From: yugecin Date: Wed, 18 Jan 2017 23:08:23 +0100 Subject: [PATCH 76/92] readd search field and sort menu in songmenu --- src/itdelatrisu/opsu/states/SongMenu.java | 200 +++++++----------- .../core/state/ComplexOpsuState.java | 2 +- 2 files changed, 79 insertions(+), 123 deletions(-) diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index af2ca892..ce6f431c 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -22,7 +22,6 @@ import itdelatrisu.opsu.GameData; import itdelatrisu.opsu.GameData.Grade; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameMod; -import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.Utils; @@ -62,25 +61,16 @@ import java.nio.file.WatchEvent.Kind; import java.util.Map; import java.util.Stack; -import org.lwjgl.opengl.Display; import org.newdawn.slick.Animation; import org.newdawn.slick.Color; -import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; -import org.newdawn.slick.SlickException; import org.newdawn.slick.SpriteSheet; import org.newdawn.slick.gui.TextField; -import org.newdawn.slick.state.StateBasedGame; -import org.newdawn.slick.state.transition.EasedFadeOutTransition; -import org.newdawn.slick.state.transition.EmptyTransition; -import org.newdawn.slick.state.transition.FadeInTransition; import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.core.events.EventListener; import yugecin.opsudance.core.inject.InstanceContainer; -import yugecin.opsudance.core.state.BaseOpsuState; -import yugecin.opsudance.events.ResolutionChangedEvent; +import yugecin.opsudance.core.state.ComplexOpsuState; /** * "Song Selection" state. @@ -88,7 +78,7 @@ import yugecin.opsudance.events.ResolutionChangedEvent; * Players are able to select a beatmap to play, view previous scores, choose game mods, * manage beatmaps, or change game options from this state. */ -public class SongMenu extends BaseOpsuState { +public class SongMenu extends ComplexOpsuState { private final InstanceContainer instanceContainer; @@ -176,8 +166,7 @@ public class SongMenu extends BaseOpsuState { private MenuButton selectModsButton, selectRandomButton, selectMapOptionsButton, selectOptionsButton; /** The search textfield. */ - //private TextField search; - // TODO d recreate textfield + private TextField searchTextField; /** * Delay timer, in milliseconds, before running another search. @@ -238,7 +227,7 @@ public class SongMenu extends BaseOpsuState { } finally { finished = true; } - }; + } /** Reloads all beatmaps. */ private void reloadBeatmaps() { @@ -329,8 +318,7 @@ public class SongMenu extends BaseOpsuState { private boolean isScrollingToFocusNode = false; /** Sort order dropdown menu. */ - // TODO: d remake dropdownmenu - //private DropdownMenu sortMenu; + private DropdownMenu sortMenu; public SongMenu(final DisplayContainer displayContainer, InstanceContainer instanceContainer) { super(displayContainer); @@ -357,11 +345,10 @@ public class SongMenu extends BaseOpsuState { footerLogoButton.setHoverExpand(1.2f); // initialize sorts - // TODO d reenable dropdown - /* int sortWidth = (int) (displayContainer.width * 0.12f); - sortMenu = new DropdownMenu(container, BeatmapSortOrder.values(), - displayContainer.width * 0.87f, headerY - GameImage.MENU_TAB.getImage().getHeight() * 2.25f, sortWidth) { + int posX = (int) (displayContainer.width * 0.87f); + int posY = (int) (headerY - GameImage.MENU_TAB.getImage().getHeight() * 2.25f); + sortMenu = new DropdownMenu(displayContainer, BeatmapSortOrder.values(), posX, posY, sortWidth) { @Override public void itemSelected(int index, BeatmapSortOrder item) { BeatmapSortOrder.set(item); @@ -375,7 +362,7 @@ public class SongMenu extends BaseOpsuState { } @Override - public boolean menuClicked(int index) { + public boolean canSelect(int index) { if (isInputBlocked()) return false; @@ -386,7 +373,7 @@ public class SongMenu extends BaseOpsuState { sortMenu.setBackgroundColor(Colors.BLACK_BG_HOVER); sortMenu.setBorderColor(Colors.BLUE_DIVIDER); sortMenu.setChevronRightColor(Color.white); - */ + components.add(sortMenu); // initialize group tabs for (BeatmapGroup group : BeatmapGroup.values()) @@ -406,20 +393,20 @@ public class SongMenu extends BaseOpsuState { buttonOffset = (footerY - headerY - DIVIDER_LINE_WIDTH) / MAX_SONG_BUTTONS; // search - // TODO d reenable search box - /* int textFieldX = (int) (displayContainer.width * 0.7125f + Fonts.BOLD.getWidth("Search: ")); int textFieldY = (int) (headerY + Fonts.BOLD.getLineHeight() / 2); - search = new TextField( - container, Fonts.BOLD, textFieldX, textFieldY, - (int) (displayContainer.width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight() - ); - search.setBackgroundColor(Color.transparent); - search.setBorderColor(Color.transparent); - search.setTextColor(Color.white); - search.setConsumeEvents(false); - search.setMaxLength(60); - */ + searchTextField = new TextField(displayContainer, Fonts.BOLD, textFieldX, textFieldY, (int) (displayContainer.width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight()) { + @Override + public boolean isFocusable() { + return false; + } + }; + searchTextField.setBackgroundColor(Color.transparent); + searchTextField.setBorderColor(Color.transparent); + searchTextField.setTextColor(Color.white); + searchTextField.setMaxLength(60); + searchTextField.setFocused(true); + components.add(searchTextField); // selection buttons Image selectionMods = GameImage.SELECTION_MODS.getImage(); @@ -482,9 +469,6 @@ public class SongMenu extends BaseOpsuState { int mouseX = displayContainer.mouseX; int mouseY = displayContainer.mouseY; - // TODO d - //boolean inDropdownMenu = sortMenu.contains(mouseX, mouseY); - // background if (focusNode != null) { Beatmap focusNodeBeatmap = focusNode.getSelectedBeatmap(); @@ -555,11 +539,9 @@ public class SongMenu extends BaseOpsuState { g.clearClip(); // scroll bar - // TODO d - /* - if (focusScores.length > MAX_SCORE_BUTTONS && ScoreData.areaContains(mouseX, mouseY) && !inDropdownMenu) + if (focusScores.length > MAX_SCORE_BUTTONS && ScoreData.areaContains(mouseX, mouseY) && !isAnyComponentFocused()) { ScoreData.drawScrollbar(g, startScorePos.getPosition(), focusScores.length * ScoreData.getButtonOffset()); - */ + } } // top/bottom bars @@ -576,9 +558,7 @@ public class SongMenu extends BaseOpsuState { Float position = MusicController.getBeatProgress(); if (position == null) // default to 60bpm position = System.currentTimeMillis() % 1000 / 1000f; - // TODO d - /* - if (footerLogoButton.contains(mouseX, mouseY, 0.25f) && !inDropdownMenu) { + if (footerLogoButton.contains(mouseX, mouseY, 0.25f)) { // hovering over logo: stop pulsing footerLogoButton.draw(); } else { @@ -591,7 +571,6 @@ public class SongMenu extends BaseOpsuState { ghostLogo.drawCentered(footerLogoButton.getX(), footerLogoButton.getY(), Colors.GHOST_LOGO); Colors.GHOST_LOGO.a = oldGhostAlpha; } - */ // header if (focusNode != null) { @@ -672,9 +651,7 @@ public class SongMenu extends BaseOpsuState { // group tabs BeatmapGroup currentGroup = BeatmapGroup.current(); BeatmapGroup hoverGroup = null; - // TODO d - /* - if (!inDropdownMenu) { + if (!isAnyComponentFocused()) { for (BeatmapGroup group : BeatmapGroup.values()) { if (group.contains(mouseX, mouseY)) { hoverGroup = group; @@ -682,7 +659,6 @@ public class SongMenu extends BaseOpsuState { } } } - */ for (BeatmapGroup group : BeatmapGroup.VALUES_REVERSED) { if (group != currentGroup) group.draw(false, group == hoverGroup); @@ -690,10 +666,9 @@ public class SongMenu extends BaseOpsuState { currentGroup.draw(true, false); // search - // TODO d - /* - boolean searchEmpty = search.getText().isEmpty(); - int searchX = search.getX(), searchY = search.getY(); + boolean searchEmpty = searchTextField.getText().isEmpty(); + int searchX = searchTextField.x; + int searchY = searchTextField.y; float searchBaseX = width * 0.7f; float searchTextX = width * 0.7125f; float searchRectHeight = Fonts.BOLD.getLineHeight() * 2; @@ -712,21 +687,15 @@ public class SongMenu extends BaseOpsuState { g.fillRect(searchBaseX, headerY + DIVIDER_LINE_WIDTH / 2, width - searchBaseX, searchRectHeight); Colors.BLACK_ALPHA.a = oldAlpha; Fonts.BOLD.drawString(searchTextX, searchY, "Search:", Colors.GREEN_SEARCH); - if (searchEmpty) + if (searchEmpty) { Fonts.BOLD.drawString(searchX, searchY, "Type to search!", Color.white); - else { + } else { g.setColor(Color.white); - // TODO: why is this needed to correctly position the TextField? - search.setLocation(searchX - 3, searchY - 1); - search.render(container, g); - search.setLocation(searchX, searchY); - Fonts.DEFAULT.drawString(searchTextX, searchY + Fonts.BOLD.getLineHeight(), - (searchResultString == null) ? "Searching..." : searchResultString, Color.white); + searchTextField.render(g); + Fonts.DEFAULT.drawString(searchTextX, searchY + Fonts.BOLD.getLineHeight(), (searchResultString == null) ? "Searching..." : searchResultString, Color.white); } - // sorting options - sortMenu.render(container, g); - */ + sortMenu.render(g); // reloading beatmaps if (reloadThread != null) { @@ -766,7 +735,6 @@ public class SongMenu extends BaseOpsuState { } int mouseX = displayContainer.mouseX; int mouseY = displayContainer.mouseY; - boolean inDropdownMenu = false; // TODO d sortMenu.contains(mouseX, mouseY); UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); selectModsButton.hoverUpdate(delta, mouseX, mouseY); selectRandomButton.hoverUpdate(delta, mouseX, mouseY); @@ -805,9 +773,6 @@ public class SongMenu extends BaseOpsuState { starStream.update(delta); // search - // TODO d - /* - search.setFocus(true); searchTimer += delta; if (searchTimer >= SEARCH_DELAY && reloadThread == null && beatmapMenuTimer == -1) { searchTimer = 0; @@ -816,12 +781,12 @@ public class SongMenu extends BaseOpsuState { if (focusNode != null) oldFocusNode = new SongNode(BeatmapSetList.get().getBaseNode(focusNode.index), focusNode.beatmapIndex); - if (BeatmapSetList.get().search(search.getText())) { + if (BeatmapSetList.get().search(searchTextField.getText())) { // reset song stack - randomStack = new Stack(); + randomStack = new Stack<>(); // empty search - if (search.getText().isEmpty()) + if (searchTextField.getText().isEmpty()) searchResultString = null; // search produced new list: re-initialize it @@ -830,7 +795,7 @@ public class SongMenu extends BaseOpsuState { focusScores = null; if (BeatmapSetList.get().size() > 0) { BeatmapSetList.get().init(); - if (search.getText().isEmpty()) { // cleared search + if (searchTextField.getText().isEmpty()) { // cleared search // use previous start/focus if possible if (oldFocusNode != null) setFocus(oldFocusNode.getNode(), oldFocusNode.getIndex(), true, true); @@ -843,7 +808,7 @@ public class SongMenu extends BaseOpsuState { setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); } oldFocusNode = null; - } else if (!search.getText().isEmpty()) + } else if (!searchTextField.getText().isEmpty()) searchResultString = "No matches found. Hit ESC to reset."; } } @@ -852,7 +817,6 @@ public class SongMenu extends BaseOpsuState { if (searchTransitionTimer > SEARCH_TRANSITION_TIME) searchTransitionTimer = SEARCH_TRANSITION_TIME; } - */ // scores if (focusScores != null) { @@ -874,7 +838,7 @@ public class SongMenu extends BaseOpsuState { // mouse hover BeatmapSetNode node = getNodeAtPosition(mouseX, mouseY); - if (node != null && !inDropdownMenu) { + if (node != null && !isAnyComponentFocused()) { if (node == hoverIndex) hoverOffset.update(delta); else { @@ -888,11 +852,9 @@ public class SongMenu extends BaseOpsuState { } // tooltips - // TODO d - /* if (sortMenu.baseContains(mouseX, mouseY)) UI.updateTooltip(delta, "Sort by...", false); - else */if (focusScores != null && ScoreData.areaContains(mouseX, mouseY)) { + else if (focusScores != null && ScoreData.areaContains(mouseX, mouseY)) { int startScore = (int) (startScorePos.getPosition() / ScoreData.getButtonOffset()); int offset = (int) (-startScorePos.getPosition() + startScore * ScoreData.getButtonOffset()); int scoreButtons = Math.min(focusScores.length - startScore, MAX_SCORE_BUTTONS); @@ -909,6 +871,10 @@ public class SongMenu extends BaseOpsuState { @Override public boolean mousePressed(int button, int x, int y) { + if (super.mousePressed(button, x, y)) { + return true; + } + if (button == Input.MOUSE_MIDDLE_BUTTON) { return false; } @@ -924,6 +890,10 @@ public class SongMenu extends BaseOpsuState { @Override public boolean mouseReleased(int button, int x, int y) { + if (super.mouseReleased(button, x, y)) { + return true; + } + if (button == Input.MOUSE_MIDDLE_BUTTON) { return false; } @@ -973,7 +943,7 @@ public class SongMenu extends BaseOpsuState { songInfo = null; scoreMap = null; focusScores = null; - // TODO d search.setText(""); + searchTextField.setText(""); searchTimer = SEARCH_DELAY; searchTransitionTimer = SEARCH_TRANSITION_TIME; searchResultString = null; @@ -1076,18 +1046,16 @@ public class SongMenu extends BaseOpsuState { switch (key) { case Input.KEY_ESCAPE: - // TODO d - /* if (reloadThread != null) { // beatmap reloading: stop parsing beatmaps by sending interrupt to BeatmapParser reloadThread.interrupt(); - } else if (!search.getText().isEmpty()) { + } else if (!searchTextField.getText().isEmpty()) { // clear search text - search.setText(""); + searchTextField.setText(""); searchTimer = SEARCH_DELAY; searchTransitionTimer = 0; searchResultString = null; - } else*/ { + } else { // return to main menu SoundController.playSound(SoundEffect.MENUBACK); displayContainer.switchState(MainMenu.class); @@ -1206,9 +1174,8 @@ public class SongMenu extends BaseOpsuState { // TODO: accept all characters (current conditions are from TextField class) if ((c > 31 && c < 127) || key == Input.KEY_BACK) { searchTimer = 0; - // TODO d - //int textLength = search.getText().length(); - int textLength = 0; + searchTextField.keyPressed(key, c); + int textLength = searchTextField.getText().length(); if (lastSearchTextLength != textLength) { if (key == Input.KEY_BACK) { if (textLength == 0) @@ -1223,36 +1190,38 @@ public class SongMenu extends BaseOpsuState { return true; } - // TODO d - /* @Override - public void mouseDragged(int oldx, int oldy, int newx, int newy) { - // block input - if (isInputBlocked()) - return; + public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { + if (super.mouseDragged(oldx, oldy, newx, newy)) { + return true; + } + + if (isInputBlocked()) { + return true; + } int diff = newy - oldy; - if (diff == 0) - return; + if (diff == 0) { + return false; + } // check mouse button (right click scrolls faster on songs) int multiplier; - if (input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) + if (displayContainer.input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) { multiplier = 10; - else if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) + } else if (displayContainer.input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) { multiplier = 1; - else - return; + } else { + return false; + } - // score buttons - if (focusScores != null && focusScores.length >= MAX_SCORE_BUTTONS && ScoreData.areaContains(oldx, oldy)) + if (focusScores != null && focusScores.length >= MAX_SCORE_BUTTONS && ScoreData.areaContains(oldx, oldy)) { startScorePos.dragged(-diff * multiplier); - - // song buttons - else + } else { songScrolling.dragged(-diff * multiplier); + } + return true; } - */ @Override public boolean mouseWheelMoved(int newValue) { @@ -1302,12 +1271,10 @@ public class SongMenu extends BaseOpsuState { songChangeTimer.setTime(songChangeTimer.getDuration()); musicIconBounceTimer.setTime(musicIconBounceTimer.getDuration()); starStream.clear(); - // TODO d - //sortMenu.activate(); - //sortMenu.reset(); + sortMenu.reset(); // reset song stack - randomStack = new Stack(); + randomStack = new Stack<>(); // reload beatmaps if song folder changed if (songFolderChanged && stateAction != MenuState.RELOAD) @@ -1467,16 +1434,6 @@ public class SongMenu extends BaseOpsuState { } } - @Override - public void leave() { - super.leave(); - // TODO d - /* - search.setFocus(false); - sortMenu.deactivate(); - */ - } - /** * Shifts the startNode forward (+) or backwards (-) by a given number of nodes. * Initiates sliding "animation" by shifting the button Y position. @@ -1734,8 +1691,7 @@ public class SongMenu extends BaseOpsuState { songInfo = null; hoverOffset.setTime(0); hoverIndex = null; - // TODO d - //search.setText(""); + searchTextField.setText(""); searchTimer = SEARCH_DELAY; searchTransitionTimer = SEARCH_TRANSITION_TIME; searchResultString = null; diff --git a/src/yugecin/opsudance/core/state/ComplexOpsuState.java b/src/yugecin/opsudance/core/state/ComplexOpsuState.java index f3d1a634..e6e12608 100644 --- a/src/yugecin/opsudance/core/state/ComplexOpsuState.java +++ b/src/yugecin/opsudance/core/state/ComplexOpsuState.java @@ -73,7 +73,7 @@ public class ComplexOpsuState extends BaseOpsuState { } focusedComponent.setFocused(false); focusedComponent = null; - return false; + return true; } @Override From e7eec143419094e579de760c0eacd46f3ed31790 Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 19 Jan 2017 16:03:53 +0100 Subject: [PATCH 77/92] add options menu as overlay --- src/itdelatrisu/opsu/Opsu.java | 2 +- src/itdelatrisu/opsu/Options.java | 11 +- src/itdelatrisu/opsu/states/OptionsMenu.java | 125 +---------------- src/itdelatrisu/opsu/states/SongMenu.java | 19 ++- .../core/state/ComplexOpsuState.java | 77 ++++++++++- .../core/state/OverlayOpsuState.java | 117 ++++++++++++++++ src/yugecin/opsudance/ui/OptionsOverlay.java | 128 +++++++++++------- src/yugecin/opsudance/ui/SBOverlay.java | 8 +- 8 files changed, 297 insertions(+), 190 deletions(-) create mode 100644 src/yugecin/opsudance/core/state/OverlayOpsuState.java diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index 7547cb0d..73493858 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -92,7 +92,7 @@ public class Opsu extends StateBasedGame { addState(new Game(STATE_GAME)); addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); addState(new GameRanking(STATE_GAMERANKING)); - addState(new OptionsMenu(STATE_OPTIONSMENU)); + //addState(new OptionsMenu(STATE_OPTIONSMENU)); //addState(new DownloadsMenu(STATE_DOWNLOADSMENU)); } diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index 272b3057..03a8c772 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -415,8 +415,7 @@ public class Options { SHOW_FPS ("Show FPS Counter", "FpsCounter", "Show an FPS counter in the bottom-right hand corner.", true), SHOW_UNICODE ("Prefer Non-English Metadata", "ShowUnicode", "Where available, song titles will be shown in their native language.", false) { @Override - public void click(GameContainer container) { - super.click(container); + public void click() { if (bool) { try { Fonts.LARGE.loadGlyphs(); @@ -468,8 +467,7 @@ public class Options { }, NEW_CURSOR ("Enable New Cursor", "NewCursor", "Use the new cursor style (may cause higher CPU usage).", true) { @Override - public void click(GameContainer container) { - super.click(container); + public void click() { UI.getCursor().reset(); } }, @@ -1131,9 +1129,8 @@ public class Options { * Processes a mouse click action (via override). *

* By default, this inverts the current {@code bool} field. - * @param container the game container */ - public void click(GameContainer container) { bool = !bool; } + public void click() { bool = !bool; } /** * Get a list of values to choose from @@ -1697,7 +1694,7 @@ public class Options { * sends a bar notification about the action. */ public static void toggleMouseDisabled() { - GameOption.DISABLE_MOUSE_BUTTONS.click(null); + GameOption.DISABLE_MOUSE_BUTTONS.click(); UI.sendBarNotification((GameOption.DISABLE_MOUSE_BUTTONS.getBooleanValue()) ? "Mouse buttons are disabled." : "Mouse buttons are enabled."); } diff --git a/src/itdelatrisu/opsu/states/OptionsMenu.java b/src/itdelatrisu/opsu/states/OptionsMenu.java index 39ffa7ca..03e458b5 100644 --- a/src/itdelatrisu/opsu/states/OptionsMenu.java +++ b/src/itdelatrisu/opsu/states/OptionsMenu.java @@ -18,35 +18,14 @@ package itdelatrisu.opsu.states; -import itdelatrisu.opsu.GameImage; -import itdelatrisu.opsu.Opsu; -import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options.GameOption; -import itdelatrisu.opsu.audio.MusicController; -import itdelatrisu.opsu.audio.SoundController; -import itdelatrisu.opsu.audio.SoundEffect; -import itdelatrisu.opsu.ui.UI; -import org.newdawn.slick.GameContainer; -import org.newdawn.slick.Graphics; -import org.newdawn.slick.Input; -import org.newdawn.slick.SlickException; -import org.newdawn.slick.state.BasicGameState; -import org.newdawn.slick.state.StateBasedGame; -import org.newdawn.slick.state.transition.FadeInTransition; -import org.newdawn.slick.state.transition.EmptyTransition; import yugecin.opsudance.ui.OptionsOverlay; import yugecin.opsudance.ui.OptionsOverlay.OptionTab; -/** - * "Game Options" state. - *

- * Players are able to view and change various game settings in this state. - */ -public class OptionsMenu extends BasicGameState implements OptionsOverlay.Parent { +public class OptionsMenu { - /** Option tabs. */ - private static final OptionTab[] options = new OptionsOverlay.OptionTab[]{ + public static final OptionTab[] normalOptions = new OptionsOverlay.OptionTab[]{ new OptionTab("Display", new GameOption[]{ GameOption.SCREEN_RESOLUTION, GameOption.FULLSCREEN, @@ -154,104 +133,4 @@ public class OptionsMenu extends BasicGameState implements OptionsOverlay.Parent }) }; - private StateBasedGame game; - private Input input; - private final int state; - - private OptionsOverlay optionsOverlay; - - public OptionsMenu(int state) { - this.state = state; - } - - @Override - public void init(GameContainer container, StateBasedGame game) - throws SlickException { - this.game = game; - this.input = container.getInput(); - - optionsOverlay = new OptionsOverlay(this, options, 5, container); - } - - @Override - public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException { - // background - GameImage.OPTIONS_BG.getImage().draw(); - - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); - optionsOverlay.render(g, mouseX, mouseY); - UI.draw(g); - } - - @Override - public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException { - UI.update(delta); - MusicController.loopTrackIfEnded(false); - optionsOverlay.update(delta, input.getMouseX(), input.getMouseY()); - } - - @Override - public int getID() { - return state; - } - - @Override - public void mouseReleased(int button, int x, int y) { - optionsOverlay.mouseReleased(button, x, y); - } - - @Override - public void mousePressed(int button, int x, int y) { - optionsOverlay.mousePressed(button, x, y); - } - - @Override - public void mouseDragged(int oldx, int oldy, int newx, int newy) { - optionsOverlay.mouseDragged(oldx, oldy, newx, newy); - } - - @Override - public void mouseWheelMoved(int newValue) { - optionsOverlay.mouseWheelMoved(newValue); - } - - @Override - public void keyPressed(int key, char c) { - optionsOverlay.keyPressed(key, c); - } - - /** - * This string is built with option values when entering the options menu. - * When leaving the options menu, this string is checked against the new optionstring with the same options. - * If those do not match, it means some option has change which requires a restart - */ - private String restartOptions; - - @Override - public void enter(GameContainer container, StateBasedGame game) - throws SlickException { - UI.enter(); - restartOptions = "" + Options.getResolutionIdx() + Options.isFullscreen() + Options.allowLargeResolutions() + Options.getSkinName(); - } - - @Override - public void leave(GameContainer container, StateBasedGame game) throws SlickException { - if (!("" + Options.getResolutionIdx() + Options.isFullscreen() + Options.allowLargeResolutions() + Options.getSkinName()).equals(restartOptions)) { - container.setForceExit(false); - container.exit(); - return; - } - SoundController.playSound(SoundEffect.MENUBACK); - } - - @Override - public void onLeave() { - game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); - } - - @Override - public void onSaveOption(GameOption option) { - - } - } diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index ce6f431c..aeef2f1e 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -71,6 +71,7 @@ import org.newdawn.slick.gui.TextField; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.state.ComplexOpsuState; +import yugecin.opsudance.ui.OptionsOverlay; /** * "Song Selection" state. @@ -320,15 +321,21 @@ public class SongMenu extends ComplexOpsuState { /** Sort order dropdown menu. */ private DropdownMenu sortMenu; + private final OptionsOverlay optionsOverlay; + public SongMenu(final DisplayContainer displayContainer, InstanceContainer instanceContainer) { super(displayContainer); this.instanceContainer = instanceContainer; + optionsOverlay = new OptionsOverlay(this, displayContainer, OptionsMenu.normalOptions, 0); + overlays.add(optionsOverlay); } @Override public void revalidate() { super.revalidate(); + components.clear(); + // header/footer coordinates headerY = displayContainer.height * 0.0075f + GameImage.MENU_MUSICNOTE.getImage().getHeight() + Fonts.BOLD.getLineHeight() + Fonts.DEFAULT.getLineHeight() + @@ -460,8 +467,6 @@ public class SongMenu extends ComplexOpsuState { @Override public void render(Graphics g) { - super.render(g); - g.setBackground(Color.black); int width = displayContainer.width; @@ -711,6 +716,8 @@ public class SongMenu extends ComplexOpsuState { UI.getBackButton().draw(); UI.draw(g); + + super.render(g); } @Override @@ -927,7 +934,7 @@ public class SongMenu extends ComplexOpsuState { return true; } else if (selectOptionsButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUHIT); - // TODO d displayContainer.switchState(OptionsMenu.class); + optionsOverlay.show(); return true; } @@ -1166,7 +1173,7 @@ public class SongMenu extends ComplexOpsuState { return true; case Input.KEY_O: if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) { - // TODO d displayContainer.switchState(OptionsMenu.class); + optionsOverlay.show(); } return true; default: @@ -1225,6 +1232,10 @@ public class SongMenu extends ComplexOpsuState { @Override public boolean mouseWheelMoved(int newValue) { + if (super.mouseWheelMoved(newValue)) { + return true; + } + Input input = displayContainer.input; if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) { diff --git a/src/yugecin/opsudance/core/state/ComplexOpsuState.java b/src/yugecin/opsudance/core/state/ComplexOpsuState.java index e6e12608..d0dc544e 100644 --- a/src/yugecin/opsudance/core/state/ComplexOpsuState.java +++ b/src/yugecin/opsudance/core/state/ComplexOpsuState.java @@ -24,15 +24,17 @@ import yugecin.opsudance.core.components.Component; import java.util.LinkedList; -public class ComplexOpsuState extends BaseOpsuState { +public abstract class ComplexOpsuState extends BaseOpsuState { protected final LinkedList components; + protected final LinkedList overlays; private Component focusedComponent; public ComplexOpsuState(DisplayContainer displayContainer) { super(displayContainer); this.components = new LinkedList<>(); + this.overlays = new LinkedList<>(); } public final void focusComponent(Component component) { @@ -47,11 +49,55 @@ public class ComplexOpsuState extends BaseOpsuState { } public boolean isAnyComponentFocused() { - return focusedComponent != null; + return focusedComponent != null || isAnyOverlayActive(); + } + + public boolean isAnyOverlayActive() { + for (OverlayOpsuState overlay : overlays) { + if (overlay.active) { + return true; + } + } + return false; + } + + @Override + public boolean mouseWheelMoved(int delta) { + for (OverlayOpsuState overlay : overlays) { + if (overlay.mouseWheelMoved(delta)) { + return true; + } + } + return false; + } + + @Override + public boolean mousePressed(int button, int x, int y) { + for (OverlayOpsuState overlay : overlays) { + if (overlay.mousePressed(button, x, y)) { + return true; + } + } + return false; + } + + @Override + public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { + for (OverlayOpsuState overlay : overlays) { + if (overlay.mouseDragged(oldx, oldy, newx, newy)) { + return true; + } + } + return false; } @Override public boolean mouseReleased(int button, int x, int y) { + for (OverlayOpsuState overlay : overlays) { + if (overlay.mouseReleased(button, x, y)) { + return true; + } + } if (focusedComponent == null) { for (Component component : components) { if (!component.isFocusable()) { @@ -83,14 +129,25 @@ public class ComplexOpsuState extends BaseOpsuState { component.updateHover(displayContainer.mouseX, displayContainer.mouseY); component.preRenderUpdate(); } + for (OverlayOpsuState overlay : overlays) { + overlay.preRenderUpdate(); + } + } + + @Override + protected void revalidate() { + super.revalidate(); + for (OverlayOpsuState overlay : overlays) { + overlay.revalidate(); + } } @Override public void render(Graphics g) { - super.render(g); - for (Component component : components) { - component.render(g); + for (OverlayOpsuState overlay : overlays) { + overlay.render(g); } + super.render(g); } @Override @@ -98,6 +155,11 @@ public class ComplexOpsuState extends BaseOpsuState { if (super.keyReleased(key, c)) { return true; } + for (OverlayOpsuState overlay : overlays) { + if (overlay.keyReleased(key, c)) { + return true; + } + } if (focusedComponent != null) { if (key == Input.KEY_ESCAPE) { focusedComponent.setFocused(false); @@ -112,6 +174,11 @@ public class ComplexOpsuState extends BaseOpsuState { @Override public boolean keyPressed(int key, char c) { + for (OverlayOpsuState overlay : overlays) { + if (overlay.keyPressed(key, c)) { + return true; + } + } if (focusedComponent != null) { if (key == Input.KEY_ESCAPE) { focusedComponent.setFocused(false); diff --git a/src/yugecin/opsudance/core/state/OverlayOpsuState.java b/src/yugecin/opsudance/core/state/OverlayOpsuState.java new file mode 100644 index 00000000..f5df8b46 --- /dev/null +++ b/src/yugecin/opsudance/core/state/OverlayOpsuState.java @@ -0,0 +1,117 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 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.core.state; + +import org.newdawn.slick.Graphics; + +import java.io.StringWriter; + +public abstract class OverlayOpsuState implements OpsuState { + + protected boolean active; + protected boolean acceptInput; + + public abstract void hide(); + public abstract void show(); + + @Override + public final void update() { + } + + public void revalidate() { + } + + protected abstract void onPreRenderUpdate(); + + @Override + public final void preRenderUpdate() { + if (active) { + onPreRenderUpdate(); + } + } + + protected abstract void onRender(Graphics g); + + @Override + public final void render(Graphics g) { + if (active) { + onRender(g); + } + } + + @Override + public final void enter() { + } + + @Override + public final void leave() { + } + + @Override + public final boolean onCloseRequest() { + return true; + } + + protected abstract boolean onKeyPressed(int key, char c); + + @Override + public final boolean keyPressed(int key, char c) { + return acceptInput && onKeyPressed(key, c); + } + + protected abstract boolean onKeyReleased(int key, char c); + + @Override + public final boolean keyReleased(int key, char c) { + return acceptInput && onKeyReleased(key, c); + } + + protected abstract boolean onMouseWheelMoved(int delta); + + @Override + public final boolean mouseWheelMoved(int delta) { + return acceptInput && onMouseWheelMoved(delta); + } + + protected abstract boolean onMousePressed(int button, int x, int y); + + @Override + public final boolean mousePressed(int button, int x, int y) { + return acceptInput && onMousePressed(button, x, y); + } + + protected abstract boolean onMouseReleased(int button, int x, int y); + + @Override + public final boolean mouseReleased(int button, int x, int y) { + return acceptInput && onMouseReleased(button, x, y); + } + + protected abstract boolean onMouseDragged(int oldx, int oldy, int newx, int newy); + + @Override + public final boolean mouseDragged(int oldx, int oldy, int newx, int newy) { + return acceptInput && onMouseDragged(oldx, oldy, newx, newy); + } + + @Override + public void writeErrorDump(StringWriter dump) { + dump.append("> OverlayOpsuState dump\n"); + dump.append("accepts input: ").append(String.valueOf(acceptInput)).append(" is active: ").append(String.valueOf(active)); + } +} diff --git a/src/yugecin/opsudance/ui/OptionsOverlay.java b/src/yugecin/opsudance/ui/OptionsOverlay.java index fdff1e3f..447a41b8 100644 --- a/src/yugecin/opsudance/ui/OptionsOverlay.java +++ b/src/yugecin/opsudance/ui/OptionsOverlay.java @@ -29,16 +29,18 @@ import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.MenuButton; import itdelatrisu.opsu.ui.UI; import org.newdawn.slick.*; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.state.ComplexOpsuState; +import yugecin.opsudance.core.state.OverlayOpsuState; -@SuppressWarnings("UnusedParameters") -public class OptionsOverlay { +public class OptionsOverlay extends OverlayOpsuState { - private Parent parent; - private GameContainer container; + private final ComplexOpsuState parent; + private final DisplayContainer displayContainer; - private final Image sliderBallImg; - private final Image checkOnImg; - private final Image checkOffImg; + private Image sliderBallImg; + private Image checkOnImg; + private Image checkOffImg; private OptionTab[] tabs; private int selectedTab; @@ -79,21 +81,26 @@ public class OptionsOverlay { private int sliderSoundDelay; - public OptionsOverlay(Parent parent, OptionTab[] tabs, int defaultSelectedTabIndex, GameContainer container) { + public OptionsOverlay(ComplexOpsuState parent, DisplayContainer displayContainer, OptionTab[] tabs, int defaultSelectedTabIndex) { this.parent = parent; - this.container = container; + this.displayContainer = displayContainer; this.tabs = tabs; selectedTab = defaultSelectedTabIndex; listHoverIndex = -1; + } + + @Override + public void revalidate() { + super.revalidate(); sliderBallImg = GameImage.CONTROL_SLIDER_BALL.getImage().getScaledCopy(20, 20); checkOnImg = GameImage.CONTROL_CHECK_ON.getImage().getScaledCopy(20, 20); checkOffImg = GameImage.CONTROL_CHECK_OFF.getImage().getScaledCopy(20, 20); - width = container.getWidth(); - height = container.getHeight(); + width = displayContainer.width; + height = displayContainer.height; // calculate positions optionWidth = width / 2; @@ -109,10 +116,12 @@ public class OptionsOverlay { maxScrollOffset = Fonts.MEDIUM.getLineHeight() * 2 * tabs.length; scrollOffset = 0; for (OptionTab tab : tabs) { + /* if (defaultSelectedTabIndex-- > 0) { scrollOffset += Fonts.MEDIUM.getLineHeight() * 2; scrollOffset += tab.options.length * optionHeight; } + */ maxScrollOffset += tab.options.length * optionHeight; tab.button = new MenuButton(tabImage, tabX, tabY); tabX += tabOffset; @@ -127,7 +136,8 @@ public class OptionsOverlay { optionStartY = (int) (tabY + tabImage.getHeight() / 2 + 2); // +2 for the separator line } - public void render(Graphics g, int mouseX, int mouseY) { + @Override + public void onRender(Graphics g) { // bg g.setColor(Colors.BLACK_ALPHA_75); g.fillRect(0, 0, width, height); @@ -136,7 +146,7 @@ public class OptionsOverlay { renderTitle(); // option tabs - renderTabs(mouseX, mouseY); + renderTabs(); // line separator g.setColor(Color.white); @@ -159,7 +169,7 @@ public class OptionsOverlay { UI.getBackButton().draw(); // tooltip - renderTooltip(g, mouseX, mouseY); + renderTooltip(g); // key input options if (keyEntryLeft || keyEntryRight) { @@ -175,15 +185,10 @@ public class OptionsOverlay { Fonts.LARGE.drawString((width - Fonts.LARGE.getWidth(prompt)) / 2, (height - Fonts.LARGE.getLineHeight()) / 2, prompt); } - private void renderTooltip(Graphics g, int mouseX, int mouseY) { + private void renderTooltip(Graphics g) { if (hoverOption != null) { - String optionDescription = hoverOption.getDescription(); - float textWidth = Fonts.SMALL.getWidth(optionDescription); - Color.black.a = 0.7f; - g.setColor(Color.black); - g.fillRoundRect(mouseX + 10, mouseY + 10, 10 + textWidth, 10 + Fonts.SMALL.getLineHeight(), 4); - Fonts.SMALL.drawString(mouseX + 15, mouseY + 15, optionDescription, Color.white); - Color.black.a = 1f; + UI.updateTooltip(displayContainer.renderDelta, hoverOption.getDescription(), false); + UI.drawTooltip(g); } } @@ -322,10 +327,10 @@ public class OptionsOverlay { Fonts.MEDIUM.drawString(optionStartX + optionWidth - valueLen, y, value, Colors.BLUE_BACKGROUND); } - public void renderTabs(int mouseX, int mouseY) { + public void renderTabs() { for (int i = 0; i < tabs.length; i++) { OptionTab tab = tabs[i]; - boolean hovering = tab.button.contains(mouseX, mouseY); + boolean hovering = tab.button.contains(displayContainer.mouseX, displayContainer.mouseY); UI.drawTab(tab.button.getX(), tab.button.getY(), tab.name, i == selectedTab, hovering); } } @@ -339,7 +344,25 @@ public class OptionsOverlay { Fonts.DEFAULT.drawString(marginX, marginY, "Change the way opsu! behaves", Color.white); } - public void update(int delta, int mouseX, int mouseY) { + @Override + public void hide() { + acceptInput = false; + SoundController.playSound(SoundEffect.MENUBACK); + active = false; + } + + @Override + public void show() { + acceptInput = true; + active = true; + } + + @Override + public void onPreRenderUpdate() { + int mouseX = displayContainer.mouseX; + int mouseY = displayContainer.mouseY; + int delta = displayContainer.renderDelta; + if (sliderSoundDelay > 0) { sliderSoundDelay -= delta; } @@ -352,7 +375,7 @@ public class OptionsOverlay { UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); if (isAdjustingSlider) { int sliderValue = hoverOption.getIntegerValue(); - updateSliderOption(mouseX, mouseY); + updateSliderOption(); if (hoverOption.getIntegerValue() - sliderValue != 0 && sliderSoundDelay <= 0) { sliderSoundDelay = 90; SoundController.playSound(SoundEffect.MENUHIT); @@ -366,22 +389,23 @@ public class OptionsOverlay { } } - public void mousePressed(int button, int x, int y) { + @Override + public boolean onMousePressed(int button, int x, int y) { if (keyEntryLeft || keyEntryRight) { keyEntryLeft = keyEntryRight = false; - return; + return true; } if (isListOptionOpen) { if (y > optionStartY && listStartX <= x && x < listStartX + listWidth && listStartY <= y && y < listStartY + listHeight) { hoverOption.clickListItem(listHoverIndex); - parent.onSaveOption(hoverOption); + // TODO d parent.onSaveOption(hoverOption); SoundController.playSound(SoundEffect.MENUCLICK); } isListOptionOpen = false; listHoverIndex = -1; updateHoverOption(x, y); - return; + return true; } mousePressY = y; @@ -393,35 +417,37 @@ public class OptionsOverlay { } else if (hoverOption.getType() == OptionType.NUMERIC) { isAdjustingSlider = sliderOptionStartX <= x && x < sliderOptionStartX + sliderOptionLength; if (isAdjustingSlider) { - updateSliderOption(x, y); + updateSliderOption(); } } } if (UI.getBackButton().contains(x, y)) { - parent.onLeave(); + hide(); } + return true; } - public void mouseReleased(int button, int x, int y) { + @Override + public boolean onMouseReleased(int button, int x, int y) { selectedOption = null; if (isAdjustingSlider) { - parent.onSaveOption(hoverOption); + // TODO d parent.onSaveOption(hoverOption); } isAdjustingSlider = false; sliderOptionLength = 0; // check if clicked, not dragged if (Math.abs(y - mousePressY) >= 5) { - return; + return true; } if (hoverOption != null) { if (hoverOption.getType() == OptionType.BOOLEAN) { - hoverOption.click(container); - parent.onSaveOption(hoverOption); + hoverOption.click(); + // TODO d parent.onSaveOption(hoverOption); SoundController.playSound(SoundEffect.MENUHIT); - return; + return true; } else if (hoverOption == GameOption.KEY_LEFT) { keyEntryLeft = true; } else if (hoverOption == GameOption.KEY_RIGHT) { @@ -435,27 +461,33 @@ public class OptionsOverlay { if (tab.button.contains(x, y)) { scrollOffset = tScrollOffset; SoundController.playSound(SoundEffect.MENUCLICK); - return; + return true; } tScrollOffset += Fonts.MEDIUM.getLineHeight() * 2; tScrollOffset += tab.options.length * optionHeight; } + return true; } - public void mouseDragged(int oldx, int oldy, int newx, int newy) { + @Override + public boolean onMouseDragged(int oldx, int oldy, int newx, int newy) { if (!isAdjustingSlider) { scrollOffset = Utils.clamp(scrollOffset + oldy - newy, 0, maxScrollOffset); } + return true; } - public void mouseWheelMoved(int delta) { + @Override + public boolean onMouseWheelMoved(int delta) { if (!isAdjustingSlider) { scrollOffset = Utils.clamp(scrollOffset - delta, 0, maxScrollOffset); } updateHoverOption(prevMouseX, prevMouseY); + return true; } - public boolean keyPressed(int key, char c) { + @Override + public boolean onKeyPressed(int key, char c) { if (keyEntryRight) { Options.setGameKeyRight(key); keyEntryRight = false; @@ -475,16 +507,21 @@ public class OptionsOverlay { listHoverIndex = -1; return true; } - parent.onLeave(); + hide(); return true; } return false; } - private void updateSliderOption(int mouseX, int mouseY) { + @Override + public boolean onKeyReleased(int key, char c) { + return false; + } + + private void updateSliderOption() { int min = hoverOption.getMinValue(); int max = hoverOption.getMaxValue(); - int value = min + Math.round((float) (max - min) * (mouseX - sliderOptionStartX) / (sliderOptionLength)); + int value = min + Math.round((float) (max - min) * (displayContainer.mouseX - sliderOptionStartX) / (sliderOptionLength)); hoverOption.setValue(Utils.clamp(value, min, max)); } @@ -536,7 +573,6 @@ public class OptionsOverlay { public interface Parent { void onLeave(); - void onSaveOption(GameOption option); } diff --git a/src/yugecin/opsudance/ui/SBOverlay.java b/src/yugecin/opsudance/ui/SBOverlay.java index bd03da4d..723c0afb 100644 --- a/src/yugecin/opsudance/ui/SBOverlay.java +++ b/src/yugecin/opsudance/ui/SBOverlay.java @@ -112,7 +112,7 @@ public class SBOverlay implements OptionsOverlay.Parent { private final Game game; private final MoveStoryboard msb; - private final OptionsOverlay overlay; + private OptionsOverlay overlay; static { for (OptionTab tab : options) { @@ -124,7 +124,7 @@ public class SBOverlay implements OptionsOverlay.Parent { this.game = game; this.msb = msb; initialOptions = new HashMap<>(); - overlay = new OptionsOverlay(this, options, 2, container); + //overlay = new OptionsOverlay(this, options, 2, container); this.width = container.getWidth(); this.height = container.getHeight(); speed = 10; @@ -160,13 +160,13 @@ public class SBOverlay implements OptionsOverlay.Parent { g.fillRect(curtime * width, height - 10f, 10f, 10f); } if (menu) { - overlay.render(g, container.getInput().getMouseX(), container.getInput().getMouseY()); + //overlay.render(g, container.getInput().getMouseX(), container.getInput().getMouseY()); } } public void update(int delta, int mouseX, int mouseY) { if (Options.isEnableSB() && menu) { - overlay.update(delta, mouseX, mouseY); + //overlay.update(delta, mouseX, mouseY); } msb.update(delta, mouseX, mouseY); } From 4b2f29df985e492cb9585d6c71a101942e9db205 Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 19 Jan 2017 16:16:30 +0100 Subject: [PATCH 78/92] convert gameranking --- src/itdelatrisu/opsu/Opsu.java | 2 +- src/itdelatrisu/opsu/states/GameRanking.java | 163 +++++++++---------- 2 files changed, 75 insertions(+), 90 deletions(-) diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index 73493858..ab738061 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -91,7 +91,7 @@ public class Opsu extends StateBasedGame { //addState(new SongMenu(STATE_SONGMENU)); addState(new Game(STATE_GAME)); addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); - addState(new GameRanking(STATE_GAMERANKING)); + //addState(new GameRanking(STATE_GAMERANKING)); //addState(new OptionsMenu(STATE_OPTIONSMENU)); //addState(new DownloadsMenu(STATE_DOWNLOADSMENU)); } diff --git a/src/itdelatrisu/opsu/states/GameRanking.java b/src/itdelatrisu/opsu/states/GameRanking.java index 00f1b48f..6a92d44b 100644 --- a/src/itdelatrisu/opsu/states/GameRanking.java +++ b/src/itdelatrisu/opsu/states/GameRanking.java @@ -21,9 +21,6 @@ package itdelatrisu.opsu.states; import itdelatrisu.opsu.GameData; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameMod; -import itdelatrisu.opsu.Opsu; -import itdelatrisu.opsu.Options; -import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundEffect; @@ -35,17 +32,13 @@ import itdelatrisu.opsu.ui.UI; import java.io.FileNotFoundException; import java.io.IOException; -import org.lwjgl.opengl.Display; -import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; -import org.newdawn.slick.SlickException; -import org.newdawn.slick.state.BasicGameState; -import org.newdawn.slick.state.StateBasedGame; -import org.newdawn.slick.state.transition.FadeInTransition; -import org.newdawn.slick.state.transition.EasedFadeOutTransition; import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.inject.InstanceContainer; +import yugecin.opsudance.core.state.BaseOpsuState; /** * "Game Ranking" (score card) state. @@ -54,7 +47,10 @@ import org.newdawn.slick.util.Log; * or watch a replay of the game from this state. * */ -public class GameRanking extends BasicGameState { +public class GameRanking extends BaseOpsuState { + + private final InstanceContainer instanceContainer; + /** Associated GameData object. */ private GameData data; @@ -64,48 +60,34 @@ public class GameRanking extends BasicGameState { /** Button coordinates. */ private float retryY, replayY; - // game-related variables - private GameContainer container; - private StateBasedGame game; - private final int state; - private Input input; - - public GameRanking(int state) { - this.state = state; + public GameRanking(DisplayContainer displayContainer, InstanceContainer instanceContainer) { + super(displayContainer); + this.instanceContainer = instanceContainer; } @Override - public void init(GameContainer container, StateBasedGame game) - throws SlickException { - this.container = container; - this.game = game; - this.input = container.getInput(); - - int width = container.getWidth(); - int height = container.getHeight(); + public void revalidate() { + super.revalidate(); // buttons Image retry = GameImage.PAUSE_RETRY.getImage(); Image replay = GameImage.PAUSE_REPLAY.getImage(); - replayY = (height * 0.985f) - replay.getHeight() / 2f; + replayY = (displayContainer.height * 0.985f) - replay.getHeight() / 2f; retryY = replayY - (replay.getHeight() / 2f) - (retry.getHeight() / 1.975f); - retryButton = new MenuButton(retry, width - (retry.getWidth() / 2f), retryY); - replayButton = new MenuButton(replay, width - (replay.getWidth() / 2f), replayY); + retryButton = new MenuButton(retry, displayContainer.width - (retry.getWidth() / 2f), retryY); + replayButton = new MenuButton(replay, displayContainer.width - (replay.getWidth() / 2f), replayY); retryButton.setHoverFade(); replayButton.setHoverFade(); } @Override - public void render(GameContainer container, StateBasedGame game, Graphics g) - throws SlickException { - int width = container.getWidth(); - int height = container.getHeight(); - + public void render(Graphics g) { Beatmap beatmap = MusicController.getBeatmap(); // background - if (!beatmap.drawBackground(width, height, 0.7f, true)) - GameImage.PLAYFIELD.getImage().draw(0,0); + if (!beatmap.drawBackground(displayContainer.width, displayContainer.height, 0.7f, true)) { + GameImage.PLAYFIELD.getImage().draw(0, 0); + } // ranking screen elements data.drawRankingElements(g, beatmap); @@ -117,63 +99,62 @@ public class GameRanking extends BasicGameState { UI.getBackButton().draw(); UI.draw(g); + + super.render(g); } @Override - public void update(GameContainer container, StateBasedGame game, int delta) - throws SlickException { + public void preRenderUpdate() { + int delta = displayContainer.renderDelta; UI.update(delta); - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); - replayButton.hoverUpdate(delta, mouseX, mouseY); - if (data.isGameplay()) - retryButton.hoverUpdate(delta, mouseX, mouseY); - else + replayButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); + if (data.isGameplay()) { + retryButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); + } else { MusicController.loopTrackIfEnded(true); - UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); - } - - @Override - public int getID() { return state; } - - @Override - public void mouseWheelMoved(int newValue) { - if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) - UI.changeVolume((newValue < 0) ? -1 : 1); - } - - @Override - public void keyPressed(int key, char c) { - switch (key) { - case Input.KEY_ESCAPE: - returnToSongMenu(); - break; - case Input.KEY_F7: - // TODO d - //Options.setNextFPS(container); - break; - case Input.KEY_F10: - Options.toggleMouseDisabled(); - break; - case Input.KEY_F12: - Utils.takeScreenShot(); - break; } + UI.getBackButton().hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); } @Override - public void mousePressed(int button, int x, int y) { + public boolean mouseWheelMoved(int newValue) { + if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) { + UI.changeVolume((newValue < 0) ? -1 : 1); + } + return true; + } + + @Override + public boolean keyPressed(int key, char c) { + if (super.keyPressed(key, c)) { + return true; + } + + if (key == Input.KEY_ESCAPE) { + returnToSongMenu(); + } + return true; + } + + @Override + public boolean mousePressed(int button, int x, int y) { + if (super.mousePressed(button, x, y)) { + return true; + } + // check mouse button - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; + if (button == Input.MOUSE_MIDDLE_BUTTON) { + return false; + } // back to menu if (UI.getBackButton().contains(x, y)) { returnToSongMenu(); - return; + return true; } // replay - Game gameState = (Game) game.getState(Opsu.STATE_GAME); + Game gameState = instanceContainer.provide(Game.class); boolean returnToGame = false; boolean replayButtonPressed = replayButton.contains(x, y); if (replayButtonPressed && !(data.isGameplay() && GameMod.AUTO.isActive())) { @@ -207,16 +188,16 @@ public class GameRanking extends BasicGameState { Beatmap beatmap = MusicController.getBeatmap(); gameState.loadBeatmap(beatmap); SoundController.playSound(SoundEffect.MENUHIT); - game.enterState(Opsu.STATE_GAME, new EasedFadeOutTransition(), new FadeInTransition()); - return; + // TODO d displayContainer.switchState(Game.class); } + return true; } @Override - public void enter(GameContainer container, StateBasedGame game) - throws SlickException { + public void enter() { + super.enter(); + UI.enter(); - Display.setTitle(game.getTitle()); if (!data.isGameplay()) { if (!MusicController.isTrackDimmed()) MusicController.toggleTrackDimmed(0.5f); @@ -230,11 +211,13 @@ public class GameRanking extends BasicGameState { } @Override - public void leave(GameContainer container, StateBasedGame game) - throws SlickException { + public void leave() { + super.leave(); + this.data = null; - if (MusicController.isTrackDimmed()) + if (MusicController.isTrackDimmed()) { MusicController.toggleTrackDimmed(1f); + } } /** @@ -243,13 +226,15 @@ public class GameRanking extends BasicGameState { private void returnToSongMenu() { SoundController.muteSoundComponent(); SoundController.playSound(SoundEffect.MENUBACK); - SongMenu songMenu = (SongMenu) game.getState(Opsu.STATE_SONGMENU); - if (data.isGameplay()) + SongMenu songMenu = instanceContainer.provide(SongMenu.class); + if (data.isGameplay()) { songMenu.resetTrackOnLoad(); + } songMenu.resetGameDataOnLoad(); - if (UI.getCursor().isBeatmapSkinned()) + if (UI.getCursor().isBeatmapSkinned()) { UI.getCursor().reset(); - game.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition()); + } + displayContainer.switchState(SongMenu.class); } /** From a3df6e12d6325d6e9f327f249481ad4ffd893373 Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 19 Jan 2017 19:23:31 +0100 Subject: [PATCH 79/92] convert game --- src/itdelatrisu/opsu/Opsu.java | 2 +- src/itdelatrisu/opsu/objects/Circle.java | 3 +- src/itdelatrisu/opsu/objects/Slider.java | 8 +- src/itdelatrisu/opsu/objects/Spinner.java | 8 +- src/itdelatrisu/opsu/states/Game.java | 482 ++++++++++-------- src/itdelatrisu/opsu/states/OptionsMenu.java | 59 +++ src/itdelatrisu/opsu/states/SongMenu.java | 4 +- .../opsudance/core/DisplayContainer.java | 8 +- .../core/inject/OpsuDanceInjector.java | 2 + .../core/state/OverlayOpsuState.java | 9 +- .../opsudance/sbv2/MoveStoryboard.java | 112 ++-- src/yugecin/opsudance/ui/OptionsOverlay.java | 50 +- ...{SBOverlay.java => StoryboardOverlay.java} | 173 ++----- 13 files changed, 497 insertions(+), 423 deletions(-) rename src/yugecin/opsudance/ui/{SBOverlay.java => StoryboardOverlay.java} (59%) diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index ab738061..131178b8 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -89,7 +89,7 @@ public class Opsu extends StateBasedGame { //addState(new MainMenu(STATE_MAINMENU)); //addState(new ButtonMenu(STATE_BUTTONMENU)); //addState(new SongMenu(STATE_SONGMENU)); - addState(new Game(STATE_GAME)); + //addState(new Game(STATE_GAME)); addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); //addState(new GameRanking(STATE_GAMERANKING)); //addState(new OptionsMenu(STATE_OPTIONSMENU)); diff --git a/src/itdelatrisu/opsu/objects/Circle.java b/src/itdelatrisu/opsu/objects/Circle.java index 7c0d5756..56d393b2 100644 --- a/src/itdelatrisu/opsu/objects/Circle.java +++ b/src/itdelatrisu/opsu/objects/Circle.java @@ -64,10 +64,9 @@ public class Circle extends GameObject { /** * Initializes the Circle data type with map modifiers, images, and dimensions. - * @param container the game container * @param circleDiameter the circle diameter */ - public static void init(GameContainer container, float circleDiameter) { + public static void init(float circleDiameter) { diameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480) int diameterInt = (int) diameter; GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(diameterInt, diameterInt)); diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java index f8ad3441..6ffaee19 100644 --- a/src/itdelatrisu/opsu/objects/Slider.java +++ b/src/itdelatrisu/opsu/objects/Slider.java @@ -37,6 +37,7 @@ import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import yugecin.opsudance.Dancer; +import yugecin.opsudance.core.DisplayContainer; /** * Data type representing a slider object. @@ -127,13 +128,12 @@ public class Slider extends GameObject { /** * Initializes the Slider data type with images and dimensions. - * @param container the game container * @param circleDiameter the circle diameter * @param beatmap the associated beatmap */ - public static void init(GameContainer container, float circleDiameter, Beatmap beatmap) { - containerWidth = container.getWidth(); - containerHeight = container.getHeight(); + public static void init(DisplayContainer displayContainer, float circleDiameter, Beatmap beatmap) { + containerWidth = displayContainer.width; + containerHeight = displayContainer.height; diameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480) int diameterInt = (int) diameter; diff --git a/src/itdelatrisu/opsu/objects/Spinner.java b/src/itdelatrisu/opsu/objects/Spinner.java index 551c80f8..bb3ed811 100644 --- a/src/itdelatrisu/opsu/objects/Spinner.java +++ b/src/itdelatrisu/opsu/objects/Spinner.java @@ -35,6 +35,7 @@ import org.newdawn.slick.Color; import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; +import yugecin.opsudance.core.DisplayContainer; /** * Data type representing a spinner object. @@ -109,12 +110,11 @@ public class Spinner extends GameObject { /** * Initializes the Spinner data type with images and dimensions. - * @param container the game container * @param difficulty the map's overall difficulty value */ - public static void init(GameContainer container, float difficulty) { - width = container.getWidth(); - height = container.getHeight(); + public static void init(DisplayContainer displayContainer, float difficulty) { + width = displayContainer.width; + height = displayContainer.height; overallDifficulty = difficulty; } diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index 49bb6c95..1dd3c7dd 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -18,11 +18,9 @@ package itdelatrisu.opsu.states; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.GameData; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameMod; -import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.Utils; @@ -59,26 +57,28 @@ import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.Display; import org.newdawn.slick.Animation; import org.newdawn.slick.Color; -import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; import org.newdawn.slick.SlickException; -import org.newdawn.slick.state.BasicGameState; -import org.newdawn.slick.state.StateBasedGame; -import org.newdawn.slick.state.transition.DelayedFadeOutTransition; -import org.newdawn.slick.state.transition.EasedFadeOutTransition; -import org.newdawn.slick.state.transition.EmptyTransition; -import org.newdawn.slick.state.transition.FadeInTransition; +import org.newdawn.slick.util.Log; import yugecin.opsudance.*; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.inject.InstanceContainer; +import yugecin.opsudance.core.state.ComplexOpsuState; +import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.objects.curves.FakeCombinedCurve; import yugecin.opsudance.sbv2.MoveStoryboard; -import yugecin.opsudance.ui.SBOverlay; +import yugecin.opsudance.ui.OptionsOverlay; +import yugecin.opsudance.ui.StoryboardOverlay; /** * "Game" state. */ -public class Game extends BasicGameState { +public class Game extends ComplexOpsuState { + + private final InstanceContainer instanceContainer; + public static boolean isInGame; // TODO delete this when #79 is fixed /** Game restart states. */ public enum Restart { @@ -307,101 +307,95 @@ public class Game extends BasicGameState { MUSICBAR_HOVER = new Color(12, 9, 10, 0.35f), MUSICBAR_FILL = new Color(255, 255, 255, 0.75f); - // game-related variables - private GameContainer container; - private StateBasedGame game; - private Input input; - private final int state; - private final Cursor mirrorCursor; - private MoveStoryboard msb; - private SBOverlay sbOverlay; + private final MoveStoryboard moveStoryboardOverlay; + private final StoryboardOverlay storyboardOverlay; + private final OptionsOverlay optionsOverlay; private FakeCombinedCurve knorkesliders; private boolean skippedToCheckpoint; - public Game(int state) { - this.state = state; + public Game(DisplayContainer displayContainer, InstanceContainer instanceContainer) { + super(displayContainer); + this.instanceContainer = instanceContainer; mirrorCursor = new Cursor(true); - } - - public void loadCheckpoint(int checkpoint) { - try { - restart = Restart.MANUAL; - checkpointLoaded = true; - skippedToCheckpoint = true; - enter(container, game); - if (isLeadIn()) { - leadInTime = 0; - epiImgTime = 0; - MusicController.resume(); - } - // skip to checkpoint - MusicController.setPosition(checkpoint); - while (objectIndex < gameObjects.length && beatmap.objects[objectIndex].getTime() <= checkpoint) { - objectIndex++; - } - if (objectIndex > 0) { - objectIndex--; - } - if (Options.isMergingSliders()) { - int obj = objectIndex; - while (obj < gameObjects.length) { - if (gameObjects[obj] instanceof Slider) { - slidercurveFrom = slidercurveTo = ((Slider) gameObjects[obj]).baseSliderFrom; - break; - } - obj++; - } - spliceSliderCurve(-1, -1); - } - Dancer.instance.setObjectIndex(objectIndex); - sbOverlay.updateIndex(objectIndex); - lastReplayTime = beatmap.objects[objectIndex].getTime(); - } catch (SlickException e) { - e.printStackTrace(); - } + this.moveStoryboardOverlay = new MoveStoryboard(displayContainer); + this.optionsOverlay = new OptionsOverlay(displayContainer, OptionsMenu.storyboardOptions, 0); + this.storyboardOverlay = new StoryboardOverlay(displayContainer, moveStoryboardOverlay, optionsOverlay, this); + storyboardOverlay.show(); + moveStoryboardOverlay.show(); + optionsOverlay.setListener(storyboardOverlay); } @Override - public void init(GameContainer container, StateBasedGame game) - throws SlickException { - this.msb = new MoveStoryboard(container); - this.sbOverlay = new SBOverlay(this, msb, container); - this.container = container; - this.game = game; - input = container.getInput(); - - int width = container.getWidth(); - int height = container.getHeight(); - + public void revalidate() { // create offscreen graphics - offscreen = new Image(width, height); - gOffscreen = offscreen.getGraphics(); - gOffscreen.setBackground(Color.black); + try { + offscreen = new Image(displayContainer.width, displayContainer.height); + gOffscreen = offscreen.getGraphics(); + gOffscreen.setBackground(Color.black); + } catch (SlickException e) { + Log.error("could not create offscreen graphics", e); + displayContainer.eventBus.post(new BubbleNotificationEvent("Exception while creating offscreen graphics. See logfile for details.", BubbleNotificationEvent.COMMONCOLOR_RED)); + } // initialize music position bar location - musicBarX = width * 0.01f; - musicBarY = height * 0.05f; - musicBarWidth = Math.max(width * 0.005f, 7); - musicBarHeight = height * 0.9f; + musicBarX = displayContainer.width * 0.01f; + musicBarY = displayContainer.height * 0.05f; + musicBarWidth = Math.max(displayContainer.width * 0.005f, 7); + musicBarHeight = displayContainer.height * 0.9f; // initialize scoreboard star stream - scoreboardStarStream = new StarStream(0, height * 2f / 3f, width / 4, 0, 0); - scoreboardStarStream.setPositionSpread(height / 20f); + scoreboardStarStream = new StarStream(0, displayContainer.height * 2f / 3f, displayContainer.width / 4, 0, 0); + scoreboardStarStream.setPositionSpread(displayContainer.height / 20f); scoreboardStarStream.setDirectionSpread(10f); scoreboardStarStream.setDurationSpread(700, 100); // create the associated GameData object - data = new GameData(width, height); + data = new GameData(displayContainer.width, displayContainer.height); + } + + + public void loadCheckpoint(int checkpoint) { + restart = Restart.MANUAL; + checkpointLoaded = true; + skippedToCheckpoint = true; + enter(); + if (isLeadIn()) { + leadInTime = 0; + epiImgTime = 0; + MusicController.resume(); + } + // skip to checkpoint + MusicController.setPosition(checkpoint); + while (objectIndex < gameObjects.length && beatmap.objects[objectIndex].getTime() <= checkpoint) { + objectIndex++; + } + if (objectIndex > 0) { + objectIndex--; + } + if (Options.isMergingSliders()) { + int obj = objectIndex; + while (obj < gameObjects.length) { + if (gameObjects[obj] instanceof Slider) { + slidercurveFrom = slidercurveTo = ((Slider) gameObjects[obj]).baseSliderFrom; + break; + } + obj++; + } + spliceSliderCurve(-1, -1); + } + Dancer.instance.setObjectIndex(objectIndex); + storyboardOverlay.updateIndex(objectIndex); + lastReplayTime = beatmap.objects[objectIndex].getTime(); } @Override - public void render(GameContainer container, StateBasedGame game, Graphics g) - throws SlickException { - int width = container.getWidth(); - int height = container.getHeight(); + public void render(Graphics g) { + int width = displayContainer.width; + int height = displayContainer.height; + int trackPosition = MusicController.getPosition(); if (isLeadIn()) { trackPosition -= leadInTime - currentMapMusicOffset - Options.getMusicOffset(); @@ -475,7 +469,7 @@ public class Game extends BasicGameState { } } - float[] sbPosition = sbOverlay.getPoint(trackPosition); + float[] sbPosition = moveStoryboardOverlay.getPoint(trackPosition); if (sbPosition != null) { autoPoint.x = sbPosition[0]; autoPoint.y = sbPosition[1]; @@ -510,8 +504,8 @@ public class Game extends BasicGameState { mouseX = replayX; mouseY = replayY; } else { - mouseX = input.getMouseX(); - mouseY = input.getMouseY(); + mouseX = displayContainer.mouseX; + mouseY = displayContainer.mouseY; } int alphaRadius = flashlightRadius * 256 / 215; int alphaX = mouseX - alphaRadius / 2; @@ -686,7 +680,7 @@ public class Game extends BasicGameState { float animation = AnimationEquation.IN_OUT_QUAD.calc( Utils.clamp((trackPosition - lastRankUpdateTime) / SCOREBOARD_ANIMATION_TIME, 0f, 1f) ); - int scoreboardPosition = 2 * container.getHeight() / 3; + int scoreboardPosition = 2 * displayContainer.height / 3; // draw star stream behind the scores scoreboardStarStream.draw(); @@ -727,8 +721,7 @@ public class Game extends BasicGameState { // draw music position bar (for replay seeking) if (isReplay && Options.isReplaySeekingEnabled()) { - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); - g.setColor((musicPositionBarContains(mouseX, mouseY)) ? MUSICBAR_HOVER : MUSICBAR_NORMAL); + g.setColor((musicPositionBarContains(displayContainer.mouseX, displayContainer.mouseY)) ? MUSICBAR_HOVER : MUSICBAR_NORMAL); g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4); if (!isLeadIn()) { g.setColor(MUSICBAR_FILL); @@ -770,24 +763,27 @@ public class Game extends BasicGameState { else UI.draw(g); - sbOverlay.render(container, g); - if (!Options.isHideWM()) { Fonts.SMALL.drawString(0.3f, 0.3f, "opsu!dance " + Updater.get().getCurrentVersion() + " by robin_be | https://github.com/yugecin/opsu-dance"); } + + super.render(g); } @Override - public void update(GameContainer container, StateBasedGame game, int delta) - throws SlickException { + public void preRenderUpdate() { + super.preRenderUpdate(); + + int delta = displayContainer.renderDelta; + UI.update(delta); Pippi.update(delta); if (epiImgTime > 0) { epiImgTime -= delta; } yugecin.opsudance.spinners.Spinner.update(delta); - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); - sbOverlay.update(delta, mouseX, mouseY); + int mouseX = displayContainer.mouseX; + int mouseY = displayContainer.mouseY; skipButton.hoverUpdate(delta, mouseX, mouseY); if (isReplay || GameMod.AUTO.isActive()) playbackSpeed.getButton().hoverUpdate(delta, mouseX, mouseY); @@ -805,8 +801,8 @@ public class Game extends BasicGameState { } // focus lost: go back to pause screen - else if (!container.hasFocus()) { - game.enterState(Opsu.STATE_GAMEPAUSEMENU); + else if (!Display.isActive()) { + // TODO d displayContainer.switchState(GamePauseMenu.class); pausePulse = 0f; } @@ -918,10 +914,13 @@ public class Game extends BasicGameState { // game finished: change state after timer expires if (gameFinished && !gameFinishedTimer.update(delta)) { - if (checkpointLoaded) // if checkpoint used, skip ranking screen - game.closeRequested(); - else // go to ranking screen - game.enterState(Opsu.STATE_GAMERANKING, new EasedFadeOutTransition(), new FadeInTransition()); + if (checkpointLoaded) { + // if checkpoint used, skip ranking screen + onCloseRequest(); + } else { + // go to ranking screen + displayContainer.switchState(GameRanking.class); + } } } @@ -949,7 +948,7 @@ public class Game extends BasicGameState { // save score and replay if (!checkpointLoaded) { boolean unranked = (GameMod.AUTO.isActive() || GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()); - ((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data); + instanceContainer.provide(GameRanking.class).setGameData(data); if (isReplay) data.setReplay(replay); else if (replayFrames != null) { @@ -1027,14 +1026,15 @@ public class Game extends BasicGameState { } // pause game if focus lost - if (!container.hasFocus() && !GameMod.AUTO.isActive() && !isReplay) { + if (!Display.isActive() && !GameMod.AUTO.isActive() && !isReplay) { if (pauseTime < 0) { pausedMousePosition = new Vec2f(mouseX, mouseY); pausePulse = 0f; } - if (MusicController.isPlaying() || isLeadIn()) + if (MusicController.isPlaying() || isLeadIn()) { pauseTime = trackPosition; - game.enterState(Opsu.STATE_GAMEPAUSEMENU, new EmptyTransition(), new FadeInTransition()); + } + // TODO d displayContainer.switchState(GamePauseMenu.class); } // drain health @@ -1058,13 +1058,10 @@ public class Game extends BasicGameState { failTrackTime = MusicController.getPosition(); MusicController.fadeOut(MUSIC_FADEOUT_TIME); MusicController.pitchFadeOut(MUSIC_FADEOUT_TIME); - rotations = new IdentityHashMap(); + rotations = new IdentityHashMap<>(); SoundController.playSound(SoundEffect.FAIL); - // fade to pause menu - game.enterState(Opsu.STATE_GAMEPAUSEMENU, - new DelayedFadeOutTransition(Color.black, MUSIC_FADEOUT_TIME, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME), - new FadeInTransition()); + // TODO d displayContainer.switchState(GamePauseMenu.class, FadeOutTransitionState.class, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME, FadeInTransitionState.class, 300); } } } @@ -1088,7 +1085,7 @@ public class Game extends BasicGameState { if (gameObjects[objectIndex].update(overlap, delta, mouseX, mouseY, keyPressed, trackPosition)) { skippedObject = true; objectIndex++; // done, so increment object index - sbOverlay.updateIndex(objectIndex); + storyboardOverlay.updateIndex(objectIndex); if (objectIndex >= mirrorTo) { Options.setMirror(false); } @@ -1099,20 +1096,25 @@ public class Game extends BasicGameState { } @Override - public int getID() { return state; } + public boolean onCloseRequest() { + instanceContainer.provide(SongMenu.class).resetGameDataOnLoad(); + displayContainer.switchState(SongMenu.class); + return false; + } @Override - public void keyPressed(int key, char c) { - if (gameFinished) - return; + public boolean keyPressed(int key, char c) { + if (super.keyPressed(key, c)) { + return true; + } - if (sbOverlay.keyPressed(key, c)) { - return; + if (gameFinished) { + return true; } int trackPosition = MusicController.getPosition(); - int mouseX = input.getMouseX(); - int mouseY = input.getMouseY(); + int mouseX = displayContainer.mouseX; + int mouseY = displayContainer.mouseY; // game keys if (!Keyboard.isRepeatEvent()) { @@ -1129,7 +1131,7 @@ public class Game extends BasicGameState { case Input.KEY_ESCAPE: // "auto" mod or watching replay: go back to song menu if (GameMod.AUTO.isActive() || isReplay) { - game.closeRequested(); + onCloseRequest(); break; } @@ -1138,9 +1140,10 @@ public class Game extends BasicGameState { pausedMousePosition = new Vec2f(mouseX, mouseY); pausePulse = 0f; } - if (MusicController.isPlaying() || isLeadIn()) + if (MusicController.isPlaying() || isLeadIn()) { pauseTime = trackPosition; - game.enterState(Opsu.STATE_GAMEPAUSEMENU, new EmptyTransition(), new FadeInTransition()); + } + // TODO d displayContainer.switchStateNow(GamePauseMenu.class); break; case Input.KEY_SPACE: // skip intro @@ -1148,23 +1151,21 @@ public class Game extends BasicGameState { break; case Input.KEY_R: // restart - if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) { - try { - if (trackPosition < beatmap.objects[0].getTime()) - retries--; // don't count this retry (cancel out later increment) - restart = Restart.MANUAL; - enter(container, game); - skipIntro(); - } catch (SlickException e) { - ErrorHandler.error("Failed to restart game.", e, false); + if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) { + if (trackPosition < beatmap.objects[0].getTime()) { + retries--; // don't count this retry (cancel out later increment) } + restart = Restart.MANUAL; + enter(); + skipIntro(); } break; case Input.KEY_S: // save checkpoint - if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) { - if (isLeadIn()) + if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) { + if (isLeadIn()) { break; + } int position = (pauseTime > -1) ? pauseTime : trackPosition; if (Options.setCheckpoint(position / 1000)) { @@ -1175,7 +1176,7 @@ public class Game extends BasicGameState { break; case Input.KEY_L: // load checkpoint - if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) { + if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) { int checkpoint = Options.getCheckpoint(); if (checkpoint == 0 || checkpoint > beatmap.endTime) break; // invalid checkpoint @@ -1197,16 +1198,6 @@ public class Game extends BasicGameState { case Input.KEY_DOWN: UI.changeVolume(-1); break; - case Input.KEY_F7: - // TODO d - //Options.setNextFPS(container); - break; - case Input.KEY_F10: - Options.toggleMouseDisabled(); - break; - case Input.KEY_F12: - Utils.takeScreenShot(); - break; case Input.KEY_TAB: if (!Options.isHideUI()) { scoreboardVisible = !scoreboardVisible; @@ -1243,29 +1234,33 @@ public class Game extends BasicGameState { currentMapMusicOffset -= 5; UI.sendBarNotification("Current map offset: " + currentMapMusicOffset); } + + return true; } @Override - public void mouseDragged(int oldx, int oldy, int newx, int newy) { - if (sbOverlay.mouseDragged(oldx, oldy, newx, newy)) { - //noinspection UnnecessaryReturnStatement - return; + public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { + if (super.mouseDragged(oldx, oldy, newx, newy)) { + return true; } + return true; } @Override - public void mousePressed(int button, int x, int y) { - if (gameFinished) - return; + public boolean mousePressed(int button, int x, int y) { + if (super.mousePressed(button, x, y)) { + return true; + } - if (sbOverlay.mousePressed(button, x, y)) { - return; + if (gameFinished) { + return true; } // watching replay if (isReplay || GameMod.AUTO.isActive()) { - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; + if (button == Input.MOUSE_MIDDLE_BUTTON) { + return true; + } // skip button if (skipButton.contains(x, y)) @@ -1284,11 +1279,12 @@ public class Game extends BasicGameState { MusicController.setPosition((int) pos); isSeeking = true; } - return; + return true; } - if (Options.isMouseDisabled()) - return; + if (Options.isMouseDisabled()) { + return true; + } // mouse wheel: pause the game if (button == Input.MOUSE_MIDDLE_BUTTON && !Options.isMouseWheelDisabled()) { @@ -1297,10 +1293,11 @@ public class Game extends BasicGameState { pausedMousePosition = new Vec2f(x, y); pausePulse = 0f; } - if (MusicController.isPlaying() || isLeadIn()) + if (MusicController.isPlaying() || isLeadIn()) { pauseTime = trackPosition; - game.enterState(Opsu.STATE_GAMEPAUSEMENU, new EmptyTransition(), new FadeInTransition()); - return; + } + // TODO d displayContainer.switchStateNow(GamePauseMenu.class); + return true; } // game keys @@ -1311,6 +1308,8 @@ public class Game extends BasicGameState { keys = ReplayFrame.KEY_M2; if (keys != ReplayFrame.KEY_NONE) gameKeyPressed(keys, x, y, MusicController.getPosition()); + + return true; } /** @@ -1353,19 +1352,22 @@ public class Game extends BasicGameState { } @Override - public void mouseReleased(int button, int x, int y) { - if (gameFinished) - return; - - if (sbOverlay.mouseReleased(button, x, y)) { - return; + public boolean mouseReleased(int button, int x, int y) { + if (super.mouseReleased(button, x, y)) { + return true; } - if (Options.isMouseDisabled()) - return; + if (gameFinished) { + return true; + } - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; + if (Options.isMouseDisabled()) { + return true; + } + + if (button == Input.MOUSE_MIDDLE_BUTTON) { + return true; + } int keys = ReplayFrame.KEY_NONE; if (button == Input.MOUSE_LEFT_BUTTON) @@ -1374,12 +1376,19 @@ public class Game extends BasicGameState { keys = ReplayFrame.KEY_M2; if (keys != ReplayFrame.KEY_NONE) gameKeyReleased(keys, x, y, MusicController.getPosition()); + + return true; } @Override - public void keyReleased(int key, char c) { - if (gameFinished) - return; + public boolean keyReleased(int key, char c) { + if (super.keyReleased(key, c)) { + return true; + } + + if (gameFinished) { + return true; + } int keys = ReplayFrame.KEY_NONE; if (key == Options.getGameKeyLeft()) @@ -1387,7 +1396,9 @@ public class Game extends BasicGameState { else if (key == Options.getGameKeyRight()) keys = ReplayFrame.KEY_K2; if (keys != ReplayFrame.KEY_NONE) - gameKeyReleased(keys, input.getMouseX(), input.getMouseY(), MusicController.getPosition()); + gameKeyReleased(keys, displayContainer.input.getMouseX(), displayContainer.input.getMouseY(), MusicController.getPosition()); + + return true; } /** @@ -1405,26 +1416,41 @@ public class Game extends BasicGameState { } @Override - public void mouseWheelMoved(int newValue) { - if (sbOverlay.mouseWheelMoved(newValue)) { - return; + public boolean mouseWheelMoved(int newValue) { + if (super.mouseWheelMoved(newValue)) { + return true; + } + + if (Options.isMouseWheelDisabled()) { + return true; } - if (Options.isMouseWheelDisabled()) - return; UI.changeVolume((newValue < 0) ? -1 : 1); + return true; } @Override - public void enter(GameContainer container, StateBasedGame game) - throws SlickException { + public void enter() { + super.enter(); + + overlays.clear(); + if (Options.isEnableSB()) { + overlays.add(optionsOverlay); + overlays.add(moveStoryboardOverlay); + overlays.add(storyboardOverlay); + storyboardOverlay.onEnter(); + optionsOverlay.revalidate(); + } + isInGame = true; if (!skippedToCheckpoint) { UI.enter(); } - if (beatmap == null || beatmap.objects == null) - throw new RuntimeException("Running game with no beatmap loaded."); + if (beatmap == null || beatmap.objects == null) { + displayContainer.eventBus.post(new BubbleNotificationEvent("Game was running without a beatmap", BubbleNotificationEvent.COMMONCOLOR_RED)); + displayContainer.switchStateInstantly(SongMenu.class); + } Dancer.instance.reset(); MoverDirection.reset(beatmap.beatmapID); @@ -1451,10 +1477,10 @@ public class Game extends BasicGameState { epiImgTime = Options.getEpilepsyWarningLength(); if (epiImgTime > 0) { epiImg = GameImage.EPILEPSY_WARNING.getImage(); - float desWidth = container.getWidth() / 2; + float desWidth = displayContainer.width / 2; epiImg = epiImg.getScaledCopy(desWidth / epiImg.getWidth()); - epiImgX = (container.getWidth() - epiImg.getWidth()) / 2; - epiImgY = (container.getHeight() - epiImg.getHeight()) / 2; + epiImgX = (displayContainer.width - epiImg.getWidth()) / 2; + epiImgY = (displayContainer.height - epiImg.getHeight()) / 2; } // load mods @@ -1522,9 +1548,9 @@ public class Game extends BasicGameState { else if (hitObject.isSpinner()) gameObjects[i] = new Spinner(hitObject, this, data); } catch (Exception e) { - // try to handle the error gracefully: substitute in a dummy GameObject - ErrorHandler.error(String.format("Failed to create %s at index %d:\n%s", - hitObject.getTypeName(), i, hitObject.toString()), e, true); + String message = String.format("Failed to create %s at index %d:\n%s", hitObject.getTypeName(), i, hitObject.toString()); + Log.error(message, e); + displayContainer.eventBus.post(new BubbleNotificationEvent(message, BubbleNotificationEvent.COMMONCOLOR_RED)); gameObjects[i] = new DummyObject(hitObject); } } @@ -1550,8 +1576,8 @@ public class Game extends BasicGameState { // load replay frames if (isReplay) { // load initial data - replayX = container.getWidth() / 2; - replayY = container.getHeight() / 2; + replayX = displayContainer.width / 2; + replayY = displayContainer.height / 2; replayKeyPressed = false; replaySkipTime = -1; for (replayIndex = 0; replayIndex < replay.frames.length; replayIndex++) { @@ -1572,8 +1598,8 @@ public class Game extends BasicGameState { else { lastKeysPressed = ReplayFrame.KEY_NONE; replaySkipTime = -1; - replayFrames = new LinkedList(); - replayFrames.add(new ReplayFrame(0, 0, input.getMouseX(), input.getMouseY(), 0)); + replayFrames = new LinkedList<>(); + replayFrames.add(new ReplayFrame(0, 0, displayContainer.mouseX, displayContainer.mouseY, 0)); } for (int i = 0; i < gameObjects.length; i++) { @@ -1635,10 +1661,10 @@ public class Game extends BasicGameState { slidercurveTo = 0; Dancer.instance.setGameObjects(gameObjects); - sbOverlay.setGameObjects(gameObjects); + storyboardOverlay.setGameObjects(gameObjects); if (!skippedToCheckpoint) { - sbOverlay.enter(); - sbOverlay.updateIndex(0); + storyboardOverlay.onEnter(); + storyboardOverlay.updateIndex(0); } Pippi.reset(); @@ -1652,15 +1678,23 @@ public class Game extends BasicGameState { } @Override - public void leave(GameContainer container, StateBasedGame game) - throws SlickException { + public void leave() { + super.leave(); + + MusicController.pause(); + MusicController.setPitch(1f); + MusicController.resume(); + + if (Options.isEnableSB()) { + storyboardOverlay.onLeave(); + } + isInGame = false; // container.setMouseGrabbed(false); skippedToCheckpoint = false; knorkesliders = null; - sbOverlay.leave(); Dancer.instance.setGameObjects(null); Cursor.lastObjColor = Color.white; @@ -1726,7 +1760,7 @@ public class Game extends BasicGameState { trackPosition = failTrackTime + (int) (System.currentTimeMillis() - failTime); // get hit objects in reverse order, or else overlapping objects are unreadable - Stack stack = new Stack(); + Stack stack = new Stack<>(); for (int index = objectIndex; index < gameObjects.length && beatmap.objects[index].getTime() < trackPosition + approachTime; index++) { stack.add(index); @@ -1739,7 +1773,7 @@ public class Game extends BasicGameState { } if (lastObjectIndex != -1 && !beatmap.objects[index].isNewCombo()) { // calculate points - final int followPointInterval = container.getHeight() / 14; + final int followPointInterval = displayContainer.height / 14; int lastObjectEndTime = gameObjects[lastObjectIndex].getEndTime() + 1; int objectStartTime = beatmap.objects[index].getTime(); Vec2f startPoint = gameObjects[lastObjectIndex].getPointAt(lastObjectEndTime); @@ -1828,7 +1862,7 @@ public class Game extends BasicGameState { g.pushTransform(); // translate and rotate the object - g.translate(0, dt * dt * container.getHeight()); + g.translate(0, dt * dt * displayContainer.height); Vec2f rotationCenter = gameObj.getPointAt((beatmap.objects[idx].getTime() + beatmap.objects[idx].getEndTime()) / 2); g.rotate(rotationCenter.x, rotationCenter.y, rotSpeed * dt); gameObj.draw(g, trackPosition, false); @@ -1847,7 +1881,7 @@ public class Game extends BasicGameState { currentMapMusicOffset = 0; } this.beatmap = beatmap; - Display.setTitle(String.format("%s - %s", game.getTitle(), beatmap.toString())); + Display.setTitle(String.format("opsu!dance - %s", beatmap.toString())); if (beatmap.breaks == null) BeatmapDB.load(beatmap, BeatmapDB.LOAD_ARRAY); BeatmapParser.parseHitObjects(beatmap); @@ -1879,7 +1913,7 @@ public class Game extends BasicGameState { lastReplayTime = 0; autoMousePosition = new Vec2f(); autoMousePressed = false; - flashlightRadius = container.getHeight() * 2 / 3; + flashlightRadius = displayContainer.height * 2 / 3; scoreboardStarStream.clear(); gameFinished = false; gameFinishedTimer.setTime(0); @@ -1917,9 +1951,6 @@ public class Game extends BasicGameState { * Loads all game images. */ private void loadImages() { - int width = container.getWidth(); - int height = container.getHeight(); - // set images File parent = beatmap.getFile().getParentFile(); for (GameImage img : GameImage.values()) { @@ -1932,17 +1963,17 @@ public class Game extends BasicGameState { // skip button if (GameImage.SKIP.getImages() != null) { Animation skip = GameImage.SKIP.getAnimation(120); - skipButton = new MenuButton(skip, width - skip.getWidth() / 2f, height - (skip.getHeight() / 2f)); + skipButton = new MenuButton(skip, displayContainer.width - skip.getWidth() / 2f, displayContainer.height - (skip.getHeight() / 2f)); } else { Image skip = GameImage.SKIP.getImage(); - skipButton = new MenuButton(skip, width - skip.getWidth() / 2f, height - (skip.getHeight() / 2f)); + skipButton = new MenuButton(skip, displayContainer.width - skip.getWidth() / 2f, displayContainer.height - (skip.getHeight() / 2f)); } skipButton.setHoverAnimationDuration(350); skipButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK); skipButton.setHoverExpand(1.1f, MenuButton.Expand.UP_LEFT); // load other images... - ((GamePauseMenu) game.getState(Opsu.STATE_GAMEPAUSEMENU)).loadImages(); + // TODO d instanceContainer.provide(GamePauseMenu.class).loadImages(); data.loadImages(); } @@ -1974,10 +2005,10 @@ public class Game extends BasicGameState { HitObject.setStackOffset(diameter * STACK_OFFSET_MODIFIER); // initialize objects - Circle.init(container, diameter); - Slider.init(container, diameter, beatmap); - Spinner.init(container, overallDifficulty); - Curve.init(container.getWidth(), container.getHeight(), diameter, (Options.isBeatmapSkinIgnored()) ? + Circle.init(diameter); + Slider.init(displayContainer, diameter, beatmap); + Spinner.init(displayContainer, overallDifficulty); + Curve.init(displayContainer.width, displayContainer.height, diameter, (Options.isBeatmapSkinIgnored()) ? Options.getSkin().getSliderBorderColor() : beatmap.getSliderBorderColor()); // approachRate (hit object approach time) @@ -2096,7 +2127,7 @@ public class Game extends BasicGameState { this.replay = null; } else { if (replay.frames == null) { - ErrorHandler.error("Attempting to set a replay with no frames.", null, false); + displayContainer.eventBus.post(new BubbleNotificationEvent("Attempting to set a replay with no frames.", BubbleNotificationEvent.COLOR_ORANGE)); return; } this.isReplay = true; @@ -2191,30 +2222,29 @@ public class Game extends BasicGameState { if (!GameMod.FLASHLIGHT.isActive()) return; - int width = container.getWidth(), height = container.getHeight(); boolean firstObject = (objectIndex == 0 && trackPosition < beatmap.objects[0].getTime()); if (isLeadIn()) { // lead-in: expand area float progress = Math.max((float) (leadInTime - beatmap.audioLeadIn) / approachTime, 0f); - flashlightRadius = width - (int) ((width - (height * 2 / 3)) * progress); + flashlightRadius = displayContainer.width - (int) ((displayContainer.width - (displayContainer.height * 2 / 3)) * progress); } else if (firstObject) { // before first object: shrink area int timeDiff = beatmap.objects[0].getTime() - trackPosition; - flashlightRadius = width; + flashlightRadius = displayContainer.width; if (timeDiff < approachTime) { float progress = (float) timeDiff / approachTime; - flashlightRadius -= (width - (height * 2 / 3)) * (1 - progress); + flashlightRadius -= (displayContainer.width - (displayContainer.height * 2 / 3)) * (1 - progress); } } else { // gameplay: size based on combo int targetRadius; int combo = data.getComboStreak(); if (combo < 100) - targetRadius = height * 2 / 3; + targetRadius = displayContainer.height * 2 / 3; else if (combo < 200) - targetRadius = height / 2; + targetRadius = displayContainer.height / 2; else - targetRadius = height / 3; + targetRadius = displayContainer.height / 3; if (beatmap.breaks != null && breakIndex < beatmap.breaks.size() && breakTime > 0) { // breaks: expand at beginning, shrink at end flashlightRadius = targetRadius; @@ -2226,11 +2256,11 @@ public class Game extends BasicGameState { progress = (float) (trackPosition - breakTime) / approachTime; else if (endTime - trackPosition < approachTime) progress = (float) (endTime - trackPosition) / approachTime; - flashlightRadius += (width - flashlightRadius) * progress; + flashlightRadius += (displayContainer.width - flashlightRadius) * progress; } } else if (flashlightRadius != targetRadius) { // radius size change - float radiusDiff = height * delta / 2000f; + float radiusDiff = displayContainer.height * delta / 2000f; if (flashlightRadius > targetRadius) { flashlightRadius -= radiusDiff; if (flashlightRadius < targetRadius) diff --git a/src/itdelatrisu/opsu/states/OptionsMenu.java b/src/itdelatrisu/opsu/states/OptionsMenu.java index 03e458b5..4f2c22eb 100644 --- a/src/itdelatrisu/opsu/states/OptionsMenu.java +++ b/src/itdelatrisu/opsu/states/OptionsMenu.java @@ -133,4 +133,63 @@ public class OptionsMenu { }) }; + public static final OptionTab[] storyboardOptions = new OptionsOverlay.OptionTab[]{ + new OptionTab("Gameplay", new GameOption[] { + GameOption.BACKGROUND_DIM, + GameOption.DANCE_REMOVE_BG, + GameOption.SNAKING_SLIDERS, + GameOption.SHRINKING_SLIDERS, + GameOption.SHOW_HIT_LIGHTING, + GameOption.SHOW_HIT_ANIMATIONS, + 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_EXGON_DELAY, + 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_HIDE_OBJECTS, + GameOption.DANCE_HIDE_UI, + 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, + }) + }; + } diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index aeef2f1e..ee06bb49 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -326,7 +326,7 @@ public class SongMenu extends ComplexOpsuState { public SongMenu(final DisplayContainer displayContainer, InstanceContainer instanceContainer) { super(displayContainer); this.instanceContainer = instanceContainer; - optionsOverlay = new OptionsOverlay(this, displayContainer, OptionsMenu.normalOptions, 0); + optionsOverlay = new OptionsOverlay(displayContainer, OptionsMenu.normalOptions, 0); overlays.add(optionsOverlay); } @@ -1794,6 +1794,6 @@ public class SongMenu extends ComplexOpsuState { gameState.loadBeatmap(beatmap); gameState.setRestart(Game.Restart.NEW); gameState.setReplay(null); - // TODO d displayContainer.switchState(Game.class); + displayContainer.switchState(Game.class); } } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index c827a2e0..fac7d085 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -334,7 +334,13 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen } public void switchStateNow(Class newState) { - switchState(newState, EmptyTransitionState.class, 0, EmptyTransitionState.class, 0); + switchState(newState, EmptyTransitionState.class, 0, FadeInTransitionState.class, 300); + } + + public void switchStateInstantly(Class newState) { + state.leave(); + state = instanceContainer.provide(newState); + state.enter(); } public void switchState(Class newState, Class outTransition, int outTime, Class inTransition, int inTime) { diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index df63f5a9..98ab2278 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -57,6 +57,8 @@ public class OpsuDanceInjector extends Injector { bind(ButtonMenu.class).asEagerSingleton(); bind(SongMenu.class).asEagerSingleton(); bind(DownloadsMenu.class).asEagerSingleton(); + bind(GameRanking.class).asEagerSingleton(); + bind(Game.class).asEagerSingleton(); } } diff --git a/src/yugecin/opsudance/core/state/OverlayOpsuState.java b/src/yugecin/opsudance/core/state/OverlayOpsuState.java index f5df8b46..849b4567 100644 --- a/src/yugecin/opsudance/core/state/OverlayOpsuState.java +++ b/src/yugecin/opsudance/core/state/OverlayOpsuState.java @@ -26,8 +26,13 @@ public abstract class OverlayOpsuState implements OpsuState { protected boolean active; protected boolean acceptInput; - public abstract void hide(); - public abstract void show(); + public void hide() { + acceptInput = active = false; + } + + public void show() { + acceptInput = active = true; + } @Override public final void update() { diff --git a/src/yugecin/opsudance/sbv2/MoveStoryboard.java b/src/yugecin/opsudance/sbv2/MoveStoryboard.java index b6119d86..aa4e95c8 100644 --- a/src/yugecin/opsudance/sbv2/MoveStoryboard.java +++ b/src/yugecin/opsudance/sbv2/MoveStoryboard.java @@ -25,6 +25,8 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation; import org.newdawn.slick.Color; import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.state.OverlayOpsuState; import yugecin.opsudance.sbv2.movers.CubicStoryboardMover; import yugecin.opsudance.sbv2.movers.LinearStoryboardMover; import yugecin.opsudance.sbv2.movers.QuadraticStoryboardMover; @@ -34,20 +36,20 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -public class MoveStoryboard { +public class MoveStoryboard extends OverlayOpsuState{ - private final SimpleButton btnAddLinear; - private final SimpleButton btnAddQuadratic; - private final SimpleButton btnAddCubic; + private final DisplayContainer displayContainer; - private final SimpleButton btnAnimLin; - private final SimpleButton btnAnimMid; - private final SimpleButton btnAnimCub; + private SimpleButton btnAddLinear; + private SimpleButton btnAddQuadratic; + private SimpleButton btnAddCubic; + + private SimpleButton btnAnimLin; + private SimpleButton btnAnimMid; + private SimpleButton btnAnimCub; private final StoryboardMove dummyMove; - private int width; - private StoryboardMove[] moves; private GameObject[] gameObjects; @@ -55,14 +57,8 @@ public class MoveStoryboard { private int trackPosition; - public MoveStoryboard(GameContainer container) { - this.width = container.getWidth(); - btnAddLinear = new SimpleButton(width - 205, 50, 200, 25, Fonts.SMALL, "add linear", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); - btnAddQuadratic = new SimpleButton(width - 205, 80, 200, 25, Fonts.SMALL, "add quadratic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); - btnAddCubic = new SimpleButton(width - 205, 110, 200, 25, Fonts.SMALL, "add cubic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); - btnAnimLin = new SimpleButton(width - 250, 50, 40, 25, Fonts.SMALL, "lin", Color.blue, Color.white, Color.white, Color.orange); - btnAnimMid = new SimpleButton(width - 250, 80, 40, 25, Fonts.SMALL, "mid", Color.blue, Color.white, Color.white, Color.orange); - btnAnimCub = new SimpleButton(width - 250, 110, 40, 25, Fonts.SMALL, "cub", Color.blue, Color.white, Color.white, Color.orange); + public MoveStoryboard(DisplayContainer displayContainer) { + this.displayContainer = displayContainer; dummyMove = (StoryboardMove) Proxy.newProxyInstance(StoryboardMove.class.getClassLoader(), new Class[]{StoryboardMove.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { @@ -71,6 +67,16 @@ public class MoveStoryboard { }); } + @Override + public void revalidate() { + btnAddLinear = new SimpleButton(displayContainer.width - 205, 50, 200, 25, Fonts.SMALL, "add linear", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); + btnAddQuadratic = new SimpleButton(displayContainer.width - 205, 80, 200, 25, Fonts.SMALL, "add quadratic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); + btnAddCubic = new SimpleButton(displayContainer.width - 205, 110, 200, 25, Fonts.SMALL, "add cubic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); + btnAnimLin = new SimpleButton(displayContainer.width - 250, 50, 40, 25, Fonts.SMALL, "lin", Color.blue, Color.white, Color.white, Color.orange); + btnAnimMid = new SimpleButton(displayContainer.width - 250, 80, 40, 25, Fonts.SMALL, "mid", Color.blue, Color.white, Color.white, Color.orange); + btnAnimCub = new SimpleButton(displayContainer.width - 250, 110, 40, 25, Fonts.SMALL, "cub", Color.blue, Color.white, Color.white, Color.orange); + } + /** * Get the point at the current time * @param trackPosition current time in ms @@ -88,7 +94,33 @@ public class MoveStoryboard { return moves[objectIndex].getPointAt(t); } - public void render(Graphics g) { + @Override + public void hide() { + + } + + @Override + public void show() { + + } + + @Override + protected void onPreRenderUpdate() { + int x = displayContainer.mouseX; + int y = displayContainer.mouseY; + btnAddLinear.update(x, y); + btnAddQuadratic.update(x, y); + btnAddCubic.update(x, y); + btnAnimLin.update(x, y); + btnAnimMid.update(x, y); + btnAnimCub.update(x, y); + if (moves[objectIndex] != null) { + moves[objectIndex].update(displayContainer.renderDelta, x, y); + } + } + + @Override + protected void onRender(Graphics g) { btnAddLinear.render(g); btnAddQuadratic.render(g); btnAddCubic.render(g); @@ -100,13 +132,31 @@ public class MoveStoryboard { } } - public void mousePressed(int x, int y) { + @Override + protected boolean onKeyPressed(int key, char c) { + return false; + } + + @Override + protected boolean onKeyReleased(int key, char c) { + return false; + } + + @Override + protected boolean onMouseWheelMoved(int delta) { + return false; + } + + @Override + protected boolean onMousePressed(int button, int x, int y) { if (moves[objectIndex] != null) { moves[objectIndex].mousePressed(x, y); } + return true; } - public void mouseReleased(int x, int y) { + @Override + protected boolean onMouseReleased(int button, int x, int y) { if (moves[objectIndex] != null) { moves[objectIndex].mouseReleased(x, y); if (moves[objectIndex].getAmountOfMovers() == 0) { @@ -114,7 +164,7 @@ public class MoveStoryboard { } } if (objectIndex == 0) { - return; + return true; } if (btnAddLinear.isHovered()) { getCurrentMoveOrCreateNew().add(new LinearStoryboardMover()); @@ -134,6 +184,12 @@ public class MoveStoryboard { if (btnAnimCub.isHovered()) { getCurrentMoveOrDummy().setAnimationEquation(AnimationEquation.IN_OUT_EASE_MIDDLE); } + return true; + } + + @Override + protected boolean onMouseDragged(int oldx, int oldy, int newx, int newy) { + return false; } private StoryboardMove getCurrentMoveOrCreateNew() { @@ -142,7 +198,7 @@ public class MoveStoryboard { return dummyMove; } if (moves[objectIndex] == null) { - return moves[objectIndex] = new StoryboardMoveImpl(gameObjects[objectIndex - 1].end, gameObjects[objectIndex].start, width); + return moves[objectIndex] = new StoryboardMoveImpl(gameObjects[objectIndex - 1].end, gameObjects[objectIndex].start, displayContainer.width); } return moves[objectIndex]; } @@ -154,18 +210,6 @@ public class MoveStoryboard { return moves[objectIndex]; } - public void update(int delta, int x, int y) { - btnAddLinear.update(x, y); - btnAddQuadratic.update(x, y); - btnAddCubic.update(x, y); - btnAnimLin.update(x, y); - btnAnimMid.update(x, y); - btnAnimCub.update(x, y); - if (moves[objectIndex] != null) { - moves[objectIndex].update(delta, x, y); - } - } - public void setGameObjects(GameObject[] gameObjects) { this.gameObjects = gameObjects; this.moves = new StoryboardMove[gameObjects.length]; diff --git a/src/yugecin/opsudance/ui/OptionsOverlay.java b/src/yugecin/opsudance/ui/OptionsOverlay.java index 447a41b8..70e607ac 100644 --- a/src/yugecin/opsudance/ui/OptionsOverlay.java +++ b/src/yugecin/opsudance/ui/OptionsOverlay.java @@ -35,9 +35,10 @@ import yugecin.opsudance.core.state.OverlayOpsuState; public class OptionsOverlay extends OverlayOpsuState { - private final ComplexOpsuState parent; private final DisplayContainer displayContainer; + private Listener listener; + private Image sliderBallImg; private Image checkOnImg; private Image checkOffImg; @@ -81,8 +82,7 @@ public class OptionsOverlay extends OverlayOpsuState { private int sliderSoundDelay; - public OptionsOverlay(ComplexOpsuState parent, DisplayContainer displayContainer, OptionTab[] tabs, int defaultSelectedTabIndex) { - this.parent = parent; + public OptionsOverlay(DisplayContainer displayContainer, OptionTab[] tabs, int defaultSelectedTabIndex) { this.displayContainer = displayContainer; this.tabs = tabs; @@ -91,6 +91,10 @@ public class OptionsOverlay extends OverlayOpsuState { listHoverIndex = -1; } + public void setListener(Listener listener) { + this.listener = listener; + } + @Override public void revalidate() { super.revalidate(); @@ -399,7 +403,9 @@ public class OptionsOverlay extends OverlayOpsuState { if (isListOptionOpen) { if (y > optionStartY && listStartX <= x && x < listStartX + listWidth && listStartY <= y && y < listStartY + listHeight) { hoverOption.clickListItem(listHoverIndex); - // TODO d parent.onSaveOption(hoverOption); + if (listener != null) { + listener.onSaveOption(hoverOption); + } SoundController.playSound(SoundEffect.MENUCLICK); } isListOptionOpen = false; @@ -431,8 +437,8 @@ public class OptionsOverlay extends OverlayOpsuState { @Override public boolean onMouseReleased(int button, int x, int y) { selectedOption = null; - if (isAdjustingSlider) { - // TODO d parent.onSaveOption(hoverOption); + if (isAdjustingSlider && listener != null) { + listener.onSaveOption(hoverOption); } isAdjustingSlider = false; sliderOptionLength = 0; @@ -445,7 +451,9 @@ public class OptionsOverlay extends OverlayOpsuState { if (hoverOption != null) { if (hoverOption.getType() == OptionType.BOOLEAN) { hoverOption.click(); - // TODO d parent.onSaveOption(hoverOption); + if (listener != null) { + listener.onSaveOption(hoverOption); + } SoundController.playSound(SoundEffect.MENUHIT); return true; } else if (hoverOption == GameOption.KEY_LEFT) { @@ -466,6 +474,11 @@ public class OptionsOverlay extends OverlayOpsuState { tScrollOffset += Fonts.MEDIUM.getLineHeight() * 2; tScrollOffset += tab.options.length * optionHeight; } + + if (UI.getBackButton().contains(x, y) && listener != null) { + listener.onLeaveOptionsMenu(); + } + return true; } @@ -500,16 +513,19 @@ public class OptionsOverlay extends OverlayOpsuState { return true; } - switch (key) { - case Input.KEY_ESCAPE: - if (isListOptionOpen) { - isListOptionOpen = false; - listHoverIndex = -1; - return true; - } - hide(); + if (key == Input.KEY_ESCAPE) { + if (isListOptionOpen) { + isListOptionOpen = false; + listHoverIndex = -1; return true; + } + hide(); + if (listener != null) { + listener.onLeaveOptionsMenu(); + } + return true; } + return false; } @@ -570,9 +586,9 @@ public class OptionsOverlay extends OverlayOpsuState { } - public interface Parent { + public interface Listener { - void onLeave(); + void onLeaveOptionsMenu(); void onSaveOption(GameOption option); } diff --git a/src/yugecin/opsudance/ui/SBOverlay.java b/src/yugecin/opsudance/ui/StoryboardOverlay.java similarity index 59% rename from src/yugecin/opsudance/ui/SBOverlay.java rename to src/yugecin/opsudance/ui/StoryboardOverlay.java index 723c0afb..725dc107 100644 --- a/src/yugecin/opsudance/ui/SBOverlay.java +++ b/src/yugecin/opsudance/ui/StoryboardOverlay.java @@ -22,86 +22,27 @@ import itdelatrisu.opsu.Options.GameOption; import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.objects.GameObject; import itdelatrisu.opsu.states.Game; +import itdelatrisu.opsu.states.OptionsMenu; 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 yugecin.opsudance.ObjectColorOverrides; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.state.OverlayOpsuState; import yugecin.opsudance.sbv2.MoveStoryboard; import yugecin.opsudance.ui.OptionsOverlay.OptionTab; import java.util.*; @SuppressWarnings("unchecked") -public class SBOverlay implements OptionsOverlay.Parent { - - private static final OptionTab[] options = new OptionsOverlay.OptionTab[]{ - new OptionTab("Gameplay", new GameOption[] { - GameOption.BACKGROUND_DIM, - GameOption.DANCE_REMOVE_BG, - GameOption.SNAKING_SLIDERS, - GameOption.SHRINKING_SLIDERS, - GameOption.SHOW_HIT_LIGHTING, - GameOption.SHOW_HIT_ANIMATIONS, - 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_EXGON_DELAY, - 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_HIDE_OBJECTS, - GameOption.DANCE_HIDE_UI, - 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, - }) - }; +public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverlay.Listener { private final static List optionList = new ArrayList<>(); - private boolean hide; - private boolean menu; + private final DisplayContainer displayContainer; - private int width; - private int height; + private boolean hide; private int speed; private GameObject[] gameObjects; @@ -112,43 +53,42 @@ public class SBOverlay implements OptionsOverlay.Parent { private final Game game; private final MoveStoryboard msb; - private OptionsOverlay overlay; + private final OptionsOverlay optionsOverlay; static { - for (OptionTab tab : options) { + for (OptionTab tab : OptionsMenu.storyboardOptions) { optionList.addAll(Arrays.asList(tab.options)); } } - public SBOverlay(Game game, MoveStoryboard msb, GameContainer container) { - this.game = game; + public StoryboardOverlay(DisplayContainer displayContainer, MoveStoryboard msb, OptionsOverlay optionsOverlay, Game game) { + this.displayContainer = displayContainer; this.msb = msb; + this.optionsOverlay = optionsOverlay; + 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]; } - public void render(GameContainer container, Graphics g) { + @Override + public void onRender(Graphics g) { if (!Options.isEnableSB() || hide) { return; } - msb.render(g); int lh = Fonts.SMALL.getLineHeight(); - Fonts.SMALL.drawString(10, height - 50 + lh, "save position: ctrl+s, load position: ctrl+l", Color.cyan); - Fonts.SMALL.drawString(10, height - 50, "speed: C " + (speed / 10f) + " V", Color.cyan); - Fonts.SMALL.drawString(10, height - 50 - lh, "Menu: N", Color.cyan); - Fonts.SMALL.drawString(10, height - 50 - lh * 2, "HIDE: H", Color.cyan); - Fonts.SMALL.drawString(10, height - 50 - lh * 3, "obj: J " + index + " K", Color.cyan); + Fonts.SMALL.drawString(10, displayContainer.height - 50 + lh, "save position: ctrl+s, load position: ctrl+l", Color.cyan); + Fonts.SMALL.drawString(10, displayContainer.height - 50, "speed: C " + (speed / 10f) + " V", Color.cyan); + Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh, "Menu: N", Color.cyan); + Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh * 2, "HIDE: H", Color.cyan); + Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh * 3, "obj: J " + index + " K", Color.cyan); g.setColor(Color.red); if (index < optionsMap.length && optionsMap[index] != null) { int i = 0; for (Object o : optionsMap[index].entrySet()) { Map.Entry option = (Map.Entry) o; Fonts.SMALL.drawString(10, 50 + i * lh, option.getKey().getName(), Color.cyan); - Fonts.SMALL.drawString(width / 5, 50 + i * lh, option.getKey().getValueString(), Color.cyan); + Fonts.SMALL.drawString(displayContainer.width / 5, 50 + i * lh, option.getKey().getValueString(), Color.cyan); g.fillRect(0, 50 + i * lh + lh / 4, 10, 10); i++; } @@ -157,27 +97,16 @@ public class SBOverlay implements OptionsOverlay.Parent { int start = gameObjects[0].getTime(); int end = gameObjects[gameObjects.length - 1].getEndTime(); float curtime = (float) (MusicController.getPosition() - start) / (end - start); - g.fillRect(curtime * width, height - 10f, 10f, 10f); - } - if (menu) { - //overlay.render(g, container.getInput().getMouseX(), container.getInput().getMouseY()); + g.fillRect(curtime * displayContainer.width, displayContainer.height - 10f, 10f, 10f); } } - public void update(int delta, int mouseX, int mouseY) { - if (Options.isEnableSB() && menu) { - //overlay.update(delta, mouseX, mouseY); - } - msb.update(delta, mouseX, mouseY); + @Override + public void onPreRenderUpdate() { } - public boolean keyPressed(int key, char c) { - if (!Options.isEnableSB()) { - return false; - } - if (menu && overlay.keyPressed(key, c)) { - return true; - } + @Override + public boolean onKeyPressed(int key, char c) { if (key == Input.KEY_C) { if (speed > 0) { speed -= 1; @@ -196,11 +125,9 @@ public class SBOverlay implements OptionsOverlay.Parent { } else if (key == Input.KEY_H) { hide = !hide; } else if (key == Input.KEY_N) { - menu = !menu; - if (menu && speed != 0) { + optionsOverlay.show(); + if (speed != 0) { MusicController.pause(); - } else if (!menu && speed != 0) { - MusicController.resume(); } } else if (key == Input.KEY_J && index > 0) { index--; @@ -214,6 +141,11 @@ public class SBOverlay implements OptionsOverlay.Parent { return false; } + @Override + protected boolean onKeyReleased(int key, char c) { + return false; + } + private void goBackOneSBIndex() { if (index + 1 < optionsMap.length) { // new options on previous index, so to revert then we have to reload them all to this point.. @@ -252,20 +184,13 @@ public class SBOverlay implements OptionsOverlay.Parent { this.gameObjects = gameObjects; } - public boolean mousePressed(int button, int x, int y) { - msb.mousePressed(x, y); - if (!menu) { - return false; - } - overlay.mousePressed(button, x, y); + @Override + public boolean onMousePressed(int button, int x, int y) { return true; } - public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { - if (!menu) { - return false; - } - overlay.mouseDragged(oldx, oldy, newx, newy); + @Override + public boolean onMouseDragged(int oldx, int oldy, int newx, int newy) { return true; } @@ -293,12 +218,8 @@ public class SBOverlay implements OptionsOverlay.Parent { this.index--; } - public boolean mouseReleased(int button, int x, int y) { - if (menu) { - overlay.mouseReleased(button, x, y); - return true; - } - msb.mouseReleased(x, y); + @Override + public boolean onMouseReleased(int button, int x, int y) { if (x > 10 || index >= optionsMap.length || optionsMap[index] == null) { return false; } @@ -318,15 +239,12 @@ public class SBOverlay implements OptionsOverlay.Parent { return true; } - public boolean mouseWheelMoved(int delta) { - if (!menu) { - return false; - } - overlay.mouseWheelMoved(delta); + @Override + public boolean onMouseWheelMoved(int delta) { return true; } - public void enter() { + public void onEnter() { // enter, save current settings for (Options.GameOption o : optionList) { initialOptions.put(o, o.write()); @@ -334,7 +252,7 @@ public class SBOverlay implements OptionsOverlay.Parent { speed = 10; } - public void leave() { + public void onLeave() { // leave, revert the settings saved before entering for (Options.GameOption o : optionList) { if (initialOptions.containsKey(o)) { @@ -358,13 +276,8 @@ public class SBOverlay implements OptionsOverlay.Parent { } } - public float[] getPoint(int trackPosition) { - return msb.getPoint(trackPosition); - } - @Override - public void onLeave() { - menu = false; + public void onLeaveOptionsMenu() { if (speed != 0) { MusicController.resume(); } From 2d0e2143d11b4585a4c697921eec464737254afc Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 19 Jan 2017 19:43:47 +0100 Subject: [PATCH 80/92] convert gamepausemenu --- src/itdelatrisu/opsu/Opsu.java | 2 +- src/itdelatrisu/opsu/states/Game.java | 14 +- .../opsu/states/GamePauseMenu.java | 171 +++++++++--------- .../core/inject/OpsuDanceInjector.java | 1 + 4 files changed, 94 insertions(+), 94 deletions(-) diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index 131178b8..b2c6d410 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -90,7 +90,7 @@ public class Opsu extends StateBasedGame { //addState(new ButtonMenu(STATE_BUTTONMENU)); //addState(new SongMenu(STATE_SONGMENU)); //addState(new Game(STATE_GAME)); - addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); + //addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); //addState(new GameRanking(STATE_GAMERANKING)); //addState(new OptionsMenu(STATE_OPTIONSMENU)); //addState(new DownloadsMenu(STATE_DOWNLOADSMENU)); diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index 1dd3c7dd..c3d555f6 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -66,6 +66,8 @@ import yugecin.opsudance.*; import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.state.ComplexOpsuState; +import yugecin.opsudance.core.state.transitions.FadeInTransitionState; +import yugecin.opsudance.core.state.transitions.FadeOutTransitionState; import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.objects.curves.FakeCombinedCurve; import yugecin.opsudance.sbv2.MoveStoryboard; @@ -802,7 +804,7 @@ public class Game extends ComplexOpsuState { // focus lost: go back to pause screen else if (!Display.isActive()) { - // TODO d displayContainer.switchState(GamePauseMenu.class); + displayContainer.switchStateNow(GamePauseMenu.class); pausePulse = 0f; } @@ -1034,7 +1036,7 @@ public class Game extends ComplexOpsuState { if (MusicController.isPlaying() || isLeadIn()) { pauseTime = trackPosition; } - // TODO d displayContainer.switchState(GamePauseMenu.class); + displayContainer.switchStateNow(GamePauseMenu.class); } // drain health @@ -1061,7 +1063,7 @@ public class Game extends ComplexOpsuState { rotations = new IdentityHashMap<>(); SoundController.playSound(SoundEffect.FAIL); - // TODO d displayContainer.switchState(GamePauseMenu.class, FadeOutTransitionState.class, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME, FadeInTransitionState.class, 300); + displayContainer.switchState(GamePauseMenu.class, FadeOutTransitionState.class, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME, FadeInTransitionState.class, 300); } } } @@ -1143,7 +1145,7 @@ public class Game extends ComplexOpsuState { if (MusicController.isPlaying() || isLeadIn()) { pauseTime = trackPosition; } - // TODO d displayContainer.switchStateNow(GamePauseMenu.class); + displayContainer.switchStateNow(GamePauseMenu.class); break; case Input.KEY_SPACE: // skip intro @@ -1296,7 +1298,7 @@ public class Game extends ComplexOpsuState { if (MusicController.isPlaying() || isLeadIn()) { pauseTime = trackPosition; } - // TODO d displayContainer.switchStateNow(GamePauseMenu.class); + displayContainer.switchStateNow(GamePauseMenu.class); return true; } @@ -1973,7 +1975,7 @@ public class Game extends ComplexOpsuState { skipButton.setHoverExpand(1.1f, MenuButton.Expand.UP_LEFT); // load other images... - // TODO d instanceContainer.provide(GamePauseMenu.class).loadImages(); + instanceContainer.provide(GamePauseMenu.class).loadImages(); data.loadImages(); } diff --git a/src/itdelatrisu/opsu/states/GamePauseMenu.java b/src/itdelatrisu/opsu/states/GamePauseMenu.java index a9d5a7ad..58d83ef5 100644 --- a/src/itdelatrisu/opsu/states/GamePauseMenu.java +++ b/src/itdelatrisu/opsu/states/GamePauseMenu.java @@ -19,9 +19,7 @@ package itdelatrisu.opsu.states; import itdelatrisu.opsu.GameImage; -import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.Options; -import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundEffect; @@ -31,14 +29,11 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation; import org.lwjgl.input.Keyboard; import org.newdawn.slick.Color; -import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; -import org.newdawn.slick.SlickException; -import org.newdawn.slick.state.BasicGameState; -import org.newdawn.slick.state.StateBasedGame; -import org.newdawn.slick.state.transition.FadeInTransition; -import org.newdawn.slick.state.transition.EasedFadeOutTransition; +import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.inject.InstanceContainer; +import yugecin.opsudance.core.state.BaseOpsuState; /** * "Game Pause/Fail" state. @@ -46,33 +41,22 @@ import org.newdawn.slick.state.transition.EasedFadeOutTransition; * Players are able to continue the game (if applicable), retry the beatmap, * or return to the song menu from this state. */ -public class GamePauseMenu extends BasicGameState { - /** "Continue", "Retry", and "Back" buttons. */ +public class GamePauseMenu extends BaseOpsuState { + + private final InstanceContainer instanceContainer; + private MenuButton continueButton, retryButton, backButton; - // game-related variables - private GameContainer container; - private StateBasedGame game; - private Input input; - private final int state; - private Game gameState; + private final Game gameState; - public GamePauseMenu(int state) { - this.state = state; + public GamePauseMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer, Game gameState) { + super(displayContainer); + this.instanceContainer = instanceContainer; + this.gameState = gameState; } @Override - public void init(GameContainer container, StateBasedGame game) - throws SlickException { - this.container = container; - this.game = game; - this.input = container.getInput(); - this.gameState = (Game) game.getState(Opsu.STATE_GAME); - } - - @Override - public void render(GameContainer container, StateBasedGame game, Graphics g) - throws SlickException { + public void render(Graphics g) { // get background image GameImage bg = (gameState.getRestart() == Game.Restart.LOSE) ? GameImage.FAIL_BACKGROUND : GameImage.PAUSE_OVERLAY; @@ -97,81 +81,78 @@ public class GamePauseMenu extends BasicGameState { } @Override - public void update(GameContainer container, StateBasedGame game, int delta) - throws SlickException { + public void preRenderUpdate() { + int delta = displayContainer.renderDelta; UI.update(delta); - int mouseX = input.getMouseX(), mouseY = input.getMouseY(); - continueButton.hoverUpdate(delta, mouseX, mouseY); - retryButton.hoverUpdate(delta, mouseX, mouseY); - backButton.hoverUpdate(delta, mouseX, mouseY); + continueButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); + retryButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); + backButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); } @Override - public int getID() { return state; } - - @Override - public void keyPressed(int key, char c) { - // game keys - if (!Keyboard.isRepeatEvent()) { - if (key == Options.getGameKeyLeft()) - mousePressed(Input.MOUSE_LEFT_BUTTON, input.getMouseX(), input.getMouseY()); - else if (key == Options.getGameKeyRight()) - mousePressed(Input.MOUSE_RIGHT_BUTTON, input.getMouseX(), input.getMouseY()); + public boolean keyPressed(int key, char c) { + if (super.keyPressed(key, c)) { + return true; } - switch (key) { - case Input.KEY_ESCAPE: + // game keys + if (!Keyboard.isRepeatEvent()) { + if (key == Options.getGameKeyLeft()) { + mousePressed(Input.MOUSE_LEFT_BUTTON, displayContainer.mouseX, displayContainer.mouseY); + } else if (key == Options.getGameKeyRight()) { + mousePressed(Input.MOUSE_RIGHT_BUTTON, displayContainer.mouseX, displayContainer.mouseY); + } + } + + if (key == Input.KEY_ESCAPE) { // 'esc' will normally unpause, but will return to song menu if health is zero if (gameState.getRestart() == Game.Restart.LOSE) { SoundController.playSound(SoundEffect.MENUBACK); - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad(); + instanceContainer.provide(SongMenu.class).resetGameDataOnLoad(); MusicController.playAt(MusicController.getBeatmap().previewTime, true); - if (UI.getCursor().isBeatmapSkinned()) + if (UI.getCursor().isBeatmapSkinned()) { UI.getCursor().reset(); - game.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition()); + } + displayContainer.switchState(SongMenu.class); } else { SoundController.playSound(SoundEffect.MENUBACK); gameState.setRestart(Game.Restart.FALSE); - game.enterState(Opsu.STATE_GAME); + displayContainer.switchState(Game.class); } - break; - case Input.KEY_R: - // restart - if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) { - gameState.setRestart(Game.Restart.MANUAL); - game.enterState(Opsu.STATE_GAME); - } - break; - case Input.KEY_F7: - // TODO d - //Options.setNextFPS(container); - break; - case Input.KEY_F10: - Options.toggleMouseDisabled(); - break; - case Input.KEY_F12: - Utils.takeScreenShot(); - break; + return true; } + + if (key == Input.KEY_R && (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL))) { + gameState.setRestart(Game.Restart.MANUAL); + displayContainer.switchState(Game.class); + return true; + } + + return false; } @Override - public void mousePressed(int button, int x, int y) { - if (button == Input.MOUSE_MIDDLE_BUTTON) - return; + public boolean mousePressed(int button, int x, int y) { + if (super.mousePressed(button, x, y)) { + return true; + } + + if (button == Input.MOUSE_MIDDLE_BUTTON) { + return true; + } boolean loseState = (gameState.getRestart() == Game.Restart.LOSE); if (continueButton.contains(x, y) && !loseState) { SoundController.playSound(SoundEffect.MENUBACK); gameState.setRestart(Game.Restart.FALSE); - game.enterState(Opsu.STATE_GAME); + displayContainer.switchState(Game.class); } else if (retryButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUHIT); gameState.setRestart(Game.Restart.MANUAL); - game.enterState(Opsu.STATE_GAME); + displayContainer.switchState(Game.class); } else if (backButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUBACK); - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad(); + instanceContainer.provide(SongMenu.class).resetGameDataOnLoad(); if (loseState) MusicController.playAt(MusicController.getBeatmap().previewTime, true); else @@ -179,21 +160,30 @@ public class GamePauseMenu extends BasicGameState { if (UI.getCursor().isBeatmapSkinned()) UI.getCursor().reset(); MusicController.setPitch(1.0f); - game.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition()); + displayContainer.switchState(SongMenu.class); } + + return true; } @Override - public void mouseWheelMoved(int newValue) { - if (Options.isMouseWheelDisabled()) - return; + public boolean mouseWheelMoved(int newValue) { + if (super.mouseWheelMoved(newValue)) { + return true; + } + + if (Options.isMouseWheelDisabled()) { + return true; + } UI.changeVolume((newValue < 0) ? -1 : 1); + return true; } @Override - public void enter(GameContainer container, StateBasedGame game) - throws SlickException { + public void enter() { + super.enter(); + UI.enter(); MusicController.pause(); continueButton.resetHover(); @@ -201,17 +191,23 @@ public class GamePauseMenu extends BasicGameState { backButton.resetHover(); } + @Override + public boolean onCloseRequest() { + SongMenu songmenu = instanceContainer.provide(SongMenu.class); + songmenu.resetTrackOnLoad(); + songmenu.resetGameDataOnLoad(); + displayContainer.switchState(SongMenu.class); + return false; + } + /** * Loads all game pause/fail menu images. */ public void loadImages() { - int width = container.getWidth(); - int height = container.getHeight(); - // initialize buttons - continueButton = new MenuButton(GameImage.PAUSE_CONTINUE.getImage(), width / 2f, height * 0.25f); - retryButton = new MenuButton(GameImage.PAUSE_RETRY.getImage(), width / 2f, height * 0.5f); - backButton = new MenuButton(GameImage.PAUSE_BACK.getImage(), width / 2f, height * 0.75f); + continueButton = new MenuButton(GameImage.PAUSE_CONTINUE.getImage(), displayContainer.width / 2f, displayContainer.height * 0.25f); + retryButton = new MenuButton(GameImage.PAUSE_RETRY.getImage(), displayContainer.width / 2f, displayContainer.height * 0.5f); + backButton = new MenuButton(GameImage.PAUSE_BACK.getImage(), displayContainer.width / 2f, displayContainer.height * 0.75f); final int buttonAnimationDuration = 300; continueButton.setHoverAnimationDuration(buttonAnimationDuration); retryButton.setHoverAnimationDuration(buttonAnimationDuration); @@ -224,4 +220,5 @@ public class GamePauseMenu extends BasicGameState { retryButton.setHoverExpand(); backButton.setHoverExpand(); } + } diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index 98ab2278..39a4ce64 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -59,6 +59,7 @@ public class OpsuDanceInjector extends Injector { bind(DownloadsMenu.class).asEagerSingleton(); bind(GameRanking.class).asEagerSingleton(); bind(Game.class).asEagerSingleton(); + bind(GamePauseMenu.class).asEagerSingleton(); } } From 0af6bc8c09bf86aeaa49301209a97ad98d6785ca Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 19 Jan 2017 19:52:13 +0100 Subject: [PATCH 81/92] return to songmenu when closing game ranking screen --- src/itdelatrisu/opsu/states/Game.java | 8 ++++++-- src/itdelatrisu/opsu/states/GameRanking.java | 11 +++++++++++ src/itdelatrisu/opsu/states/SongMenu.java | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index c3d555f6..53200135 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -1894,7 +1894,9 @@ public class Game extends ComplexOpsuState { * Resets all game data and structures. */ public void resetGameData() { - data.clear(); + if (data != null) { + data.clear(); + } objectIndex = 0; breakIndex = 0; breakTime = 0; @@ -1916,7 +1918,9 @@ public class Game extends ComplexOpsuState { autoMousePosition = new Vec2f(); autoMousePressed = false; flashlightRadius = displayContainer.height * 2 / 3; - scoreboardStarStream.clear(); + if (scoreboardStarStream != null) { + scoreboardStarStream.clear(); + } gameFinished = false; gameFinishedTimer.setTime(0); diff --git a/src/itdelatrisu/opsu/states/GameRanking.java b/src/itdelatrisu/opsu/states/GameRanking.java index 6a92d44b..fdec21fc 100644 --- a/src/itdelatrisu/opsu/states/GameRanking.java +++ b/src/itdelatrisu/opsu/states/GameRanking.java @@ -220,6 +220,17 @@ public class GameRanking extends BaseOpsuState { } } + @Override + public boolean onCloseRequest() { + SongMenu songmenu = instanceContainer.provide(SongMenu.class); + if (data != null && data.isGameplay()) { + songmenu.resetTrackOnLoad(); + } + songmenu.resetGameDataOnLoad(); + displayContainer.switchState(SongMenu.class); + return false; + } + /** * Returns to the song menu. */ diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index ee06bb49..d1b98ac6 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -1025,7 +1025,7 @@ public class SongMenu extends ComplexOpsuState { if (button != Input.MOUSE_RIGHT_BUTTON) { // view score instanceContainer.provide(GameRanking.class).setGameData(new GameData(focusScores[rank], displayContainer.width, displayContainer.height)); - // TODO d displayContainer.switchState(GameRanking.class); + displayContainer.switchState(GameRanking.class); } else { // score management instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.SCORE, focusScores[rank]); From 84fb44475f0dbd7d01d980a634432cff1bb37638 Mon Sep 17 00:00:00 2001 From: yugecin Date: Thu, 19 Jan 2017 19:55:25 +0100 Subject: [PATCH 82/92] fix array index oob exception when changing dropdown option (fixes #118) --- src/yugecin/opsudance/ui/OptionsOverlay.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/yugecin/opsudance/ui/OptionsOverlay.java b/src/yugecin/opsudance/ui/OptionsOverlay.java index 70e607ac..0aae9e08 100644 --- a/src/yugecin/opsudance/ui/OptionsOverlay.java +++ b/src/yugecin/opsudance/ui/OptionsOverlay.java @@ -30,7 +30,6 @@ import itdelatrisu.opsu.ui.MenuButton; import itdelatrisu.opsu.ui.UI; import org.newdawn.slick.*; import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.core.state.ComplexOpsuState; import yugecin.opsudance.core.state.OverlayOpsuState; public class OptionsOverlay extends OverlayOpsuState { @@ -402,9 +401,11 @@ public class OptionsOverlay extends OverlayOpsuState { if (isListOptionOpen) { if (y > optionStartY && listStartX <= x && x < listStartX + listWidth && listStartY <= y && y < listStartY + listHeight) { - hoverOption.clickListItem(listHoverIndex); - if (listener != null) { - listener.onSaveOption(hoverOption); + if (0 <= listHoverIndex && listHoverIndex < hoverOption.getListItems().length) { + hoverOption.clickListItem(listHoverIndex); + if (listener != null) { + listener.onSaveOption(hoverOption); + } } SoundController.playSound(SoundEffect.MENUCLICK); } From 63660dfe94a01b17da721e2806d65e63626d639a Mon Sep 17 00:00:00 2001 From: yugecin Date: Fri, 20 Jan 2017 00:15:50 +0100 Subject: [PATCH 83/92] added exit confirmations when download is going on --- .../opsudance/core/DisplayContainer.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index fac7d085..e8d84665 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -22,6 +22,8 @@ import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.beatmap.Beatmap; +import itdelatrisu.opsu.downloads.DownloadList; +import itdelatrisu.opsu.downloads.Updater; import itdelatrisu.opsu.ui.Fonts; import org.lwjgl.Sys; import org.lwjgl.openal.AL; @@ -102,6 +104,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen private String glVersion; private String glVendor; + private long exitconfirmation; + public DisplayContainer(InstanceContainer instanceContainer, EventBus eventBus) { this.instanceContainer = instanceContainer; this.eventBus = eventBus; @@ -155,7 +159,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen public void run() throws Exception { - while(!exitRequested && !(Display.isCloseRequested() && state.onCloseRequest())) { + while(!exitRequested && !(Display.isCloseRequested() && state.onCloseRequest()) || !confirmExit()) { delta = getDelta(); timeSinceLastRender += delta; @@ -238,6 +242,23 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen } } + private boolean confirmExit() { + if (System.currentTimeMillis() - exitconfirmation < 10000) { + return true; + } + if (DownloadList.get().hasActiveDownloads()) { + eventBus.post(new BubbleNotificationEvent(DownloadList.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE)); + exitRequested = false; + return false; + } + if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING) { + eventBus.post(new BubbleNotificationEvent(Updater.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE)); + exitRequested = false; + return false; + } + return true; + } + public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception { if (this.width == width && this.height == height) { Display.setFullscreen(fullscreen); From 4bf469d40b25e270bf16faac63a62195724d2f90 Mon Sep 17 00:00:00 2001 From: yugecin Date: Fri, 20 Jan 2017 00:18:25 +0100 Subject: [PATCH 84/92] some more shutdown calls --- src/yugecin/opsudance/OpsuDance.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index e8b5b28a..04b04054 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -19,9 +19,11 @@ package yugecin.opsudance; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; +import itdelatrisu.opsu.beatmap.BeatmapWatchService; import itdelatrisu.opsu.db.DBController; import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.Updater; +import itdelatrisu.opsu.render.CurveRenderState; import itdelatrisu.opsu.states.Splash; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.DisplayContainer; @@ -76,6 +78,10 @@ public class OpsuDance { DBController.closeConnections(); DownloadList.get().cancelAllDownloads(); Utils.deleteDirectory(Options.TEMP_DIR); + CurveRenderState.shutdown(); + if (!Options.isWatchServiceEnabled()) { + BeatmapWatchService.destroy(); + } } private boolean rungame() { From 04de1d024fa741990350a03d3e894ddfde775ec9 Mon Sep 17 00:00:00 2001 From: yugecin Date: Fri, 20 Jan 2017 00:25:32 +0100 Subject: [PATCH 85/92] fix not being able to shutdown when download is in progress --- src/yugecin/opsudance/core/DisplayContainer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index e8d84665..4385de56 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -249,11 +249,13 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen if (DownloadList.get().hasActiveDownloads()) { eventBus.post(new BubbleNotificationEvent(DownloadList.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE)); exitRequested = false; + exitconfirmation = System.currentTimeMillis(); return false; } if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING) { eventBus.post(new BubbleNotificationEvent(Updater.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE)); exitRequested = false; + exitconfirmation = System.currentTimeMillis(); return false; } return true; From aceebb95ca3dc7cd92c54af221d605b8113592f8 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sat, 21 Jan 2017 00:39:56 +0100 Subject: [PATCH 86/92] re-add the runupdate call --- src/yugecin/opsudance/core/Entrypoint.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/yugecin/opsudance/core/Entrypoint.java b/src/yugecin/opsudance/core/Entrypoint.java index 88225e8c..dfba2245 100644 --- a/src/yugecin/opsudance/core/Entrypoint.java +++ b/src/yugecin/opsudance/core/Entrypoint.java @@ -17,6 +17,7 @@ */ package yugecin.opsudance.core; +import itdelatrisu.opsu.downloads.Updater; import yugecin.opsudance.OpsuDance; import yugecin.opsudance.core.inject.OpsuDanceInjector; @@ -27,6 +28,10 @@ public class Entrypoint { public static void main(String[] args) { sout("launched"); (new OpsuDanceInjector()).provide(OpsuDance.class).start(args); + + if (Updater.get().getStatus() == Updater.Status.UPDATE_FINAL) { + Updater.get().runUpdate(); + } } public static long runtime() { From f730935622d33da4e168f18f7bcc927997cd9772 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sat, 21 Jan 2017 01:16:27 +0100 Subject: [PATCH 87/92] use the new errorhandler & bubble notifs --- src/itdelatrisu/opsu/GameImage.java | 16 +++++--- src/itdelatrisu/opsu/Options.java | 20 ++++++---- src/itdelatrisu/opsu/Utils.java | 24 +++++++----- src/itdelatrisu/opsu/audio/MultiClip.java | 4 +- .../opsu/audio/MusicController.java | 15 +++++--- .../opsu/audio/SoundController.java | 12 +++--- .../opsu/beatmap/BeatmapParser.java | 26 +++++++++---- .../opsu/beatmap/BeatmapSetList.java | 7 ++-- .../opsu/beatmap/BeatmapWatchService.java | 9 +++-- src/itdelatrisu/opsu/beatmap/OszUnpacker.java | 9 +++-- src/itdelatrisu/opsu/db/BeatmapDB.java | 38 +++++++++---------- src/itdelatrisu/opsu/db/DBController.java | 6 +-- src/itdelatrisu/opsu/db/ScoreDB.java | 20 +++++----- src/itdelatrisu/opsu/downloads/Download.java | 10 +++-- .../opsu/downloads/DownloadNode.java | 5 ++- src/itdelatrisu/opsu/downloads/Updater.java | 4 +- .../downloads/servers/BloodcatServer.java | 4 +- .../opsu/downloads/servers/HexideServer.java | 4 +- .../opsu/downloads/servers/MengSkyServer.java | 4 +- .../downloads/servers/MnetworkServer.java | 4 +- .../downloads/servers/OsuMirrorServer.java | 4 +- .../downloads/servers/YaSOnlineServer.java | 6 +-- .../opsu/render/CurveRenderState.java | 2 +- .../opsu/render/FrameBufferCache.java | 1 + src/itdelatrisu/opsu/replay/Replay.java | 10 +++-- .../opsu/replay/ReplayImporter.java | 17 ++++++--- src/itdelatrisu/opsu/skins/SkinLoader.java | 7 +++- .../opsu/states/DownloadsMenu.java | 1 - src/itdelatrisu/opsu/states/MainMenu.java | 10 +++-- src/itdelatrisu/opsu/ui/Cursor.java | 5 +-- src/itdelatrisu/opsu/ui/UI.java | 16 -------- src/yugecin/opsudance/OpsuDance.java | 8 ++-- .../opsudance/core/DisplayContainer.java | 2 + .../core/errorhandling/ErrorHandler.java | 12 +++++- .../opsudance/core/events/EventBus.java | 4 ++ 35 files changed, 199 insertions(+), 147 deletions(-) diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index cd1dd463..17efa2de 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -27,7 +27,11 @@ import java.util.List; import org.newdawn.slick.Animation; import org.newdawn.slick.Image; import org.newdawn.slick.SlickException; +import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; +import yugecin.opsudance.core.errorhandling.ErrorHandler; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.utils.SlickUtil; /** @@ -706,7 +710,9 @@ public enum GameImage { return; } - ErrorHandler.error(String.format("Could not find default image '%s'.", filename), null, false); + String err = String.format("Could not find default image '%s'.", filename); + Log.warn(err); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } /** @@ -765,7 +771,7 @@ public enum GameImage { img = img.getScaledCopy(0.5f); list.add(img); } catch (SlickException e) { - ErrorHandler.error(String.format("Failed to set image '%s'.", name), null, false); + EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", name), BubbleNotificationEvent.COMMONCOLOR_RED)); break; } } @@ -779,7 +785,7 @@ public enum GameImage { img = img.getScaledCopy(0.5f); list.add(img); } catch (SlickException e) { - ErrorHandler.error(String.format("Failed to set image '%s'.", name), null, false); + EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", name), BubbleNotificationEvent.COMMONCOLOR_RED)); break; } } @@ -806,7 +812,7 @@ public enum GameImage { img = img.getScaledCopy(0.5f); return img; } catch (SlickException e) { - ErrorHandler.error(String.format("Failed to set image '%s'.", filename), null, false); + EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", filename), BubbleNotificationEvent.COMMONCOLOR_RED)); } } } @@ -851,7 +857,7 @@ public enum GameImage { skinImages = null; } } catch (SlickException e) { - ErrorHandler.error(String.format("Failed to destroy beatmap skin images for '%s'.", this.name()), e, true); + ErrorHandler.error(String.format("Failed to destroy beatmap skin images for '%s'.", this.name()), e).show(); } } diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index 03a8c772..e095c5d2 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -60,6 +60,8 @@ import com.sun.jna.platform.win32.Win32Exception; import com.sun.jna.platform.win32.WinReg; import yugecin.opsudance.*; import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.errorhandling.ErrorHandler; +import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.movers.factories.ExgonMoverFactory; import yugecin.opsudance.movers.factories.QuadraticBezierMoverFactory; @@ -197,7 +199,7 @@ public class Options { } File dir = new File(rootPath, "opsu"); if (!dir.isDirectory() && !dir.mkdir()) - ErrorHandler.error(String.format("Failed to create configuration folder at '%s/opsu'.", rootPath), null, false); + ErrorHandler.error(String.format("Failed to create configuration folder at '%s/opsu'.", rootPath), new Exception("empty")).preventReport().show(); return dir; } else return workingDir; @@ -1785,7 +1787,7 @@ public class Options { // use default directory beatmapDir = BEATMAP_DIR; if (!beatmapDir.isDirectory() && !beatmapDir.mkdir()) - ErrorHandler.error(String.format("Failed to create beatmap directory at '%s'.", beatmapDir.getAbsolutePath()), null, false); + EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create beatmap directory at '%s'.", beatmapDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED)); return beatmapDir; } @@ -1800,7 +1802,7 @@ public class Options { oszDir = new File(DATA_DIR, "SongPacks/"); if (!oszDir.isDirectory() && !oszDir.mkdir()) - ErrorHandler.error(String.format("Failed to create song packs directory at '%s'.", oszDir.getAbsolutePath()), null, false); + EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create song packs directory at '%s'.", oszDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED)); return oszDir; } @@ -1815,7 +1817,7 @@ public class Options { replayImportDir = new File(DATA_DIR, "ReplayImport/"); if (!replayImportDir.isDirectory() && !replayImportDir.mkdir()) - ErrorHandler.error(String.format("Failed to create replay import directory at '%s'.", replayImportDir.getAbsolutePath()), null, false); + EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create replay import directory at '%s'.", replayImportDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED)); return replayImportDir; } @@ -1865,7 +1867,7 @@ public class Options { // use default directory skinRootDir = SKIN_ROOT_DIR; if (!skinRootDir.isDirectory() && !skinRootDir.mkdir()) - ErrorHandler.error(String.format("Failed to create skins directory at '%s'.", skinRootDir.getAbsolutePath()), null, false); + EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create skins directory at '%s'.", skinRootDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED)); return skinRootDir; } @@ -1998,7 +2000,9 @@ public class Options { } GameOption.DANCE_HIDE_WATERMARK.setValue(false); } catch (IOException e) { - ErrorHandler.error(String.format("Failed to read file '%s'.", OPTIONS_FILE.getAbsolutePath()), e, false); + String err = String.format("Failed to read file '%s'.", OPTIONS_FILE.getAbsolutePath()); + Log.error(err, e); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } } @@ -2027,7 +2031,9 @@ public class Options { } writer.close(); } catch (IOException e) { - ErrorHandler.error(String.format("Failed to write to file '%s'.", OPTIONS_FILE.getAbsolutePath()), e, false); + String err = String.format("Failed to write to file '%s'.", OPTIONS_FILE.getAbsolutePath()); + Log.error(err, e); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } } } diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index a66e320d..746a5e68 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -73,6 +73,9 @@ import org.newdawn.slick.util.Log; import com.sun.jna.platform.FileUtils; import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.errorhandling.ErrorHandler; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * Contains miscellaneous utilities. @@ -245,14 +248,14 @@ public class Utils { // create the screenshot directory File dir = Options.getScreenshotDir(); if (!dir.isDirectory() && !dir.mkdir()) { - ErrorHandler.error(String.format("Failed to create screenshot directory at '%s'.", dir.getAbsolutePath()), null, false); + EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create screenshot directory at '%s'.", dir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED)); return; } // create file name SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss"); - final File file = new File(dir, String.format("screenshot_%s.%s", - date.format(new Date()), Options.getScreenshotFormat())); + final String fileName = String.format("screenshot_%s.%s", date.format(new Date()), Options.getScreenshotFormat()); + final File file = new File(dir, fileName); SoundController.playSound(SoundEffect.SHUTTER); @@ -279,8 +282,9 @@ public class Utils { } } ImageIO.write(image, Options.getScreenshotFormat(), file); + EventBus.instance.post(new BubbleNotificationEvent("Created " + fileName, BubbleNotificationEvent.COMMONCOLOR_PURPLE)); } catch (Exception e) { - ErrorHandler.error("Failed to take a screenshot.", e, true); + ErrorHandler.error("Failed to take a screenshot.", e).show(); } } }.start(); @@ -429,7 +433,7 @@ public class Utils { try { json = new JSONObject(s); } catch (JSONException e) { - ErrorHandler.error("Failed to create JSON object.", e, true); + ErrorHandler.error("Failed to create JSON object.", e).show(); } } return json; @@ -448,7 +452,7 @@ public class Utils { try { json = new JSONArray(s); } catch (JSONException e) { - ErrorHandler.error("Failed to create JSON array.", e, true); + ErrorHandler.error("Failed to create JSON array.", e).show(); } } return json; @@ -490,7 +494,7 @@ public class Utils { result.append(String.format("%02x", b)); return result.toString(); } catch (NoSuchAlgorithmException | IOException e) { - ErrorHandler.error("Failed to calculate MD5 hash.", e, true); + ErrorHandler.error("Failed to calculate MD5 hash.", e).show(); } return null; } @@ -514,7 +518,7 @@ public class Utils { * @return true if JAR, false if file */ public static boolean isJarRunning() { - return Opsu.class.getResource(String.format("%s.class", Opsu.class.getSimpleName())).toString().startsWith("jar:"); + return Utils.class.getResource(String.format("%s.class", Utils.class.getSimpleName())).toString().startsWith("jar:"); } /** @@ -526,7 +530,7 @@ public class Utils { return null; try { - return new JarFile(new File(Opsu.class.getProtectionDomain().getCodeSource().getLocation().toURI()), false); + return new JarFile(new File(Utils.class.getProtectionDomain().getCodeSource().getLocation().toURI()), false); } catch (URISyntaxException | IOException e) { Log.error("Could not determine the JAR file.", e); return null; @@ -539,7 +543,7 @@ public class Utils { */ public static File getRunningDirectory() { try { - return new File(Opsu.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); + return new File(Utils.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); } catch (URISyntaxException e) { Log.error("Could not get the running directory.", e); return null; diff --git a/src/itdelatrisu/opsu/audio/MultiClip.java b/src/itdelatrisu/opsu/audio/MultiClip.java index 3193aac7..d314815f 100644 --- a/src/itdelatrisu/opsu/audio/MultiClip.java +++ b/src/itdelatrisu/opsu/audio/MultiClip.java @@ -1,6 +1,6 @@ package itdelatrisu.opsu.audio; -import itdelatrisu.opsu.ErrorHandler; +import yugecin.opsudance.core.errorhandling.ErrorHandler; import java.io.IOException; import java.util.Iterator; @@ -194,7 +194,7 @@ public class MultiClip { try { audioIn.close(); } catch (IOException e) { - ErrorHandler.error(String.format("Could not close AudioInputStream for MultiClip %s.", name), e, true); + ErrorHandler.error(String.format("Could not close AudioInputStream for MultiClip %s.", name), e).show(); } } } diff --git a/src/itdelatrisu/opsu/audio/MusicController.java b/src/itdelatrisu/opsu/audio/MusicController.java index ba4ec00a..e8c6a0ad 100644 --- a/src/itdelatrisu/opsu/audio/MusicController.java +++ b/src/itdelatrisu/opsu/audio/MusicController.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.audio; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.BeatmapParser; @@ -44,8 +43,12 @@ import org.newdawn.slick.MusicListener; import org.newdawn.slick.SlickException; import org.newdawn.slick.openal.Audio; import org.newdawn.slick.openal.SoundStore; +import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; import org.tritonus.share.sampled.file.TAudioFileFormat; +import yugecin.opsudance.core.errorhandling.ErrorHandler; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * Controller for all music. @@ -152,7 +155,9 @@ public class MusicController { }); playAt(position, loop); } catch (Exception e) { - ErrorHandler.error(String.format("Could not play track '%s'.", file.getName()), e, false); + String err = String.format("Could not play track '%s'.", file.getName()); + Log.error(err, e); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } } @@ -496,9 +501,7 @@ public class MusicController { trackLoader.interrupt(); try { trackLoader.join(); - } catch (InterruptedException e) { - ErrorHandler.error(null, e, true); - } + } catch (InterruptedException ignored) { } } trackLoader = null; @@ -575,7 +578,7 @@ public class MusicController { player = null; } catch (Exception e) { - ErrorHandler.error("Failed to destroy OpenAL.", e, true); + ErrorHandler.error("Failed to destroy OpenAL.", e).show(); } } diff --git a/src/itdelatrisu/opsu/audio/SoundController.java b/src/itdelatrisu/opsu/audio/SoundController.java index 7db7f608..10e8e850 100644 --- a/src/itdelatrisu/opsu/audio/SoundController.java +++ b/src/itdelatrisu/opsu/audio/SoundController.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.audio; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.audio.HitSound.SampleSet; import itdelatrisu.opsu.beatmap.HitObject; @@ -41,6 +40,9 @@ import javax.sound.sampled.LineUnavailableException; import org.newdawn.slick.SlickException; import org.newdawn.slick.util.ResourceLoader; +import yugecin.opsudance.core.errorhandling.ErrorHandler; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * Controller for all (non-music) sound components. @@ -99,7 +101,7 @@ public class SoundController { AudioInputStream audioIn = AudioSystem.getAudioInputStream(url); return loadClip(ref, audioIn, isMP3); } catch (Exception e) { - ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e, true); + ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e).show(); return null; } } @@ -214,7 +216,7 @@ public class SoundController { // menu and game sounds for (SoundEffect s : SoundEffect.values()) { if ((currentFileName = getSoundFileName(s.getFileName())) == null) { - ErrorHandler.error(String.format("Could not find sound file '%s'.", s.getFileName()), null, false); + EventBus.instance.post(new BubbleNotificationEvent("Could not find sound file " + s.getFileName(), BubbleNotificationEvent.COLOR_ORANGE)); continue; } MultiClip newClip = loadClip(currentFileName, currentFileName.endsWith(".mp3")); @@ -233,7 +235,7 @@ public class SoundController { for (HitSound s : HitSound.values()) { String filename = String.format("%s-%s", ss.getName(), s.getFileName()); if ((currentFileName = getSoundFileName(filename)) == null) { - ErrorHandler.error(String.format("Could not find hit sound file '%s'.", filename), null, false); + EventBus.instance.post(new BubbleNotificationEvent("Could not find hit sound file " + filename, BubbleNotificationEvent.COLOR_ORANGE)); continue; } MultiClip newClip = loadClip(currentFileName, false); @@ -277,7 +279,7 @@ public class SoundController { try { clip.start(volume, listener); } catch (LineUnavailableException e) { - ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e, true); + ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e).show(); } } } diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapParser.java b/src/itdelatrisu/opsu/beatmap/BeatmapParser.java index 3e3fa4f1..97e6442a 100644 --- a/src/itdelatrisu/opsu/beatmap/BeatmapParser.java +++ b/src/itdelatrisu/opsu/beatmap/BeatmapParser.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.beatmap; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.db.BeatmapDB; @@ -35,6 +34,9 @@ import java.util.Map; import org.newdawn.slick.Color; import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.errorhandling.ErrorHandler; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * Parser for beatmaps. @@ -243,9 +245,11 @@ public class BeatmapParser { } map.timingPoints.trimToSize(); } catch (IOException e) { - ErrorHandler.error(String.format("Failed to read file '%s'.", map.getFile().getAbsolutePath()), e, false); + String err = String.format("Failed to read file '%s'.", map.getFile().getAbsolutePath()); + Log.error(err, e); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } catch (NoSuchAlgorithmException e) { - ErrorHandler.error("Failed to get MD5 hash stream.", e, true); + ErrorHandler.error("Failed to get MD5 hash stream.", e).show(); // retry without MD5 hasNoMD5Algorithm = true; @@ -646,9 +650,11 @@ public class BeatmapParser { if (md5stream != null) beatmap.md5Hash = md5stream.getMD5(); } catch (IOException e) { - ErrorHandler.error(String.format("Failed to read file '%s'.", file.getAbsolutePath()), e, false); + String err = String.format("Failed to read file '%s'.", file.getAbsolutePath()); + Log.error(err, e); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } catch (NoSuchAlgorithmException e) { - ErrorHandler.error("Failed to get MD5 hash stream.", e, true); + ErrorHandler.error("Failed to get MD5 hash stream.", e).show(); // retry without MD5 hasNoMD5Algorithm = true; @@ -727,7 +733,9 @@ public class BeatmapParser { } beatmap.timingPoints.trimToSize(); } catch (IOException e) { - ErrorHandler.error(String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath()), e, false); + String err = String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath()); + Log.error(err, e); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } } @@ -803,9 +811,11 @@ public class BeatmapParser { // check that all objects were parsed if (objectIndex != beatmap.objects.length) ErrorHandler.error(String.format("Parsed %d objects for beatmap '%s', %d objects expected.", - objectIndex, beatmap.toString(), beatmap.objects.length), null, true); + objectIndex, beatmap.toString(), beatmap.objects.length), new Exception("no")).show(); } catch (IOException e) { - ErrorHandler.error(String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath()), e, false); + String err = String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath()); + Log.error(err, e); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } } diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java b/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java index dc9471ce..eda1338a 100644 --- a/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java +++ b/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java @@ -18,11 +18,12 @@ package itdelatrisu.opsu.beatmap; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.db.BeatmapDB; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; import java.io.File; import java.io.IOException; @@ -215,7 +216,7 @@ public class BeatmapSetList { try { Utils.deleteToTrash(dir); } catch (IOException e) { - ErrorHandler.error("Could not delete song group.", e, true); + EventBus.instance.post(new BubbleNotificationEvent("Could not delete song group", BubbleNotificationEvent.COLOR_ORANGE)); } if (ws != null) ws.resume(); @@ -270,7 +271,7 @@ public class BeatmapSetList { try { Utils.deleteToTrash(file); } catch (IOException e) { - ErrorHandler.error("Could not delete song.", e, true); + EventBus.instance.post(new BubbleNotificationEvent("Could not delete song", BubbleNotificationEvent.COLOR_ORANGE)); } if (ws != null) ws.resume(); diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapWatchService.java b/src/itdelatrisu/opsu/beatmap/BeatmapWatchService.java index 8399cc22..f864c2ad 100644 --- a/src/itdelatrisu/opsu/beatmap/BeatmapWatchService.java +++ b/src/itdelatrisu/opsu/beatmap/BeatmapWatchService.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.beatmap; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Options; import java.io.IOException; @@ -42,6 +41,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. @@ -96,7 +97,8 @@ public class BeatmapWatchService { ws = new BeatmapWatchService(); ws.register(Options.getBeatmapDir().toPath()); } catch (IOException e) { - ErrorHandler.error("An I/O exception occurred while creating the watch service.", e, true); + Log.error("Could not create watch service", e); + EventBus.instance.post(new BubbleNotificationEvent("Could not create watch service", BubbleNotificationEvent.COMMONCOLOR_RED)); return; } @@ -117,8 +119,9 @@ public class BeatmapWatchService { ws.service.shutdownNow(); ws = null; } catch (IOException e) { + Log.error("An I/O exception occurred while closing the previous watch service.", e); + EventBus.instance.post(new BubbleNotificationEvent("An I/O exception occurred while closing the previous watch service.", BubbleNotificationEvent.COMMONCOLOR_RED)); ws = null; - ErrorHandler.error("An I/O exception occurred while closing the previous watch service.", e, true); } } diff --git a/src/itdelatrisu/opsu/beatmap/OszUnpacker.java b/src/itdelatrisu/opsu/beatmap/OszUnpacker.java index 5b9c1071..e1e40f41 100644 --- a/src/itdelatrisu/opsu/beatmap/OszUnpacker.java +++ b/src/itdelatrisu/opsu/beatmap/OszUnpacker.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.beatmap; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Options; import java.io.File; @@ -28,6 +27,9 @@ import java.util.List; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; +import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * Unpacker for OSZ (ZIP) archives. @@ -97,8 +99,9 @@ public class OszUnpacker { ZipFile zipFile = new ZipFile(file); zipFile.extractAll(dest.getAbsolutePath()); } catch (ZipException e) { - ErrorHandler.error(String.format("Failed to unzip file %s to dest %s.", - file.getAbsolutePath(), dest.getAbsolutePath()), e, false); + String err = String.format("Failed to unzip file %s to dest %s.", file.getAbsolutePath(), dest.getAbsolutePath()); + Log.error(err, e); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } } diff --git a/src/itdelatrisu/opsu/db/BeatmapDB.java b/src/itdelatrisu/opsu/db/BeatmapDB.java index 2dbb1e2f..e6e0e4d5 100644 --- a/src/itdelatrisu/opsu/db/BeatmapDB.java +++ b/src/itdelatrisu/opsu/db/BeatmapDB.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.db; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.BeatmapParser; @@ -35,6 +34,7 @@ import java.util.List; import java.util.Map; import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.errorhandling.ErrorHandler; /** * Handles connections and queries with the cached beatmap database. @@ -110,7 +110,7 @@ public class BeatmapDB { try { updateSizeStmt = connection.prepareStatement("REPLACE INTO info (key, value) VALUES ('size', ?)"); } catch (SQLException e) { - ErrorHandler.error("Failed to prepare beatmap statements.", e, true); + ErrorHandler.error("Failed to prepare beatmap statements.", e).show(); } // retrieve the cache size @@ -132,7 +132,7 @@ public class BeatmapDB { updatePlayStatsStmt = connection.prepareStatement("UPDATE beatmaps SET playCount = ?, lastPlayed = ? WHERE dir = ? AND file = ?"); setFavoriteStmt = connection.prepareStatement("UPDATE beatmaps SET favorite = ? WHERE dir = ? AND file = ?"); } catch (SQLException e) { - ErrorHandler.error("Failed to prepare beatmap statements.", e, true); + ErrorHandler.error("Failed to prepare beatmap statements.", e).show(); } } @@ -170,7 +170,7 @@ public class BeatmapDB { sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', '%s')", DATABASE_VERSION); stmt.executeUpdate(sql); } catch (SQLException e) { - ErrorHandler.error("Could not create beatmap database.", e, true); + ErrorHandler.error("Coudl not create beatmap database.", e).show(); } } @@ -222,7 +222,7 @@ public class BeatmapDB { ps.close(); } } catch (SQLException e) { - ErrorHandler.error("Failed to update beatmap database.", e, true); + ErrorHandler.error("Failed to update beatmap database.", e).show(); } } @@ -240,7 +240,7 @@ public class BeatmapDB { } rs.close(); } catch (SQLException e) { - ErrorHandler.error("Could not get beatmap cache size.", e, true); + ErrorHandler.error("Could not get beatmap cache size.", e).show(); } } @@ -255,7 +255,7 @@ public class BeatmapDB { updateSizeStmt.setString(1, Integer.toString(Math.max(cacheSize, 0))); updateSizeStmt.executeUpdate(); } catch (SQLException e) { - ErrorHandler.error("Could not update beatmap cache size.", e, true); + ErrorHandler.error("Could not update beatmap cache size.", e).show(); } } @@ -273,7 +273,7 @@ public class BeatmapDB { cacheSize = 0; updateCacheSize(); } catch (SQLException e) { - ErrorHandler.error("Could not drop beatmap database.", e, true); + ErrorHandler.error("Could not drop beatmap database.", e).show(); } createDatabase(); } @@ -291,7 +291,7 @@ public class BeatmapDB { cacheSize += insertStmt.executeUpdate(); updateCacheSize(); } catch (SQLException e) { - ErrorHandler.error("Failed to add beatmap to database.", e, true); + ErrorHandler.error("Failed to add beatmap to database.", e).show(); } } @@ -344,7 +344,7 @@ public class BeatmapDB { // update cache size updateCacheSize(); } catch (SQLException e) { - ErrorHandler.error("Failed to add beatmaps to database.", e, true); + ErrorHandler.error("Failed to add beatmaps to database.", e).show(); } } @@ -432,7 +432,7 @@ public class BeatmapDB { } rs.close(); } catch (SQLException e) { - ErrorHandler.error("Failed to load Beatmap from database.", e, true); + ErrorHandler.error("Failed to load Beatmap from database.", e).show(); } } @@ -495,7 +495,7 @@ public class BeatmapDB { } rs.close(); } catch (SQLException e) { - ErrorHandler.error("Failed to load beatmaps from database.", e, true); + ErrorHandler.error("Failed to load beatmaps from database.", e).show(); } } @@ -596,7 +596,7 @@ public class BeatmapDB { rs.close(); return map; } catch (SQLException e) { - ErrorHandler.error("Failed to get last modified map from database.", e, true); + ErrorHandler.error("Failed to get last modified map from database.", e).show(); return null; } } @@ -616,7 +616,7 @@ public class BeatmapDB { cacheSize -= deleteMapStmt.executeUpdate(); updateCacheSize(); } catch (SQLException e) { - ErrorHandler.error("Failed to delete beatmap entry from database.", e, true); + ErrorHandler.error("Failed to delete beatmap entry from database.", e).show(); } } @@ -633,7 +633,7 @@ public class BeatmapDB { cacheSize -= deleteGroupStmt.executeUpdate(); updateCacheSize(); } catch (SQLException e) { - ErrorHandler.error("Failed to delete beatmap group entry from database.", e, true); + ErrorHandler.error("Failed to delete beatmap group entry from database.", e).show(); } } @@ -652,7 +652,7 @@ public class BeatmapDB { setStarsStmt.executeUpdate(); } catch (SQLException e) { ErrorHandler.error(String.format("Failed to save star rating '%.4f' for beatmap '%s' in database.", - beatmap.starRating, beatmap.toString()), e, true); + beatmap.starRating, beatmap.toString()), e).show(); } } @@ -672,7 +672,7 @@ public class BeatmapDB { updatePlayStatsStmt.executeUpdate(); } catch (SQLException e) { ErrorHandler.error(String.format("Failed to update play statistics for beatmap '%s' in database.", - beatmap.toString()), e, true); + beatmap.toString()), e).show(); } } @@ -691,7 +691,7 @@ public class BeatmapDB { setFavoriteStmt.executeUpdate(); } catch (SQLException e) { ErrorHandler.error(String.format("Failed to update favorite status for beatmap '%s' in database.", - beatmap.toString()), e, true); + beatmap.toString()), e).show(); } } @@ -711,7 +711,7 @@ public class BeatmapDB { connection.close(); connection = null; } catch (SQLException e) { - ErrorHandler.error("Failed to close beatmap database.", e, true); + ErrorHandler.error("Failed to close beatmap database.", e).show(); } } } diff --git a/src/itdelatrisu/opsu/db/DBController.java b/src/itdelatrisu/opsu/db/DBController.java index aebd414b..61e0d5ca 100644 --- a/src/itdelatrisu/opsu/db/DBController.java +++ b/src/itdelatrisu/opsu/db/DBController.java @@ -18,7 +18,7 @@ package itdelatrisu.opsu.db; -import itdelatrisu.opsu.ErrorHandler; +import yugecin.opsudance.core.errorhandling.ErrorHandler; import java.sql.Connection; import java.sql.DriverManager; @@ -39,7 +39,7 @@ public class DBController { try { Class.forName("org.sqlite.JDBC"); } catch (ClassNotFoundException e) { - ErrorHandler.error("Could not load sqlite-JDBC driver.", e, true); + ErrorHandler.error("Could not load sqlite-JDBC driver.", e).show(); } // initialize the databases @@ -65,7 +65,7 @@ public class DBController { return DriverManager.getConnection(String.format("jdbc:sqlite:%s", path)); } catch (SQLException e) { // if the error message is "out of memory", it probably means no database file is found - ErrorHandler.error(String.format("Could not connect to database: '%s'.", path), e, true); + ErrorHandler.error(String.format("Could not connect to database: '%s'.", path), e).show(); return null; } } diff --git a/src/itdelatrisu/opsu/db/ScoreDB.java b/src/itdelatrisu/opsu/db/ScoreDB.java index babf1841..6fa86910 100644 --- a/src/itdelatrisu/opsu/db/ScoreDB.java +++ b/src/itdelatrisu/opsu/db/ScoreDB.java @@ -18,10 +18,10 @@ package itdelatrisu.opsu.db; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.beatmap.Beatmap; +import yugecin.opsudance.core.errorhandling.ErrorHandler; import java.sql.Connection; import java.sql.PreparedStatement; @@ -124,7 +124,7 @@ public class ScoreDB { // TODO: extra playerName checks not needed if name is guaranteed not null ); } catch (SQLException e) { - ErrorHandler.error("Failed to prepare score statements.", e, true); + ErrorHandler.error("Failed to prepare score statements.", e).show(); } } @@ -157,7 +157,7 @@ public class ScoreDB { sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', %d)", DATABASE_VERSION); stmt.executeUpdate(sql); } catch (SQLException e) { - ErrorHandler.error("Could not create score database.", e, true); + ErrorHandler.error("Could not create score database.", e).show(); } } @@ -209,7 +209,7 @@ public class ScoreDB { ps.close(); } } catch (SQLException e) { - ErrorHandler.error("Failed to update score database.", e, true); + ErrorHandler.error("Failed to update score database.", e).show(); } } @@ -227,7 +227,7 @@ public class ScoreDB { insertStmt.setString(19, data.playerName); insertStmt.executeUpdate(); } catch (SQLException e) { - ErrorHandler.error("Failed to save score to database.", e, true); + ErrorHandler.error("Failed to save score to database.", e).show(); } } @@ -247,7 +247,7 @@ public class ScoreDB { deleteScoreStmt.setString(21, data.playerName); deleteScoreStmt.executeUpdate(); } catch (SQLException e) { - ErrorHandler.error("Failed to delete score from database.", e, true); + ErrorHandler.error("Failed to delete score from database.", e).show(); } } @@ -267,7 +267,7 @@ public class ScoreDB { deleteSongStmt.setString(5, beatmap.version); deleteSongStmt.executeUpdate(); } catch (SQLException e) { - ErrorHandler.error("Failed to delete scores from database.", e, true); + ErrorHandler.error("Failed to delete scores from database.", e).show(); } } @@ -335,7 +335,7 @@ public class ScoreDB { } rs.close(); } catch (SQLException e) { - ErrorHandler.error("Failed to read scores from database.", e, true); + ErrorHandler.error("Failed to read scores from database.", e).show(); return null; } return getSortedArray(list); @@ -377,7 +377,7 @@ public class ScoreDB { map.put(version, getSortedArray(list)); rs.close(); } catch (SQLException e) { - ErrorHandler.error("Failed to read scores from database.", e, true); + ErrorHandler.error("Failed to read scores from database.", e).show(); return null; } return map; @@ -406,7 +406,7 @@ public class ScoreDB { connection.close(); connection = null; } catch (SQLException e) { - ErrorHandler.error("Failed to close score database.", e, true); + ErrorHandler.error("Failed to close score database.", e).show(); } } } diff --git a/src/itdelatrisu/opsu/downloads/Download.java b/src/itdelatrisu/opsu/downloads/Download.java index 58818490..218533d3 100644 --- a/src/itdelatrisu/opsu/downloads/Download.java +++ b/src/itdelatrisu/opsu/downloads/Download.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.downloads; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Utils; import java.io.File; @@ -35,6 +34,9 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.errorhandling.ErrorHandler; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * File download. @@ -142,7 +144,7 @@ public class Download { this.url = new URL(remoteURL); } catch (MalformedURLException e) { this.status = Status.ERROR; - ErrorHandler.error(String.format("Bad download URL: '%s'", remoteURL), e, true); + ErrorHandler.error(String.format("Bad download URL: '%s'", remoteURL), e).show(); return; } this.localPath = localPath; @@ -215,7 +217,7 @@ public class Download { else if (redirectCount > MAX_REDIRECTS) error = String.format("Download for URL '%s' is attempting too many redirects (over %d).", base.toString(), MAX_REDIRECTS); if (error != null) { - ErrorHandler.error(error, null, false); + EventBus.instance.post(new BubbleNotificationEvent(error, BubbleNotificationEvent.COLOR_ORANGE)); throw new IOException(); } @@ -417,7 +419,7 @@ public class Download { } } catch (IOException e) { this.status = Status.ERROR; - ErrorHandler.error("Failed to cancel download.", e, true); + ErrorHandler.error("Failed to cancel download.", e).show(); } } } diff --git a/src/itdelatrisu/opsu/downloads/DownloadNode.java b/src/itdelatrisu/opsu/downloads/DownloadNode.java index 728dacf5..d8869fe4 100644 --- a/src/itdelatrisu/opsu/downloads/DownloadNode.java +++ b/src/itdelatrisu/opsu/downloads/DownloadNode.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.downloads; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; @@ -35,6 +34,8 @@ import java.io.File; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * Node containing song data and a Download object. @@ -402,7 +403,7 @@ public class DownloadNode { public void drawDownload(Graphics g, float position, int id, boolean hover) { Download download = this.download; // in case clearDownload() is called asynchronously if (download == null) { - ErrorHandler.error("Trying to draw download information for button without Download object.", null, false); + EventBus.instance.post(new BubbleNotificationEvent("Trying to draw download information for button without Download object", BubbleNotificationEvent.COLOR_ORANGE)); return; } diff --git a/src/itdelatrisu/opsu/downloads/Updater.java b/src/itdelatrisu/opsu/downloads/Updater.java index 307b8e54..fbd607ea 100644 --- a/src/itdelatrisu/opsu/downloads/Updater.java +++ b/src/itdelatrisu/opsu/downloads/Updater.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.downloads; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.downloads.Download.DownloadListener; @@ -38,6 +37,7 @@ import java.util.Properties; import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; +import yugecin.opsudance.core.errorhandling.ErrorHandler; /** * Handles automatic program updates. @@ -298,7 +298,7 @@ public class Updater { pb.start(); } catch (IOException e) { status = Status.INTERNAL_ERROR; - ErrorHandler.error("Failed to start new process.", e, true); + ErrorHandler.error("Failed to start new process.", e).show(); } } } diff --git a/src/itdelatrisu/opsu/downloads/servers/BloodcatServer.java b/src/itdelatrisu/opsu/downloads/servers/BloodcatServer.java index 335982e9..d86a4579 100644 --- a/src/itdelatrisu/opsu/downloads/servers/BloodcatServer.java +++ b/src/itdelatrisu/opsu/downloads/servers/BloodcatServer.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.downloads.servers; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.downloads.DownloadNode; @@ -34,6 +33,7 @@ import java.util.Date; import org.json.JSONArray; import org.json.JSONObject; +import yugecin.opsudance.core.errorhandling.ErrorHandler; /** * Download server: http://bloodcat.com/osu/ @@ -97,7 +97,7 @@ public class BloodcatServer extends DownloadServer { resultCount++; this.totalResults = resultCount; } catch (MalformedURLException | UnsupportedEncodingException e) { - ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); + ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); } return nodes; } diff --git a/src/itdelatrisu/opsu/downloads/servers/HexideServer.java b/src/itdelatrisu/opsu/downloads/servers/HexideServer.java index e9b7c683..f0c6cb7d 100644 --- a/src/itdelatrisu/opsu/downloads/servers/HexideServer.java +++ b/src/itdelatrisu/opsu/downloads/servers/HexideServer.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.downloads.servers; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.downloads.DownloadNode; @@ -30,6 +29,7 @@ import java.net.URLEncoder; import org.json.JSONArray; import org.json.JSONObject; +import yugecin.opsudance.core.errorhandling.ErrorHandler; /** * Download server: https://osu.hexide.com/ @@ -128,7 +128,7 @@ public class HexideServer extends DownloadServer { // all results at once; this approach just gets pagination correct. this.totalResults = arr.length() + resultIndex; } catch (MalformedURLException | UnsupportedEncodingException e) { - ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); + ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); } return nodes; } diff --git a/src/itdelatrisu/opsu/downloads/servers/MengSkyServer.java b/src/itdelatrisu/opsu/downloads/servers/MengSkyServer.java index 1c72dff5..a2e298a4 100644 --- a/src/itdelatrisu/opsu/downloads/servers/MengSkyServer.java +++ b/src/itdelatrisu/opsu/downloads/servers/MengSkyServer.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.downloads.servers; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.downloads.DownloadNode; @@ -30,6 +29,7 @@ import java.net.URLEncoder; import org.json.JSONArray; import org.json.JSONObject; +import yugecin.opsudance.core.errorhandling.ErrorHandler; /** * Download server: http://osu.mengsky.net/ @@ -101,7 +101,7 @@ public class MengSkyServer extends DownloadServer { resultCount = 1 + (pageTotal - 1) * PAGE_LIMIT; this.totalResults = resultCount; } catch (MalformedURLException | UnsupportedEncodingException e) { - ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); + ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); } return nodes; } diff --git a/src/itdelatrisu/opsu/downloads/servers/MnetworkServer.java b/src/itdelatrisu/opsu/downloads/servers/MnetworkServer.java index 4341512a..e9c2e3c6 100644 --- a/src/itdelatrisu/opsu/downloads/servers/MnetworkServer.java +++ b/src/itdelatrisu/opsu/downloads/servers/MnetworkServer.java @@ -18,9 +18,9 @@ package itdelatrisu.opsu.downloads.servers; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.downloads.DownloadNode; +import yugecin.opsudance.core.errorhandling.ErrorHandler; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -120,7 +120,7 @@ public class MnetworkServer extends DownloadServer { // store total result count this.totalResults = nodes.length; } catch (MalformedURLException | UnsupportedEncodingException e) { - ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); + ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); } return nodes; } diff --git a/src/itdelatrisu/opsu/downloads/servers/OsuMirrorServer.java b/src/itdelatrisu/opsu/downloads/servers/OsuMirrorServer.java index 834f2b9f..435eee2f 100644 --- a/src/itdelatrisu/opsu/downloads/servers/OsuMirrorServer.java +++ b/src/itdelatrisu/opsu/downloads/servers/OsuMirrorServer.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.downloads.servers; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.downloads.DownloadNode; @@ -36,6 +35,7 @@ import java.util.TimeZone; import org.json.JSONArray; import org.json.JSONObject; +import yugecin.opsudance.core.errorhandling.ErrorHandler; /** * Download server: http://loli.al/ @@ -123,7 +123,7 @@ public class OsuMirrorServer extends DownloadServer { else this.totalResults = maxServerID; } catch (MalformedURLException | UnsupportedEncodingException e) { - ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); + ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); } return nodes; } diff --git a/src/itdelatrisu/opsu/downloads/servers/YaSOnlineServer.java b/src/itdelatrisu/opsu/downloads/servers/YaSOnlineServer.java index 3d25459a..9e13dc58 100644 --- a/src/itdelatrisu/opsu/downloads/servers/YaSOnlineServer.java +++ b/src/itdelatrisu/opsu/downloads/servers/YaSOnlineServer.java @@ -17,7 +17,6 @@ */ package itdelatrisu.opsu.downloads.servers; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.downloads.DownloadNode; @@ -34,6 +33,7 @@ import java.util.Iterator; import java.util.List; import org.json.JSONObject; +import yugecin.opsudance.core.errorhandling.ErrorHandler; /** * Download server: http://osu.yas-online.net/ @@ -114,7 +114,7 @@ public class YaSOnlineServer extends DownloadServer { String downloadLink = item.getString("downloadLink"); return String.format(DOWNLOAD_FETCH_URL, downloadLink); } catch (MalformedURLException | UnsupportedEncodingException e) { - ErrorHandler.error(String.format("Problem retrieving download URL for beatmap '%d'.", beatmapSetID), e, true); + ErrorHandler.error(String.format("Problem retrieving download URL for beatmap '%d'.", beatmapSetID), e).show(); return null; } finally { Utils.setSSLCertValidation(true); @@ -186,7 +186,7 @@ public class YaSOnlineServer extends DownloadServer { else this.totalResults = maxServerID; } catch (MalformedURLException | UnsupportedEncodingException e) { - ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); + ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); } finally { Utils.setSSLCertValidation(true); } diff --git a/src/itdelatrisu/opsu/render/CurveRenderState.java b/src/itdelatrisu/opsu/render/CurveRenderState.java index a70846b6..9ca3a33e 100644 --- a/src/itdelatrisu/opsu/render/CurveRenderState.java +++ b/src/itdelatrisu/opsu/render/CurveRenderState.java @@ -96,7 +96,7 @@ public class CurveRenderState { */ public static void shutdown() { staticState.shutdown(); - FrameBufferCache.shutdown(); + //FrameBufferCache.shutdown(); } /** diff --git a/src/itdelatrisu/opsu/render/FrameBufferCache.java b/src/itdelatrisu/opsu/render/FrameBufferCache.java index c27a80cb..bf1a96aa 100644 --- a/src/itdelatrisu/opsu/render/FrameBufferCache.java +++ b/src/itdelatrisu/opsu/render/FrameBufferCache.java @@ -128,6 +128,7 @@ public class FrameBufferCache { *

* This is necessary for cases when the game gets restarted with a * different resolution without closing the process. + * // TODO d do we still need this */ public static void shutdown() { FrameBufferCache fbcInstance = FrameBufferCache.getInstance(); diff --git a/src/itdelatrisu/opsu/replay/Replay.java b/src/itdelatrisu/opsu/replay/Replay.java index 6f2d9fb4..5ee6e271 100644 --- a/src/itdelatrisu/opsu/replay/Replay.java +++ b/src/itdelatrisu/opsu/replay/Replay.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.replay; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.Utils; @@ -45,6 +44,9 @@ import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream; import org.newdawn.slick.util.Log; import lzma.streams.LzmaOutputStream; +import yugecin.opsudance.core.errorhandling.ErrorHandler; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * Captures osu! replay data. @@ -273,7 +275,7 @@ public class Replay { File dir = Options.getReplayDir(); if (!dir.isDirectory()) { if (!dir.mkdir()) { - ErrorHandler.error("Failed to create replay directory.", null, false); + EventBus.instance.post(new BubbleNotificationEvent("Failed to create replay directory.", BubbleNotificationEvent.COMMONCOLOR_RED)); return; } } @@ -343,7 +345,7 @@ public class Replay { compressedOut.write(bytes); } catch (IOException e) { // possible OOM: https://github.com/jponge/lzma-java/issues/9 - ErrorHandler.error("LZMA compression failed (possible out-of-memory error).", e, true); + ErrorHandler.error("LZMA compression failed (possible out-of-memory error).", e).show(); } compressedOut.close(); bout.close(); @@ -357,7 +359,7 @@ public class Replay { writer.close(); } catch (IOException e) { - ErrorHandler.error("Could not save replay data.", e, true); + ErrorHandler.error("Could not save replay data.", e).show(); } } }.start(); diff --git a/src/itdelatrisu/opsu/replay/ReplayImporter.java b/src/itdelatrisu/opsu/replay/ReplayImporter.java index 22e4a7dd..ee901143 100644 --- a/src/itdelatrisu/opsu/replay/ReplayImporter.java +++ b/src/itdelatrisu/opsu/replay/ReplayImporter.java @@ -18,7 +18,7 @@ package itdelatrisu.opsu.replay; -import itdelatrisu.opsu.ErrorHandler; +import com.sun.deploy.security.EnhancedJarVerifier; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.BeatmapSetList; @@ -31,6 +31,8 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * Importer for replay files. @@ -70,7 +72,9 @@ public class ReplayImporter { File replayDir = Options.getReplayDir(); if (!replayDir.isDirectory()) { if (!replayDir.mkdir()) { - ErrorHandler.error(String.format("Failed to create replay directory '%s'.", replayDir.getAbsolutePath()), null, false); + String err = String.format("Failed to create replay directory '%s'.", replayDir.getAbsolutePath()); + Log.error(err); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); return; } } @@ -83,7 +87,9 @@ public class ReplayImporter { r.loadHeader(); } catch (IOException e) { moveToFailedDirectory(file); - ErrorHandler.error(String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName()), e, false); + String err = String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName()); + Log.error(err, e); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); continue; } Beatmap beatmap = BeatmapSetList.get().getBeatmapFromHash(r.beatmapHash); @@ -100,8 +106,9 @@ public class ReplayImporter { } } else { moveToFailedDirectory(file); - ErrorHandler.error(String.format("Failed to import replay '%s'. The associated beatmap could not be found.", file.getName()), null, false); - continue; + String err = String.format("Failed to import replay '%s'. The associated beatmap could not be found.", file.getName()); + Log.error(err); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } } diff --git a/src/itdelatrisu/opsu/skins/SkinLoader.java b/src/itdelatrisu/opsu/skins/SkinLoader.java index b7ac04ec..5c9be6e6 100644 --- a/src/itdelatrisu/opsu/skins/SkinLoader.java +++ b/src/itdelatrisu/opsu/skins/SkinLoader.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.skins; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.Utils; @@ -32,6 +31,8 @@ import java.util.LinkedList; import org.newdawn.slick.Color; import org.newdawn.slick.util.Log; +import yugecin.opsudance.core.events.EventBus; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * Loads skin configuration files. @@ -290,7 +291,9 @@ public class SkinLoader { } } } catch (IOException e) { - ErrorHandler.error(String.format("Failed to read file '%s'.", skinFile.getAbsolutePath()), e, false); + String err = String.format("Failed to read file '%s'.", skinFile.getAbsolutePath()); + Log.error(err, e); + EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); } return skin; diff --git a/src/itdelatrisu/opsu/states/DownloadsMenu.java b/src/itdelatrisu/opsu/states/DownloadsMenu.java index 718be4dd..83a31bf6 100644 --- a/src/itdelatrisu/opsu/states/DownloadsMenu.java +++ b/src/itdelatrisu/opsu/states/DownloadsMenu.java @@ -19,7 +19,6 @@ package itdelatrisu.opsu.states; import itdelatrisu.opsu.GameImage; -import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.audio.MusicController; diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index a56418fe..a099e037 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.states; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; @@ -46,10 +45,13 @@ import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; +import org.newdawn.slick.util.Log; import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.OpsuState; +import yugecin.opsudance.events.BubbleNotificationEvent; /** * "Main Menu" state. @@ -570,7 +572,8 @@ public class MainMenu extends BaseOpsuState { } catch (UnsupportedOperationException e) { UI.sendBarNotification("The repository web page could not be opened."); } catch (IOException e) { - ErrorHandler.error("Could not browse to repository URI.", e, false); + Log.error("could not browse to repo", e); + displayContainer.eventBus.post(new BubbleNotificationEvent("Could not browse to repo", BubbleNotificationEvent.COLOR_ORANGE)); } return true; } @@ -581,7 +584,8 @@ public class MainMenu extends BaseOpsuState { } catch (UnsupportedOperationException e) { UI.sendBarNotification("The repository web page could not be opened."); } catch (IOException e) { - ErrorHandler.error("Could not browse to repository URI.", e, false); + Log.error("could not browse to repo", e); + displayContainer.eventBus.post(new BubbleNotificationEvent("Could not browse to repo", BubbleNotificationEvent.COLOR_ORANGE)); } return true; } diff --git a/src/itdelatrisu/opsu/ui/Cursor.java b/src/itdelatrisu/opsu/ui/Cursor.java index c3025ab7..1b7894c3 100644 --- a/src/itdelatrisu/opsu/ui/Cursor.java +++ b/src/itdelatrisu/opsu/ui/Cursor.java @@ -18,9 +18,7 @@ package itdelatrisu.opsu.ui; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.GameImage; -import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.audio.MusicController; @@ -37,6 +35,7 @@ import org.newdawn.slick.*; import org.newdawn.slick.state.StateBasedGame; import yugecin.opsudance.Dancer; import yugecin.opsudance.core.DisplayContainer; +import yugecin.opsudance.core.errorhandling.ErrorHandler; /** * Updates and draws the cursor. @@ -91,7 +90,7 @@ public class Cursor { IntBuffer tmp = BufferUtils.createIntBuffer(min * min); emptyCursor = new org.lwjgl.input.Cursor(min, min, min/2, min/2, 1, tmp, null); } catch (LWJGLException e) { - ErrorHandler.error("Failed to create hidden cursor.", e, true); + ErrorHandler.error("Failed to create hidden cursor.", e).show(); } } diff --git a/src/itdelatrisu/opsu/ui/UI.java b/src/itdelatrisu/opsu/ui/UI.java index 1afed82e..8c5d5a10 100644 --- a/src/itdelatrisu/opsu/ui/UI.java +++ b/src/itdelatrisu/opsu/ui/UI.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.ui; -import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; @@ -455,19 +454,4 @@ public class UI { Colors.WHITE_ALPHA.a = oldAlphaW; } - /** - * Shows a confirmation dialog (used before exiting the game). - * @param message the message to display - * @return true if user selects "yes", false otherwise - */ - public static boolean showExitConfirmation(String message) { - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception e) { - ErrorHandler.error("Could not set system look and feel for exit confirmation.", e, true); - } - int n = JOptionPane.showConfirmDialog(null, message, "Warning", - JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - return (n != JOptionPane.YES_OPTION); - } } diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 04b04054..2fa43c60 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -23,7 +23,6 @@ import itdelatrisu.opsu.beatmap.BeatmapWatchService; import itdelatrisu.opsu.db.DBController; import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.Updater; -import itdelatrisu.opsu.render.CurveRenderState; import itdelatrisu.opsu.states.Splash; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.DisplayContainer; @@ -78,7 +77,6 @@ public class OpsuDance { DBController.closeConnections(); DownloadList.get().cancelAllDownloads(); Utils.deleteDirectory(Options.TEMP_DIR); - CurveRenderState.shutdown(); if (!Options.isWatchServiceEnabled()) { BeatmapWatchService.destroy(); } @@ -89,7 +87,7 @@ public class OpsuDance { container.setup(); container.resume(); } catch (Exception e) { - ErrorHandler.error("could not initialize GL", e).preventContinue().show(); + ErrorHandler.error("could not initialize GL", e).allowTerminate().preventContinue().show(); return false; } Exception caughtException = null; @@ -100,7 +98,7 @@ public class OpsuDance { } container.teardown(); container.pause(); - return caughtException != null && ErrorHandler.error("update/render error", caughtException).show().shouldIgnoreAndContinue(); + return caughtException != null && ErrorHandler.error("update/render error", caughtException).allowTerminate().show().shouldIgnoreAndContinue(); } private void initDatabase() { @@ -174,7 +172,7 @@ public class OpsuDance { } private void errorAndExit(String errstr) { - ErrorHandler.error(errstr, new Throwable()).preventContinue().show(); + ErrorHandler.error(errstr, new Throwable()).allowTerminate().preventContinue().show(); System.exit(1); } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 4385de56..4e7f6308 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -24,6 +24,7 @@ import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.Updater; +import itdelatrisu.opsu.render.CurveRenderState; import itdelatrisu.opsu.ui.Fonts; import org.lwjgl.Sys; import org.lwjgl.openal.AL; @@ -222,6 +223,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen GameImage.destroyImages(); GameData.Grade.destroyImages(); Beatmap.destroyBackgroundImageCache(); + CurveRenderState.shutdown(); Display.destroy(); } diff --git a/src/yugecin/opsudance/core/errorhandling/ErrorHandler.java b/src/yugecin/opsudance/core/errorhandling/ErrorHandler.java index 163a1940..99321ba4 100644 --- a/src/yugecin/opsudance/core/errorhandling/ErrorHandler.java +++ b/src/yugecin/opsudance/core/errorhandling/ErrorHandler.java @@ -52,6 +52,7 @@ public class ErrorHandler { private boolean preventContinue; private boolean preventReport; private boolean ignoreAndContinue; + private boolean allowTerminate; public ErrorHandler(DisplayContainer displayContainer) { this.displayContainer = displayContainer; @@ -95,6 +96,11 @@ public class ErrorHandler { return this; } + public ErrorHandler allowTerminate() { + allowTerminate = true; + return this; + } + public ErrorHandler preventContinue() { preventContinue = true; return this; @@ -127,7 +133,9 @@ public class ErrorHandler { Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(), createReportButton() }; String[] buttons; - if (preventContinue) { + if (!allowTerminate && !preventContinue) { + buttons = new String[] { "Ignore & continue" }; + } else if (preventContinue) { buttons = new String[] { "Terminate" }; } else { buttons = new String[] { "Terminate", "Ignore & continue" }; @@ -145,7 +153,7 @@ public class ErrorHandler { null, buttons, buttons[buttons.length - 1]); - ignoreAndContinue = result == 1; + ignoreAndContinue = !allowTerminate || result == 1; frame.dispose(); return this; diff --git a/src/yugecin/opsudance/core/events/EventBus.java b/src/yugecin/opsudance/core/events/EventBus.java index 51f142a3..5bdf6c98 100644 --- a/src/yugecin/opsudance/core/events/EventBus.java +++ b/src/yugecin/opsudance/core/events/EventBus.java @@ -22,10 +22,14 @@ import java.util.*; @SuppressWarnings("unchecked") public class EventBus { + @Deprecated + public static EventBus instance; // TODO get rid of this + private final List subscribers; public EventBus() { subscribers = new LinkedList<>(); + instance = this; } public void subscribe(Class eventType, EventListener eventListener) { From 95a466f92f8bff8958c3831fccc4b96982bcf958 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sat, 21 Jan 2017 01:17:47 +0100 Subject: [PATCH 88/92] removing now unused classes --- src/itdelatrisu/opsu/Container.java | 184 ----- src/itdelatrisu/opsu/ErrorHandler.java | 242 ------ src/itdelatrisu/opsu/Opsu.java | 309 -------- src/org/newdawn/slick/GameContainer.java | 907 ----------------------- 4 files changed, 1642 deletions(-) delete mode 100644 src/itdelatrisu/opsu/Container.java delete mode 100644 src/itdelatrisu/opsu/ErrorHandler.java delete mode 100644 src/itdelatrisu/opsu/Opsu.java delete mode 100644 src/org/newdawn/slick/GameContainer.java diff --git a/src/itdelatrisu/opsu/Container.java b/src/itdelatrisu/opsu/Container.java deleted file mode 100644 index 63880381..00000000 --- a/src/itdelatrisu/opsu/Container.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * opsu! - an open-source osu! client - * Copyright (C) 2014, 2015 Jeffrey Han - * - * opsu! 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! 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!. If not, see . - */ - -package itdelatrisu.opsu; - -import itdelatrisu.opsu.audio.MusicController; -import itdelatrisu.opsu.audio.SoundController; -import itdelatrisu.opsu.beatmap.Beatmap; -import itdelatrisu.opsu.beatmap.BeatmapGroup; -import itdelatrisu.opsu.beatmap.BeatmapSetList; -import itdelatrisu.opsu.beatmap.BeatmapSortOrder; -import itdelatrisu.opsu.beatmap.BeatmapWatchService; -import itdelatrisu.opsu.downloads.DownloadList; -import itdelatrisu.opsu.downloads.Updater; -import itdelatrisu.opsu.render.CurveRenderState; -import itdelatrisu.opsu.ui.UI; - -import org.lwjgl.opengl.Display; -import org.newdawn.slick.AppGameContainer; -import org.newdawn.slick.Game; -import org.newdawn.slick.SlickException; -import org.newdawn.slick.opengl.InternalTextureLoader; - -/** - * AppGameContainer extension that sends critical errors to ErrorHandler. - */ -public class Container extends AppGameContainer { - /** Exception causing game failure. */ - protected Exception e = null; - - public static Container instance; - - /** - * Create a new container wrapping a game - * - * @param game The game to be wrapped - * @throws SlickException Indicates a failure to initialise the display - */ - public Container(Game game) throws SlickException { - super(game); - instance = this; - } - - /** - * Create a new container wrapping a game - * - * @param game The game to be wrapped - * @param width The width of the display required - * @param height The height of the display required - * @param fullscreen True if we want fullscreen mode - * @throws SlickException Indicates a failure to initialise the display - */ - public Container(Game game, int width, int height, boolean fullscreen) throws SlickException { - super(game, width, height, fullscreen); - } - - @Override - public void start() throws SlickException { - try { - setup(); - ErrorHandler.setGlString(); - getDelta(); - while (running()) - gameLoop(); - } catch (Exception e) { - this.e = e; - } - - // destroy the game container - try { - close_sub(); - } catch (Exception e) { - if (this.e == null) // suppress if caused by a previous exception - this.e = e; - } - destroy(); - - // report any critical errors - if (e != null) { - ErrorHandler.error(null, e, true); - e = null; - forceExit = true; - } - - if (forceExit) { - Opsu.close(); - System.exit(0); - } - } - - @Override - protected void gameLoop() throws SlickException { - int delta = getDelta(); - if (!Display.isVisible() && updateOnlyOnVisible) { - try { Thread.sleep(100); } catch (Exception e) {} - } else { - try { - updateAndRender(delta); - } catch (SlickException e) { - this.e = e; // store exception to display later - running = false; - return; - } - } - updateFPS(); - Display.update(); - if (Display.isCloseRequested()) { - if (game.closeRequested()) - running = false; - } - } - - /** - * Actions to perform before destroying the game container. - */ - private void close_sub() { - // save user options - Options.saveOptions(); - - // reset cursor - UI.getCursor().reset(); - - // destroy images - InternalTextureLoader.get().clear(); - - // reset image references - GameImage.clearReferences(); - GameData.Grade.clearReferences(); - Beatmap.clearBackgroundImageCache(); - - // prevent loading tracks from re-initializing OpenAL - MusicController.reset(); - - // stop any playing track - SoundController.stopTrack(); - - // reset BeatmapSetList data - BeatmapGroup.set(BeatmapGroup.ALL); - BeatmapSortOrder.set(BeatmapSortOrder.TITLE); - if (BeatmapSetList.get() != null) - BeatmapSetList.get().reset(); - - // delete OpenGL objects involved in the Curve rendering - CurveRenderState.shutdown(); - - // destroy watch service - if (!Options.isWatchServiceEnabled()) - BeatmapWatchService.destroy(); - BeatmapWatchService.removeListeners(); - - // delete temporary directory - Utils.deleteDirectory(Options.TEMP_DIR); - } - - @Override - public void exit() { - // show confirmation dialog if any downloads are active - if (forceExit) { - if (DownloadList.get().hasActiveDownloads() && - UI.showExitConfirmation(DownloadList.EXIT_CONFIRMATION)) - return; - if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING && - UI.showExitConfirmation(Updater.EXIT_CONFIRMATION)) - return; - } - - super.exit(); - } -} diff --git a/src/itdelatrisu/opsu/ErrorHandler.java b/src/itdelatrisu/opsu/ErrorHandler.java deleted file mode 100644 index 1ccead20..00000000 --- a/src/itdelatrisu/opsu/ErrorHandler.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * opsu! - an open-source osu! client - * Copyright (C) 2014, 2015 Jeffrey Han - * - * opsu! 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! 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!. If not, see . - */ - -package itdelatrisu.opsu; - -import java.awt.Cursor; -import java.awt.Desktop; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLEncoder; -import java.util.Properties; - -import javax.swing.JOptionPane; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; -import javax.swing.UIManager; - -import org.lwjgl.opengl.GL11; -import org.newdawn.slick.util.Log; -import org.newdawn.slick.util.ResourceLoader; - -/** - * Error handler to log and display errors. - */ -public class ErrorHandler { - /** Error popup title. */ - private static final String title = "Error"; - - /** Error popup description text. */ - private static final String - desc = "opsu! has encountered an error.", - descReport = "opsu! has encountered an error. Please report this!"; - - /** Error popup button options. */ - private static final String[] - optionsLog = {"View Error Log", "Close"}, - optionsReport = {"Send Report", "Close"}, - optionsLogReport = {"Send Report", "View Error Log", "Close"}; - - /** Text area for Exception. */ - private static final JTextArea textArea = new JTextArea(15, 100); - static { - textArea.setEditable(false); - textArea.setBackground(UIManager.getColor("Panel.background")); - textArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); - textArea.setTabSize(2); - textArea.setLineWrap(false); - textArea.setWrapStyleWord(true); - } - - /** Scroll pane holding JTextArea. */ - private static final JScrollPane scroll = new JScrollPane(textArea); - - /** Error popup objects. */ - private static final Object[] - message = { desc, scroll }, - messageReport = { descReport, scroll }; - - /** OpenGL string (if any). */ - private static String glString = null; - - // This class should not be instantiated. - private ErrorHandler() {} - - /** - * Sets the OpenGL version string. - */ - public static void setGlString() { - try { - String glVersion = GL11.glGetString(GL11.GL_VERSION); - String glVendor = GL11.glGetString(GL11.GL_VENDOR); - glString = String.format("%s (%s)", glVersion, glVendor); - } catch (Exception e) {} - } - - /** - * Displays an error popup and logs the given error. - * @param error a description of the error - * @param e the exception causing the error - * @param report whether to ask to report the error - */ - public static void error(String error, Throwable e, boolean report) { - if (error == null && e == null) - return; - - // log the error - if (error == null) - Log.error(e); - else if (e == null) - Log.error(error); - else - Log.error(error, e); - - // set the textArea to the error message - textArea.setText(null); - if (error != null) { - textArea.append(error); - textArea.append("\n"); - } - String trace = null; - if (e != null) { - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - trace = sw.toString(); - textArea.append(trace); - } - - // display popup - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - Desktop desktop = null; - boolean isBrowseSupported = false, isOpenSupported = false; - if (Desktop.isDesktopSupported()) { - desktop = Desktop.getDesktop(); - isBrowseSupported = desktop.isSupported(Desktop.Action.BROWSE); - isOpenSupported = desktop.isSupported(Desktop.Action.OPEN); - } - if (desktop != null && (isOpenSupported || (report && isBrowseSupported))) { // try to open the log file and/or issues webpage - if (report && isBrowseSupported) { // ask to report the error - if (isOpenSupported) { // also ask to open the log - int n = JOptionPane.showOptionDialog(null, messageReport, title, - JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, - null, optionsLogReport, optionsLogReport[2]); - if (n == 0) - desktop.browse(getIssueURI(error, e, trace)); - else if (n == 1) - desktop.open(Options.LOG_FILE); - } else { // only ask to report the error - int n = JOptionPane.showOptionDialog(null, message, title, - JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, - null, optionsReport, optionsReport[1]); - if (n == 0) - desktop.browse(getIssueURI(error, e, trace)); - } - } else { // don't report the error - int n = JOptionPane.showOptionDialog(null, message, title, - JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, - null, optionsLog, optionsLog[1]); - if (n == 0) - desktop.open(Options.LOG_FILE); - } - } else { // display error only - JOptionPane.showMessageDialog(null, report ? messageReport : message, - title, JOptionPane.ERROR_MESSAGE); - } - } catch (Exception e1) { - Log.error("An error occurred in the crash popup.", e1); - } - } - - /** - * Returns the issue reporting URI. - * This will auto-fill the report with the relevant information if possible. - * @param error a description of the error - * @param e the exception causing the error - * @param trace the stack trace - * @return the created URI - */ - private static URI getIssueURI(String error, Throwable e, String trace) { - // generate report information - String issueTitle = (error != null) ? error : e.getMessage(); - StringBuilder sb = new StringBuilder(); - try { - // read version and build date from version file, if possible - Properties props = new Properties(); - props.load(ResourceLoader.getResourceAsStream(Options.VERSION_FILE)); - String version = props.getProperty("version"); - if (version != null && !version.equals("${pom.version}")) { - sb.append("**Version:** "); - sb.append(version); - String hash = Utils.getGitHash(); - if (hash != null) { - sb.append(" ("); - sb.append(hash.substring(0, 12)); - sb.append(')'); - } - sb.append('\n'); - } - String timestamp = props.getProperty("build.date"); - if (timestamp != null && - !timestamp.equals("${maven.build.timestamp}") && !timestamp.equals("${timestamp}")) { - sb.append("**Build date:** "); - sb.append(timestamp); - sb.append('\n'); - } - } catch (IOException e1) { - Log.warn("Could not read version file.", e1); - } - sb.append("**OS:** "); - sb.append(System.getProperty("os.name")); - sb.append(" ("); - sb.append(System.getProperty("os.arch")); - sb.append(")\n"); - sb.append("**JRE:** "); - sb.append(System.getProperty("java.version")); - sb.append('\n'); - if (glString != null) { - sb.append("**OpenGL Version:** "); - sb.append(glString); - sb.append('\n'); - } - if (error != null) { - sb.append("**Error:** `"); - sb.append(error); - sb.append("`\n"); - } - if (trace != null) { - sb.append("**Stack trace:**"); - sb.append("\n```\n"); - sb.append(trace); - sb.append("```"); - } - - // return auto-filled URI - try { - return URI.create(String.format(Options.ISSUES_URL, - URLEncoder.encode(issueTitle, "UTF-8"), - URLEncoder.encode(sb.toString(), "UTF-8"))); - } catch (UnsupportedEncodingException e1) { - Log.warn("URLEncoder failed to encode the auto-filled issue report URL."); - return URI.create(String.format(Options.ISSUES_URL, "", "")); - } - } -} diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java deleted file mode 100644 index b2c6d410..00000000 --- a/src/itdelatrisu/opsu/Opsu.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * opsu! - an open-source osu! client - * Copyright (C) 2014, 2015 Jeffrey Han - * - * opsu! 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! 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!. If not, see . - */ - -package itdelatrisu.opsu; - -import itdelatrisu.opsu.audio.MusicController; -import itdelatrisu.opsu.db.DBController; -import itdelatrisu.opsu.downloads.DownloadList; -import itdelatrisu.opsu.downloads.Updater; -import itdelatrisu.opsu.states.ButtonMenu; -import itdelatrisu.opsu.states.DownloadsMenu; -import itdelatrisu.opsu.states.Game; -import itdelatrisu.opsu.states.GamePauseMenu; -import itdelatrisu.opsu.states.GameRanking; -import itdelatrisu.opsu.states.MainMenu; -import itdelatrisu.opsu.states.OptionsMenu; -import itdelatrisu.opsu.states.SongMenu; -import itdelatrisu.opsu.states.Splash; -import itdelatrisu.opsu.ui.UI; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.Field; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.UnknownHostException; - -import org.newdawn.slick.GameContainer; -import org.newdawn.slick.Input; -import org.newdawn.slick.SlickException; -import org.newdawn.slick.state.StateBasedGame; -import org.newdawn.slick.state.transition.FadeInTransition; -import org.newdawn.slick.state.transition.EasedFadeOutTransition; -import org.newdawn.slick.util.DefaultLogSystem; -import org.newdawn.slick.util.FileSystemLocation; -import org.newdawn.slick.util.Log; -import org.newdawn.slick.util.ResourceLoader; - -/** - * Main class. - *

- * Creates game container, adds all other states, and initializes song data. - */ -public class Opsu extends StateBasedGame { - /** Game states. */ - public static final int - STATE_SPLASH = 0, - STATE_MAINMENU = 1, - STATE_BUTTONMENU = 2, - STATE_SONGMENU = 3, - STATE_GAME = 4, - STATE_GAMEPAUSEMENU = 5, - STATE_GAMERANKING = 6, - STATE_OPTIONSMENU = 7, - STATE_DOWNLOADSMENU = 8; - - /** Server socket for restricting the program to a single instance. */ - private static ServerSocket SERVER_SOCKET; - - /** - * Constructor. - * @param name the program name - */ - public Opsu(String name) { - super(name); - } - - @Override - public void initStatesList(GameContainer container) throws SlickException { - //addState(new Splash(STATE_SPLASH)); - //addState(new MainMenu(STATE_MAINMENU)); - //addState(new ButtonMenu(STATE_BUTTONMENU)); - //addState(new SongMenu(STATE_SONGMENU)); - //addState(new Game(STATE_GAME)); - //addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); - //addState(new GameRanking(STATE_GAMERANKING)); - //addState(new OptionsMenu(STATE_OPTIONSMENU)); - //addState(new DownloadsMenu(STATE_DOWNLOADSMENU)); - } - - /** - * Launches opsu!. - */ - public static void main(String[] args) { - // log all errors to a file - Log.setVerbose(false); - try { - DefaultLogSystem.out = new PrintStream(new FileOutputStream(Options.LOG_FILE, true)); - } catch (FileNotFoundException e) { - Log.error(e); - } - - // set default exception handler - Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - ErrorHandler.error("** Uncaught Exception! **", e, true); - System.exit(1); - } - }); - - // parse configuration file - Options.parseOptions(); - - // only allow a single instance - if (!Options.noSingleInstance()) { - try { - SERVER_SOCKET = new ServerSocket(Options.getPort(), 1, InetAddress.getLocalHost()); - } catch (UnknownHostException e) { - // shouldn't happen - } catch (IOException e) { - errorAndExit( - null, - String.format( - "opsu! could not be launched for one of these reasons:\n" + - "- An instance of opsu! is already running.\n" + - "- Another program is bound to port %d. " + - "You can change the port opsu! uses by editing the \"Port\" field in the configuration file.", - Options.getPort() - ), - false - ); - } - } - - // load natives - File nativeDir; - if (!Utils.isJarRunning() && ( - (nativeDir = new File("./target/natives/")).isDirectory() || - (nativeDir = new File("./build/natives/")).isDirectory())) - ; - else { - nativeDir = Options.NATIVE_DIR; - try { - new NativeLoader(nativeDir).loadNatives(); - } catch (IOException e) { - Log.error("Error loading natives.", e); - } - } - System.setProperty("org.lwjgl.librarypath", nativeDir.getAbsolutePath()); - System.setProperty("java.library.path", nativeDir.getAbsolutePath()); - try { - // Workaround for "java.library.path" property being read-only. - // http://stackoverflow.com/a/24988095 - Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths"); - fieldSysPath.setAccessible(true); - fieldSysPath.set(null, null); - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - Log.warn("Failed to set 'sys_paths' field.", e); - } - - // set the resource paths - ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/"))); - - // initialize databases - try { - DBController.init(); - } catch (UnsatisfiedLinkError e) { - errorAndExit(e, "The databases could not be initialized.", true); - } - - // check if just updated - if (args.length >= 2) - Updater.get().setUpdateInfo(args[0], args[1]); - - // check for updates - if (!Options.isUpdaterDisabled()) { - new Thread() { - @Override - public void run() { - try { - Updater.get().checkForUpdates(); - } catch (IOException e) { - Log.warn("Check for updates failed.", e); - } - } - }.start(); - } - - // disable jinput - Input.disableControllers(); - - // start the game - try { - // loop until force exit - while (true) { - Opsu opsu = new Opsu("opsu!"); - Container app = new Container(opsu); - - // basic game settings - //Options.setDisplayMode(app); - String[] icons = { "icon16.png", "icon32.png" }; - try { - app.setIcons(icons); - } catch (Exception e) { - Log.error("could not set icon"); - } - app.setForceExit(true); - - app.start(); - - // run update if available - if (Updater.get().getStatus() == Updater.Status.UPDATE_FINAL) { - close(); - Updater.get().runUpdate(); - break; - } - } - } catch (SlickException e) { - errorAndExit(e, "An error occurred while creating the game container.", true); - } - } - - @Override - public boolean closeRequested() { - int id = this.getCurrentStateID(); - - // intercept close requests in game-related states and return to song menu - if (id == STATE_GAME || id == STATE_GAMEPAUSEMENU || id == STATE_GAMERANKING) { - // start playing track at preview position - SongMenu songMenu = (SongMenu) this.getState(Opsu.STATE_SONGMENU); - if (id == STATE_GAMERANKING) { - GameData data = ((GameRanking) this.getState(Opsu.STATE_GAMERANKING)).getGameData(); - if (data != null && data.isGameplay()) - songMenu.resetTrackOnLoad(); - } else { - if (id == STATE_GAME) { - MusicController.pause(); - MusicController.setPitch(1.0f); - MusicController.resume(); - } else - songMenu.resetTrackOnLoad(); - } - - // reset game data - if (UI.getCursor().isBeatmapSkinned()) - UI.getCursor().reset(); - songMenu.resetGameDataOnLoad(); - - this.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition()); - return false; - } - - // show confirmation dialog if any downloads are active - if (DownloadList.get().hasActiveDownloads() && - UI.showExitConfirmation(DownloadList.EXIT_CONFIRMATION)) - return false; - if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING && - UI.showExitConfirmation(Updater.EXIT_CONFIRMATION)) - return false; - - return true; - } - - /** - * Closes all resources. - */ - public static void close() { - // close databases - DBController.closeConnections(); - - // cancel all downloads - DownloadList.get().cancelAllDownloads(); - - // close server socket - if (SERVER_SOCKET != null) { - try { - SERVER_SOCKET.close(); - } catch (IOException e) { - ErrorHandler.error("Failed to close server socket.", e, false); - } - } - } - - /** - * Throws an error and exits the application with the given message. - * @param e the exception that caused the crash - * @param message the message to display - * @param report whether to ask to report the error - */ - private static void errorAndExit(Throwable e, String message, boolean report) { - // JARs will not run properly inside directories containing '!' - // http://bugs.java.com/view_bug.do?bug_id=4523159 - if (Utils.isJarRunning() && Utils.getRunningDirectory() != null && - Utils.getRunningDirectory().getAbsolutePath().indexOf('!') != -1) - ErrorHandler.error("JARs cannot be run from some paths containing '!'. Please move or rename the file and try again.", null, false); - else - ErrorHandler.error(message, e, report); - System.exit(1); - } -} diff --git a/src/org/newdawn/slick/GameContainer.java b/src/org/newdawn/slick/GameContainer.java deleted file mode 100644 index 01c7604f..00000000 --- a/src/org/newdawn/slick/GameContainer.java +++ /dev/null @@ -1,907 +0,0 @@ -/* - * Copyright (c) 2013, Slick2D - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - Neither the name of the Slick2D nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package org.newdawn.slick; - -import java.io.IOException; -import java.util.Properties; - -import org.lwjgl.LWJGLException; -import org.lwjgl.Sys; -import org.lwjgl.input.Cursor; -import org.lwjgl.opengl.Display; -import org.lwjgl.opengl.Drawable; -import org.lwjgl.opengl.Pbuffer; -import org.lwjgl.opengl.PixelFormat; -import org.newdawn.slick.gui.GUIContext; -import org.newdawn.slick.openal.SoundStore; -import org.newdawn.slick.opengl.CursorLoader; -import org.newdawn.slick.opengl.ImageData; -import org.newdawn.slick.opengl.renderer.Renderer; -import org.newdawn.slick.opengl.renderer.SGL; -import org.newdawn.slick.util.Log; -import org.newdawn.slick.util.ResourceLoader; - -/** - * A generic game container that handles the game loop, fps recording and - * managing the input system - * - * @author kevin - */ -public abstract class GameContainer implements GUIContext { - /** The renderer to use for all GL operations */ - protected static SGL GL = Renderer.get(); - /** The shared drawable if any */ - protected static Drawable SHARED_DRAWABLE; - - /** The time the last frame was rendered */ - protected long lastFrame; - /** The last time the FPS recorded */ - protected long lastFPS; - /** The last recorded FPS */ - protected int recordedFPS; - /** The current count of FPS */ - protected int fps; - /** True if we're currently running the game loop */ - protected boolean running = true; - - /** The width of the display */ - protected int width; - /** The height of the display */ - protected int height; - /** The game being managed */ - protected Game game; - - /** The default font to use in the graphics context */ - private Font defaultFont; - /** The graphics context to be passed to the game */ - private Graphics graphics; - - /** The input system to pass to the game */ - protected Input input; - /** The FPS we want to lock to */ - protected int targetFPS = -1; - /** True if we should show the fps */ - private boolean showFPS = true; - /** The minimum logic update interval */ - protected long minimumLogicInterval = 1; - /** The stored delta */ - protected long storedDelta; - /** The maximum logic update interval */ - protected long maximumLogicInterval = 0; - /** The last game started */ - protected Game lastGame; - /** True if we should clear the screen each frame */ - protected boolean clearEachFrame = true; - - /** True if the game is paused */ - protected boolean paused; - /** True if we should force exit */ - protected boolean forceExit = true; - /** True if vsync has been requested */ - protected boolean vsync; - /** Smoothed deltas requested */ - protected boolean smoothDeltas; - /** The number of samples we'll attempt through hardware */ - protected int samples; - - /** True if this context supports multisample */ - protected boolean supportsMultiSample; - - /** True if we should render when not focused */ - protected boolean alwaysRender; - /** True if we require stencil bits */ - protected static boolean stencil; - - /** - * Create a new game container wrapping a given game - * - * @param game The game to be wrapped - */ - protected GameContainer(Game game) { - this.game = game; - lastFrame = getTime(); - - getBuildVersion(); - Log.checkVerboseLogSetting(); - } - - public static void enableStencil() { - stencil = true; - } - - /** - * Set the default font that will be intialised in the graphics held in this container - * - * @param font The font to use as default - */ - public void setDefaultFont(Font font) { - if (font != null) { - this.defaultFont = font; - } else { - Log.warn("Please provide a non null font"); - } - } - - /** - * Indicate whether we want to try to use fullscreen multisampling. This will - * give antialiasing across the whole scene using a hardware feature. - * - * @param samples The number of samples to attempt (2 is safe) - */ - public void setMultiSample(int samples) { - this.samples = samples; - } - - /** - * Check if this hardware can support multi-sampling - * - * @return True if the hardware supports multi-sampling - */ - public boolean supportsMultiSample() { - return supportsMultiSample; - } - - /** - * The number of samples we're attempting to performing using - * hardware multisampling - * - * @return The number of samples requested - */ - public int getSamples() { - return samples; - } - - /** - * Indicate if we should force exitting the VM at the end - * of the game (default = true) - * - * @param forceExit True if we should force the VM exit - */ - public void setForceExit(boolean forceExit) { - this.forceExit = forceExit; - } - - /** - * Indicate if we want to smooth deltas. This feature will report - * a delta based on the FPS not the time passed. This works well with - * vsync. - * - * @param smoothDeltas True if we should report smooth deltas - */ - public void setSmoothDeltas(boolean smoothDeltas) { - this.smoothDeltas = smoothDeltas; - } - - /** - * Check if the display is in fullscreen mode - * - * @return True if the display is in fullscreen mode - */ - public boolean isFullscreen() { - return false; - } - - /** - * Get the aspect ratio of the screen - * - * @return The aspect ratio of the display - */ - public float getAspectRatio() { - return getWidth() / getHeight(); - } - - /** - * Indicate whether we want to be in fullscreen mode. Note that the current - * display mode must be valid as a fullscreen mode for this to work - * - * @param fullscreen True if we want to be in fullscreen mode - * @throws SlickException Indicates we failed to change the display mode - */ - public void setFullscreen(boolean fullscreen) throws SlickException { - } - - /** - * Enable shared OpenGL context. After calling this all containers created - * will shared a single parent context - * - * @throws SlickException Indicates a failure to create the shared drawable - */ - public static void enableSharedContext() throws SlickException { - try { - SHARED_DRAWABLE = new Pbuffer(64, 64, new PixelFormat(8, 0, 0), null); - } catch (LWJGLException e) { - throw new SlickException("Unable to create the pbuffer used for shard context, buffers not supported", e); - } - } - - /** - * Get the context shared by all containers - * - * @return The context shared by all the containers or null if shared context isn't enabled - */ - public static Drawable getSharedContext() { - return SHARED_DRAWABLE; - } - - /** - * Indicate if we should clear the screen at the beginning of each frame. If you're - * rendering to the whole screen each frame then setting this to false can give - * some performance improvements - * - * @param clear True if the the screen should be cleared each frame - */ - public void setClearEachFrame(boolean clear) { - this.clearEachFrame = clear; - } - - /** - * Renitialise the game and the context in which it's being rendered - * - * @throws SlickException Indicates a failure rerun initialisation routines - */ - public void reinit() throws SlickException { - } - - /** - * Pause the game - i.e. suspend updates - */ - public void pause() - { - setPaused(true); - } - - /** - * Resumt the game - i.e. continue updates - */ - public void resume() - { - setPaused(false); - } - - /** - * Check if the container is currently paused. - * - * @return True if the container is paused - */ - public boolean isPaused() { - return paused; - } - - /** - * Indicates if the game should be paused, i.e. if updates - * should be propogated through to the game. - * - * @param paused True if the game should be paused - */ - public void setPaused(boolean paused) - { - this.paused = paused; - } - - /** - * True if this container should render when it has focus - * - * @return True if this container should render when it has focus - */ - public boolean getAlwaysRender () { - return alwaysRender; - } - - /** - * Indicate whether we want this container to render when it has focus - * - * @param alwaysRender True if this container should render when it has focus - */ - public void setAlwaysRender (boolean alwaysRender) { - this.alwaysRender = alwaysRender; - } - - /** - * Get the build number of slick - * - * @return The build number of slick - */ - public static int getBuildVersion() { - try { - Properties props = new Properties(); - props.load(ResourceLoader.getResourceAsStream("version")); - - int build = Integer.parseInt(props.getProperty("build")); - Log.info("Slick Build #"+build); - - return build; - } catch (Exception e) { - Log.info("Unable to determine Slick build number"); - return -1; - } - } - - /** - * Get the default system font - * - * @return The default system font - */ - @Override - public Font getDefaultFont() { - return defaultFont; - } - - /** - * Check if sound effects are enabled - * - * @return True if sound effects are enabled - */ - public boolean isSoundOn() { - return SoundStore.get().soundsOn(); - } - - /** - * Check if music is enabled - * - * @return True if music is enabled - */ - public boolean isMusicOn() { - return SoundStore.get().musicOn(); - } - - /** - * Indicate whether music should be enabled - * - * @param on True if music should be enabled - */ - public void setMusicOn(boolean on) { - SoundStore.get().setMusicOn(on); - } - - /** - * Indicate whether sound effects should be enabled - * - * @param on True if sound effects should be enabled - */ - public void setSoundOn(boolean on) { - SoundStore.get().setSoundsOn(on); - } - - /** - * Retrieve the current default volume for music - * @return the current default volume for music - */ - public float getMusicVolume() { - return SoundStore.get().getMusicVolume(); - } - - /** - * Retrieve the current default volume for sound fx - * @return the current default volume for sound fx - */ - public float getSoundVolume() { - return SoundStore.get().getSoundVolume(); - } - - /** - * Set the default volume for sound fx - * @param volume the new default value for sound fx volume - */ - public void setSoundVolume(float volume) { - SoundStore.get().setSoundVolume(volume); - } - - /** - * Set the default volume for music - * @param volume the new default value for music volume - */ - public void setMusicVolume(float volume) { - SoundStore.get().setMusicVolume(volume); - } - - /** - * Get the width of the standard screen resolution - * - * @return The screen width - */ - @Override - public abstract int getScreenWidth(); - - /** - * Get the height of the standard screen resolution - * - * @return The screen height - */ - @Override - public abstract int getScreenHeight(); - - /** - * Get the width of the game canvas - * - * @return The width of the game canvas - */ - @Override - public int getWidth() { - return width; - } - - /** - * Get the height of the game canvas - * - * @return The height of the game canvas - */ - @Override - public int getHeight() { - return height; - } - - /** - * Set the icon to be displayed if possible in this type of - * container - * - * @param ref The reference to the icon to be displayed - * @throws SlickException Indicates a failure to load the icon - */ - public abstract void setIcon(String ref) throws SlickException; - - /** - * Set the icons to be used for this application. Note that the size of the icon - * defines how it will be used. Important ones to note - * - * Windows window icon must be 16x16 - * Windows alt-tab icon must be 24x24 or 32x32 depending on Windows version (XP=32) - * - * @param refs The reference to the icon to be displayed - * @throws SlickException Indicates a failure to load the icon - */ - public abstract void setIcons(String[] refs) throws SlickException; - - /** - * Get the accurate system time - * - * @return The system time in milliseconds - */ - @Override - public long getTime() { - return (Sys.getTime() * 1000) / Sys.getTimerResolution(); - } - - /** - * Sleep for a given period - * - * @param milliseconds The period to sleep for in milliseconds - */ - public void sleep(int milliseconds) { - long target = getTime()+milliseconds; - while (getTime() < target) { - try { Thread.sleep(1); } catch (Exception e) {} - } - } - - /** - * Set the mouse cursor to be displayed - this is a hardware cursor and hence - * shouldn't have any impact on FPS. - * - * @param ref The location of the image to be loaded for the cursor - * @param hotSpotX The x coordinate of the hotspot within the cursor image - * @param hotSpotY The y coordinate of the hotspot within the cursor image - * @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor - */ - @Override - public abstract void setMouseCursor(String ref, int hotSpotX, int hotSpotY) throws SlickException; - - /** - * Set the mouse cursor to be displayed - this is a hardware cursor and hence - * shouldn't have any impact on FPS. - * - * @param data The image data from which the cursor can be construted - * @param hotSpotX The x coordinate of the hotspot within the cursor image - * @param hotSpotY The y coordinate of the hotspot within the cursor image - * @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor - */ - @Override - public abstract void setMouseCursor(ImageData data, int hotSpotX, int hotSpotY) throws SlickException; - - /** - * Set the mouse cursor based on the contents of the image. Note that this will not take - * account of render state type changes to images (rotation and such). If these effects - * are required it is recommended that an offscreen buffer be used to produce an appropriate - * image. An offscreen buffer will always be used to produce the new cursor and as such - * this operation an be very expensive - * - * @param image The image to use as the cursor - * @param hotSpotX The x coordinate of the hotspot within the cursor image - * @param hotSpotY The y coordinate of the hotspot within the cursor image - * @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor - */ - public abstract void setMouseCursor(Image image, int hotSpotX, int hotSpotY) throws SlickException; - - /** - * Set the mouse cursor to be displayed - this is a hardware cursor and hence - * shouldn't have any impact on FPS. - * - * @param cursor The cursor to use - * @param hotSpotX The x coordinate of the hotspot within the cursor image - * @param hotSpotY The y coordinate of the hotspot within the cursor image - * @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor - */ - @Override - public abstract void setMouseCursor(Cursor cursor, int hotSpotX, int hotSpotY) throws SlickException; - - /** - * Get a cursor based on a image reference on the classpath. The image - * is assumed to be a set/strip of cursor animation frames running from top to - * bottom. - * - * @param ref The reference to the image to be loaded - * @param x The x-coordinate of the cursor hotspot (left {@literal ->} right) - * @param y The y-coordinate of the cursor hotspot (bottom {@literal ->} top) - * @param width The x width of the cursor - * @param height The y height of the cursor - * @param cursorDelays image delays between changing frames in animation - * - * @throws SlickException Indicates a failure to load the image or a failure to create the hardware cursor - */ - public void setAnimatedMouseCursor(String ref, int x, int y, int width, int height, int[] cursorDelays) throws SlickException - { - try { - Cursor cursor; - cursor = CursorLoader.get().getAnimatedCursor(ref, x, y, width, height, cursorDelays); - setMouseCursor(cursor, x, y); - } catch (IOException e) { - throw new SlickException("Failed to set mouse cursor", e); - } catch (LWJGLException e) { - throw new SlickException("Failed to set mouse cursor", e); - } - } - - /** - * Set the default mouse cursor - i.e. the original cursor before any native - * cursor was set - */ - @Override - public abstract void setDefaultMouseCursor(); - - /** - * Get the input system - * - * @return The input system available to this game container - */ - @Override - public Input getInput() { - return input; - } - - /** - * Get the current recorded FPS (frames per second) - * - * @return The current FPS - */ - public int getFPS() { - return recordedFPS; - } - - /** - * Indicate whether mouse cursor should be grabbed or not - * - * @param grabbed True if mouse cursor should be grabbed - */ - public abstract void setMouseGrabbed(boolean grabbed); - - /** - * Check if the mouse cursor is current grabbed. This will cause it not - * to be seen. - * - * @return True if the mouse is currently grabbed - */ - public abstract boolean isMouseGrabbed(); - - /** - * Retrieve the time taken to render the last frame, i.e. the change in time - delta. - * - * @return The time taken to render the last frame - */ - protected int getDelta() { - long time = getTime(); - int delta = (int) (time - lastFrame); - lastFrame = time; - - return delta; - } - - /** - * Updated the FPS counter - */ - protected void updateFPS() { - if (getTime() - lastFPS > 1000) { - lastFPS = getTime(); - recordedFPS = fps; - fps = 0; - } - fps++; - } - - /** - * Set the minimum amount of time in milliseonds that has to - * pass before update() is called on the container game. This gives - * a way to limit logic updates compared to renders. - * - * @param interval The minimum interval between logic updates - */ - public void setMinimumLogicUpdateInterval(int interval) { - minimumLogicInterval = interval; - } - - /** - * Set the maximum amount of time in milliseconds that can passed - * into the update method. Useful for collision detection without - * sweeping. - * - * @param interval The maximum interval between logic updates - */ - public void setMaximumLogicUpdateInterval(int interval) { - maximumLogicInterval = interval; - } - - /** - * Update and render the game - * - * @param delta The change in time since last update and render - * @throws SlickException Indicates an internal fault to the game. - */ - protected void updateAndRender(int delta) throws SlickException { - if (smoothDeltas) { - if (getFPS() != 0) { - delta = 1000 / getFPS(); - } - } - - input.poll(width, height); - - Music.poll(delta); - if (!paused) { - storedDelta += delta; - - if (storedDelta >= minimumLogicInterval) { - try { - if (maximumLogicInterval != 0) { - long cycles = storedDelta / maximumLogicInterval; - for (int i=0;i minimumLogicInterval) { - game.update(this, (int) (remainder % maximumLogicInterval)); - storedDelta = 0; - } else { - storedDelta = remainder; - } - } else { - game.update(this, (int) storedDelta); - storedDelta = 0; - } - - } catch (Throwable e) { -// Log.error(e); - throw new SlickException("Game.update() failure.", e); - } - } - } else { - game.update(this, 0); - } - - if (hasFocus() || getAlwaysRender()) { - if (clearEachFrame) { - GL.glClear(SGL.GL_COLOR_BUFFER_BIT | SGL.GL_DEPTH_BUFFER_BIT); - } - - GL.glLoadIdentity(); - - graphics.resetTransform(); - graphics.resetFont(); - graphics.resetLineWidth(); - graphics.setAntiAlias(false); - try { - game.render(this, graphics); - } catch (Throwable e) { -// Log.error(e); - throw new SlickException("Game.render() failure.", e); - } - graphics.resetTransform(); - - if (showFPS) { - defaultFont.drawString(10, 10, "FPS: "+recordedFPS); - } - - GL.flush(); - } - - if (targetFPS != -1) { - Display.sync(targetFPS); - } - } - - /** - * Indicate if the display should update only when the game is visible - * (the default is true) - * - * @param updateOnlyWhenVisible True if we should updated only when the display is visible - */ - public void setUpdateOnlyWhenVisible(boolean updateOnlyWhenVisible) { - } - - /** - * Check if this game is only updating when visible to the user (default = true) - * - * @return True if the game is only updated when the display is visible - */ - public boolean isUpdatingOnlyWhenVisible() { - return true; - } - - /** - * Initialise the GL context - */ - protected void initGL() { - Log.info("Starting display "+width+"x"+height); - GL.initDisplay(width, height); - - if (input == null) { - input = new Input(height); - } - input.init(height); - // no need to remove listeners? - //input.removeAllListeners(); - if (game instanceof InputListener) { - input.removeListener((InputListener) game); - input.addListener((InputListener) game); - } - - if (graphics != null) { - graphics.setDimensions(getWidth(), getHeight()); - } - lastGame = game; - } - - /** - * Initialise the system components, OpenGL and OpenAL. - * - * @throws SlickException Indicates a failure to create a native handler - */ - protected void initSystem() throws SlickException { - initGL(); - setMusicVolume(1.0f); - setSoundVolume(1.0f); - - graphics = new Graphics(width, height); - defaultFont = graphics.getFont(); - } - - /** - * Enter the orthographic mode - */ - protected void enterOrtho() { - enterOrtho(width, height); - } - - /** - * Indicate whether the container should show the FPS - * - * @param show True if the container should show the FPS - */ - public void setShowFPS(boolean show) { - showFPS = show; - } - - /** - * Check if the FPS is currently showing - * - * @return True if the FPS is showing - */ - public boolean isShowingFPS() { - return showFPS; - } - - /** - * Set the target fps we're hoping to get - * - * @param fps The target fps we're hoping to get - */ - public void setTargetFrameRate(int fps) { - targetFPS = fps; - } - - /** - * Indicate whether the display should be synced to the - * vertical refresh (stops tearing) - * - * @param vsync True if we want to sync to vertical refresh - */ - public void setVSync(boolean vsync) { - this.vsync = vsync; - Display.setVSyncEnabled(vsync); - } - - /** - * True if vsync is requested - * - * @return True if vsync is requested - */ - public boolean isVSyncRequested() { - return vsync; - } - - /** - * True if the game is running - * - * @return True if the game is running - */ - protected boolean running() { - return running; - } - - /** - * Inidcate we want verbose logging - * - * @param verbose True if we want verbose logging (INFO and DEBUG) - */ - public void setVerbose(boolean verbose) { - Log.setVerbose(verbose); - } - - /** - * Cause the game to exit and shutdown cleanly - */ - public void exit() { - running = false; - } - - /** - * Check if the game currently has focus - * - * @return True if the game currently has focus - */ - public abstract boolean hasFocus(); - - /** - * Get the graphics context used by this container. Note that this - * value may vary over the life time of the game. - * - * @return The graphics context used by this container - */ - public Graphics getGraphics() { - return graphics; - } - - /** - * Enter the orthographic mode - * - * @param xsize The size of the panel being used - * @param ysize The size of the panel being used - */ - protected void enterOrtho(int xsize, int ysize) { - GL.enterOrtho(xsize, ysize); - } -} From 978a41116c7a10edf267e4e7d6c525f19bbf5fb2 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sat, 21 Jan 2017 13:11:34 +0100 Subject: [PATCH 89/92] enable cursor --- src/itdelatrisu/opsu/Options.java | 7 +- src/itdelatrisu/opsu/states/Game.java | 130 ++++++++++-------- .../opsu/states/GamePauseMenu.java | 8 +- src/itdelatrisu/opsu/states/GameRanking.java | 5 +- src/itdelatrisu/opsu/ui/Cursor.java | 99 ++----------- src/itdelatrisu/opsu/ui/UI.java | 35 ----- .../opsudance/core/DisplayContainer.java | 22 +++ .../core/inject/OpsuDanceInjector.java | 2 +- src/yugecin/opsudance/utils/GLHelper.java | 23 ++++ 9 files changed, 142 insertions(+), 189 deletions(-) diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index e095c5d2..553c54cb 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -467,12 +467,7 @@ public class Options { val = i; } }, - NEW_CURSOR ("Enable New Cursor", "NewCursor", "Use the new cursor style (may cause higher CPU usage).", true) { - @Override - public void click() { - UI.getCursor().reset(); - } - }, + NEW_CURSOR ("Enable New Cursor", "NewCursor", "Use the new cursor style (may cause higher CPU usage).", true), DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "DynamicBackground", "The song background will be used as the main menu background.", true), LOAD_VERBOSE ("Show Detailed Loading Progress", "LoadVerbose", "Display more specific loading information in the splash screen.", false), COLOR_MAIN_MENU_LOGO ("Use cursor color as main menu logo tint", "ColorMainMenuLogo", "Colorful main menu logo", false), diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index 53200135..1cfc90c8 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -73,6 +73,7 @@ import yugecin.opsudance.objects.curves.FakeCombinedCurve; import yugecin.opsudance.sbv2.MoveStoryboard; import yugecin.opsudance.ui.OptionsOverlay; import yugecin.opsudance.ui.StoryboardOverlay; +import yugecin.opsudance.utils.GLHelper; /** * "Game" state. @@ -449,38 +450,6 @@ public class Game extends ComplexOpsuState { if (GameMod.FLASHLIGHT.isActive()) Graphics.setCurrent(g); - // "auto" and "autopilot" mods: move cursor automatically - // TODO: this should really be in update(), not render() - autoMousePosition.set(width / 2, height / 2); - autoMousePressed = false; - if (GameMod.AUTO.isActive() || GameMod.AUTOPILOT.isActive()) { - Vec2f autoPoint; - if (objectIndex < beatmap.objects.length - Dancer.instance.getPolyMoverFactoryMinBufferSize()) { - Dancer d = Dancer.instance; - d.update(trackPosition, objectIndex); - autoPoint = new Vec2f(d.x, d.y); - if (trackPosition < gameObjects[objectIndex].getTime()) { - autoMousePressed = true; - } - } else { - if (objectIndex < beatmap.objects.length) { - autoPoint = gameObjects[objectIndex].getPointAt(trackPosition); - } else { - // last object - autoPoint = gameObjects[objectIndex - 1].getPointAt(trackPosition); - } - } - - float[] sbPosition = moveStoryboardOverlay.getPoint(trackPosition); - if (sbPosition != null) { - autoPoint.x = sbPosition[0]; - autoPoint.y = sbPosition[1]; - } - - // set mouse coordinates - autoMousePosition.set(autoPoint.x, autoPoint.y); - } - // "flashlight" mod: restricted view of hit objects around cursor if (GameMod.FLASHLIGHT.isActive()) { // render hit objects offscreen @@ -748,22 +717,18 @@ public class Game extends ComplexOpsuState { cursorCirclePulse.drawCentered(pausedMousePosition.x, pausedMousePosition.y); } - if (isReplay) - UI.draw(g, replayX, replayY, replayKeyPressed); - else if (GameMod.AUTO.isActive()) { - UI.draw(g, (int) autoMousePosition.x, (int) autoMousePosition.y, autoMousePressed); + if (isReplay) { + displayContainer.cursor.draw(replayKeyPressed); + } else if (GameMod.AUTO.isActive()) { + displayContainer.cursor.draw(autoMousePressed); if (Options.isMirror() && GameMod.AUTO.isActive()) { - double dx = autoMousePosition.x - Options.width / 2d; - double dy = autoMousePosition.y - Options.height / 2d; - double d = Math.sqrt(dx * dx + dy * dy); - double a = Math.atan2(dy, dx) + Math.PI; - mirrorCursor.draw((int) (Math.cos(a) * d + Options.width / 2), (int) (Math.sin(a) * d + Options.height / 2), autoMousePressed); + mirrorCursor.draw(autoMousePressed); } + } else if (GameMod.AUTOPILOT.isActive()) { + displayContainer.cursor.draw(Utils.isGameKeyPressed()); } - else if (GameMod.AUTOPILOT.isActive()) - UI.draw(g, (int) autoMousePosition.x, (int) autoMousePosition.y, Utils.isGameKeyPressed()); - else - UI.draw(g); + + UI.draw(g); if (!Options.isHideWM()) { Fonts.SMALL.drawString(0.3f, 0.3f, "opsu!dance " + Updater.get().getCurrentVersion() + " by robin_be | https://github.com/yugecin/opsu-dance"); @@ -926,6 +891,58 @@ public class Game extends ComplexOpsuState { } } + @Override + public void update() { + super.update(); + + int trackPosition = MusicController.getPosition(); + + // "auto" and "autopilot" mods: move cursor automatically + autoMousePressed = false; + if (GameMod.AUTO.isActive() || GameMod.AUTOPILOT.isActive()) { + Vec2f autoPoint; + if (objectIndex < beatmap.objects.length - Dancer.instance.getPolyMoverFactoryMinBufferSize()) { + Dancer d = Dancer.instance; + d.update(trackPosition, objectIndex); + autoPoint = new Vec2f(d.x, d.y); + if (trackPosition < gameObjects[objectIndex].getTime()) { + autoMousePressed = true; + } + } else { + if (objectIndex < beatmap.objects.length) { + autoPoint = gameObjects[objectIndex].getPointAt(trackPosition); + } else { + // last object + autoPoint = gameObjects[objectIndex - 1].getPointAt(trackPosition); + } + } + + float[] sbPosition = moveStoryboardOverlay.getPoint(trackPosition); + if (sbPosition != null) { + autoPoint.x = sbPosition[0]; + autoPoint.y = sbPosition[1]; + } + + // set mouse coordinates + autoMousePosition.set(autoPoint.x, autoPoint.y); + } + + if (isReplay) { + displayContainer.cursor.setCursorPosition(displayContainer.delta, replayX, replayY); + } else if (GameMod.AUTO.isActive()) { + displayContainer.cursor.setCursorPosition(displayContainer.delta, (int) autoMousePosition.x, (int) autoMousePosition.y); + if (Options.isMirror() && GameMod.AUTO.isActive()) { + double dx = autoMousePosition.x - Options.width / 2d; + double dy = autoMousePosition.y - Options.height / 2d; + double d = Math.sqrt(dx * dx + dy * dy); + double a = Math.atan2(dy, dx) + Math.PI; + mirrorCursor.setCursorPosition(displayContainer.delta, (int) (Math.cos(a) * d + Options.width / 2), (int) (Math.sin(a) * d + Options.height / 2)); + } + } else if (GameMod.AUTOPILOT.isActive()) { + displayContainer.cursor.setCursorPosition(displayContainer.delta, (int) autoMousePosition.x, (int) autoMousePosition.y); + } + } + /** * Updates the game. * @param mouseX the mouse x coordinate @@ -1078,11 +1095,6 @@ public class Game extends ComplexOpsuState { boolean overlap = (objectIndex + 1 < gameObjects.length && trackPosition > beatmap.objects[objectIndex + 1].getTime() - hitResultOffset[GameData.HIT_50]); - if (skippedObject && (GameMod.AUTO.isActive() || GameMod.AUTOPILOT.isActive())) { - Vec2f start = gameObjects[objectIndex - 1].start; - UI.getCursor().setCursorPosition((int) start.x, (int) start.y); - } - // update hit object and check completion status if (gameObjects[objectIndex].update(overlap, delta, mouseX, mouseY, keyPressed, trackPosition)) { skippedObject = true; @@ -1210,7 +1222,7 @@ public class Game extends ComplexOpsuState { mirrorTo = objectIndex; Options.setMirror(false); } else { - mirrorCursor.resetLocations(); + mirrorCursor.resetLocations((int) autoMousePosition.x, (int) autoMousePosition.y); mirrorFrom = objectIndex; mirrorTo = gameObjects.length; Options.setMirror(true); @@ -1221,7 +1233,7 @@ public class Game extends ComplexOpsuState { mirrorTo = objectIndex; Options.setMirror(false); } else { - mirrorCursor.resetLocations(); + mirrorCursor.resetLocations((int) autoMousePosition.x, (int) autoMousePosition.y); mirrorFrom = objectIndex; mirrorTo = mirrorFrom + 1; Options.setMirror(true); @@ -1435,6 +1447,8 @@ public class Game extends ComplexOpsuState { public void enter() { super.enter(); + displayContainer.drawCursor = false; + overlays.clear(); if (Options.isEnableSB()) { overlays.add(optionsOverlay); @@ -1572,8 +1586,9 @@ public class Game extends ComplexOpsuState { } // unhide cursor for "auto" mod and replays - if (GameMod.AUTO.isActive() || isReplay) - UI.getCursor().show(); + if (GameMod.AUTO.isActive() || isReplay) { + GLHelper.showNativeCursor(); + } // load replay frames if (isReplay) { @@ -1683,6 +1698,8 @@ public class Game extends ComplexOpsuState { public void leave() { super.leave(); + displayContainer.drawCursor = true; + MusicController.pause(); MusicController.setPitch(1f); MusicController.resume(); @@ -1705,8 +1722,9 @@ public class Game extends ComplexOpsuState { Cursor.nextMirroredObjColor = Color.white; // re-hide cursor - if (GameMod.AUTO.isActive() || isReplay) - UI.getCursor().hide(); + if (GameMod.AUTO.isActive() || isReplay) { + GLHelper.hideNativeCursor(); + } // replays if (isReplay) diff --git a/src/itdelatrisu/opsu/states/GamePauseMenu.java b/src/itdelatrisu/opsu/states/GamePauseMenu.java index 58d83ef5..642c958e 100644 --- a/src/itdelatrisu/opsu/states/GamePauseMenu.java +++ b/src/itdelatrisu/opsu/states/GamePauseMenu.java @@ -110,9 +110,6 @@ public class GamePauseMenu extends BaseOpsuState { SoundController.playSound(SoundEffect.MENUBACK); instanceContainer.provide(SongMenu.class).resetGameDataOnLoad(); MusicController.playAt(MusicController.getBeatmap().previewTime, true); - if (UI.getCursor().isBeatmapSkinned()) { - UI.getCursor().reset(); - } displayContainer.switchState(SongMenu.class); } else { SoundController.playSound(SoundEffect.MENUBACK); @@ -157,8 +154,9 @@ public class GamePauseMenu extends BaseOpsuState { MusicController.playAt(MusicController.getBeatmap().previewTime, true); else MusicController.resume(); - if (UI.getCursor().isBeatmapSkinned()) - UI.getCursor().reset(); + if (displayContainer.cursor.isBeatmapSkinned()) { + displayContainer.resetCursor(); + } MusicController.setPitch(1.0f); displayContainer.switchState(SongMenu.class); } diff --git a/src/itdelatrisu/opsu/states/GameRanking.java b/src/itdelatrisu/opsu/states/GameRanking.java index fdec21fc..300d6dee 100644 --- a/src/itdelatrisu/opsu/states/GameRanking.java +++ b/src/itdelatrisu/opsu/states/GameRanking.java @@ -63,6 +63,7 @@ public class GameRanking extends BaseOpsuState { public GameRanking(DisplayContainer displayContainer, InstanceContainer instanceContainer) { super(displayContainer); this.instanceContainer = instanceContainer; + } @Override @@ -242,8 +243,8 @@ public class GameRanking extends BaseOpsuState { songMenu.resetTrackOnLoad(); } songMenu.resetGameDataOnLoad(); - if (UI.getCursor().isBeatmapSkinned()) { - UI.getCursor().reset(); + if (displayContainer.cursor.isBeatmapSkinned()) { + displayContainer.resetCursor(); } displayContainer.switchState(SongMenu.class); } diff --git a/src/itdelatrisu/opsu/ui/Cursor.java b/src/itdelatrisu/opsu/ui/Cursor.java index 1b7894c3..f5ad23bf 100644 --- a/src/itdelatrisu/opsu/ui/Cursor.java +++ b/src/itdelatrisu/opsu/ui/Cursor.java @@ -21,28 +21,19 @@ package itdelatrisu.opsu.ui; import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils; -import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.skins.Skin; import itdelatrisu.opsu.ui.animations.AnimationEquation; import java.awt.Point; -import java.nio.IntBuffer; import java.util.LinkedList; -import org.lwjgl.BufferUtils; -import org.lwjgl.LWJGLException; import org.newdawn.slick.*; -import org.newdawn.slick.state.StateBasedGame; import yugecin.opsudance.Dancer; -import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.core.errorhandling.ErrorHandler; /** * Updates and draws the cursor. */ public class Cursor { - /** Empty cursor. */ - private static org.lwjgl.input.Cursor emptyCursor; /** Last cursor coordinates. */ private Point lastPosition; @@ -63,13 +54,10 @@ public class Cursor { private static final float CURSOR_SCALE_TIME = 125; /** Stores all previous cursor locations to display a trail. */ - private LinkedList trail = new LinkedList(); + private LinkedList trail = new LinkedList<>(); private boolean newStyle; - // game-related variables - private static DisplayContainer displayContainer; - public static Color lastObjColor = Color.white; public static Color lastMirroredObjColor = Color.white; public static Color nextObjColor = Color.white; @@ -78,22 +66,6 @@ public class Cursor { private boolean isMirrored; - /** - * Initializes the class. - */ - public static void init(DisplayContainer displayContainer) { - Cursor.displayContainer = displayContainer; - - // create empty cursor to simulate hiding the cursor - try { - int min = org.lwjgl.input.Cursor.getMinCursorSize(); - IntBuffer tmp = BufferUtils.createIntBuffer(min * min); - emptyCursor = new org.lwjgl.input.Cursor(min, min, min/2, min/2, 1, tmp, null); - } catch (LWJGLException e) { - ErrorHandler.error("Failed to create hidden cursor.", e).show(); - } - } - /** * Constructor. */ @@ -102,31 +74,15 @@ public class Cursor { } public Cursor(boolean isMirrored) { - resetLocations(); + resetLocations(0, 0); this.isMirrored = isMirrored; } /** * Draws the cursor. - */ - public void draw() { - /* - int state = game.getCurrentStateID(); - boolean mousePressed = - (((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && Utils.isGameKeyPressed()) || - ((input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) && - !(state == Opsu.STATE_GAME && Options.isMouseDisabled()))); - draw(input.getMouseX(), input.getMouseY(), mousePressed); - */ - } - - /** - * Draws the cursor. - * @param mouseX the mouse x coordinate - * @param mouseY the mouse y coordinate * @param mousePressed whether or not the mouse button is pressed */ - public void draw(int mouseX, int mouseY, boolean mousePressed) { + public void draw(boolean mousePressed) { if (Options.isCursorDisabled()) return; @@ -168,8 +124,6 @@ public class Cursor { cursorTrail = cursorTrail.getScaledCopy(cursorScale); } - setCursorPosition(mouseX, mouseY); - Color filter; if (isMirrored) { filter = Dancer.cursorColorMirrorOverride.getMirrorColor(); @@ -191,16 +145,16 @@ public class Cursor { cursorTrailWidth, cursorTrailHeight, cursorTrailRotation); } cursorTrail.drawEmbedded( - mouseX - (cursorTrailWidth / 2f), mouseY - (cursorTrailHeight / 2f), + lastPosition.x - (cursorTrailWidth / 2f), lastPosition.y - (cursorTrailHeight / 2f), cursorTrailWidth, cursorTrailHeight, cursorTrailRotation); cursorTrail.endUse(); // draw the other components if (newStyle && skin.isCursorRotated()) cursor.setRotation(cursorAngle); - cursor.drawCentered(mouseX, mouseY, Options.isCursorOnlyColorTrail() ? Color.white : filter); + cursor.drawCentered(lastPosition.x, lastPosition.y, Options.isCursorOnlyColorTrail() ? Color.white : filter); if (hasMiddle) - cursorMiddle.drawCentered(mouseX, mouseY, Options.isCursorOnlyColorTrail() ? Color.white : filter); + cursorMiddle.drawCentered(lastPosition.x, lastPosition.y, Options.isCursorOnlyColorTrail() ? Color.white : filter); } /** @@ -208,10 +162,10 @@ public class Cursor { * @param mouseX x coordinate to set position to * @param mouseY y coordinate to set position to */ - public void setCursorPosition(int mouseX, int mouseY) { + public void setCursorPosition(int delta, int mouseX, int mouseY) { // TODO: use an image buffer int removeCount = 0; - float FPSmod = Math.max(1000 / displayContainer.renderDelta, 1) / 30f; // TODO + float FPSmod = Math.max(1000 / Math.max(delta, 1), 1) / 30f; // TODO if (newStyle) { // new style: add all points between cursor movements if ((lastPosition.x == 0 && lastPosition.y == 0) || !addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY)) { @@ -297,7 +251,7 @@ public class Cursor { * If the old style cursor is being used, this will do nothing. * @param delta the delta interval since the last call */ - public void update(int delta) { + public void updateAngle(int delta) { cursorAngle += delta / 40f; cursorAngle %= 360; } @@ -305,14 +259,14 @@ public class Cursor { /** * Resets all cursor data and beatmap skins. */ - public void reset() { + public void reset(int mouseX, int mouseY) { // destroy skin images GameImage.CURSOR.destroyBeatmapSkinImage(); GameImage.CURSOR_MIDDLE.destroyBeatmapSkinImage(); GameImage.CURSOR_TRAIL.destroyBeatmapSkinImage(); // reset locations - resetLocations(); + resetLocations(mouseX, mouseY); // reset angles cursorAngle = 0f; @@ -321,14 +275,12 @@ public class Cursor { /** * Resets all cursor location data. */ - public void resetLocations() { + public void resetLocations(int mouseX, int mouseY) { trail.clear(); - if (lastPosition != null) { - for (int i = 0; i < 50; i++) { - trail.add(new Point(lastPosition)); - } + lastPosition = new Point(mouseX, mouseY); + for (int i = 0; i < 50; i++) { + trail.add(new Point(lastPosition)); } - lastPosition = new Point(0, 0); } /** @@ -340,25 +292,4 @@ public class Cursor { GameImage.CURSOR_TRAIL.hasBeatmapSkinImage()); } - /** - * Hides the cursor, if possible. - */ - public void hide() { - if (emptyCursor != null) { - /* - try { - container.setMouseCursor(emptyCursor, 0, 0); - } catch (SlickException e) { - ErrorHandler.error("Failed to hide the cursor.", e, true); - } - */ - } - } - - /** - * Unhides the cursor. - */ - public void show() { - //container.setDefaultMouseCursor(); - } } diff --git a/src/itdelatrisu/opsu/ui/UI.java b/src/itdelatrisu/opsu/ui/UI.java index 8c5d5a10..f2d7f65d 100644 --- a/src/itdelatrisu/opsu/ui/UI.java +++ b/src/itdelatrisu/opsu/ui/UI.java @@ -28,24 +28,16 @@ import itdelatrisu.opsu.replay.ReplayImporter; import itdelatrisu.opsu.ui.animations.AnimatedValue; import itdelatrisu.opsu.ui.animations.AnimationEquation; -import javax.swing.JOptionPane; -import javax.swing.UIManager; - import org.newdawn.slick.Animation; import org.newdawn.slick.Color; -import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; -import org.newdawn.slick.Input; -import org.newdawn.slick.state.StateBasedGame; import yugecin.opsudance.core.DisplayContainer; /** * Draws common UI components. */ public class UI { - /** Cursor. */ - private static Cursor cursor = new Cursor(); /** Back button. */ private static MenuButton backButton; @@ -86,10 +78,6 @@ public class UI { public static void init(DisplayContainer displayContainer) { UI.displayContainer = displayContainer; - // initialize cursor - Cursor.init(displayContainer); - cursor.hide(); - // back button if (GameImage.MENU_BACK.getImages() != null) { Animation back = GameImage.MENU_BACK.getAnimation(120); @@ -108,7 +96,6 @@ public class UI { * @param delta the delta interval since the last call. */ public static void update(int delta) { - cursor.update(delta); updateVolumeDisplay(delta); updateBarNotification(delta); tooltipAlpha.update(-delta); @@ -121,22 +108,6 @@ public class UI { public static void draw(Graphics g) { drawBarNotification(g); drawVolume(g); - cursor.draw(); - drawTooltip(g); - } - - /** - * Draws the global UI components: cursor, FPS, volume bar, tooltips, bar notifications. - * @param g the graphics context - * @param mouseX the mouse x coordinate - * @param mouseY the mouse y coordinate - * @param mousePressed whether or not the mouse button is pressed - */ - public static void draw(Graphics g, int mouseX, int mouseY, boolean mousePressed) { - drawBarNotification(g); - drawVolume(g); - cursor.draw(mouseX, mouseY, mousePressed); - drawTooltip(g); } /** @@ -144,16 +115,10 @@ public class UI { */ public static void enter() { backButton.resetHover(); - cursor.resetLocations(); resetBarNotification(); resetTooltip(); } - /** - * Returns the game cursor. - */ - public static Cursor getCursor() { return cursor; } - /** * Returns the 'menu-back' MenuButton. */ diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index 4e7f6308..35a47aba 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -25,7 +25,9 @@ import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.Updater; import itdelatrisu.opsu.render.CurveRenderState; +import itdelatrisu.opsu.ui.Cursor; import itdelatrisu.opsu.ui.Fonts; +import itdelatrisu.opsu.ui.UI; import org.lwjgl.Sys; import org.lwjgl.openal.AL; import org.lwjgl.opengl.Display; @@ -107,9 +109,14 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen private long exitconfirmation; + public final Cursor cursor; + public boolean drawCursor; + public DisplayContainer(InstanceContainer instanceContainer, EventBus eventBus) { this.instanceContainer = instanceContainer; this.eventBus = eventBus; + this.cursor = new Cursor(); + drawCursor = true; outTransitionListener = new TransitionFinishedListener() { @Override @@ -169,7 +176,11 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen Music.poll(delta); mouseX = input.getMouseX(); mouseY = input.getMouseY(); + state.update(); + if (drawCursor) { + cursor.setCursorPosition(delta, mouseX, mouseY); + } int maxRenderInterval; if (Display.isVisible() && Display.isActive()) { @@ -196,6 +207,12 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen bubNotifState.render(graphics); barNotifState.render(graphics); + cursor.updateAngle(renderDelta); + if (drawCursor) { + cursor.draw(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)); + } + UI.drawTooltip(graphics); + timeSinceLastRender = 0; Display.update(false); @@ -216,6 +233,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen initGL(); glVersion = GL11.glGetString(GL11.GL_VERSION); glVendor = GL11.glGetString(GL11.GL_VENDOR); + GLHelper.hideNativeCursor(); } public void teardown() { @@ -318,6 +336,10 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen eventBus.post(new ResolutionChangedEvent(this.width, this.height)); } + public void resetCursor() { + cursor.reset(mouseX, mouseY); + } + private int getDelta() { long time = getTime(); int delta = (int) (time - lastFrame); diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java index 39a4ce64..13f72a50 100644 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java @@ -57,8 +57,8 @@ public class OpsuDanceInjector extends Injector { bind(ButtonMenu.class).asEagerSingleton(); bind(SongMenu.class).asEagerSingleton(); bind(DownloadsMenu.class).asEagerSingleton(); - bind(GameRanking.class).asEagerSingleton(); bind(Game.class).asEagerSingleton(); + bind(GameRanking.class).asEagerSingleton(); bind(GamePauseMenu.class).asEagerSingleton(); } diff --git a/src/yugecin/opsudance/utils/GLHelper.java b/src/yugecin/opsudance/utils/GLHelper.java index b1d4a965..66db778b 100644 --- a/src/yugecin/opsudance/utils/GLHelper.java +++ b/src/yugecin/opsudance/utils/GLHelper.java @@ -17,7 +17,10 @@ */ package yugecin.opsudance.utils; +import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; +import org.lwjgl.input.Cursor; +import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; import org.newdawn.slick.opengl.ImageIOImageData; @@ -25,8 +28,10 @@ import org.newdawn.slick.opengl.LoadableImageData; import org.newdawn.slick.opengl.TGAImageData; import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; +import yugecin.opsudance.core.errorhandling.ErrorHandler; import java.nio.ByteBuffer; +import java.nio.IntBuffer; public class GLHelper { @@ -85,4 +90,22 @@ public class GLHelper { Display.setIcon(bufs); } + public static void hideNativeCursor() { + try { + int min = Cursor.getMinCursorSize(); + IntBuffer tmp = BufferUtils.createIntBuffer(min * min); + Mouse.setNativeCursor(new Cursor(min, min, min / 2, min / 2, 1, tmp, null)); + } catch (LWJGLException e) { + ErrorHandler.error("Cannot hide native cursor", e).show(); + } + } + + public static void showNativeCursor() { + try { + Mouse.setNativeCursor(null); + } catch (LWJGLException e) { + ErrorHandler.error("Cannot show native cursor", e).show(); + } + } + } From 4dc8b34557889109f349ac41818571658994e55b Mon Sep 17 00:00:00 2001 From: yugecin Date: Sat, 21 Jan 2017 13:17:22 +0100 Subject: [PATCH 90/92] fix not being able to enter O in the search bar in songmenu --- src/itdelatrisu/opsu/states/SongMenu.java | 35 +++++++++++------------ 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index d1b98ac6..f8743200 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -1171,28 +1171,25 @@ public class SongMenu extends ComplexOpsuState { case Input.KEY_PRIOR: changeIndex(-MAX_SONG_BUTTONS); return true; - case Input.KEY_O: - if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) { - optionsOverlay.show(); - } + } + if (key == Input.KEY_O && (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL))) { + optionsOverlay.show(); return true; - default: - // wait for user to finish typing - // TODO: accept all characters (current conditions are from TextField class) - if ((c > 31 && c < 127) || key == Input.KEY_BACK) { - searchTimer = 0; - searchTextField.keyPressed(key, c); - int textLength = searchTextField.getText().length(); - if (lastSearchTextLength != textLength) { - if (key == Input.KEY_BACK) { - if (textLength == 0) - searchTransitionTimer = 0; - } else if (textLength == 1) + } + // wait for user to finish typing + // TODO: accept all characters (current conditions are from TextField class) + if ((c > 31 && c < 127) || key == Input.KEY_BACK) { + searchTimer = 0; + searchTextField.keyPressed(key, c); + int textLength = searchTextField.getText().length(); + if (lastSearchTextLength != textLength) { + if (key == Input.KEY_BACK) { + if (textLength == 0) searchTransitionTimer = 0; - lastSearchTextLength = textLength; - } + } else if (textLength == 1) + searchTransitionTimer = 0; + lastSearchTextLength = textLength; } - return true; } return true; } From b2f85edb132ab432f54392027c4e68f3ae450517 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sat, 21 Jan 2017 13:29:10 +0100 Subject: [PATCH 91/92] fix nullpointer for mover storyboard --- src/itdelatrisu/opsu/states/Game.java | 10 ++++++---- src/yugecin/opsudance/sbv2/MoveStoryboard.java | 12 ++---------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index 1cfc90c8..bbabf670 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -333,6 +333,8 @@ public class Game extends ComplexOpsuState { @Override public void revalidate() { + super.revalidate(); + // create offscreen graphics try { offscreen = new Image(displayContainer.width, displayContainer.height); @@ -1445,10 +1447,6 @@ public class Game extends ComplexOpsuState { @Override public void enter() { - super.enter(); - - displayContainer.drawCursor = false; - overlays.clear(); if (Options.isEnableSB()) { overlays.add(optionsOverlay); @@ -1458,6 +1456,10 @@ public class Game extends ComplexOpsuState { optionsOverlay.revalidate(); } + super.enter(); + + displayContainer.drawCursor = false; + isInGame = true; if (!skippedToCheckpoint) { UI.enter(); diff --git a/src/yugecin/opsudance/sbv2/MoveStoryboard.java b/src/yugecin/opsudance/sbv2/MoveStoryboard.java index aa4e95c8..a7d1818d 100644 --- a/src/yugecin/opsudance/sbv2/MoveStoryboard.java +++ b/src/yugecin/opsudance/sbv2/MoveStoryboard.java @@ -69,6 +69,8 @@ public class MoveStoryboard extends OverlayOpsuState{ @Override public void revalidate() { + super.revalidate(); + btnAddLinear = new SimpleButton(displayContainer.width - 205, 50, 200, 25, Fonts.SMALL, "add linear", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); btnAddQuadratic = new SimpleButton(displayContainer.width - 205, 80, 200, 25, Fonts.SMALL, "add quadratic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); btnAddCubic = new SimpleButton(displayContainer.width - 205, 110, 200, 25, Fonts.SMALL, "add cubic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); @@ -94,16 +96,6 @@ public class MoveStoryboard extends OverlayOpsuState{ return moves[objectIndex].getPointAt(t); } - @Override - public void hide() { - - } - - @Override - public void show() { - - } - @Override protected void onPreRenderUpdate() { int x = displayContainer.mouseX; From 030f204e5437a65e5a389eb1695fda62d98ab761 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sat, 21 Jan 2017 13:30:10 +0100 Subject: [PATCH 92/92] reset cursor location on leaving game --- src/itdelatrisu/opsu/states/Game.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index bbabf670..2d2a6e42 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -1700,6 +1700,7 @@ public class Game extends ComplexOpsuState { public void leave() { super.leave(); + displayContainer.resetCursor(); displayContainer.drawCursor = true; MusicController.pause();