Merge branch 'master' into kockout-wdata

# Conflicts:
#	src/itdelatrisu/opsu/states/Game.java
#	src/itdelatrisu/opsu/states/Splash.java
This commit is contained in:
yugecin 2018-10-22 21:59:11 +02:00
commit 3d470cbe7a
145 changed files with 2112 additions and 1884 deletions

View File

@ -21,6 +21,7 @@ The images included in opsu! belong to their respective authors.
* kouyang
* teinecthel
* Font Awesome by Dave Gandy - http://fontawesome.io
* Chaaoos/C0N - the opsu!dance logo
Projects
--------
@ -32,5 +33,4 @@ The following projects were referenced in creating opsu!:
Theme Song
----------
The theme song is "On the Bach" by Jingle Punks, from the [YouTube Audio Library]
(https://www.youtube.com/audiolibrary/music).
The theme song is "On the Bach" by Jingle Punks, from the [YouTube Audio Library](https://www.youtube.com/audiolibrary/music).

View File

@ -2,7 +2,7 @@
**Table of contents**
* [What](#What) - [Why](#why) - [Downloads](#downloads) - [Running](#running) - [Building a JAR](#building-a-jar) - [Credits](#credits) - [License](#license)
* [What](#what) - [Why](#why) - [Downloads](#downloads) - [Running](#running) - [Building a JAR](#building-a-jar) - [Credits](#credits) - [License](#license)
What
----
@ -38,10 +38,7 @@ You should know how to do this. It's recommended to use a working directory like
### Using apache ant
Resolve dependencies first, use either:
* `ant ivyresolve` to download dependencies using apache ivy
* `ant mvnresolve` to download dependencies using apache mvn
Resolve dependencies first by doing `ant mvnresolve` or `mvn initialize`
Then do `ant run`

View File

@ -1,9 +1,8 @@
<project name="opsu!dance" default="hi" xmlns:ivy="antlib:org.apache.ivy.ant">
<project name="opsu!dance" default="hi">
<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" />
@ -21,14 +20,12 @@
<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),
resolve dependencies first (mvnresolve)
then run (code is compiled automatically when you run)
</echo>
</target>
@ -40,12 +37,6 @@ then run (code is compiled automatically when you run)
<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">
@ -119,9 +110,6 @@ then run (code is compiled automatically when you run)
<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">

38
ivy.xml
View File

@ -1,38 +0,0 @@
<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>

11
pom.xml
View File

@ -8,12 +8,12 @@
<artifactId>opsu-dance</artifactId>
<version>0.5.0-SNAPSHOT</version>
<properties>
<nativesTargetDir>target/Natives</nativesTargetDir>
<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>
<mainClassName>yugecin.opsudance.core.Entrypoint</mainClassName>
<XDG>false</XDG>
</properties>
<build>
<sourceDirectory>src</sourceDirectory>
@ -147,10 +147,6 @@
<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>
@ -236,11 +232,6 @@
<artifactId>jna-platform</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>

BIN
res/epiwarning.pdn Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 822 B

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

BIN
res/logo2piece.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

BIN
res/logo2pulse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 B

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 328 KiB

BIN
res/menu-background.pdn Normal file

Binary file not shown.

BIN
res/menu-background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

BIN
res/menu-options-1.pdn Normal file

Binary file not shown.

BIN
res/menu-options-2.pdn Normal file

Binary file not shown.

BIN
res/menu-options-3.pdn Normal file

Binary file not shown.

Binary file not shown.

BIN
res/menu-options.pdn Normal file

Binary file not shown.

BIN
res/menu-options.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 568 B

After

Width:  |  Height:  |  Size: 306 B

BIN
res/music-np-bg-black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

BIN
res/music-np-bg-white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

BIN
res/music-np.pdn Normal file

Binary file not shown.

BIN
res/music-np.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 B

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 B

After

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 572 B

After

Width:  |  Height:  |  Size: 318 B

BIN
res/music-stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 KiB

After

Width:  |  Height:  |  Size: 116 B

BIN
res/selection-mode-over.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
res/selection-mode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 767 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

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

View File

@ -131,6 +131,6 @@ public class CombinedSpiralMover extends Mover {
}
private boolean checkBounds(double[] pos) {
return 0 < pos[0] && pos[0] < displayContainer.width && 0 < pos[1] && pos[1] < displayContainer.height;
return 0 < pos[0] && pos[0] < width && 0 < pos[1] && pos[1] < height;
}
}

View File

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

View File

@ -43,18 +43,16 @@ public class SpiralSpinner extends Spinner {
double ang;
double rad;
for (int i = 0; i < SIZE / 2; i++) {
MAX_RAD = (int) (displayContainer.height * .35);
MAX_RAD = (int) (height * .35);
ang = (DENSITY * (Math.PI / SIZE) * i);
rad = (MAX_RAD / (SIZE / 2)) * i;
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)
width2 + rad * Math.cos(ang),
height2 + rad * Math.sin(ang)
};
points[SIZE / 2 + i] = new double[]{
offsetX + rad * (Math.cos(ang) * Math.cos(Math.PI) - Math.sin(ang) * Math.sin(Math.PI)),
offsetY + rad * -Math.sin(ang)
width2 + rad * (Math.cos(ang) * Math.cos(Math.PI) - Math.sin(ang) * Math.sin(Math.PI)),
height2 + rad * -Math.sin(ang)
};
}
}
@ -84,12 +82,12 @@ public class SpiralSpinner extends Spinner {
}
private void rotatePointAroundCenter(double[] point, double beta) {
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);
double angle = Math.atan2(point[1] - height2, point[0] - width2);
double rad = Utils.distance(point[0], point[1], width2, height2);
//rotationMatrix
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));
point[0] = width2 + rad * (Math.cos(angle) * Math.cos(beta) - Math.sin(angle) * Math.sin(beta));
point[1] = height2 + rad * (Math.cos(angle) * Math.sin(beta) + Math.sin(angle) * Math.cos(beta));
}

View File

@ -603,8 +603,6 @@ 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();
@ -796,9 +794,6 @@ 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;
@ -863,7 +858,7 @@ public class GameData {
if (comboMax == fullObjectCount) {
GameImage.RANKING_PERFECT.getImage().draw(
width * 0.08f,
(height * 0.99f) - GameImage.RANKING_PERFECT.getImage().getHeight()
(height * 0.99f) - GameImage.RANKING_PERFECT.getHeight()
);
}
@ -911,7 +906,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(displayContainer.width / 2, displayContainer.height / 4);
spinnerOsu.drawCentered(width2, height / 4);
spinnerOsu.setAlpha(1f);
} else if (OPTION_SHOW_HIT_LIGHTING.state && !hitResult.hideResult && hitResult.result != HIT_MISS &&
// hit lighting
@ -1185,7 +1180,7 @@ public class GameData {
// combo burst
if (comboBurstIndex > -1 && OPTION_SHOW_COMBO_BURSTS.state) {
int leftX = 0;
int rightX = displayContainer.width - comboBurstImages[comboBurstIndex].getWidth();
int rightX = width - comboBurstImages[comboBurstIndex].getWidth();
if (comboBurstX < leftX) {
comboBurstX += (delta / 2f) * GameImage.getUIscale();
if (comboBurstX > leftX)
@ -1246,7 +1241,7 @@ public class GameData {
}
comboBurstAlpha = 0.8f;
if ((comboBurstIndex % 2) == 0) {
comboBurstX = displayContainer.width;
comboBurstX = width;
} else {
comboBurstX = comboBurstImages[0].getWidth() * -1;
}

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu;
import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.Fonts;
import java.io.File;
@ -31,10 +30,11 @@ 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.events.BubNotifListener;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.SlickUtil;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
/**
@ -90,7 +90,9 @@ public enum GameImage {
PLAYFIELD ("playfield", "png|jpg", false, false) {
@Override
protected Image process_sub(Image img, int w, int h) {
img.setAlpha(0.7f);
if (img.getWidth() == 1 && img.getHeight() == 1) {
img = MENU_BG.getImage().getFlippedCopy(/*h*/ false, /*v*/ true);
}
return img.getScaledCopy(w, h);
}
},
@ -230,14 +232,14 @@ public enum GameImage {
MOD_AUTOPILOT ("selection-mod-relax2", "png", false, false),
// Selection Buttons
SELECTION_MODE ("selection-mode", "png", false, false),
SELECTION_MODE_OVERLAY ("selection-mode-over", "png", false, false),
SELECTION_MODS ("selection-mods", "png", false, false),
SELECTION_MODS_OVERLAY ("selection-mods-over", "png", false, false),
SELECTION_RANDOM ("selection-random", "png", false, false),
SELECTION_RANDOM_OVERLAY ("selection-random-over", "png", false, false),
SELECTION_OPTIONS ("selection-options", "png", false, false),
SELECTION_OPTIONS_OVERLAY ("selection-options-over", "png", false, false),
SELECTION_OTHER_OPTIONS ("selection-selectoptions", "png", false, false),
SELECTION_OTHER_OPTIONS_OVERLAY ("selection-selectoptions-over", "png", false, false),
// Replay Speed Buttons
REPLAY_PLAYBACK_NORMAL ("playback-normal", "png", false, false),
@ -299,25 +301,37 @@ public enum GameImage {
MENU_LOGO ("logo2", "png", false, true) {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(0.8f);
return img.getScaledCopy(0.75f);
}
},
MENU_LOGO_PIECE ("logo2piece2", "png", false, true) {
MENU_LOGO_PIECE ("logo2piece", "png", false, true) {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(0.8f);
return img.getScaledCopy(0.75f);
}
},
MENU_PLAY ("menu-play2", "png", false, false) {
MENU_LOGO_PULSE ("logo2pulse", "png", false, true) {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(0.8f);
return img.getScaledCopy(0.75f);
}
},
MENU_EXIT ("menu-exit2", "png", false, false) {
MENU_PLAY ("menu-play", "png", false, false) {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(0.8f);
return img.getScaledCopy(0.75f);
}
},
MENU_EXIT ("menu-exit", "png", false, false) {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(0.75f);
}
},
MENU_OPTIONS ("menu-options", "png", false, false) {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(0.75f);
}
},
MENU_BUTTON_MID ("button-middle", "png", false, false),
@ -326,21 +340,25 @@ public enum GameImage {
STAR ("star", "png", false, false) {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy((MENU_BUTTON_BG.getImage().getHeight() * 0.16f) / img.getHeight());
return img.getScaledCopy((MENU_BUTTON_BG.getHeight() * 0.16f) / img.getHeight());
}
},
STAR2 ("star2", "png", false, false) {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy((MENU_BUTTON_BG.getImage().getHeight() * 0.33f) / img.getHeight());
return img.getScaledCopy((MENU_BUTTON_BG.getHeight() * 0.33f) / img.getHeight());
}
},
// Music Player Buttons
MUSIC_PLAY ("music-play", "png", false, false),
MUSIC_PAUSE ("music-pause", "png", false, false),
MUSIC_STOP ("music-stop", "png", false, false),
MUSIC_NEXT ("music-next", "png", false, false),
MUSIC_PREVIOUS ("music-previous", "png", false, false),
MUSIC_NOWPLAYING ("music-np", "png", false, false),
MUSIC_NOWPLAYING_BG_BLACK ("music-np-bg-black", "png", false, false),
MUSIC_NOWPLAYING_BG_WHITE ("music-np-bg-white", "png", false, false),
DOWNLOADS ("downloads", "png", false, false) {
@Override
@ -386,13 +404,6 @@ public enum GameImage {
return img.getScaledCopy((h / 14f) / img.getHeight());
}
},
OPTIONS_BG ("options-background", "png|jpg", false, true) {
@Override
protected Image process_sub(Image img, int w, int h) {
img.setAlpha(0.7f);
return img.getScaledCopy(w, h);
}
},
CHEVRON_DOWN ("chevron-down", "png", false, false),
CHEVRON_RIGHT ("chevron-right", "png", false, false),
@ -646,6 +657,30 @@ public enum GameImage {
return (skinImage != null) ? skinImage : defaultImage;
}
public int getHeight() {
return getImage().getHeight();
}
public int getWidth() {
return getImage().getWidth();
}
/**
* Returns the image associated with this resource, with a scale applied.
* The beatmap skin image takes priority over the default image.
*/
public Image getScaledImage(float scale) {
return this.getImage().getScaledCopy(scale);
}
/**
* Returns the image associated with this resource, with a scale applied.
* The beatmap skin image takes priority over the default image.
*/
public Image getScaledImage(int width, int height) {
return this.getImage().getScaledCopy(width, height);
}
/**
* Returns an Animation based on the image array.
* If no image array exists, returns the single image as an animation.
@ -743,7 +778,7 @@ public enum GameImage {
String err = String.format("Could not find default image '%s'.", filename);
Log.warn(err);
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
bubNotifs.send(BUB_RED, err);
}
/**
@ -806,8 +841,7 @@ public enum GameImage {
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);
bubNotifs.sendf(BUB_RED, "Failed to set image '%s'.", name);
break;
}
}
@ -834,8 +868,7 @@ public enum GameImage {
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);
bubNotifs.sendf(BUB_RED, "Failed to set image '%s'.", filename);
}
}
return null;

View File

@ -100,7 +100,7 @@ public enum GameMod {
*/
public void init(int width, int height) {
float multY = Fonts.LARGE.getLineHeight() * 2 + height * 0.06f;
float offsetY = GameImage.MOD_EASY.getImage().getHeight() * 1.5f;
float offsetY = GameImage.MOD_EASY.getHeight() * 1.5f;
this.x = width / 30f;
this.y = multY + Fonts.LARGE.getLineHeight() * 3f + offsetY * index;
}
@ -193,7 +193,7 @@ public enum GameMod {
// create buttons
float baseX = Category.EASY.getX() + Fonts.LARGE.getWidth(Category.EASY.getName()) * 1.25f;
float offsetX = GameImage.MOD_EASY.getImage().getWidth() * 2.1f;
float offsetX = GameImage.MOD_EASY.getWidth() * 2.1f;
for (GameMod mod : GameMod.values()) {
Image img = mod.image.getImage();
mod.button = new MenuButton(img,

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.utils.ManifestWrapper;
import java.io.File;
import java.io.IOException;
@ -50,42 +49,32 @@ public class NativeLoader {
* Unpacks natives for the current operating system to the natives directory.
* @throws IOException if an I/O exception occurs
*/
public static void loadNatives(JarFile jarfile, ManifestWrapper manifest) throws IOException {
public static void loadNatives(JarFile jarfile) 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);
}
String osName = System.getProperty("os.name");
String nativekey = null;
final String osName = System.getProperty("os.name");
final String[] files;
if (osName.startsWith("Win")) {
nativekey = "WinNatives";
files = new String[] { "OpenAL32.dll", "OpenAL64.dll", "lwjgl.dll", "lwjgl64.dll" };
} else if (osName.startsWith("Linux")) {
nativekey = "NixNatives";
files = new String[] { "liblwjgl.so", "liblwjgl64.so", "libopenal.so", "libopenal64.so" };
} else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
nativekey = "MacNatives";
}
if (nativekey == null) {
files = new String[] { "liblwjgl.dylib", "openal.dylib" };
} else {
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);
for (String file : files) {
File unpackedFile = new File(config.NATIVE_DIR, file);
if (unpackedFile.exists()) {
continue;
}
Utils.unpackFromJar(jarfile, unpackedFile, nativefile);
Utils.unpackFromJar(jarfile, unpackedFile, file);
}
}

View File

@ -101,7 +101,7 @@ public class ScoreData implements Comparable<ScoreData> {
baseX = containerWidth * 0.01f;
baseY = topY;
buttonWidth = containerWidth * 0.4f;
float gradeHeight = GameImage.MENU_BUTTON_BG.getImage().getHeight() * 0.45f;
float gradeHeight = GameImage.MENU_BUTTON_BG.getHeight() * 0.45f;
buttonHeight = Math.max(gradeHeight, Fonts.DEFAULT.getLineHeight() * 3.03f);
buttonOffset = buttonHeight + gradeHeight / 10f;
buttonAreaHeight = (SongMenu.MAX_SCORE_BUTTONS - 1) * buttonOffset + buttonHeight;

View File

@ -464,8 +464,8 @@ public class Utils {
*/
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;
if (y < height2) q = 2;
if (x < width2) q |= 1;
return q;
}
@ -483,24 +483,24 @@ public class Utils {
*/
public static float[] mirrorPoint(float x, float y) {
double dx = x - displayContainer.width / 2d;
double dy = y - displayContainer.height / 2d;
double dx = x - width2;
double dy = y - height2;
double ang = Math.atan2(dy, dx);
double d = -Math.sqrt(dx * dx + dy * dy);
return new float[]{
(float) (displayContainer.width / 2d + Math.cos(ang) * d),
(float) (displayContainer.height / 2d + Math.sin(ang) * d)
(float) (width2 + Math.cos(ang) * d),
(float) (height2 + Math.sin(ang) * d)
};
}
public static float[] mirrorPoint(float x, float y, float degrees) {
double dx = x - displayContainer.width / 2d;
double dy = y - displayContainer.height / 2d;
double dx = x - width2;
double dy = y - height2;
double ang = Math.atan2(dy, dx) + (degrees * Math.PI / 180d);
double d = Math.sqrt(dx * dx + dy * dy);
return new float[]{
(float) (displayContainer.width / 2d + Math.cos(ang) * d),
(float) (displayContainer.height / 2d + Math.sin(ang) * d)
(float) (width2 + Math.cos(ang) * d),
(float) (height2 + Math.sin(ang) * d)
};
}

View File

@ -22,6 +22,7 @@ import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.TimingPoint;
import itdelatrisu.opsu.states.Game;
import yugecin.opsudance.options.Options;
import java.io.File;
import java.io.IOException;
@ -33,7 +34,6 @@ 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;
@ -45,10 +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.events.BarNotifListener;
import yugecin.opsudance.events.BubNotifListener;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
/**
@ -103,8 +103,7 @@ public class MusicController {
if (lastBeatmap == null || !beatmap.audioFilename.equals(lastBeatmap.audioFilename)) {
final File audioFile = beatmap.audioFilename;
if (!audioFile.isFile() && !ResourceLoader.resourceExists(audioFile.getPath())) {
BarNotifListener.EVENT.make().onBarNotif(String.format("Could not find track '%s'.",
audioFile.getName()));
barNotifs.sendf("Could not find track '%s'.", audioFile.getName());
return;
}
@ -159,7 +158,7 @@ public class MusicController {
} catch (Exception e) {
String err = String.format("Could not play track '%s'.", file.getName());
Log.error(err, e);
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
bubNotifs.send(BUB_RED, err);
}
}
@ -219,6 +218,17 @@ public class MusicController {
return (float) ((((trackPosition - beatTime) * 100.0) % beatLength) / beatLength);
}
/**
* gets the current beat length
* @return
*/
public static Float getBeatLength() {
if (!updateTimingPoint())
return null;
return lastTimingPoint.getBeatLength();
}
/**
* Gets the progress of the current measure.
* @return a measure progress value [0,1) where 0 marks the start of the measure and
@ -349,10 +359,14 @@ public class MusicController {
* If no track is loaded, 0 will be returned.
*/
public static int getPosition() {
int offset = OPTION_MUSIC_OFFSET.val;
if (lastBeatmap != null)
offset += lastBeatmap.localMusicOffset;
if (isPlaying())
return (int) (player.getPosition() * 1000 + OPTION_MUSIC_OFFSET.val + Game.currentMapMusicOffset);
return (int) (player.getPosition() * 1000 + offset);
else if (isPaused())
return Math.max((int) (pauseTime * 1000 + OPTION_MUSIC_OFFSET.val + Game.currentMapMusicOffset), 0);
return Math.max((int) (pauseTime * 1000 + offset), 0);
else
return 0;
}

View File

@ -36,15 +36,14 @@ 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.events.BarNotifListener;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
/**
@ -220,8 +219,8 @@ public class SoundController {
// menu and game sounds
for (SoundEffect s : SoundEffect.values()) {
if ((currentFileName = getSoundFileName(s.getFileName())) == null) {
BubNotifListener.EVENT.make().onBubNotif(
"Could not find sound file " + s.getFileName(), Colors.BUB_ORANGE);
final String name = s.getFileName();
bubNotifs.send(BUB_ORANGE, "Could not find sound file " + name);
continue;
}
MultiClip newClip = loadClip(currentFileName, currentFileName.endsWith(".mp3"));
@ -240,8 +239,10 @@ public class SoundController {
for (HitSound s : HitSound.values()) {
String filename = String.format("%s-%s", ss.getName(), s.getFileName());
if ((currentFileName = getSoundFileName(filename)) == null) {
BubNotifListener.EVENT.make().onBubNotif(
"Could not find hit sound file " + filename, Colors.BUB_ORANGE);
bubNotifs.send(
BUB_ORANGE,
"Could not find hit sound file " + filename
);
continue;
}
MultiClip newClip = loadClip(currentFileName, false);
@ -398,8 +399,7 @@ public class SoundController {
@Override
public void error() {
BarNotifListener.EVENT.make().onBarNotif(
"Failed to download track preview");
barNotifs.send("Failed to download track preview");
}
});
try {

View File

@ -89,6 +89,9 @@ public class Beatmap implements Comparable<Beatmap> {
/** The last time this beatmap was played (timestamp). */
public long lastPlayed = 0;
/** The local music offset. */
public int localMusicOffset = 0;
/**
* [General]
*/

View File

@ -31,13 +31,12 @@ 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.Nullable;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.skinning.SkinService;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
@ -890,7 +889,7 @@ public class BeatmapParser {
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);
bubNotifs.send(BUB_RED, message);
}
}

View File

@ -21,8 +21,6 @@ package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.db.BeatmapDB;
import itdelatrisu.opsu.ui.Colors;
import yugecin.opsudance.events.BubNotifListener;
import java.io.File;
import java.io.IOException;
@ -36,6 +34,9 @@ import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Indexed, expanding, doubly-linked list data type for song groups.
*/
@ -215,7 +216,7 @@ public class BeatmapSetList {
try {
Utils.deleteToTrash(dir);
} catch (IOException e) {
BubNotifListener.EVENT.make().onBubNotif("Could not delete song group", Colors.BUB_ORANGE);
bubNotifs.send(BUB_ORANGE, "Could not delete song group");
}
if (ws != null)
ws.resume();
@ -271,7 +272,7 @@ public class BeatmapSetList {
try {
Utils.deleteToTrash(file);
} catch (IOException e) {
BubNotifListener.EVENT.make().onBubNotif("Could not delete song", Colors.BUB_ORANGE);
bubNotifs.send(BUB_ORANGE, "Could not delete song");
}
if (ws != null)
ws.resume();

View File

@ -38,11 +38,9 @@ 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.events.BarNotifListener;
import yugecin.opsudance.events.BubNotifListener;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
@ -101,7 +99,7 @@ public class BeatmapWatchService {
ws.register(config.beatmapDir.toPath());
} catch (IOException e) {
Log.error("Could not create watch service", e);
BubNotifListener.EVENT.make().onBubNotif("Could not create watch service", Colors.BUB_RED);
bubNotifs.send(BUB_RED, "Could not create watch service");
return;
}
@ -124,7 +122,7 @@ public class BeatmapWatchService {
} catch (IOException e) {
String msg = "An I/O exception occurred while closing the previous watch service.";
Log.error(msg, e);
BarNotifListener.EVENT.make().onBarNotif(msg);
barNotifs.send(msg);
ws = null;
}
}

View File

@ -240,7 +240,7 @@ public class HitObject {
String[] edgeHitSoundTokens = tokens[8].split("\\|");
this.edgeHitSound = new byte[edgeHitSoundTokens.length];
for (int j = 0; j < edgeHitSoundTokens.length; j++)
edgeHitSound[j] = Byte.parseByte(edgeHitSoundTokens[j]);
edgeHitSound[j] = (byte) Short.parseShort(edgeHitSoundTokens[j]);
}
if (tokens.length > 9) {
String[] edgeAdditionTokens = tokens[9].split("\\|");

View File

@ -23,12 +23,11 @@ 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.events.BubNotifListener;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
@ -97,7 +96,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);
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
bubNotifs.send(BUB_RED, err);
}
}

View File

@ -20,6 +20,7 @@ package itdelatrisu.opsu.db;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import java.io.File;
import java.sql.Connection;
@ -47,7 +48,7 @@ public class BeatmapDB {
* This value should be changed whenever the database format changes.
* Add any update queries to the {@link #getUpdateQueries(int)} method.
*/
private static final int DATABASE_VERSION = 20161222;
private static final int DATABASE_VERSION = 20161225;
/**
* Returns a list of SQL queries to apply, in order, to update from
@ -64,6 +65,10 @@ public class BeatmapDB {
list.add("ALTER TABLE beatmaps ADD COLUMN lastPlayed INTEGER");
list.add("UPDATE beatmaps SET dateAdded = 0, favorite = 0, playCount = 0, lastPlayed = 0");
}
if (version < 20161225) {
list.add("ALTER TABLE beatmaps ADD COLUMN localOffset INTEGER");
list.add("UPDATE beatmaps SET localOffset = 0");
}
/* add future updates here */
@ -85,7 +90,7 @@ public class BeatmapDB {
/** Query statements. */
private static PreparedStatement
insertStmt, selectStmt, deleteMapStmt, deleteGroupStmt,
setStarsStmt, updatePlayStatsStmt, setFavoriteStmt, updateSizeStmt;
setStarsStmt, updatePlayStatsStmt, setFavoriteStmt, setLocalOffsetStmt, updateSizeStmt;
/** Current size of beatmap cache table. */
private static int cacheSize = -1;
@ -121,7 +126,7 @@ public class BeatmapDB {
"INSERT INTO beatmaps VALUES (" +
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?," +
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?," +
"?, ?, ?, ?, ?, ?" +
"?, ?, ?, ?, ?, ?, ?" +
")"
);
selectStmt = connection.prepareStatement("SELECT * FROM beatmaps WHERE dir = ? AND file = ?");
@ -130,6 +135,7 @@ public class BeatmapDB {
setStarsStmt = connection.prepareStatement("UPDATE beatmaps SET stars = ? WHERE dir = ? AND file = ?");
updatePlayStatsStmt = connection.prepareStatement("UPDATE beatmaps SET playCount = ?, lastPlayed = ? WHERE dir = ? AND file = ?");
setFavoriteStmt = connection.prepareStatement("UPDATE beatmaps SET favorite = ? WHERE dir = ? AND file = ?");
setLocalOffsetStmt = connection.prepareStatement("UPDATE beatmaps SET localOffset = ? WHERE dir = ? AND file = ?");
} catch (SQLException e) {
explode("Failed to prepare beatmap statements.", e, DEFAULT_OPTIONS);
}
@ -153,7 +159,7 @@ public class BeatmapDB {
"mode INTEGER, letterboxInBreaks BOOLEAN, widescreenStoryboard BOOLEAN, epilepsyWarning BOOLEAN, " +
"bg TEXT, sliderBorder TEXT, timingPoints TEXT, breaks TEXT, combo TEXT, " +
"md5hash TEXT, stars REAL, " +
"dateAdded INTEGER, favorite BOOLEAN, playCount INTEGER, lastPlayed INTEGER" +
"dateAdded INTEGER, favorite BOOLEAN, playCount INTEGER, lastPlayed INTEGER, localOffset INTEGER" +
"); " +
"CREATE TABLE IF NOT EXISTS info (" +
"key TEXT NOT NULL UNIQUE, value TEXT" +
@ -402,6 +408,7 @@ public class BeatmapDB {
stmt.setBoolean(44, beatmap.favorite);
stmt.setInt(45, beatmap.playCount);
stmt.setLong(46, beatmap.lastPlayed);
stmt.setInt(47, beatmap.localMusicOffset);
} catch (SQLException e) {
throw e;
} catch (Exception e) {
@ -549,6 +556,7 @@ public class BeatmapDB {
beatmap.favorite = rs.getBoolean(44);
beatmap.playCount = rs.getInt(45);
beatmap.lastPlayed = rs.getLong(46);
beatmap.localMusicOffset = rs.getInt(47);
} catch (SQLException e) {
throw e;
} catch (Exception e) {
@ -694,6 +702,24 @@ public class BeatmapDB {
}
}
/**
* Updates the local music offset for a beatmap in the database.
* @param beatmap the beatmap
*/
public static void updateLocalOffset(Beatmap beatmap) {
if (connection == null)
return;
try {
setLocalOffsetStmt.setInt(1, beatmap.localMusicOffset);
setLocalOffsetStmt.setString(2, beatmap.getFile().getParentFile().getName());
setLocalOffsetStmt.setString(3, beatmap.getFile().getName());
setLocalOffsetStmt.executeUpdate();
} catch (SQLException e) {
ErrorHandler.explode(String.format("Failed to update local music offset for beatmap '%s' in database.",
beatmap.toString()), e, ErrorHandler.DEFAULT_OPTIONS);
}
}
/**
* Closes the connection to the database.
*/

View File

@ -33,10 +33,10 @@ 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.events.BubNotifListener;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
/**
@ -220,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) {
BubNotifListener.EVENT.make().onBubNotif(error, Colors.BUB_ORANGE);
bubNotifs.send(BUB_ORANGE, error);
throw new IOException();
}

View File

@ -33,9 +33,8 @@ import java.io.File;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.events.BubNotifListener;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
@ -132,7 +131,7 @@ public class DownloadNode {
* @param index the index (to offset the button from the topmost button)
*/
public static boolean resultIconContains(float cx, float cy, int index) {
int iconWidth = GameImage.MUSIC_PLAY.getImage().getWidth();
int iconWidth = GameImage.MUSIC_PLAY.getWidth();
float x = buttonBaseX + buttonWidth * 0.001f;
float y = buttonBaseY + (index * buttonOffset) + buttonHeight / 2f;
return ((cx > x && cx < x + iconWidth) &&
@ -187,7 +186,7 @@ public class DownloadNode {
* @param index the index (to offset the button from the topmost button)
*/
public static boolean downloadIconContains(float cx, float cy, int index) {
int iconWidth = GameImage.DELETE.getImage().getWidth();
int iconWidth = GameImage.DELETE.getWidth();
float edgeX = infoBaseX + infoWidth * 0.985f;
float y = infoBaseY + (index * infoHeight);
float marginY = infoHeight * 0.04f;
@ -280,14 +279,12 @@ public class DownloadNode {
download.setListener(new DownloadListener() {
@Override
public void completed() {
BarNotifListener.EVENT.make().onBarNotif(
String.format("Download complete: %s", getTitle()));
barNotifs.sendf("Download complete: %s", getTitle());
}
@Override
public void error() {
BarNotifListener.EVENT.make().onBarNotif(
"Download failed due to a connection error.");
barNotifs.send("Download failed due to a connection error.");
}
});
this.download = download;
@ -409,9 +406,10 @@ 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) {
BubNotifListener.EVENT.make().onBubNotif(
"Trying to draw download information for button without Download object",
Colors.BUB_ORANGE);
bubNotifs.send(
BUB_ORANGE,
"Trying to draw download information for button without Download object"
);
return;
}

View File

@ -32,11 +32,10 @@ import java.util.Date;
import java.util.Locale;
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.Constants;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.utils.SimpleVersion;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
@ -92,7 +91,7 @@ public class Updater {
private Status status;
/** The current and latest versions. */
private DefaultArtifactVersion currentVersion, latestVersion;
private SimpleVersion currentVersion, latestVersion;
/** The version information if the program was just updated. */
private String updatedFromVersion, updatedToVersion;
@ -107,7 +106,7 @@ public class Updater {
if (currentVersion == null) {
return "unknown version";
}
return currentVersion.getMajorVersion() + "." + currentVersion.getMinorVersion() + "." + currentVersion.getIncrementalVersion();
return currentVersion.toString();
}
public Updater() {
@ -182,13 +181,13 @@ public class Updater {
* @param props the set of properties
* @return the version, or null if not found
*/
private DefaultArtifactVersion getVersion(Properties props) {
String version = props.getProperty("version");
if (version == null || version.equals("${pom.version}")) {
status = Status.INTERNAL_ERROR;
private SimpleVersion getVersion(Properties props) {
final String version = props.getProperty("version");
if (version != null && !version.equals("${pom.version}")) {
return SimpleVersion.parse(version);
}
this.status = Status.INTERNAL_ERROR;
return null;
} else
return new DefaultArtifactVersion(version);
}
/**
@ -196,7 +195,7 @@ public class Updater {
* @throws IOException if an I/O exception occurs
*/
public void checkForUpdates() throws IOException {
if (status != Status.INITIAL || config.USE_XDG)
if (status != Status.INITIAL)
return;
status = Status.CHECKING;
@ -241,14 +240,13 @@ public class Updater {
@Override
public void completed() {
status = Status.UPDATE_DOWNLOADED;
BarNotifListener.EVENT.make().onBarNotif("Update has finished downloading");
barNotifs.send("Update has finished downloading");
}
@Override
public void error() {
status = Status.CONNECTION_ERROR;
BarNotifListener.EVENT.make().onBarNotif(
"Update failed due to a connection error.");
barNotifs.send("Update failed due to a connection error.");
}
});
}

View File

@ -370,13 +370,13 @@ public class Slider extends GameObject {
float followCircleScale = 1f + (tickExpandTime / (float) TICK_EXPAND_TIME) * 0.1f;
float followAlpha = 1f;
if (followCircleActive && followExpandTime < FOLLOW_EXPAND_TIME) {
followExpandTime += displayContainer.renderDelta;
followExpandTime += renderDelta;
followCircleScale *= 0.5f;
float progress = AnimationEquation.OUT_QUAD.calc((float) followExpandTime / FOLLOW_EXPAND_TIME);
followCircleScale = followCircleScale + followCircleScale * progress;
followAlpha = progress;
} else if (!followCircleActive) {
followExpandTime -= displayContainer.renderDelta;
followExpandTime -= renderDelta;
if (followExpandTime > FOLLOW_SHRINK_TIME) {
followExpandTime = FOLLOW_SHRINK_TIME;
}
@ -391,7 +391,7 @@ public class Slider extends GameObject {
float oldAlphaBlack = Colors.BLACK_ALPHA.a;
Colors.BLACK_ALPHA.a = 0.75f;
g.setColor(Colors.BLACK_ALPHA);
g.fillRect(0, 0, displayContainer.width, displayContainer.height);
g.fillRect(0, 0, width, height);
Colors.BLACK_ALPHA.a = oldAlphaBlack;
}
}

View File

@ -34,15 +34,14 @@ import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Data type representing a spinner object.
*/
public class Spinner extends GameObject {
/** Container dimensions. */
private static int width, height;
/** The map's overall difficulty value. */
private static float overallDifficulty = 5f;
@ -112,9 +111,7 @@ public class Spinner extends GameObject {
* Initializes the Spinner data type with images and dimensions.
* @param difficulty the map's overall difficulty value
*/
public static void init(DisplayContainer displayContainer, float difficulty) {
width = displayContainer.width;
height = displayContainer.height;
public static void init(float difficulty) {
overallDifficulty = difficulty;
}
@ -207,7 +204,7 @@ public class Spinner extends GameObject {
// rpm
Image rpmImg = GameImage.SPINNER_RPM.getImage();
rpmImg.setAlpha(alpha);
rpmImg.drawCentered(width / 2f, height - rpmImg.getHeight() / 2f);
rpmImg.drawCentered(width2, height - rpmImg.getHeight() / 2f);
if (timeDiff < 0)
data.drawSymbolString(Integer.toString(drawnRPM), (width + rpmImg.getWidth() * 0.95f) / 2f,
height - data.getScoreSymbolImage('0').getHeight() * 1.025f, 1f, 1f, true);
@ -225,21 +222,21 @@ public class Spinner extends GameObject {
// main spinner elements
GameImage.SPINNER_CIRCLE.getImage().setAlpha(alpha);
GameImage.SPINNER_CIRCLE.getImage().setRotation(drawRotation * 360f);
GameImage.SPINNER_CIRCLE.getImage().drawCentered(width / 2, height / 2);
GameImage.SPINNER_CIRCLE.getImage().drawCentered(width2, height2);
if (!GameMod.HIDDEN.isActive()) {
float approachScale = 1 - Utils.clamp(((float) timeDiff / (hitObject.getTime() - hitObject.getEndTime())), 0f, 1f);
Image approachCircleScaled = GameImage.SPINNER_APPROACHCIRCLE.getImage().getScaledCopy(approachScale);
approachCircleScaled.setAlpha(alpha);
approachCircleScaled.drawCentered(width / 2, height / 2);
approachCircleScaled.drawCentered(width2, height2);
}
GameImage.SPINNER_SPIN.getImage().setAlpha(alpha);
GameImage.SPINNER_SPIN.getImage().drawCentered(width / 2, height * 3 / 4);
GameImage.SPINNER_SPIN.getImage().drawCentered(width2, height * 3 / 4);
if (spinnerComplete) {
GameImage.SPINNER_CLEAR.getImage().drawCentered(width / 2, height / 4);
GameImage.SPINNER_CLEAR.getImage().drawCentered(width2, height / 4);
int extraRotations = (int) (rotations - rotationsNeeded);
if (extraRotations > 0)
data.drawSymbolNumber(extraRotations * 1000, width / 2, height * 2 / 3, 1f, 1f);
data.drawSymbolNumber(extraRotations * 1000, width2, height * 2 / 3, 1f, 1f);
}
}
@ -261,14 +258,14 @@ public class Spinner extends GameObject {
else
result = GameData.HIT_MISS;
data.sendHitResult(hitObject.getEndTime(), result, width / 2, height / 2,
data.sendHitResult(hitObject.getEndTime(), result, width2, height2,
Color.transparent, true, hitObject, HitObjectType.SPINNER, true, 0, null, false);
return result;
}
@Override
public boolean mousePressed(int x, int y, int trackPosition) {
lastAngle = (float) Math.atan2(x - (height / 2), y - (width / 2));
lastAngle = (float) Math.atan2(x - height2, y - width2);
return false;
}
@ -297,7 +294,7 @@ public class Spinner extends GameObject {
angleDiff = delta * SPUN_OUT_MULTIPLIER;
isSpinning = true;
} else {
float angle = (float) Math.atan2(mouseY - (height / 2), mouseX - (width / 2));
float angle = (float) Math.atan2(mouseY - height2, mouseX - width2);
// set initial angle to current mouse position to skip first click
if (!isSpinning && (keyPressed || GameMod.RELAX.isActive())) {

View File

@ -98,13 +98,13 @@ public abstract class Curve {
* @param circleDiameter the circle diameter
* @param borderColor the curve border color
*/
public static void init(int width, int height, float circleDiameter, Color borderColor) {
public static void init(float circleDiameter, Color borderColor) {
Curve.borderColor = borderColor;
ContextCapabilities capabilities = GLContext.getCapabilities();
mmsliderSupported = capabilities.OpenGL30;
if (mmsliderSupported) {
CurveRenderState.init(width, height, circleDiameter);
CurveRenderState.init(circleDiameter);
} else if (SkinService.skin.getSliderStyle() != Skin.STYLE_PEPPYSLIDER) {
Log.warn("New slider style requires OpenGL 3.0.");
}

View File

@ -48,8 +48,6 @@ import static yugecin.opsudance.options.Options.*;
* @author Bigpet {@literal <dravorek (at) gmail.com>}
*/
public class CurveRenderState {
/** The width and height of the display container this curve gets drawn into. */
protected static int containerWidth, containerHeight;
/** Thickness of the curve. */
protected static int scale;
@ -79,14 +77,9 @@ public class CurveRenderState {
/**
* Set the width and height of the container that Curves get drawn into.
* Should be called before any curves are drawn.
* @param width the container width
* @param height the container height
* @param circleDiameter the circle diameter
*/
public static void init(int width, int height, float circleDiameter) {
containerWidth = width;
containerHeight = height;
public static void init(float circleDiameter) {
// equivalent to what happens in Slider.init()
scale = (int) (circleDiameter * HitObject.getXMultiplier()); // convert from Osupixels (640x480)
//scale = scale * 118 / 128; //for curves exactly as big as the sliderball
@ -410,8 +403,6 @@ public class CurveRenderState {
x1 = m[0];
y1 = m[1];
}
float divx = containerWidth / 2.0f;
float divy = containerHeight / 2.0f;
float offx = -1.0f;
float offy = 1.0f;
float radius = scale / 2;
@ -419,8 +410,8 @@ public class CurveRenderState {
for (int i = 0; i < NewCurveStyleState.unitCone.length / 6; ++i) {
buff.put(NewCurveStyleState.unitCone[i * 6 + 0]);
buff.put(NewCurveStyleState.unitCone[i * 6 + 1]);
buff.put(offx + (x1 + radius * NewCurveStyleState.unitCone[i * 6 + 2]) / divx);
buff.put(offy - (y1 + radius * NewCurveStyleState.unitCone[i * 6 + 3]) / divy);
buff.put(offx + (x1 + radius * NewCurveStyleState.unitCone[i * 6 + 2]) / width2);
buff.put(offy - (y1 + radius * NewCurveStyleState.unitCone[i * 6 + 3]) / height2);
buff.put(NewCurveStyleState.unitCone[i * 6 + 4]);
buff.put(NewCurveStyleState.unitCone[i * 6 + 5]);
}

View File

@ -39,13 +39,12 @@ 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.events.BubNotifListener;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.InstanceContainer.*;
@ -275,7 +274,7 @@ public class Replay {
public void save() {
// create replay directory
if (!config.replayDir.isDirectory() && !config.replayDir.mkdir()) {
BubNotifListener.EVENT.make().onBubNotif("Failed to create replay directory", Colors.BUB_RED);
bubNotifs.send(BUB_RED, "Failed to create replay directory");
return;
}

View File

@ -28,10 +28,9 @@ 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.events.BubNotifListener;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
@ -69,7 +68,7 @@ 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);
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
bubNotifs.send(BUB_RED, err);
return;
}
@ -83,7 +82,7 @@ public class ReplayImporter {
moveToFailedDirectory(file);
String err = String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName());
Log.error(err, e);
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
bubNotifs.send(BUB_RED, err);
continue;
}
Beatmap beatmap = BeatmapSetList.get().getBeatmapFromHash(r.beatmapHash);
@ -102,7 +101,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);
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
bubNotifs.send(BUB_RED, err);
}
}

View File

@ -29,10 +29,11 @@ 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.events.BubNotifListener;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Loads skin configuration files.
@ -293,7 +294,7 @@ public class SkinLoader {
} catch (IOException e) {
String err = String.format("Failed to read file '%s'.", skinFile.getAbsolutePath());
Log.error(err, e);
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
bubNotifs.send(BUB_RED, err);
}
return skin;

View File

@ -181,7 +181,7 @@ public class ButtonMenu extends BaseOpsuState {
@Override
protected float getBaseY() {
return displayContainer.height * 2f / 3;
return height * 2f / 3;
}
@Override
@ -203,10 +203,9 @@ public class ButtonMenu extends BaseOpsuState {
float mult = GameMod.getScoreMultiplier();
String multString = String.format("Score Multiplier: %.2fx", mult);
Color multColor = (mult == 1f) ? Color.white : (mult > 1f) ? Color.green : Color.red;
float multY = Fonts.LARGE.getLineHeight() * 2 + displayContainer.height * 0.06f;
Fonts.LARGE.drawString(
(displayContainer.width - Fonts.LARGE.getWidth(multString)) / 2f,
multY, multString, multColor);
float multY = Fonts.LARGE.getLineHeight() * 2 + height * 0.06f;
final float multX = width2 - Fonts.LARGE.getWidth(multString) / 2f;
Fonts.LARGE.drawString(multX, multY, multString, multColor);
// category text
for (GameMod.Category category : GameMod.Category.values()) {
@ -227,14 +226,14 @@ public class ButtonMenu extends BaseOpsuState {
super.preRenderUpdate();
GameMod hoverMod = null;
for (GameMod mod : GameMod.values()) {
mod.hoverUpdate(displayContainer.renderDelta, mod.isActive());
if (hoverMod == null && mod.contains(displayContainer.mouseX, displayContainer.mouseY))
mod.hoverUpdate(renderDelta, mod.isActive());
if (hoverMod == null && mod.contains(mouseX, mouseY))
hoverMod = mod;
}
// tooltips
if (hoverMod != null) {
UI.updateTooltip(displayContainer.renderDelta, hoverMod.getDescription(), true);
UI.updateTooltip(renderDelta, hoverMod.getDescription(), true);
}
}
@ -296,13 +295,12 @@ public class ButtonMenu extends BaseOpsuState {
* Initializes the menu state.
*/
public void revalidate(Image button, Image buttonL, Image buttonR) {
float center = displayContainer.width / 2;
float baseY = getBaseY();
float offsetY = button.getHeight() * 1.25f;
menuButtons = new MenuButton[buttons.length];
for (int i = 0; i < buttons.length; i++) {
MenuButton b = new MenuButton(button, buttonL, buttonR, center, baseY + (i * offsetY));
MenuButton b = new MenuButton(button, buttonL, buttonR, width2, baseY + (i * offsetY));
b.setText(String.format("%d. %s", i + 1, buttons[i].getText()), Fonts.XLARGE, Color.white);
b.setHoverFade();
menuButtons[i] = b;
@ -313,7 +311,7 @@ public class ButtonMenu extends BaseOpsuState {
* Returns the base Y coordinate for the buttons.
*/
protected float getBaseY() {
float baseY = displayContainer.height * 0.2f;
float baseY = height * 0.2f;
baseY += ((getTitle().length - 1) * Fonts.LARGE.getLineHeight());
return baseY;
}
@ -325,7 +323,7 @@ public class ButtonMenu extends BaseOpsuState {
public void render(Graphics g) {
// draw title
if (actualTitle != null) {
float marginX = displayContainer.width * 0.015f, marginY = displayContainer.height * 0.01f;
float marginX = width * 0.015f, marginY = height * 0.01f;
int lineHeight = Fonts.LARGE.getLineHeight();
for (int i = 0, size = actualTitle.size(); i < size; i++)
Fonts.LARGE.drawString(marginX, marginY + (i * lineHeight), actualTitle.get(i), Color.white);
@ -342,15 +340,16 @@ public class ButtonMenu extends BaseOpsuState {
* Updates the menu state.
*/
public void preRenderUpdate() {
float center = displayContainer.width / 2f;
boolean centerOffsetUpdated = centerOffset.update(displayContainer.renderDelta);
boolean centerOffsetUpdated = centerOffset.update(renderDelta);
float centerOffsetX = centerOffset.getValue();
final float[] offsets = { centerOffsetX, - centerOffsetX };
for (int i = 0; i < buttons.length; i++) {
menuButtons[i].hoverUpdate(displayContainer.renderDelta, displayContainer.mouseX, displayContainer.mouseY);
menuButtons[i].hoverUpdate(renderDelta, mouseX, mouseY);
// move button to center
if (centerOffsetUpdated)
menuButtons[i].setX((i % 2 == 0) ? center + centerOffsetX : center - centerOffsetX);
if (centerOffsetUpdated) {
menuButtons[i].setX(width2 + offsets[i & 1]);
}
}
}
@ -394,18 +393,17 @@ public class ButtonMenu extends BaseOpsuState {
* Processes a state enter request.
*/
public void enter() {
float center = displayContainer.width / 2f;
float centerOffsetX = displayContainer.width * OFFSET_WIDTH_RATIO;
float centerOffsetX = width * OFFSET_WIDTH_RATIO;
centerOffset = new AnimatedValue(700, centerOffsetX, 0, AnimationEquation.OUT_BOUNCE);
for (int i = 0; i < buttons.length; i++) {
menuButtons[i].setX(center + ((i % 2 == 0) ? centerOffsetX : centerOffsetX * -1));
menuButtons[i].setX(width2 + ((i % 2 == 0) ? centerOffsetX : centerOffsetX * -1));
menuButtons[i].resetHover();
}
// create title string list
actualTitle = new ArrayList<>();
String[] title = getTitle();
int maxLineWidth = (int) (displayContainer.width * 0.96f);
int maxLineWidth = (int) (width * 0.96f);
for (String aTitle : title) {
// wrap text if too long
if (Fonts.LARGE.getWidth(aTitle) > maxLineWidth) {
@ -601,7 +599,7 @@ public class ButtonMenu extends BaseOpsuState {
// initialize buttons
Image button = GameImage.MENU_BUTTON_MID.getImage();
button = button.getScaledCopy(displayContainer.width / 2, button.getHeight());
button = button.getScaledCopy(width2, button.getHeight());
Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage();
Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage();
for (MenuState ms : MenuState.values()) {
@ -624,7 +622,7 @@ public class ButtonMenu extends BaseOpsuState {
public void preRenderUpdate() {
super.preRenderUpdate();
UI.update(displayContainer.renderDelta);
UI.update(renderDelta);
MusicController.loopTrackIfEnded(false);
menuState.preRenderUpdate();
}

View File

@ -53,7 +53,6 @@ import org.newdawn.slick.SlickException;
import org.newdawn.slick.gui.TextField;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.state.ComplexOpsuState;
import yugecin.opsudance.events.BarNotifListener;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
@ -150,9 +149,6 @@ public class DownloadsMenu extends ComplexOpsuState {
/** Beatmap set ID of the current beatmap being previewed, or -1 if none. */
private int previewID = -1;
/** The bar notification to send upon entering the state. */
private String barNotificationOnLoad;
/** Search query, executed in {@code queryThread}. */
private SearchQuery searchQuery;
@ -273,13 +269,12 @@ public class DownloadsMenu extends ComplexOpsuState {
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);
barNotifs.send("Imported 1 new song.");
return;
}
BarNotifListener.EVENT.make().onBarNotif(msg);
barNotifs.sendf("Imported %d new songs.", dirs.length);
}
}
@ -298,8 +293,6 @@ public class DownloadsMenu extends ComplexOpsuState {
components.clear();
int width = displayContainer.width;
int height = displayContainer.height;
int baseX = (int) (width * 0.024f);
int searchY = (int) (height * 0.04f + Fonts.LARGE.getLineHeight());
int searchWidth = (int) (width * 0.3f);
@ -371,7 +364,7 @@ public class DownloadsMenu extends ComplexOpsuState {
// dropdown menu
int serverWidth = (int) (width * 0.12f);
int x = baseX + searchWidth + buttonMarginX * 3 + resetButtonWidth + rankedButtonWidth;
serverMenu = new DropdownMenu<DownloadServer>(displayContainer, SERVERS, x, searchY, serverWidth) {
serverMenu = new DropdownMenu<DownloadServer>(SERVERS, x, searchY, serverWidth) {
@Override
public void itemSelected(int index, DownloadServer item) {
resultList = null;
@ -414,7 +407,7 @@ public class DownloadsMenu extends ComplexOpsuState {
GameImage.SEARCH_BG.getImage().draw();
// title
Fonts.LARGE.drawString(displayContainer.width * 0.024f, displayContainer.height * 0.03f, "Download Beatmaps!", Color.white);
Fonts.LARGE.drawString(width * 0.024f, height * 0.03f, "Download Beatmaps!", Color.white);
// search
g.setColor(Color.white);
@ -440,7 +433,7 @@ public class DownloadsMenu extends ComplexOpsuState {
if (index >= nodes.length)
break;
nodes[index].drawResult(g, offset + i * DownloadNode.getButtonOffset(),
DownloadNode.resultContains(displayContainer.mouseX, displayContainer.mouseY - offset, i) && !serverMenu.isHovered(),
DownloadNode.resultContains(mouseX, mouseY - offset, i) && !serverMenu.isHovered(),
(index == focusResult), (previewID == nodes[index].getID()));
}
g.clearClip();
@ -451,9 +444,9 @@ public class DownloadsMenu extends ComplexOpsuState {
// pages
if (nodes.length > 0) {
float baseX = displayContainer.width * 0.024f;
float buttonY = displayContainer.height * 0.2f;
float buttonWidth = displayContainer.width * 0.7f;
float baseX = width * 0.024f;
float buttonY = height * 0.2f;
float buttonWidth = width * 0.7f;
Fonts.BOLD.drawString(
baseX + (buttonWidth - Fonts.BOLD.getWidth("Page 1")) / 2f,
buttonY - Fonts.BOLD.getLineHeight() * 1.3f,
@ -467,11 +460,10 @@ public class DownloadsMenu extends ComplexOpsuState {
}
// downloads
float downloadsX = displayContainer.width * 0.75f, downloadsY = search.y;
float downloadsX = width * 0.75f, downloadsY = search.y;
g.setColor(Colors.BLACK_BG_NORMAL);
g.fillRect(downloadsX, downloadsY,
displayContainer.width * 0.25f, displayContainer.height - downloadsY * 2f);
Fonts.LARGE.drawString(downloadsX + displayContainer.width * 0.015f, downloadsY + displayContainer.height * 0.015f, "Downloads", Color.white);
g.fillRect(downloadsX, downloadsY, width * 0.25f, height - downloadsY * 2f);
Fonts.LARGE.drawString(downloadsX + width * 0.015f, downloadsY + height * 0.015f, "Downloads", Color.white);
int downloadsSize = DownloadList.get().size();
if (downloadsSize > 0) {
int maxDownloadsShown = DownloadNode.maxDownloadsShown();
@ -487,7 +479,7 @@ public class DownloadsMenu extends ComplexOpsuState {
if (node == null)
break;
node.drawDownload(g, i * DownloadNode.getInfoHeight() + offset, index,
DownloadNode.downloadContains(displayContainer.mouseX, displayContainer.mouseY - offset, i));
DownloadNode.downloadContains(mouseX, mouseY - offset, i));
}
g.clearClip();
@ -510,15 +502,13 @@ public class DownloadsMenu extends ComplexOpsuState {
if (importThread != null) {
// darken the screen
g.setColor(Colors.BLACK_ALPHA);
g.fillRect(0, 0, displayContainer.width, displayContainer.height);
g.fillRect(0, 0, width, height);
UI.drawLoadingProgress(g);
} else {
backButton.draw(g);
}
// back button
else
UI.getBackButton().draw(g);
UI.draw(g);
}
@ -526,7 +516,7 @@ public class DownloadsMenu extends ComplexOpsuState {
public void preRenderUpdate() {
super.preRenderUpdate();
int delta = displayContainer.renderDelta;
int delta = renderDelta;
UI.update(delta);
if (importThread == null)
MusicController.loopTrackIfEnded(false);
@ -547,9 +537,7 @@ public class DownloadsMenu extends ComplexOpsuState {
}
importThread = null;
}
int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
backButton.hoverUpdate();
prevPage.hoverUpdate(delta, mouseX, mouseY);
nextPage.hoverUpdate(delta, mouseX, mouseY);
clearButton.hoverUpdate(delta, mouseX, mouseY);
@ -619,7 +607,7 @@ public class DownloadsMenu extends ComplexOpsuState {
}
// back
if (UI.getBackButton().contains(x, y)) {
if (backButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK);
displayContainer.switchState(mainmenuState);
return true;
@ -686,7 +674,7 @@ public class DownloadsMenu extends ComplexOpsuState {
if (playing)
previewID = node.getID();
} catch (SlickException e) {
BarNotifListener.EVENT.make().onBarNotif("Failed to load track preview. See log for details.");
barNotifs.send("Failed to load track preview. See log for details.");
Log.error(e);
}
}
@ -709,7 +697,7 @@ public class DownloadsMenu extends ComplexOpsuState {
if (!DownloadList.get().contains(node.getID())) {
node.createDownload(serverMenu.getSelectedItem());
if (node.getDownload() == null) {
BarNotifListener.EVENT.make().onBarNotif("The download could not be started");
barNotifs.send("The download could not be started");
} else {
DownloadList.get().addNode(node);
node.getDownload().start();
@ -856,7 +844,7 @@ public class DownloadsMenu extends ComplexOpsuState {
}
int shift = (newValue < 0) ? 1 : -1;
scrollLists(displayContainer.mouseX, displayContainer.mouseY, shift);
scrollLists(mouseX, mouseY, shift);
return true;
}
@ -950,10 +938,6 @@ public class DownloadsMenu extends ComplexOpsuState {
startDownloadIndexPos.setPosition(0);
pageDir = Page.RESET;
previewID = -1;
if (barNotificationOnLoad != null) {
BarNotifListener.EVENT.make().onBarNotif(barNotificationOnLoad);
barNotificationOnLoad = null;
}
}
@Override
@ -994,10 +978,4 @@ public class DownloadsMenu extends ComplexOpsuState {
else if (DownloadNode.downloadAreaContains(cx, cy))
startDownloadIndexPos.scrollOffset(shift * DownloadNode.getInfoHeight());
}
/**
* Sends a bar notification upon entering the state.
* @param s the notification string
*/
public void notifyOnLoad(String s) { barNotificationOnLoad = s; }
}

View File

@ -24,6 +24,7 @@ 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,9 +61,6 @@ import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.*;
import yugecin.opsudance.core.state.ComplexOpsuState;
import yugecin.opsudance.core.state.specialstates.BubNotifState;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.objects.curves.FakeCombinedCurve;
import yugecin.opsudance.options.OptionGroups;
import yugecin.opsudance.sbv2.MoveStoryboard;
@ -71,6 +69,8 @@ import yugecin.opsudance.ui.OptionsOverlay;
import yugecin.opsudance.ui.StoryboardOverlay;
import yugecin.opsudance.utils.GLHelper;
import static itdelatrisu.opsu.GameImage.*;
import static itdelatrisu.opsu.ui.Colors.*;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
@ -268,8 +268,6 @@ public class Game extends ComplexOpsuState {
/** Music position bar coordinates and dimensions (for replay seeking). */
private float musicBarX, musicBarY, musicBarWidth, musicBarHeight;
public static int currentMapMusicOffset;
private int mirrorFrom;
private int mirrorTo;
@ -320,9 +318,9 @@ public class Game extends ComplexOpsuState {
public Game() {
super();
mirrorCursor = new Cursor(true);
this.moveStoryboardOverlay = new MoveStoryboard(displayContainer);
this.optionsOverlay = new OptionsOverlay(displayContainer, OptionGroups.storyboardOptions);
this.storyboardOverlay = new StoryboardOverlay(displayContainer, moveStoryboardOverlay, optionsOverlay, this);
this.moveStoryboardOverlay = new MoveStoryboard();
this.optionsOverlay = new OptionsOverlay(OptionGroups.storyboardOptions);
this.storyboardOverlay = new StoryboardOverlay(moveStoryboardOverlay, optionsOverlay, this);
storyboardOverlay.show();
moveStoryboardOverlay.show();
optionsOverlay.setListener(storyboardOverlay);
@ -334,25 +332,26 @@ public class Game extends ComplexOpsuState {
// create offscreen graphics
try {
offscreen = new Image(displayContainer.width, displayContainer.height);
offscreen = new Image(width, height);
gOffscreen = offscreen.getGraphics();
gOffscreen.setBackground(Color.black);
} catch (SlickException e) {
Log.error("could not create offscreen graphics", e);
BubNotifListener.EVENT.make().onBubNotif(
"Exception while creating offscreen graphics. See logfile for details.",
Colors.BUB_RED);
bubNotifs.send(
BUB_RED,
"Exception while creating offscreen graphics. See logfile for details."
);
}
// initialize music position bar location
musicBarX = displayContainer.width * 0.01f;
musicBarY = displayContainer.height * 0.05f;
musicBarWidth = Math.max(displayContainer.width * 0.005f, 7);
musicBarHeight = displayContainer.height * 0.9f;
musicBarX = width * 0.01f;
musicBarY = height * 0.05f;
musicBarWidth = Math.max(width * 0.005f, 7);
musicBarHeight = height * 0.9f;
// initialize scoreboard star stream
scoreboardStarStream = new StarStream(0, displayContainer.height * 2f / 3f, displayContainer.width / 4, 0, 0);
scoreboardStarStream.setPositionSpread(displayContainer.height / 20f);
scoreboardStarStream = new StarStream(0, height * 2f / 3f, width / 4, 0, 0);
scoreboardStarStream.setPositionSpread(height / 20f);
scoreboardStarStream.setDirectionSpread(10f);
scoreboardStarStream.setDurationSpread(700, 100);
@ -386,12 +385,9 @@ public class Game extends ComplexOpsuState {
@Override
public void render(Graphics g) {
int width = displayContainer.width;
int height = displayContainer.height;
int trackPosition = MusicController.getPosition();
if (isLeadIn()) {
trackPosition -= leadInTime - currentMapMusicOffset - OPTION_MUSIC_OFFSET.val;
trackPosition -= leadInTime - OPTION_MUSIC_OFFSET.val - beatmap.localMusicOffset;
}
if (pauseTime > -1) // returning from pause screen
trackPosition = pauseTime;
@ -444,7 +440,7 @@ public class Game extends ComplexOpsuState {
if (GameMod.FLASHLIGHT.isActive()) {
// render hit objects offscreen
Graphics.setCurrent(gOffscreen);
int trackPos = (isLeadIn()) ? (leadInTime - OPTION_MUSIC_OFFSET.val) * -1 : trackPosition;
int trackPos = (isLeadIn()) ? (leadInTime - OPTION_MUSIC_OFFSET.val - beatmap.localMusicOffset) * -1 : trackPosition;
drawHitObjects(gOffscreen, trackPos);
// restore original graphics context
@ -454,23 +450,23 @@ public class Game extends ComplexOpsuState {
// draw alpha map around cursor
g.setDrawMode(Graphics.MODE_ALPHA_MAP);
g.clearAlphaMap();
int mouseX, mouseY;
int mx, my;
if (pauseTime > -1 && pausedMousePosition != null) {
mouseX = (int) pausedMousePosition.x;
mouseY = (int) pausedMousePosition.y;
mx = (int) pausedMousePosition.x;
my = (int) pausedMousePosition.y;
} else if (GameMod.AUTO.isActive() || GameMod.AUTOPILOT.isActive()) {
mouseX = (int) autoMousePosition.x;
mouseY = (int) autoMousePosition.y;
mx = (int) autoMousePosition.x;
my = (int) autoMousePosition.y;
} else if (isReplay) {
mouseX = replayX;
mouseY = replayY;
mx = replayX;
my = replayY;
} else {
mouseX = displayContainer.mouseX;
mouseY = displayContainer.mouseY;
mx = mouseX;
my = mouseY;
}
int alphaRadius = flashlightRadius * 256 / 215;
int alphaX = mouseX - alphaRadius / 2;
int alphaY = mouseY - alphaRadius / 2;
int alphaX = mx - alphaRadius / 2;
int alphaY = my - alphaRadius / 2;
GameImage.ALPHA_MAP.getImage().draw(alphaX, alphaY, alphaRadius, alphaRadius);
// blend offscreen image
@ -557,8 +553,8 @@ public class Game extends ComplexOpsuState {
// show retries
if (retries >= 2 && timeDiff >= -1000) {
int retryHeight = Math.max(
GameImage.SCOREBAR_BG.getImage().getHeight(),
GameImage.SCOREBAR_KI.getImage().getHeight()
GameImage.SCOREBAR_BG.getHeight(),
GameImage.SCOREBAR_KI.getHeight()
);
float oldAlpha = Colors.WHITE_FADE.a;
if (timeDiff < -500)
@ -571,8 +567,8 @@ public class Game extends ComplexOpsuState {
Colors.WHITE_FADE.a = oldAlpha;
}
if (isLeadIn())
trackPosition = (leadInTime - OPTION_MUSIC_OFFSET.val) * -1; // render approach circles during song lead-in
if (isLeadIn()) // render approach circles during song lead-in
trackPosition = (leadInTime - OPTION_MUSIC_OFFSET.val - beatmap.localMusicOffset) * -1;
// countdown
if (beatmap.countdown > 0) {
@ -594,7 +590,7 @@ public class Game extends ComplexOpsuState {
}
}
if (timeDiff < 1500 * speedModifier) {
GameImage.COUNTDOWN_2.getImage().draw(width - GameImage.COUNTDOWN_2.getImage().getWidth(), 0);
COUNTDOWN_2.getImage().draw(width - COUNTDOWN_2.getWidth(), 0);
if (!countdown2Sound) {
SoundController.playSound(SoundEffect.COUNT2);
countdown2Sound = true;
@ -641,7 +637,7 @@ public class Game extends ComplexOpsuState {
float animation = AnimationEquation.IN_OUT_QUAD.calc(
Utils.clamp((trackPosition - lastRankUpdateTime) / SCOREBOARD_ANIMATION_TIME, 0f, 1f)
);
int scoreboardPosition = 2 * displayContainer.height / 3;
int scoreboardPosition = 2 * height / 3;
// draw star stream behind the scores
scoreboardStarStream.draw();
@ -682,7 +678,7 @@ public class Game extends ComplexOpsuState {
// draw music position bar (for replay seeking)
if (isReplay && OPTION_REPLAY_SEEKING.state) {
g.setColor((musicPositionBarContains(displayContainer.mouseX, displayContainer.mouseY)) ? MUSICBAR_HOVER : MUSICBAR_NORMAL);
g.setColor((musicPositionBarContains(mouseX, mouseY)) ? MUSICBAR_HOVER : MUSICBAR_NORMAL);
g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4);
if (!isLeadIn()) {
g.setColor(MUSICBAR_FILL);
@ -698,8 +694,8 @@ public class Game extends ComplexOpsuState {
g.fillRect(0, 0, width, height);
// draw glowing hit select circle and pulse effect
int circleDiameter = GameImage.HITCIRCLE.getImage().getWidth();
Image cursorCircle = GameImage.HITCIRCLE_SELECT.getImage().getScaledCopy(circleDiameter, circleDiameter);
int circleDiameter = HITCIRCLE.getWidth();
Image cursorCircle = HITCIRCLE_SELECT.getScaledImage(circleDiameter, circleDiameter);
cursorCircle.setAlpha(1.0f);
cursorCircle.drawCentered(pausedMousePosition.x, pausedMousePosition.y);
Image cursorCirclePulse = cursorCircle.getScaledCopy(1f + pausePulse);
@ -718,6 +714,15 @@ public class Game extends ComplexOpsuState {
displayContainer.cursor.draw(Utils.isGameKeyPressed());
}
super.render(g);
if (OPTION_DANCE_ENABLE_SB.state) {
optionsOverlay.render(g);
if (optionsOverlay.isActive()) {
backButton.draw(g);
}
}
UI.draw(g);
//g.setColor(new Color(0.2f, 0.2f, 0.2f));
@ -736,15 +741,20 @@ public class Game extends ComplexOpsuState {
replayPlayback.render(displayContainer.renderDelta, g, ypos, trackPosition);
//}
}
super.render(g);
}
@Override
public void preRenderUpdate() {
super.preRenderUpdate();
int delta = displayContainer.renderDelta;
if (OPTION_DANCE_ENABLE_SB.state) {
optionsOverlay.preRenderUpdate();
if (optionsOverlay.isActive()) {
backButton.hoverUpdate();
}
}
int delta = renderDelta;
UI.update(delta);
Pippi.update(delta);
@ -752,8 +762,6 @@ public class Game extends ComplexOpsuState {
epiImgTime -= delta;
}
yugecin.opsudance.spinners.Spinner.update(delta);
int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
skipButton.hoverUpdate(delta, mouseX, mouseY);
if (isReplay || GameMod.AUTO.isActive())
playbackSpeed.getButton().hoverUpdate(delta, mouseX, mouseY);
@ -929,16 +937,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 - displayContainer.width / 2d;
double dy = autoMousePosition.y - displayContainer.height / 2d;
double dx = autoMousePosition.x - width2;
double dy = autoMousePosition.y - height2;
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 + displayContainer.width / 2), (int) (Math.sin(a) * d + displayContainer.height / 2));
mirrorCursor.setCursorPosition(displayContainer.delta, (int) (Math.cos(a) * d + width2), (int) (Math.sin(a) * d + height2));
}
} 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);
displayContainer.cursor.setCursorPosition(displayContainer.delta, mouseX, mouseY);
}
}
@ -1088,7 +1096,6 @@ public class Game extends ComplexOpsuState {
if (restart != Restart.LOSE) {
// update objects (loop in unlikely event of any skipped indexes)
boolean keyPressed = keys != ReplayFrame.KEY_NONE;
boolean skippedObject = false;
while (objectIndex < gameObjects.length && trackPosition > beatmap.objects[objectIndex].getTime()) {
// check if we've already passed the next object's start time
boolean overlap = (objectIndex + 1 < gameObjects.length &&
@ -1096,7 +1103,6 @@ public class Game extends ComplexOpsuState {
// update hit object and check completion status
if (gameObjects[objectIndex].update(overlap, delta, mouseX, mouseY, keyPressed, trackPosition)) {
skippedObject = true;
objectIndex++; // done, so increment object index
storyboardOverlay.updateIndex(objectIndex);
if (objectIndex >= mirrorTo) {
@ -1119,6 +1125,10 @@ public class Game extends ComplexOpsuState {
@Override
public boolean keyPressed(int key, char c) {
if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.keyPressed(key, c)) {
return true;
}
if (super.keyPressed(key, c)) {
return true;
}
@ -1128,8 +1138,6 @@ public class Game extends ComplexOpsuState {
}
int trackPosition = MusicController.getPosition();
int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
// game keys
if (!Keyboard.isRepeatEvent()) {
@ -1189,7 +1197,7 @@ public class Game extends ComplexOpsuState {
if (0 <= time && time < 3600) {
OPTION_CHECKPOINT.setValue(time);
SoundController.playSound(SoundEffect.MENUCLICK);
BarNotifListener.EVENT.make().onBarNotif("Checkpoint saved.");
barNotifs.send("Checkpoint saved.");
}
}
break;
@ -1201,7 +1209,7 @@ public class Game extends ComplexOpsuState {
break; // invalid checkpoint
loadCheckpoint(checkpoint);
SoundController.playSound(SoundEffect.MENUHIT);
BarNotifListener.EVENT.make().onBarNotif("Checkpoint loaded.");
barNotifs.send("Checkpoint loaded.");
}
break;
case KEY_F:
@ -1242,14 +1250,13 @@ public class Game extends ComplexOpsuState {
}
OPTION_DANCE_MIRROR.toggle();
break;
case KEY_SUBTRACT:
case KEY_MINUS:
currentMapMusicOffset += 5;
BarNotifListener.EVENT.make().onBarNotif("Current map offset: " + currentMapMusicOffset);
adjustLocalMusicOffset(-5);
break;
}
if (key == KEY_ADD || c == '+') {
currentMapMusicOffset -= 5;
BarNotifListener.EVENT.make().onBarNotif("Current map offset: " + currentMapMusicOffset);
if (key == KEY_EQUALS || key == KEY_ADD || c == '+') {
adjustLocalMusicOffset(5);
}
return true;
@ -1257,14 +1264,21 @@ public class Game extends ComplexOpsuState {
@Override
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
if (super.mouseDragged(oldx, oldy, newx, newy)) {
if (OPTION_DANCE_ENABLE_SB.state &&
optionsOverlay.mouseDragged(oldx, oldy, newx, newy))
{
return true;
}
return true;
return super.mouseDragged(oldx, oldy, newx, newy);
}
@Override
public boolean mousePressed(int button, int x, int y) {
if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.mousePressed(button, x, y)) {
return true;
}
if (super.mousePressed(button, x, y)) {
return true;
}
@ -1340,7 +1354,7 @@ public class Game extends ComplexOpsuState {
// returning from pause screen
if (pauseTime > -1) {
double distance = Math.hypot(pausedMousePosition.x - x, pausedMousePosition.y - y);
int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
int circleRadius = GameImage.HITCIRCLE.getWidth() / 2;
if (distance < circleRadius) {
// unpause the game
pauseTime = -1;
@ -1370,6 +1384,10 @@ public class Game extends ComplexOpsuState {
@Override
public boolean mouseReleased(int button, int x, int y) {
if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.mouseReleased(button, x, y)) {
return true;
}
if (super.mouseReleased(button, x, y)) {
return true;
}
@ -1399,6 +1417,10 @@ public class Game extends ComplexOpsuState {
@Override
public boolean keyReleased(int key, char c) {
if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.keyReleased(key, c)) {
return true;
}
if (super.keyReleased(key, c)) {
return true;
}
@ -1436,6 +1458,10 @@ public class Game extends ComplexOpsuState {
@Override
public boolean mouseWheelMoved(int newValue) {
if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.mouseWheelMoved(newValue)) {
return true;
}
if (super.mouseWheelMoved(newValue)) {
return true;
}
@ -1453,11 +1479,9 @@ public class Game extends ComplexOpsuState {
public void enter() {
overlays.clear();
if (OPTION_DANCE_ENABLE_SB.state) {
overlays.add(optionsOverlay);
overlays.add(moveStoryboardOverlay);
overlays.add(storyboardOverlay);
storyboardOverlay.onEnter();
optionsOverlay.revalidate();
}
super.enter();
@ -1523,8 +1547,9 @@ public class Game extends ComplexOpsuState {
}
if (beatmap == null || beatmap.objects == null) {
BubNotifListener.EVENT.make().onBubNotif("Game was running without a beatmap", Colors.BUB_RED);
bubNotifs.send(BUB_RED, "Game was running without a beatmap");
displayContainer.switchStateInstantly(songMenuState);
return;
}
Dancer.instance.reset();
@ -1552,10 +1577,9 @@ public class Game extends ComplexOpsuState {
epiImgTime = OPTION_EPILEPSY_WARNING.val * 100;
if (epiImgTime > 0) {
epiImg = GameImage.EPILEPSY_WARNING.getImage();
float desWidth = displayContainer.width / 2;
epiImg = epiImg.getScaledCopy(desWidth / epiImg.getWidth());
epiImgX = (displayContainer.width - epiImg.getWidth()) / 2;
epiImgY = (displayContainer.height - epiImg.getHeight()) / 2;
epiImg = epiImg.getScaledCopy(width2 / epiImg.getWidth());
epiImgX = width2 - epiImg.getWidth() / 2;
epiImgY = height2 - epiImg.getHeight() / 2;
}
// load mods
@ -1630,7 +1654,7 @@ public class Game extends ComplexOpsuState {
} 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);
BubNotifListener.EVENT.make().onBubNotif(message, Colors.BUB_RED);
bubNotifs.send(BUB_RED, message);
gameObjects[i] = new DummyObject(hitObject);
}
}
@ -1657,8 +1681,8 @@ public class Game extends ComplexOpsuState {
// load replay frames
if (isReplay) {
// load initial data
replayX = displayContainer.width / 2;
replayY = displayContainer.height / 2;
replayX = width2;
replayY = height2;
replayKeyPressed = false;
replaySkipTime = -1;
for (replayIndex = 0; replayIndex < replay.frames.length; replayIndex++) {
@ -1680,7 +1704,7 @@ public class Game extends ComplexOpsuState {
lastKeysPressed = ReplayFrame.KEY_NONE;
replaySkipTime = -1;
replayFrames = new LinkedList<>();
replayFrames.add(new ReplayFrame(0, 0, displayContainer.mouseX, displayContainer.mouseY, 0));
replayFrames.add(new ReplayFrame(0, 0, mouseX, mouseY, 0));
}
for (int i = 0; i < gameObjects.length; i++) {
@ -1698,6 +1722,10 @@ public class Game extends ComplexOpsuState {
scoreboardVisible = true;
currentScoreboardAlpha = 0f;
// using local offset?
if (beatmap.localMusicOffset != 0)
barNotifs.send(String.format("Using local beatmap offset (%dms)", beatmap.localMusicOffset));
// needs to play before setting position to resume without lag later
MusicController.play(false);
MusicController.setPosition(0);
@ -1773,6 +1801,8 @@ public class Game extends ComplexOpsuState {
storyboardOverlay.onLeave();
}
optionsOverlay.hide();
isInGame = false;
// container.setMouseGrabbed(false);
skippedToCheckpoint = false;
@ -1796,6 +1826,18 @@ public class Game extends ComplexOpsuState {
GameMod.loadModState(previousMods);
}
/**
* Adjusts the beatmap's local music offset.
* @param sign the sign (multiplier)
*/
public void adjustLocalMusicOffset(int amount) {
int newOffset = beatmap.localMusicOffset + amount;
barNotifs.send(String.format("Local beatmap offset set to %dms", newOffset));
if (beatmap.localMusicOffset != newOffset) {
beatmap.localMusicOffset = newOffset;
BeatmapDB.updateLocalOffset(beatmap);
}
}
public void addMergedSliderPointsToRender(int from, int to) {
knorkesliders.addRange(from, to);
}
@ -1850,7 +1892,7 @@ public class Game extends ComplexOpsuState {
}
if (lastObjectIndex != -1 && !beatmap.objects[index].isNewCombo()) {
// calculate points
final int followPointInterval = displayContainer.height / 14;
final int followPointInterval = height / 14;
int lastObjectEndTime = gameObjects[lastObjectIndex].getEndTime() + 1;
int objectStartTime = beatmap.objects[index].getTime();
Vec2f startPoint = gameObjects[lastObjectIndex].getPointAt(lastObjectEndTime);
@ -1858,7 +1900,7 @@ public class Game extends ComplexOpsuState {
float xDiff = endPoint.x - startPoint.x;
float yDiff = endPoint.y - startPoint.y;
float dist = (float) Math.hypot(xDiff, yDiff);
int numPoints = (int) ((dist - GameImage.HITCIRCLE.getImage().getWidth()) / followPointInterval);
int numPoints = (int) ((dist - GameImage.HITCIRCLE.getWidth()) / followPointInterval);
if (numPoints > 0) {
// set the image angle
Image followPoint = GameImage.FOLLOWPOINT.getImage();
@ -1911,7 +1953,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(displayContainer.width / 2f, displayContainer.height / 2f, 180f);
g.rotate(width2, height2, 180f);
gameObj.draw(g, trackPosition, true);
g.popTransform();
}
@ -1939,7 +1981,7 @@ public class Game extends ComplexOpsuState {
g.pushTransform();
// translate and rotate the object
g.translate(0, dt * dt * displayContainer.height);
g.translate(0, dt * dt * height);
Vec2f rotationCenter = gameObj.getPointAt((beatmap.objects[idx].getTime() + beatmap.objects[idx].getEndTime()) / 2);
g.rotate(rotationCenter.x, rotationCenter.y, rotSpeed * dt);
gameObj.draw(g, trackPosition, false);
@ -1954,15 +1996,12 @@ public class Game extends ComplexOpsuState {
* @param beatmap the beatmap to load
*/
public void loadBeatmap(Beatmap beatmap) {
if (this.beatmap == null || this.beatmap.beatmapID != beatmap.beatmapID) {
currentMapMusicOffset = 0;
}
this.beatmap = beatmap;
Display.setTitle(String.format("opsu!dance - %s", beatmap.toString()));
if (beatmap.breaks == null) {
BeatmapDB.load(beatmap, BeatmapDB.LOAD_ARRAY);
}
beatmapParser.parseHitObjects(beatmap);
BeatmapParser.parseHitObjects(beatmap);
HitSound.setDefaultSampleSet(beatmap.sampleSet);
}
@ -1993,7 +2032,7 @@ public class Game extends ComplexOpsuState {
lastReplayTime = 0;
autoMousePosition = new Vec2f();
autoMousePressed = false;
flashlightRadius = displayContainer.height * 2 / 3;
flashlightRadius = height * 2 / 3;
if (scoreboardStarStream != null) {
scoreboardStarStream.clear();
}
@ -2045,10 +2084,10 @@ public class Game extends ComplexOpsuState {
// skip button
if (GameImage.SKIP.getImages() != null) {
Animation skip = GameImage.SKIP.getAnimation(120);
skipButton = new MenuButton(skip, displayContainer.width - skip.getWidth() / 2f, displayContainer.height - (skip.getHeight() / 2f));
skipButton = new MenuButton(skip, width - skip.getWidth() / 2f, height - (skip.getHeight() / 2f));
} else {
Image skip = GameImage.SKIP.getImage();
skipButton = new MenuButton(skip, displayContainer.width - skip.getWidth() / 2f, displayContainer.height - (skip.getHeight() / 2f));
skipButton = new MenuButton(skip, width - skip.getWidth() / 2f, height - (skip.getHeight() / 2f));
}
skipButton.setHoverAnimationDuration(350);
skipButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK);
@ -2093,12 +2132,12 @@ public class Game extends ComplexOpsuState {
// initialize objects
gameObjectRenderer.initForGame(data, diameter);
Slider.init(gameObjectRenderer.circleDiameter, beatmap);
Spinner.init(displayContainer, overallDifficulty);
Spinner.init(overallDifficulty);
Color sliderBorderColor = SkinService.skin.getSliderBorderColor();
if (!OPTION_IGNORE_BEATMAP_SKINS.state && beatmap.getSliderBorderColor() != null) {
sliderBorderColor = beatmap.getSliderBorderColor();
}
Curve.init(displayContainer.width, displayContainer.height, diameter, sliderBorderColor);
Curve.init(diameter, sliderBorderColor);
// approachRate (hit object approach time)
if (approachRate < 5)
@ -2214,16 +2253,17 @@ public class Game extends ComplexOpsuState {
if (replay == null) {
this.isReplay = false;
this.replay = null;
} else {
if (replay.frames == null) {
BubNotifListener.EVENT.make().onBubNotif("Attempting to set a replay with no frames.",
Colors.BUB_ORANGE);
return;
}
if (replay.frames == null) {
bubNotifs.send(BUB_ORANGE, "Attempting to set a replay with no frames.");
return;
}
this.isReplay = true;
this.replay = replay;
}
}
/**
* Adds a replay frame to the list, if possible, and runs it.
@ -2316,25 +2356,25 @@ public class Game extends ComplexOpsuState {
if (isLeadIn()) {
// lead-in: expand area
float progress = Math.max((float) (leadInTime - beatmap.audioLeadIn) / approachTime, 0f);
flashlightRadius = displayContainer.width - (int) ((displayContainer.width - (displayContainer.height * 2 / 3)) * progress);
flashlightRadius = width - (int) ((width - (height * 2 / 3)) * progress);
} else if (firstObject) {
// before first object: shrink area
int timeDiff = beatmap.objects[0].getTime() - trackPosition;
flashlightRadius = displayContainer.width;
flashlightRadius = width;
if (timeDiff < approachTime) {
float progress = (float) timeDiff / approachTime;
flashlightRadius -= (displayContainer.width - (displayContainer.height * 2 / 3)) * (1 - progress);
flashlightRadius -= (width - (height * 2 / 3)) * (1 - progress);
}
} else {
// gameplay: size based on combo
int targetRadius;
int combo = data.getComboStreak();
if (combo < 100)
targetRadius = displayContainer.height * 2 / 3;
targetRadius = height * 2 / 3;
else if (combo < 200)
targetRadius = displayContainer.height / 2;
targetRadius = height2;
else
targetRadius = displayContainer.height / 3;
targetRadius = height / 3;
if (beatmap.breaks != null && breakIndex < beatmap.breaks.size() && breakTime > 0) {
// breaks: expand at beginning, shrink at end
flashlightRadius = targetRadius;
@ -2346,11 +2386,11 @@ public class Game extends ComplexOpsuState {
progress = (float) (trackPosition - breakTime) / approachTime;
else if (endTime - trackPosition < approachTime)
progress = (float) (endTime - trackPosition) / approachTime;
flashlightRadius += (displayContainer.width - flashlightRadius) * progress;
flashlightRadius += (width - flashlightRadius) * progress;
}
} else if (flashlightRadius != targetRadius) {
// radius size change
float radiusDiff = displayContainer.height * delta / 2000f;
float radiusDiff = height * delta / 2000f;
if (flashlightRadius > targetRadius) {
flashlightRadius -= radiusDiff;
if (flashlightRadius < targetRadius)

View File

@ -32,6 +32,7 @@ import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import yugecin.opsudance.core.state.BaseOpsuState;
import static itdelatrisu.opsu.GameImage.*;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
@ -73,11 +74,11 @@ public class GamePauseMenu extends BaseOpsuState {
@Override
public void preRenderUpdate() {
int delta = displayContainer.renderDelta;
int delta = renderDelta;
UI.update(delta);
continueButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
retryButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
backButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
continueButton.hoverUpdate(delta, mouseX, mouseY);
retryButton.hoverUpdate(delta, mouseX, mouseY);
backButton.hoverUpdate(delta, mouseX, mouseY);
}
@Override
@ -89,9 +90,9 @@ public class GamePauseMenu extends BaseOpsuState {
// game keys
if (!Keyboard.isRepeatEvent()) {
if (key == OPTION_KEY_LEFT.intval) {
mousePressed(Input.MOUSE_LEFT_BUTTON, displayContainer.mouseX, displayContainer.mouseY);
mousePressed(Input.MOUSE_LEFT_BUTTON, mouseX, mouseY);
} else if (key == OPTION_KEY_RIGHT.intval) {
mousePressed(Input.MOUSE_RIGHT_BUTTON, displayContainer.mouseX, displayContainer.mouseY);
mousePressed(Input.MOUSE_RIGHT_BUTTON, mouseX, mouseY);
}
}
@ -116,6 +117,15 @@ public class GamePauseMenu extends BaseOpsuState {
return true;
}
if (key == KEY_SUBTRACT || key == KEY_MINUS) {
gameState.adjustLocalMusicOffset(-5);
return true;
}
if (key == KEY_EQUALS || key == KEY_ADD || c == '+') {
gameState.adjustLocalMusicOffset(5);
return true;
}
return false;
}
@ -193,9 +203,9 @@ public class GamePauseMenu extends BaseOpsuState {
*/
public void loadImages() {
// initialize buttons
continueButton = new MenuButton(GameImage.PAUSE_CONTINUE.getImage(), displayContainer.width / 2f, displayContainer.height * 0.25f);
retryButton = new MenuButton(GameImage.PAUSE_RETRY.getImage(), displayContainer.width / 2f, displayContainer.height * 0.5f);
backButton = new MenuButton(GameImage.PAUSE_BACK.getImage(), displayContainer.width / 2f, displayContainer.height * 0.75f);
continueButton = new MenuButton(PAUSE_CONTINUE.getImage(), width2, height * 0.25f);
retryButton = new MenuButton(PAUSE_RETRY.getImage(), width2, height2);
backButton = new MenuButton(PAUSE_BACK.getImage(), width2, height * 0.75f);
final int buttonAnimationDuration = 300;
continueButton.setHoverAnimationDuration(buttonAnimationDuration);
retryButton.setHoverAnimationDuration(buttonAnimationDuration);

View File

@ -38,7 +38,6 @@ import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.state.BaseOpsuState;
import yugecin.opsudance.events.BarNotifListener;
import static yugecin.opsudance.core.InstanceContainer.*;
@ -67,10 +66,10 @@ public class GameRanking extends BaseOpsuState {
// buttons
Image retry = GameImage.PAUSE_RETRY.getImage();
Image replay = GameImage.PAUSE_REPLAY.getImage();
replayY = (displayContainer.height * 0.985f) - replay.getHeight() / 2f;
replayY = height * 0.985f - replay.getHeight() / 2f;
retryY = replayY - (replay.getHeight() / 2f) - (retry.getHeight() / 1.975f);
retryButton = new MenuButton(retry, displayContainer.width - (retry.getWidth() / 2f), retryY);
replayButton = new MenuButton(replay, displayContainer.width - (replay.getWidth() / 2f), replayY);
retryButton = new MenuButton(retry, width - (retry.getWidth() / 2f), retryY);
replayButton = new MenuButton(replay, width - (replay.getWidth() / 2f), replayY);
retryButton.setHoverFade();
replayButton.setHoverFade();
}
@ -80,7 +79,7 @@ public class GameRanking extends BaseOpsuState {
Beatmap beatmap = MusicController.getBeatmap();
// background
if (!beatmap.drawBackground(displayContainer.width, displayContainer.height, 0.7f, true)) {
if (!beatmap.drawBackground(width, height, 0.7f, true)) {
GameImage.PLAYFIELD.getImage().draw(0, 0);
}
@ -91,7 +90,7 @@ public class GameRanking extends BaseOpsuState {
replayButton.draw();
if (data.isGameplay() && !GameMod.AUTO.isActive())
retryButton.draw();
UI.getBackButton().draw(g);
backButton.draw(g);
UI.draw(g);
@ -100,15 +99,15 @@ public class GameRanking extends BaseOpsuState {
@Override
public void preRenderUpdate() {
int delta = displayContainer.renderDelta;
int delta = renderDelta;
UI.update(delta);
replayButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
replayButton.hoverUpdate(delta, mouseX, mouseY);
if (data.isGameplay()) {
retryButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
retryButton.hoverUpdate(delta, mouseX, mouseY);
} else {
MusicController.loopTrackIfEnded(true);
}
UI.getBackButton().hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
backButton.hoverUpdate();
}
@Override
@ -140,7 +139,7 @@ public class GameRanking extends BaseOpsuState {
}
// back to menu
if (UI.getBackButton().contains(x, y)) {
if (backButton.contains(x, y)) {
returnToSongMenu();
return true;
}
@ -157,14 +156,14 @@ public class GameRanking extends BaseOpsuState {
gameState.setRestart((data.isGameplay()) ? Game.Restart.REPLAY : Game.Restart.NEW);
returnToGame = true;
} catch (FileNotFoundException e) {
BarNotifListener.EVENT.make().onBarNotif("Replay file not found.");
barNotifs.send("Replay file not found.");
} catch (IOException e) {
Log.error("Failed to load replay data.", e);
BarNotifListener.EVENT.make().onBarNotif(
"Failed to load replay data. See log for details.");
barNotifs.send("Failed to load replay data. See log for details.");
}
} else {
barNotifs.send("Replay file not found.");
}
} else
BarNotifListener.EVENT.make().onBarNotif("Replay file not found.");
}
// retry

File diff suppressed because it is too large Load Diff

View File

@ -48,6 +48,7 @@ import itdelatrisu.opsu.ui.UI;
import itdelatrisu.opsu.ui.animations.AnimatedValue;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import java.awt.Point;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
@ -63,11 +64,11 @@ 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.state.ComplexOpsuState;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.options.OptionGroups;
import yugecin.opsudance.ui.OptionsOverlay;
import yugecin.opsudance.core.InstanceContainer;
import yugecin.opsudance.core.state.ComplexOpsuState;
import static itdelatrisu.opsu.GameImage.*;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
@ -99,7 +100,7 @@ public class SongMenu extends ComplexOpsuState {
private static final int SEARCH_TRANSITION_TIME = 250;
/** Line width of the header/footer divider. */
private static final int DIVIDER_LINE_WIDTH = 4;
private static final int DIVIDER_LINE_WIDTH = 3;
/** Song node class representing an BeatmapSetNode and file index. */
private static class SongNode {
@ -161,7 +162,7 @@ public class SongMenu extends ComplexOpsuState {
private BeatmapSetNode hoverIndex = null;
/** The selection buttons. */
private MenuButton selectModsButton, selectRandomButton, selectMapOptionsButton, selectOptionsButton;
private MenuButton selectModeButton, selectModsButton, selectRandomButton, selectMapOptionsButton;
/** The search textfield. */
private TextField searchTextField;
@ -311,12 +312,8 @@ public class SongMenu extends ComplexOpsuState {
/** Sort order dropdown menu. */
private DropdownMenu<BeatmapSortOrder> sortMenu;
private final OptionsOverlay optionsOverlay;
public SongMenu() {
super();
optionsOverlay = new OptionsOverlay(displayContainer, OptionGroups.normalOptions);
overlays.add(optionsOverlay);
}
@Override
@ -325,26 +322,27 @@ public class SongMenu extends ComplexOpsuState {
components.clear();
final float footerHeight = height * 0.116666666666f;
// header/footer coordinates
headerY = displayContainer.height * 0.0075f + GameImage.MENU_MUSICNOTE.getImage().getHeight() +
headerY = height * 0.0075f + GameImage.MENU_MUSICNOTE.getHeight() +
Fonts.BOLD.getLineHeight() + Fonts.DEFAULT.getLineHeight() +
Fonts.SMALL.getLineHeight();
footerY = displayContainer.height - GameImage.SELECTION_MODS.getImage().getHeight();
footerY = height - footerHeight;
// footer logo coordinates
float footerHeight = displayContainer.height - footerY;
footerLogoSize = footerHeight * 3.25f;
Image logo = GameImage.MENU_LOGO.getImage();
logo = logo.getScaledCopy(footerLogoSize / logo.getWidth());
footerLogoButton = new MenuButton(logo, displayContainer.width - footerHeight * 0.8f, displayContainer.height - footerHeight * 0.65f);
footerLogoButton = new MenuButton(logo, width - footerHeight * 0.8f, height - footerHeight * 0.65f);
footerLogoButton.setHoverAnimationDuration(1);
footerLogoButton.setHoverExpand(1.2f);
// initialize sorts
int sortWidth = (int) (displayContainer.width * 0.12f);
int posX = (int) (displayContainer.width * 0.87f);
int posY = (int) (headerY - GameImage.MENU_TAB.getImage().getHeight() * 2.25f);
sortMenu = new DropdownMenu<BeatmapSortOrder>(displayContainer, BeatmapSortOrder.values(), posX, posY, sortWidth) {
int sortWidth = (int) (width * 0.12f);
int posX = (int) (width * 0.87f);
int posY = (int) (headerY - GameImage.MENU_TAB.getHeight() * 2.25f);
sortMenu = new DropdownMenu<BeatmapSortOrder>(BeatmapSortOrder.values(), posX, posY, sortWidth) {
@Override
public void itemSelected(int index, BeatmapSortOrder item) {
BeatmapSortOrder.set(item);
@ -373,25 +371,25 @@ public class SongMenu extends ComplexOpsuState {
// initialize group tabs
for (BeatmapGroup group : BeatmapGroup.values())
group.init(displayContainer.width, headerY - DIVIDER_LINE_WIDTH / 2);
group.init(width, headerY - DIVIDER_LINE_WIDTH / 2);
// initialize score data buttons
ScoreData.init(displayContainer.width, headerY + displayContainer.height * 0.01f);
ScoreData.init(width, headerY + height * 0.01f);
// song button background & graphics context
Image menuBackground = GameImage.MENU_BUTTON_BG.getImage();
// song button coordinates
buttonX = displayContainer.width * 0.6f;
buttonX = width * 0.6f;
//buttonY = headerY;
buttonWidth = menuBackground.getWidth();
buttonHeight = menuBackground.getHeight();
buttonOffset = (footerY - headerY - DIVIDER_LINE_WIDTH) / MAX_SONG_BUTTONS;
// search
int textFieldX = (int) (displayContainer.width * 0.7125f + Fonts.BOLD.getWidth("Search: "));
int textFieldX = (int) (width * 0.7125f + Fonts.BOLD.getWidth("Search: "));
int textFieldY = (int) (headerY + Fonts.BOLD.getLineHeight() / 2);
searchTextField = new TextField(Fonts.BOLD, textFieldX, textFieldY, (int) (displayContainer.width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight()) {
searchTextField = new TextField(Fonts.BOLD, textFieldX, textFieldY, (int) (width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight()) {
@Override
public boolean isFocusable() {
return false;
@ -405,33 +403,23 @@ public class SongMenu extends ComplexOpsuState {
components.add(searchTextField);
// selection buttons
Image selectionMods = GameImage.SELECTION_MODS.getImage();
int selectButtonsWidth = selectionMods.getWidth();
int selectButtonsHeight = selectionMods.getHeight();
if (selectButtonsHeight < 20) {
selectButtonsHeight = 100;
}
if (selectButtonsWidth < 20) {
selectButtonsWidth = 100;
}
float selectX = displayContainer.width * 0.183f + selectButtonsWidth / 2f;
float selectY = displayContainer.height - selectButtonsHeight / 2f;
float selectOffset = selectButtonsWidth * 1.05f;
selectModsButton = new MenuButton(GameImage.SELECTION_MODS_OVERLAY.getImage(),
selectX, selectY);
selectRandomButton = new MenuButton(GameImage.SELECTION_RANDOM_OVERLAY.getImage(),
selectX + selectOffset, selectY);
selectMapOptionsButton = new MenuButton(GameImage.SELECTION_OPTIONS_OVERLAY.getImage(),
selectX + selectOffset * 2f, selectY);
selectOptionsButton = new MenuButton(GameImage.SELECTION_OTHER_OPTIONS_OVERLAY.getImage(),
selectX + selectOffset * 3f, selectY);
// TODO: the origin should be bottomleft or something
float selectX = width * (isWidescreen ? 0.164f : 0.1875f);
final float footerButtonWidth = footerHeight * 0.84f;
selectModeButton = new MenuButton(SELECTION_MODE_OVERLAY, selectX, footerY);
selectX += footerHeight + 2;
selectModsButton = new MenuButton(SELECTION_MODS_OVERLAY, selectX, footerY);
selectX += footerButtonWidth;
selectRandomButton = new MenuButton(SELECTION_RANDOM_OVERLAY, selectX, footerY);
selectX += footerButtonWidth;
selectMapOptionsButton = new MenuButton(SELECTION_OPTIONS_OVERLAY, selectX, footerY);
selectModeButton.setHoverFade(0f);
selectModsButton.setHoverFade(0f);
selectRandomButton.setHoverFade(0f);
selectMapOptionsButton.setHoverFade(0f);
selectOptionsButton.setHoverFade(0f);
// loader
int loaderDim = GameImage.MENU_MUSICNOTE.getImage().getWidth();
int loaderDim = GameImage.MENU_MUSICNOTE.getWidth();
SpriteSheet spr = new SpriteSheet(GameImage.MENU_LOADER.getImage(), loaderDim, loaderDim);
loader = new Animation(spr, 50);
@ -446,14 +434,13 @@ public class SongMenu extends ComplexOpsuState {
if (!displayContainer.isInState(SongMenu.class)) {
return;
}
BarNotifListener.EVENT.make().onBarNotif(
"Changed is Songs folder detected. Hit F5 to refresh.");
barNotifs.send("Changes in Songs folder detected. Hit F5 to refresh.");
}
});
// star stream
starStream = new StarStream(displayContainer.width, (displayContainer.height - GameImage.STAR.getImage().getHeight()) / 2, -displayContainer.width, 0, MAX_STREAM_STARS);
starStream.setPositionSpread(displayContainer.height / 20f);
starStream = new StarStream(width, height2 - GameImage.STAR.getImage().getHeight() / 2, -width, 0, MAX_STREAM_STARS);
starStream.setPositionSpread(height / 20f);
starStream.setDirectionSpread(10f);
}
@ -461,11 +448,6 @@ public class SongMenu extends ComplexOpsuState {
public void render(Graphics g) {
g.setBackground(Color.black);
int width = displayContainer.width;
int height = displayContainer.height;
int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
// background
if (focusNode != null) {
Beatmap focusNodeBeatmap = focusNode.getSelectedBeatmap();
@ -542,14 +524,12 @@ public class SongMenu extends ComplexOpsuState {
}
// top/bottom bars
g.setColor(Colors.BLACK_ALPHA);
g.setColor(Color.black);
g.fillRect(0, 0, width, headerY);
g.fillRect(0, footerY, width, height - footerY);
g.setColor(Colors.BLUE_DIVIDER);
g.setLineWidth(DIVIDER_LINE_WIDTH);
g.drawLine(0, headerY, width, headerY);
g.drawLine(0, footerY, width, footerY);
g.resetLineWidth();
g.fillRect(0, headerY, width, DIVIDER_LINE_WIDTH);
g.fillRect(0, footerY, width, DIVIDER_LINE_WIDTH);
// footer logo (pulsing)
Float position = MusicController.getBeatProgress();
@ -563,10 +543,8 @@ public class SongMenu extends ComplexOpsuState {
footerLogoButton.draw(Color.white, 1f - expand);
Image ghostLogo = GameImage.MENU_LOGO.getImage();
ghostLogo = ghostLogo.getScaledCopy((1f + expand) * footerLogoSize / ghostLogo.getWidth());
float oldGhostAlpha = Colors.GHOST_LOGO.a;
Colors.GHOST_LOGO.a *= (1f - position);
ghostLogo.drawCentered(footerLogoButton.getX(), footerLogoButton.getY(), Colors.GHOST_LOGO);
Colors.GHOST_LOGO.a = oldGhostAlpha;
ghostLogo.setAlpha(0.25f * (1f - position));
ghostLogo.drawCentered(footerLogoButton.getX(), footerLogoButton.getY());
}
// header
@ -636,14 +614,19 @@ public class SongMenu extends ComplexOpsuState {
}
// selection buttons
GameImage.SELECTION_MODS.getImage().drawCentered(selectModsButton.getX(), selectModsButton.getY());
Point c;
c = selectModeButton.bottomLeft();
SELECTION_MODE.getImage().draw(c.x, c.y - SELECTION_MODE.getHeight());
selectModeButton.draw();
c = selectModsButton.bottomLeft();
SELECTION_MODS.getImage().draw(c.x, c.y - SELECTION_MODS.getHeight());
selectModsButton.draw();
GameImage.SELECTION_RANDOM.getImage().drawCentered(selectRandomButton.getX(), selectRandomButton.getY());
c = selectRandomButton.bottomLeft();
SELECTION_RANDOM.getImage().draw(c.x, c.y - SELECTION_RANDOM.getHeight());
selectRandomButton.draw();
GameImage.SELECTION_OPTIONS.getImage().drawCentered(selectMapOptionsButton.getX(), selectMapOptionsButton.getY());
c = selectMapOptionsButton.bottomLeft();
SELECTION_OPTIONS.getImage().draw(c.x, c.y - SELECTION_OPTIONS.getHeight());
selectMapOptionsButton.draw();
GameImage.SELECTION_OTHER_OPTIONS.getImage().drawCentered(selectOptionsButton.getX(), selectOptionsButton.getY());
selectOptionsButton.draw();
// group tabs
BeatmapGroup currentGroup = BeatmapGroup.current();
@ -701,11 +684,11 @@ public class SongMenu extends ComplexOpsuState {
g.fillRect(0, 0, width, height);
UI.drawLoadingProgress(g);
} else {
optionsOverlay.render(g);
backButton.draw(g);
}
// back button
else
UI.getBackButton().draw(g);
UI.draw(g);
@ -716,7 +699,17 @@ public class SongMenu extends ComplexOpsuState {
public void preRenderUpdate() {
super.preRenderUpdate();
int delta = displayContainer.renderDelta;
optionsOverlay.preRenderUpdate();
int mouseX = InstanceContainer.mouseX;
int mouseY = InstanceContainer.mouseY;
if (optionsOverlay.containsMouse()) {
// dirty hack to not show elements underneath options overlay as hovered
mouseX = -mouseX;
mouseY = -mouseY;
}
int delta = renderDelta;
UI.update(delta);
if (reloadThread == null)
MusicController.loopTrackIfEnded(true);
@ -732,13 +725,11 @@ public class SongMenu extends ComplexOpsuState {
MusicController.playThemeSong(config.themeBeatmap);
reloadThread = null;
}
int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
backButton.hoverUpdate();
selectModeButton.hoverUpdate(delta, mouseX, mouseY);
selectModsButton.hoverUpdate(delta, mouseX, mouseY);
selectRandomButton.hoverUpdate(delta, mouseX, mouseY);
selectMapOptionsButton.hoverUpdate(delta, mouseX, mouseY);
selectOptionsButton.hoverUpdate(delta, mouseX, mouseY);
footerLogoButton.hoverUpdate(delta, mouseX, mouseY, 0.25f);
// beatmap menu timer
@ -874,6 +865,10 @@ public class SongMenu extends ComplexOpsuState {
return true;
}
if (optionsOverlay.mousePressed(button, x, y)) {
return true;
}
if (button == Input.MOUSE_MIDDLE_BUTTON) {
return false;
}
@ -893,6 +888,10 @@ public class SongMenu extends ComplexOpsuState {
return true;
}
if (optionsOverlay.mouseReleased(button, x, y)) {
return true;
}
if (button == Input.MOUSE_MIDDLE_BUTTON) {
return false;
}
@ -908,14 +907,17 @@ public class SongMenu extends ComplexOpsuState {
return true;
}
if (UI.getBackButton().contains(x, y)) {
if (backButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK);
displayContainer.switchState(mainmenuState);
return true;
}
// selection buttons
if (selectModsButton.contains(x, y)) {
if (selectModeButton.contains(x, y)) {
barNotifs.send("There are no other modes available.");
return true;
} else if (selectModsButton.contains(x, y)) {
this.keyPressed(KEY_F1, '\0');
return true;
} else if (selectRandomButton.contains(x, y)) {
@ -924,10 +926,6 @@ public class SongMenu extends ComplexOpsuState {
} else if (selectMapOptionsButton.contains(x, y)) {
this.keyPressed(KEY_F3, '\0');
return true;
} else if (selectOptionsButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT);
optionsOverlay.show();
return true;
}
// group tabs
@ -955,7 +953,7 @@ public class SongMenu extends ComplexOpsuState {
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
if (BeatmapSetList.get().size() < 1 && group.getEmptyMessage() != null) {
BarNotifListener.EVENT.make().onBarNotif(group.getEmptyMessage());
barNotifs.send(group.getEmptyMessage());
}
return true;
}
@ -1039,6 +1037,10 @@ public class SongMenu extends ComplexOpsuState {
return true;
}
if (optionsOverlay.keyPressed(key, c)) {
return true;
}
// block input
if ((reloadThread != null && key != KEY_ESCAPE) || beatmapMenuTimer > -1 || isScrollingToFocusNode) {
return true;
@ -1193,6 +1195,10 @@ public class SongMenu extends ComplexOpsuState {
return true;
}
if (optionsOverlay.mouseDragged(oldx, oldy, newx, newy)) {
return true;
}
if (isInputBlocked()) {
return true;
}
@ -1226,6 +1232,10 @@ public class SongMenu extends ComplexOpsuState {
return true;
}
if (optionsOverlay.mouseWheelMoved(newValue)) {
return true;
}
if (isInputBlocked()) {
return true;
}
@ -1243,15 +1253,24 @@ public class SongMenu extends ComplexOpsuState {
return false;
}
@Override
public boolean keyReleased(int key, char c) {
if (super.keyReleased(key, c)) {
return true;
}
return optionsOverlay.keyReleased(key, c);
}
@Override
public void enter() {
super.enter();
UI.enter();
selectModeButton.resetHover();
selectModsButton.resetHover();
selectRandomButton.resetHover();
selectMapOptionsButton.resetHover();
selectOptionsButton.resetHover();
hoverOffset.setTime(0);
hoverIndex = null;
isScrollingToFocusNode = false;
@ -1761,7 +1780,7 @@ public class SongMenu extends ComplexOpsuState {
Beatmap beatmap = MusicController.getBeatmap();
if (focusNode == null || beatmap != focusNode.getSelectedBeatmap()) {
BarNotifListener.EVENT.make().onBarNotif("Unable to load the beatmap audio.");
barNotifs.send("Unable to load the beatmap audio.");
return;
}

View File

@ -22,13 +22,12 @@ import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.ui.Cursor;
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.state.BaseOpsuState;
@ -65,9 +64,6 @@ public class Splash extends BaseOpsuState {
return;
}
System.out.println(
Renderer.get().getClass()
);
inited = true;
thread = new Thread() {
@Override
@ -111,7 +107,13 @@ public class Splash extends BaseOpsuState {
@Override
public void render(Graphics g) {
g.setBackground(Color.black);
GameImage.MENU_LOGO.getImage().drawCentered(displayContainer.width / 2, displayContainer.height / 2);
final Color col;
if (OPTION_COLOR_MAIN_MENU_LOGO.state) {
col = Cursor.lastCursorColor;
} else {
col = Color.white;
}
GameImage.MENU_LOGO.getImage().drawCentered(width2, height2, col);
UI.drawLoadingProgress(g);
}

View File

@ -28,6 +28,7 @@ public class Colors {
BLACK_ALPHA = new Color(0, 0, 0, 0.5f),
BLACK_ALPHA_75 = new Color(0, 0, 0, 0.75f),
BLACK_ALPHA_85 = new Color(0, 0, 0, 0.85f),
WHITE_ALPHA_75 = new Color(1, 1, 1, 0.75f),
WHITE_ALPHA = new Color(255, 255, 255, 0.5f),
BLUE_DIVIDER = new Color(49, 94, 237),
BLUE_BACKGROUND = new Color(74, 130, 255),
@ -53,8 +54,7 @@ public class Colors {
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);
BUB_ORANGE = new Color(138, 72, 51);
// This class should not be instantiated.
private Colors() {}

View File

@ -30,6 +30,7 @@ import yugecin.opsudance.Dancer;
import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Updates and draws the cursor.
@ -263,8 +264,8 @@ public class Cursor {
* If the old style cursor is being used, this will do nothing.
* @param delta the delta interval since the last call
*/
public void updateAngle(int delta) {
cursorAngle += delta / 40f;
public void updateAngle() {
cursorAngle += renderDelta / 40f;
cursorAngle %= 360;
}

View File

@ -29,15 +29,14 @@ import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.UnicodeFont;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.components.Component;
import static yugecin.opsudance.core.InstanceContainer.*;
public class DropdownMenu<E> extends Component {
private static final float PADDING_Y = 0.1f, CHEVRON_X = 0.03f;
private final DisplayContainer displayContainer;
private E[] items;
private String[] itemNames;
private int selectedItemIndex;
@ -61,8 +60,7 @@ public class DropdownMenu<E> extends Component {
private Image chevronDown;
private Image chevronRight;
public DropdownMenu(DisplayContainer displayContainer, E[] items, int x, int y, int width) {
this.displayContainer = displayContainer;
public DropdownMenu(E[] items, int x, int y, int width) {
init(items, x, y, width);
}
@ -143,13 +141,13 @@ public class DropdownMenu<E> extends Component {
@Override
public void render(Graphics g) {
int delta = displayContainer.renderDelta;
int delta = renderDelta;
// update animation
expandProgress.update((expanded) ? delta : -delta * 2);
// get parameters
int idx = getIndexAt(displayContainer.mouseY);
int idx = getIndexAt(mouseY);
float t = expandProgress.getValue();
if (expanded) {
t = AnimationEquation.OUT_CUBIC.calc(t);
@ -245,7 +243,7 @@ public class DropdownMenu<E> extends Component {
return;
}
int idx = getIndexAt(displayContainer.mouseY);
int idx = getIndexAt(mouseY);
if (idx == -2) {
this.expanded = false;
return;

View File

@ -18,10 +18,13 @@
package itdelatrisu.opsu.ui;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.ui.animations.AnimatedValue;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import java.awt.Point;
import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Font;
@ -98,9 +101,6 @@ public class MenuButton {
/** The default max rotation angle of the button. */
private static final float DEFAULT_ANGLE_MAX = 30f;
/** The last scale at which the button was drawn. */
private float lastScale = 1f;
/**
* Creates a new button from an Image.
* @param img the image
@ -115,6 +115,14 @@ public class MenuButton {
this.yRadius = img.getHeight() / 2f;
}
public MenuButton(GameImage img, float topleftX, float topleftY) {
this.img = img.getImage();
this.xRadius = img.getWidth() / 2f;
this.yRadius = img.getHeight() / 2f;
this.x = topleftX + this.xRadius;
this.y = topleftY + this.yRadius;
}
/**
* Creates a new button from a 3-part Image.
* @param imgCenter the center image
@ -169,10 +177,19 @@ public class MenuButton {
*/
public float getY() { return y; }
public Point bottomLeft() {
return new Point((int) (x - xRadius), (int) (y + yRadius));
}
/**
* Returns the last scale at which the button was drawn.
* Returns the scale multiplier, from the hover effect, used to draw the button.
*/
public float getLastScale() { return lastScale; }
public float getCurrentHoverExpandValue() {
if ((hoverEffect & EFFECT_EXPAND) == 0) {
return 1f;
}
return scale.getValue();
}
/**
* Sets text to draw in the middle of the button.
@ -229,7 +246,6 @@ public class MenuButton {
xScaleOffset = image.getWidth() / 2f - xRadius;
yScaleOffset = image.getHeight() / 2f - yRadius;
}
lastScale = scaleOverride;
if (hoverEffect == 0)
image.draw(x - xRadius, y - yRadius, filter);
else {
@ -243,7 +259,6 @@ public class MenuButton {
xScaleOffset = image.getWidth() / 2f - xRadius;
yScaleOffset = image.getHeight() / 2f - yRadius;
}
lastScale *= scale.getValue();
}
}
if ((hoverEffect & EFFECT_FADE) > 0)

View File

@ -22,27 +22,21 @@ import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.ui.animations.AnimatedValue;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.ui.BackButton;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* Draws common UI components.
*/
public class UI {
/** Back button. */
private static BackButton backButton;
/** Time to show volume image, in milliseconds. */
private static final int VOLUME_DISPLAY_TIME = 1500;
@ -58,20 +52,9 @@ public class UI {
/** The alpha level of the current tooltip (if any). */
private static AnimatedValue tooltipAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR);
// game-related variables
private static DisplayContainer displayContainer;
// This class should not be instantiated.
private UI() {}
/**
* Initializes UI data.
*/
public static void init(DisplayContainer displayContainer) {
UI.displayContainer = displayContainer;
backButton = new BackButton(displayContainer);
}
/**
* Updates all UI components by a delta interval.
* @param delta the delta interval since the last call.
@ -93,15 +76,9 @@ public class UI {
* Resets the necessary UI components upon entering a state.
*/
public static void enter() {
backButton.resetHover();
resetTooltip();
}
/**
* Returns the 'menu-back' MenuButton.
*/
public static BackButton getBackButton() { return backButton; }
/**
* Draws a tab image and text centered at a location.
* @param x the center x coordinate
@ -145,13 +122,13 @@ public class UI {
else if (ratio >= 0.9f)
xOffset = img.getWidth() * (1 - ((1 - ratio) * 10f));
img.drawCentered(displayContainer.width - img.getWidth() / 2f + xOffset, displayContainer.height / 2f);
img.drawCentered(width - img.getWidth() / 2f + xOffset, height2);
float barHeight = img.getHeight() * 0.9f;
float volume = OPTION_MASTER_VOLUME.val / 100f;
g.setColor(Color.white);
g.fillRoundRect(
displayContainer.width - (img.getWidth() * 0.368f) + xOffset,
(displayContainer.height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)),
width - (img.getWidth() * 0.368f) + xOffset,
height2 - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)),
img.getWidth() * 0.15f, barHeight * volume, 3
);
}
@ -193,33 +170,25 @@ public class UI {
int progress;
// determine current action
//
/*
TODO
if ((file = OszUnpacker.getCurrentFileName()) != null) {
if ((file = oszunpacker.getCurrentFileName()) != null) {
text = "Unpacking new beatmaps...";
progress = OszUnpacker.getUnpackerProgress();
} else if ((file = BeatmapParser.getCurrentFileName()) != null) {
text = (BeatmapParser.getStatus() == BeatmapParser.Status.INSERTING) ?
progress = oszunpacker.getUnpackerProgress();
} else if ((file = beatmapParser.getCurrentFileName()) != null) {
text = (beatmapParser.getStatus() == BeatmapParser.Status.INSERTING) ?
"Updating database..." : "Loading beatmaps...";
progress = BeatmapParser.getParserProgress();
} else if ((file = ReplayImporter.getCurrentFileName()) != null) {
progress = beatmapParser.getParserProgress();
} else if ((file = replayImporter.getCurrentFileName()) != null) {
text = "Importing replays...";
progress = ReplayImporter.getLoadingProgress();
progress = replayImporter.getLoadingProgress();
} else if ((file = SoundController.getCurrentFileName()) != null) {
text = "Loading sounds...";
progress = SoundController.getLoadingProgress();
} else
return;
*/
if (true) {
return; // TODO
}
// draw loading info
float marginX = displayContainer.width * 0.02f, marginY = displayContainer.height * 0.02f;
float lineY = displayContainer.height - marginY;
float marginX = width * 0.02f, marginY = height * 0.02f;
float lineY = height - marginY;
int lineOffsetY = Fonts.MEDIUM.getLineHeight();
if (OPTION_LOAD_VERBOSE.state) {
// verbose: display percentages and file names
@ -232,7 +201,7 @@ public class UI {
Fonts.MEDIUM.drawString(marginX, lineY - (lineOffsetY * 2), text, Color.white);
g.setColor(Color.white);
g.fillRoundRect(marginX, lineY - (lineOffsetY / 2f),
(displayContainer.width - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4
(width - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4
);
}
}
@ -256,7 +225,7 @@ public class UI {
float unitBaseX, float unitBaseY, float unitWidth, float scrollAreaHeight,
Color bgColor, Color scrollbarColor, boolean right
) {
float scrollbarWidth = displayContainer.width * 0.00347f;
float scrollbarWidth = width * 0.00347f;
float scrollbarHeight = scrollAreaHeight * lengthShown / totalLength;
float offsetY = (scrollAreaHeight - scrollbarHeight) * (position / (totalLength - lengthShown));
float scrollbarX = unitBaseX + unitWidth - ((right) ? scrollbarWidth : 0);
@ -292,8 +261,8 @@ public class UI {
if (tooltipAlpha.getTime() == 0 || tooltip == null)
return;
int margin = displayContainer.width / 100, textMarginX = 2;
int offset = GameImage.CURSOR_MIDDLE.getImage().getWidth() / 2;
int margin = width / 100, textMarginX = 2;
int offset = GameImage.CURSOR_MIDDLE.getWidth() / 2;
int lineHeight = Fonts.SMALL.getLineHeight();
int textWidth = textMarginX * 2, textHeight = lineHeight;
if (tooltipNewlines) {
@ -310,14 +279,14 @@ public class UI {
textWidth += Fonts.SMALL.getWidth(tooltip);
// get drawing coordinates
int x = displayContainer.mouseX + offset;
int y = displayContainer.mouseY + offset;
if (x + textWidth > displayContainer.width - margin)
x = displayContainer.width - margin - textWidth;
int x = mouseX + offset;
int y = mouseY + offset;
if (x + textWidth > width - margin)
x = width - margin - textWidth;
else if (x < margin)
x = margin;
if (y + textHeight > displayContainer.height - margin)
y = displayContainer.height - margin - textHeight;
if (y + textHeight > height - margin)
y = height - margin - textHeight;
else if (y < margin)
y = margin;

View File

@ -58,6 +58,41 @@ public class AnimatedValue {
this.eqn = eqn;
}
public void change(int duration, float min, float max, AnimationEquation eqn) {
float progress = (float) this.time / this.duration;
if (this.eqn != eqn) {
if (this.time != 0 && this.time != this.duration) {
progress = eqn.uncalc(this.eqn.calc(progress));
}
this.eqn = eqn;
}
this.duration = duration;
this.time = (int) (this.duration * progress);
this.base = min;
this.diff = max - min;
this.updateValue();
}
/**
* Change the from and to values
* @param min start value
* @param max end value
*/
public void setValues(float min, float max) {
this.base = min;
this.value = min;
this.diff = max - min;
this.updateValue();
}
/**
* Gets the max (end) value
* @return the max (end) value
*/
public float getMax() {
return this.base + this.diff;
}
/**
* Returns the current value.
*/

View File

@ -312,4 +312,19 @@ public enum AnimationEquation {
* @return the new {@code t} value [0,1]
*/
public abstract float calc(float t);
public float uncalc(float x) {
float min = 0f;
float max = 1f;
// 7 iterations is already decent, do we need 8?
for (int i = 0; i < 8; i++) {
float pos = (min + max) / 2f;
if (this.calc(pos) > x) {
max = pos;
} else {
min = pos;
}
}
return (min + max) / 2f;
}
}

View File

@ -43,7 +43,7 @@ import static org.lwjgl.input.Keyboard.*;
*
* @author kevin
*/
@SuppressWarnings({"rawtypes", "unchecked", "unused"})
@SuppressWarnings({"unused"})
public class Input {
/** A helper for left ALT */
@ -98,8 +98,6 @@ public class Input {
protected char[] keys = new char[1024];
/** True if the key has been pressed since last queries */
protected boolean[] pressed = new boolean[1024];
/** The time since the next key repeat to be fired for the key */
protected long[] nextRepeat = new long[1024];
/** The listeners to notify of key events */
protected ArrayList<KeyListener> keyListeners = new ArrayList<>();
@ -113,13 +111,6 @@ public class Input {
/** True if the display is active */
private boolean displayActive = true;
/** True if key repeat is enabled */
private boolean keyRepeat;
/** The initial delay for key repeat starts */
private int keyRepeatInitial;
/** The interval of key repeat */
private int keyRepeatInterval;
/** The clicked button */
private int clickButton;
@ -344,7 +335,6 @@ public class Input {
keys[eventKey] = Keyboard.getEventCharacter();
pressed[eventKey] = true;
nextRepeat[eventKey] = System.currentTimeMillis() + keyRepeatInitial;
for (KeyListener listener : keyListeners) {
if (listener.keyPressed(eventKey, Keyboard.getEventCharacter())) {
@ -353,7 +343,6 @@ public class Input {
}
} else {
int eventKey = Keyboard.getEventKey();
nextRepeat[eventKey] = 0;
for (KeyListener listener : keyListeners) {
if (listener.keyReleased(eventKey, keys[eventKey])) {
@ -425,21 +414,6 @@ public class Input {
}
}
if (keyRepeat) {
for (int i=0;i<1024;i++) {
if (pressed[i] && (nextRepeat[i] != 0)) {
if (System.currentTimeMillis() > nextRepeat[i]) {
nextRepeat[i] = System.currentTimeMillis() + keyRepeatInterval;
for (KeyListener listener : keyListeners) {
if (listener.keyPressed(i, keys[i])) {
break;
}
}
}
}
}
}
if (Display.isCreated()) {
displayActive = Display.isActive();
}

View File

@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@ -35,7 +35,6 @@ import yugecin.opsudance.movers.factories.*;
import yugecin.opsudance.movers.slidermovers.DefaultSliderMoverController;
import yugecin.opsudance.movers.slidermovers.InheritedSliderMoverController;
import yugecin.opsudance.movers.slidermovers.SliderMoverController;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.spinners.*;
import java.awt.*;
@ -252,8 +251,8 @@ public class Dancer {
}
}
Pippi.dance(time, c, isCurrentLazySlider);
x = Utils.clamp(x, 10, displayContainer.width - 10);
y = Utils.clamp(y, 10, displayContainer.height - 10);
x = Utils.clamp(x, 10, width - 10);
y = Utils.clamp(y, 10, height - 10);
}
private void createNewMover() {

View File

@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@ -25,8 +25,8 @@ import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.DownloadNode;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.render.FrameBufferCache;
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;
@ -43,16 +43,16 @@ import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorDumpable;
import yugecin.opsudance.core.state.OpsuState;
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
import yugecin.opsudance.core.state.specialstates.BubNotifState;
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.events.SkinChangedListener;
import yugecin.opsudance.ui.BackButton;
import yugecin.opsudance.utils.GLHelper;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.Entrypoint.sout;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
@ -60,33 +60,23 @@ import static yugecin.opsudance.options.Options.*;
/**
* based on org.newdawn.slick.AppGameContainer
*/
public class DisplayContainer implements ErrorDumpable, ResolutionChangedListener, SkinChangedListener {
public class DisplayContainer implements ErrorDumpable, SkinChangedListener {
private static SGL GL = Renderer.get();
private FpsRenderState fpsState;
private BarNotificationState barNotifState;
private BubNotifState bubNotifState;
private OpsuState state;
public final DisplayMode nativeDisplayMode;
private Graphics graphics;
public int width;
public int height;
public int mouseX;
public int mouseY;
private int targetUpdatesPerSecond;
public int targetUpdateInterval;
private int targetRendersPerSecond;
public int targetRenderInterval;
public int targetBackgroundRenderInterval;
public int renderDelta;
private boolean rendering;
public int delta;
public boolean exitRequested;
@ -105,52 +95,21 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
public final Cursor cursor;
public boolean drawCursor;
class Transition {
int in;
int out;
int total;
int progress = -1;
OpsuState nextstate;
Color OVERLAY = new Color(Color.black);
private final List<ResolutionChangedListener> resolutionChangedListeners;
public void update() {
if (progress == -1) {
return;
}
progress += delta;
if (progress > out && nextstate != null) {
switchStateInstantly(nextstate);
nextstate = null;
}
if (progress > total) {
progress = -1;
}
}
private int tIn;
private int tOut;
private int tProgress = -1;
private OpsuState tNextState;
private final Color tOVERLAY = new Color(Color.black);
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() {
public DisplayContainer()
{
this.resolutionChangedListeners = new ArrayList<>();
this.cursor = new Cursor();
drawCursor = true;
ResolutionChangedListener.EVENT.addListener(this);
SkinChangedListener.EVENT.addListener(this);
skinservice.addSkinChangedListener(this);
this.nativeDisplayMode = Display.getDisplayMode();
targetBackgroundRenderInterval = 41; // ~24 fps
@ -159,10 +118,9 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
renderDelta = 1;
}
@Override
public void onResolutionChanged(int w, int h) {
destroyImages();
reinit();
public void addResolutionChangedListener(ResolutionChangedListener l)
{
this.resolutionChangedListeners.add(l);
}
@Override
@ -186,12 +144,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
}
}
backButton = new BackButton();
// TODO clean this up
GameMod.init(width, height);
PlaybackSpeed.init(width, height);
HitObject.init(width, height);
DownloadNode.init(width, height);
UI.init(this);
}
public void setUPS(int ups) {
@ -205,13 +164,9 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
}
public void init(OpsuState startingState) {
setUPS(OPTION_TARGET_UPS.val);
setUPS(targetUPS[OPTION_TARGET_UPS.val]);
setFPS(targetFPS[targetFPSIndex]);
fpsState = new FpsRenderState();
bubNotifState = new BubNotifState();
barNotifState = new BarNotificationState();
state = startingState;
state.enter();
}
@ -228,8 +183,18 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
mouseX = input.getMouseX();
mouseY = input.getMouseY();
transition.update();
fpsState.update();
// state transition
if (tProgress != -1) {
tProgress += delta;
if (tProgress > tOut && tNextState != null) {
switchStateInstantly(tNextState);
tNextState = null;
}
if (tProgress > tIn + tOut) {
tProgress = -1;
}
}
fpsDisplay.update();
state.update();
if (drawCursor) {
@ -244,6 +209,7 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
}
if (timeSinceLastRender >= maxRenderInterval) {
rendering = true;
GL.glClear(SGL.GL_COLOR_BUFFER_BIT);
/*
@ -257,32 +223,44 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
state.preRenderUpdate();
state.render(graphics);
fpsState.render(graphics);
bubNotifState.render(graphics);
barNotifState.render(graphics);
fpsDisplay.render(graphics);
bubNotifs.render(graphics);
barNotifs.render(graphics);
cursor.updateAngle(renderDelta);
cursor.updateAngle();
if (drawCursor) {
cursor.draw(Mouse.isButtonDown(Input.MOUSE_LEFT_BUTTON) ||
Mouse.isButtonDown(Input.MOUSE_RIGHT_BUTTON));
}
UI.drawTooltip(graphics);
transition.render(graphics);
// transition
if (tProgress != -1) {
if (tProgress > tOut) {
tOVERLAY.a = 1f - (tProgress - tOut) / (float) tIn;
} else {
tOVERLAY.a = tProgress / (float) tOut;
}
graphics.setColor(tOVERLAY);
graphics.fillRect(0, 0, width, height);
}
timeSinceLastRender = 0;
Display.update(false);
GL11.glFlush();
rendering = false;
}
Display.processMessages();
if (targetUpdatesPerSecond >= 60) {
Display.sync(targetUpdatesPerSecond);
}
}
}
public void setup() throws Exception {
width = height = -1;
width = height = width2 = height2 = -1;
Display.setTitle("opsu!dance");
setupResolutionOptionlist(nativeDisplayMode.getWidth(), nativeDisplayMode.getHeight());
updateDisplayMode(OPTION_SCREEN_RESOLUTION.getValueString());
@ -318,6 +296,7 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
GameImage.destroyImages();
GameData.Grade.destroyImages();
Beatmap.destroyBackgroundImageCache();
FrameBufferCache.shutdown();
}
public void teardownAL() {
@ -342,13 +321,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
return true;
}
if (DownloadList.get().hasActiveDownloads()) {
BubNotifListener.EVENT.make().onBubNotif(DownloadList.EXIT_CONFIRMATION, Colors.BUB_RED);
bubNotifs.send(BUB_RED, DownloadList.EXIT_CONFIRMATION);
exitRequested = false;
exitconfirmation = System.currentTimeMillis();
return false;
}
if (updater.getStatus() == Updater.Status.UPDATE_DOWNLOADING) {
BubNotifListener.EVENT.make().onBubNotif(Updater.EXIT_CONFIRMATION, Colors.BUB_PURPLE);
bubNotifs.send(BUB_PURPLE, Updater.EXIT_CONFIRMATION);
exitRequested = false;
exitconfirmation = System.currentTimeMillis();
return false;
@ -373,6 +352,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
height = Integer.parseInt(res[1]);
}
updateDisplayMode(width, height);
}
public void updateDisplayMode(int width, int height) {
int screenWidth = nativeDisplayMode.getWidth();
int screenHeight = nativeDisplayMode.getHeight();
// check for larger-than-screen dimensions
if (!OPTION_ALLOW_LARGER_RESOLUTIONS.state && (screenWidth < width || screenHeight < height)) {
width = 800;
@ -387,34 +373,35 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
try {
setDisplayMode(width, height, OPTION_FULLSCREEN.state);
} catch (Exception e) {
BubNotifListener.EVENT.make().onBubNotif("Failed to change resolution", Colors.BUB_RED);
bubNotifs.send(BUB_RED, "Failed to change display mode");
Log.error("Failed to set display mode.", e);
}
}
public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception {
if (this.width == width && this.height == height) {
Display.setFullscreen(fullscreen);
return;
}
public void setDisplayMode(int w, int h, boolean fullscreen) throws Exception {
DisplayMode displayMode = null;
if (fullscreen) {
displayMode = GLHelper.findFullscreenDisplayMode(nativeDisplayMode.getBitsPerPixel(), nativeDisplayMode.getFrequency(), width, height);
final int bpp = this.nativeDisplayMode.getBitsPerPixel();
final int freq = this.nativeDisplayMode.getFrequency();
displayMode = GLHelper.findFullscreenDisplayMode(bpp, freq, w, h);
}
if (displayMode == null) {
displayMode = new DisplayMode(width, height);
displayMode = new DisplayMode(w, h);
if (fullscreen) {
fullscreen = false;
String msg = String.format("Fullscreen mode is not supported for %sx%s", width, height);
String msg = "Fullscreen mode is not supported for %sx%s";
msg = String.format(msg, w, h);
Log.warn(msg);
BubNotifListener.EVENT.make().onBubNotif(msg, Colors.BUB_ORANGE);
bubNotifs.send(BUB_ORANGE, msg);
}
}
this.width = displayMode.getWidth();
this.height = displayMode.getHeight();
width = displayMode.getWidth();
height = displayMode.getHeight();
width2 = width / 2;
height2 = height / 2;
isWidescreen = width * 1000 / height > 1500; // 1777 = 16:9, 1333 = 4:3
Display.setDisplayMode(displayMode);
Display.setFullscreen(fullscreen);
@ -439,7 +426,7 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
input = new Input(height);
input.enableKeyRepeat();
input.addListener(new GlobalInputListener());
input.addMouseListener(bubNotifState);
input.addMouseListener(bubNotifs);
}
input.addListener(state);
@ -448,7 +435,15 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
GameImage.init(width, height);
Fonts.init();
ResolutionChangedListener.EVENT.make().onResolutionChanged(width, height);
destroyImages();
reinit();
barNotifs.onResolutionChanged(width, height);
bubNotifs.onResolutionChanged(width, height);
fpsDisplay.onResolutionChanged(width, height);
for (ResolutionChangedListener l : this.resolutionChangedListeners) {
l.onResolutionChanged(width, height);
}
}
public void resetCursor() {
@ -466,10 +461,6 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
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");
@ -487,22 +478,32 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
}
public void switchState(OpsuState state) {
switchState(state, 200, 300);
switchState(state, 150, 250);
}
public void switchState(OpsuState newstate, int outtime, int intime) {
if (transition.progress != -1) {
if (tProgress != -1 && tProgress <= tOut) {
return;
}
if (outtime == 0) {
switchStateInstantly(newstate);
newstate = null;
} else {
input.removeListener(this.state);
}
transition.nextstate = newstate;
transition.total = transition.in = intime;
transition.out = outtime;
transition.total += outtime;
transition.progress = 0;
if (tProgress == -1) {
tProgress = 0;
} else {
// we were in a transition (out state), so start from the time
// that was already spent transitioning in
tProgress = (int) (((1f - (tProgress - tOut) / (float) tIn)) * outtime);
}
tNextState = newstate;
tIn = intime;
tOut = outtime;
}
public void switchStateInstantly(OpsuState state) {
@ -511,6 +512,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
this.state = state;
this.state.enter();
input.addListener(this.state);
backButton.resetHover();
if (this.rendering) {
// state might be changed in preRenderUpdate,
// in that case the new state will be rendered without having
// preRenderUpdate being called first, so do that now
this.state.preRenderUpdate();
}
}
}

View File

@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@ -21,7 +21,6 @@ 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.*;
@ -38,8 +37,8 @@ public class GlobalInputListener implements InputListener {
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()));
final String value = OPTION_TARGET_FPS.getValueString();
barNotifs.sendf("Frame limiter: %s", value);
return true;
}
if (key == KEY_F10) {

View File

@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@ -26,11 +26,17 @@ import itdelatrisu.opsu.states.*;
import org.newdawn.slick.Input;
import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
import yugecin.opsudance.core.state.specialstates.BubNotifState;
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionGroups;
import yugecin.opsudance.options.OptionsService;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.ManifestWrapper;
import yugecin.opsudance.ui.BackButton;
import yugecin.opsudance.ui.OptionsOverlay;
import java.io.File;
import java.io.IOException;
@ -51,11 +57,18 @@ public class InstanceContainer {
public static BeatmapParser beatmapParser;
public static Updater updater;
public static BackButton backButton;
public static DisplayContainer displayContainer;
public static Input input;
public static GameObjectRenderer gameObjectRenderer;
public static BarNotificationState barNotifs;
public static BubNotifState bubNotifs;
public static FpsRenderState fpsDisplay;
public static OptionsOverlay optionsOverlay;
public static Splash splashState;
public static MainMenu mainmenuState;
public static ButtonMenu buttonState;
@ -65,16 +78,20 @@ public class InstanceContainer {
public static GameRanking gameRankingState;
public static GamePauseMenu pauseState;
public static int width, width2, height, height2;
public static boolean isWidescreen;
public static int mouseX, mouseY;
public static int renderDelta;
public static void kickstart() {
updater = new Updater();
env = new Environment();
JarFile jarfile = getJarfile();
ManifestWrapper manifest = new ManifestWrapper(getJarManifest(jarfile));
config = new Configuration(manifest);
config = new Configuration();
if (jarfile != null) {
try {
NativeLoader.loadNatives(jarfile, manifest);
NativeLoader.loadNatives(jarfile);
} catch (IOException e) {
String msg = String.format("Could not unpack native(s): %s", e.getMessage());
throw new RuntimeException(msg, e);
@ -95,8 +112,14 @@ public class InstanceContainer {
displayContainer = new DisplayContainer();
barNotifs = new BarNotificationState();
bubNotifs = new BubNotifState();
fpsDisplay = new FpsRenderState();
gameObjectRenderer = new GameObjectRenderer();
optionsOverlay = new OptionsOverlay(OptionGroups.normalOptions);
splashState = new Splash();
mainmenuState = new MainMenu();
buttonState = new ButtonMenu();

View File

@ -24,6 +24,11 @@ import yugecin.opsudance.utils.MiscUtils;
import javax.swing.*;
import java.awt.Desktop;
import java.awt.Dialog.ModalityType;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@ -94,30 +99,15 @@ public class ErrorHandler {
}
JLabel message = new JLabel(messageText);
JTextArea textArea = new JTextArea(15, 100);
textArea.setEditable(false);
textArea.setBackground(UIManager.getColor("Panel.background"));
textArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
textArea.setTabSize(2);
textArea.setLineWrap(false);
textArea.setWrapStyleWord(true);
textArea.setText(messageBody);
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);
}
Window parent = SwingUtilities.getWindowAncestor(message);
showCreateIssueDialog(parent, errorDump, customMessage, cause);
}
};
Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(),
Object[] messageComponents = new Object[] { message, readonlyTextarea(messageBody), createViewLogButton(),
createReportButton(flags, reportAction) };
String[] buttons;
@ -182,7 +172,70 @@ public class ErrorHandler {
return button;
}
private static URI createGithubIssueUrl(String customMessage, Throwable cause, String errorDump) {
private static void showCreateIssueDialog(
Window parent,
String errorDump,
String customMessage,
Throwable cause)
{
final String dump = createIssueDump(customMessage, cause, errorDump);
final String title = "report error";
JDialog d = new JDialog(parent, title, ModalityType.APPLICATION_MODAL);
d.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.gridx = 1;
c.gridy = 1;
c.weightx = 1d;
c.weighty = 0d;
c.fill = GridBagConstraints.BOTH;
c.insets = new Insets(4, 8, 4, 8);
d.add(new JLabel(
"<html>Copy the text in the box below.<br/>"
+ "Then click the button below.<br/>"
+ "Your browser should open a page where you can report the issue.<br/>"
+ "Please paste the dump below in the issue box."
), c);
c.gridy++;
c.weighty = 1d;
d.add(readonlyTextarea(dump), c);
c.gridy++;
c.weighty = c.weightx = 0d;
c.fill = GridBagConstraints.NONE;
JButton btn = new JButton("Report");
btn.addActionListener(e -> {
try {
URI url = createGithubIssueUrl(customMessage, cause);
Desktop.getDesktop().browse(url);
d.dispose();
} catch (IOException t) {
Log.warn("Could not open browser to report issue", t);
JOptionPane.showMessageDialog(null, "whoops could not launch a browser",
"errorception", JOptionPane.ERROR_MESSAGE);
}
});
d.add(btn, c);
d.pack();
d.setLocationRelativeTo(parent);
d.setVisible(true);
}
private static URI createGithubIssueUrl(String customMessage, Throwable cause) {
String issueTitle = "";
String issueBody = "";
try {
issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + ": " +
customMessage, "UTF-8");
issueBody = URLEncoder.encode("PASTE THE DUMP HERE", "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e);
}
return URI.create(String.format(Constants.ISSUES_URL, issueTitle, issueBody));
}
private static String createIssueDump(String customMessage, Throwable cause, String errorDump) {
StringWriter dump = new StringWriter();
dump.append(customMessage).append("\n");
@ -203,25 +256,19 @@ public class ErrorHandler {
dump.append("**info dump**").append('\n');
dump.append("```\n").append(errorDump).append("\n```\n\n");
String issueTitle = "";
String issueBody = "";
try {
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(Constants.ISSUES_URL, issueTitle, issueBody));
return dump.toString();
}
private static String truncateGithubIssueBody(String body) {
if (body.replaceAll("[^a-zA-Z+-]", "").length() < 1750) {
return body;
private static JComponent readonlyTextarea(String contents) {
JTextArea textArea = new JTextArea(15, 100);
textArea.setEditable(false);
textArea.setBackground(UIManager.getColor("Panel.background"));
textArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
textArea.setTabSize(2);
textArea.setLineWrap(false);
textArea.setWrapStyleWord(true);
textArea.setFont(new JLabel().getFont());
textArea.setText(contents);
return new JScrollPane(textArea);
}
Log.warn("error dump too long to fit into github issue url, truncating");
return body.substring(0, 1640) + "** TRUNCATED **\n```";
}
}

View File

@ -1,53 +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.events;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedList;
@SuppressWarnings("unchecked")
public class Event<T> {
private final Class<T> type;
private final LinkedList<T> listeners;
public Event(Class<T> type) {
this.type = type;
this.listeners = new LinkedList<>();
}
public void addListener(T listener) {
this.listeners.add(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

@ -1,25 +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.inject;
public interface Binder<T> {
void asEagerSingleton();
void asLazySingleton();
void to(Class<? extends T> type);
}

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