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

6
.gitignore vendored
View File

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

View File

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

View File

@ -1,23 +1,69 @@
# 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.
My goal is to to add cool cursordancing things to this fork, but also make it possible to play the normal way.
### Downloads
Click on the releases link (scroll up) to go to the downloadpage with prebuilt jars.
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.
### Building
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.
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, ...
### Credits
opsu! was made by Jeffrey Han ([@itdelatrisu](https://github.com/itdelatrisu)). All game concepts and designs are based on work by osu! developer Dean Herbert. Other opsu! credits can be found [here](CREDITS.md).
opsu!dance (everything in the src package yugecin.opsudance) was made by me ([@yugecin](https://github.com/yugecin)). Edits were made in the opsu! sources, too.
Downloads
---------
You can find prebuilt jars on [the releases page](https://github.com/yugecin/opsu-dance/releases).
### License
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!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.**
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"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project
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>
<groupId>yugecin</groupId>
<artifactId>opsu-dance</artifactId>
<version>0.5.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<version>${project.version}</version>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
@ -20,6 +24,7 @@
<excludes>
<exclude>**/Thumbs.db</exclude>
<exclude>**/version</exclude>
<exclude>**.pdn</exclude>
</excludes>
</resource>
<resource>
@ -31,13 +36,40 @@
</resource>
</resources>
<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>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
@ -74,6 +106,7 @@
<classpath />
<argument>${mainClassName}</argument>
</arguments>
<workingDirectory>${project.build.outputDirectory}/../</workingDirectory>
</configuration>
</execution>
</executions>
@ -92,18 +125,32 @@
<exclude>org/newdawn/slick/Music.*</exclude>
<exclude>org/newdawn/slick/Input.*</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/openal/AudioInputStream*</exclude>
<exclude>org/newdawn/slick/openal/OpenALStreamPlayer*</exclude>
<exclude>org/newdawn/slick/openal/SoundStore*</exclude>
</excludes>
</filter>
<filter>
<!-- sqlite contains sources for some reason -->
<artifact>*:sqlite-jdbc</artifact>
<excludes>
<exclude>**/*.java</exclude>
<exclude>**/*.c</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${mainClassName}</Main-Class>
<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>
</transformer>
</transformers>
@ -142,6 +189,10 @@
<groupId>org.lwjgl.lwjgl</groupId>
<artifactId>lwjgl</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jnlp</groupId>
<artifactId>jnlp-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@ -154,20 +205,16 @@
<artifactId>zip4j</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.googlecode.soundlibs</groupId>
<artifactId>jlayer</artifactId>
<version>1.0.1-1</version>
</dependency>
<dependency>
<groupId>com.googlecode.soundlibs</groupId>
<artifactId>mp3spi</artifactId>
<version>1.9.5-1</version>
</dependency>
<dependency>
<groupId>com.googlecode.soundlibs</groupId>
<artifactId>tritonus-share</artifactId>
<version>0.3.7-2</version>
<version>1.9.5.4</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
@ -209,10 +256,11 @@
<artifactId>lzma-java</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>gov.nist.math</groupId>
<artifactId>jama</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>gov.nist.math</groupId>
<artifactId>jama</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
</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 org.newdawn.slick.Color;
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.
@ -22,8 +23,8 @@ public class FakeGameObject extends GameObject {
public FakeGameObject() {
this.start = new Vec2f();
this.end = new Vec2f();
this.start.x = this.end.x = Options.width / 2;
this.start.y = this.end.y = Options.height / 2;
this.start.x = this.end.x = displayContainer.width / 2;
this.start.y = this.end.y = displayContainer.height / 2;
}
public FakeGameObject(GameObject start, GameObject end) {

View File

@ -5,7 +5,8 @@ import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.objects.GameObject;
import yugecin.opsudance.movers.Mover;
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.
@ -130,6 +131,6 @@ public class CombinedSpiralMover extends Mover {
}
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 awlex.ospu.movers.SpiralToMover;
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.
@ -87,7 +88,7 @@ public class SpiralMoverFactory implements MoverFactory {
* @return
*/
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

View File

@ -1,9 +1,10 @@
package awlex.ospu.spinners;
import itdelatrisu.opsu.Utils;
import yugecin.opsudance.options.Options;
import yugecin.opsudance.spinners.Spinner;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Created by Alex Wieser on 09.10.2016.
* WHO DO YOU THINK I AM?
@ -42,11 +43,11 @@ public class SpiralSpinner extends Spinner {
double ang;
double rad;
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);
rad = (MAX_RAD / (SIZE / 2)) * i;
int offsetX = Options.width / 2;
int offsetY = Options.height / 2;
int offsetX = displayContainer.width / 2;
int offsetY = displayContainer.height / 2;
points[SIZE / 2 - 1 - i] = new double[]{
offsetX + rad * Math.cos(ang),
offsetY + rad * Math.sin(ang)
@ -83,12 +84,12 @@ public class SpiralSpinner extends Spinner {
}
private void rotatePointAroundCenter(double[] point, double beta) {
double angle = Math.atan2(point[1] - Options.height / 2, point[0] - Options.width / 2);
double rad = Utils.distance(point[0], point[1], Options.width / 2, Options.height / 2);
double angle = Math.atan2(point[1] - displayContainer.height / 2, point[0] - displayContainer.width / 2);
double rad = Utils.distance(point[0], point[1], displayContainer.width / 2, displayContainer.height / 2);
//rotationMatrix
point[0] = Options.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[0] = displayContainer.width / 2 + rad * (Math.cos(angle) * Math.cos(beta) - Math.sin(angle) * Math.sin(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.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.objects.curves.Curve;
import itdelatrisu.opsu.replay.Replay;
import itdelatrisu.opsu.replay.ReplayFrame;
@ -42,25 +41,17 @@ import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
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.utils.SlickUtil;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Holds game data and renders all related elements.
*/
public class GameData {
@Inject
private Configuration config;
@Inject
private InstanceContainer instanceContainer;
/** Delta multiplier for steady HP drain. */
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). */
private boolean isGameplay;
/** Container dimensions. */
private int width, height;
/**
* Constructor for gameplay.
* @param width container width
* @param height container height
*/
public GameData(int width, int height) {
this.width = width;
this.height = height;
public GameData() {
this.isGameplay = true;
clear();
@ -375,12 +359,8 @@ public class GameData {
* This will initialize all parameters and images needed for the
* {@link #drawRankingElements(Graphics, Beatmap)} method.
* @param s the ScoreData object
* @param width container width
* @param height container height
*/
public GameData(ScoreData s, int width, int height) {
this.width = width;
this.height = height;
public GameData(ScoreData s) {
this.isGameplay = false;
this.scoreData = s;
@ -395,8 +375,9 @@ public class GameData {
hitResultCount[HIT_300K] = 0;
hitResultCount[HIT_100K] = s.katu;
hitResultCount[HIT_MISS] = s.miss;
this.replay = (s.replayString == null) ? null :
instanceContainer.injectFields(new Replay(new File(config.replayDir, String.format("%s.osr", s.replayString))));
if (s.replayString != null) {
this.replay = new Replay(new File(config.replayDir, s.replayString + ".osr"));
}
loadImages();
}
@ -622,6 +603,8 @@ public class GameData {
*/
@SuppressWarnings("deprecation")
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());
int margin = (int) (width * 0.008f);
float uiScale = GameImage.getUIscale();
@ -813,6 +796,9 @@ public class GameData {
* @param beatmap the beatmap
*/
public void drawRankingElements(Graphics g, Beatmap beatmap) {
int width = displayContainer.width;
int height = displayContainer.height;
// TODO Version 2 skins
float rankingHeight = 75;
float scoreTextScale = 1.0f;
@ -925,7 +911,7 @@ public class GameData {
if (hitResult.hitResultType == HitObjectType.SPINNER && hitResult.result != HIT_MISS) {
Image spinnerOsu = GameImage.SPINNER_OSU.getImage();
spinnerOsu.setAlpha(hitResult.alpha);
spinnerOsu.drawCentered(width / 2, height / 4);
spinnerOsu.drawCentered(displayContainer.width / 2, displayContainer.height / 4);
spinnerOsu.setAlpha(1f);
} else if (OPTION_SHOW_HIT_LIGHTING.state && !hitResult.hideResult && hitResult.result != HIT_MISS &&
// hit lighting
@ -1199,7 +1185,7 @@ public class GameData {
// combo burst
if (comboBurstIndex > -1 && OPTION_SHOW_COMBO_BURSTS.state) {
int leftX = 0;
int rightX = width - comboBurstImages[comboBurstIndex].getWidth();
int rightX = displayContainer.width - comboBurstImages[comboBurstIndex].getWidth();
if (comboBurstX < leftX) {
comboBurstX += (delta / 2f) * GameImage.getUIscale();
if (comboBurstX > leftX)
@ -1260,7 +1246,7 @@ public class GameData {
}
comboBurstAlpha = 0.8f;
if ((comboBurstIndex % 2) == 0) {
comboBurstX = width;
comboBurstX = displayContainer.width;
} else {
comboBurstX = comboBurstImages[0].getWidth() * -1;
}
@ -1603,7 +1589,7 @@ public class GameData {
replay = new Replay();
replay.mode = Beatmap.MODE_OSU;
replay.version = Updater.get().getBuildDate();
replay.version = updater.getBuildDate();
replay.beatmapHash = (beatmap == null) ? "" : beatmap.md5Hash;
replay.playerName = ""; // TODO
replay.replayHash = Long.toString(System.currentTimeMillis()); // TODO

View File

@ -18,6 +18,7 @@
package itdelatrisu.opsu;
import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.Fonts;
import java.io.File;
@ -30,8 +31,7 @@ import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.SlickUtil;
@ -249,6 +249,16 @@ public enum GameImage {
CONTROL_SLIDER_BALL ("control-sliderball", "png", false, false),
CONTROL_CHECK_ON ("control-check-on", "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) {
@Override
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);
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
*/
private Image[] loadImageArray(File dir) {
if (filenameFormat != null) {
for (String suffix : getSuffixes()) {
List<Image> list = new ArrayList<Image>();
int i = 0;
while (true) {
// look for next image
String filenameFormatted = String.format(filenameFormat + suffix, i++);
String name = getImageFileName(filenameFormatted, dir, type, true);
if (name == null)
break;
if (filenameFormat == null) {
return null;
}
for (String suffix : getSuffixes()) {
List<Image> list = new ArrayList<Image>();
int i = 0;
while (true) {
// look for next image
String filenameFormatted = String.format(filenameFormat + suffix, i++);
String name = getImageFileName(filenameFormatted, dir, type, true);
if (name == null)
break;
// add image to list
try {
Image img = new Image(name);
if (suffix.equals(HD_SUFFIX))
img = img.getScaledCopy(0.5f);
list.add(img);
} catch (SlickException e) {
EventBus.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", name), BubbleNotificationEvent.COMMONCOLOR_RED));
break;
}
// add image to list
try {
Image img = new Image(name);
if (suffix.equals(HD_SUFFIX))
img = img.getScaledCopy(0.5f);
list.add(img);
} catch (SlickException e) {
BubNotifListener.EVENT.make().onBubNotif(
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;
}
@ -813,15 +825,17 @@ public enum GameImage {
private Image loadImageSingle(File dir) {
for (String suffix : getSuffixes()) {
String name = getImageFileName(filename + suffix, dir, type, true);
if (name != null) {
try {
Image img = new Image(name);
if (suffix.equals(HD_SUFFIX))
img = img.getScaledCopy(0.5f);
return img;
} catch (SlickException e) {
EventBus.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", filename), BubbleNotificationEvent.COMMONCOLOR_RED));
}
if (name == null) {
continue;
}
try {
Image img = new Image(name);
if (suffix.equals(HD_SUFFIX))
img = img.getScaledCopy(0.5f);
return img;
} catch (SlickException e) {
BubNotifListener.EVENT.make().onBubNotif(
String.format("Failed to set image '%s'.", filename), Colors.BUB_RED);
}
}
return null;
@ -865,7 +879,8 @@ public enum GameImage {
skinImages = null;
}
} 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.Image;
import org.newdawn.slick.Input;
import static org.lwjgl.input.Keyboard.*;
/**
* Game mods.
*/
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."),
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."),
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."),
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..."),
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."),
// PERFECT (Category.HARD, 1, GameImage.MOD_PERFECT, "PF", 64, Input.KEY_S, 1f,
// "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."),
// NIGHTCORE (Category.HARD, 2, GameImage.MOD_NIGHTCORE, "NT", 64, Input.KEY_D, 1.12f,
// "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."),
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."),
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**"),
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**"),
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."),
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.");
/** Mod categories. */

View File

@ -18,87 +18,75 @@
package itdelatrisu.opsu;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.utils.ManifestWrapper;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.lang.reflect.Field;
import java.util.jar.JarFile;
/**
* Native loader, based on the JarSplice launcher.
*
* @author http://ninjacave.com
*/
public class NativeLoader {
/** The directory to unpack natives to. */
private final File nativeDir;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Constructor.
* @param dir the directory to unpack natives to
*/
public NativeLoader(File dir) {
nativeDir = dir;
public class NativeLoader {
public static void setNativePath() {
String nativepath = config.NATIVE_DIR.getAbsolutePath();
System.setProperty("org.lwjgl.librarypath", nativepath);
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.
* @throws IOException if an I/O exception occurs
*/
public void loadNatives() throws IOException {
if (!nativeDir.exists())
nativeDir.mkdir();
JarFile jarFile = Utils.getJarFile();
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();
}
public static void loadNatives(JarFile jarfile, ManifestWrapper manifest) throws IOException {
if (!config.NATIVE_DIR.exists() && !config.NATIVE_DIR.mkdir()) {
String msg = String.format("Could not create folder '%s'",
config.NATIVE_DIR.getAbsolutePath());
throw new RuntimeException(msg);
}
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 name = entryName.toLowerCase();
String nativekey = null;
if (osName.startsWith("Win")) {
if (name.endsWith(".dll"))
return true;
nativekey = "WinNatives";
} else if (osName.startsWith("Linux")) {
if (name.endsWith(".so"))
return true;
nativekey = "NixNatives";
} else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
if (name.endsWith(".dylib") || name.endsWith(".jnilib"))
return true;
nativekey = "MacNatives";
}
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 java.io.BufferedInputStream;
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.io.*;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Scanner;
import java.util.jar.JarFile;
@ -50,41 +41,21 @@ import org.json.JSONObject;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Input;
import org.newdawn.slick.util.Log;
import com.sun.jna.platform.FileUtils;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.NotNull;
import yugecin.opsudance.core.Nullable;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Contains miscellaneous utilities.
*/
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.
@ -183,15 +154,12 @@ public class Utils {
* @return true if pressed
*/
public static boolean isGameKeyPressed() {
/*
boolean mouseDown = !Options.isMouseDisabled() && (
input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) ||
input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON));
return (mouseDown ||
input.isKeyDown(Options.getGameKeyLeft()) ||
input.isKeyDown(Options.getGameKeyRight()));
*/
return true;
return
input.isKeyPressed(Options.OPTION_KEY_LEFT.intval) ||
input.isKeyPressed(Options.OPTION_KEY_RIGHT.intval) ||
(!Options.OPTION_DISABLE_MOUSE_BUTTONS.state && (
input.isMousePressed(Input.MOUSE_LEFT_BUTTON) ||
input.isMousePressed(Input.MOUSE_RIGHT_BUTTON)));
}
@ -210,26 +178,31 @@ public class Utils {
}
/**
* Cleans a file name.
* @param badFileName the original name string
* @param replace the character to replace illegal characters with (or 0 if none)
* @return the cleaned file name
* @author Sarel Botha (http://stackoverflow.com/a/5626340)
* Changes bad characters to the replacement char given.
* Bad characters:
* non-printable (0-31)
* " (34) * (42) / (47) : (58)
* < (60) > (62) ? (63) \ (92)
* DEL (124)
*/
public static String cleanFileName(String badFileName, char replace) {
if (badFileName == null)
return null;
boolean doReplace = (replace > 0 && Arrays.binarySearch(illegalChars, replace) < 0);
StringBuilder cleanName = new StringBuilder();
for (int i = 0, n = badFileName.length(); i < n; i++) {
int c = badFileName.charAt(i);
if (Arrays.binarySearch(illegalChars, c) < 0)
cleanName.append((char) c);
else if (doReplace)
cleanName.append(replace);
public static String cleanFileName(@NotNull String badFileName, char replacement) {
char[] chars = badFileName.toCharArray();
long additionalBadChars =
1L << (34 - 32)|
1L << (42 - 32)|
1L << (47 - 32)|
1L << (58 - 32)|
1L << (60 - 32)|
1L << (62 - 32)|
1L << (63 - 32)|
1L << (92 - 32);
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 {
json = new JSONObject(s);
} catch (JSONException e) {
ErrorHandler.error("Failed to create JSON object.", e).show();
explode("Failed to create JSON object.", e, DEFAULT_OPTIONS);
}
}
return json;
@ -357,7 +330,7 @@ public class Utils {
try {
json = new JSONArray(s);
} catch (JSONException e) {
ErrorHandler.error("Failed to create JSON array.", e).show();
explode("Failed to create JSON array.", e, DEFAULT_OPTIONS);
}
}
return json;
@ -399,7 +372,7 @@ public class Utils {
result.append(String.format("%02x", b));
return result.toString();
} catch (NoSuchAlgorithmException | IOException e) {
ErrorHandler.error("Failed to calculate MD5 hash.", e).show();
explode("Failed to calculate MD5 hash.", e, DEFAULT_OPTIONS);
}
return null;
}
@ -418,43 +391,6 @@ public class Utils {
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:
* {@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).
* @return the 40-character SHA-1 hash, or null if it could not be determined
*/
@Nullable
public static String getGitHash() {
if (isJarRunning())
if (env.isJarRunning)
return null;
File f = new File(".git/refs/remotes/origin/master");
if (!f.isFile())
@ -516,11 +453,20 @@ public class Utils {
} catch (Exception e) {}
}
public static int getQuadrant(double x, double y) {
if (x < Options.width / 2d) {
return y < Options.height / 2d ? 2 : 3;
}
return y < Options.height / 2d ? 1 : 4;
/**
* Gets the region where the given point is in.
* First bit is set if x > half
* Second bit is set if y > half
*
* 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) {
double dx = x - Options.width / 2d;
double dy = y - Options.height / 2d;
double dx = x - displayContainer.width / 2d;
double dy = y - displayContainer.height / 2d;
double ang = Math.atan2(dy, dx);
double d = -Math.sqrt(dx * dx + dy * dy);
return new float[]{
(float) (Options.width / 2d + Math.cos(ang) * d),
(float) (Options.height / 2d + Math.sin(ang) * d)
(float) (displayContainer.width / 2d + Math.cos(ang) * d),
(float) (displayContainer.height / 2d + Math.sin(ang) * d)
};
}
public static float[] mirrorPoint(float x, float y, float degrees) {
double dx = x - Options.width / 2d;
double dy = y - Options.height / 2d;
double dx = x - displayContainer.width / 2d;
double dy = y - displayContainer.height / 2d;
double ang = Math.atan2(dy, dx) + (degrees * Math.PI / 180d);
double d = Math.sqrt(dx * dx + dy * dy);
return new float[]{
(float) (Options.width / 2d + Math.cos(ang) * d),
(float) (Options.height / 2d + Math.sin(ang) * d)
(float) (displayContainer.width / 2d + Math.cos(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);
}
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;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
@ -14,6 +12,8 @@ import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
/**
* Extension of Clip that allows playing multiple copies of a Clip simultaneously.
* http://stackoverflow.com/questions/1854616/
@ -194,7 +194,8 @@ public class MultiClip {
try {
audioIn.close();
} 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.UnsupportedAudioFileException;
import itdelatrisu.opsu.ui.Colors;
import org.lwjgl.BufferUtils;
import org.lwjgl.openal.AL;
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.ResourceLoader;
import org.tritonus.share.sampled.file.TAudioFileFormat;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.events.BubNotifListener;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.options.Options.*;
/**
@ -103,7 +103,8 @@ public class MusicController {
if (lastBeatmap == null || !beatmap.audioFilename.equals(lastBeatmap.audioFilename)) {
final File audioFile = beatmap.audioFilename;
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;
}
@ -158,7 +159,7 @@ public class MusicController {
} catch (Exception e) {
String err = String.format("Could not play track '%s'.", file.getName());
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;
} 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.LineUnavailableException;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.options.Options.*;
/**
@ -104,7 +104,7 @@ public class SoundController {
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
return loadClip(ref, audioIn, isMP3);
} catch (Exception e) {
ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e).show();
explode(String.format("Failed to load file '%s'.", ref), e, DEFAULT_OPTIONS);
return null;
}
}
@ -220,7 +220,8 @@ public class SoundController {
// menu and game sounds
for (SoundEffect s : SoundEffect.values()) {
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;
}
MultiClip newClip = loadClip(currentFileName, currentFileName.endsWith(".mp3"));
@ -239,7 +240,8 @@ public class SoundController {
for (HitSound s : HitSound.values()) {
String filename = String.format("%s-%s", ss.getName(), s.getFileName());
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;
}
MultiClip newClip = loadClip(currentFileName, false);
@ -283,7 +285,7 @@ public class SoundController {
try {
clip.start(volume, listener);
} catch (LineUnavailableException e) {
ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e).show();
explode(String.format("Could not start a clip '%s'.", clip.getName()), e, DEFAULT_OPTIONS);
}
}
}
@ -396,7 +398,8 @@ public class SoundController {
@Override
public void error() {
EventBus.post(new BarNotificationEvent("Failed to download track preview."));
BarNotifListener.EVENT.make().onBarNotif(
"Failed to download track preview");
}
});
try {

View File

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

View File

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

View File

@ -23,36 +23,27 @@ import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.List;
import itdelatrisu.opsu.ui.Colors;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.events.BubNotifListener;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Unpacker for OSZ (ZIP) archives.
*/
public class OszUnpacker {
@Inject
private Configuration config;
/** The index of the current file being unpacked. */
private int fileIndex = -1;
/** The total number of files to unpack. */
private File[] files;
@Inject
public OszUnpacker() {
}
/**
* 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
* if no OSZs found
*/
@ -106,7 +97,7 @@ public class OszUnpacker {
} catch (ZipException e) {
String err = String.format("Failed to unzip file %s to dest %s.", file.getAbsolutePath(), dest.getAbsolutePath());
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 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.
@ -89,17 +90,10 @@ public class BeatmapDB {
/** Current size of beatmap cache table. */
private static int cacheSize = -1;
// This class should not be instantiated.
private BeatmapDB() {}
private static Configuration config; // TODO
/**
* Initializes the database connection.
*/
public static void init(Configuration config) {
BeatmapDB.config = config;
public static void init() {
// create a database connection
connection = DBController.createConnection(config.BEATMAP_DB.getPath());
if (connection == null)
@ -115,7 +109,7 @@ public class BeatmapDB {
try {
updateSizeStmt = connection.prepareStatement("REPLACE INTO info (key, value) VALUES ('size', ?)");
} catch (SQLException e) {
ErrorHandler.error("Failed to prepare beatmap statements.", e).show();
explode("Failed to prepare beatmap statements.", e, DEFAULT_OPTIONS);
}
// retrieve the cache size
@ -137,7 +131,7 @@ public class BeatmapDB {
updatePlayStatsStmt = connection.prepareStatement("UPDATE beatmaps SET playCount = ?, lastPlayed = ? WHERE dir = ? AND file = ?");
setFavoriteStmt = connection.prepareStatement("UPDATE beatmaps SET favorite = ? WHERE dir = ? AND file = ?");
} catch (SQLException e) {
ErrorHandler.error("Failed to prepare beatmap statements.", e).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);
stmt.executeUpdate(sql);
} 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();
}
} 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();
} 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.executeUpdate();
} 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;
updateCacheSize();
} catch (SQLException e) {
ErrorHandler.error("Could not drop beatmap database.", e).show();
explode("Could not drop beatmap database.", e, DEFAULT_OPTIONS);
}
createDatabase();
}
@ -296,7 +290,7 @@ public class BeatmapDB {
cacheSize += insertStmt.executeUpdate();
updateCacheSize();
} 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
updateCacheSize();
} 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();
} 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();
} 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();
return map;
} 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;
}
}
@ -621,7 +615,7 @@ public class BeatmapDB {
cacheSize -= deleteMapStmt.executeUpdate();
updateCacheSize();
} 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();
updateCacheSize();
} 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.executeUpdate();
} catch (SQLException e) {
ErrorHandler.error(String.format("Failed to save star rating '%.4f' for beatmap '%s' in database.",
beatmap.starRating, beatmap.toString()), e).show();
explode(String.format("Failed to save star rating '%.4f' for beatmap '%s' in database.",
beatmap.starRating, beatmap.toString()), e, DEFAULT_OPTIONS);
}
}
@ -676,8 +670,8 @@ public class BeatmapDB {
updatePlayStatsStmt.setString(4, beatmap.getFile().getName());
updatePlayStatsStmt.executeUpdate();
} catch (SQLException e) {
ErrorHandler.error(String.format("Failed to update play statistics for beatmap '%s' in database.",
beatmap.toString()), e).show();
explode(String.format("Failed to update play statistics for beatmap '%s' in database.",
beatmap.toString()), e, DEFAULT_OPTIONS);
}
}
@ -695,8 +689,8 @@ public class BeatmapDB {
setFavoriteStmt.setString(3, beatmap.getFile().getName());
setFavoriteStmt.executeUpdate();
} catch (SQLException e) {
ErrorHandler.error(String.format("Failed to update favorite status for beatmap '%s' in database.",
beatmap.toString()), e).show();
explode(String.format("Failed to update favorite status for beatmap '%s' in database.",
beatmap.toString()), e, DEFAULT_OPTIONS);
}
}
@ -716,7 +710,7 @@ public class BeatmapDB {
connection.close();
connection = null;
} 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;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
/**
* Database controller.
*/
@ -35,17 +34,17 @@ public class DBController {
/**
* Initializes all databases.
*/
public static void init(Configuration config) {
public static void init() {
// load the sqlite-JDBC driver using the current class loader
try {
Class.forName("org.sqlite.JDBC");
} 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
BeatmapDB.init(config);
ScoreDB.init(config);
BeatmapDB.init();
ScoreDB.init();
}
/**
@ -66,7 +65,7 @@ public class DBController {
return DriverManager.getConnection(String.format("jdbc:sqlite:%s", path));
} catch (SQLException e) {
// if the error message is "out of memory", it probably means no database file is found
ErrorHandler.error(String.format("Could not connect to database: '%s'.", path), e).show();
explode(String.format("Could not connect to database: '%s'.", path), e, DEFAULT_OPTIONS);
return null;
}
}

View File

@ -20,8 +20,6 @@ package itdelatrisu.opsu.db;
import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.beatmap.Beatmap;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration;
import java.sql.Connection;
import java.sql.PreparedStatement;
@ -36,6 +34,9 @@ import java.util.LinkedList;
import java.util.List;
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.
*/
@ -77,13 +78,10 @@ public class ScoreDB {
/** Score deletion statement. */
private static PreparedStatement deleteSongStmt, deleteScoreStmt;
// This class should not be instantiated.
private ScoreDB() {}
/**
* Initializes the database connection.
*/
public static void init(Configuration config) {
public static void init() {
// create a database connection
connection = DBController.createConnection(config.SCORE_DB.getPath());
if (connection == null)
@ -124,7 +122,7 @@ public class ScoreDB {
// TODO: extra playerName checks not needed if name is guaranteed not null
);
} catch (SQLException e) {
ErrorHandler.error("Failed to prepare score statements.", e).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);
stmt.executeUpdate(sql);
} 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();
}
} 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.executeUpdate();
} 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.executeUpdate();
} 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.executeUpdate();
} 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();
} 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 getSortedArray(list);
@ -377,7 +375,7 @@ public class ScoreDB {
map.put(version, getSortedArray(list));
rs.close();
} 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 map;
@ -406,7 +404,7 @@ public class ScoreDB {
connection.close();
connection = null;
} 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.StandardCopyOption;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.BubNotifListener;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
/**
* File download.
@ -92,7 +93,7 @@ public class Download {
private String localPath;
/** The local path to rename the file to when finished. */
private String rename;
private String renamedFileName;
/** The download URL. */
private URL url;
@ -137,18 +138,20 @@ public class Download {
* Constructor.
* @param remoteURL the download URL
* @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 {
this.url = new URL(remoteURL);
} catch (MalformedURLException e) {
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;
}
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).
*/
public String getLocalPath() { return (rename != null) ? rename : localPath; }
public String getLocalPath() { return (renamedFileName != null) ? renamedFileName : localPath; }
/**
* Sets the download listener.
@ -217,7 +220,7 @@ public class Download {
else if (redirectCount > MAX_REDIRECTS)
error = String.format("Download for URL '%s' is attempting too many redirects (over %d).", base.toString(), MAX_REDIRECTS);
if (error != null) {
EventBus.post(new BubbleNotificationEvent(error, BubbleNotificationEvent.COLOR_ORANGE));
BubNotifListener.EVENT.make().onBubNotif(error, Colors.BUB_ORANGE);
throw new IOException();
}
@ -263,9 +266,9 @@ public class Download {
status = Status.COMPLETE;
rbc.close();
fos.close();
if (rename != null) {
if (renamedFileName != null) {
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)
listener.completed();
@ -419,7 +422,7 @@ public class Download {
}
} catch (IOException e) {
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.Graphics;
import org.newdawn.slick.Image;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.events.BubNotifListener;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
/**
@ -46,9 +44,6 @@ import static yugecin.opsudance.options.Options.*;
*/
public class DownloadNode {
@Inject
private Configuration config;
/** The associated Download object. */
private Download download;
@ -285,12 +280,14 @@ public class DownloadNode {
download.setListener(new DownloadListener() {
@Override
public void completed() {
EventBus.post(new BarNotificationEvent(String.format("Download complete: %s", getTitle())));
BarNotifListener.EVENT.make().onBarNotif(
String.format("Download complete: %s", getTitle()));
}
@Override
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;
@ -412,7 +409,9 @@ public class DownloadNode {
public void drawDownload(Graphics g, float position, int id, boolean hover) {
Download download = this.download; // in case clearDownload() is called asynchronously
if (download == null) {
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;
}

View File

@ -35,26 +35,17 @@ import java.util.Properties;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.core.Constants;
import yugecin.opsudance.events.BarNotifListener;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Handles automatic program updates.
*/
public class Updater {
@Inject
private Configuration config;
private static Updater updater;
public static Updater get() {
return updater;
}
/** The exit confirmation message. */
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.
*/
public String getDescription() { return description; }
};
}
/** The current updater status. */
private Status status;
@ -119,10 +110,8 @@ public class Updater {
return currentVersion.getMajorVersion() + "." + currentVersion.getMinorVersion() + "." + currentVersion.getIncrementalVersion();
}
@Inject
public Updater() {
status = Status.INITIAL;
updater = this;
}
/**
@ -145,7 +134,7 @@ public class Updater {
Date date = null;
try {
Properties props = new Properties();
props.load(ResourceLoader.getResourceAsStream(config.VERSION_FILE));
props.load(ResourceLoader.getResourceAsStream(Constants.VERSION_FILE));
String build = props.getProperty("build.date");
if (build == null || build.equals("${timestamp}") || build.equals("${maven.build.timestamp}"))
date = new Date();
@ -214,16 +203,16 @@ public class Updater {
// get current version
Properties props = new Properties();
props.load(ResourceLoader.getResourceAsStream(config.VERSION_FILE));
props.load(ResourceLoader.getResourceAsStream(Constants.VERSION_FILE));
if ((currentVersion = getVersion(props)) == null)
return;
// get latest version
String s = null;
try {
s = Utils.readDataFromUrl(new URL(config.VERSION_REMOTE));
s = Utils.readDataFromUrl(new URL(Constants.VERSION_REMOTE));
} 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) {
status = Status.CONNECTION_ERROR;
@ -252,13 +241,14 @@ public class Updater {
@Override
public void completed() {
status = Status.UPDATE_DOWNLOADED;
EventBus.post(new BarNotificationEvent("Update has finished downloading"));
BarNotifListener.EVENT.make().onBarNotif("Update has finished downloading");
}
@Override
public void 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();
} catch (IOException e) {
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.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
/**
* Download server: http://bloodcat.com/osu/
*/
public class BloodcatServer extends DownloadServer {
@Inject
public InstanceContainer instanceContainer;
/** Server name. */
private static final String SERVER_NAME = "Bloodcat";
@ -60,10 +56,6 @@ public class BloodcatServer extends DownloadServer {
/** Total result count from the last query. */
private int totalResults = -1;
@Inject
public BloodcatServer() {
}
@Override
public String getName() { return SERVER_NAME; }
@ -89,12 +81,12 @@ public class BloodcatServer extends DownloadServer {
nodes = new DownloadNode[arr.length()];
for (int i = 0; i < nodes.length; 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.getString("title"), item.isNull("titleU") ? null : item.getString("titleU"), //"titleUnicode"
item.getString("artist"), item.isNull("artistU") ? null : item.getString("artistU"), //"artistUnicode"
item.getString("creator")
));
);
}
// store total result count
@ -104,7 +96,7 @@ public class BloodcatServer extends DownloadServer {
resultCount++;
this.totalResults = resultCount;
} catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
explode(String.format("Problem loading result list for query '%s'.", query), e, DEFAULT_OPTIONS);
}
return nodes;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -33,18 +33,14 @@ import java.util.Iterator;
import java.util.List;
import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
/**
* Download server: http://osu.yas-online.net/
*/
public class YaSOnlineServer extends DownloadServer {
@Inject
public InstanceContainer instanceContainer;
/** Server name. */
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). */
private int maxServerID = 0;
@Inject
public YaSOnlineServer() {
}
@Override
public String getName() { return SERVER_NAME; }
@ -121,7 +113,8 @@ public class YaSOnlineServer extends DownloadServer {
String downloadLink = item.getString("downloadLink");
return String.format(DOWNLOAD_FETCH_URL, downloadLink);
} catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem retrieving download URL for beatmap '%d'.", beatmapSetID), e).show();
explode(String.format("Problem retrieving download URL for beatmap '%d'.", beatmapSetID), e,
DEFAULT_OPTIONS);
return null;
} finally {
Utils.setSSLCertValidation(true);
@ -183,7 +176,7 @@ public class YaSOnlineServer extends DownloadServer {
if (serverID > maxServerID)
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()]);
@ -193,7 +186,7 @@ public class YaSOnlineServer extends DownloadServer {
else
this.totalResults = maxServerID;
} catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
explode(String.format("Problem loading result list for query '%s'.", query), e, DEFAULT_OPTIONS);
} finally {
Utils.setSSLCertValidation(true);
}

View File

@ -30,9 +30,8 @@ import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
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.*;
/**
@ -40,9 +39,6 @@ import static yugecin.opsudance.options.Options.*;
*/
public class Circle extends GameObject {
@Inject
private GameObjectRenderer gameObjectRenderer;
/** The associated HitObject. */
private HitObject hitObject;
@ -156,7 +152,7 @@ public class Circle extends GameObject {
@Override
public boolean mousePressed(int x, int y, int trackPosition) {
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 result = hitResult(timeDiff);

View File

@ -35,11 +35,9 @@ import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
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 static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
/**
@ -47,12 +45,6 @@ import static yugecin.opsudance.options.Options.*;
*/
public class Slider extends GameObject {
@Inject
private DisplayContainer displayContainer;
@Inject
private GameObjectRenderer gameObjectRenderer;
/** Slider ball frames. */
private static Image[] sliderBallImages;
@ -605,7 +597,7 @@ public class Slider extends GameObject {
return false;
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[] hitResultOffset = game.getHitResultOffsets();

View File

@ -37,8 +37,8 @@ import org.lwjgl.opengl.GL20;
import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.render.GameObjectRenderer;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
/**
@ -316,7 +316,7 @@ public class CurveRenderState {
double diff_x = x - last_x;
double diff_y = y - 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);
y = (float) (y - diff_y / 2);
} else {

View File

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

View File

@ -28,24 +28,17 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import itdelatrisu.opsu.ui.Colors;
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.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.events.BubNotifListener;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Importer for replay files.
*/
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. */
private final String FAILED_IMPORT_DIR = "failed";
@ -55,10 +48,6 @@ public class ReplayImporter {
/** The total number of replays to import. */
private File[] files;
@Inject
public ReplayImporter() {
}
/**
* 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.
@ -80,21 +69,21 @@ public class ReplayImporter {
if (!config.replayDir.isDirectory() && !config.replayDir.mkdir()) {
String err = String.format("Failed to create replay directory '%s'.", config.replayDir.getAbsolutePath());
Log.error(err);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
return;
}
// import OSRs
for (File file : files) {
fileIndex++;
Replay r = instanceContainer.injectFields(new Replay(file));
Replay r = new Replay(file);
try {
r.loadHeader();
} catch (IOException e) {
moveToFailedDirectory(file);
String err = String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName());
Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
continue;
}
Beatmap beatmap = BeatmapSetList.get().getBeatmapFromHash(r.beatmapHash);
@ -113,7 +102,7 @@ public class ReplayImporter {
moveToFailedDirectory(file);
String err = String.format("Failed to import replay '%s'. The associated beatmap could not be found.", file.getName());
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.LinkedList;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.Color;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.BubNotifListener;
/**
* Loads skin configuration files.
@ -293,7 +293,7 @@ public class SkinLoader {
} catch (IOException e) {
String err = String.format("Failed to read file '%s'.", skinFile.getAbsolutePath());
Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
}
return skin;

View File

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

View File

@ -22,10 +22,8 @@ import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapSetNode;
import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.downloads.Download;
import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.DownloadNode;
@ -54,12 +52,11 @@ import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.gui.TextField;
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.events.BarNotificationEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.events.BarNotifListener;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Downloads menu.
@ -69,18 +66,6 @@ import yugecin.opsudance.options.Configuration;
*/
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. */
private static final int SEARCH_DELAY = 700;
@ -275,32 +260,35 @@ public class DownloadsMenu extends ComplexOpsuState {
} finally {
finished = true;
}
};
}
/** Imports all packed beatmaps. */
private void importBeatmaps() {
// invoke unpacker and parser
File[] dirs = oszUnpacker.unpackAll();
if (dirs != null && dirs.length > 0) {
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)));
}
}
File[] dirs = oszunpacker.unpackAll();
this.importedNode = beatmapParser.parseDirectories(dirs);
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(InstanceContainer instanceContainer) {
public DownloadsMenu() {
SERVERS = new DownloadServer[] {
instanceContainer.provide(BloodcatServer.class),
instanceContainer.provide(YaSOnlineServer.class),
instanceContainer.provide(MnetworkServer.class),
instanceContainer.provide(MengSkyServer.class),
new BloodcatServer(),
new YaSOnlineServer(),
new MnetworkServer(),
new MengSkyServer(),
};
}
@ -319,7 +307,7 @@ public class DownloadsMenu extends ComplexOpsuState {
// search
searchTimer = SEARCH_DELAY;
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
public boolean isFocusable() {
return false;
@ -555,7 +543,7 @@ public class DownloadsMenu extends ComplexOpsuState {
// focus new beatmap
// 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;
}
@ -633,7 +621,7 @@ public class DownloadsMenu extends ComplexOpsuState {
// back
if (UI.getBackButton().contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK);
displayContainer.switchState(MainMenu.class);
displayContainer.switchState(mainmenuState);
return true;
}
@ -698,7 +686,7 @@ public class DownloadsMenu extends ComplexOpsuState {
if (playing)
previewID = node.getID();
} 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);
}
}
@ -721,7 +709,7 @@ public class DownloadsMenu extends ComplexOpsuState {
if (!DownloadList.get().contains(node.getID())) {
node.createDownload(serverMenu.getSelectedItem());
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 {
DownloadList.get().addNode(node);
node.getDownload().start();
@ -901,12 +889,12 @@ public class DownloadsMenu extends ComplexOpsuState {
}
// block input during beatmap importing
if (importThread != null && key != Input.KEY_ESCAPE) {
if (importThread != null && key != KEY_ESCAPE) {
return true;
}
switch (key) {
case Input.KEY_ESCAPE:
case KEY_ESCAPE:
if (importThread != null) {
// beatmap importing: stop parsing beatmaps by sending interrupt to BeatmapParser
importThread.interrupt();
@ -918,16 +906,16 @@ public class DownloadsMenu extends ComplexOpsuState {
} else {
// return to main menu
SoundController.playSound(SoundEffect.MENUBACK);
displayContainer.switchState(MainMenu.class);
displayContainer.switchState(mainmenuState);
}
return true;
case Input.KEY_ENTER:
case KEY_RETURN:
if (!search.getText().isEmpty()) {
pageDir = Page.RESET;
resetSearchTimer();
}
return true;
case Input.KEY_F5:
case KEY_F5:
SoundController.playSound(SoundEffect.MENUCLICK);
lastQuery = null;
pageDir = Page.CURRENT;
@ -937,7 +925,7 @@ public class DownloadsMenu extends ComplexOpsuState {
return true;
}
// 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);
searchTimer = 0;
pageDir = Page.RESET;
@ -963,7 +951,7 @@ public class DownloadsMenu extends ComplexOpsuState {
pageDir = Page.RESET;
previewID = -1;
if (barNotificationOnLoad != null) {
EventBus.post(new BarNotificationEvent(barNotificationOnLoad));
BarNotifListener.EVENT.make().onBarNotif(barNotificationOnLoad);
barNotificationOnLoad = null;
}
}

View File

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

View File

@ -30,10 +30,10 @@ import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.BaseOpsuState;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
/**
@ -44,12 +44,6 @@ import static yugecin.opsudance.options.Options.*;
*/
public class GamePauseMenu extends BaseOpsuState {
@Inject
private InstanceContainer instanceContainer;
@Inject
private Game gameState;
private MenuButton continueButton, retryButton, backButton;
@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
if (gameState.getRestart() == Game.Restart.LOSE) {
SoundController.playSound(SoundEffect.MENUBACK);
instanceContainer.provide(SongMenu.class).resetGameDataOnLoad();
songMenuState.resetGameDataOnLoad();
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
displayContainer.switchState(SongMenu.class);
displayContainer.switchState(songMenuState);
} else {
SoundController.playSound(SoundEffect.MENUBACK);
gameState.setRestart(Game.Restart.FALSE);
displayContainer.switchState(Game.class);
displayContainer.switchState(gameState);
}
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);
displayContainer.switchState(Game.class);
displayContainer.switchState(gameState);
return true;
}
@ -139,14 +133,14 @@ public class GamePauseMenu extends BaseOpsuState {
if (continueButton.contains(x, y) && !loseState) {
SoundController.playSound(SoundEffect.MENUBACK);
gameState.setRestart(Game.Restart.FALSE);
displayContainer.switchState(Game.class);
displayContainer.switchState(gameState);
} else if (retryButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT);
gameState.setRestart(Game.Restart.MANUAL);
displayContainer.switchState(Game.class);
displayContainer.switchState(gameState);
} else if (backButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK);
instanceContainer.provide(SongMenu.class).resetGameDataOnLoad();
songMenuState.resetGameDataOnLoad();
if (loseState)
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
else
@ -155,7 +149,7 @@ public class GamePauseMenu extends BaseOpsuState {
displayContainer.resetCursor();
}
MusicController.setPitch(1.0f);
displayContainer.switchState(SongMenu.class);
displayContainer.switchState(songMenuState);
}
return true;
@ -188,10 +182,9 @@ public class GamePauseMenu extends BaseOpsuState {
@Override
public boolean onCloseRequest() {
SongMenu songmenu = instanceContainer.provide(SongMenu.class);
songmenu.resetTrackOnLoad();
songmenu.resetGameDataOnLoad();
displayContainer.switchState(SongMenu.class);
songMenuState.resetTrackOnLoad();
songMenuState.resetGameDataOnLoad();
displayContainer.switchState(songMenuState);
return false;
}

View File

@ -32,15 +32,15 @@ import itdelatrisu.opsu.ui.UI;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
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.
@ -51,9 +51,6 @@ import yugecin.opsudance.events.BarNotificationEvent;
*/
public class GameRanking extends BaseOpsuState {
@Inject
private InstanceContainer instanceContainer;
/** Associated GameData object. */
private GameData data;
@ -125,7 +122,7 @@ public class GameRanking extends BaseOpsuState {
return true;
}
if (key == Input.KEY_ESCAPE) {
if (key == Keyboard.KEY_ESCAPE) {
returnToSongMenu();
}
return true;
@ -149,7 +146,6 @@ public class GameRanking extends BaseOpsuState {
}
// replay
Game gameState = instanceContainer.provide(Game.class);
boolean returnToGame = false;
boolean replayButtonPressed = replayButton.contains(x, y);
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);
returnToGame = true;
} catch (FileNotFoundException e) {
EventBus.post(new BarNotificationEvent("Replay file not found."));
BarNotifListener.EVENT.make().onBarNotif("Replay file not found.");
} catch (IOException 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
EventBus.post(new BarNotificationEvent("Replay file not found."));
BarNotifListener.EVENT.make().onBarNotif("Replay file not found.");
}
// retry
@ -183,7 +180,7 @@ public class GameRanking extends BaseOpsuState {
Beatmap beatmap = MusicController.getBeatmap();
gameState.loadBeatmap(beatmap);
SoundController.playSound(SoundEffect.MENUHIT);
displayContainer.switchState(Game.class);
displayContainer.switchState(gameState);
}
return true;
}
@ -217,12 +214,11 @@ public class GameRanking extends BaseOpsuState {
@Override
public boolean onCloseRequest() {
SongMenu songmenu = instanceContainer.provide(SongMenu.class);
if (data != null && data.isGameplay()) {
songmenu.resetTrackOnLoad();
songMenuState.resetTrackOnLoad();
}
songmenu.resetGameDataOnLoad();
displayContainer.switchState(SongMenu.class);
songMenuState.resetGameDataOnLoad();
displayContainer.switchState(songMenuState);
return false;
}
@ -232,25 +228,24 @@ public class GameRanking extends BaseOpsuState {
private void returnToSongMenu() {
SoundController.muteSoundComponent();
SoundController.playSound(SoundEffect.MENUBACK);
SongMenu songMenu = instanceContainer.provide(SongMenu.class);
if (data.isGameplay()) {
songMenu.resetTrackOnLoad();
songMenuState.resetTrackOnLoad();
}
songMenu.resetGameDataOnLoad();
songMenuState.resetGameDataOnLoad();
if (displayContainer.cursor.isBeatmapSkinned()) {
displayContainer.resetCursor();
}
displayContainer.switchState(SongMenu.class);
displayContainer.switchState(songMenuState);
}
/**
* Sets the associated GameData object.
* @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).
*/
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.Input;
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.Constants;
import yugecin.opsudance.core.state.BaseOpsuState;
import yugecin.opsudance.core.state.OpsuState;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.events.BubNotifListener;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
/**
@ -62,12 +62,6 @@ import static yugecin.opsudance.options.Options.*;
*/
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. */
private static final short LOGO_IDLE_DELAY = 10000;
@ -472,12 +466,11 @@ public class MainMenu extends BaseOpsuState {
UI.enter();
if (!enterNotification) {
if (updater.getStatus() == Updater.Status.UPDATE_AVAILABLE) {
EventBus.post(new BarNotificationEvent("An opsu! update is available."));
enterNotification = true;
BarNotifListener.EVENT.make().onBarNotif("An opsu! update is available.");
} else if (updater.justUpdated()) {
EventBus.post(new BarNotificationEvent("opsu! is now up to date!"));
enterNotification = true;
BarNotifListener.EVENT.make().onBarNotif("opsu! is now up to date!");
}
enterNotification = true;
}
// reset measure info
@ -538,58 +531,60 @@ public class MainMenu extends BaseOpsuState {
if (musicPlay.contains(x, y)) {
if (MusicController.isPlaying()) {
MusicController.pause();
EventBus.post(new BarNotificationEvent("Pause"));
BarNotifListener.EVENT.make().onBarNotif("Pause");
} else if (!MusicController.isTrackLoading()) {
MusicController.resume();
EventBus.post(new BarNotificationEvent("Play"));
BarNotifListener.EVENT.make().onBarNotif("Play");
}
return true;
} else if (musicNext.contains(x, y)) {
nextTrack(true);
EventBus.post(new BarNotificationEvent(">> Next"));
BarNotifListener.EVENT.make().onBarNotif(">> Next");
return true;
} else if (musicPrevious.contains(x, y)) {
lastMeasureProgress = 0f;
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) {
bgAlpha.setTime(0);
}
} else {
MusicController.setPosition(0);
}
EventBus.post(new BarNotificationEvent("<< Previous"));
BarNotifListener.EVENT.make().onBarNotif("<< Previous");
return true;
}
// downloads button actions
if (downloadsButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT);
displayContainer.switchState(DownloadsMenu.class);
displayContainer.switchState(downloadState);
return true;
}
// repository button actions
if (repoButton != null && repoButton.contains(x, y)) {
try {
Desktop.getDesktop().browse(config.REPOSITORY_URI);
Desktop.getDesktop().browse(Constants.REPOSITORY_URI);
} 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) {
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;
}
if (danceRepoButton != null && danceRepoButton.contains(x, y)) {
try {
Desktop.getDesktop().browse(config.DANCE_REPOSITORY_URI);
Desktop.getDesktop().browse(Constants.DANCE_REPOSITORY_URI);
} 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) {
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;
}
@ -657,18 +652,18 @@ public class MainMenu extends BaseOpsuState {
}
switch (key) {
case Input.KEY_ESCAPE:
case Input.KEY_Q:
case KEY_ESCAPE:
case KEY_Q:
if (logoTimer > 0) {
logoState = LogoState.CLOSING;
logoClose.setTime(0);
logoTimer = 0;
break;
}
instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.EXIT);
displayContainer.switchState(ButtonMenu.class);
buttonState.setMenuState(MenuState.EXIT);
displayContainer.switchState(buttonState);
return true;
case Input.KEY_P:
case KEY_P:
SoundController.playSound(SoundEffect.MENUHIT);
if (logoState == LogoState.DEFAULT || logoState == LogoState.CLOSING) {
logoState = LogoState.OPENING;
@ -679,17 +674,17 @@ public class MainMenu extends BaseOpsuState {
} else
enterSongMenu();
return true;
case Input.KEY_D:
case KEY_D:
SoundController.playSound(SoundEffect.MENUHIT);
displayContainer.switchState(DownloadsMenu.class);
displayContainer.switchState(downloadState);
return true;
case Input.KEY_R:
case KEY_R:
nextTrack(true);
return true;
case Input.KEY_UP:
case KEY_UP:
UI.changeVolume(1);
return true;
case Input.KEY_DOWN:
case KEY_DOWN:
UI.changeVolume(-1);
return true;
}
@ -719,7 +714,7 @@ public class MainMenu extends BaseOpsuState {
MusicController.playAt(0, false);
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;
if (node != null) {
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.
*/
private void enterSongMenu() {
Class<? extends OpsuState> state = SongMenu.class;
OpsuState state = songMenuState;
if (BeatmapSetList.get().getMapSetCount() == 0) {
instanceContainer.provide(DownloadsMenu.class).notifyOnLoad("Download some beatmaps to get started!");
state = DownloadsMenu.class;
downloadState.notifyOnLoad("Download some beatmaps to get started!");
state = downloadState;
}
displayContainer.switchState(state);
}

View File

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

View File

@ -49,6 +49,11 @@ public class Colors {
BLACK_BG_NORMAL = new Color(0, 0, 0, 0.25f),
BLACK_BG_HOVER = new Color(0, 0, 0, 0.5f),
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);
// 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.AnimationEquation;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Color;
import org.newdawn.slick.Font;
import org.newdawn.slick.Graphics;
@ -82,7 +83,7 @@ public class DropdownMenu<E> extends Component {
@Override
public void keyPressed(int key, char c) {
if (key == Input.KEY_ESCAPE) {
if (key == Keyboard.KEY_ESCAPE) {
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.util.Log;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.core.Constants;
/**
* 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 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();
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));
DEFAULT = new UnicodeFont(font);
BOLD = new UnicodeFont(font.deriveFont(Font.BOLD));

View File

@ -47,7 +47,7 @@ public class KineticScrolling {
private float totalDelta;
/** 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. */
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.Font;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.geom.Rectangle;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.components.ActionListener;
import yugecin.opsudance.core.components.Component;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* 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 KEY_REPEAT_INTERVAL = 50;
private final DisplayContainer displayContainer;
private String value = "";
private Font font;
private int maxCharacter = 10000;
@ -65,8 +64,7 @@ public class TextField extends Component {
private ActionListener listener;
public TextField(DisplayContainer displayContainer, Font font, int x, int y, int width, int height) {
this.displayContainer = displayContainer;
public TextField(Font font, int x, int y, int width, int height) {
this.font = font;
this.x = x;
this.y = y;
@ -97,7 +95,7 @@ public class TextField extends Component {
public void render(Graphics g) {
if (lastKey != -1) {
if (displayContainer.input.isKeyDown(lastKey)) {
if (isKeyDown(lastKey)) {
if (repeatTimer < System.currentTimeMillis()) {
repeatTimer = System.currentTimeMillis() + KEY_REPEAT_INTERVAL;
keyPressed(lastKey, lastChar);
@ -170,10 +168,8 @@ public class TextField extends Component {
}
public void keyPressed(int key, char c) {
if (key != -1)
{
if ((key == Input.KEY_V) &&
((displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) || (displayContainer.input.isKeyDown(Input.KEY_RCONTROL)))) {
if (key != -1) {
if (key == KEY_V && input.isControlDown()) {
String text = Sys.getClipboard();
if (text != null) {
doPaste(text);
@ -190,7 +186,7 @@ public class TextField extends Component {
}
lastChar = c;
if (key == Input.KEY_LEFT) { /*
if (key == KEY_LEFT) { /*
if (cursorPos > 0) {
cursorPos--;
}
@ -198,7 +194,7 @@ public class TextField extends Component {
if (consume) {
container.getInput().consumeEvent();
}
*/ } else if (key == Input.KEY_RIGHT) { /*
*/ } else if (key == KEY_RIGHT) { /*
if (cursorPos < value.length()) {
cursorPos++;
}
@ -206,9 +202,9 @@ public class TextField extends Component {
if (consume) {
container.getInput().consumeEvent();
}
*/ } else if (key == Input.KEY_BACK) {
*/ } else if (key == KEY_BACK) {
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;
boolean startSpace = Character.isWhitespace(value.charAt(cursorPos - 1));
boolean charSeen = false;
@ -240,7 +236,7 @@ public class TextField extends Component {
cursorPos--;
}
}
} else if (key == Input.KEY_DELETE) {
} else if (key == KEY_DELETE) {
if (value.length() > cursorPos) {
value = value.substring(0,cursorPos) + value.substring(cursorPos+1);
}
@ -252,7 +248,7 @@ public class TextField extends Component {
value = value.substring(0, cursorPos) + c;
}
cursorPos++;
} else if (key == Input.KEY_RETURN) {
} else if (key == KEY_RETURN) {
if (listener != null) {
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 static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
public class Dancer {
@ -194,12 +195,12 @@ public class Dancer {
}
isCurrentLazySlider = false;
// 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;
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);
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;
}
}
@ -251,8 +252,8 @@ public class Dancer {
}
}
Pippi.dance(time, c, isCurrentLazySlider);
x = Utils.clamp(x, 10, width - 10);
y = Utils.clamp(y, 10, height - 10);
x = Utils.clamp(x, 10, displayContainer.width - 10);
y = Utils.clamp(y, 10, displayContainer.height - 10);
}
private void createNewMover() {

View File

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

View File

@ -19,9 +19,9 @@ package yugecin.opsudance;
import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.Slider;
import yugecin.opsudance.render.GameObjectRenderer;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
public class Pippi {
@ -38,14 +38,14 @@ public class Pippi {
public static void setRadiusPercent(int 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() {
angle = 0;
currentdelta = 0;
setRadiusPercent(radiusPercent);
pippimaxrad = GameObjectRenderer.instance.getCircleDiameter() - 10d;
pippimaxrad = gameObjectRenderer.circleDiameter - 10d;
}
public static void dance(int time, GameObject c, boolean isCurrentLazySlider) {
@ -92,7 +92,7 @@ public class Pippi {
}
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
* 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 org.newdawn.slick.Graphics;
import java.net.URI;
public abstract class FadeTransitionState extends TransitionState {
public class Constants {
private final Color black;
public FadeTransitionState() {
super();
black = new Color(Color.black);
}
@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);
public static final String PROJECT_NAME = "opsu!dance";
public static final String FONT_NAME = "DroidSansFallback.ttf";
public static final String VERSION_FILE = "version";
public static final URI REPOSITORY_URI = URI.create("https://github.com/itdelatrisu/opsu");
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";
}

View File

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

View File

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

View File

@ -17,38 +17,37 @@
*/
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")
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 static <T> void subscribe(Class<T> eventType, EventListener<T> eventListener) {
subscribers.add(new Subscriber<>(eventType, eventListener));
public void addListener(T listener) {
this.listeners.add(listener);
}
public static void post(Object event) {
for (Subscriber s : subscribers) {
if (s.eventType.isInstance(event)) {
s.listener.onEvent(event);
}
}
}
private static class Subscriber<T> {
private final Class<T> eventType;
private final EventListener<T> listener;
private Subscriber(Class<T> eventType, EventListener<T> listener) {
this.eventType = eventType;
this.listener = listener;
}
public T make() {
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
for (T listener : listeners) {
method.invoke(listener, args);
}
return null;
}
});
}
}

View File

@ -13,6 +13,7 @@
* 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.inject;
@ -22,14 +23,10 @@ import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.states.*;
import yugecin.opsudance.PreStartupInitializer;
import yugecin.opsudance.core.DisplayContainer;
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.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.options.Configuration;
import yugecin.opsudance.options.OptionsService;
@ -48,18 +45,14 @@ public class OpsuDanceInjector extends Injector {
bind(Updater.class).asLazySingleton();
bind(SkinService.class).asEagerSingleton();
bind(PreStartupInitializer.class).asEagerSingleton();
//bind(PreStartupInitializer.class).asEagerSingleton();
bind(DisplayContainer.class).asEagerSingleton();
bind(ErrorHandler.class).asEagerSingleton();
bind(FpsRenderState.class).asEagerSingleton();
bind(BarNotificationState.class).asEagerSingleton();
bind(BubbleNotificationState.class).asEagerSingleton();
bind(EmptyTransitionState.class).asEagerSingleton();
bind(FadeInTransitionState.class).asEagerSingleton();
bind(FadeOutTransitionState.class).asEagerSingleton();
bind(BubNotifState.class).asEagerSingleton();
bind(GameObjectRenderer.class).asEagerSingleton();

View File

@ -17,33 +17,12 @@
*/
package yugecin.opsudance.core.state;
import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.UI;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
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 yugecin.opsudance.events.ResolutionChangedListener;
import java.io.StringWriter;
import static yugecin.opsudance.options.Options.*;
public abstract class BaseOpsuState implements OpsuState, EventListener<ResolutionOrSkinChangedEvent> {
@Inject
protected DisplayContainer displayContainer;
@Inject
protected Configuration config;
@Inject
protected SkinService skinService;
public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListener {
/**
* 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;
public BaseOpsuState() {
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, this);
ResolutionChangedListener.EVENT.addListener(this);
}
protected void revalidate() {
@ -71,7 +50,7 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
}
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
public void onResolutionChanged(int w, int h) {
if (isCurrentState) {
revalidate();
return;
@ -105,32 +84,11 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
@Override
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;
}
@Override
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;
}

View File

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

View File

@ -18,9 +18,10 @@
package yugecin.opsudance.core.state;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.InputListener;
import yugecin.opsudance.core.errorhandling.ErrorDumpable;
public interface OpsuState extends ErrorDumpable {
public interface OpsuState extends ErrorDumpable, InputListener {
void update();
void preRenderUpdate();
@ -33,34 +34,4 @@ public interface OpsuState extends ErrorDumpable {
*/
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 org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.events.ResolutionChangedListener;
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 DISPLAY_TIME = 5700 + IN_TIME;
private final int OUT_TIME = 200;
private final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME;
private final DisplayContainer displayContainer;
private final Color bgcol;
private final Color textCol;
@ -49,21 +47,12 @@ public class BarNotificationState implements EventListener<BarNotificationEvent>
private int barHalfTargetHeight;
private int barHalfHeight;
public BarNotificationState(DisplayContainer displayContainer) {
this.displayContainer = displayContainer;
public BarNotificationState() {
this.bgcol = new Color(Color.black);
this.textCol = new Color(Color.white);
this.timeShown = TOTAL_TIME;
EventBus.subscribe(BarNotificationEvent.class, this);
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, new EventListener<ResolutionOrSkinChangedEvent>() {
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
if (timeShown >= TOTAL_TIME) {
return;
}
calculatePosition();
}
});
BarNotifListener.EVENT.addListener(this);
ResolutionChangedListener.EVENT.addListener(this);
}
public void render(Graphics g) {
@ -108,10 +97,18 @@ public class BarNotificationState implements EventListener<BarNotificationEvent>
}
@Override
public void onEvent(BarNotificationEvent event) {
this.message = event.message;
public void onBarNotif(String message) {
this.message = message;
calculatePosition();
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 org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import org.newdawn.slick.MouseListener;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.ResolutionChangedListener;
import java.util.LinkedList;
import java.util.List;
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 DISPLAY_TIME = 7000 + IN_TIME;
public static final int OUT_TIME = 433;
public static final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME;
private final DisplayContainer displayContainer;
private final LinkedList<Notification> bubbles;
private int addAnimationTime;
private int addAnimationHeight;
public BubbleNotificationState(DisplayContainer displayContainer) {
this.displayContainer = displayContainer;
public BubNotifState() {
this.bubbles = new LinkedList<>();
this.addAnimationTime = IN_TIME;
EventBus.subscribe(BubbleNotificationEvent.class, this);
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, new EventListener<ResolutionOrSkinChangedEvent>() {
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
calculatePositions();
}
});
BubNotifListener.EVENT.addListener(this);
ResolutionChangedListener.EVENT.addListener(this);
}
public void render(Graphics g) {
@ -79,20 +72,9 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
} 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() {
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.paddingY = (int) (displayContainer.height * 0.0144f);
Notification.finalX = displayContainer.width - Notification.width - (int) (displayContainer.width * 0.01);
@ -130,9 +112,9 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
}
@Override
public void onEvent(BubbleNotificationEvent event) {
public void onBubNotif(String message, Color borderColor) {
finishAddAnimation();
Notification newBubble = new Notification(event.message, event.borderColor);
Notification newBubble = new Notification(message, borderColor);
bubbles.add(0, newBubble);
addAnimationTime = 0;
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 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);
y += lineHeight;
}
return timeShown > BubbleNotificationState.TOTAL_TIME;
return timeShown > BubNotifState.TOTAL_TIME;
}
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.g = targetBorderColor.g + (0.977f - targetBorderColor.g) * hoverProgress;
borderColor.b = targetBorderColor.b + (0.977f - targetBorderColor.b) * hoverProgress;
if (timeShown < BubbleNotificationState.IN_TIME) {
float progress = (float) timeShown / BubbleNotificationState.IN_TIME;
if (timeShown < BubNotifState.IN_TIME) {
float progress = (float) timeShown / BubNotifState.IN_TIME;
this.x = finalX + (int) ((1 - AnimationEquation.OUT_BACK.calc(progress)) * width / 2);
textColor.a = borderColor.a = bgcol.a = progress;
bgcol.a = borderColor.a * 0.8f;
return;
}
x = Notification.finalX;
if (timeShown > BubbleNotificationState.DISPLAY_TIME) {
if (timeShown > BubNotifState.DISPLAY_TIME) {
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;
bgcol.a = borderColor.a * 0.8f;
}
@ -236,7 +251,7 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
private boolean mouseReleased(int x, int y) {
if (!isFading && isMouseHovered(x, y)) {
timeShown = BubbleNotificationState.DISPLAY_TIME;
timeShown = BubNotifState.DISPLAY_TIME;
return true;
}
return false;

View File

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

View File

@ -15,13 +15,14 @@
* 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;
package yugecin.opsudance.events;
public class FadeInTransitionState extends FadeTransitionState {
import yugecin.opsudance.core.events.Event;
@Override
protected float getMaskAlphaLevel(float fadeProgress) {
return 1f - fadeProgress;
}
public interface SkinChangedListener {
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