Merge branch 'master' into replaystuff

# Conflicts:
#	src/itdelatrisu/opsu/states/Game.java
This commit is contained in:
yugecin 2017-12-16 16:35:11 +01:00
commit 9320767f21
131 changed files with 2595 additions and 3598 deletions

4
.gitignore vendored
View File

@ -29,4 +29,8 @@
build/ build/
Thumbs.db Thumbs.db
.DS_STORE
/target /target
/out/
/lib/
/mvnlibs/

View File

@ -20,6 +20,7 @@ The images included in opsu! belong to their respective authors.
* sherrie__fay * sherrie__fay
* kouyang * kouyang
* teinecthel * teinecthel
* Font Awesome by Dave Gandy - http://fontawesome.io
Projects Projects
-------- --------
@ -31,6 +32,5 @@ The following projects were referenced in creating opsu!:
Theme Song Theme Song
---------- ----------
Rainbows - Kevin MacLeod (incompetech.com) The theme song is "On the Bach" by Jingle Punks, from the [YouTube Audio Library]
Licensed under Creative Commons: By Attribution 3.0 License (https://www.youtube.com/audiolibrary/music).
http://creativecommons.org/licenses/by/3.0/

View File

@ -1,23 +1,69 @@
# opsu!dance # opsu!dance
[example video](https://www.youtube.com/watch?v=tqZqn7nx8N0)
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. **Table of contents**
* [What](#What) - [Why](#why) - [Downloads](#downloads) - [Running](#running) - [Building a JAR](#building-a-jar) - [Credits](#credits) - [License](#license)
What
----
Fork of [opsu!](https://github.com/itdelatrisu/opsu), which is a clone of the [osu!](https://osu.ppy.sh/) rythm game.
* [example video: Cursor Dance | MOMOIRO CLOVER Z - SANTA SAN](https://youtu.be/tqZqn7nx8N0)
* [example video: osu! 50 top replays | Getty vs. DJ DiA - Fox4-Raize- [Extreme]](https://youtu.be/T2AiGn2xOQo)
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 get more control over the base system 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. 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 get more control over the base system 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. Why
---
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.
### Downloads My goal is to to add cool cursordancing things to this fork, but also make it possible to play the normal way. In the meantime I'm also adding various improvements to opsu! while I make a mess here, like the option menu, default back button, slider stuff, ...
Click on the releases link (scroll up) to go to the downloadpage with prebuilt jars.
### Building Downloads
You can find general (run/build) instructions in the original [opsu! README](README-OPSU.md). ---------
Please note that I am only using maven, gradle scripts are not being updated. You can find prebuilt jars on [the releases page](https://github.com/yugecin/opsu-dance/releases).
### Credits Running
-------
If you don't need to edit the source, just download a jar from [the releases page](https://github.com/yugecin/opsu-dance/releases).
Using an IDE is recommended because it is usually faster than the other options and provides debugging.
### Using your favorite IDE
You should know how to do this. It's recommended to use a working directory like `out` to not pollute the project directory with config/db files.
### Using apache maven
`mvn compile`
### Using apache ant
Resolve dependencies first, use either:
* `ant ivyresolve` to download dependencies using apache ivy
* `ant mvnresolve` to download dependencies using apache mvn
Then do `ant run`
Building a JAR
--------------
Using ant is recommended. Ant is used since release 0.5.0
### Using apache maven
`mvn package -Djar`, find it in the `target` folder.
### Using apache ant
`ant jar`, find it in the `bin` folder
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! 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)). Edits were made in the opsu! sources, too.
### License opsu!dance (everything in the src package yugecin.opsudance) was made by me ([@yugecin](https://github.com/yugecin)). Lots of edits were made in the opsu! sources, too.
License
-------
**This software is licensed under GNU GPL version 3.** **This software is licensed under GNU GPL version 3.**
You can find the full text of the license [here](LICENSE). You can find the full text of the license in [the LICENSE file](LICENSE).

159
build.xml Normal file
View File

@ -0,0 +1,159 @@
<project name="opsu!dance" default="hi" xmlns:ivy="antlib:org.apache.ivy.ant">
<property name="dir.src" value="${basedir}/src" />
<property name="dir.lib" value="${basedir}/lib" />
<property name="dir.mvnlibs" value="${basedir}/mvnlibs" />
<property name="dir.ivylibs" value="${basedir}/ivylibs" />
<property name="dir.res" value="${basedir}/res" />
<property name="dir.out" value="${basedir}/bin" />
<property name="lang.src" value="1.8" />
<property name="lang.target" value="1.8" />
<property name="version" value="0.5.0-SNAPSHOT" />
<property name="main" value="yugecin.opsudance.core.Entrypoint" />
<tstamp>
<format property="timestamp" pattern="yyyy-MM-dd HH:mm" />
</tstamp>
<target name="hi">
<echo>
ant clean --> clean the ant working dir
ant cleanlib --> clean the lib folder
ant ivyresolve --> resolve dependencies using ivy
ant mvnresolve --> resolve dependencies using mvn
ant compile --> compile the code
ant run --> prepare to run and run
ant jar --> package a jar
resolve dependencies first
(using either mvnresolve or ivyresolve),
then run (code is compiled automatically when you run)
</echo>
</target>
<target name="clean" description="--> clean the ant working dir">
<delete dir="${dir.out}" />
</target>
<target name="cleanlib" description="--> clean the lib folder">
<delete dir="${dir.lib}" />
<delete dir="${dir.mvnlibs}" />
<delete dir="${dir.ivylibs}" />
</target>
<target name="ivyresolve" depends="cleanlib" description="--> resolve dependencies using ivy">
<ivy:retrieve pattern="${dir.ivylibs}/[artifact]-[revision](-[classifier]).[ext]" />
<move file="${dir.ivylibs}" tofile="${dir.lib}" />
</target>
<target name="mvnresolve" depends="cleanlib" description="--> resolve dependencies using mvn">
<condition property="shellexecutable" value="cmd">
<os family="windows" />
</condition>
<condition property="shellcmdarg" value="/c">
<os family="windows" />
</condition>
<!-- properties are immutable, the following 2 lines won't do anything if os is windows -->
<property name="shellexecutable" value="sh" />
<property name="shellcmdarg" value="-c" />
<exec executable="${shellexecutable}">
<arg value="${shellcmdarg}" />
<arg value="mvn initialize" />
</exec>
<move file="${dir.mvnlibs}" tofile="${dir.lib}" />
</target>
<target name="compile" description="--> compile sources">
<mkdir dir="${dir.out}/classes" />
<javac
srcdir="${dir.src}"
destdir="${dir.out}/classes"
includes="**/*.java"
source="${lang.src}"
target="${lang.target}"
includeantruntime="false"
classpathref="classpath.base" />
<copy todir="${dir.out}/classes">
<fileset dir="${dir.res}" excludes="version,*.pdn" />
</copy>
<copy todir="${dir.out}/classes">
<filterchain>
<expandproperties />
</filterchain>
<fileset dir="${dir.res}" includes="version" />
</copy>
</target>
<target name="run" depends="compile" description="--> run opsu!dance">
<mkdir dir="${dir.out}/Natives" />
<unzip dest="${dir.out}/Natives">
<fileset dir="${dir.lib}" includes="**/lwjgl-*-natives-*.jar" />
</unzip>
<java
fork="true"
dir="${dir.out}"
failonerror="false"
classpathref="classpath.run"
classname="${main}" />
</target>
<target name="jar" depends="compile" description="--> package a jar">
<property name="jarfile" value="${dir.out}/opsu-dance-${version}.jar" />
<delete file="${jarfile}" />
<jar jarfile="${dir.out}/lib.jar" roundup="false">
<zipgroupfileset dir="${dir.lib}" />
</jar>
<jar destfile="${jarfile}" duplicate="fail">
<manifest>
<attribute name="Manifest-Version" value="1.0" />
<attribute name="Built-By" value="${user.name}" />
<attribute name="Main-Class" value="${main}" />
<attribute name="WinNatives" value="OpenAL32.dll,OpenAL64.dll,lwjgl.dll,lwjgl64.dll" />
<attribute name="NixNatives" value="liblwjgl.so,liblwjgl64.so,libopenal.so,libopenal64.so" />
<attribute name="MacNatives" value="liblwjgl.dylib,openal.dylib" />
</manifest>
<fileset dir="${dir.out}/classes" />
<zipfileset src="${dir.out}/lib.jar">
<exclude name="META-INF/**" />
<exclude name="org/newdawn/slick/GameContainer.*" />
<exclude name="org/newdawn/slick/Image.*" />
<exclude name="org/newdawn/slick/Music.*" />
<exclude name="org/newdawn/slick/Input.*" />
<exclude name="org/newdawn/slick/Input$NullOutputStream.*" />
<exclude name="org/newdawn/slick/MouseListener.*" />
<exclude name="org/newdawn/slick/KeyListener.*" />
<exclude name="org/newdawn/slick/InputListener.*" />
<exclude name="org/newdawn/slick/gui/TextField.*" />
<exclude name="org/newdawn/slick/openal/AudioInputStream*" />
<exclude name="org/newdawn/slick/openal/OpenALStreamPlayer*" />
<exclude name="org/newdawn/slick/openal/SoundStore*" />
<!-- sqlite contains sources for some reason -->
<exclude name="**/*.java" />
<exclude name="**/*.c" />
</zipfileset>
</jar>
<delete file="${dir.out}/lib.jar" />
</target>
<path id="classpath.base">
<fileset dir="${dir.lib}" includes="**/*.jar" />
</path>
<path id="classpath.run">
<pathelement path="${dir.out}/classes" />
<fileset dir="${dir.lib}" includes="**/*.jar" />
</path>
</project>

38
ivy.xml Normal file
View File

@ -0,0 +1,38 @@
<ivy-module
version="2.0"
xmlns:extra="http://ant.apache.org/ivy/extra"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="yugecin.opsudance" module="opsu!dance" />
<dependencies defaultconfmapping="default">
<dependency org="org.lwjgl.lwjgl" name="lwjgl" rev="2.9.3">
<exclude module="jinput" />
</dependency>
<dependency org="org.lwjgl.lwjgl" name="lwjgl-platform" rev="2.9.3">
<artifact name="lwjgl-platform" type="jar" extra:classifier="natives-linux" />
<artifact name="lwjgl-platform" type="jar" extra:classifier="natives-osx" />
<artifact name="lwjgl-platform" type="jar" extra:classifier="natives-windows" />
</dependency>
<dependency org="org.slick2d" name="slick2d-core" rev="1.0.1">
<exclude module="jnlp" />
<exclude module="lwjgl" />
</dependency>
<dependency org="org.jcraft" name="jorbis" rev="0.0.17" />
<dependency org="net.lingala.zip4j" name="zip4j" rev="1.3.2" />
<dependency org="com.googlecode.soundlibs" name="mp3spi" rev="1.9.5.4">
<exclude module="junit" />
</dependency>
<dependency org="org.xerial" name="sqlite-jdbc" rev="3.8.10.2" />
<dependency org="org.json" name="json" rev="20140107" />
<dependency org="net.java.dev.jna" name="jna" rev="4.1.0" />
<dependency org="net.java.dev.jna" name="jna-platform" rev="4.1.0" />
<dependency org="org.apache.maven" name="maven-artifact" rev="3.3.3" />
<dependency org="org.apache.commons" name="commons-compress" rev="1.9" />
<dependency org="org.tukaani" name="xz" rev="1.5" />
<dependency org="com.github.jponge" name="lzma-java" rev="1.3" />
<dependency org="gov.nist.math" name="jama" rev="1.0.3" />
</dependencies>
</ivy-module>

88
pom.xml
View File

@ -1,10 +1,14 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>yugecin</groupId> <groupId>yugecin</groupId>
<artifactId>opsu-dance</artifactId> <artifactId>opsu-dance</artifactId>
<version>0.5.0-SNAPSHOT</version> <version>0.5.0-SNAPSHOT</version>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<version>${project.version}</version> <version>${project.version}</version>
<timestamp>${maven.build.timestamp}</timestamp> <timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format> <maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
@ -20,6 +24,7 @@
<excludes> <excludes>
<exclude>**/Thumbs.db</exclude> <exclude>**/Thumbs.db</exclude>
<exclude>**/version</exclude> <exclude>**/version</exclude>
<exclude>**.pdn</exclude>
</excludes> </excludes>
</resource> </resource>
<resource> <resource>
@ -31,13 +36,40 @@
</resource> </resource>
</resources> </resources>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<filesets>
<fileset>
<directory>${project.basedir}/mvnlibs</directory>
<followSymlinks>true</followSymlinks>
</fileset>
</filesets>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.basedir}/mvnlibs</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version> <version>3.2</version>
<configuration> <configuration>
<source>1.7</source> <source>1.8</source>
<target>1.7</target> <target>1.8</target>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
</configuration> </configuration>
</plugin> </plugin>
@ -74,6 +106,7 @@
<classpath /> <classpath />
<argument>${mainClassName}</argument> <argument>${mainClassName}</argument>
</arguments> </arguments>
<workingDirectory>${project.build.outputDirectory}/../</workingDirectory>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -92,18 +125,32 @@
<exclude>org/newdawn/slick/Music.*</exclude> <exclude>org/newdawn/slick/Music.*</exclude>
<exclude>org/newdawn/slick/Input.*</exclude> <exclude>org/newdawn/slick/Input.*</exclude>
<exclude>org/newdawn/slick/Input$NullOutputStream.*</exclude> <exclude>org/newdawn/slick/Input$NullOutputStream.*</exclude>
<exclude>org/newdawn/slick/MouseListener.*</exclude>
<exclude>org/newdawn/slick/KeyListener.*</exclude>
<exclude>org/newdawn/slick/InputListener.*</exclude>
<exclude>org/newdawn/slick/gui/TextField.*</exclude> <exclude>org/newdawn/slick/gui/TextField.*</exclude>
<exclude>org/newdawn/slick/openal/AudioInputStream*</exclude> <exclude>org/newdawn/slick/openal/AudioInputStream*</exclude>
<exclude>org/newdawn/slick/openal/OpenALStreamPlayer*</exclude> <exclude>org/newdawn/slick/openal/OpenALStreamPlayer*</exclude>
<exclude>org/newdawn/slick/openal/SoundStore*</exclude> <exclude>org/newdawn/slick/openal/SoundStore*</exclude>
</excludes> </excludes>
</filter> </filter>
<filter>
<!-- sqlite contains sources for some reason -->
<artifact>*:sqlite-jdbc</artifact>
<excludes>
<exclude>**/*.java</exclude>
<exclude>**/*.c</exclude>
</excludes>
</filter>
</filters> </filters>
<transformers> <transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries> <manifestEntries>
<Main-Class>${mainClassName}</Main-Class> <Main-Class>${mainClassName}</Main-Class>
<Use-XDG>${XDG}</Use-XDG> <Use-XDG>${XDG}</Use-XDG>
<WinNatives>OpenAL32.dll,OpenAL64.dll,lwjgl.dll,lwjgl64.dll</WinNatives>
<NixNatives>liblwjgl.so,liblwjgl64.so,libopenal.so,libopenal64.so</NixNatives>
<MacNatives>liblwjgl.dylib,openal.dylib</MacNatives>
</manifestEntries> </manifestEntries>
</transformer> </transformer>
</transformers> </transformers>
@ -142,6 +189,10 @@
<groupId>org.lwjgl.lwjgl</groupId> <groupId>org.lwjgl.lwjgl</groupId>
<artifactId>lwjgl</artifactId> <artifactId>lwjgl</artifactId>
</exclusion> </exclusion>
<exclusion>
<groupId>javax.jnlp</groupId>
<artifactId>jnlp-api</artifactId>
</exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
@ -154,20 +205,16 @@
<artifactId>zip4j</artifactId> <artifactId>zip4j</artifactId>
<version>1.3.2</version> <version>1.3.2</version>
</dependency> </dependency>
<dependency>
<groupId>com.googlecode.soundlibs</groupId>
<artifactId>jlayer</artifactId>
<version>1.0.1-1</version>
</dependency>
<dependency> <dependency>
<groupId>com.googlecode.soundlibs</groupId> <groupId>com.googlecode.soundlibs</groupId>
<artifactId>mp3spi</artifactId> <artifactId>mp3spi</artifactId>
<version>1.9.5-1</version> <version>1.9.5.4</version>
</dependency> <exclusions>
<dependency> <exclusion>
<groupId>com.googlecode.soundlibs</groupId> <groupId>junit</groupId>
<artifactId>tritonus-share</artifactId> <artifactId>junit</artifactId>
<version>0.3.7-2</version> </exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.xerial</groupId> <groupId>org.xerial</groupId>
@ -209,10 +256,11 @@
<artifactId>lzma-java</artifactId> <artifactId>lzma-java</artifactId>
<version>1.3</version> <version>1.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>gov.nist.math</groupId> <groupId>gov.nist.math</groupId>
<artifactId>jama</artifactId> <artifactId>jama</artifactId>
<version>1.0.3</version> <version>1.0.3</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

BIN
res/menu-nav-advanced.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

BIN
res/menu-nav-audio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

BIN
res/menu-nav-custom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
res/menu-nav-dance.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

BIN
res/menu-nav-gameplay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
res/menu-nav-general.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
res/menu-nav-graphics.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

BIN
res/menu-nav-input.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
res/menu-nav-pippi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

BIN
res/menu-nav-skin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

BIN
res/theme.ogg Normal file

Binary file not shown.

View File

@ -8,7 +8,8 @@ import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.curves.Vec2f; import itdelatrisu.opsu.objects.curves.Vec2f;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
/** /**
* This class is just a dummy {@link GameObject} to place in the middle of 2 GameObjects. * This class is just a dummy {@link GameObject} to place in the middle of 2 GameObjects.
@ -22,8 +23,8 @@ public class FakeGameObject extends GameObject {
public FakeGameObject() { public FakeGameObject() {
this.start = new Vec2f(); this.start = new Vec2f();
this.end = new Vec2f(); this.end = new Vec2f();
this.start.x = this.end.x = Options.width / 2; this.start.x = this.end.x = displayContainer.width / 2;
this.start.y = this.end.y = Options.height / 2; this.start.y = this.end.y = displayContainer.height / 2;
} }
public FakeGameObject(GameObject start, GameObject end) { public FakeGameObject(GameObject start, GameObject end) {

View File

@ -5,7 +5,8 @@ import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.objects.GameObject; import itdelatrisu.opsu.objects.GameObject;
import yugecin.opsudance.movers.Mover; import yugecin.opsudance.movers.Mover;
import yugecin.opsudance.movers.factories.AutoMoverFactory; import yugecin.opsudance.movers.factories.AutoMoverFactory;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* Created by Alex Wieser on 09.10.2016. * Created by Alex Wieser on 09.10.2016.
@ -130,6 +131,6 @@ public class CombinedSpiralMover extends Mover {
} }
private boolean checkBounds(double[] pos) { private boolean checkBounds(double[] pos) {
return 0 < pos[0] && pos[0] < Options.width && 0 < pos[1] && pos[1] < Options.height; return 0 < pos[0] && pos[0] < displayContainer.width && 0 < pos[1] && pos[1] < displayContainer.height;
} }
} }

View File

@ -7,7 +7,8 @@ import awlex.ospu.movers.CombinedSpiralMover;
import yugecin.opsudance.movers.Mover; import yugecin.opsudance.movers.Mover;
import awlex.ospu.movers.SpiralToMover; import awlex.ospu.movers.SpiralToMover;
import yugecin.opsudance.movers.factories.MoverFactory; import yugecin.opsudance.movers.factories.MoverFactory;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
/** /**
* Created by Alex Wieser on 09.10.2016. * Created by Alex Wieser on 09.10.2016.
@ -87,7 +88,7 @@ public class SpiralMoverFactory implements MoverFactory {
* @return * @return
*/ */
private boolean checkBounds(double[] pos) { private boolean checkBounds(double[] pos) {
return 0 < pos[0] && pos[0] < Options.width && 0 < pos[1] && pos[1] < Options.height; return 0 < pos[0] && pos[0] < displayContainer.width && 0 < pos[1] && pos[1] < displayContainer.height;
} }
@Override @Override

View File

@ -1,9 +1,10 @@
package awlex.ospu.spinners; package awlex.ospu.spinners;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import yugecin.opsudance.options.Options;
import yugecin.opsudance.spinners.Spinner; import yugecin.opsudance.spinners.Spinner;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* Created by Alex Wieser on 09.10.2016. * Created by Alex Wieser on 09.10.2016.
* WHO DO YOU THINK I AM? * WHO DO YOU THINK I AM?
@ -42,11 +43,11 @@ public class SpiralSpinner extends Spinner {
double ang; double ang;
double rad; double rad;
for (int i = 0; i < SIZE / 2; i++) { for (int i = 0; i < SIZE / 2; i++) {
MAX_RAD = (int) (Options.height * .35); MAX_RAD = (int) (displayContainer.height * .35);
ang = (DENSITY * (Math.PI / SIZE) * i); ang = (DENSITY * (Math.PI / SIZE) * i);
rad = (MAX_RAD / (SIZE / 2)) * i; rad = (MAX_RAD / (SIZE / 2)) * i;
int offsetX = Options.width / 2; int offsetX = displayContainer.width / 2;
int offsetY = Options.height / 2; int offsetY = displayContainer.height / 2;
points[SIZE / 2 - 1 - i] = new double[]{ points[SIZE / 2 - 1 - i] = new double[]{
offsetX + rad * Math.cos(ang), offsetX + rad * Math.cos(ang),
offsetY + rad * Math.sin(ang) offsetY + rad * Math.sin(ang)
@ -83,12 +84,12 @@ public class SpiralSpinner extends Spinner {
} }
private void rotatePointAroundCenter(double[] point, double beta) { private void rotatePointAroundCenter(double[] point, double beta) {
double angle = Math.atan2(point[1] - Options.height / 2, point[0] - Options.width / 2); double angle = Math.atan2(point[1] - displayContainer.height / 2, point[0] - displayContainer.width / 2);
double rad = Utils.distance(point[0], point[1], Options.width / 2, Options.height / 2); double rad = Utils.distance(point[0], point[1], displayContainer.width / 2, displayContainer.height / 2);
//rotationMatrix //rotationMatrix
point[0] = Options.width / 2 + rad * (Math.cos(angle) * Math.cos(beta) - Math.sin(angle) * Math.sin(beta)); point[0] = displayContainer.width / 2 + rad * (Math.cos(angle) * Math.cos(beta) - Math.sin(angle) * Math.sin(beta));
point[1] = Options.height / 2 + rad * (Math.cos(angle) * Math.sin(beta) + Math.sin(angle) * Math.cos(beta)); point[1] = displayContainer.height / 2 + rad * (Math.cos(angle) * Math.sin(beta) + Math.sin(angle) * Math.cos(beta));
} }

View File

@ -24,7 +24,6 @@ import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.objects.curves.Curve; import itdelatrisu.opsu.objects.curves.Curve;
import itdelatrisu.opsu.replay.Replay; import itdelatrisu.opsu.replay.Replay;
import itdelatrisu.opsu.replay.ReplayFrame; import itdelatrisu.opsu.replay.ReplayFrame;
@ -42,25 +41,17 @@ import org.newdawn.slick.Animation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService; import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.SlickUtil; import yugecin.opsudance.utils.SlickUtil;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* Holds game data and renders all related elements. * Holds game data and renders all related elements.
*/ */
public class GameData { public class GameData {
@Inject
private Configuration config;
@Inject
private InstanceContainer instanceContainer;
/** Delta multiplier for steady HP drain. */ /** Delta multiplier for steady HP drain. */
public static final float HP_DRAIN_MULTIPLIER = 1 / 200f; public static final float HP_DRAIN_MULTIPLIER = 1 / 200f;
@ -354,17 +345,10 @@ public class GameData {
/** Whether this object is used for gameplay (true) or score viewing (false). */ /** Whether this object is used for gameplay (true) or score viewing (false). */
private boolean isGameplay; private boolean isGameplay;
/** Container dimensions. */
private int width, height;
/** /**
* Constructor for gameplay. * Constructor for gameplay.
* @param width container width
* @param height container height
*/ */
public GameData(int width, int height) { public GameData() {
this.width = width;
this.height = height;
this.isGameplay = true; this.isGameplay = true;
clear(); clear();
@ -375,12 +359,8 @@ public class GameData {
* This will initialize all parameters and images needed for the * This will initialize all parameters and images needed for the
* {@link #drawRankingElements(Graphics, Beatmap)} method. * {@link #drawRankingElements(Graphics, Beatmap)} method.
* @param s the ScoreData object * @param s the ScoreData object
* @param width container width
* @param height container height
*/ */
public GameData(ScoreData s, int width, int height) { public GameData(ScoreData s) {
this.width = width;
this.height = height;
this.isGameplay = false; this.isGameplay = false;
this.scoreData = s; this.scoreData = s;
@ -395,8 +375,9 @@ public class GameData {
hitResultCount[HIT_300K] = 0; hitResultCount[HIT_300K] = 0;
hitResultCount[HIT_100K] = s.katu; hitResultCount[HIT_100K] = s.katu;
hitResultCount[HIT_MISS] = s.miss; hitResultCount[HIT_MISS] = s.miss;
this.replay = (s.replayString == null) ? null : if (s.replayString != null) {
instanceContainer.injectFields(new Replay(new File(config.replayDir, String.format("%s.osr", s.replayString)))); this.replay = new Replay(new File(config.replayDir, s.replayString + ".osr"));
}
loadImages(); loadImages();
} }
@ -622,6 +603,8 @@ public class GameData {
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public void drawGameElements(Graphics g, boolean breakPeriod, boolean firstObject, float alpha) { public void drawGameElements(Graphics g, boolean breakPeriod, boolean firstObject, float alpha) {
int width = displayContainer.width;
int height = displayContainer.height;
boolean relaxAutoPilot = (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()); boolean relaxAutoPilot = (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive());
int margin = (int) (width * 0.008f); int margin = (int) (width * 0.008f);
float uiScale = GameImage.getUIscale(); float uiScale = GameImage.getUIscale();
@ -813,6 +796,9 @@ public class GameData {
* @param beatmap the beatmap * @param beatmap the beatmap
*/ */
public void drawRankingElements(Graphics g, Beatmap beatmap) { public void drawRankingElements(Graphics g, Beatmap beatmap) {
int width = displayContainer.width;
int height = displayContainer.height;
// TODO Version 2 skins // TODO Version 2 skins
float rankingHeight = 75; float rankingHeight = 75;
float scoreTextScale = 1.0f; float scoreTextScale = 1.0f;
@ -925,7 +911,7 @@ public class GameData {
if (hitResult.hitResultType == HitObjectType.SPINNER && hitResult.result != HIT_MISS) { if (hitResult.hitResultType == HitObjectType.SPINNER && hitResult.result != HIT_MISS) {
Image spinnerOsu = GameImage.SPINNER_OSU.getImage(); Image spinnerOsu = GameImage.SPINNER_OSU.getImage();
spinnerOsu.setAlpha(hitResult.alpha); spinnerOsu.setAlpha(hitResult.alpha);
spinnerOsu.drawCentered(width / 2, height / 4); spinnerOsu.drawCentered(displayContainer.width / 2, displayContainer.height / 4);
spinnerOsu.setAlpha(1f); spinnerOsu.setAlpha(1f);
} else if (OPTION_SHOW_HIT_LIGHTING.state && !hitResult.hideResult && hitResult.result != HIT_MISS && } else if (OPTION_SHOW_HIT_LIGHTING.state && !hitResult.hideResult && hitResult.result != HIT_MISS &&
// hit lighting // hit lighting
@ -1199,7 +1185,7 @@ public class GameData {
// combo burst // combo burst
if (comboBurstIndex > -1 && OPTION_SHOW_COMBO_BURSTS.state) { if (comboBurstIndex > -1 && OPTION_SHOW_COMBO_BURSTS.state) {
int leftX = 0; int leftX = 0;
int rightX = width - comboBurstImages[comboBurstIndex].getWidth(); int rightX = displayContainer.width - comboBurstImages[comboBurstIndex].getWidth();
if (comboBurstX < leftX) { if (comboBurstX < leftX) {
comboBurstX += (delta / 2f) * GameImage.getUIscale(); comboBurstX += (delta / 2f) * GameImage.getUIscale();
if (comboBurstX > leftX) if (comboBurstX > leftX)
@ -1260,7 +1246,7 @@ public class GameData {
} }
comboBurstAlpha = 0.8f; comboBurstAlpha = 0.8f;
if ((comboBurstIndex % 2) == 0) { if ((comboBurstIndex % 2) == 0) {
comboBurstX = width; comboBurstX = displayContainer.width;
} else { } else {
comboBurstX = comboBurstImages[0].getWidth() * -1; comboBurstX = comboBurstImages[0].getWidth() * -1;
} }
@ -1603,7 +1589,7 @@ public class GameData {
replay = new Replay(); replay = new Replay();
replay.mode = Beatmap.MODE_OSU; replay.mode = Beatmap.MODE_OSU;
replay.version = Updater.get().getBuildDate(); replay.version = updater.getBuildDate();
replay.beatmapHash = (beatmap == null) ? "" : beatmap.md5Hash; replay.beatmapHash = (beatmap == null) ? "" : beatmap.md5Hash;
replay.playerName = ""; // TODO replay.playerName = ""; // TODO
replay.replayHash = Long.toString(System.currentTimeMillis()); // TODO replay.replayHash = Long.toString(System.currentTimeMillis()); // TODO

View File

@ -18,6 +18,7 @@
package itdelatrisu.opsu; package itdelatrisu.opsu;
import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.Fonts;
import java.io.File; import java.io.File;
@ -30,8 +31,7 @@ import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.skinning.SkinService; import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.SlickUtil; import yugecin.opsudance.utils.SlickUtil;
@ -249,6 +249,16 @@ public enum GameImage {
CONTROL_SLIDER_BALL ("control-sliderball", "png", false, false), CONTROL_SLIDER_BALL ("control-sliderball", "png", false, false),
CONTROL_CHECK_ON ("control-check-on", "png", false, false), CONTROL_CHECK_ON ("control-check-on", "png", false, false),
CONTROL_CHECK_OFF ("control-check-off", "png", false, false), CONTROL_CHECK_OFF ("control-check-off", "png", false, false),
MENU_NAV_AUDIO ("menu-nav-audio", "png", false, false),
MENU_NAV_CUSTOM ("menu-nav-custom", "png", false, false),
MENU_NAV_GAMEPLAY ("menu-nav-gameplay", "png", false, false),
MENU_NAV_GENERAL ("menu-nav-general", "png", false, false),
MENU_NAV_GRAPHICS ("menu-nav-graphics", "png", false, false),
MENU_NAV_INPUT ("menu-nav-input", "png", false, false),
MENU_NAV_SKIN ("menu-nav-skin", "png", false, false),
MENU_NAV_ADVANCED ("menu-nav-advanced", "png", false, false),
MENU_NAV_DANCE ("menu-nav-dance", "png", false, false),
MENU_NAV_PIPPI ("menu-nav-pippi", "png", false, false),
VOLUME ("volume-bg", "png", false, false) { VOLUME ("volume-bg", "png", false, false) {
@Override @Override
protected Image process_sub(Image img, int w, int h) { protected Image process_sub(Image img, int w, int h) {
@ -733,7 +743,7 @@ public enum GameImage {
String err = String.format("Could not find default image '%s'.", filename); String err = String.format("Could not find default image '%s'.", filename);
Log.warn(err); Log.warn(err);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
} }
/** /**
@ -776,31 +786,33 @@ public enum GameImage {
* @return an array of the loaded images, or null if not found * @return an array of the loaded images, or null if not found
*/ */
private Image[] loadImageArray(File dir) { private Image[] loadImageArray(File dir) {
if (filenameFormat != null) { if (filenameFormat == null) {
for (String suffix : getSuffixes()) { return null;
List<Image> list = new ArrayList<Image>(); }
int i = 0; for (String suffix : getSuffixes()) {
while (true) { List<Image> list = new ArrayList<Image>();
// look for next image int i = 0;
String filenameFormatted = String.format(filenameFormat + suffix, i++); while (true) {
String name = getImageFileName(filenameFormatted, dir, type, true); // look for next image
if (name == null) String filenameFormatted = String.format(filenameFormat + suffix, i++);
break; String name = getImageFileName(filenameFormatted, dir, type, true);
if (name == null)
break;
// add image to list // add image to list
try { try {
Image img = new Image(name); Image img = new Image(name);
if (suffix.equals(HD_SUFFIX)) if (suffix.equals(HD_SUFFIX))
img = img.getScaledCopy(0.5f); img = img.getScaledCopy(0.5f);
list.add(img); list.add(img);
} catch (SlickException e) { } catch (SlickException e) {
EventBus.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", name), BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif(
break; String.format("Failed to set image '%s'.", name), Colors.BUB_RED);
} break;
} }
if (!list.isEmpty())
return list.toArray(new Image[list.size()]);
} }
if (!list.isEmpty())
return list.toArray(new Image[list.size()]);
} }
return null; return null;
} }
@ -813,15 +825,17 @@ public enum GameImage {
private Image loadImageSingle(File dir) { private Image loadImageSingle(File dir) {
for (String suffix : getSuffixes()) { for (String suffix : getSuffixes()) {
String name = getImageFileName(filename + suffix, dir, type, true); String name = getImageFileName(filename + suffix, dir, type, true);
if (name != null) { if (name == null) {
try { continue;
Image img = new Image(name); }
if (suffix.equals(HD_SUFFIX)) try {
img = img.getScaledCopy(0.5f); Image img = new Image(name);
return img; if (suffix.equals(HD_SUFFIX))
} catch (SlickException e) { img = img.getScaledCopy(0.5f);
EventBus.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", filename), BubbleNotificationEvent.COMMONCOLOR_RED)); return img;
} } catch (SlickException e) {
BubNotifListener.EVENT.make().onBubNotif(
String.format("Failed to set image '%s'.", filename), Colors.BUB_RED);
} }
} }
return null; return null;
@ -865,7 +879,8 @@ public enum GameImage {
skinImages = null; skinImages = null;
} }
} catch (SlickException e) { } catch (SlickException e) {
ErrorHandler.error(String.format("Failed to destroy beatmap skin images for '%s'.", this.name()), e).show(); String msg = String.format("Failed to destroy beatmap skin images for '%s'.", this.name());
ErrorHandler.explode(msg, e, ErrorHandler.DEFAULT_OPTIONS);
} }
} }

View File

@ -27,39 +27,40 @@ import java.util.Collections;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import static org.lwjgl.input.Keyboard.*;
/** /**
* Game mods. * Game mods.
*/ */
public enum GameMod { public enum GameMod {
EASY (Category.EASY, 0, GameImage.MOD_EASY, "EZ", 2, Input.KEY_Q, 0.5f, EASY (Category.EASY, 0, GameImage.MOD_EASY, "EZ", 2, KEY_Q, 0.5f,
"Easy", "Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required."), "Easy", "Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required."),
NO_FAIL (Category.EASY, 1, GameImage.MOD_NO_FAIL, "NF", 1, Input.KEY_W, 0.5f, NO_FAIL (Category.EASY, 1, GameImage.MOD_NO_FAIL, "NF", 1, KEY_W, 0.5f,
"NoFail", "You can't fail. No matter what."), "NoFail", "You can't fail. No matter what."),
HALF_TIME (Category.EASY, 2, GameImage.MOD_HALF_TIME, "HT", 256, Input.KEY_E, 0.3f, HALF_TIME (Category.EASY, 2, GameImage.MOD_HALF_TIME, "HT", 256, KEY_E, 0.3f,
"HalfTime", "Less zoom."), "HalfTime", "Less zoom."),
HARD_ROCK (Category.HARD, 0, GameImage.MOD_HARD_ROCK, "HR", 16, Input.KEY_A, 1.06f, HARD_ROCK (Category.HARD, 0, GameImage.MOD_HARD_ROCK, "HR", 16, KEY_A, 1.06f,
"HardRock", "Everything just got a bit harder..."), "HardRock", "Everything just got a bit harder..."),
SUDDEN_DEATH (Category.HARD, 1, GameImage.MOD_SUDDEN_DEATH, "SD", 32, Input.KEY_S, 1f, SUDDEN_DEATH (Category.HARD, 1, GameImage.MOD_SUDDEN_DEATH, "SD", 32, KEY_S, 1f,
"SuddenDeath", "Miss a note and fail."), "SuddenDeath", "Miss a note and fail."),
// PERFECT (Category.HARD, 1, GameImage.MOD_PERFECT, "PF", 64, Input.KEY_S, 1f, // PERFECT (Category.HARD, 1, GameImage.MOD_PERFECT, "PF", 64, Input.KEY_S, 1f,
// "Perfect", "SS or quit."), // "Perfect", "SS or quit."),
DOUBLE_TIME (Category.HARD, 2, GameImage.MOD_DOUBLE_TIME, "DT", 64, Input.KEY_D, 1.12f, DOUBLE_TIME (Category.HARD, 2, GameImage.MOD_DOUBLE_TIME, "DT", 64, KEY_D, 1.12f,
"DoubleTime", "Zoooooooooom."), "DoubleTime", "Zoooooooooom."),
// NIGHTCORE (Category.HARD, 2, GameImage.MOD_NIGHTCORE, "NT", 64, Input.KEY_D, 1.12f, // NIGHTCORE (Category.HARD, 2, GameImage.MOD_NIGHTCORE, "NT", 64, Input.KEY_D, 1.12f,
// "Nightcore", "uguuuuuuuu"), // "Nightcore", "uguuuuuuuu"),
HIDDEN (Category.HARD, 3, GameImage.MOD_HIDDEN, "HD", 8, Input.KEY_F, 1.06f, HIDDEN (Category.HARD, 3, GameImage.MOD_HIDDEN, "HD", 8, KEY_F, 1.06f,
"Hidden", "Play with no approach circles and fading notes for a slight score advantage."), "Hidden", "Play with no approach circles and fading notes for a slight score advantage."),
FLASHLIGHT (Category.HARD, 4, GameImage.MOD_FLASHLIGHT, "FL", 1024, Input.KEY_G, 1.12f, FLASHLIGHT (Category.HARD, 4, GameImage.MOD_FLASHLIGHT, "FL", 1024, KEY_G, 1.12f,
"Flashlight", "Restricted view area."), "Flashlight", "Restricted view area."),
RELAX (Category.SPECIAL, 0, GameImage.MOD_RELAX, "RL", 128, Input.KEY_Z, 0f, RELAX (Category.SPECIAL, 0, GameImage.MOD_RELAX, "RL", 128, KEY_Z, 0f,
"Relax", "You don't need to click.\nGive your clicking/tapping finger a break from the heat of things.\n**UNRANKED**"), "Relax", "You don't need to click.\nGive your clicking/tapping finger a break from the heat of things.\n**UNRANKED**"),
AUTOPILOT (Category.SPECIAL, 1, GameImage.MOD_AUTOPILOT, "AP", 8192, Input.KEY_X, 0f, AUTOPILOT (Category.SPECIAL, 1, GameImage.MOD_AUTOPILOT, "AP", 8192, KEY_X, 0f,
"Relax2", "Automatic cursor movement - just follow the rhythm.\n**UNRANKED**"), "Relax2", "Automatic cursor movement - just follow the rhythm.\n**UNRANKED**"),
SPUN_OUT (Category.SPECIAL, 2, GameImage.MOD_SPUN_OUT, "SO", 4096, Input.KEY_C, 0.9f, SPUN_OUT (Category.SPECIAL, 2, GameImage.MOD_SPUN_OUT, "SO", 4096, KEY_C, 0.9f,
"SpunOut", "Spinners will be automatically completed."), "SpunOut", "Spinners will be automatically completed."),
AUTO (Category.SPECIAL, 3, GameImage.MOD_AUTO, "", 2048, Input.KEY_V, 1f, AUTO (Category.SPECIAL, 3, GameImage.MOD_AUTO, "", 2048, KEY_V, 1f,
"Autoplay", "Watch a perfect automated play through the song."); "Autoplay", "Watch a perfect automated play through the song.");
/** Mod categories. */ /** Mod categories. */

View File

@ -18,87 +18,75 @@
package itdelatrisu.opsu; package itdelatrisu.opsu;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.utils.ManifestWrapper;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.lang.reflect.Field;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
/** import static yugecin.opsudance.core.InstanceContainer.*;
* Native loader, based on the JarSplice launcher.
*
* @author http://ninjacave.com
*/
public class NativeLoader {
/** The directory to unpack natives to. */
private final File nativeDir;
/** public class NativeLoader {
* Constructor.
* @param dir the directory to unpack natives to public static void setNativePath() {
*/ String nativepath = config.NATIVE_DIR.getAbsolutePath();
public NativeLoader(File dir) { System.setProperty("org.lwjgl.librarypath", nativepath);
nativeDir = dir; System.setProperty("java.library.path", nativepath);
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 (Exception e) {
Log.warn("Failed to set 'sys_paths' field.", e);
}
} }
/** /**
* Unpacks natives for the current operating system to the natives directory. * Unpacks natives for the current operating system to the natives directory.
* @throws IOException if an I/O exception occurs * @throws IOException if an I/O exception occurs
*/ */
public void loadNatives() throws IOException { public static void loadNatives(JarFile jarfile, ManifestWrapper manifest) throws IOException {
if (!nativeDir.exists()) if (!config.NATIVE_DIR.exists() && !config.NATIVE_DIR.mkdir()) {
nativeDir.mkdir(); String msg = String.format("Could not create folder '%s'",
config.NATIVE_DIR.getAbsolutePath());
JarFile jarFile = Utils.getJarFile(); throw new RuntimeException(msg);
if (jarFile == null)
return;
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry e = entries.nextElement();
if (e == null)
break;
File f = new File(nativeDir, e.getName());
if (isNativeFile(e.getName()) && !e.isDirectory() && e.getName().indexOf('/') == -1 && !f.exists()) {
InputStream in = jarFile.getInputStream(jarFile.getEntry(e.getName()));
OutputStream out = new FileOutputStream(f);
byte[] buffer = new byte[65536];
int bufferSize;
while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1)
out.write(buffer, 0, bufferSize);
in.close();
out.close();
}
} }
jarFile.close();
}
/**
* Returns whether the given file name is a native file for the current operating system.
* @param entryName the file name
* @return true if the file is a native that should be loaded, false otherwise
*/
private boolean isNativeFile(String entryName) {
String osName = System.getProperty("os.name"); String osName = System.getProperty("os.name");
String name = entryName.toLowerCase(); String nativekey = null;
if (osName.startsWith("Win")) { if (osName.startsWith("Win")) {
if (name.endsWith(".dll")) nativekey = "WinNatives";
return true;
} else if (osName.startsWith("Linux")) { } else if (osName.startsWith("Linux")) {
if (name.endsWith(".so")) nativekey = "NixNatives";
return true;
} else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) { } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
if (name.endsWith(".dylib") || name.endsWith(".jnilib")) nativekey = "MacNatives";
return true; }
if (nativekey == null) {
Log.warn("Cannot determine natives for os " + osName);
return;
}
String natives = manifest.valueOrDefault(null, nativekey, null);
if (natives == null) {
String msg = String.format("No entry for '%s' in manifest, jar is badly packed or damaged",
nativekey);
throw new RuntimeException(msg);
}
String[] nativefiles = natives.split(",");
for (String nativefile : nativefiles) {
File unpackedFile = new File(config.NATIVE_DIR, nativefile);
if (unpackedFile.exists()) {
continue;
}
Utils.unpackFromJar(jarfile, unpackedFile, nativefile);
} }
return false;
} }
} }

View File

@ -20,22 +20,13 @@ package itdelatrisu.opsu;
import itdelatrisu.opsu.downloads.Download; import itdelatrisu.opsu.downloads.Download;
import java.io.BufferedInputStream; import java.io.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Scanner; import java.util.Scanner;
import java.util.jar.JarFile; import java.util.jar.JarFile;
@ -50,41 +41,21 @@ import org.json.JSONObject;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Animation; import org.newdawn.slick.Animation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Input;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import com.sun.jna.platform.FileUtils; import com.sun.jna.platform.FileUtils;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.NotNull;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.Nullable;
import yugecin.opsudance.options.Options; import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* Contains miscellaneous utilities. * Contains miscellaneous utilities.
*/ */
public class Utils { public class Utils {
/**
* List of illegal filename characters.
* @see #cleanFileName(String, char)
*/
private final static int[] illegalChars = {
34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47
};
static {
Arrays.sort(illegalChars);
}
// This class should not be instantiated.
private Utils() {}
/**
* Initializes game settings and class data.
*/
public static void init(DisplayContainer displayContainer) {
// TODO clean this up
// game settings
}
/** /**
* Draws an animation based on its center. * Draws an animation based on its center.
@ -183,15 +154,12 @@ public class Utils {
* @return true if pressed * @return true if pressed
*/ */
public static boolean isGameKeyPressed() { public static boolean isGameKeyPressed() {
/* return
boolean mouseDown = !Options.isMouseDisabled() && ( input.isKeyPressed(Options.OPTION_KEY_LEFT.intval) ||
input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isKeyPressed(Options.OPTION_KEY_RIGHT.intval) ||
input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)); (!Options.OPTION_DISABLE_MOUSE_BUTTONS.state && (
return (mouseDown || input.isMousePressed(Input.MOUSE_LEFT_BUTTON) ||
input.isKeyDown(Options.getGameKeyLeft()) || input.isMousePressed(Input.MOUSE_RIGHT_BUTTON)));
input.isKeyDown(Options.getGameKeyRight()));
*/
return true;
} }
@ -210,26 +178,31 @@ public class Utils {
} }
/** /**
* Cleans a file name. * Changes bad characters to the replacement char given.
* @param badFileName the original name string * Bad characters:
* @param replace the character to replace illegal characters with (or 0 if none) * non-printable (0-31)
* @return the cleaned file name * " (34) * (42) / (47) : (58)
* @author Sarel Botha (http://stackoverflow.com/a/5626340) * < (60) > (62) ? (63) \ (92)
* DEL (124)
*/ */
public static String cleanFileName(String badFileName, char replace) { public static String cleanFileName(@NotNull String badFileName, char replacement) {
if (badFileName == null) char[] chars = badFileName.toCharArray();
return null; long additionalBadChars =
1L << (34 - 32)|
boolean doReplace = (replace > 0 && Arrays.binarySearch(illegalChars, replace) < 0); 1L << (42 - 32)|
StringBuilder cleanName = new StringBuilder(); 1L << (47 - 32)|
for (int i = 0, n = badFileName.length(); i < n; i++) { 1L << (58 - 32)|
int c = badFileName.charAt(i); 1L << (60 - 32)|
if (Arrays.binarySearch(illegalChars, c) < 0) 1L << (62 - 32)|
cleanName.append((char) c); 1L << (63 - 32)|
else if (doReplace) 1L << (92 - 32);
cleanName.append(replace); for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (c < 32 || c == 124 || (c < 93 && 0 != (additionalBadChars & (1L << (c - 32))))) {
chars[i] = replacement;
}
} }
return cleanName.toString(); return new String(chars);
} }
/** /**
@ -338,7 +311,7 @@ public class Utils {
try { try {
json = new JSONObject(s); json = new JSONObject(s);
} catch (JSONException e) { } catch (JSONException e) {
ErrorHandler.error("Failed to create JSON object.", e).show(); explode("Failed to create JSON object.", e, DEFAULT_OPTIONS);
} }
} }
return json; return json;
@ -357,7 +330,7 @@ public class Utils {
try { try {
json = new JSONArray(s); json = new JSONArray(s);
} catch (JSONException e) { } catch (JSONException e) {
ErrorHandler.error("Failed to create JSON array.", e).show(); explode("Failed to create JSON array.", e, DEFAULT_OPTIONS);
} }
} }
return json; return json;
@ -399,7 +372,7 @@ public class Utils {
result.append(String.format("%02x", b)); result.append(String.format("%02x", b));
return result.toString(); return result.toString();
} catch (NoSuchAlgorithmException | IOException e) { } catch (NoSuchAlgorithmException | IOException e) {
ErrorHandler.error("Failed to calculate MD5 hash.", e).show(); explode("Failed to calculate MD5 hash.", e, DEFAULT_OPTIONS);
} }
return null; return null;
} }
@ -418,43 +391,6 @@ public class Utils {
return String.format("%02d:%02d:%02d", seconds / 3600, (seconds / 60) % 60, seconds % 60); return String.format("%02d:%02d:%02d", seconds / 3600, (seconds / 60) % 60, seconds % 60);
} }
/**
* Returns whether or not the application is running within a JAR.
* @return true if JAR, false if file
*/
public static boolean isJarRunning() {
return Utils.class.getResource(String.format("%s.class", Utils.class.getSimpleName())).toString().startsWith("jar:");
}
/**
* Returns the JarFile for the application.
* @return the JAR file, or null if it could not be determined
*/
public static JarFile getJarFile() {
if (!isJarRunning())
return null;
try {
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;
}
}
/**
* Returns the directory where the application is being run.
* @return the directory, or null if it could not be determined
*/
public static File getRunningDirectory() {
try {
return new File(Utils.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
} catch (URISyntaxException e) {
Log.error("Could not get the running directory.", e);
return null;
}
}
/** /**
* Parses the integer string argument as a boolean: * Parses the integer string argument as a boolean:
* {@code 1} is {@code true}, and all other values are {@code false}. * {@code 1} is {@code true}, and all other values are {@code false}.
@ -470,8 +406,9 @@ public class Utils {
* most recent update to the working directory (e.g. fetch or successful push). * most recent update to the working directory (e.g. fetch or successful push).
* @return the 40-character SHA-1 hash, or null if it could not be determined * @return the 40-character SHA-1 hash, or null if it could not be determined
*/ */
@Nullable
public static String getGitHash() { public static String getGitHash() {
if (isJarRunning()) if (env.isJarRunning)
return null; return null;
File f = new File(".git/refs/remotes/origin/master"); File f = new File(".git/refs/remotes/origin/master");
if (!f.isFile()) if (!f.isFile())
@ -516,11 +453,20 @@ public class Utils {
} catch (Exception e) {} } catch (Exception e) {}
} }
public static int getQuadrant(double x, double y) { /**
if (x < Options.width / 2d) { * Gets the region where the given point is in.
return y < Options.height / 2d ? 2 : 3; * First bit is set if x > half
} * Second bit is set if y > half
return y < Options.height / 2d ? 1 : 4; *
* 2 | 3
* --+--
* 0 | 1
*/
public static int getRegion(double x, double y) {
int q = 0;
if (y < displayContainer.height / 2d) q = 2;
if (x < displayContainer.width / 2d) q |= 1;
return q;
} }
/* /*
@ -537,24 +483,24 @@ public class Utils {
*/ */
public static float[] mirrorPoint(float x, float y) { public static float[] mirrorPoint(float x, float y) {
double dx = x - Options.width / 2d; double dx = x - displayContainer.width / 2d;
double dy = y - Options.height / 2d; double dy = y - displayContainer.height / 2d;
double ang = Math.atan2(dy, dx); double ang = Math.atan2(dy, dx);
double d = -Math.sqrt(dx * dx + dy * dy); double d = -Math.sqrt(dx * dx + dy * dy);
return new float[]{ return new float[]{
(float) (Options.width / 2d + Math.cos(ang) * d), (float) (displayContainer.width / 2d + Math.cos(ang) * d),
(float) (Options.height / 2d + Math.sin(ang) * d) (float) (displayContainer.height / 2d + Math.sin(ang) * d)
}; };
} }
public static float[] mirrorPoint(float x, float y, float degrees) { public static float[] mirrorPoint(float x, float y, float degrees) {
double dx = x - Options.width / 2d; double dx = x - displayContainer.width / 2d;
double dy = y - Options.height / 2d; double dy = y - displayContainer.height / 2d;
double ang = Math.atan2(dy, dx) + (degrees * Math.PI / 180d); double ang = Math.atan2(dy, dx) + (degrees * Math.PI / 180d);
double d = Math.sqrt(dx * dx + dy * dy); double d = Math.sqrt(dx * dx + dy * dy);
return new float[]{ return new float[]{
(float) (Options.width / 2d + Math.cos(ang) * d), (float) (displayContainer.width / 2d + Math.cos(ang) * d),
(float) (Options.height / 2d + Math.sin(ang) * d) (float) (displayContainer.height / 2d + Math.sin(ang) * d)
}; };
} }
@ -573,4 +519,19 @@ public class Utils {
key != Keyboard.KEY_F7 && key != Keyboard.KEY_F10 && key != Keyboard.KEY_F12); key != Keyboard.KEY_F7 && key != Keyboard.KEY_F10 && key != Keyboard.KEY_F12);
} }
public static void unpackFromJar(@NotNull JarFile jarfile, @NotNull File unpackedFile,
@NotNull String filename) throws IOException {
InputStream in = jarfile.getInputStream(jarfile.getEntry(filename));
OutputStream out = new FileOutputStream(unpackedFile);
byte[] buffer = new byte[65536];
int bufferSize;
while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1) {
out.write(buffer, 0, bufferSize);
}
in.close();
out.close();
}
} }

View File

@ -1,7 +1,5 @@
package itdelatrisu.opsu.audio; package itdelatrisu.opsu.audio;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
@ -14,6 +12,8 @@ import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineListener; import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.LineUnavailableException;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
/** /**
* Extension of Clip that allows playing multiple copies of a Clip simultaneously. * Extension of Clip that allows playing multiple copies of a Clip simultaneously.
* http://stackoverflow.com/questions/1854616/ * http://stackoverflow.com/questions/1854616/
@ -194,7 +194,8 @@ public class MultiClip {
try { try {
audioIn.close(); audioIn.close();
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error(String.format("Could not close AudioInputStream for MultiClip %s.", name), e).show(); explode(String.format("Could not close AudioInputStream for MultiClip %s.", name), e,
DEFAULT_OPTIONS);
} }
} }
} }

View File

@ -33,6 +33,7 @@ import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException; import javax.sound.sampled.UnsupportedAudioFileException;
import itdelatrisu.opsu.ui.Colors;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import org.lwjgl.openal.AL; import org.lwjgl.openal.AL;
import org.lwjgl.openal.AL10; import org.lwjgl.openal.AL10;
@ -44,11 +45,10 @@ import org.newdawn.slick.openal.SoundStore;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import org.tritonus.share.sampled.file.TAudioFileFormat; import org.tritonus.share.sampled.file.TAudioFileFormat;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -103,7 +103,8 @@ public class MusicController {
if (lastBeatmap == null || !beatmap.audioFilename.equals(lastBeatmap.audioFilename)) { if (lastBeatmap == null || !beatmap.audioFilename.equals(lastBeatmap.audioFilename)) {
final File audioFile = beatmap.audioFilename; final File audioFile = beatmap.audioFilename;
if (!audioFile.isFile() && !ResourceLoader.resourceExists(audioFile.getPath())) { if (!audioFile.isFile() && !ResourceLoader.resourceExists(audioFile.getPath())) {
EventBus.post(new BarNotificationEvent(String.format("Could not find track '%s'.", audioFile.getName()))); BarNotifListener.EVENT.make().onBarNotif(String.format("Could not find track '%s'.",
audioFile.getName()));
return; return;
} }
@ -158,7 +159,7 @@ public class MusicController {
} catch (Exception e) { } catch (Exception e) {
String err = String.format("Could not play track '%s'.", file.getName()); String err = String.format("Could not play track '%s'.", file.getName());
Log.error(err, e); Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
} }
} }
@ -575,7 +576,7 @@ public class MusicController {
player = null; player = null;
} catch (Exception e) { } catch (Exception e) {
ErrorHandler.error("Failed to destroy OpenAL.", e).show(); explode("Failed to destroy OpenAL.", e, DEFAULT_OPTIONS);
} }
} }

View File

@ -36,15 +36,15 @@ import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineListener; import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.LineUnavailableException;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.SlickException; import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration; import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService; import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -104,7 +104,7 @@ public class SoundController {
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url); AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
return loadClip(ref, audioIn, isMP3); return loadClip(ref, audioIn, isMP3);
} catch (Exception e) { } catch (Exception e) {
ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e).show(); explode(String.format("Failed to load file '%s'.", ref), e, DEFAULT_OPTIONS);
return null; return null;
} }
} }
@ -220,7 +220,8 @@ public class SoundController {
// menu and game sounds // menu and game sounds
for (SoundEffect s : SoundEffect.values()) { for (SoundEffect s : SoundEffect.values()) {
if ((currentFileName = getSoundFileName(s.getFileName())) == null) { if ((currentFileName = getSoundFileName(s.getFileName())) == null) {
EventBus.post(new BubbleNotificationEvent("Could not find sound file " + s.getFileName(), BubbleNotificationEvent.COLOR_ORANGE)); BubNotifListener.EVENT.make().onBubNotif(
"Could not find sound file " + s.getFileName(), Colors.BUB_ORANGE);
continue; continue;
} }
MultiClip newClip = loadClip(currentFileName, currentFileName.endsWith(".mp3")); MultiClip newClip = loadClip(currentFileName, currentFileName.endsWith(".mp3"));
@ -239,7 +240,8 @@ public class SoundController {
for (HitSound s : HitSound.values()) { for (HitSound s : HitSound.values()) {
String filename = String.format("%s-%s", ss.getName(), s.getFileName()); String filename = String.format("%s-%s", ss.getName(), s.getFileName());
if ((currentFileName = getSoundFileName(filename)) == null) { if ((currentFileName = getSoundFileName(filename)) == null) {
EventBus.post(new BubbleNotificationEvent("Could not find hit sound file " + filename, BubbleNotificationEvent.COLOR_ORANGE)); BubNotifListener.EVENT.make().onBubNotif(
"Could not find hit sound file " + filename, Colors.BUB_ORANGE);
continue; continue;
} }
MultiClip newClip = loadClip(currentFileName, false); MultiClip newClip = loadClip(currentFileName, false);
@ -283,7 +285,7 @@ public class SoundController {
try { try {
clip.start(volume, listener); clip.start(volume, listener);
} catch (LineUnavailableException e) { } catch (LineUnavailableException e) {
ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e).show(); explode(String.format("Could not start a clip '%s'.", clip.getName()), e, DEFAULT_OPTIONS);
} }
} }
} }
@ -396,7 +398,8 @@ public class SoundController {
@Override @Override
public void error() { public void error() {
EventBus.post(new BarNotificationEvent("Failed to download track preview.")); BarNotifListener.EVENT.make().onBarNotif(
"Failed to download track preview");
} }
}); });
try { try {

View File

@ -31,16 +31,15 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.Nullable;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService; import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -48,12 +47,6 @@ import static yugecin.opsudance.options.Options.*;
*/ */
public class BeatmapParser { public class BeatmapParser {
@Inject
private InstanceContainer instanceContainer;
@Inject
private Configuration config;
/** The string lookup database. */ /** The string lookup database. */
private static HashMap<String, String> stringdb = new HashMap<String, String>(); private static HashMap<String, String> stringdb = new HashMap<String, String>();
@ -78,14 +71,9 @@ public class BeatmapParser {
/** If no Provider supports a MessageDigestSpi implementation for the MD5 algorithm. */ /** If no Provider supports a MessageDigestSpi implementation for the MD5 algorithm. */
private boolean hasNoMD5Algorithm = false; private boolean hasNoMD5Algorithm = false;
@Inject
public BeatmapParser() {
}
/** /**
* Invokes parser for each OSU file in a root directory and * Invokes parser for each OSU file in a root directory and
* adds the beatmaps to a new BeatmapSetList. * adds the beatmaps to a new BeatmapSetList.
* @param root the root directory (search has depth 1)
*/ */
public void parseAll() { public void parseAll() {
// create a new BeatmapSetList // create a new BeatmapSetList
@ -93,7 +81,7 @@ public class BeatmapParser {
// create a new watch service // create a new watch service
if (OPTION_ENABLE_WATCH_SERVICE.state) { if (OPTION_ENABLE_WATCH_SERVICE.state) {
BeatmapWatchService.create(instanceContainer); BeatmapWatchService.create();
} }
// parse all directories // parse all directories
@ -106,8 +94,8 @@ public class BeatmapParser {
* @param dirs the array of directories to parse * @param dirs the array of directories to parse
* @return the last BeatmapSetNode parsed, or null if none * @return the last BeatmapSetNode parsed, or null if none
*/ */
public BeatmapSetNode parseDirectories(File[] dirs) { public BeatmapSetNode parseDirectories(@Nullable File[] dirs) {
if (dirs == null) if (dirs == null || dirs.length == 0)
return null; return null;
// progress tracking // progress tracking
@ -172,8 +160,8 @@ public class BeatmapParser {
try { try {
beatmap = parseFile(file, dir, beatmaps, false); beatmap = parseFile(file, dir, beatmaps, false);
} catch(Exception e) { } catch(Exception e) {
Log.error("could not parse beatmap " + file.getName() + ": " + e.getMessage()); logAndShowErrorNotification(e, "Could not parse beatmap %s: %s",
EventBus.post(new BubbleNotificationEvent("Could not parse beatmap " + file.getName(), BubbleNotificationEvent.COLOR_ORANGE)); file.getName(), e.getMessage());
} }
// add to parsed beatmap list // add to parsed beatmap list
@ -260,11 +248,9 @@ public class BeatmapParser {
} }
map.timingPoints.trimToSize(); map.timingPoints.trimToSize();
} catch (IOException e) { } catch (IOException e) {
String err = String.format("Failed to read file '%s'.", map.getFile().getAbsolutePath()); logAndShowErrorNotification(e, "Failed to read file '%s'.", map.getFile().getAbsolutePath());
Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
ErrorHandler.error("Failed to get MD5 hash stream.", e).show(); explode("Failed to get MD5 hash stream.", e, DEFAULT_OPTIONS);
// retry without MD5 // retry without MD5
hasNoMD5Algorithm = true; hasNoMD5Algorithm = true;
@ -665,11 +651,9 @@ public class BeatmapParser {
if (md5stream != null) if (md5stream != null)
beatmap.md5Hash = md5stream.getMD5(); beatmap.md5Hash = md5stream.getMD5();
} catch (IOException e) { } catch (IOException e) {
String err = String.format("Failed to read file '%s'.", file.getAbsolutePath()); logAndShowErrorNotification(e, "Failed to read file '%s'.", file.getAbsolutePath());
Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
ErrorHandler.error("Failed to get MD5 hash stream.", e).show(); explode("Failed to get MD5 hash stream.", e, DEFAULT_OPTIONS);
// retry without MD5 // retry without MD5
hasNoMD5Algorithm = true; hasNoMD5Algorithm = true;
@ -748,9 +732,8 @@ public class BeatmapParser {
} }
beatmap.timingPoints.trimToSize(); beatmap.timingPoints.trimToSize();
} catch (IOException e) { } catch (IOException e) {
String err = String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath()); logAndShowErrorNotification(e, "Failed to read file '%s'.",
Log.error(err, e); beatmap.getFile().getAbsolutePath());
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
} }
@ -828,12 +811,12 @@ public class BeatmapParser {
// check that all objects were parsed // check that all objects were parsed
if (objectIndex != beatmap.objects.length) if (objectIndex != beatmap.objects.length)
ErrorHandler.error(String.format("Parsed %d objects for beatmap '%s', %d objects expected.", explode(String.format("Parsed %d objects for beatmap '%s', %d objects expected.",
objectIndex, beatmap.toString(), beatmap.objects.length), new Exception("no")).show(); objectIndex, beatmap.toString(), beatmap.objects.length), new Exception("no"),
DEFAULT_OPTIONS);
} catch (IOException e) { } catch (IOException e) {
String err = String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath()); logAndShowErrorNotification(e, "Failed to read file '%s'.",
Log.error(err, e); beatmap.getFile().getAbsolutePath());
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
} }
@ -904,4 +887,10 @@ public class BeatmapParser {
return DBString; return DBString;
} }
private static void logAndShowErrorNotification(Exception e, String message, Object... formatArgs) {
message = String.format(message, formatArgs);
Log.error(message, e);
BubNotifListener.EVENT.make().onBubNotif(message, Colors.BUB_RED);
}
} }

View File

@ -21,8 +21,8 @@ package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.db.BeatmapDB; import itdelatrisu.opsu.db.BeatmapDB;
import yugecin.opsudance.core.events.EventBus; import itdelatrisu.opsu.ui.Colors;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubNotifListener;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -215,7 +215,7 @@ public class BeatmapSetList {
try { try {
Utils.deleteToTrash(dir); Utils.deleteToTrash(dir);
} catch (IOException e) { } catch (IOException e) {
EventBus.post(new BubbleNotificationEvent("Could not delete song group", BubbleNotificationEvent.COLOR_ORANGE)); BubNotifListener.EVENT.make().onBubNotif("Could not delete song group", Colors.BUB_ORANGE);
} }
if (ws != null) if (ws != null)
ws.resume(); ws.resume();
@ -271,7 +271,7 @@ public class BeatmapSetList {
try { try {
Utils.deleteToTrash(file); Utils.deleteToTrash(file);
} catch (IOException e) { } catch (IOException e) {
EventBus.post(new BubbleNotificationEvent("Could not delete song", BubbleNotificationEvent.COLOR_ORANGE)); BubNotifListener.EVENT.make().onBubNotif("Could not delete song", Colors.BUB_ORANGE);
} }
if (ws != null) if (ws != null)
ws.resume(); ws.resume();

View File

@ -38,12 +38,12 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/* /*
@ -83,6 +83,7 @@ import static yugecin.opsudance.options.Options.*;
* @author The Java Tutorials (http://docs.oracle.com/javase/tutorial/essential/io/examples/WatchDir.java) (base) * @author The Java Tutorials (http://docs.oracle.com/javase/tutorial/essential/io/examples/WatchDir.java) (base)
*/ */
public class BeatmapWatchService { public class BeatmapWatchService {
/** Beatmap watcher service instance. */ /** Beatmap watcher service instance. */
private static BeatmapWatchService ws; private static BeatmapWatchService ws;
@ -90,17 +91,17 @@ public class BeatmapWatchService {
* Creates a new watch service instance (overwriting any previous instance), * Creates a new watch service instance (overwriting any previous instance),
* registers the beatmap directory, and starts processing events. * registers the beatmap directory, and starts processing events.
*/ */
public static void create(InstanceContainer instanceContainer) { public static void create() {
// close the existing watch service // close the existing watch service
destroy(); destroy();
// create a new watch service // create a new watch service
try { try {
ws = instanceContainer.provide(BeatmapWatchService.class); ws = new BeatmapWatchService();
ws.register(instanceContainer.provide(Configuration.class).beatmapDir.toPath()); ws.register(config.beatmapDir.toPath());
} catch (IOException e) { } catch (IOException e) {
Log.error("Could not create watch service", e); Log.error("Could not create watch service", e);
EventBus.post(new BubbleNotificationEvent("Could not create watch service", BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif("Could not create watch service", Colors.BUB_RED);
return; return;
} }
@ -121,8 +122,9 @@ public class BeatmapWatchService {
ws.service.shutdownNow(); ws.service.shutdownNow();
ws = null; ws = null;
} catch (IOException e) { } catch (IOException e) {
Log.error("An I/O exception occurred while closing the previous watch service.", e); String msg = "An I/O exception occurred while closing the previous watch service.";
EventBus.post(new BubbleNotificationEvent("An I/O exception occurred while closing the previous watch service.", BubbleNotificationEvent.COMMONCOLOR_RED)); Log.error(msg, e);
BarNotifListener.EVENT.make().onBarNotif(msg);
ws = null; ws = null;
} }
} }
@ -137,7 +139,10 @@ public class BeatmapWatchService {
return ws; return ws;
} }
/** Watch service listener interface. */ /**
* Watch service listener interface.
* TODO: replace by event system?
*/
public interface BeatmapWatchServiceListener { public interface BeatmapWatchServiceListener {
/** /**
* Indication that an event was received. * Indication that an event was received.

View File

@ -23,36 +23,27 @@ import java.io.FilenameFilter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import itdelatrisu.opsu.ui.Colors;
import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.exception.ZipException;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BubbleNotificationEvent; import static yugecin.opsudance.core.InstanceContainer.*;
import yugecin.opsudance.options.Configuration;
/** /**
* Unpacker for OSZ (ZIP) archives. * Unpacker for OSZ (ZIP) archives.
*/ */
public class OszUnpacker { public class OszUnpacker {
@Inject
private Configuration config;
/** The index of the current file being unpacked. */ /** The index of the current file being unpacked. */
private int fileIndex = -1; private int fileIndex = -1;
/** The total number of files to unpack. */ /** The total number of files to unpack. */
private File[] files; private File[] files;
@Inject
public OszUnpacker() {
}
/** /**
* Invokes the unpacker for each OSZ archive in a root directory. * Invokes the unpacker for each OSZ archive in a root directory.
* @param root the root directory
* @param dest the destination directory
* @return an array containing the new (unpacked) directories, or null * @return an array containing the new (unpacked) directories, or null
* if no OSZs found * if no OSZs found
*/ */
@ -106,7 +97,7 @@ public class OszUnpacker {
} catch (ZipException e) { } catch (ZipException e) {
String err = String.format("Failed to unzip file %s to dest %s.", file.getAbsolutePath(), dest.getAbsolutePath()); String err = String.format("Failed to unzip file %s to dest %s.", file.getAbsolutePath(), dest.getAbsolutePath());
Log.error(err, e); Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
} }
} }

View File

@ -33,8 +33,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* Handles connections and queries with the cached beatmap database. * Handles connections and queries with the cached beatmap database.
@ -89,17 +90,10 @@ public class BeatmapDB {
/** Current size of beatmap cache table. */ /** Current size of beatmap cache table. */
private static int cacheSize = -1; private static int cacheSize = -1;
// This class should not be instantiated.
private BeatmapDB() {}
private static Configuration config; // TODO
/** /**
* Initializes the database connection. * Initializes the database connection.
*/ */
public static void init(Configuration config) { public static void init() {
BeatmapDB.config = config;
// create a database connection // create a database connection
connection = DBController.createConnection(config.BEATMAP_DB.getPath()); connection = DBController.createConnection(config.BEATMAP_DB.getPath());
if (connection == null) if (connection == null)
@ -115,7 +109,7 @@ public class BeatmapDB {
try { try {
updateSizeStmt = connection.prepareStatement("REPLACE INTO info (key, value) VALUES ('size', ?)"); updateSizeStmt = connection.prepareStatement("REPLACE INTO info (key, value) VALUES ('size', ?)");
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to prepare beatmap statements.", e).show(); explode("Failed to prepare beatmap statements.", e, DEFAULT_OPTIONS);
} }
// retrieve the cache size // retrieve the cache size
@ -137,7 +131,7 @@ public class BeatmapDB {
updatePlayStatsStmt = connection.prepareStatement("UPDATE beatmaps SET playCount = ?, lastPlayed = ? WHERE dir = ? AND file = ?"); updatePlayStatsStmt = connection.prepareStatement("UPDATE beatmaps SET playCount = ?, lastPlayed = ? WHERE dir = ? AND file = ?");
setFavoriteStmt = connection.prepareStatement("UPDATE beatmaps SET favorite = ? WHERE dir = ? AND file = ?"); setFavoriteStmt = connection.prepareStatement("UPDATE beatmaps SET favorite = ? WHERE dir = ? AND file = ?");
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to prepare beatmap statements.", e).show(); explode("Failed to prepare beatmap statements.", e, DEFAULT_OPTIONS);
} }
} }
@ -175,7 +169,7 @@ public class BeatmapDB {
sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', '%s')", DATABASE_VERSION); sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', '%s')", DATABASE_VERSION);
stmt.executeUpdate(sql); stmt.executeUpdate(sql);
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Coudl not create beatmap database.", e).show(); explode("Could not create beatmap database.", e, DEFAULT_OPTIONS);
} }
} }
@ -227,7 +221,7 @@ public class BeatmapDB {
ps.close(); ps.close();
} }
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to update beatmap database.", e).show(); explode("Failed to update beatmap database.", e, DEFAULT_OPTIONS);
} }
} }
@ -245,7 +239,7 @@ public class BeatmapDB {
} }
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Could not get beatmap cache size.", e).show(); explode("Could not get beatmap cache size.", e, DEFAULT_OPTIONS);
} }
} }
@ -260,7 +254,7 @@ public class BeatmapDB {
updateSizeStmt.setString(1, Integer.toString(Math.max(cacheSize, 0))); updateSizeStmt.setString(1, Integer.toString(Math.max(cacheSize, 0)));
updateSizeStmt.executeUpdate(); updateSizeStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Could not update beatmap cache size.", e).show(); explode("Could not update beatmap cache size.", e, DEFAULT_OPTIONS);
} }
} }
@ -278,7 +272,7 @@ public class BeatmapDB {
cacheSize = 0; cacheSize = 0;
updateCacheSize(); updateCacheSize();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Could not drop beatmap database.", e).show(); explode("Could not drop beatmap database.", e, DEFAULT_OPTIONS);
} }
createDatabase(); createDatabase();
} }
@ -296,7 +290,7 @@ public class BeatmapDB {
cacheSize += insertStmt.executeUpdate(); cacheSize += insertStmt.executeUpdate();
updateCacheSize(); updateCacheSize();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to add beatmap to database.", e).show(); explode("Failed to add beatmap to database.", e, DEFAULT_OPTIONS);
} }
} }
@ -349,7 +343,7 @@ public class BeatmapDB {
// update cache size // update cache size
updateCacheSize(); updateCacheSize();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to add beatmaps to database.", e).show(); explode("Failed to add beatmaps to database.", e, DEFAULT_OPTIONS);
} }
} }
@ -437,7 +431,7 @@ public class BeatmapDB {
} }
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to load Beatmap from database.", e).show(); explode("Failed to load Beatmap from database.", e, DEFAULT_OPTIONS);
} }
} }
@ -500,7 +494,7 @@ public class BeatmapDB {
} }
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to load beatmaps from database.", e).show(); explode("Failed to load beatmaps from database.", e, DEFAULT_OPTIONS);
} }
} }
@ -601,7 +595,7 @@ public class BeatmapDB {
rs.close(); rs.close();
return map; return map;
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to get last modified map from database.", e).show(); explode("Failed to get last modified map from database.", e, DEFAULT_OPTIONS);
return null; return null;
} }
} }
@ -621,7 +615,7 @@ public class BeatmapDB {
cacheSize -= deleteMapStmt.executeUpdate(); cacheSize -= deleteMapStmt.executeUpdate();
updateCacheSize(); updateCacheSize();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to delete beatmap entry from database.", e).show(); explode("Failed to delete beatmap entry from database.", e, DEFAULT_OPTIONS);
} }
} }
@ -638,7 +632,7 @@ public class BeatmapDB {
cacheSize -= deleteGroupStmt.executeUpdate(); cacheSize -= deleteGroupStmt.executeUpdate();
updateCacheSize(); updateCacheSize();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to delete beatmap group entry from database.", e).show(); explode("Failed to delete beatmap group entry from database.", e, DEFAULT_OPTIONS);
} }
} }
@ -656,8 +650,8 @@ public class BeatmapDB {
setStarsStmt.setString(3, beatmap.getFile().getName()); setStarsStmt.setString(3, beatmap.getFile().getName());
setStarsStmt.executeUpdate(); setStarsStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error(String.format("Failed to save star rating '%.4f' for beatmap '%s' in database.", explode(String.format("Failed to save star rating '%.4f' for beatmap '%s' in database.",
beatmap.starRating, beatmap.toString()), e).show(); beatmap.starRating, beatmap.toString()), e, DEFAULT_OPTIONS);
} }
} }
@ -676,8 +670,8 @@ public class BeatmapDB {
updatePlayStatsStmt.setString(4, beatmap.getFile().getName()); updatePlayStatsStmt.setString(4, beatmap.getFile().getName());
updatePlayStatsStmt.executeUpdate(); updatePlayStatsStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error(String.format("Failed to update play statistics for beatmap '%s' in database.", explode(String.format("Failed to update play statistics for beatmap '%s' in database.",
beatmap.toString()), e).show(); beatmap.toString()), e, DEFAULT_OPTIONS);
} }
} }
@ -695,8 +689,8 @@ public class BeatmapDB {
setFavoriteStmt.setString(3, beatmap.getFile().getName()); setFavoriteStmt.setString(3, beatmap.getFile().getName());
setFavoriteStmt.executeUpdate(); setFavoriteStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error(String.format("Failed to update favorite status for beatmap '%s' in database.", explode(String.format("Failed to update favorite status for beatmap '%s' in database.",
beatmap.toString()), e).show(); beatmap.toString()), e, DEFAULT_OPTIONS);
} }
} }
@ -716,7 +710,7 @@ public class BeatmapDB {
connection.close(); connection.close();
connection = null; connection = null;
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to close beatmap database.", e).show(); explode("Failed to close beatmap database.", e, DEFAULT_OPTIONS);
} }
} }
} }

View File

@ -18,13 +18,12 @@
package itdelatrisu.opsu.db; package itdelatrisu.opsu.db;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
/** /**
* Database controller. * Database controller.
*/ */
@ -35,17 +34,17 @@ public class DBController {
/** /**
* Initializes all databases. * Initializes all databases.
*/ */
public static void init(Configuration config) { public static void init() {
// load the sqlite-JDBC driver using the current class loader // load the sqlite-JDBC driver using the current class loader
try { try {
Class.forName("org.sqlite.JDBC"); Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
ErrorHandler.error("Could not load sqlite-JDBC driver.", e).show(); explode("Could not load sqlite-JDBC driver.", e, DEFAULT_OPTIONS);
} }
// initialize the databases // initialize the databases
BeatmapDB.init(config); BeatmapDB.init();
ScoreDB.init(config); ScoreDB.init();
} }
/** /**
@ -66,7 +65,7 @@ public class DBController {
return DriverManager.getConnection(String.format("jdbc:sqlite:%s", path)); return DriverManager.getConnection(String.format("jdbc:sqlite:%s", path));
} catch (SQLException e) { } catch (SQLException e) {
// if the error message is "out of memory", it probably means no database file is found // 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).show(); explode(String.format("Could not connect to database: '%s'.", path), e, DEFAULT_OPTIONS);
return null; return null;
} }
} }

View File

@ -20,8 +20,6 @@ package itdelatrisu.opsu.db;
import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -36,6 +34,9 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* Handles connections and queries with the scores database. * Handles connections and queries with the scores database.
*/ */
@ -77,13 +78,10 @@ public class ScoreDB {
/** Score deletion statement. */ /** Score deletion statement. */
private static PreparedStatement deleteSongStmt, deleteScoreStmt; private static PreparedStatement deleteSongStmt, deleteScoreStmt;
// This class should not be instantiated.
private ScoreDB() {}
/** /**
* Initializes the database connection. * Initializes the database connection.
*/ */
public static void init(Configuration config) { public static void init() {
// create a database connection // create a database connection
connection = DBController.createConnection(config.SCORE_DB.getPath()); connection = DBController.createConnection(config.SCORE_DB.getPath());
if (connection == null) if (connection == null)
@ -124,7 +122,7 @@ public class ScoreDB {
// TODO: extra playerName checks not needed if name is guaranteed not null // TODO: extra playerName checks not needed if name is guaranteed not null
); );
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to prepare score statements.", e).show(); explode("Failed to prepare score statements.", e, DEFAULT_OPTIONS);
} }
} }
@ -157,7 +155,7 @@ public class ScoreDB {
sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', %d)", DATABASE_VERSION); sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', %d)", DATABASE_VERSION);
stmt.executeUpdate(sql); stmt.executeUpdate(sql);
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Could not create score database.", e).show(); explode("Could not create score database.", e, DEFAULT_OPTIONS);
} }
} }
@ -209,7 +207,7 @@ public class ScoreDB {
ps.close(); ps.close();
} }
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to update score database.", e).show(); explode("Failed to update score database.", e, DEFAULT_OPTIONS);
} }
} }
@ -227,7 +225,7 @@ public class ScoreDB {
insertStmt.setString(19, data.playerName); insertStmt.setString(19, data.playerName);
insertStmt.executeUpdate(); insertStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to save score to database.", e).show(); explode("Failed to save score to database.", e, DEFAULT_OPTIONS);
} }
} }
@ -247,7 +245,7 @@ public class ScoreDB {
deleteScoreStmt.setString(21, data.playerName); deleteScoreStmt.setString(21, data.playerName);
deleteScoreStmt.executeUpdate(); deleteScoreStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to delete score from database.", e).show(); explode("Failed to delete score from database.", e, DEFAULT_OPTIONS);
} }
} }
@ -267,7 +265,7 @@ public class ScoreDB {
deleteSongStmt.setString(5, beatmap.version); deleteSongStmt.setString(5, beatmap.version);
deleteSongStmt.executeUpdate(); deleteSongStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to delete scores from database.", e).show(); explode("Failed to delete scores from database.", e, DEFAULT_OPTIONS);
} }
} }
@ -335,7 +333,7 @@ public class ScoreDB {
} }
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to read scores from database.", e).show(); explode("Failed to read scores from database.", e, DEFAULT_OPTIONS);
return null; return null;
} }
return getSortedArray(list); return getSortedArray(list);
@ -377,7 +375,7 @@ public class ScoreDB {
map.put(version, getSortedArray(list)); map.put(version, getSortedArray(list));
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to read scores from database.", e).show(); explode("Failed to read scores from database.", e, DEFAULT_OPTIONS);
return null; return null;
} }
return map; return map;
@ -406,7 +404,7 @@ public class ScoreDB {
connection.close(); connection.close();
connection = null; connection = null;
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to close score database.", e).show(); explode("Failed to close score database.", e, DEFAULT_OPTIONS);
} }
} }
} }

View File

@ -33,10 +33,11 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
/** /**
* File download. * File download.
@ -92,7 +93,7 @@ public class Download {
private String localPath; private String localPath;
/** The local path to rename the file to when finished. */ /** The local path to rename the file to when finished. */
private String rename; private String renamedFileName;
/** The download URL. */ /** The download URL. */
private URL url; private URL url;
@ -137,18 +138,20 @@ public class Download {
* Constructor. * Constructor.
* @param remoteURL the download URL * @param remoteURL the download URL
* @param localPath the path to save the download * @param localPath the path to save the download
* @param rename the file name to rename the download to when complete * @param renamedFileName the file name to rename the download to when complete
*/ */
public Download(String remoteURL, String localPath, String rename) { public Download(String remoteURL, String localPath, String renamedFileName) {
try { try {
this.url = new URL(remoteURL); this.url = new URL(remoteURL);
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
this.status = Status.ERROR; this.status = Status.ERROR;
ErrorHandler.error(String.format("Bad download URL: '%s'", remoteURL), e).show(); explode(String.format("Bad download URL: '%s'", remoteURL), e, DEFAULT_OPTIONS);
return; return;
} }
this.localPath = localPath; this.localPath = localPath;
this.rename = Utils.cleanFileName(rename, '-'); if (renamedFileName != null) {
this.renamedFileName = Utils.cleanFileName(renamedFileName, '-');
}
} }
/** /**
@ -159,7 +162,7 @@ public class Download {
/** /**
* Returns the local path to save the download (after renamed). * Returns the local path to save the download (after renamed).
*/ */
public String getLocalPath() { return (rename != null) ? rename : localPath; } public String getLocalPath() { return (renamedFileName != null) ? renamedFileName : localPath; }
/** /**
* Sets the download listener. * Sets the download listener.
@ -217,7 +220,7 @@ public class Download {
else if (redirectCount > MAX_REDIRECTS) else if (redirectCount > MAX_REDIRECTS)
error = String.format("Download for URL '%s' is attempting too many redirects (over %d).", base.toString(), MAX_REDIRECTS); error = String.format("Download for URL '%s' is attempting too many redirects (over %d).", base.toString(), MAX_REDIRECTS);
if (error != null) { if (error != null) {
EventBus.post(new BubbleNotificationEvent(error, BubbleNotificationEvent.COLOR_ORANGE)); BubNotifListener.EVENT.make().onBubNotif(error, Colors.BUB_ORANGE);
throw new IOException(); throw new IOException();
} }
@ -263,9 +266,9 @@ public class Download {
status = Status.COMPLETE; status = Status.COMPLETE;
rbc.close(); rbc.close();
fos.close(); fos.close();
if (rename != null) { if (renamedFileName != null) {
Path source = new File(localPath).toPath(); Path source = new File(localPath).toPath();
Files.move(source, source.resolveSibling(rename), StandardCopyOption.REPLACE_EXISTING); Files.move(source, source.resolveSibling(renamedFileName), StandardCopyOption.REPLACE_EXISTING);
} }
if (listener != null) if (listener != null)
listener.completed(); listener.completed();
@ -419,7 +422,7 @@ public class Download {
} }
} catch (IOException e) { } catch (IOException e) {
this.status = Status.ERROR; this.status = Status.ERROR;
ErrorHandler.error("Failed to cancel download.", e).show(); explode("Failed to cancel download.", e, DEFAULT_OPTIONS);
} }
} }
} }

View File

@ -33,12 +33,10 @@ import java.io.File;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.core.inject.Inject; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -46,9 +44,6 @@ import static yugecin.opsudance.options.Options.*;
*/ */
public class DownloadNode { public class DownloadNode {
@Inject
private Configuration config;
/** The associated Download object. */ /** The associated Download object. */
private Download download; private Download download;
@ -285,12 +280,14 @@ public class DownloadNode {
download.setListener(new DownloadListener() { download.setListener(new DownloadListener() {
@Override @Override
public void completed() { public void completed() {
EventBus.post(new BarNotificationEvent(String.format("Download complete: %s", getTitle()))); BarNotifListener.EVENT.make().onBarNotif(
String.format("Download complete: %s", getTitle()));
} }
@Override @Override
public void error() { public void error() {
EventBus.post(new BarNotificationEvent("Download failed due to a connection error.")); BarNotifListener.EVENT.make().onBarNotif(
"Download failed due to a connection error.");
} }
}); });
this.download = download; this.download = download;
@ -412,7 +409,9 @@ public class DownloadNode {
public void drawDownload(Graphics g, float position, int id, boolean hover) { public void drawDownload(Graphics g, float position, int id, boolean hover) {
Download download = this.download; // in case clearDownload() is called asynchronously Download download = this.download; // in case clearDownload() is called asynchronously
if (download == null) { if (download == null) {
EventBus.post(new BubbleNotificationEvent("Trying to draw download information for button without Download object", BubbleNotificationEvent.COLOR_ORANGE)); BubNotifListener.EVENT.make().onBubNotif(
"Trying to draw download information for button without Download object",
Colors.BUB_ORANGE);
return; return;
} }

View File

@ -35,26 +35,17 @@ import java.util.Properties;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.Constants;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BarNotificationEvent; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import yugecin.opsudance.options.Configuration; import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* Handles automatic program updates. * Handles automatic program updates.
*/ */
public class Updater { public class Updater {
@Inject
private Configuration config;
private static Updater updater;
public static Updater get() {
return updater;
}
/** The exit confirmation message. */ /** The exit confirmation message. */
public static final String EXIT_CONFIRMATION = "An opsu! update is being downloaded.\nAre you sure you want to quit opsu!?"; public static final String EXIT_CONFIRMATION = "An opsu! update is being downloaded.\nAre you sure you want to quit opsu!?";
@ -95,7 +86,7 @@ public class Updater {
* Returns the status description. * Returns the status description.
*/ */
public String getDescription() { return description; } public String getDescription() { return description; }
}; }
/** The current updater status. */ /** The current updater status. */
private Status status; private Status status;
@ -119,10 +110,8 @@ public class Updater {
return currentVersion.getMajorVersion() + "." + currentVersion.getMinorVersion() + "." + currentVersion.getIncrementalVersion(); return currentVersion.getMajorVersion() + "." + currentVersion.getMinorVersion() + "." + currentVersion.getIncrementalVersion();
} }
@Inject
public Updater() { public Updater() {
status = Status.INITIAL; status = Status.INITIAL;
updater = this;
} }
/** /**
@ -145,7 +134,7 @@ public class Updater {
Date date = null; Date date = null;
try { try {
Properties props = new Properties(); Properties props = new Properties();
props.load(ResourceLoader.getResourceAsStream(config.VERSION_FILE)); props.load(ResourceLoader.getResourceAsStream(Constants.VERSION_FILE));
String build = props.getProperty("build.date"); String build = props.getProperty("build.date");
if (build == null || build.equals("${timestamp}") || build.equals("${maven.build.timestamp}")) if (build == null || build.equals("${timestamp}") || build.equals("${maven.build.timestamp}"))
date = new Date(); date = new Date();
@ -214,16 +203,16 @@ public class Updater {
// get current version // get current version
Properties props = new Properties(); Properties props = new Properties();
props.load(ResourceLoader.getResourceAsStream(config.VERSION_FILE)); props.load(ResourceLoader.getResourceAsStream(Constants.VERSION_FILE));
if ((currentVersion = getVersion(props)) == null) if ((currentVersion = getVersion(props)) == null)
return; return;
// get latest version // get latest version
String s = null; String s = null;
try { try {
s = Utils.readDataFromUrl(new URL(config.VERSION_REMOTE)); s = Utils.readDataFromUrl(new URL(Constants.VERSION_REMOTE));
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
Log.warn(String.format("Check for updates failed. Please check your internet connection, or your connection to %s.", config.VERSION_REMOTE)); Log.warn(String.format("Check for updates failed. Please check your internet connection, or your connection to %s.", Constants.VERSION_REMOTE));
} }
if (s == null) { if (s == null) {
status = Status.CONNECTION_ERROR; status = Status.CONNECTION_ERROR;
@ -252,13 +241,14 @@ public class Updater {
@Override @Override
public void completed() { public void completed() {
status = Status.UPDATE_DOWNLOADED; status = Status.UPDATE_DOWNLOADED;
EventBus.post(new BarNotificationEvent("Update has finished downloading")); BarNotifListener.EVENT.make().onBarNotif("Update has finished downloading");
} }
@Override @Override
public void error() { public void error() {
status = Status.CONNECTION_ERROR; status = Status.CONNECTION_ERROR;
EventBus.post(new BarNotificationEvent("Update failed due to a connection error.")); BarNotifListener.EVENT.make().onBarNotif(
"Update failed due to a connection error.");
} }
}); });
} }
@ -301,7 +291,7 @@ public class Updater {
pb.start(); pb.start();
} catch (IOException e) { } catch (IOException e) {
status = Status.INTERNAL_ERROR; status = Status.INTERNAL_ERROR;
ErrorHandler.error("Failed to start new process.", e).show(); explode("Failed to start new process.", e, DEFAULT_OPTIONS);
} }
} }
} }

View File

@ -33,18 +33,14 @@ import java.util.Date;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import yugecin.opsudance.core.inject.InstanceContainer;
/** /**
* Download server: http://bloodcat.com/osu/ * Download server: http://bloodcat.com/osu/
*/ */
public class BloodcatServer extends DownloadServer { public class BloodcatServer extends DownloadServer {
@Inject
public InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "Bloodcat"; private static final String SERVER_NAME = "Bloodcat";
@ -60,10 +56,6 @@ public class BloodcatServer extends DownloadServer {
/** Total result count from the last query. */ /** Total result count from the last query. */
private int totalResults = -1; private int totalResults = -1;
@Inject
public BloodcatServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -89,12 +81,12 @@ public class BloodcatServer extends DownloadServer {
nodes = new DownloadNode[arr.length()]; nodes = new DownloadNode[arr.length()];
for (int i = 0; i < nodes.length; i++) { for (int i = 0; i < nodes.length; i++) {
JSONObject item = arr.getJSONObject(i); JSONObject item = arr.getJSONObject(i);
nodes[i] = instanceContainer.injectFields(new DownloadNode( nodes[i] = new DownloadNode(
item.getInt("id"), formatDate(item.getString("synced")), //"date" item.getInt("id"), formatDate(item.getString("synced")), //"date"
item.getString("title"), item.isNull("titleU") ? null : item.getString("titleU"), //"titleUnicode" item.getString("title"), item.isNull("titleU") ? null : item.getString("titleU"), //"titleUnicode"
item.getString("artist"), item.isNull("artistU") ? null : item.getString("artistU"), //"artistUnicode" item.getString("artist"), item.isNull("artistU") ? null : item.getString("artistU"), //"artistUnicode"
item.getString("creator") item.getString("creator")
)); );
} }
// store total result count // store total result count
@ -104,7 +96,7 @@ public class BloodcatServer extends DownloadServer {
resultCount++; resultCount++;
this.totalResults = resultCount; this.totalResults = resultCount;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); explode(String.format("Problem loading result list for query '%s'.", query), e, DEFAULT_OPTIONS);
} }
return nodes; return nodes;
} }

View File

@ -29,9 +29,8 @@ import java.net.URLEncoder;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import yugecin.opsudance.core.inject.InstanceContainer;
/** /**
* Download server: https://osu.hexide.com/ * Download server: https://osu.hexide.com/
@ -40,9 +39,6 @@ import yugecin.opsudance.core.inject.InstanceContainer;
*/ */
public class HexideServer extends DownloadServer { public class HexideServer extends DownloadServer {
@Inject
private InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "Hexide"; private static final String SERVER_NAME = "Hexide";
@ -64,10 +60,6 @@ public class HexideServer extends DownloadServer {
/** Total result count from the last query. */ /** Total result count from the last query. */
private int totalResults = -1; private int totalResults = -1;
@Inject
public HexideServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -124,10 +116,10 @@ public class HexideServer extends DownloadServer {
artist = creator = "?"; artist = creator = "?";
} }
} }
nodes[i] = instanceContainer.injectFields(new DownloadNode( nodes[i] = new DownloadNode(
item.getInt("ranked_id"), item.getString("date"), item.getInt("ranked_id"), item.getString("date"),
title, null, artist, null, creator title, null, artist, null, creator
)); );
} }
// store total result count // store total result count
@ -135,7 +127,7 @@ public class HexideServer extends DownloadServer {
// all results at once; this approach just gets pagination correct. // all results at once; this approach just gets pagination correct.
this.totalResults = arr.length() + resultIndex; this.totalResults = arr.length() + resultIndex;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); explode(String.format("Problem loading result list for query '%s'.", query), e, DEFAULT_OPTIONS);
} }
return nodes; return nodes;
} }

View File

@ -29,18 +29,14 @@ import java.net.URLEncoder;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import yugecin.opsudance.core.inject.InstanceContainer;
/** /**
* Download server: http://osu.mengsky.net/ * Download server: http://osu.mengsky.net/
*/ */
public class MengSkyServer extends DownloadServer { public class MengSkyServer extends DownloadServer {
@Inject
private InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "MengSky"; private static final String SERVER_NAME = "MengSky";
@ -56,10 +52,6 @@ public class MengSkyServer extends DownloadServer {
/** Total result count from the last query. */ /** Total result count from the last query. */
private int totalResults = -1; private int totalResults = -1;
@Inject
public MengSkyServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -93,10 +85,10 @@ public class MengSkyServer extends DownloadServer {
// sometimes titleU is artistU instead of the proper title // sometimes titleU is artistU instead of the proper title
if (titleU.equals(artistU) && !titleU.equals(title)) if (titleU.equals(artistU) && !titleU.equals(title))
titleU = title; titleU = title;
nodes[i] = instanceContainer.injectFields(new DownloadNode( nodes[i] = new DownloadNode(
item.getInt("id"), item.getString("syncedDateTime"), item.getInt("id"), item.getString("syncedDateTime"),
title, titleU, artist, artistU, creator title, titleU, artist, artistU, creator
)); );
} }
// store total result count // store total result count
@ -107,7 +99,7 @@ public class MengSkyServer extends DownloadServer {
} }
this.totalResults = resultCount; this.totalResults = resultCount;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); explode(String.format("Problem loading result list for query '%s'.", query), e, DEFAULT_OPTIONS);
} }
return nodes; return nodes;
} }

View File

@ -20,9 +20,6 @@ package itdelatrisu.opsu.downloads.servers;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.DownloadNode; import itdelatrisu.opsu.downloads.DownloadNode;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@ -34,14 +31,13 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
/** /**
* Download server: http://osu.uu.gl/ * Download server: http://osu.uu.gl/
*/ */
public class MnetworkServer extends DownloadServer { public class MnetworkServer extends DownloadServer {
@Inject
private InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "Mnetwork"; private static final String SERVER_NAME = "Mnetwork";
@ -57,10 +53,6 @@ public class MnetworkServer extends DownloadServer {
/** Beatmap pattern. */ /** Beatmap pattern. */
private Pattern BEATMAP_PATTERN = Pattern.compile("^(\\d+) ([^-]+) - (.+)\\.osz$"); private Pattern BEATMAP_PATTERN = Pattern.compile("^(\\d+) ([^-]+) - (.+)\\.osz$");
@Inject
public MnetworkServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -119,7 +111,7 @@ public class MnetworkServer extends DownloadServer {
if (!m.matches()) if (!m.matches())
continue; continue;
nodeList.add(instanceContainer.injectFields(new DownloadNode(Integer.parseInt(m.group(1)), date, m.group(3), null, m.group(2), null, ""))); nodeList.add(new DownloadNode(Integer.parseInt(m.group(1)), date, m.group(3), null, m.group(2), null, ""));
} }
nodes = nodeList.toArray(new DownloadNode[nodeList.size()]); nodes = nodeList.toArray(new DownloadNode[nodeList.size()]);
@ -127,7 +119,7 @@ public class MnetworkServer extends DownloadServer {
// store total result count // store total result count
this.totalResults = nodes.length; this.totalResults = nodes.length;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); explode(String.format("Problem loading result list for query '%s'.", query), e, DEFAULT_OPTIONS);
} }
return nodes; return nodes;
} }

View File

@ -35,9 +35,8 @@ import java.util.TimeZone;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import yugecin.opsudance.core.inject.InstanceContainer;
/** /**
* Download server: http://loli.al/ * Download server: http://loli.al/
@ -46,9 +45,6 @@ import yugecin.opsudance.core.inject.InstanceContainer;
*/ */
public class OsuMirrorServer extends DownloadServer { public class OsuMirrorServer extends DownloadServer {
@Inject
private InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "osu!Mirror"; private static final String SERVER_NAME = "osu!Mirror";
@ -73,10 +69,6 @@ public class OsuMirrorServer extends DownloadServer {
/** Lookup table from beatmap set ID -> server download ID. */ /** Lookup table from beatmap set ID -> server download ID. */
private HashMap<Integer, Integer> idTable = new HashMap<Integer, Integer>(); private HashMap<Integer, Integer> idTable = new HashMap<Integer, Integer>();
@Inject
public OsuMirrorServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -113,12 +105,12 @@ public class OsuMirrorServer extends DownloadServer {
JSONObject item = arr.getJSONObject(i); JSONObject item = arr.getJSONObject(i);
int beatmapSetID = item.getInt("OSUSetid"); int beatmapSetID = item.getInt("OSUSetid");
int serverID = item.getInt("id"); int serverID = item.getInt("id");
nodes[i] = instanceContainer.injectFields(new DownloadNode( nodes[i] = new DownloadNode(
beatmapSetID, formatDate(item.getString("ModifyDate")), beatmapSetID, formatDate(item.getString("ModifyDate")),
item.getString("Title"), null, item.getString("Title"), null,
item.getString("Artist"), null, item.getString("Artist"), null,
item.getString("Mapper") item.getString("Mapper")
)); );
idTable.put(beatmapSetID, serverID); idTable.put(beatmapSetID, serverID);
if (serverID > maxServerID) if (serverID > maxServerID)
maxServerID = serverID; maxServerID = serverID;
@ -130,7 +122,7 @@ public class OsuMirrorServer extends DownloadServer {
else else
this.totalResults = maxServerID; this.totalResults = maxServerID;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); explode(String.format("Problem loading result list for query '%s'.", query), e, DEFAULT_OPTIONS);
} }
return nodes; return nodes;
} }

View File

@ -33,18 +33,14 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import yugecin.opsudance.core.inject.InstanceContainer;
/** /**
* Download server: http://osu.yas-online.net/ * Download server: http://osu.yas-online.net/
*/ */
public class YaSOnlineServer extends DownloadServer { public class YaSOnlineServer extends DownloadServer {
@Inject
public InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "YaS Online"; private static final String SERVER_NAME = "YaS Online";
@ -72,10 +68,6 @@ public class YaSOnlineServer extends DownloadServer {
/** Max server download ID seen (for approximating total pages). */ /** Max server download ID seen (for approximating total pages). */
private int maxServerID = 0; private int maxServerID = 0;
@Inject
public YaSOnlineServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -121,7 +113,8 @@ public class YaSOnlineServer extends DownloadServer {
String downloadLink = item.getString("downloadLink"); String downloadLink = item.getString("downloadLink");
return String.format(DOWNLOAD_FETCH_URL, downloadLink); return String.format(DOWNLOAD_FETCH_URL, downloadLink);
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem retrieving download URL for beatmap '%d'.", beatmapSetID), e).show(); explode(String.format("Problem retrieving download URL for beatmap '%d'.", beatmapSetID), e,
DEFAULT_OPTIONS);
return null; return null;
} finally { } finally {
Utils.setSSLCertValidation(true); Utils.setSSLCertValidation(true);
@ -183,7 +176,7 @@ public class YaSOnlineServer extends DownloadServer {
if (serverID > maxServerID) if (serverID > maxServerID)
maxServerID = serverID; maxServerID = serverID;
nodeList.add(instanceContainer.injectFields(new DownloadNode(item.getInt("mapid"), date, title, null, artist, null, ""))); nodeList.add(new DownloadNode(item.getInt("mapid"), date, title, null, artist, null, ""));
} }
nodes = nodeList.toArray(new DownloadNode[nodeList.size()]); nodes = nodeList.toArray(new DownloadNode[nodeList.size()]);
@ -193,7 +186,7 @@ public class YaSOnlineServer extends DownloadServer {
else else
this.totalResults = maxServerID; this.totalResults = maxServerID;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); explode(String.format("Problem loading result list for query '%s'.", query), e, DEFAULT_OPTIONS);
} finally { } finally {
Utils.setSSLCertValidation(true); Utils.setSSLCertValidation(true);
} }

View File

@ -30,9 +30,8 @@ import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import yugecin.opsudance.Dancer; import yugecin.opsudance.Dancer;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.render.GameObjectRenderer;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -40,9 +39,6 @@ import static yugecin.opsudance.options.Options.*;
*/ */
public class Circle extends GameObject { public class Circle extends GameObject {
@Inject
private GameObjectRenderer gameObjectRenderer;
/** The associated HitObject. */ /** The associated HitObject. */
private HitObject hitObject; private HitObject hitObject;
@ -156,7 +152,7 @@ public class Circle extends GameObject {
@Override @Override
public boolean mousePressed(int x, int y, int trackPosition) { public boolean mousePressed(int x, int y, int trackPosition) {
double distance = Math.hypot(this.x - x, this.y - y); double distance = Math.hypot(this.x - x, this.y - y);
if (distance < gameObjectRenderer.getCircleDiameter() / 2) { if (distance < gameObjectRenderer.circleDiameter / 2) {
int timeDiff = trackPosition - hitObject.getTime(); int timeDiff = trackPosition - hitObject.getTime();
int result = hitResult(timeDiff); int result = hitResult(timeDiff);

View File

@ -35,11 +35,9 @@ import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.Dancer; import yugecin.opsudance.Dancer;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.skinning.SkinService; import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -47,12 +45,6 @@ import static yugecin.opsudance.options.Options.*;
*/ */
public class Slider extends GameObject { public class Slider extends GameObject {
@Inject
private DisplayContainer displayContainer;
@Inject
private GameObjectRenderer gameObjectRenderer;
/** Slider ball frames. */ /** Slider ball frames. */
private static Image[] sliderBallImages; private static Image[] sliderBallImages;
@ -605,7 +597,7 @@ public class Slider extends GameObject {
return false; return false;
double distance = Math.hypot(this.x - x, this.y - y); double distance = Math.hypot(this.x - x, this.y - y);
if (distance < gameObjectRenderer.getCircleDiameter() / 2) { if (distance < gameObjectRenderer.circleDiameter / 2) {
int timeDiff = Math.abs(trackPosition - hitObject.getTime()); int timeDiff = Math.abs(trackPosition - hitObject.getTime());
int[] hitResultOffset = game.getHitResultOffsets(); int[] hitResultOffset = game.getHitResultOffsets();

View File

@ -37,8 +37,8 @@ import org.lwjgl.opengl.GL20;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.render.GameObjectRenderer;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -316,7 +316,7 @@ public class CurveRenderState {
double diff_x = x - last_x; double diff_x = x - last_x;
double diff_y = y - last_y; double diff_y = y - last_y;
float dist = Utils.distance(x, y, last_x, last_y); float dist = Utils.distance(x, y, last_x, last_y);
if (dist < GameObjectRenderer.instance.getCircleDiameter() / 8) { if (dist < gameObjectRenderer.circleDiameter / 8) {
x = (float) (x - diff_x / 2); x = (float) (x - diff_x / 2);
y = (float) (y - diff_y / 2); y = (float) (y - diff_y / 2);
} else { } else {

View File

@ -39,15 +39,15 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import itdelatrisu.opsu.ui.Colors;
import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream; import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import lzma.streams.LzmaOutputStream; import lzma.streams.LzmaOutputStream;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import yugecin.opsudance.events.BubbleNotificationEvent; import static yugecin.opsudance.core.InstanceContainer.*;
import yugecin.opsudance.options.Configuration;
/** /**
* Captures osu! replay data. * Captures osu! replay data.
@ -57,9 +57,6 @@ import yugecin.opsudance.options.Configuration;
*/ */
public class Replay { public class Replay {
@Inject
public Configuration config;
/** The associated file. */ /** The associated file. */
private File file; private File file;
@ -278,7 +275,7 @@ public class Replay {
public void save() { public void save() {
// create replay directory // create replay directory
if (!config.replayDir.isDirectory() && !config.replayDir.mkdir()) { if (!config.replayDir.isDirectory() && !config.replayDir.mkdir()) {
EventBus.post(new BubbleNotificationEvent("Failed to create replay directory.", BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif("Failed to create replay directory", Colors.BUB_RED);
return; return;
} }
@ -347,7 +344,7 @@ public class Replay {
compressedOut.write(bytes); compressedOut.write(bytes);
} catch (IOException e) { } catch (IOException e) {
// possible OOM: https://github.com/jponge/lzma-java/issues/9 // possible OOM: https://github.com/jponge/lzma-java/issues/9
ErrorHandler.error("LZMA compression failed (possible out-of-memory error).", e).show(); explode("LZMA compression failed (possible out-of-memory error).", e, DEFAULT_OPTIONS);
} }
compressedOut.close(); compressedOut.close();
bout.close(); bout.close();
@ -361,7 +358,7 @@ public class Replay {
writer.close(); writer.close();
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error("Could not save replay data.", e).show(); explode("Could not save replay data.", e, DEFAULT_OPTIONS);
} }
} }
}.start(); }.start();

View File

@ -28,24 +28,17 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer; import static yugecin.opsudance.core.InstanceContainer.*;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
/** /**
* Importer for replay files. * Importer for replay files.
*/ */
public class ReplayImporter { public class ReplayImporter {
@Inject
private InstanceContainer instanceContainer;
@Inject
private Configuration config;
/** The subdirectory (within the replay import directory) to move replays that could not be imported. */ /** The subdirectory (within the replay import directory) to move replays that could not be imported. */
private final String FAILED_IMPORT_DIR = "failed"; private final String FAILED_IMPORT_DIR = "failed";
@ -55,10 +48,6 @@ public class ReplayImporter {
/** The total number of replays to import. */ /** The total number of replays to import. */
private File[] files; private File[] files;
@Inject
public ReplayImporter() {
}
/** /**
* Invokes the importer for each OSR file in the replay import dir, adding the replay * Invokes the importer for each OSR file in the replay import dir, adding the replay
* to the score database and moving the file into the replay directory. * to the score database and moving the file into the replay directory.
@ -80,21 +69,21 @@ public class ReplayImporter {
if (!config.replayDir.isDirectory() && !config.replayDir.mkdir()) { if (!config.replayDir.isDirectory() && !config.replayDir.mkdir()) {
String err = String.format("Failed to create replay directory '%s'.", config.replayDir.getAbsolutePath()); String err = String.format("Failed to create replay directory '%s'.", config.replayDir.getAbsolutePath());
Log.error(err); Log.error(err);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
return; return;
} }
// import OSRs // import OSRs
for (File file : files) { for (File file : files) {
fileIndex++; fileIndex++;
Replay r = instanceContainer.injectFields(new Replay(file)); Replay r = new Replay(file);
try { try {
r.loadHeader(); r.loadHeader();
} catch (IOException e) { } catch (IOException e) {
moveToFailedDirectory(file); moveToFailedDirectory(file);
String err = String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName()); String err = String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName());
Log.error(err, e); Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
continue; continue;
} }
Beatmap beatmap = BeatmapSetList.get().getBeatmapFromHash(r.beatmapHash); Beatmap beatmap = BeatmapSetList.get().getBeatmapFromHash(r.beatmapHash);
@ -113,7 +102,7 @@ public class ReplayImporter {
moveToFailedDirectory(file); moveToFailedDirectory(file);
String err = String.format("Failed to import replay '%s'. The associated beatmap could not be found.", file.getName()); String err = String.format("Failed to import replay '%s'. The associated beatmap could not be found.", file.getName());
Log.error(err); Log.error(err);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
} }
} }

View File

@ -29,10 +29,10 @@ import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Loads skin configuration files. * Loads skin configuration files.
@ -293,7 +293,7 @@ public class SkinLoader {
} catch (IOException e) { } catch (IOException e) {
String err = String.format("Failed to read file '%s'.", skinFile.getAbsolutePath()); String err = String.format("Failed to read file '%s'.", skinFile.getAbsolutePath());
Log.error(err, e); Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
} }
return skin; return skin;

View File

@ -35,14 +35,15 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.BaseOpsuState;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* Generic button menu state. * Generic button menu state.
* <p> * <p>
@ -68,7 +69,7 @@ public class ButtonMenu extends BaseOpsuState {
BEATMAP (new Button[] { Button.CLEAR_SCORES, Button.FAVORITE_ADD, Button.DELETE, Button.CANCEL }) { BEATMAP (new Button[] { Button.CLEAR_SCORES, Button.FAVORITE_ADD, Button.DELETE, Button.CANCEL }) {
@Override @Override
public String[] getTitle() { public String[] getTitle() {
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); BeatmapSetNode node = buttonState.getNode();
String beatmapString = (node != null) ? BeatmapSetList.get().getBaseNode(node.index).toString() : ""; String beatmapString = (node != null) ? BeatmapSetList.get().getBaseNode(node.index).toString() : "";
return new String[] { beatmapString, "What do you want to do with this beatmap?" }; return new String[] { beatmapString, "What do you want to do with this beatmap?" };
} }
@ -99,7 +100,7 @@ public class ButtonMenu extends BaseOpsuState {
BEATMAP_DELETE_SELECT (new Button[] { Button.DELETE_GROUP, Button.DELETE_SONG, Button.CANCEL_DELETE }) { BEATMAP_DELETE_SELECT (new Button[] { Button.DELETE_GROUP, Button.DELETE_SONG, Button.CANCEL_DELETE }) {
@Override @Override
public String[] getTitle() { public String[] getTitle() {
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); BeatmapSetNode node = buttonState.getNode();
String beatmapString = (node != null) ? node.toString() : ""; String beatmapString = (node != null) ? node.toString() : "";
return new String[] { String.format("Are you sure you wish to delete '%s' from disk?", beatmapString) }; return new String[] { String.format("Are you sure you wish to delete '%s' from disk?", beatmapString) };
} }
@ -179,7 +180,7 @@ public class ButtonMenu extends BaseOpsuState {
} }
@Override @Override
protected float getBaseY(DisplayContainer displayContainer) { protected float getBaseY() {
return displayContainer.height * 2f / 3; return displayContainer.height * 2f / 3;
} }
@ -268,9 +269,6 @@ public class ButtonMenu extends BaseOpsuState {
} }
}; };
public static DisplayContainer displayContainer;
public static InstanceContainer instanceContainer;
/** The buttons in the state. */ /** The buttons in the state. */
private final Button[] buttons; private final Button[] buttons;
@ -299,7 +297,7 @@ public class ButtonMenu extends BaseOpsuState {
*/ */
public void revalidate(Image button, Image buttonL, Image buttonR) { public void revalidate(Image button, Image buttonL, Image buttonR) {
float center = displayContainer.width / 2; float center = displayContainer.width / 2;
float baseY = getBaseY(displayContainer); float baseY = getBaseY();
float offsetY = button.getHeight() * 1.25f; float offsetY = button.getHeight() * 1.25f;
menuButtons = new MenuButton[buttons.length]; menuButtons = new MenuButton[buttons.length];
@ -314,7 +312,7 @@ public class ButtonMenu extends BaseOpsuState {
/** /**
* Returns the base Y coordinate for the buttons. * Returns the base Y coordinate for the buttons.
*/ */
protected float getBaseY(DisplayContainer displayContainer) { protected float getBaseY() {
float baseY = displayContainer.height * 0.2f; float baseY = displayContainer.height * 0.2f;
baseY += ((getTitle().length - 1) * Fonts.LARGE.getLineHeight()); baseY += ((getTitle().length - 1) * Fonts.LARGE.getLineHeight());
return baseY; return baseY;
@ -437,62 +435,62 @@ public class ButtonMenu extends BaseOpsuState {
@Override @Override
public void click() { public void click() {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
displayContainer.switchState(MainMenu.class); displayContainer.switchState(mainmenuState);
} }
}, },
CLEAR_SCORES ("Clear local scores", Color.magenta) { CLEAR_SCORES ("Clear local scores", Color.magenta) {
@Override @Override
public void click() { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); BeatmapSetNode node = buttonState.getNode();
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP, node); songMenuState.doStateActionOnLoad(MenuState.BEATMAP, node);
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} }
}, },
FAVORITE_ADD ("Add to Favorites", Color.blue) { FAVORITE_ADD ("Add to Favorites", Color.blue) {
@Override @Override
public void click() { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); BeatmapSetNode node = buttonState.getNode();
node.getBeatmapSet().setFavorite(true); node.getBeatmapSet().setFavorite(true);
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} }
}, },
FAVORITE_REMOVE ("Remove from Favorites", Color.blue) { FAVORITE_REMOVE ("Remove from Favorites", Color.blue) {
@Override @Override
public void click() { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); BeatmapSetNode node = buttonState.getNode();
node.getBeatmapSet().setFavorite(false); node.getBeatmapSet().setFavorite(false);
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_FAVORITE); songMenuState.doStateActionOnLoad(MenuState.BEATMAP_FAVORITE);
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} }
}, },
DELETE ("Delete...", Color.red) { DELETE ("Delete...", Color.red) {
@Override @Override
public void click() { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); BeatmapSetNode node = buttonState.getNode();
MenuState ms = (node.beatmapIndex == -1 || node.getBeatmapSet().size() == 1) ? MenuState ms = (node.beatmapIndex == -1 || node.getBeatmapSet().size() == 1) ?
MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT; MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT;
instanceContainer.provide(ButtonMenu.class).setMenuState(ms, node); buttonState.setMenuState(ms, node);
displayContainer.switchState(ButtonMenu.class); displayContainer.switchState(buttonState);
} }
}, },
CANCEL ("Cancel", Color.gray) { CANCEL ("Cancel", Color.gray) {
@Override @Override
public void click() { public void click() {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} }
}, },
DELETE_CONFIRM ("Yes, delete this beatmap!", Color.red) { DELETE_CONFIRM ("Yes, delete this beatmap!", Color.red) {
@Override @Override
public void click() { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); BeatmapSetNode node = buttonState.getNode();
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_DELETE_CONFIRM, node); songMenuState.doStateActionOnLoad(MenuState.BEATMAP_DELETE_CONFIRM, node);
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} }
}, },
DELETE_GROUP ("Yes, delete all difficulties!", Color.red) { DELETE_GROUP ("Yes, delete all difficulties!", Color.red) {
@ -505,9 +503,9 @@ public class ButtonMenu extends BaseOpsuState {
@Override @Override
public void click() { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode(); BeatmapSetNode node = buttonState.getNode();
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_DELETE_SELECT, node); songMenuState.doStateActionOnLoad(MenuState.BEATMAP_DELETE_SELECT, node);
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} }
}, },
CANCEL_DELETE ("Nooooo! I didn't mean to!", Color.gray) { CANCEL_DELETE ("Nooooo! I didn't mean to!", Color.gray) {
@ -520,8 +518,8 @@ public class ButtonMenu extends BaseOpsuState {
@Override @Override
public void click() { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.RELOAD); songMenuState.doStateActionOnLoad(MenuState.RELOAD);
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} }
}, },
RELOAD_CANCEL ("Cancel", Color.red) { RELOAD_CANCEL ("Cancel", Color.red) {
@ -534,9 +532,9 @@ public class ButtonMenu extends BaseOpsuState {
@Override @Override
public void click() { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
ScoreData scoreData = instanceContainer.provide(ButtonMenu.class).getScoreData(); ScoreData scoreData = buttonState.getScoreData();
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.SCORE, scoreData); songMenuState.doStateActionOnLoad(MenuState.SCORE, scoreData);
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} }
}, },
CLOSE ("Close", Color.gray) { CLOSE ("Close", Color.gray) {
@ -556,9 +554,6 @@ public class ButtonMenu extends BaseOpsuState {
} }
}; };
public static DisplayContainer displayContainer;
public static InstanceContainer instanceContainer;
/** The text to show on the button. */ /** The text to show on the button. */
private final String text; private final String text;
@ -600,12 +595,6 @@ public class ButtonMenu extends BaseOpsuState {
/** The score data to process in the state. */ /** The score data to process in the state. */
private ScoreData scoreData; private ScoreData scoreData;
public ButtonMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
super();
Button.displayContainer = MenuState.displayContainer = displayContainer;
Button.instanceContainer = MenuState.instanceContainer = instanceContainer;
}
@Override @Override
public void revalidate() { public void revalidate() {
super.revalidate(); super.revalidate();
@ -662,7 +651,7 @@ public class ButtonMenu extends BaseOpsuState {
return true; return true;
} }
if (key == Input.KEY_ESCAPE) { if (key == Keyboard.KEY_ESCAPE) {
menuState.leave(); menuState.leave();
return true; return true;
} }

View File

@ -22,10 +22,8 @@ import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.BeatmapSetList; import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapSetNode; import itdelatrisu.opsu.beatmap.BeatmapSetNode;
import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.downloads.Download; import itdelatrisu.opsu.downloads.Download;
import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.DownloadNode; import itdelatrisu.opsu.downloads.DownloadNode;
@ -54,12 +52,11 @@ import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException; import org.newdawn.slick.SlickException;
import org.newdawn.slick.gui.TextField; import org.newdawn.slick.gui.TextField;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.ComplexOpsuState; import yugecin.opsudance.core.state.ComplexOpsuState;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.options.Configuration;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* Downloads menu. * Downloads menu.
@ -69,18 +66,6 @@ import yugecin.opsudance.options.Configuration;
*/ */
public class DownloadsMenu extends ComplexOpsuState { public class DownloadsMenu extends ComplexOpsuState {
@Inject
private InstanceContainer instanceContainer;
@Inject
private Configuration config;
@Inject
private OszUnpacker oszUnpacker;
@Inject
private BeatmapParser beatmapParser;
/** Delay time, in milliseconds, between each search. */ /** Delay time, in milliseconds, between each search. */
private static final int SEARCH_DELAY = 700; private static final int SEARCH_DELAY = 700;
@ -275,32 +260,35 @@ public class DownloadsMenu extends ComplexOpsuState {
} finally { } finally {
finished = true; finished = true;
} }
}; }
/** Imports all packed beatmaps. */ /** Imports all packed beatmaps. */
private void importBeatmaps() { private void importBeatmaps() {
// invoke unpacker and parser // invoke unpacker and parser
File[] dirs = oszUnpacker.unpackAll(); File[] dirs = oszunpacker.unpackAll();
if (dirs != null && dirs.length > 0) { this.importedNode = beatmapParser.parseDirectories(dirs);
this.importedNode = beatmapParser.parseDirectories(dirs);
if (importedNode != null) {
// send notification
EventBus.post(new BarNotificationEvent((dirs.length == 1) ? "Imported 1 new song." :
String.format("Imported %d new songs.", dirs.length)));
}
}
DownloadList.get().clearDownloads(Download.Status.COMPLETE); DownloadList.get().clearDownloads(Download.Status.COMPLETE);
if (this.importedNode == null) {
return;
}
String msg;
if (dirs.length == 1) {
msg = "Imported 1 new song.";
} else {
msg = String.format("Imported %d new songs.", dirs.length);
}
BarNotifListener.EVENT.make().onBarNotif(msg);
} }
} }
@Inject public DownloadsMenu() {
public DownloadsMenu(InstanceContainer instanceContainer) {
SERVERS = new DownloadServer[] { SERVERS = new DownloadServer[] {
instanceContainer.provide(BloodcatServer.class), new BloodcatServer(),
instanceContainer.provide(YaSOnlineServer.class), new YaSOnlineServer(),
instanceContainer.provide(MnetworkServer.class), new MnetworkServer(),
instanceContainer.provide(MengSkyServer.class), new MengSkyServer(),
}; };
} }
@ -319,7 +307,7 @@ public class DownloadsMenu extends ComplexOpsuState {
// search // search
searchTimer = SEARCH_DELAY; searchTimer = SEARCH_DELAY;
searchResultString = "Loading data from server..."; searchResultString = "Loading data from server...";
search = new TextField(displayContainer, Fonts.DEFAULT, baseX, searchY, searchWidth, Fonts.MEDIUM.getLineHeight()) { search = new TextField(Fonts.DEFAULT, baseX, searchY, searchWidth, Fonts.MEDIUM.getLineHeight()) {
@Override @Override
public boolean isFocusable() { public boolean isFocusable() {
return false; return false;
@ -555,7 +543,7 @@ public class DownloadsMenu extends ComplexOpsuState {
// focus new beatmap // focus new beatmap
// NOTE: This can't be called in another thread because it makes OpenGL calls. // NOTE: This can't be called in another thread because it makes OpenGL calls.
instanceContainer.provide(SongMenu.class).setFocus(importedNode, -1, true, true); songMenuState.setFocus(importedNode, -1, true, true);
} }
importThread = null; importThread = null;
} }
@ -633,7 +621,7 @@ public class DownloadsMenu extends ComplexOpsuState {
// back // back
if (UI.getBackButton().contains(x, y)) { if (UI.getBackButton().contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
displayContainer.switchState(MainMenu.class); displayContainer.switchState(mainmenuState);
return true; return true;
} }
@ -698,7 +686,7 @@ public class DownloadsMenu extends ComplexOpsuState {
if (playing) if (playing)
previewID = node.getID(); previewID = node.getID();
} catch (SlickException e) { } catch (SlickException e) {
EventBus.post(new BarNotificationEvent("Failed to load track preview. See log for details.")); BarNotifListener.EVENT.make().onBarNotif("Failed to load track preview. See log for details.");
Log.error(e); Log.error(e);
} }
} }
@ -721,7 +709,7 @@ public class DownloadsMenu extends ComplexOpsuState {
if (!DownloadList.get().contains(node.getID())) { if (!DownloadList.get().contains(node.getID())) {
node.createDownload(serverMenu.getSelectedItem()); node.createDownload(serverMenu.getSelectedItem());
if (node.getDownload() == null) { if (node.getDownload() == null) {
EventBus.post(new BarNotificationEvent("The download could not be started")); BarNotifListener.EVENT.make().onBarNotif("The download could not be started");
} else { } else {
DownloadList.get().addNode(node); DownloadList.get().addNode(node);
node.getDownload().start(); node.getDownload().start();
@ -901,12 +889,12 @@ public class DownloadsMenu extends ComplexOpsuState {
} }
// block input during beatmap importing // block input during beatmap importing
if (importThread != null && key != Input.KEY_ESCAPE) { if (importThread != null && key != KEY_ESCAPE) {
return true; return true;
} }
switch (key) { switch (key) {
case Input.KEY_ESCAPE: case KEY_ESCAPE:
if (importThread != null) { if (importThread != null) {
// beatmap importing: stop parsing beatmaps by sending interrupt to BeatmapParser // beatmap importing: stop parsing beatmaps by sending interrupt to BeatmapParser
importThread.interrupt(); importThread.interrupt();
@ -918,16 +906,16 @@ public class DownloadsMenu extends ComplexOpsuState {
} else { } else {
// return to main menu // return to main menu
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
displayContainer.switchState(MainMenu.class); displayContainer.switchState(mainmenuState);
} }
return true; return true;
case Input.KEY_ENTER: case KEY_RETURN:
if (!search.getText().isEmpty()) { if (!search.getText().isEmpty()) {
pageDir = Page.RESET; pageDir = Page.RESET;
resetSearchTimer(); resetSearchTimer();
} }
return true; return true;
case Input.KEY_F5: case KEY_F5:
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
lastQuery = null; lastQuery = null;
pageDir = Page.CURRENT; pageDir = Page.CURRENT;
@ -937,7 +925,7 @@ public class DownloadsMenu extends ComplexOpsuState {
return true; return true;
} }
// wait for user to finish typing // wait for user to finish typing
if (Character.isLetterOrDigit(c) || key == Input.KEY_BACK) { if (Character.isLetterOrDigit(c) || key == KEY_BACK) {
search.keyPressed(key, c); search.keyPressed(key, c);
searchTimer = 0; searchTimer = 0;
pageDir = Page.RESET; pageDir = Page.RESET;
@ -963,7 +951,7 @@ public class DownloadsMenu extends ComplexOpsuState {
pageDir = Page.RESET; pageDir = Page.RESET;
previewID = -1; previewID = -1;
if (barNotificationOnLoad != null) { if (barNotificationOnLoad != null) {
EventBus.post(new BarNotificationEvent(barNotificationOnLoad)); BarNotifListener.EVENT.make().onBarNotif(barNotificationOnLoad);
barNotificationOnLoad = null; barNotificationOnLoad = null;
} }
} }

View File

@ -24,7 +24,6 @@ import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.beatmap.TimingPoint; import itdelatrisu.opsu.beatmap.TimingPoint;
import itdelatrisu.opsu.db.BeatmapDB; import itdelatrisu.opsu.db.BeatmapDB;
@ -60,41 +59,26 @@ import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException; import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.*; import yugecin.opsudance.*;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.ComplexOpsuState; import yugecin.opsudance.core.state.ComplexOpsuState;
import yugecin.opsudance.core.state.transitions.FadeInTransitionState; import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.core.state.transitions.FadeOutTransitionState; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.objects.curves.FakeCombinedCurve; import yugecin.opsudance.objects.curves.FakeCombinedCurve;
import yugecin.opsudance.options.OptionGroups; import yugecin.opsudance.options.OptionGroups;
import yugecin.opsudance.options.Options;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.sbv2.MoveStoryboard; import yugecin.opsudance.sbv2.MoveStoryboard;
import yugecin.opsudance.skinning.SkinService; import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.ui.OptionsOverlay; import yugecin.opsudance.ui.OptionsOverlay;
import yugecin.opsudance.ui.StoryboardOverlay; import yugecin.opsudance.ui.StoryboardOverlay;
import yugecin.opsudance.utils.GLHelper; import yugecin.opsudance.utils.GLHelper;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* "Game" state. * "Game" state.
*/ */
public class Game extends ComplexOpsuState { public class Game extends ComplexOpsuState {
@Inject
private InstanceContainer instanceContainer;
@Inject
private GameObjectRenderer gameObjectRenderer;
@Inject
private BeatmapParser beatmapParser;
public static boolean isInGame; // TODO delete this when #79 is fixed public static boolean isInGame; // TODO delete this when #79 is fixed
/** Game restart states. */ /** Game restart states. */
public enum Restart { public enum Restart {
@ -332,7 +316,7 @@ public class Game extends ComplexOpsuState {
private boolean skippedToCheckpoint; private boolean skippedToCheckpoint;
public Game(DisplayContainer displayContainer) { public Game() {
super(); super();
mirrorCursor = new Cursor(true); mirrorCursor = new Cursor(true);
this.moveStoryboardOverlay = new MoveStoryboard(displayContainer); this.moveStoryboardOverlay = new MoveStoryboard(displayContainer);
@ -354,7 +338,9 @@ public class Game extends ComplexOpsuState {
gOffscreen.setBackground(Color.black); gOffscreen.setBackground(Color.black);
} catch (SlickException e) { } catch (SlickException e) {
Log.error("could not create offscreen graphics", e); Log.error("could not create offscreen graphics", e);
EventBus.post(new BubbleNotificationEvent("Exception while creating offscreen graphics. See logfile for details.", BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif(
"Exception while creating offscreen graphics. See logfile for details.",
Colors.BUB_RED);
} }
// initialize music position bar location // initialize music position bar location
@ -370,8 +356,7 @@ public class Game extends ComplexOpsuState {
scoreboardStarStream.setDurationSpread(700, 100); scoreboardStarStream.setDurationSpread(700, 100);
// create the associated GameData object // create the associated GameData object
data = instanceContainer.injectFields(new GameData(displayContainer.width, displayContainer.height)); gameObjectRenderer.gameData = data = new GameData();
gameObjectRenderer.setGameData(data);
} }
@ -725,10 +710,10 @@ public class Game extends ComplexOpsuState {
displayContainer.cursor.draw(replayKeyPressed); displayContainer.cursor.draw(replayKeyPressed);
} else if (GameMod.AUTO.isActive()) { } else if (GameMod.AUTO.isActive()) {
displayContainer.cursor.draw(autoMousePressed); displayContainer.cursor.draw(autoMousePressed);
if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) { if (OPTION_DANCE_MIRROR.state) {
mirrorCursor.draw(autoMousePressed); mirrorCursor.draw(autoMousePressed);
} }
} else if (GameMod.AUTOPILOT.isActive()) { } else {
displayContainer.cursor.draw(Utils.isGameKeyPressed()); displayContainer.cursor.draw(Utils.isGameKeyPressed());
} }
@ -775,13 +760,6 @@ public class Game extends ComplexOpsuState {
if (!isLeadIn()) if (!isLeadIn())
MusicController.resume(); MusicController.resume();
} }
// focus lost: go back to pause screen
else if (!Display.isActive()) {
displayContainer.switchStateNow(GamePauseMenu.class);
pausePulse = 0f;
}
// advance pulse animation // advance pulse animation
else { else {
pausePulse += delta / 750f; pausePulse += delta / 750f;
@ -895,7 +873,7 @@ public class Game extends ComplexOpsuState {
onCloseRequest(); onCloseRequest();
} else { } else {
// go to ranking screen // go to ranking screen
displayContainer.switchState(GameRanking.class); displayContainer.switchState(gameRankingState);
} }
} }
} }
@ -942,14 +920,16 @@ public class Game extends ComplexOpsuState {
} else if (GameMod.AUTO.isActive()) { } else if (GameMod.AUTO.isActive()) {
displayContainer.cursor.setCursorPosition(displayContainer.delta, (int) autoMousePosition.x, (int) autoMousePosition.y); displayContainer.cursor.setCursorPosition(displayContainer.delta, (int) autoMousePosition.x, (int) autoMousePosition.y);
if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) { if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) {
double dx = autoMousePosition.x - Options.width / 2d; double dx = autoMousePosition.x - displayContainer.width / 2d;
double dy = autoMousePosition.y - Options.height / 2d; double dy = autoMousePosition.y - displayContainer.height / 2d;
double d = Math.sqrt(dx * dx + dy * dy); double d = Math.sqrt(dx * dx + dy * dy);
double a = Math.atan2(dy, dx) + Math.PI; 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)); mirrorCursor.setCursorPosition(displayContainer.delta, (int) (Math.cos(a) * d + displayContainer.width / 2), (int) (Math.sin(a) * d + displayContainer.height / 2));
} }
} else if (GameMod.AUTOPILOT.isActive()) { } else if (GameMod.AUTOPILOT.isActive()) {
displayContainer.cursor.setCursorPosition(displayContainer.delta, (int) autoMousePosition.x, (int) autoMousePosition.y); displayContainer.cursor.setCursorPosition(displayContainer.delta, (int) autoMousePosition.x, (int) autoMousePosition.y);
} else {
displayContainer.cursor.setCursorPosition(displayContainer.delta, displayContainer.mouseX, displayContainer.mouseY);
} }
} }
@ -977,7 +957,7 @@ public class Game extends ComplexOpsuState {
// save score and replay // save score and replay
if (!checkpointLoaded) { if (!checkpointLoaded) {
boolean unranked = (GameMod.AUTO.isActive() || GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()); boolean unranked = (GameMod.AUTO.isActive() || GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive());
instanceContainer.provide(GameRanking.class).setGameData(data); gameRankingState.setGameData(data);
if (isReplay) if (isReplay)
data.setReplay(replay); data.setReplay(replay);
else if (replayFrames != null) { else if (replayFrames != null) {
@ -1063,7 +1043,7 @@ public class Game extends ComplexOpsuState {
if (MusicController.isPlaying() || isLeadIn()) { if (MusicController.isPlaying() || isLeadIn()) {
pauseTime = trackPosition; pauseTime = trackPosition;
} }
displayContainer.switchStateNow(GamePauseMenu.class); displayContainer.switchStateInstantly(pauseState);
} }
// drain health // drain health
@ -1090,7 +1070,7 @@ public class Game extends ComplexOpsuState {
rotations = new IdentityHashMap<>(); rotations = new IdentityHashMap<>();
SoundController.playSound(SoundEffect.FAIL); SoundController.playSound(SoundEffect.FAIL);
displayContainer.switchState(GamePauseMenu.class, FadeOutTransitionState.class, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME, FadeInTransitionState.class, 300); displayContainer.switchState(pauseState, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME, 300);
} }
} }
} }
@ -1123,8 +1103,8 @@ public class Game extends ComplexOpsuState {
@Override @Override
public boolean onCloseRequest() { public boolean onCloseRequest() {
instanceContainer.provide(SongMenu.class).resetGameDataOnLoad(); songMenuState.resetGameDataOnLoad();
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
return false; return false;
} }
@ -1156,7 +1136,7 @@ public class Game extends ComplexOpsuState {
} }
switch (key) { switch (key) {
case Input.KEY_ESCAPE: case KEY_ESCAPE:
// "auto" mod or watching replay: go back to song menu // "auto" mod or watching replay: go back to song menu
if (GameMod.AUTO.isActive() || isReplay) { if (GameMod.AUTO.isActive() || isReplay) {
onCloseRequest(); onCloseRequest();
@ -1171,15 +1151,15 @@ public class Game extends ComplexOpsuState {
if (MusicController.isPlaying() || isLeadIn()) { if (MusicController.isPlaying() || isLeadIn()) {
pauseTime = trackPosition; pauseTime = trackPosition;
} }
displayContainer.switchStateNow(GamePauseMenu.class); displayContainer.switchStateInstantly(pauseState);
break; break;
case Input.KEY_SPACE: case KEY_SPACE:
// skip intro // skip intro
skipIntro(); skipIntro();
break; break;
case Input.KEY_R: case KEY_R:
// restart // restart
if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) { if (input.isControlDown()) {
if (trackPosition < beatmap.objects[0].getTime()) { if (trackPosition < beatmap.objects[0].getTime()) {
retries--; // don't count this retry (cancel out later increment) retries--; // don't count this retry (cancel out later increment)
} }
@ -1188,9 +1168,9 @@ public class Game extends ComplexOpsuState {
skipIntro(); skipIntro();
} }
break; break;
case Input.KEY_S: case KEY_S:
// save checkpoint // save checkpoint
if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) { if (input.isControlDown()) {
if (isLeadIn()) { if (isLeadIn()) {
break; break;
} }
@ -1200,40 +1180,40 @@ public class Game extends ComplexOpsuState {
if (0 <= time && time < 3600) { if (0 <= time && time < 3600) {
OPTION_CHECKPOINT.setValue(time); OPTION_CHECKPOINT.setValue(time);
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
EventBus.post(new BarNotificationEvent("Checkpoint saved.")); BarNotifListener.EVENT.make().onBarNotif("Checkpoint saved.");
} }
} }
break; break;
case Input.KEY_L: case KEY_L:
// load checkpoint // load checkpoint
if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) { if (input.isControlDown()) {
int checkpoint = OPTION_CHECKPOINT.val * 1000; int checkpoint = OPTION_CHECKPOINT.val * 1000;
if (checkpoint == 0 || checkpoint > beatmap.endTime) if (checkpoint == 0 || checkpoint > beatmap.endTime)
break; // invalid checkpoint break; // invalid checkpoint
loadCheckpoint(checkpoint); loadCheckpoint(checkpoint);
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
EventBus.post(new BarNotificationEvent("Checkpoint loaded.")); BarNotifListener.EVENT.make().onBarNotif("Checkpoint loaded.");
} }
break; break;
case Input.KEY_F: case KEY_F:
// change playback speed // change playback speed
if (isReplay || GameMod.AUTO.isActive()) { if (isReplay || GameMod.AUTO.isActive()) {
playbackSpeed = playbackSpeed.next(); playbackSpeed = playbackSpeed.next();
MusicController.setPitch(GameMod.getSpeedMultiplier() * playbackSpeed.getModifier()); MusicController.setPitch(GameMod.getSpeedMultiplier() * playbackSpeed.getModifier());
} }
break; break;
case Input.KEY_UP: case KEY_UP:
UI.changeVolume(1); UI.changeVolume(1);
break; break;
case Input.KEY_DOWN: case KEY_DOWN:
UI.changeVolume(-1); UI.changeVolume(-1);
break; break;
case Input.KEY_TAB: case KEY_TAB:
if (!OPTION_DANCE_HIDE_UI.state) { if (!OPTION_DANCE_HIDE_UI.state) {
scoreboardVisible = !scoreboardVisible; scoreboardVisible = !scoreboardVisible;
} }
break; break;
case Input.KEY_M: case KEY_M:
if (OPTION_DANCE_MIRROR.state) { if (OPTION_DANCE_MIRROR.state) {
mirrorTo = objectIndex; mirrorTo = objectIndex;
} else { } else {
@ -1243,7 +1223,7 @@ public class Game extends ComplexOpsuState {
} }
OPTION_DANCE_MIRROR.toggle(); OPTION_DANCE_MIRROR.toggle();
break; break;
case Input.KEY_P: case KEY_P:
if (OPTION_DANCE_MIRROR.state) { if (OPTION_DANCE_MIRROR.state) {
mirrorTo = objectIndex; mirrorTo = objectIndex;
} else { } else {
@ -1253,14 +1233,14 @@ public class Game extends ComplexOpsuState {
} }
OPTION_DANCE_MIRROR.toggle(); OPTION_DANCE_MIRROR.toggle();
break; break;
case Input.KEY_MINUS: case KEY_MINUS:
currentMapMusicOffset += 5; currentMapMusicOffset += 5;
EventBus.post(new BarNotificationEvent("Current map offset: " + currentMapMusicOffset)); BarNotifListener.EVENT.make().onBarNotif("Current map offset: " + currentMapMusicOffset);
break; break;
} }
if (key == Input.KEY_ADD || c == '+') { if (key == KEY_ADD || c == '+') {
currentMapMusicOffset -= 5; currentMapMusicOffset -= 5;
EventBus.post(new BarNotificationEvent("Current map offset: " + currentMapMusicOffset)); BarNotifListener.EVENT.make().onBarNotif("Current map offset: " + currentMapMusicOffset);
} }
return true; return true;
@ -1324,7 +1304,7 @@ public class Game extends ComplexOpsuState {
if (MusicController.isPlaying() || isLeadIn()) { if (MusicController.isPlaying() || isLeadIn()) {
pauseTime = trackPosition; pauseTime = trackPosition;
} }
displayContainer.switchStateNow(GamePauseMenu.class); displayContainer.switchStateInstantly(pauseState);
return true; return true;
} }
@ -1425,7 +1405,7 @@ public class Game extends ComplexOpsuState {
keys = ReplayFrame.KEY_K2; keys = ReplayFrame.KEY_K2;
} }
if (keys != ReplayFrame.KEY_NONE) { if (keys != ReplayFrame.KEY_NONE) {
gameKeyReleased(keys, displayContainer.input.getMouseX(), displayContainer.input.getMouseY(), MusicController.getPosition()); gameKeyReleased(keys, input.getMouseX(), input.getMouseY(), MusicController.getPosition());
} }
return true; return true;
@ -1511,9 +1491,7 @@ public class Game extends ComplexOpsuState {
hue += hueshift; hue += hueshift;
} }
if (isReplay || GameMod.AUTO.isActive() || GameMod.AUTOPILOT.isActive()) { displayContainer.drawCursor = false;
displayContainer.drawCursor = false;
}
isInGame = true; isInGame = true;
if (!skippedToCheckpoint) { if (!skippedToCheckpoint) {
@ -1521,8 +1499,8 @@ public class Game extends ComplexOpsuState {
} }
if (beatmap == null || beatmap.objects == null) { if (beatmap == null || beatmap.objects == null) {
EventBus.post(new BubbleNotificationEvent("Game was running without a beatmap", BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif("Game was running without a beatmap", Colors.BUB_RED);
displayContainer.switchStateInstantly(SongMenu.class); displayContainer.switchStateInstantly(songMenuState);
} }
Dancer.instance.reset(); Dancer.instance.reset();
@ -1619,16 +1597,16 @@ public class Game extends ComplexOpsuState {
try { try {
if (hitObject.isCircle()) { if (hitObject.isCircle()) {
gameObjects[i] = instanceContainer.injectFields(new Circle(hitObject, this, data, hitObject.getComboIndex(), comboEnd)); gameObjects[i] = new Circle(hitObject, this, data, hitObject.getComboIndex(), comboEnd);
} else if (hitObject.isSlider()) { } else if (hitObject.isSlider()) {
gameObjects[i] = instanceContainer.injectFields(new Slider(hitObject, this, data, hitObject.getComboIndex(), comboEnd)); gameObjects[i] = new Slider(hitObject, this, data, hitObject.getComboIndex(), comboEnd);
} else if (hitObject.isSpinner()) { } else if (hitObject.isSpinner()) {
gameObjects[i] = new Spinner(hitObject, this, data); gameObjects[i] = new Spinner(hitObject, this, data);
} }
} catch (Exception e) { } catch (Exception e) {
String message = String.format("Failed to create %s at index %d:\n%s", hitObject.getTypeName(), i, hitObject.toString()); String message = String.format("Failed to create %s at index %d:\n%s", hitObject.getTypeName(), i, hitObject.toString());
Log.error(message, e); Log.error(message, e);
EventBus.post(new BubbleNotificationEvent(message, BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif(message, Colors.BUB_RED);
gameObjects[i] = new DummyObject(hitObject); gameObjects[i] = new DummyObject(hitObject);
} }
} }
@ -1909,7 +1887,7 @@ public class Game extends ComplexOpsuState {
gameObj.draw(g, trackPosition, false); gameObj.draw(g, trackPosition, false);
if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive() && idx < mirrorTo && idx >= mirrorFrom) { if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive() && idx < mirrorTo && idx >= mirrorFrom) {
g.pushTransform(); g.pushTransform();
g.rotate(Options.width / 2f, Options.height / 2f, 180f); g.rotate(displayContainer.width / 2f, displayContainer.height / 2f, 180f);
gameObj.draw(g, trackPosition, true); gameObj.draw(g, trackPosition, true);
g.popTransform(); g.popTransform();
} }
@ -2053,7 +2031,7 @@ public class Game extends ComplexOpsuState {
skipButton.setHoverExpand(1.1f, MenuButton.Expand.UP_LEFT); skipButton.setHoverExpand(1.1f, MenuButton.Expand.UP_LEFT);
// load other images... // load other images...
instanceContainer.provide(GamePauseMenu.class).loadImages(); pauseState.loadImages();
data.loadImages(); data.loadImages();
} }
@ -2090,7 +2068,7 @@ public class Game extends ComplexOpsuState {
// initialize objects // initialize objects
gameObjectRenderer.initForGame(data, diameter); gameObjectRenderer.initForGame(data, diameter);
Slider.init(gameObjectRenderer.getCircleDiameter(), beatmap); Slider.init(gameObjectRenderer.circleDiameter, beatmap);
Spinner.init(displayContainer, overallDifficulty); Spinner.init(displayContainer, overallDifficulty);
Color sliderBorderColor = SkinService.skin.getSliderBorderColor(); Color sliderBorderColor = SkinService.skin.getSliderBorderColor();
if (!OPTION_IGNORE_BEATMAP_SKINS.state && beatmap.getSliderBorderColor() != null) { if (!OPTION_IGNORE_BEATMAP_SKINS.state && beatmap.getSliderBorderColor() != null) {
@ -2214,7 +2192,8 @@ public class Game extends ComplexOpsuState {
this.replay = null; this.replay = null;
} else { } else {
if (replay.frames == null) { if (replay.frames == null) {
EventBus.post(new BubbleNotificationEvent("Attempting to set a replay with no frames.", BubbleNotificationEvent.COLOR_ORANGE)); BubNotifListener.EVENT.make().onBubNotif("Attempting to set a replay with no frames.",
Colors.BUB_ORANGE);
return; return;
} }
this.isReplay = true; this.isReplay = true;

View File

@ -30,10 +30,10 @@ import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.BaseOpsuState;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -44,12 +44,6 @@ import static yugecin.opsudance.options.Options.*;
*/ */
public class GamePauseMenu extends BaseOpsuState { public class GamePauseMenu extends BaseOpsuState {
@Inject
private InstanceContainer instanceContainer;
@Inject
private Game gameState;
private MenuButton continueButton, retryButton, backButton; private MenuButton continueButton, retryButton, backButton;
@Override @Override
@ -101,24 +95,24 @@ public class GamePauseMenu extends BaseOpsuState {
} }
} }
if (key == Input.KEY_ESCAPE) { if (key == KEY_ESCAPE) {
// 'esc' will normally unpause, but will return to song menu if health is zero // 'esc' will normally unpause, but will return to song menu if health is zero
if (gameState.getRestart() == Game.Restart.LOSE) { if (gameState.getRestart() == Game.Restart.LOSE) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
instanceContainer.provide(SongMenu.class).resetGameDataOnLoad(); songMenuState.resetGameDataOnLoad();
MusicController.playAt(MusicController.getBeatmap().previewTime, true); MusicController.playAt(MusicController.getBeatmap().previewTime, true);
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} else { } else {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
gameState.setRestart(Game.Restart.FALSE); gameState.setRestart(Game.Restart.FALSE);
displayContainer.switchState(Game.class); displayContainer.switchState(gameState);
} }
return true; return true;
} }
if (key == Input.KEY_R && (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL))) { if (key == KEY_R && input.isControlDown()) {
gameState.setRestart(Game.Restart.MANUAL); gameState.setRestart(Game.Restart.MANUAL);
displayContainer.switchState(Game.class); displayContainer.switchState(gameState);
return true; return true;
} }
@ -139,14 +133,14 @@ public class GamePauseMenu extends BaseOpsuState {
if (continueButton.contains(x, y) && !loseState) { if (continueButton.contains(x, y) && !loseState) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
gameState.setRestart(Game.Restart.FALSE); gameState.setRestart(Game.Restart.FALSE);
displayContainer.switchState(Game.class); displayContainer.switchState(gameState);
} else if (retryButton.contains(x, y)) { } else if (retryButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
gameState.setRestart(Game.Restart.MANUAL); gameState.setRestart(Game.Restart.MANUAL);
displayContainer.switchState(Game.class); displayContainer.switchState(gameState);
} else if (backButton.contains(x, y)) { } else if (backButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
instanceContainer.provide(SongMenu.class).resetGameDataOnLoad(); songMenuState.resetGameDataOnLoad();
if (loseState) if (loseState)
MusicController.playAt(MusicController.getBeatmap().previewTime, true); MusicController.playAt(MusicController.getBeatmap().previewTime, true);
else else
@ -155,7 +149,7 @@ public class GamePauseMenu extends BaseOpsuState {
displayContainer.resetCursor(); displayContainer.resetCursor();
} }
MusicController.setPitch(1.0f); MusicController.setPitch(1.0f);
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} }
return true; return true;
@ -188,10 +182,9 @@ public class GamePauseMenu extends BaseOpsuState {
@Override @Override
public boolean onCloseRequest() { public boolean onCloseRequest() {
SongMenu songmenu = instanceContainer.provide(SongMenu.class); songMenuState.resetTrackOnLoad();
songmenu.resetTrackOnLoad(); songMenuState.resetGameDataOnLoad();
songmenu.resetGameDataOnLoad(); displayContainer.switchState(songMenuState);
displayContainer.switchState(SongMenu.class);
return false; return false;
} }

View File

@ -32,15 +32,15 @@ import itdelatrisu.opsu.ui.UI;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.BaseOpsuState;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotifListener;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* "Game Ranking" (score card) state. * "Game Ranking" (score card) state.
@ -51,9 +51,6 @@ import yugecin.opsudance.events.BarNotificationEvent;
*/ */
public class GameRanking extends BaseOpsuState { public class GameRanking extends BaseOpsuState {
@Inject
private InstanceContainer instanceContainer;
/** Associated GameData object. */ /** Associated GameData object. */
private GameData data; private GameData data;
@ -125,7 +122,7 @@ public class GameRanking extends BaseOpsuState {
return true; return true;
} }
if (key == Input.KEY_ESCAPE) { if (key == Keyboard.KEY_ESCAPE) {
returnToSongMenu(); returnToSongMenu();
} }
return true; return true;
@ -149,7 +146,6 @@ public class GameRanking extends BaseOpsuState {
} }
// replay // replay
Game gameState = instanceContainer.provide(Game.class);
boolean returnToGame = false; boolean returnToGame = false;
boolean replayButtonPressed = replayButton.contains(x, y); boolean replayButtonPressed = replayButton.contains(x, y);
if (replayButtonPressed && !(data.isGameplay() && GameMod.AUTO.isActive())) { if (replayButtonPressed && !(data.isGameplay() && GameMod.AUTO.isActive())) {
@ -161,13 +157,14 @@ public class GameRanking extends BaseOpsuState {
gameState.setRestart((data.isGameplay()) ? Game.Restart.REPLAY : Game.Restart.NEW); gameState.setRestart((data.isGameplay()) ? Game.Restart.REPLAY : Game.Restart.NEW);
returnToGame = true; returnToGame = true;
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
EventBus.post(new BarNotificationEvent("Replay file not found.")); BarNotifListener.EVENT.make().onBarNotif("Replay file not found.");
} catch (IOException e) { } catch (IOException e) {
Log.error("Failed to load replay data.", e); Log.error("Failed to load replay data.", e);
EventBus.post(new BarNotificationEvent("Failed to load replay data. See log for details.")); BarNotifListener.EVENT.make().onBarNotif(
"Failed to load replay data. See log for details.");
} }
} else } else
EventBus.post(new BarNotificationEvent("Replay file not found.")); BarNotifListener.EVENT.make().onBarNotif("Replay file not found.");
} }
// retry // retry
@ -183,7 +180,7 @@ public class GameRanking extends BaseOpsuState {
Beatmap beatmap = MusicController.getBeatmap(); Beatmap beatmap = MusicController.getBeatmap();
gameState.loadBeatmap(beatmap); gameState.loadBeatmap(beatmap);
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
displayContainer.switchState(Game.class); displayContainer.switchState(gameState);
} }
return true; return true;
} }
@ -217,12 +214,11 @@ public class GameRanking extends BaseOpsuState {
@Override @Override
public boolean onCloseRequest() { public boolean onCloseRequest() {
SongMenu songmenu = instanceContainer.provide(SongMenu.class);
if (data != null && data.isGameplay()) { if (data != null && data.isGameplay()) {
songmenu.resetTrackOnLoad(); songMenuState.resetTrackOnLoad();
} }
songmenu.resetGameDataOnLoad(); songMenuState.resetGameDataOnLoad();
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
return false; return false;
} }
@ -232,25 +228,24 @@ public class GameRanking extends BaseOpsuState {
private void returnToSongMenu() { private void returnToSongMenu() {
SoundController.muteSoundComponent(); SoundController.muteSoundComponent();
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
SongMenu songMenu = instanceContainer.provide(SongMenu.class);
if (data.isGameplay()) { if (data.isGameplay()) {
songMenu.resetTrackOnLoad(); songMenuState.resetTrackOnLoad();
} }
songMenu.resetGameDataOnLoad(); songMenuState.resetGameDataOnLoad();
if (displayContainer.cursor.isBeatmapSkinned()) { if (displayContainer.cursor.isBeatmapSkinned()) {
displayContainer.resetCursor(); displayContainer.resetCursor();
} }
displayContainer.switchState(SongMenu.class); displayContainer.switchState(songMenuState);
} }
/** /**
* Sets the associated GameData object. * Sets the associated GameData object.
* @param data the GameData * @param data the GameData
*/ */
public void setGameData(GameData data) { this.data = data; } public void setGameData(GameData data) { this.data = data; } // TODO why is this unused
/** /**
* Returns the current GameData object (usually null unless state active). * Returns the current GameData object (usually null unless state active).
*/ */
public GameData getGameData() { return data; } public GameData getGameData() { return data; } // TODO why is this unused
} }

View File

@ -45,14 +45,14 @@ import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.Constants;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.BaseOpsuState;
import yugecin.opsudance.core.state.OpsuState; import yugecin.opsudance.core.state.OpsuState;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubNotifListener;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -62,12 +62,6 @@ import static yugecin.opsudance.options.Options.*;
*/ */
public class MainMenu extends BaseOpsuState { public class MainMenu extends BaseOpsuState {
@Inject
private InstanceContainer instanceContainer;
@Inject
private Updater updater;
/** Idle time, in milliseconds, before returning the logo to its original position. */ /** Idle time, in milliseconds, before returning the logo to its original position. */
private static final short LOGO_IDLE_DELAY = 10000; private static final short LOGO_IDLE_DELAY = 10000;
@ -472,12 +466,11 @@ public class MainMenu extends BaseOpsuState {
UI.enter(); UI.enter();
if (!enterNotification) { if (!enterNotification) {
if (updater.getStatus() == Updater.Status.UPDATE_AVAILABLE) { if (updater.getStatus() == Updater.Status.UPDATE_AVAILABLE) {
EventBus.post(new BarNotificationEvent("An opsu! update is available.")); BarNotifListener.EVENT.make().onBarNotif("An opsu! update is available.");
enterNotification = true;
} else if (updater.justUpdated()) { } else if (updater.justUpdated()) {
EventBus.post(new BarNotificationEvent("opsu! is now up to date!")); BarNotifListener.EVENT.make().onBarNotif("opsu! is now up to date!");
enterNotification = true;
} }
enterNotification = true;
} }
// reset measure info // reset measure info
@ -538,58 +531,60 @@ public class MainMenu extends BaseOpsuState {
if (musicPlay.contains(x, y)) { if (musicPlay.contains(x, y)) {
if (MusicController.isPlaying()) { if (MusicController.isPlaying()) {
MusicController.pause(); MusicController.pause();
EventBus.post(new BarNotificationEvent("Pause")); BarNotifListener.EVENT.make().onBarNotif("Pause");
} else if (!MusicController.isTrackLoading()) { } else if (!MusicController.isTrackLoading()) {
MusicController.resume(); MusicController.resume();
EventBus.post(new BarNotificationEvent("Play")); BarNotifListener.EVENT.make().onBarNotif("Play");
} }
return true; return true;
} else if (musicNext.contains(x, y)) { } else if (musicNext.contains(x, y)) {
nextTrack(true); nextTrack(true);
EventBus.post(new BarNotificationEvent(">> Next")); BarNotifListener.EVENT.make().onBarNotif(">> Next");
return true; return true;
} else if (musicPrevious.contains(x, y)) { } else if (musicPrevious.contains(x, y)) {
lastMeasureProgress = 0f; lastMeasureProgress = 0f;
if (!previous.isEmpty()) { if (!previous.isEmpty()) {
instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false); songMenuState.setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false);
if (OPTION_DYNAMIC_BACKGROUND.state) { if (OPTION_DYNAMIC_BACKGROUND.state) {
bgAlpha.setTime(0); bgAlpha.setTime(0);
} }
} else { } else {
MusicController.setPosition(0); MusicController.setPosition(0);
} }
EventBus.post(new BarNotificationEvent("<< Previous")); BarNotifListener.EVENT.make().onBarNotif("<< Previous");
return true; return true;
} }
// downloads button actions // downloads button actions
if (downloadsButton.contains(x, y)) { if (downloadsButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
displayContainer.switchState(DownloadsMenu.class); displayContainer.switchState(downloadState);
return true; return true;
} }
// repository button actions // repository button actions
if (repoButton != null && repoButton.contains(x, y)) { if (repoButton != null && repoButton.contains(x, y)) {
try { try {
Desktop.getDesktop().browse(config.REPOSITORY_URI); Desktop.getDesktop().browse(Constants.REPOSITORY_URI);
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
EventBus.post(new BarNotificationEvent("The repository web page could not be opened.")); BarNotifListener.EVENT.make().onBarNotif(
"The repository web page could not be opened.");
} catch (IOException e) { } catch (IOException e) {
Log.error("could not browse to repo", e); Log.error("could not browse to repo", e);
EventBus.post(new BubbleNotificationEvent("Could not browse to repo", BubbleNotificationEvent.COLOR_ORANGE)); BubNotifListener.EVENT.make().onBubNotif("Could not browse to repo", Colors.BUB_ORANGE);
} }
return true; return true;
} }
if (danceRepoButton != null && danceRepoButton.contains(x, y)) { if (danceRepoButton != null && danceRepoButton.contains(x, y)) {
try { try {
Desktop.getDesktop().browse(config.DANCE_REPOSITORY_URI); Desktop.getDesktop().browse(Constants.DANCE_REPOSITORY_URI);
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
EventBus.post(new BarNotificationEvent("The repository web page could not be opened.")); BarNotifListener.EVENT.make().onBarNotif(
"The repository web page could not be opened.");
} catch (IOException e) { } catch (IOException e) {
Log.error("could not browse to repo", e); Log.error("could not browse to repo", e);
EventBus.post(new BubbleNotificationEvent("Could not browse to repo", BubbleNotificationEvent.COLOR_ORANGE)); BubNotifListener.EVENT.make().onBubNotif("Could not browse to repo", Colors.BUB_ORANGE);
} }
return true; return true;
} }
@ -657,18 +652,18 @@ public class MainMenu extends BaseOpsuState {
} }
switch (key) { switch (key) {
case Input.KEY_ESCAPE: case KEY_ESCAPE:
case Input.KEY_Q: case KEY_Q:
if (logoTimer > 0) { if (logoTimer > 0) {
logoState = LogoState.CLOSING; logoState = LogoState.CLOSING;
logoClose.setTime(0); logoClose.setTime(0);
logoTimer = 0; logoTimer = 0;
break; break;
} }
instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.EXIT); buttonState.setMenuState(MenuState.EXIT);
displayContainer.switchState(ButtonMenu.class); displayContainer.switchState(buttonState);
return true; return true;
case Input.KEY_P: case KEY_P:
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
if (logoState == LogoState.DEFAULT || logoState == LogoState.CLOSING) { if (logoState == LogoState.DEFAULT || logoState == LogoState.CLOSING) {
logoState = LogoState.OPENING; logoState = LogoState.OPENING;
@ -679,17 +674,17 @@ public class MainMenu extends BaseOpsuState {
} else } else
enterSongMenu(); enterSongMenu();
return true; return true;
case Input.KEY_D: case KEY_D:
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
displayContainer.switchState(DownloadsMenu.class); displayContainer.switchState(downloadState);
return true; return true;
case Input.KEY_R: case KEY_R:
nextTrack(true); nextTrack(true);
return true; return true;
case Input.KEY_UP: case KEY_UP:
UI.changeVolume(1); UI.changeVolume(1);
return true; return true;
case Input.KEY_DOWN: case KEY_DOWN:
UI.changeVolume(-1); UI.changeVolume(-1);
return true; return true;
} }
@ -719,7 +714,7 @@ public class MainMenu extends BaseOpsuState {
MusicController.playAt(0, false); MusicController.playAt(0, false);
return; return;
} }
BeatmapSetNode node = instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, false); BeatmapSetNode node = songMenuState.setFocus(BeatmapSetList.get().getRandomNode(), -1, true, false);
boolean sameAudio = false; boolean sameAudio = false;
if (node != null) { if (node != null) {
sameAudio = MusicController.getBeatmap().audioFilename.equals(node.getBeatmapSet().get(0).audioFilename); sameAudio = MusicController.getBeatmap().audioFilename.equals(node.getBeatmapSet().get(0).audioFilename);
@ -735,10 +730,10 @@ public class MainMenu extends BaseOpsuState {
* Enters the song menu, or the downloads menu if no beatmaps are loaded. * Enters the song menu, or the downloads menu if no beatmaps are loaded.
*/ */
private void enterSongMenu() { private void enterSongMenu() {
Class<? extends OpsuState> state = SongMenu.class; OpsuState state = songMenuState;
if (BeatmapSetList.get().getMapSetCount() == 0) { if (BeatmapSetList.get().getMapSetCount() == 0) {
instanceContainer.provide(DownloadsMenu.class).notifyOnLoad("Download some beatmaps to get started!"); downloadState.notifyOnLoad("Download some beatmaps to get started!");
state = DownloadsMenu.class; state = downloadState;
} }
displayContainer.switchState(state); displayContainer.switchState(state);
} }

View File

@ -35,7 +35,6 @@ import itdelatrisu.opsu.beatmap.BeatmapSortOrder;
import itdelatrisu.opsu.beatmap.BeatmapWatchService; import itdelatrisu.opsu.beatmap.BeatmapWatchService;
import itdelatrisu.opsu.beatmap.BeatmapWatchService.BeatmapWatchServiceListener; import itdelatrisu.opsu.beatmap.BeatmapWatchService.BeatmapWatchServiceListener;
import itdelatrisu.opsu.beatmap.LRUCache; import itdelatrisu.opsu.beatmap.LRUCache;
import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.db.BeatmapDB; import itdelatrisu.opsu.db.BeatmapDB;
import itdelatrisu.opsu.db.ScoreDB; import itdelatrisu.opsu.db.ScoreDB;
import itdelatrisu.opsu.states.ButtonMenu.MenuState; import itdelatrisu.opsu.states.ButtonMenu.MenuState;
@ -56,6 +55,7 @@ import java.nio.file.WatchEvent.Kind;
import java.util.Map; import java.util.Map;
import java.util.Stack; import java.util.Stack;
import org.lwjgl.input.Mouse;
import org.newdawn.slick.Animation; import org.newdawn.slick.Animation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
@ -63,16 +63,13 @@ import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SpriteSheet; import org.newdawn.slick.SpriteSheet;
import org.newdawn.slick.gui.TextField; import org.newdawn.slick.gui.TextField;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.ComplexOpsuState; import yugecin.opsudance.core.state.ComplexOpsuState;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionGroups; import yugecin.opsudance.options.OptionGroups;
import yugecin.opsudance.ui.OptionsOverlay; import yugecin.opsudance.ui.OptionsOverlay;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -83,18 +80,6 @@ import static yugecin.opsudance.options.Options.*;
*/ */
public class SongMenu extends ComplexOpsuState { public class SongMenu extends ComplexOpsuState {
@Inject
private InstanceContainer instanceContainer;
@Inject
private Configuration config;
@Inject
private OszUnpacker oszUnpacker;
@Inject
private BeatmapParser beatmapParser;
/** The max number of song buttons to be shown on each screen. */ /** The max number of song buttons to be shown on each screen. */
public static final int MAX_SONG_BUTTONS = 6; public static final int MAX_SONG_BUTTONS = 6;
@ -246,7 +231,7 @@ public class SongMenu extends ComplexOpsuState {
private void reloadBeatmaps() { private void reloadBeatmaps() {
if (fullReload) { if (fullReload) {
BeatmapDB.clearDatabase(); BeatmapDB.clearDatabase();
oszUnpacker.unpackAll(); oszunpacker.unpackAll();
} }
beatmapParser.parseAll(); beatmapParser.parseAll();
} }
@ -328,7 +313,7 @@ public class SongMenu extends ComplexOpsuState {
private final OptionsOverlay optionsOverlay; private final OptionsOverlay optionsOverlay;
public SongMenu(DisplayContainer displayContainer) { public SongMenu() {
super(); super();
optionsOverlay = new OptionsOverlay(displayContainer, OptionGroups.normalOptions); optionsOverlay = new OptionsOverlay(displayContainer, OptionGroups.normalOptions);
overlays.add(optionsOverlay); overlays.add(optionsOverlay);
@ -406,7 +391,7 @@ public class SongMenu extends ComplexOpsuState {
// search // search
int textFieldX = (int) (displayContainer.width * 0.7125f + Fonts.BOLD.getWidth("Search: ")); int textFieldX = (int) (displayContainer.width * 0.7125f + Fonts.BOLD.getWidth("Search: "));
int textFieldY = (int) (headerY + Fonts.BOLD.getLineHeight() / 2); int textFieldY = (int) (headerY + Fonts.BOLD.getLineHeight() / 2);
searchTextField = new TextField(displayContainer, Fonts.BOLD, textFieldX, textFieldY, (int) (displayContainer.width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight()) { searchTextField = new TextField(Fonts.BOLD, textFieldX, textFieldY, (int) (displayContainer.width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight()) {
@Override @Override
public boolean isFocusable() { public boolean isFocusable() {
return false; return false;
@ -454,12 +439,15 @@ public class SongMenu extends ComplexOpsuState {
BeatmapWatchService.addListener(new BeatmapWatchServiceListener() { BeatmapWatchService.addListener(new BeatmapWatchServiceListener() {
@Override @Override
public void eventReceived(Kind<?> kind, Path child) { public void eventReceived(Kind<?> kind, Path child) {
if (!songFolderChanged && kind != StandardWatchEventKinds.ENTRY_MODIFY) { if (songFolderChanged || kind == StandardWatchEventKinds.ENTRY_MODIFY) {
songFolderChanged = true; return;
if (displayContainer.isInState(SongMenu.class)) {
EventBus.post(new BarNotificationEvent("Changed is Songs folder detected. Hit F5 to refresh."));
}
} }
songFolderChanged = true;
if (!displayContainer.isInState(SongMenu.class)) {
return;
}
BarNotifListener.EVENT.make().onBarNotif(
"Changed is Songs folder detected. Hit F5 to refresh.");
} }
}); });
@ -761,8 +749,8 @@ public class SongMenu extends ComplexOpsuState {
if (focusNode != null) { if (focusNode != null) {
MenuState state = focusNode.getBeatmapSet().isFavorite() ? MenuState state = focusNode.getBeatmapSet().isFavorite() ?
MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP; MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP;
instanceContainer.provide(ButtonMenu.class).setMenuState(state, focusNode); buttonState.setMenuState(state, focusNode);
displayContainer.switchState(ButtonMenu.class); displayContainer.switchState(buttonState);
} }
return; return;
} }
@ -922,19 +910,19 @@ public class SongMenu extends ComplexOpsuState {
if (UI.getBackButton().contains(x, y)) { if (UI.getBackButton().contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
displayContainer.switchState(MainMenu.class); displayContainer.switchState(mainmenuState);
return true; return true;
} }
// selection buttons // selection buttons
if (selectModsButton.contains(x, y)) { if (selectModsButton.contains(x, y)) {
this.keyPressed(Input.KEY_F1, '\0'); this.keyPressed(KEY_F1, '\0');
return true; return true;
} else if (selectRandomButton.contains(x, y)) { } else if (selectRandomButton.contains(x, y)) {
this.keyPressed(Input.KEY_F2, '\0'); this.keyPressed(KEY_F2, '\0');
return true; return true;
} else if (selectMapOptionsButton.contains(x, y)) { } else if (selectMapOptionsButton.contains(x, y)) {
this.keyPressed(Input.KEY_F3, '\0'); this.keyPressed(KEY_F3, '\0');
return true; return true;
} else if (selectOptionsButton.contains(x, y)) { } else if (selectOptionsButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
@ -944,30 +932,32 @@ public class SongMenu extends ComplexOpsuState {
// group tabs // group tabs
for (BeatmapGroup group : BeatmapGroup.values()) { for (BeatmapGroup group : BeatmapGroup.values()) {
if (group.contains(x, y)) { if (!group.contains(x, y)) {
if (group != BeatmapGroup.current()) { continue;
BeatmapGroup.set(group); }
SoundController.playSound(SoundEffect.MENUCLICK); if (group == BeatmapGroup.current()) {
startNode = focusNode = null;
oldFocusNode = null;
randomStack = new Stack<SongNode>();
songInfo = null;
scoreMap = null;
focusScores = null;
searchTextField.setText("");
searchTimer = SEARCH_DELAY;
searchTransitionTimer = SEARCH_TRANSITION_TIME;
searchResultString = null;
BeatmapSetList.get().reset();
BeatmapSetList.get().init();
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
if (BeatmapSetList.get().size() < 1 && group.getEmptyMessage() != null) {
EventBus.post(new BarNotificationEvent(group.getEmptyMessage()));
}
}
return true; return true;
} }
BeatmapGroup.set(group);
SoundController.playSound(SoundEffect.MENUCLICK);
startNode = focusNode = null;
oldFocusNode = null;
randomStack = new Stack<SongNode>();
songInfo = null;
scoreMap = null;
focusScores = null;
searchTextField.setText("");
searchTimer = SEARCH_DELAY;
searchTransitionTimer = SEARCH_TRANSITION_TIME;
searchResultString = null;
BeatmapSetList.get().reset();
BeatmapSetList.get().init();
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
if (BeatmapSetList.get().size() < 1 && group.getEmptyMessage() != null) {
BarNotifListener.EVENT.make().onBarNotif(group.getEmptyMessage());
}
return true;
} }
if (focusNode == null) { if (focusNode == null) {
@ -1029,12 +1019,12 @@ public class SongMenu extends ComplexOpsuState {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
if (button != Input.MOUSE_RIGHT_BUTTON) { if (button != Input.MOUSE_RIGHT_BUTTON) {
// view score // view score
instanceContainer.provide(GameRanking.class).setGameData(instanceContainer.injectFields(new GameData(focusScores[rank], displayContainer.width, displayContainer.height))); gameRankingState.setGameData(new GameData(focusScores[rank]));
displayContainer.switchState(GameRanking.class); displayContainer.switchState(gameRankingState);
} else { } else {
// score management // score management
instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.SCORE, focusScores[rank]); buttonState.setMenuState(MenuState.SCORE, focusScores[rank]);
displayContainer.switchState(ButtonMenu.class); displayContainer.switchState(buttonState);
} }
return true; return true;
} }
@ -1050,14 +1040,12 @@ public class SongMenu extends ComplexOpsuState {
} }
// block input // block input
if ((reloadThread != null && key != Input.KEY_ESCAPE) || beatmapMenuTimer > -1 || isScrollingToFocusNode) { if ((reloadThread != null && key != KEY_ESCAPE) || beatmapMenuTimer > -1 || isScrollingToFocusNode) {
return true; return true;
} }
Input input = displayContainer.input;
switch (key) { switch (key) {
case Input.KEY_ESCAPE: case KEY_ESCAPE:
if (reloadThread != null) { if (reloadThread != null) {
// beatmap reloading: stop parsing beatmaps by sending interrupt to BeatmapParser // beatmap reloading: stop parsing beatmaps by sending interrupt to BeatmapParser
reloadThread.interrupt(); reloadThread.interrupt();
@ -1070,19 +1058,19 @@ public class SongMenu extends ComplexOpsuState {
} else { } else {
// return to main menu // return to main menu
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
displayContainer.switchState(MainMenu.class); displayContainer.switchState(mainmenuState);
} }
return true; return true;
case Input.KEY_F1: case KEY_F1:
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.MODS); buttonState.setMenuState(MenuState.MODS);
displayContainer.switchState(ButtonMenu.class); displayContainer.switchState(buttonState);
return true; return true;
case Input.KEY_F2: case KEY_F2:
if (focusNode == null) if (focusNode == null)
break; break;
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
if (input.isKeyDown(Input.KEY_RSHIFT) || input.isKeyDown(Input.KEY_LSHIFT)) { if (isKeyDown(KEY_RSHIFT) || isKeyDown(KEY_LSHIFT)) {
// shift key: previous random track // shift key: previous random track
SongNode prev; SongNode prev;
if (randomStack.isEmpty() || (prev = randomStack.pop()) == null) if (randomStack.isEmpty() || (prev = randomStack.pop()) == null)
@ -1098,47 +1086,47 @@ public class SongMenu extends ComplexOpsuState {
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
} }
return true; return true;
case Input.KEY_F3: case KEY_F3:
if (focusNode == null) if (focusNode == null)
break; break;
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
MenuState state = focusNode.getBeatmapSet().isFavorite() ? MenuState state = focusNode.getBeatmapSet().isFavorite() ?
MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP; MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP;
instanceContainer.provide(ButtonMenu.class).setMenuState(state, focusNode); buttonState.setMenuState(state, focusNode);
displayContainer.switchState(ButtonMenu.class); displayContainer.switchState(buttonState);
return true; return true;
case Input.KEY_F5: case KEY_F5:
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
if (songFolderChanged) if (songFolderChanged)
reloadBeatmaps(false); reloadBeatmaps(false);
else { else {
instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.RELOAD); buttonState.setMenuState(MenuState.RELOAD);
displayContainer.switchState(ButtonMenu.class); displayContainer.switchState(buttonState);
} }
return true; return true;
case Input.KEY_DELETE: case KEY_DELETE:
if (focusNode == null) if (focusNode == null)
break; break;
if (input.isKeyDown(Input.KEY_RSHIFT) || input.isKeyDown(Input.KEY_LSHIFT)) { if (isKeyDown(KEY_RSHIFT) || isKeyDown(KEY_LSHIFT)) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
MenuState ms = (focusNode.beatmapIndex == -1 || focusNode.getBeatmapSet().size() == 1) ? MenuState ms = (focusNode.beatmapIndex == -1 || focusNode.getBeatmapSet().size() == 1) ?
MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT; MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT;
instanceContainer.provide(ButtonMenu.class).setMenuState(ms, focusNode); buttonState.setMenuState(ms, focusNode);
displayContainer.switchState(ButtonMenu.class); displayContainer.switchState(buttonState);
} }
return true; return true;
case Input.KEY_ENTER: case KEY_RETURN:
if (focusNode == null) if (focusNode == null)
break; break;
startGame(); startGame();
return true; return true;
case Input.KEY_DOWN: case KEY_DOWN:
changeIndex(1); changeIndex(1);
return true; return true;
case Input.KEY_UP: case KEY_UP:
changeIndex(-1); changeIndex(-1);
return true; return true;
case Input.KEY_RIGHT: case KEY_RIGHT:
if (focusNode == null) if (focusNode == null)
break; break;
BeatmapSetNode next = focusNode.next; BeatmapSetNode next = focusNode.next;
@ -1154,7 +1142,7 @@ public class SongMenu extends ComplexOpsuState {
} }
} }
return true; return true;
case Input.KEY_LEFT: case KEY_LEFT:
if (focusNode == null) if (focusNode == null)
break; break;
BeatmapSetNode prev = focusNode.prev; BeatmapSetNode prev = focusNode.prev;
@ -1170,25 +1158,25 @@ public class SongMenu extends ComplexOpsuState {
} }
} }
return true; return true;
case Input.KEY_NEXT: case KEY_NEXT:
changeIndex(MAX_SONG_BUTTONS); changeIndex(MAX_SONG_BUTTONS);
return true; return true;
case Input.KEY_PRIOR: case KEY_PRIOR:
changeIndex(-MAX_SONG_BUTTONS); changeIndex(-MAX_SONG_BUTTONS);
return true; return true;
} }
if (key == Input.KEY_O && (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL))) { if (key == KEY_O && input.isControlDown()) {
optionsOverlay.show(); optionsOverlay.show();
return true; return true;
} }
// wait for user to finish typing // wait for user to finish typing
// TODO: accept all characters (current conditions are from TextField class) // TODO: accept all characters (current conditions are from TextField class)
if ((c > 31 && c < 127) || key == Input.KEY_BACK) { if ((c > 31 && c < 127) || key == KEY_BACK) {
searchTimer = 0; searchTimer = 0;
searchTextField.keyPressed(key, c); searchTextField.keyPressed(key, c);
int textLength = searchTextField.getText().length(); int textLength = searchTextField.getText().length();
if (lastSearchTextLength != textLength) { if (lastSearchTextLength != textLength) {
if (key == Input.KEY_BACK) { if (key == KEY_BACK) {
if (textLength == 0) if (textLength == 0)
searchTransitionTimer = 0; searchTransitionTimer = 0;
} else if (textLength == 1) } else if (textLength == 1)
@ -1216,9 +1204,9 @@ public class SongMenu extends ComplexOpsuState {
// check mouse button (right click scrolls faster on songs) // check mouse button (right click scrolls faster on songs)
int multiplier; int multiplier;
if (displayContainer.input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) { if (Mouse.isButtonDown(Input.MOUSE_RIGHT_BUTTON)) {
multiplier = 10; multiplier = 10;
} else if (displayContainer.input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) { } else if (Mouse.isButtonDown(Input.MOUSE_LEFT_BUTTON)) {
multiplier = 1; multiplier = 1;
} else { } else {
return false; return false;
@ -1238,8 +1226,6 @@ public class SongMenu extends ComplexOpsuState {
return true; return true;
} }
Input input = displayContainer.input;
if (isInputBlocked()) { if (isInputBlocked()) {
return true; return true;
} }
@ -1310,7 +1296,7 @@ public class SongMenu extends ComplexOpsuState {
// reset game data // reset game data
if (resetGame) { if (resetGame) {
instanceContainer.provide(Game.class).resetGameData(); gameState.resetGameData();
// destroy extra Clips // destroy extra Clips
MultiClip.destroyExtraClips(); MultiClip.destroyExtraClips();
@ -1775,22 +1761,20 @@ public class SongMenu extends ComplexOpsuState {
Beatmap beatmap = MusicController.getBeatmap(); Beatmap beatmap = MusicController.getBeatmap();
if (focusNode == null || beatmap != focusNode.getSelectedBeatmap()) { if (focusNode == null || beatmap != focusNode.getSelectedBeatmap()) {
EventBus.post(new BarNotificationEvent("Unable to load the beatmap audio.")); BarNotifListener.EVENT.make().onBarNotif("Unable to load the beatmap audio.");
return; return;
} }
// turn on "auto" mod if holding "ctrl" key // turn on "auto" mod if holding "ctrl" key
if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) { if (input.isControlDown() && !GameMod.AUTO.isActive()) {
if (!GameMod.AUTO.isActive()) GameMod.AUTO.toggle(true);
GameMod.AUTO.toggle(true);
} }
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
MultiClip.destroyExtraClips(); MultiClip.destroyExtraClips();
Game gameState = instanceContainer.provide(Game.class);
gameState.loadBeatmap(beatmap); gameState.loadBeatmap(beatmap);
gameState.setRestart(Game.Restart.NEW); gameState.setRestart(Game.Restart.NEW);
gameState.setReplay(null); gameState.setReplay(null);
displayContainer.switchState(Game.class); displayContainer.switchState(gameState);
} }
} }

View File

@ -21,20 +21,18 @@ package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.BeatmapSetList; import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.ui.UI; import itdelatrisu.opsu.ui.UI;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.BaseOpsuState;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
@ -44,18 +42,6 @@ import static yugecin.opsudance.options.Options.*;
*/ */
public class Splash extends BaseOpsuState { public class Splash extends BaseOpsuState {
@Inject
private SongMenu songMenu;
@Inject
private ReplayImporter replayImporter;
@Inject
private OszUnpacker oszUnpacker;
@Inject
private BeatmapParser beatmapParser;
/** Whether or not loading has completed. */ /** Whether or not loading has completed. */
private boolean finished; private boolean finished;
@ -73,7 +59,7 @@ public class Splash extends BaseOpsuState {
super.revalidate(); super.revalidate();
// pre-revalidate some states to reduce lag between switching // pre-revalidate some states to reduce lag between switching
songMenu.revalidate(); songMenuState.revalidate();
if (inited) { if (inited) {
return; return;
@ -86,7 +72,7 @@ public class Splash extends BaseOpsuState {
thread = new Thread() { thread = new Thread() {
@Override @Override
public void run() { public void run() {
oszUnpacker.unpackAll(); oszunpacker.unpackAll();
beatmapParser.parseAll(); beatmapParser.parseAll();
replayImporter.importAll(); replayImporter.importAll();
@ -109,7 +95,7 @@ public class Splash extends BaseOpsuState {
// initialize song list // initialize song list
if (BeatmapSetList.get().size() == 0) { if (BeatmapSetList.get().size() == 0) {
MusicController.playThemeSong(config.themeBeatmap); MusicController.playThemeSong(config.themeBeatmap);
displayContainer.switchStateInstantly(MainMenu.class); displayContainer.switchStateInstantly(mainmenuState);
return; return;
} }
@ -117,9 +103,9 @@ public class Splash extends BaseOpsuState {
if (OPTION_ENABLE_THEME_SONG.state) { if (OPTION_ENABLE_THEME_SONG.state) {
MusicController.playThemeSong(config.themeBeatmap); MusicController.playThemeSong(config.themeBeatmap);
} else { } else {
songMenu.setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); songMenuState.setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
} }
displayContainer.switchStateInstantly(MainMenu.class); displayContainer.switchStateInstantly(mainmenuState);
} }
@Override @Override
@ -147,7 +133,7 @@ public class Splash extends BaseOpsuState {
@Override @Override
public boolean keyPressed(int key, char c) { public boolean keyPressed(int key, char c) {
if (key != Input.KEY_ESCAPE) { if (key != Keyboard.KEY_ESCAPE) {
return false; return false;
} }
if (++escapeCount >= 3) { if (++escapeCount >= 3) {

View File

@ -49,6 +49,11 @@ public class Colors {
BLACK_BG_NORMAL = new Color(0, 0, 0, 0.25f), BLACK_BG_NORMAL = new Color(0, 0, 0, 0.25f),
BLACK_BG_HOVER = new Color(0, 0, 0, 0.5f), BLACK_BG_HOVER = new Color(0, 0, 0, 0.5f),
BLACK_BG_FOCUS = new Color(0, 0, 0, 0.75f), BLACK_BG_FOCUS = new Color(0, 0, 0, 0.75f),
BUB_GREEN = new Color(98, 131, 59),
BUB_WHITE = new Color(220, 220, 220),
BUB_PURPLE = new Color(94, 46, 149),
BUB_RED = new Color(141, 49, 16),
BUB_ORANGE = new Color(138, 72, 51),
GHOST_LOGO = new Color(1.0f, 1.0f, 1.0f, 0.25f); GHOST_LOGO = new Color(1.0f, 1.0f, 1.0f, 0.25f);
// This class should not be instantiated. // This class should not be instantiated.

View File

@ -22,6 +22,7 @@ import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.ui.animations.AnimatedValue; import itdelatrisu.opsu.ui.animations.AnimatedValue;
import itdelatrisu.opsu.ui.animations.AnimationEquation; import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Font; import org.newdawn.slick.Font;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
@ -82,7 +83,7 @@ public class DropdownMenu<E> extends Component {
@Override @Override
public void keyPressed(int key, char c) { public void keyPressed(int key, char c) {
if (key == Input.KEY_ESCAPE) { if (key == Keyboard.KEY_ESCAPE) {
this.expanded = false; this.expanded = false;
} }
} }

View File

@ -34,7 +34,7 @@ import org.newdawn.slick.font.effects.ColorEffect;
import org.newdawn.slick.font.effects.Effect; import org.newdawn.slick.font.effects.Effect;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.options.Configuration; import yugecin.opsudance.core.Constants;
/** /**
* Fonts used for drawing. * Fonts used for drawing.
@ -54,9 +54,9 @@ public class Fonts {
* @throws FontFormatException if any font stream data does not contain the required font tables * @throws FontFormatException if any font stream data does not contain the required font tables
* @throws IOException if a font stream cannot be completely read * @throws IOException if a font stream cannot be completely read
*/ */
public static void init(Configuration config) throws SlickException, FontFormatException, IOException { public static void init() throws SlickException, FontFormatException, IOException {
float fontBase = 12f * GameImage.getUIscale(); float fontBase = 12f * GameImage.getUIscale();
Font javaFont = Font.createFont(Font.TRUETYPE_FONT, ResourceLoader.getResourceAsStream(config.FONT_NAME)); Font javaFont = Font.createFont(Font.TRUETYPE_FONT, ResourceLoader.getResourceAsStream(Constants.FONT_NAME));
Font font = javaFont.deriveFont(Font.PLAIN, (int) (fontBase * 4 / 3)); Font font = javaFont.deriveFont(Font.PLAIN, (int) (fontBase * 4 / 3));
DEFAULT = new UnicodeFont(font); DEFAULT = new UnicodeFont(font);
BOLD = new UnicodeFont(font.deriveFont(Font.BOLD)); BOLD = new UnicodeFont(font.deriveFont(Font.BOLD));

View File

@ -47,7 +47,7 @@ public class KineticScrolling {
private float totalDelta; private float totalDelta;
/** The maximum and minimum value the position can reach. */ /** The maximum and minimum value the position can reach. */
private float max = Float.MAX_VALUE, min = -Float.MAX_VALUE; public float max = Float.MAX_VALUE, min = -Float.MAX_VALUE;
/** Whether the mouse is currently pressed or not. */ /** Whether the mouse is currently pressed or not. */
private boolean pressed = false; private boolean pressed = false;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
package org.newdawn.slick;
/**
* A listener that will be notified of keyboard and mouse events
* Edited for opsu!
*
* @author kevin
*/
public interface InputListener extends MouseListener, KeyListener {
}

View File

@ -0,0 +1,27 @@
package org.newdawn.slick;
/**
* Describes classes capable of responding to key presses
* Edited for opsu!
*
* @author kevin
*/
public interface KeyListener {
/**
* Notification that a key was pressed
*
* @param key The key code that was pressed (@see org.newdawn.slick.Input)
* @param c The character of the key that was pressed
*/
boolean keyPressed(int key, char c);
/**
* Notification that a key was released
*
* @param key The key code that was released (@see org.newdawn.slick.Input)
* @param c The character of the key that was released
*/
boolean keyReleased(int key, char c);
}

View File

@ -0,0 +1,46 @@
package org.newdawn.slick;
/**
* Description of classes that respond to mouse related input events
* Edited for opsu!
*
* @author kevin
*/
public interface MouseListener {
/**
* Notification that the mouse wheel position was updated
*
* @param delta The amount of the wheel has moved
*/
boolean mouseWheelMoved(int delta);
/**
* Notification that a mouse button was pressed
*
* @param button The index of the button (starting at 0)
* @param x The x position of the mouse when the button was pressed
* @param y The y position of the mouse when the button was pressed
*/
boolean mousePressed(int button, int x, int y);
/**
* Notification that a mouse button was released
*
* @param button The index of the button (starting at 0)
* @param x The x position of the mouse when the button was released
* @param y The y position of the mouse when the button was released
*/
boolean mouseReleased(int button, int x, int y);
/**
* Notification that mouse cursor was dragged
*
* @param oldx The old x position of the mouse
* @param oldy The old y position of the mouse
* @param newx The new x position of the mouse
* @param newy The new y position of the mouse
*/
boolean mouseDragged(int oldx, int oldy, int newx, int newy);
}

View File

@ -32,12 +32,13 @@ import org.lwjgl.Sys;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Font; import org.newdawn.slick.Font;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.geom.Rectangle; import org.newdawn.slick.geom.Rectangle;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.components.ActionListener; import yugecin.opsudance.core.components.ActionListener;
import yugecin.opsudance.core.components.Component; import yugecin.opsudance.core.components.Component;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* A single text field supporting text entry * A single text field supporting text entry
* *
@ -48,8 +49,6 @@ public class TextField extends Component {
private static final int INITIAL_KEY_REPEAT_INTERVAL = 400; private static final int INITIAL_KEY_REPEAT_INTERVAL = 400;
private static final int KEY_REPEAT_INTERVAL = 50; private static final int KEY_REPEAT_INTERVAL = 50;
private final DisplayContainer displayContainer;
private String value = ""; private String value = "";
private Font font; private Font font;
private int maxCharacter = 10000; private int maxCharacter = 10000;
@ -65,8 +64,7 @@ public class TextField extends Component {
private ActionListener listener; private ActionListener listener;
public TextField(DisplayContainer displayContainer, Font font, int x, int y, int width, int height) { public TextField(Font font, int x, int y, int width, int height) {
this.displayContainer = displayContainer;
this.font = font; this.font = font;
this.x = x; this.x = x;
this.y = y; this.y = y;
@ -97,7 +95,7 @@ public class TextField extends Component {
public void render(Graphics g) { public void render(Graphics g) {
if (lastKey != -1) { if (lastKey != -1) {
if (displayContainer.input.isKeyDown(lastKey)) { if (isKeyDown(lastKey)) {
if (repeatTimer < System.currentTimeMillis()) { if (repeatTimer < System.currentTimeMillis()) {
repeatTimer = System.currentTimeMillis() + KEY_REPEAT_INTERVAL; repeatTimer = System.currentTimeMillis() + KEY_REPEAT_INTERVAL;
keyPressed(lastKey, lastChar); keyPressed(lastKey, lastChar);
@ -170,10 +168,8 @@ public class TextField extends Component {
} }
public void keyPressed(int key, char c) { public void keyPressed(int key, char c) {
if (key != -1) if (key != -1) {
{ if (key == KEY_V && input.isControlDown()) {
if ((key == Input.KEY_V) &&
((displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) || (displayContainer.input.isKeyDown(Input.KEY_RCONTROL)))) {
String text = Sys.getClipboard(); String text = Sys.getClipboard();
if (text != null) { if (text != null) {
doPaste(text); doPaste(text);
@ -190,7 +186,7 @@ public class TextField extends Component {
} }
lastChar = c; lastChar = c;
if (key == Input.KEY_LEFT) { /* if (key == KEY_LEFT) { /*
if (cursorPos > 0) { if (cursorPos > 0) {
cursorPos--; cursorPos--;
} }
@ -198,7 +194,7 @@ public class TextField extends Component {
if (consume) { if (consume) {
container.getInput().consumeEvent(); container.getInput().consumeEvent();
} }
*/ } else if (key == Input.KEY_RIGHT) { /* */ } else if (key == KEY_RIGHT) { /*
if (cursorPos < value.length()) { if (cursorPos < value.length()) {
cursorPos++; cursorPos++;
} }
@ -206,9 +202,9 @@ public class TextField extends Component {
if (consume) { if (consume) {
container.getInput().consumeEvent(); container.getInput().consumeEvent();
} }
*/ } else if (key == Input.KEY_BACK) { */ } else if (key == KEY_BACK) {
if ((cursorPos > 0) && (value.length() > 0)) { if ((cursorPos > 0) && (value.length() > 0)) {
if (displayContainer.input.isKeyDown(Input.KEY_LCONTROL) || displayContainer.input.isKeyDown(Input.KEY_RCONTROL)) { if (input.isControlDown()) {
int sp = 0; int sp = 0;
boolean startSpace = Character.isWhitespace(value.charAt(cursorPos - 1)); boolean startSpace = Character.isWhitespace(value.charAt(cursorPos - 1));
boolean charSeen = false; boolean charSeen = false;
@ -240,7 +236,7 @@ public class TextField extends Component {
cursorPos--; cursorPos--;
} }
} }
} else if (key == Input.KEY_DELETE) { } else if (key == KEY_DELETE) {
if (value.length() > cursorPos) { if (value.length() > cursorPos) {
value = value.substring(0,cursorPos) + value.substring(cursorPos+1); value = value.substring(0,cursorPos) + value.substring(cursorPos+1);
} }
@ -252,7 +248,7 @@ public class TextField extends Component {
value = value.substring(0, cursorPos) + c; value = value.substring(0, cursorPos) + c;
} }
cursorPos++; cursorPos++;
} else if (key == Input.KEY_RETURN) { } else if (key == KEY_RETURN) {
if (listener != null) { if (listener != null) {
listener.onAction(); listener.onAction();
} }

View File

@ -1,82 +0,0 @@
package org.newdawn.slick.state.transition;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.state.GameState;
import org.newdawn.slick.state.StateBasedGame;
/**
* A transition to fade out to a given colour after a delay.
*
* @author kevin (base)
*/
public class DelayedFadeOutTransition implements Transition {
/** The color to fade to */
private final Color color;
/** The time it takes the fade to happen */
private final int fadeTime;
/** The time it takes before the fade starts */
private final int delay;
/** The elapsed time */
private int elapsedTime = 0;
/**
* Create a new delayed fade out transition.
*/
public DelayedFadeOutTransition() { this(Color.black, 500, 0); }
/**
* Create a new delayed fade out transition.
*
* @param color The color we're going to fade out to
*/
public DelayedFadeOutTransition(Color color) { this(color, 500, 0); }
/**
* Create a new delayed fade out transition
*
* @param color The color we're going to fade out to
* @param fadeTime The time it takes the fade to occur
* @param delay The time before the fade starts (must be less than {@code fadeTime})
* @throws IllegalArgumentException if {@code delay} is greater than {@code fadeTime}
*/
public DelayedFadeOutTransition(Color color, int fadeTime, int delay) {
if (delay > fadeTime)
throw new IllegalArgumentException();
this.color = new Color(color);
this.color.a = 0;
this.fadeTime = fadeTime;
this.delay = delay;
}
@Override
public boolean isComplete() { return (color.a >= 1); }
@Override
public void postRender(StateBasedGame game, GameContainer container, Graphics g) {
Color old = g.getColor();
g.setColor(color);
g.fillRect(0, 0, container.getWidth() * 2, container.getHeight() * 2);
g.setColor(old);
}
@Override
public void update(StateBasedGame game, GameContainer container, int delta) {
if (elapsedTime < delay) {
elapsedTime += delta;
return;
}
color.a += delta * (1.0f / (fadeTime - delay));
if (color.a > 1)
color.a = 1;
}
@Override
public void preRender(StateBasedGame game, GameContainer container, Graphics g) {}
@Override
public void init(GameState firstState, GameState secondState) {}
}

View File

@ -1,83 +0,0 @@
package org.newdawn.slick.state.transition;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.state.GameState;
import org.newdawn.slick.state.StateBasedGame;
/**
* A transition to fade out to a given colour using an easing function.
*
* @author kevin (base)
*/
public class EasedFadeOutTransition implements Transition {
/** The color to fade to */
private final Color color;
/** The time it takes the fade to happen */
private final int fadeTime;
/** The easing function */
private final AnimationEquation eq;
/** The transition progress */
private float t = 0f;
/**
* Create a new eased fade out transition.
*/
public EasedFadeOutTransition() { this(Color.black, 500, AnimationEquation.OUT_QUART); }
/**
* Create a new eased fade out transition.
*
* @param color The color we're going to fade out to
*/
public EasedFadeOutTransition(Color color) { this(color, 500, AnimationEquation.OUT_QUART); }
/**
* Create a new eased fade out transition.
*
* @param color The color we're going to fade out to
* @param fadeTime The time it takes the fade to occur
*/
public EasedFadeOutTransition(Color color, int fadeTime) { this(color, fadeTime, AnimationEquation.OUT_QUART); }
/**
* Create a new eased fade out transition.
*
* @param color The color we're going to fade out to
* @param fadeTime The time it takes the fade to occur
* @param eq The easing function to use
*/
public EasedFadeOutTransition(Color color, int fadeTime, AnimationEquation eq) {
this.color = new Color(color);
this.color.a = 0;
this.fadeTime = fadeTime;
this.eq = eq;
}
@Override
public boolean isComplete() { return (color.a >= 1); }
@Override
public void postRender(StateBasedGame game, GameContainer container, Graphics g) {
Color old = g.getColor();
g.setColor(color);
g.fillRect(0, 0, container.getWidth() * 2, container.getHeight() * 2);
g.setColor(old);
}
@Override
public void update(StateBasedGame game, GameContainer container, int delta) {
t += delta * (1.0f / fadeTime);
float alpha = t > 1f ? 1f : eq.calc(t);
color.a = alpha;
}
@Override
public void preRender(StateBasedGame game, GameContainer container, Graphics g) {}
@Override
public void init(GameState firstState, GameState secondState) {}
}

View File

@ -40,6 +40,7 @@ import yugecin.opsudance.spinners.*;
import java.awt.*; import java.awt.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
public class Dancer { public class Dancer {
@ -194,12 +195,12 @@ public class Dancer {
} }
isCurrentLazySlider = false; isCurrentLazySlider = false;
// detect lazy sliders, should work pretty good // detect lazy sliders, should work pretty good
if (c.isSlider() && OPTION_DANCE_LAZY_SLIDERS.state && Utils.distance(c.start.x, c.start.y, c.end.x, c.end.y) <= GameObjectRenderer.instance.getCircleDiameter() * 0.8f) { if (c.isSlider() && OPTION_DANCE_LAZY_SLIDERS.state && Utils.distance(c.start.x, c.start.y, c.end.x, c.end.y) <= gameObjectRenderer.circleDiameter * 0.8f) {
Slider s = (Slider) c; Slider s = (Slider) c;
Vec2f mid = s.getCurve().pointAt(1f); Vec2f mid = s.getCurve().pointAt(1f);
if (s.getRepeats() == 1 || Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= GameObjectRenderer.instance.getCircleDiameter() * 0.8f) { if (s.getRepeats() == 1 || Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= gameObjectRenderer.circleDiameter * 0.8f) {
mid = s.getCurve().pointAt(0.5f); mid = s.getCurve().pointAt(0.5f);
if (Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= GameObjectRenderer.instance.getCircleDiameter() * 0.8f) { if (Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= gameObjectRenderer.circleDiameter * 0.8f) {
isCurrentLazySlider = true; isCurrentLazySlider = true;
} }
} }
@ -251,8 +252,8 @@ public class Dancer {
} }
} }
Pippi.dance(time, c, isCurrentLazySlider); Pippi.dance(time, c, isCurrentLazySlider);
x = Utils.clamp(x, 10, width - 10); x = Utils.clamp(x, 10, displayContainer.width - 10);
y = Utils.clamp(y, 10, height - 10); y = Utils.clamp(y, 10, displayContainer.height - 10);
} }
private void createNewMover() { private void createNewMover() {

View File

@ -21,22 +21,16 @@ import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.BeatmapWatchService; import itdelatrisu.opsu.beatmap.BeatmapWatchService;
import itdelatrisu.opsu.db.DBController; import itdelatrisu.opsu.db.DBController;
import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.states.Splash;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionsService;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.Entrypoint.sout; import static yugecin.opsudance.core.Entrypoint.sout;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/* /*
@ -44,30 +38,13 @@ import static yugecin.opsudance.options.Options.*;
*/ */
public class OpsuDance { public class OpsuDance {
@Inject
private DisplayContainer container;
@Inject
private OptionsService optionsService;
@Inject
private Configuration config;
@Inject
private Updater updater;
private ServerSocket singleInstanceSocket; private ServerSocket singleInstanceSocket;
@Inject
public OpsuDance() {
}
public void start(String[] args) { public void start(String[] args) {
try { try {
sout("initialized"); sout("initialized");
checkRunningDirectory(); optionservice.loadOptions();
optionsService.loadOptions();
ensureSingleInstance(); ensureSingleInstance();
sout("prechecks done and options parsed"); sout("prechecks done and options parsed");
@ -75,15 +52,15 @@ public class OpsuDance {
initUpdater(args); initUpdater(args);
sout("database & updater initialized"); sout("database & updater initialized");
container.init(Splash.class); displayContainer.init(splashState);
} catch (Exception e) { } catch (Exception e) {
errorAndExit("startup failure", e); errorAndExit("startup failure", e);
} }
while (rungame()); while (rungame());
container.teardownAL(); displayContainer.teardownAL();
optionsService.saveOptions(); optionservice.saveOptions();
closeSingleInstanceSocket(); closeSingleInstanceSocket();
DBController.closeConnections(); DBController.closeConnections();
DownloadList.get().cancelAllDownloads(); DownloadList.get().cancelAllDownloads();
@ -95,26 +72,26 @@ public class OpsuDance {
private boolean rungame() { private boolean rungame() {
try { try {
container.setup(); displayContainer.setup();
container.resume(); displayContainer.resume();
} catch (Exception e) { } catch (Exception e) {
ErrorHandler.error("could not initialize GL", e).allowTerminate().preventContinue().show(); explode("could not initialize GL", e, ALLOW_TERMINATE | PREVENT_CONTINUE);
return false; return false;
} }
Exception caughtException = null; Exception caughtException = null;
try { try {
container.run(); displayContainer.run();
} catch (Exception e) { } catch (Exception e) {
caughtException = e; caughtException = e;
} }
container.teardown(); displayContainer.teardown();
container.pause(); displayContainer.pause();
return caughtException != null && ErrorHandler.error("update/render error", caughtException).allowTerminate().show().shouldIgnoreAndContinue(); return caughtException != null && explode("update/render error", caughtException, ALLOW_TERMINATE);
} }
private void initDatabase() { private void initDatabase() {
try { try {
DBController.init(config); DBController.init();
} catch (UnsatisfiedLinkError e) { } catch (UnsatisfiedLinkError e) {
errorAndExit("Could not initialize database.", e); errorAndExit("Could not initialize database.", e);
} }
@ -142,20 +119,6 @@ public class OpsuDance {
}.start(); }.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() { private void ensureSingleInstance() {
if (OPTION_NOSINGLEINSTANCE.state) { if (OPTION_NOSINGLEINSTANCE.state) {
return; return;
@ -183,13 +146,8 @@ public class OpsuDance {
} }
} }
private void errorAndExit(String errstr) {
ErrorHandler.error(errstr, new Throwable()).allowTerminate().preventContinue().show();
System.exit(1);
}
private void errorAndExit(String errstr, Throwable cause) { private void errorAndExit(String errstr, Throwable cause) {
ErrorHandler.error(errstr, cause).preventContinue().show(); explode(errstr, cause, PREVENT_CONTINUE);
System.exit(1); System.exit(1);
} }

View File

@ -19,9 +19,9 @@ package yugecin.opsudance;
import itdelatrisu.opsu.objects.GameObject; import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.Slider; import itdelatrisu.opsu.objects.Slider;
import yugecin.opsudance.render.GameObjectRenderer;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
public class Pippi { public class Pippi {
@ -38,14 +38,14 @@ public class Pippi {
public static void setRadiusPercent(int radiusPercent) { public static void setRadiusPercent(int radiusPercent) {
Pippi.radiusPercent = radiusPercent; Pippi.radiusPercent = radiusPercent;
pippiminrad = pippirad = (GameObjectRenderer.instance.getCircleDiameter() / 2d - 10d) * radiusPercent / 100d; pippiminrad = pippirad = (gameObjectRenderer.circleDiameter / 2d - 10d) * radiusPercent / 100d;
} }
public static void reset() { public static void reset() {
angle = 0; angle = 0;
currentdelta = 0; currentdelta = 0;
setRadiusPercent(radiusPercent); setRadiusPercent(radiusPercent);
pippimaxrad = GameObjectRenderer.instance.getCircleDiameter() - 10d; pippimaxrad = gameObjectRenderer.circleDiameter - 10d;
} }
public static void dance(int time, GameObject c, boolean isCurrentLazySlider) { public static void dance(int time, GameObject c, boolean isCurrentLazySlider) {
@ -92,7 +92,7 @@ public class Pippi {
} }
public static boolean shouldPreventWobblyStream(double distance) { public static boolean shouldPreventWobblyStream(double distance) {
return OPTION_PIPPI_ENABLE.state && distance < GameObjectRenderer.instance.getCircleDiameter() * 0.93f && OPTION_PIPPI_PREVENT_WOBBLY_STREAMS.state; return OPTION_PIPPI_ENABLE.state && distance < gameObjectRenderer.circleDiameter * 0.93f && OPTION_PIPPI_PREVENT_WOBBLY_STREAMS.state;
} }
} }

View File

@ -1,73 +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 <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance;
import itdelatrisu.opsu.NativeLoader;
import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.options.Configuration;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
public class PreStartupInitializer {
private final Configuration config;
@Inject
public PreStartupInitializer(Configuration config) {
this.config = config;
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
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);
}
}
private File loadNativesUsingOptionsPath() {
File nativeDir = config.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/")));
}
}

View File

@ -15,28 +15,18 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>. * along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/ */
package yugecin.opsudance.core.state.transitions; package yugecin.opsudance.core;
import org.newdawn.slick.Color; import java.net.URI;
import org.newdawn.slick.Graphics;
public abstract class FadeTransitionState extends TransitionState { public class Constants {
private final Color black; public static final String PROJECT_NAME = "opsu!dance";
public static final String FONT_NAME = "DroidSansFallback.ttf";
public FadeTransitionState() { public static final String VERSION_FILE = "version";
super(); public static final URI REPOSITORY_URI = URI.create("https://github.com/itdelatrisu/opsu");
black = new Color(Color.black); public static final URI DANCE_REPOSITORY_URI = URI.create("https://github.com/yugecin/opsu-dance");
} public static final String ISSUES_URL = "https://github.com/yugecin/opsu-dance/issues/new?title=%s&body=%s";
public static final String VERSION_REMOTE = "https://raw.githubusercontent.com/yugecin/opsu-dance/master/version";
@Override
public void render(Graphics g) {
applicableState.render(g);
black.a = getMaskAlphaLevel((float) transitionTime / transitionTargetTime);
g.setColor(black);
g.fillRect(0, 0, displayContainer.width, displayContainer.height);
}
protected abstract float getMaskAlphaLevel(float fadeProgress);
} }

View File

@ -26,10 +26,12 @@ import itdelatrisu.opsu.downloads.DownloadNode;
import itdelatrisu.opsu.downloads.Updater; import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.render.CurveRenderState; import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.replay.PlaybackSpeed; import itdelatrisu.opsu.replay.PlaybackSpeed;
import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.Cursor; import itdelatrisu.opsu.ui.Cursor;
import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.UI; import itdelatrisu.opsu.ui.UI;
import org.lwjgl.Sys; import org.lwjgl.Sys;
import org.lwjgl.input.Mouse;
import org.lwjgl.openal.AL; import org.lwjgl.openal.AL;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode; import org.lwjgl.opengl.DisplayMode;
@ -39,58 +41,38 @@ import org.newdawn.slick.opengl.InternalTextureLoader;
import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.errorhandling.ErrorDumpable; import yugecin.opsudance.core.errorhandling.ErrorDumpable;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.OpsuState; import yugecin.opsudance.core.state.OpsuState;
import yugecin.opsudance.core.state.specialstates.BarNotificationState; import yugecin.opsudance.core.state.specialstates.BarNotificationState;
import yugecin.opsudance.core.state.specialstates.BubbleNotificationState; import yugecin.opsudance.core.state.specialstates.BubNotifState;
import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.specialstates.FpsRenderState;
import yugecin.opsudance.core.state.transitions.*; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent; import yugecin.opsudance.events.SkinChangedListener;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.GLHelper; import yugecin.opsudance.utils.GLHelper;
import java.io.StringWriter; import java.io.StringWriter;
import static yugecin.opsudance.core.Entrypoint.sout; import static yugecin.opsudance.core.Entrypoint.sout;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
/** /**
* based on org.newdawn.slick.AppGameContainer * based on org.newdawn.slick.AppGameContainer
*/ */
public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListener { public class DisplayContainer implements ErrorDumpable, ResolutionChangedListener, SkinChangedListener {
@Inject
private SkinService skinService;
@Inject
private Configuration config;
private static SGL GL = Renderer.get(); private static SGL GL = Renderer.get();
private final InstanceContainer instanceContainer;
private FpsRenderState fpsState; private FpsRenderState fpsState;
private BarNotificationState barNotifState; private BarNotificationState barNotifState;
private BubbleNotificationState bubNotifState; private BubNotifState bubNotifState;
private TransitionState outTransitionState;
private TransitionState inTransitionState;
private final TransitionFinishedListener outTransitionListener;
private final TransitionFinishedListener inTransitionListener;
private OpsuState state; private OpsuState state;
public final DisplayMode nativeDisplayMode; public final DisplayMode nativeDisplayMode;
private Graphics graphics; private Graphics graphics;
public Input input;
public int width; public int width;
public int height; public int height;
@ -123,38 +105,52 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
public final Cursor cursor; public final Cursor cursor;
public boolean drawCursor; public boolean drawCursor;
@Inject class Transition {
public DisplayContainer(InstanceContainer instanceContainer) { int in;
this.instanceContainer = instanceContainer; int out;
int total;
int progress = -1;
OpsuState nextstate;
Color OVERLAY = new Color(Color.black);
public void update() {
if (progress == -1) {
return;
}
progress += delta;
if (progress > out && nextstate != null) {
switchStateInstantly(nextstate);
nextstate = null;
}
if (progress > total) {
progress = -1;
}
}
public void render(Graphics graphics) {
if (progress == -1) {
return;
}
int relprogress = progress;
int reltotal = out;
if (progress > out) {
reltotal = in;
relprogress = total - progress;
}
OVERLAY.a = (float) relprogress / reltotal;
graphics.setColor(OVERLAY);
graphics.fillRect(0, 0, width, height);
}
}
private final Transition transition = new Transition();
public DisplayContainer() {
this.cursor = new Cursor(); this.cursor = new Cursor();
drawCursor = true; drawCursor = true;
outTransitionListener = new TransitionFinishedListener() { ResolutionChangedListener.EVENT.addListener(this);
@Override SkinChangedListener.EVENT.addListener(this);
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();
}
};
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, new EventListener<ResolutionOrSkinChangedEvent>() {
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
destroyImages();
reinit();
}
});
this.nativeDisplayMode = Display.getDisplayMode(); this.nativeDisplayMode = Display.getDisplayMode();
targetBackgroundRenderInterval = 41; // ~24 fps targetBackgroundRenderInterval = 41; // ~24 fps
@ -163,13 +159,25 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
renderDelta = 1; renderDelta = 1;
} }
@Override
public void onResolutionChanged(int w, int h) {
destroyImages();
reinit();
}
@Override
public void onSkinChanged(String stringName) {
destroyImages();
reinit();
}
private void reinit() { private void reinit() {
// this used to be in Utils.init // this used to be in Utils.init
// TODO find a better place for this? // TODO find a better place for this?
setFPS(targetFPS[targetFPSIndex]); setFPS(targetFPS[targetFPSIndex]);
MusicController.setMusicVolume(OPTION_MUSIC_VOLUME.val / 100f * OPTION_MASTER_VOLUME.val / 100f); MusicController.setMusicVolume(OPTION_MUSIC_VOLUME.val / 100f * OPTION_MASTER_VOLUME.val / 100f);
skinService.loadSkin(); skinservice.loadSkin();
// initialize game images // initialize game images
for (GameImage img : GameImage.values()) { for (GameImage img : GameImage.values()) {
@ -196,16 +204,16 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
targetRenderInterval = 1000 / targetRendersPerSecond; targetRenderInterval = 1000 / targetRendersPerSecond;
} }
public void init(Class<? extends OpsuState> startingState) { public void init(OpsuState startingState) {
setUPS(OPTION_TARGET_UPS.val); setUPS(OPTION_TARGET_UPS.val);
setFPS(targetFPS[targetFPSIndex]); setFPS(targetFPS[targetFPSIndex]);
state = instanceContainer.provide(startingState); fpsState = new FpsRenderState();
state.enter(); bubNotifState = new BubNotifState();
barNotifState = new BarNotificationState();
fpsState = instanceContainer.provide(FpsRenderState.class); state = startingState;
bubNotifState = instanceContainer.provide(BubbleNotificationState.class); state.enter();
barNotifState = instanceContainer.provide(BarNotificationState.class);
} }
@ -220,6 +228,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
mouseX = input.getMouseX(); mouseX = input.getMouseX();
mouseY = input.getMouseY(); mouseY = input.getMouseY();
transition.update();
fpsState.update(); fpsState.update();
state.update(); state.update();
@ -254,13 +263,17 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
cursor.updateAngle(renderDelta); cursor.updateAngle(renderDelta);
if (drawCursor) { if (drawCursor) {
cursor.draw(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)); cursor.draw(Mouse.isButtonDown(Input.MOUSE_LEFT_BUTTON) ||
Mouse.isButtonDown(Input.MOUSE_RIGHT_BUTTON));
} }
UI.drawTooltip(graphics); UI.drawTooltip(graphics);
transition.render(graphics);
timeSinceLastRender = 0; timeSinceLastRender = 0;
Display.update(false); Display.update(false);
GL11.glFlush();
} }
Display.processMessages(); Display.processMessages();
@ -270,8 +283,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
public void setup() throws Exception { public void setup() throws Exception {
width = height = -1; width = height = -1;
Input.disableControllers();
Display.setTitle("opsu!dance"); Display.setTitle("opsu!dance");
setupResolutionOptionlist(nativeDisplayMode.getWidth(), nativeDisplayMode.getHeight());
updateDisplayMode(OPTION_SCREEN_RESOLUTION.getValueString()); updateDisplayMode(OPTION_SCREEN_RESOLUTION.getValueString());
Display.create(); Display.create();
GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" }); GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" });
@ -281,6 +294,19 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
GLHelper.hideNativeCursor(); GLHelper.hideNativeCursor();
} }
// TODO: move this elsewhere
private void setupResolutionOptionlist(int width, int height) {
final Object[] resolutions = OPTION_SCREEN_RESOLUTION.getListItems();
final String nativeRes = width + "x" + height;
resolutions[0] = nativeRes;
for (int i = 0; i < resolutions.length; i++) {
if (nativeRes.equals(resolutions[i].toString())) {
resolutions[i] = resolutions[i] + " (borderless)";
}
}
}
public void teardown() { public void teardown() {
destroyImages(); destroyImages();
CurveRenderState.shutdown(); CurveRenderState.shutdown();
@ -316,13 +342,13 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
return true; return true;
} }
if (DownloadList.get().hasActiveDownloads()) { if (DownloadList.get().hasActiveDownloads()) {
EventBus.post(new BubbleNotificationEvent(DownloadList.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE)); BubNotifListener.EVENT.make().onBubNotif(DownloadList.EXIT_CONFIRMATION, Colors.BUB_RED);
exitRequested = false; exitRequested = false;
exitconfirmation = System.currentTimeMillis(); exitconfirmation = System.currentTimeMillis();
return false; return false;
} }
if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING) { if (updater.getStatus() == Updater.Status.UPDATE_DOWNLOADING) {
EventBus.post(new BubbleNotificationEvent(Updater.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE)); BubNotifListener.EVENT.make().onBubNotif(Updater.EXIT_CONFIRMATION, Colors.BUB_PURPLE);
exitRequested = false; exitRequested = false;
exitconfirmation = System.currentTimeMillis(); exitconfirmation = System.currentTimeMillis();
return false; return false;
@ -334,6 +360,11 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
int screenWidth = nativeDisplayMode.getWidth(); int screenWidth = nativeDisplayMode.getWidth();
int screenHeight = nativeDisplayMode.getHeight(); int screenHeight = nativeDisplayMode.getHeight();
int eos = resolutionString.indexOf(' ');
if (eos > -1) {
resolutionString = resolutionString.substring(0, eos);
}
int width = screenWidth; int width = screenWidth;
int height = screenHeight; int height = screenHeight;
if (resolutionString.matches("^[0-9]+x[0-9]+$")) { if (resolutionString.matches("^[0-9]+x[0-9]+$")) {
@ -348,18 +379,17 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
height = 600; height = 600;
} }
if (!OPTION_FULLSCREEN.state) {
boolean borderless = (screenWidth == width && screenHeight == height);
System.setProperty("org.lwjgl.opengl.Window.undecorated", Boolean.toString(borderless));
}
try { try {
setDisplayMode(width, height, OPTION_FULLSCREEN.state); setDisplayMode(width, height, OPTION_FULLSCREEN.state);
} catch (Exception e) { } catch (Exception e) {
EventBus.post(new BubbleNotificationEvent("Failed to change resolution", BubbleNotificationEvent.COMMONCOLOR_RED)); BubNotifListener.EVENT.make().onBubNotif("Failed to change resolution", Colors.BUB_RED);
Log.error("Failed to set display mode.", e); Log.error("Failed to set display mode.", e);
} }
if (OPTION_FULLSCREEN.state) {
// set borderless window if dimensions match screen size
boolean borderless = (screenWidth == width && screenHeight == height);
System.setProperty("org.lwjgl.opengl.Window.undecorated", Boolean.toString(borderless));
}
} }
public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception { public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception {
@ -377,8 +407,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
displayMode = new DisplayMode(width, height); displayMode = new DisplayMode(width, height);
if (fullscreen) { if (fullscreen) {
fullscreen = false; fullscreen = false;
Log.warn("could not find fullscreen displaymode for " + width + "x" + height); String msg = String.format("Fullscreen mode is not supported for %sx%s", width, height);
EventBus.post(new BubbleNotificationEvent("Fullscreen mode is not supported for " + width + "x" + height, BubbleNotificationEvent.COLOR_ORANGE)); Log.warn(msg);
BubNotifListener.EVENT.make().onBubNotif(msg, Colors.BUB_ORANGE);
} }
} }
@ -404,17 +435,20 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
graphics = new Graphics(width, height); graphics = new Graphics(width, height);
graphics.setAntiAlias(false); graphics.setAntiAlias(false);
input = new Input(height); if (input == null) {
input.enableKeyRepeat(); input = new Input(height);
input.addKeyListener(this); input.enableKeyRepeat();
input.addMouseListener(this); input.addListener(new GlobalInputListener());
input.addMouseListener(bubNotifState);
}
input.addListener(state);
sout("GL ready"); sout("GL ready");
GameImage.init(width, height); GameImage.init(width, height);
Fonts.init(config); Fonts.init();
EventBus.post(new ResolutionOrSkinChangedEvent(null, width, height)); ResolutionChangedListener.EVENT.make().onResolutionChanged(width, height);
} }
public void resetCursor() { public void resetCursor() {
@ -432,110 +466,51 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
return (Sys.getTime() * 1000) / Sys.getTimerResolution(); return (Sys.getTime() * 1000) / Sys.getTimerResolution();
} }
public boolean isWidescreen() {
return width * 1000 / height > 1500; // 1777 = 16:9, 1333 = 4:3
}
@Override @Override
public void writeErrorDump(StringWriter dump) { public void writeErrorDump(StringWriter dump) {
dump.append("> DisplayContainer dump\n"); dump.append("> DisplayContainer dump\n");
dump.append("OpenGL version: ").append(glVersion).append( "(").append(glVendor).append(")\n"); dump.append("OpenGL version: ").append(glVersion).append( "(").append(glVendor).append(")\n");
if (isTransitioning()) { if (state == null) {
dump.append("doing a transition\n"); dump.append("state is null!\n");
dump.append("using out transition ").append(outTransitionState.getClass().getSimpleName()).append('\n'); return;
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); state.writeErrorDump(dump);
} }
// TODO change this
public boolean isInState(Class<? extends OpsuState> state) { public boolean isInState(Class<? extends OpsuState> state) {
return state.isInstance(state); return state.isInstance(state);
} }
public boolean isTransitioning() { public void switchState(OpsuState state) {
return state instanceof TransitionState; switchState(state, 200, 300);
} }
public void switchState(Class<? extends OpsuState> newState) { public void switchState(OpsuState newstate, int outtime, int intime) {
switchState(newState, FadeOutTransitionState.class, 200, FadeInTransitionState.class, 300); if (transition.progress != -1) {
}
public void switchStateNow(Class<? extends OpsuState> newState) {
switchState(newState, EmptyTransitionState.class, 0, FadeInTransitionState.class, 300);
}
public void switchStateInstantly(Class<? extends OpsuState> newState) {
state.leave();
state = instanceContainer.provide(newState);
state.enter();
}
public void switchState(Class<? extends OpsuState> newState, Class<? extends TransitionState> outTransition, int outTime, Class<? extends TransitionState> inTransition, int inTime) {
if (isTransitioning()) {
return; return;
} }
outTransitionState = instanceContainer.provide(outTransition).set(state, outTime, outTransitionListener); if (outtime == 0) {
inTransitionState = instanceContainer.provide(inTransition).set(instanceContainer.provide(newState), inTime, inTransitionListener); switchStateInstantly(newstate);
state = outTransitionState; newstate = null;
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) {
if (bubNotifState.mouseReleased(x, y)) {
return;
} }
state.mouseReleased(button, x, y); transition.nextstate = newstate;
transition.total = transition.in = intime;
transition.out = outtime;
transition.total += outtime;
transition.progress = 0;
} }
@Override public void switchStateInstantly(OpsuState state) {
public void mouseMoved(int oldx, int oldy, int newx, int newy) { } this.state.leave();
input.removeListener(this.state);
@Override this.state = state;
public void mouseDragged(int oldx, int oldy, int newx, int newy) { this.state.enter();
state.mouseDragged(oldx, oldy, newx, newy); input.addListener(this.state);
} }
@Override
public void setInput(Input input) { }
@Override
public boolean isAcceptingInput() {
return true;
}
@Override
public void inputEnded() { }
@Override
public void inputStarted() { }
} }

View File

@ -19,7 +19,11 @@ package yugecin.opsudance.core;
import itdelatrisu.opsu.downloads.Updater; import itdelatrisu.opsu.downloads.Updater;
import yugecin.opsudance.OpsuDance; import yugecin.opsudance.OpsuDance;
import yugecin.opsudance.core.inject.OpsuDanceInjector;
import javax.swing.*;
import static yugecin.opsudance.core.Constants.PROJECT_NAME;
import static yugecin.opsudance.core.InstanceContainer.*;
public class Entrypoint { public class Entrypoint {
@ -27,10 +31,18 @@ public class Entrypoint {
public static void main(String[] args) { public static void main(String[] args) {
sout("launched"); sout("launched");
(new OpsuDanceInjector()).provide(OpsuDance.class).start(args);
if (Updater.get().getStatus() == Updater.Status.UPDATE_FINAL) { try {
Updater.get().runUpdate(); InstanceContainer.kickstart();
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage(), "Cannot start " + PROJECT_NAME, JOptionPane.ERROR_MESSAGE);
// TODO replace with errorhandler
}
new OpsuDance().start(args);
if (updater.getStatus() == Updater.Status.UPDATE_FINAL) {
updater.runUpdate();
} }
} }
@ -39,7 +51,7 @@ public class Entrypoint {
} }
public static void sout(String message) { public static void sout(String message) {
System.out.println(String.format("[%7d] %s", runtime(), message)); System.out.println(String.format("[%8d] %s", runtime(), message));
} }
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core;
import java.io.File;
import java.nio.file.Paths;
import static yugecin.opsudance.core.Constants.PROJECT_NAME;
public class Environment {
public final boolean isJarRunning;
public final File workingdir;
public final File jarfile;
public Environment() {
Class thiz = Environment.class;
String thisClassLocation = thiz.getResource(thiz.getSimpleName() + ".class").toString();
this.isJarRunning = thisClassLocation.startsWith("jar:");
if (!isJarRunning) {
this.workingdir = Paths.get(".").toAbsolutePath().normalize().toFile();
this.jarfile = null;
} else {
String wdir = thisClassLocation.substring(9); // remove jar:file:
String separator = "!/";
int separatorIdx = wdir.indexOf(separator);
int lastSeparatorIdx = wdir.lastIndexOf(separator);
if (separatorIdx != lastSeparatorIdx) {
String msg = String.format("%s cannot run from paths containing '!/', please move the file. Current directory: %s",
PROJECT_NAME, thisClassLocation.substring(0, lastSeparatorIdx));
throw new RuntimeException(msg);
}
this.jarfile = new File(wdir.substring(0, separatorIdx));
this.workingdir = jarfile.getParentFile();
}
}
}

View File

@ -0,0 +1,84 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core;
import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.UI;
import org.newdawn.slick.Input;
import org.newdawn.slick.InputListener;
import yugecin.opsudance.events.BarNotifListener;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
public class GlobalInputListener implements InputListener {
@Override
public boolean keyPressed(int key, char c) {
return false;
}
@Override
public boolean keyReleased(int key, char c) {
if (key == KEY_F7) {
OPTION_TARGET_FPS.clickListItem((targetFPSIndex + 1) % targetFPS.length);
BarNotifListener.EVENT.make().onBarNotif(String.format("Frame limiter: %s",
OPTION_TARGET_FPS.getValueString()));
return true;
}
if (key == KEY_F10) {
OPTION_DISABLE_MOUSE_BUTTONS.toggle();
return true;
}
if (key == KEY_F12) {
config.takeScreenShot();
return true;
}
if (key == KEY_S && isKeyDown(KEY_LMENU) && isKeyDown(KEY_LSHIFT) &&
input.isControlDown() && !displayContainer.isInState(Game.class)) {
skinservice.reloadSkin();
}
return false;
}
@Override
public boolean mouseWheelMoved(int delta) {
if (isKeyDown(Input.KEY_LALT) || isKeyDown(Input.KEY_RALT)) {
UI.changeVolume((delta < 0) ? -1 : 1);
return true;
}
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;
}
@Override
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
return false;
}
}

View File

@ -0,0 +1,137 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core;
import itdelatrisu.opsu.NativeLoader;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.states.*;
import org.newdawn.slick.Input;
import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionsService;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.ManifestWrapper;
import java.io.File;
import java.io.IOException;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import static yugecin.opsudance.utils.SyntacticSugar.closeAndSwallow;
public class InstanceContainer {
public static Environment env;
public static Configuration config;
public static OptionsService optionservice;
public static SkinService skinservice;
public static OszUnpacker oszunpacker;
public static ReplayImporter replayImporter;
public static BeatmapParser beatmapParser;
public static Updater updater;
public static DisplayContainer displayContainer;
public static Input input;
public static GameObjectRenderer gameObjectRenderer;
public static Splash splashState;
public static MainMenu mainmenuState;
public static ButtonMenu buttonState;
public static SongMenu songMenuState;
public static DownloadsMenu downloadState;
public static Game gameState;
public static GameRanking gameRankingState;
public static GamePauseMenu pauseState;
public static void kickstart() {
updater = new Updater();
env = new Environment();
JarFile jarfile = getJarfile();
ManifestWrapper manifest = new ManifestWrapper(getJarManifest(jarfile));
config = new Configuration(manifest);
if (jarfile != null) {
try {
NativeLoader.loadNatives(jarfile, manifest);
} catch (IOException e) {
String msg = String.format("Could not unpack native(s): %s", e.getMessage());
throw new RuntimeException(msg, e);
} finally {
closeAndSwallow(jarfile);
}
}
NativeLoader.setNativePath();
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
optionservice = new OptionsService();
skinservice = new SkinService();
oszunpacker = new OszUnpacker();
replayImporter = new ReplayImporter();
beatmapParser = new BeatmapParser();
updater = new Updater();
displayContainer = new DisplayContainer();
gameObjectRenderer = new GameObjectRenderer();
splashState = new Splash();
mainmenuState = new MainMenu();
buttonState = new ButtonMenu();
songMenuState = new SongMenu();
downloadState = new DownloadsMenu();
gameState = new Game();
gameRankingState = new GameRanking();
pauseState = new GamePauseMenu();
}
@Nullable
private static JarFile getJarfile() {
if (env.jarfile == null) {
return null;
}
try {
return new JarFile(env.jarfile);
} catch (IOException e) {
String msg = String.format("Cannot read from jarfile (%s): %s", env.jarfile.getAbsolutePath(),
e.getMessage());
throw new RuntimeException(msg, e);
}
}
@Nullable
private static Manifest getJarManifest(@Nullable JarFile jarfile) {
if (jarfile == null) {
return null;
}
try {
return jarfile.getManifest();
} catch (IOException e) {
String msg = String.format("Cannot read manifest from jarfile: %s", e.getMessage());
throw new RuntimeException(msg, e);
}
}
}

View File

@ -15,13 +15,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>. * along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/ */
package yugecin.opsudance.core.state.transitions; package yugecin.opsudance.core;
public class EmptyTransitionState extends TransitionState { import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Override @Retention(RetentionPolicy.SOURCE) public @interface NotNull {}
public void enter() {
finish();
}
}

View File

@ -15,10 +15,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>. * along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/ */
package yugecin.opsudance.core.events; package yugecin.opsudance.core;
public interface EventListener<T> { import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
void onEvent(T event); @Retention(RetentionPolicy.SOURCE) public @interface Nullable {}
}

View File

@ -19,8 +19,7 @@ package yugecin.opsudance.core.errorhandling;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.Constants;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.utils.MiscUtils; import yugecin.opsudance.utils.MiscUtils;
import javax.swing.*; import javax.swing.*;
@ -35,80 +34,52 @@ import java.io.UnsupportedEncodingException;
import java.net.URI; import java.net.URI;
import java.net.URLEncoder; import java.net.URLEncoder;
import static yugecin.opsudance.core.InstanceContainer.*;
/** /**
* based on itdelatrisu.opsu.ErrorHandler * based on itdelatrisu.opsu.ErrorHandler
*/ */
public class ErrorHandler { public class ErrorHandler {
private static ErrorHandler instance; public final static int DEFAULT_OPTIONS = 0;
public final static int PREVENT_CONTINUE = 1;
private final Configuration config; public final static int PREVENT_REPORT = 2;
private final DisplayContainer displayContainer; public final static int ALLOW_TERMINATE = 4;
private String customMessage;
private Throwable cause;
private String errorDump;
private String messageBody;
private boolean preventContinue;
private boolean preventReport;
private boolean ignoreAndContinue;
private boolean allowTerminate;
public ErrorHandler(DisplayContainer displayContainer, Configuration config) {
this.displayContainer = displayContainer;
this.config = config;
instance = this;
}
private ErrorHandler init(String customMessage, Throwable cause) {
this.customMessage = customMessage;
this.cause = cause;
public static boolean explode(String customMessage, Throwable cause, int flags) {
StringWriter dump = new StringWriter(); StringWriter dump = new StringWriter();
try { if (displayContainer == null) {
displayContainer.writeErrorDump(dump); dump.append("displayContainer is null!\n");
} catch (Exception e) { } else {
dump try {
.append("### ") displayContainer.writeErrorDump(dump);
.append(e.getClass().getSimpleName()) } catch (Exception e) {
.append(" while creating errordump"); dump
e.printStackTrace(new PrintWriter(dump)); .append("### ")
.append(e.getClass().getSimpleName())
.append(" while creating errordump");
e.printStackTrace(new PrintWriter(dump));
}
} }
errorDump = dump.toString(); String errorDump = dump.toString();
dump = new StringWriter(); dump = new StringWriter();
dump.append(customMessage).append("\n"); dump.append(customMessage).append("\n");
cause.printStackTrace(new PrintWriter(dump)); cause.printStackTrace(new PrintWriter(dump));
dump.append("\n").append(errorDump); dump.append("\n").append(errorDump);
messageBody = dump.toString(); String messageBody = dump.toString();
Log.error("====== start unhandled exception dump"); Log.error("====== start unhandled exception dump");
Log.error(messageBody); Log.error(messageBody);
Log.error("====== end unhandled exception dump"); Log.error("====== end unhandled exception dump");
return this;
int result = show(messageBody, customMessage, cause, errorDump, flags);
return (flags & ALLOW_TERMINATE) == 0 || result == 1;
} }
public static ErrorHandler error(String message, Throwable cause) { private static int show(final String messageBody, final String customMessage, final Throwable cause,
return instance.init(message, cause); final String errorDump, final int flags) {
}
public ErrorHandler preventReport() {
preventReport = true;
return this;
}
public ErrorHandler allowTerminate() {
allowTerminate = true;
return this;
}
public ErrorHandler preventContinue() {
preventContinue = true;
return this;
}
public ErrorHandler show() {
try { try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) { } catch (Exception e) {
@ -118,7 +89,7 @@ public class ErrorHandler {
String title = "opsu!dance error - " + customMessage; String title = "opsu!dance error - " + customMessage;
String messageText = "opsu!dance has encountered an error."; String messageText = "opsu!dance has encountered an error.";
if (!preventReport) { if ((flags & PREVENT_REPORT) == 0) {
messageText += " Please report this!"; messageText += " Please report this!";
} }
JLabel message = new JLabel(messageText); JLabel message = new JLabel(messageText);
@ -132,12 +103,27 @@ public class ErrorHandler {
textArea.setWrapStyleWord(true); textArea.setWrapStyleWord(true);
textArea.setText(messageBody); textArea.setText(messageBody);
Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(), createReportButton() }; ActionListener reportAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
try {
URI url = createGithubIssueUrl(customMessage, cause, errorDump);
Desktop.getDesktop().browse(url);
} 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);
}
}
};
Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(),
createReportButton(flags, reportAction) };
String[] buttons; String[] buttons;
if (!allowTerminate && !preventContinue) { if ((flags & (ALLOW_TERMINATE | PREVENT_CONTINUE)) == 0) {
buttons = new String[] { "Ignore & continue" }; buttons = new String[] { "Ignore & continue" };
} else if (preventContinue) { } else if ((flags & PREVENT_CONTINUE) == 0) {
buttons = new String[] { "Terminate" }; buttons = new String[] { "Terminate" };
} else { } else {
buttons = new String[] { "Terminate", "Ignore & continue" }; buttons = new String[] { "Terminate", "Ignore & continue" };
@ -147,52 +133,46 @@ public class ErrorHandler {
frame.setUndecorated(true); frame.setUndecorated(true);
frame.setVisible(true); frame.setVisible(true);
frame.setLocationRelativeTo(null); frame.setLocationRelativeTo(null);
int result = JOptionPane.showOptionDialog(frame, int result = JOptionPane.showOptionDialog(frame, messageComponents, title, JOptionPane.DEFAULT_OPTION,
messageComponents, JOptionPane.ERROR_MESSAGE, null, buttons, buttons[buttons.length - 1]);
title,
JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE,
null,
buttons,
buttons[buttons.length - 1]);
ignoreAndContinue = !allowTerminate || result == 1;
frame.dispose(); frame.dispose();
return this; return result;
} }
private JComponent createViewLogButton() { private static JComponent createViewLogButton() {
return createButton("View log", Desktop.Action.OPEN, new ActionListener() { return createButton("View log", Desktop.Action.OPEN, new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
try { openLogfile();
Desktop.getDesktop().open(config.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() { private static void openLogfile() {
if (preventReport) { if (config == null) {
JOptionPane.showMessageDialog(null,
"Cannot open logfile, check your opsu! installation folder for .opsu.cfg",
"errorception", JOptionPane.ERROR_MESSAGE);
return;
}
try {
Desktop.getDesktop().open(config.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 static JComponent createReportButton(int flags, ActionListener reportAction) {
if ((flags & PREVENT_REPORT) > 0) {
return new JLabel(); return new JLabel();
} }
return createButton("Report error", Desktop.Action.BROWSE, new ActionListener() { return createButton("Report error", Desktop.Action.BROWSE, reportAction);
@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) { private static JButton createButton(String buttonText, Desktop.Action action, ActionListener listener) {
JButton button = new JButton(buttonText); JButton button = new JButton(buttonText);
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(action)) { if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(action)) {
button.addActionListener(listener); button.addActionListener(listener);
@ -202,7 +182,7 @@ public class ErrorHandler {
return button; return button;
} }
private URI createGithubIssueUrl() { private static URI createGithubIssueUrl(String customMessage, Throwable cause, String errorDump) {
StringWriter dump = new StringWriter(); StringWriter dump = new StringWriter();
dump.append(customMessage).append("\n"); dump.append(customMessage).append("\n");
@ -227,15 +207,16 @@ public class ErrorHandler {
String issueTitle = ""; String issueTitle = "";
String issueBody = ""; String issueBody = "";
try { try {
issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + " " + customMessage, "UTF-8"); issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + " " +
customMessage, "UTF-8");
issueBody = URLEncoder.encode(truncateGithubIssueBody(dump.toString()), "UTF-8"); issueBody = URLEncoder.encode(truncateGithubIssueBody(dump.toString()), "UTF-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e); Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e);
} }
return URI.create(String.format(config.ISSUES_URL, issueTitle, issueBody)); return URI.create(String.format(Constants.ISSUES_URL, issueTitle, issueBody));
} }
private String truncateGithubIssueBody(String body) { private static String truncateGithubIssueBody(String body) {
if (body.replaceAll("[^a-zA-Z+-]", "").length() < 1750) { if (body.replaceAll("[^a-zA-Z+-]", "").length() < 1750) {
return body; return body;
} }
@ -243,8 +224,4 @@ public class ErrorHandler {
return body.substring(0, 1640) + "** TRUNCATED **\n```"; return body.substring(0, 1640) + "** TRUNCATED **\n```";
} }
public boolean shouldIgnoreAndContinue() {
return ignoreAndContinue;
}
} }

View File

@ -17,38 +17,37 @@
*/ */
package yugecin.opsudance.core.events; package yugecin.opsudance.core.events;
import java.util.*; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedList;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class EventBus { public class Event<T> {
private EventBus() { private final Class<T> type;
private final LinkedList<T> listeners;
public Event(Class<T> type) {
this.type = type;
this.listeners = new LinkedList<>();
} }
private static final List<Subscriber> subscribers = new LinkedList<>(); public void addListener(T listener) {
this.listeners.add(listener);
public static <T> void subscribe(Class<T> eventType, EventListener<T> eventListener) {
subscribers.add(new Subscriber<>(eventType, eventListener));
} }
public static void post(Object event) { public T make() {
for (Subscriber s : subscribers) { return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},
if (s.eventType.isInstance(event)) { new InvocationHandler() {
s.listener.onEvent(event); @Override
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
} for (T listener : listeners) {
} method.invoke(listener, args);
}
private static class Subscriber<T> { return null;
}
private final Class<T> eventType; });
private final EventListener<T> listener;
private Subscriber(Class<T> eventType, EventListener<T> listener) {
this.eventType = eventType;
this.listener = listener;
}
} }
} }

View File

@ -13,6 +13,7 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
*
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>. * along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/ */
package yugecin.opsudance.core.inject; package yugecin.opsudance.core.inject;
@ -22,14 +23,10 @@ import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.downloads.Updater; import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.replay.ReplayImporter; import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.states.*; import itdelatrisu.opsu.states.*;
import yugecin.opsudance.PreStartupInitializer;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.specialstates.BarNotificationState; import yugecin.opsudance.core.state.specialstates.BarNotificationState;
import yugecin.opsudance.core.state.specialstates.BubbleNotificationState; import yugecin.opsudance.core.state.specialstates.BubNotifState;
import yugecin.opsudance.core.state.specialstates.FpsRenderState; 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.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration; import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionsService; import yugecin.opsudance.options.OptionsService;
@ -48,18 +45,14 @@ public class OpsuDanceInjector extends Injector {
bind(Updater.class).asLazySingleton(); bind(Updater.class).asLazySingleton();
bind(SkinService.class).asEagerSingleton(); bind(SkinService.class).asEagerSingleton();
bind(PreStartupInitializer.class).asEagerSingleton(); //bind(PreStartupInitializer.class).asEagerSingleton();
bind(DisplayContainer.class).asEagerSingleton(); bind(DisplayContainer.class).asEagerSingleton();
bind(ErrorHandler.class).asEagerSingleton(); bind(ErrorHandler.class).asEagerSingleton();
bind(FpsRenderState.class).asEagerSingleton(); bind(FpsRenderState.class).asEagerSingleton();
bind(BarNotificationState.class).asEagerSingleton(); bind(BarNotificationState.class).asEagerSingleton();
bind(BubbleNotificationState.class).asEagerSingleton(); bind(BubNotifState.class).asEagerSingleton();
bind(EmptyTransitionState.class).asEagerSingleton();
bind(FadeInTransitionState.class).asEagerSingleton();
bind(FadeOutTransitionState.class).asEagerSingleton();
bind(GameObjectRenderer.class).asEagerSingleton(); bind(GameObjectRenderer.class).asEagerSingleton();

View File

@ -17,33 +17,12 @@
*/ */
package yugecin.opsudance.core.state; package yugecin.opsudance.core.state;
import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.UI;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input; import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService;
import java.io.StringWriter; import java.io.StringWriter;
import static yugecin.opsudance.options.Options.*; public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListener {
public abstract class BaseOpsuState implements OpsuState, EventListener<ResolutionOrSkinChangedEvent> {
@Inject
protected DisplayContainer displayContainer;
@Inject
protected Configuration config;
@Inject
protected SkinService skinService;
/** /**
* state is dirty when resolution or skin changed but hasn't rendered yet * state is dirty when resolution or skin changed but hasn't rendered yet
@ -52,7 +31,7 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
private boolean isCurrentState; private boolean isCurrentState;
public BaseOpsuState() { public BaseOpsuState() {
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, this); ResolutionChangedListener.EVENT.addListener(this);
} }
protected void revalidate() { protected void revalidate() {
@ -71,7 +50,7 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
} }
@Override @Override
public void onEvent(ResolutionOrSkinChangedEvent event) { public void onResolutionChanged(int w, int h) {
if (isCurrentState) { if (isCurrentState) {
revalidate(); revalidate();
return; return;
@ -105,32 +84,11 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
@Override @Override
public boolean keyReleased(int key, char c) { public boolean keyReleased(int key, char c) {
if (key == Input.KEY_F7) {
OPTION_TARGET_FPS.clickListItem((targetFPSIndex + 1) % targetFPS.length);
EventBus.post(new BarNotificationEvent(String.format("Frame limiter: %s", OPTION_TARGET_FPS.getValueString())));
return true;
}
if (key == Input.KEY_F10) {
OPTION_DISABLE_MOUSE_BUTTONS.toggle();
return true;
}
if (key == Input.KEY_F12) {
config.takeScreenShot();
return true;
}
Input input = displayContainer.input;
if (key == Input.KEY_S && input.isKeyDown(Input.KEY_LMENU) && input.isKeyDown(Input.KEY_LSHIFT) &&input.isKeyDown(Input.KEY_LCONTROL) && !displayContainer.isInState(Game.class)) {
skinService.reloadSkin();
}
return false; return false;
} }
@Override @Override
public boolean mouseWheelMoved(int delta) { public boolean mouseWheelMoved(int delta) {
if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) {
UI.changeVolume((delta < 0) ? -1 : 1);
return true;
}
return false; return false;
} }

View File

@ -17,12 +17,14 @@
*/ */
package yugecin.opsudance.core.state; package yugecin.opsudance.core.state;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import yugecin.opsudance.core.components.Component; import yugecin.opsudance.core.components.Component;
import java.util.LinkedList; import java.util.LinkedList;
import static yugecin.opsudance.core.InstanceContainer.*;
public abstract class ComplexOpsuState extends BaseOpsuState { public abstract class ComplexOpsuState extends BaseOpsuState {
protected final LinkedList<Component> components; protected final LinkedList<Component> components;
@ -163,7 +165,7 @@ public abstract class ComplexOpsuState extends BaseOpsuState {
} }
} }
if (focusedComponent != null) { if (focusedComponent != null) {
if (key == Input.KEY_ESCAPE) { if (key == Keyboard.KEY_ESCAPE) {
focusedComponent.setFocused(false); focusedComponent.setFocused(false);
focusedComponent = null; focusedComponent = null;
return true; return true;
@ -182,7 +184,7 @@ public abstract class ComplexOpsuState extends BaseOpsuState {
} }
} }
if (focusedComponent != null) { if (focusedComponent != null) {
if (key == Input.KEY_ESCAPE) { if (key == Keyboard.KEY_ESCAPE) {
focusedComponent.setFocused(false); focusedComponent.setFocused(false);
focusedComponent = null; focusedComponent = null;
return true; return true;

View File

@ -18,9 +18,10 @@
package yugecin.opsudance.core.state; package yugecin.opsudance.core.state;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.InputListener;
import yugecin.opsudance.core.errorhandling.ErrorDumpable; import yugecin.opsudance.core.errorhandling.ErrorDumpable;
public interface OpsuState extends ErrorDumpable { public interface OpsuState extends ErrorDumpable, InputListener {
void update(); void update();
void preRenderUpdate(); void preRenderUpdate();
@ -33,34 +34,4 @@ public interface OpsuState extends ErrorDumpable {
*/ */
boolean onCloseRequest(); boolean onCloseRequest();
/**
* @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);
/**
* @return false to stop event bubbling
*/
boolean mouseDragged(int oldx, int oldy, int newx, int newy);
} }

View File

@ -21,22 +21,20 @@ import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.animations.AnimationEquation; import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import java.util.List; import java.util.List;
public class BarNotificationState implements EventListener<BarNotificationEvent> { import static yugecin.opsudance.core.InstanceContainer.displayContainer;
public class BarNotificationState implements BarNotifListener, ResolutionChangedListener {
private final int IN_TIME = 200; private final int IN_TIME = 200;
private final int DISPLAY_TIME = 5700 + IN_TIME; private final int DISPLAY_TIME = 5700 + IN_TIME;
private final int OUT_TIME = 200; private final int OUT_TIME = 200;
private final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME; private final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME;
private final DisplayContainer displayContainer;
private final Color bgcol; private final Color bgcol;
private final Color textCol; private final Color textCol;
@ -49,21 +47,12 @@ public class BarNotificationState implements EventListener<BarNotificationEvent>
private int barHalfTargetHeight; private int barHalfTargetHeight;
private int barHalfHeight; private int barHalfHeight;
public BarNotificationState(DisplayContainer displayContainer) { public BarNotificationState() {
this.displayContainer = displayContainer;
this.bgcol = new Color(Color.black); this.bgcol = new Color(Color.black);
this.textCol = new Color(Color.white); this.textCol = new Color(Color.white);
this.timeShown = TOTAL_TIME; this.timeShown = TOTAL_TIME;
EventBus.subscribe(BarNotificationEvent.class, this); BarNotifListener.EVENT.addListener(this);
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, new EventListener<ResolutionOrSkinChangedEvent>() { ResolutionChangedListener.EVENT.addListener(this);
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
if (timeShown >= TOTAL_TIME) {
return;
}
calculatePosition();
}
});
} }
public void render(Graphics g) { public void render(Graphics g) {
@ -108,10 +97,18 @@ public class BarNotificationState implements EventListener<BarNotificationEvent>
} }
@Override @Override
public void onEvent(BarNotificationEvent event) { public void onBarNotif(String message) {
this.message = event.message; this.message = message;
calculatePosition(); calculatePosition();
timeShown = 0; timeShown = 0;
} }
@Override
public void onResolutionChanged(int w, int h) {
if (timeShown >= TOTAL_TIME) {
return;
}
calculatePosition();
}
} }

View File

@ -21,40 +21,33 @@ import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.animations.AnimationEquation; import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer; import org.newdawn.slick.MouseListener;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.core.events.EventListener; import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
public class BubbleNotificationState implements EventListener<BubbleNotificationEvent> { import static yugecin.opsudance.core.InstanceContainer.*;
public class BubNotifState implements MouseListener, BubNotifListener, ResolutionChangedListener {
public static final int IN_TIME = 633; public static final int IN_TIME = 633;
public static final int DISPLAY_TIME = 7000 + IN_TIME; public static final int DISPLAY_TIME = 7000 + IN_TIME;
public static final int OUT_TIME = 433; public static final int OUT_TIME = 433;
public static final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME; public static final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME;
private final DisplayContainer displayContainer;
private final LinkedList<Notification> bubbles; private final LinkedList<Notification> bubbles;
private int addAnimationTime; private int addAnimationTime;
private int addAnimationHeight; private int addAnimationHeight;
public BubbleNotificationState(DisplayContainer displayContainer) { public BubNotifState() {
this.displayContainer = displayContainer;
this.bubbles = new LinkedList<>(); this.bubbles = new LinkedList<>();
this.addAnimationTime = IN_TIME; this.addAnimationTime = IN_TIME;
EventBus.subscribe(BubbleNotificationEvent.class, this); BubNotifListener.EVENT.addListener(this);
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, new EventListener<ResolutionOrSkinChangedEvent>() { ResolutionChangedListener.EVENT.addListener(this);
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
calculatePositions();
}
});
} }
public void render(Graphics g) { public void render(Graphics g) {
@ -79,20 +72,9 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
} while (iter.hasNext()); } while (iter.hasNext());
} }
public boolean mouseReleased(int x, int y) {
if (x < Notification.finalX) {
return false;
}
for (Notification bubble : bubbles) {
if (bubble.mouseReleased(x, y)) {
return true;
}
}
return false;
}
private void calculatePositions() { private void calculatePositions() {
Notification.width = (int) (displayContainer.width * 0.1703125f); // if width is 0, attempting to wrap it will result in infinite loop
Notification.width = Math.max(50, (int) (displayContainer.width * 0.1703125f));
Notification.baseLine = (int) (displayContainer.height * 0.9645f); Notification.baseLine = (int) (displayContainer.height * 0.9645f);
Notification.paddingY = (int) (displayContainer.height * 0.0144f); Notification.paddingY = (int) (displayContainer.height * 0.0144f);
Notification.finalX = displayContainer.width - Notification.width - (int) (displayContainer.width * 0.01); Notification.finalX = displayContainer.width - Notification.width - (int) (displayContainer.width * 0.01);
@ -130,9 +112,9 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
} }
@Override @Override
public void onEvent(BubbleNotificationEvent event) { public void onBubNotif(String message, Color borderColor) {
finishAddAnimation(); finishAddAnimation();
Notification newBubble = new Notification(event.message, event.borderColor); Notification newBubble = new Notification(message, borderColor);
bubbles.add(0, newBubble); bubbles.add(0, newBubble);
addAnimationTime = 0; addAnimationTime = 0;
addAnimationHeight = newBubble.height + Notification.paddingY; addAnimationHeight = newBubble.height + Notification.paddingY;
@ -144,6 +126,39 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
} }
} }
@Override
public void onResolutionChanged(int w, int h) {
calculatePositions();
}
@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) {
if (x < Notification.finalX) {
return false;
}
for (Notification bubble : bubbles) {
if (bubble.mouseReleased(x, y)) {
return true;
}
}
return false;
}
@Override
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
return false;
}
private static class Notification { private static class Notification {
private final static int HOVER_ANIM_TIME = 150; private final static int HOVER_ANIM_TIME = 150;
@ -205,7 +220,7 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
Fonts.SMALLBOLD.drawString(x + fontPaddingX, y, line, textColor); Fonts.SMALLBOLD.drawString(x + fontPaddingX, y, line, textColor);
y += lineHeight; y += lineHeight;
} }
return timeShown > BubbleNotificationState.TOTAL_TIME; return timeShown > BubNotifState.TOTAL_TIME;
} }
private void processAnimations(boolean mouseHovered, int delta) { private void processAnimations(boolean mouseHovered, int delta) {
@ -218,17 +233,17 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
borderColor.r = targetBorderColor.r + (0.977f - targetBorderColor.r) * hoverProgress; borderColor.r = targetBorderColor.r + (0.977f - targetBorderColor.r) * hoverProgress;
borderColor.g = targetBorderColor.g + (0.977f - targetBorderColor.g) * hoverProgress; borderColor.g = targetBorderColor.g + (0.977f - targetBorderColor.g) * hoverProgress;
borderColor.b = targetBorderColor.b + (0.977f - targetBorderColor.b) * hoverProgress; borderColor.b = targetBorderColor.b + (0.977f - targetBorderColor.b) * hoverProgress;
if (timeShown < BubbleNotificationState.IN_TIME) { if (timeShown < BubNotifState.IN_TIME) {
float progress = (float) timeShown / BubbleNotificationState.IN_TIME; float progress = (float) timeShown / BubNotifState.IN_TIME;
this.x = finalX + (int) ((1 - AnimationEquation.OUT_BACK.calc(progress)) * width / 2); this.x = finalX + (int) ((1 - AnimationEquation.OUT_BACK.calc(progress)) * width / 2);
textColor.a = borderColor.a = bgcol.a = progress; textColor.a = borderColor.a = bgcol.a = progress;
bgcol.a = borderColor.a * 0.8f; bgcol.a = borderColor.a * 0.8f;
return; return;
} }
x = Notification.finalX; x = Notification.finalX;
if (timeShown > BubbleNotificationState.DISPLAY_TIME) { if (timeShown > BubNotifState.DISPLAY_TIME) {
isFading = true; isFading = true;
float progress = (float) (timeShown - BubbleNotificationState.DISPLAY_TIME) / BubbleNotificationState.OUT_TIME; float progress = (float) (timeShown - BubNotifState.DISPLAY_TIME) / BubNotifState.OUT_TIME;
textColor.a = borderColor.a = 1f - progress; textColor.a = borderColor.a = 1f - progress;
bgcol.a = borderColor.a * 0.8f; bgcol.a = borderColor.a * 0.8f;
} }
@ -236,7 +251,7 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
private boolean mouseReleased(int x, int y) { private boolean mouseReleased(int x, int y) {
if (!isFading && isMouseHovered(x, y)) { if (!isFading && isMouseHovered(x, y)) {
timeShown = BubbleNotificationState.DISPLAY_TIME; timeShown = BubNotifState.DISPLAY_TIME;
return true; return true;
} }
return false; return false;

View File

@ -20,21 +20,18 @@ package yugecin.opsudance.core.state.specialstates;
import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.Fonts;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.utils.FPSMeter; import yugecin.opsudance.utils.FPSMeter;
import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
public class FpsRenderState implements EventListener<ResolutionOrSkinChangedEvent> { public class FpsRenderState implements ResolutionChangedListener {
private final static Color GREEN = new Color(171, 218, 25); private final static Color GREEN = new Color(171, 218, 25);
private final static Color ORANGE = new Color(255, 204, 34); private final static Color ORANGE = new Color(255, 204, 34);
private final static Color DARKORANGE = new Color(255, 149, 24); private final static Color DARKORANGE = new Color(255, 149, 24);
private final DisplayContainer displayContainer;
private final FPSMeter fpsMeter; private final FPSMeter fpsMeter;
private final FPSMeter upsMeter; private final FPSMeter upsMeter;
@ -42,11 +39,10 @@ public class FpsRenderState implements EventListener<ResolutionOrSkinChangedEven
private int y; private int y;
private int singleHeight; private int singleHeight;
public FpsRenderState(DisplayContainer displayContainer) { public FpsRenderState() {
this.displayContainer = displayContainer;
fpsMeter = new FPSMeter(10); fpsMeter = new FPSMeter(10);
upsMeter = new FPSMeter(10); upsMeter = new FPSMeter(10);
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, this); ResolutionChangedListener.EVENT.addListener(this);
} }
public void update() { public void update() {
@ -93,7 +89,7 @@ public class FpsRenderState implements EventListener<ResolutionOrSkinChangedEven
} }
@Override @Override
public void onEvent(ResolutionOrSkinChangedEvent event) { public void onResolutionChanged(int w, int h) {
singleHeight = Fonts.SMALL.getLineHeight(); singleHeight = Fonts.SMALL.getLineHeight();
x = displayContainer.width - 3; x = displayContainer.width - 3;
y = displayContainer.height - 3 - singleHeight - 10; y = displayContainer.height - 3 - singleHeight - 10;

View File

@ -1,27 +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 <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.state.transitions;
public class FadeOutTransitionState extends FadeTransitionState {
@Override
protected float getMaskAlphaLevel(float fadeProgress) {
return fadeProgress;
}
}

View File

@ -1,24 +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 <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.state.transitions;
public interface TransitionFinishedListener {
void onFinish();
}

View File

@ -1,93 +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 <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.state.transitions;
import org.newdawn.slick.Graphics;
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;
protected int transitionTargetTime;
protected int transitionTime;
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 final OpsuState getApplicableState() {
return applicableState;
}
@Override
public void update() {
applicableState.update();
transitionTime += displayContainer.delta;
if (transitionTime >= transitionTargetTime) {
finish();
}
}
@Override
public void preRenderUpdate() {
applicableState.preRenderUpdate();
}
@Override
public void render(Graphics g) {
applicableState.render(g);
}
@Override
public void enter() {
super.enter();
transitionTime = 0;
}
protected final void finish() {
listener.onFinish();
}
@Override
public boolean onCloseRequest() {
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);
}
}

View File

@ -17,12 +17,12 @@
*/ */
package yugecin.opsudance.events; package yugecin.opsudance.events;
public class BarNotificationEvent { import yugecin.opsudance.core.events.Event;
public final String message; public interface BarNotifListener {
public BarNotificationEvent(String message) { Event<BarNotifListener> EVENT = new Event<>(BarNotifListener.class);
this.message = message;
} void onBarNotif(String message);
} }

View File

@ -0,0 +1,30 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.events;
import org.newdawn.slick.Color;
import yugecin.opsudance.core.events.Event;
@SuppressWarnings({"UnnecessaryInterfaceModifier", "unused"})
public interface BubNotifListener {
Event<BubNotifListener> EVENT = new Event<>(BubNotifListener.class);
void onBubNotif(String message, Color borderColor);
}

View File

@ -1,38 +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 <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.events;
import org.newdawn.slick.Color;
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 COMMONCOLOR_RED = new Color(141, 49, 16);
public static final Color COLOR_ORANGE = new Color(138, 72, 51);
public final String message;
public final Color borderColor;
public BubbleNotificationEvent(String message, Color borderColor) {
this.message = message;
this.borderColor = borderColor;
}
}

View File

@ -17,16 +17,12 @@
*/ */
package yugecin.opsudance.events; package yugecin.opsudance.events;
public class ResolutionOrSkinChangedEvent { import yugecin.opsudance.core.events.Event;
public final String skin; public interface ResolutionChangedListener {
public final int width;
public final int height;
public ResolutionOrSkinChangedEvent(String skin, int width, int height) { Event<ResolutionChangedListener> EVENT = new Event<>(ResolutionChangedListener.class);
this.skin = skin;
this.width = width; void onResolutionChanged(int w, int h);
this.height = height;
}
} }

View File

@ -15,13 +15,14 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>. * along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/ */
package yugecin.opsudance.core.state.transitions; package yugecin.opsudance.events;
public class FadeInTransitionState extends FadeTransitionState { import yugecin.opsudance.core.events.Event;
@Override public interface SkinChangedListener {
protected float getMaskAlphaLevel(float fadeProgress) {
return 1f - fadeProgress; Event<SkinChangedListener> EVENT = new Event<>(SkinChangedListener.class);
}
void onSkinChanged(String stringName);
} }

Some files were not shown because too many files have changed in this diff Show More