Merge branch 'reorganise'
This commit is contained in:
commit
d311f8bb95
25
README.md
25
README.md
|
@ -1,23 +1,22 @@
|
|||
#opsu!dance
|
||||
[example video](https://www.youtube.com/watch?v=QvXWRMg1gwE)
|
||||
[example video](https://www.youtube.com/watch?v=tqZqn7nx8N0)
|
||||
|
||||
fork of [opsu!](https://github.com/itdelatrisu/opsu) with cursordancing auto.
|
||||
I make cursordancing video's -[example](https://www.youtube.com/watch?v=1oFH58X_lTY)-, but I have too many requests and this way I can give people the opportunity to just run it and see the result instead of waiting for me to make a video. Original bot is written in C# and videos are made on osu!, but I don't want to distribute that program, because I don't want to endorse cheating in any way. The sources found in this repo are very representative of the ones of my bot (most files are the exact same, except for C#/java changes).
|
||||
Originally started as a fork of [opsu!](https://github.com/itdelatrisu/opsu) with cursordance stuff. I made a cursordancing bot in C# for osu!, and by adding it into this clone, it allows me to do even more stuff with it. This way I can also provide this client to other players so they can play with it too, as I will not give my bot to people because I don't want to endorse cheating in any way.
|
||||
|
||||
As of 2017 some major changes were made in this fork which changed the inner workings of opsu. This was done in an attempt to make the code more organised (for me, my subjective opinion) and allowed a.o. changing resolution and skin at runtime without the need to restart the whole system. This fork was pretty even to opsu! before this change, but now there are way more differences.
|
||||
|
||||
My goal is to to add cool cursordancing things to this fork, but also make it possible to play the normal way.
|
||||
|
||||
###Downloads
|
||||
Click on the releases link (scroll up) to go to the downloadpage with prebuilt jars.
|
||||
|
||||
###Building
|
||||
You can find general (run/build) instructions in the original [opsu! README](README-OPSU.md).
|
||||
|
||||
###Making videos
|
||||
You can make videos under following conditions:
|
||||
|
||||
* You will wait a reasonable amount of time between making videos, making too much too fast means it won't be entertaining anymore.
|
||||
* You will not deny you used opsu!dance to make your video
|
||||
* You may provide a link to this repository. This is always very appreciated. Please do keep in mind I only made some adjustements/fixes and added the dancing stuff. opsu! was made by [@itdelatrisu](https://github.com/itdelatrisu) (see credits below)
|
||||
* Asking for beatmap requests is discouraged. After all, everyone can download this and see it for themselves.
|
||||
* __YOU WILL NOT PRETEND LIKE YOU MADE/OWN THIS SOFTWARE__
|
||||
Please note that I am only using maven and gradle scripts are not being updated.
|
||||
|
||||
###Credits
|
||||
opsu! was made by Jeffrey Han ([@itdelatrisu](https://github.com/itdelatrisu)). All game concepts and designs are based on work by osu! developer Dean Herbert. Other opsu! credits can be found [here](CREDITS.md).
|
||||
opsu!dance (everything in the src package yugecin.opsudance) was made by me ([@yugecin](https://github.com/yugecin)). Originally in C#, now ported to java. Some edits were made in the opsu! sources.
|
||||
opsu!dance (everything in the src package yugecin.opsudance) was made by me ([@yugecin](https://github.com/yugecin)). Eits were made in the opsu! sources, too.
|
||||
|
||||
###License
|
||||
**This software is licensed under GNU GPL version 3.**
|
||||
|
|
109
build.gradle
109
build.gradle
|
@ -1,109 +0,0 @@
|
|||
apply plugin: 'java'
|
||||
apply plugin: 'maven'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'application'
|
||||
|
||||
import org.apache.tools.ant.filters.*
|
||||
|
||||
group = 'itdelatrisu'
|
||||
version = '0.12.1'
|
||||
|
||||
mainClassName = 'itdelatrisu.opsu.Opsu'
|
||||
buildDir = new File(rootProject.projectDir, "build/")
|
||||
|
||||
def useXDG = 'false'
|
||||
if (hasProperty('XDG')) {
|
||||
useXDG = XDG
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.7
|
||||
targetCompatibility = 1.7
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir 'src'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile('org.lwjgl.lwjgl:lwjgl:2.9.3') {
|
||||
exclude group: 'net.java.jinput', module: 'jinput'
|
||||
}
|
||||
compile('org.slick2d:slick2d-core:1.0.1') {
|
||||
exclude group: 'org.lwjgl.lwjgl', module: 'lwjgl'
|
||||
}
|
||||
compile 'org.jcraft:jorbis:0.0.17'
|
||||
compile 'net.lingala.zip4j:zip4j:1.3.2'
|
||||
compile 'com.googlecode.soundlibs:jlayer:1.0.1-1'
|
||||
compile 'com.googlecode.soundlibs:mp3spi:1.9.5-1'
|
||||
compile 'com.googlecode.soundlibs:tritonus-share:0.3.7-2'
|
||||
compile 'org.xerial:sqlite-jdbc:3.8.10.2'
|
||||
compile 'org.json:json:20140107'
|
||||
compile 'net.java.dev.jna:jna:4.1.0'
|
||||
compile 'net.java.dev.jna:jna-platform:4.1.0'
|
||||
compile 'org.apache.maven:maven-artifact:3.3.3'
|
||||
compile 'org.apache.commons:commons-compress:1.9'
|
||||
compile 'org.tukaani:xz:1.5'
|
||||
compile 'com.github.jponge:lzma-java:1.3'
|
||||
}
|
||||
|
||||
def nativePlatforms = ['windows', 'linux', 'osx']
|
||||
nativePlatforms.each { platform -> //noinspection GroovyAssignabilityCheck
|
||||
task "${platform}Natives" {
|
||||
def outputDir = "${buildDir}/natives/"
|
||||
inputs.files(configurations.compile)
|
||||
outputs.dir(outputDir)
|
||||
doLast {
|
||||
copy {
|
||||
def artifacts = configurations.compile.resolvedConfiguration.resolvedArtifacts
|
||||
.findAll { it.classifier == "natives-$platform" }
|
||||
artifacts.each {
|
||||
from zipTree(it.file)
|
||||
}
|
||||
into outputDir
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
from 'res'
|
||||
exclude '**/Thumbs.db'
|
||||
|
||||
filesMatching('version') {
|
||||
expand(version: project.version, timestamp: new Date().format("yyyy-MM-dd HH:mm"))
|
||||
}
|
||||
}
|
||||
|
||||
task unpackNatives {
|
||||
description "Copies native libraries to the build directory."
|
||||
dependsOn nativePlatforms.collect { "${it}Natives" }.findAll { tasks[it] }
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes 'Implementation-Title': 'opsu!',
|
||||
'Implementation-Version': version,
|
||||
'Main-Class': mainClassName,
|
||||
'Use-XDG': useXDG
|
||||
}
|
||||
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
baseName = "opsu"
|
||||
|
||||
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||
exclude '**/Thumbs.db'
|
||||
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
run {
|
||||
dependsOn 'unpackNatives'
|
||||
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
6
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +0,0 @@
|
|||
#Tue Aug 25 19:45:21 BST 2015
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip
|
164
gradlew
vendored
164
gradlew
vendored
|
@ -1,164 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
90
gradlew.bat
vendored
90
gradlew.bat
vendored
|
@ -1,90 +0,0 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
4
pom.xml
4
pom.xml
|
@ -3,12 +3,12 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>yugecin</groupId>
|
||||
<artifactId>opsu-dance</artifactId>
|
||||
<version>0.4.2</version>
|
||||
<version>0.5.0-SNAPSHOT</version>
|
||||
<properties>
|
||||
<version>${project.version}</version>
|
||||
<timestamp>${maven.build.timestamp}</timestamp>
|
||||
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
|
||||
<mainClassName>itdelatrisu.opsu.Opsu</mainClassName>
|
||||
<mainClassName>yugecin.opsudance.core.Entrypoint</mainClassName>
|
||||
<XDG>false</XDG>
|
||||
</properties>
|
||||
<build>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
rootProject.name = 'opsu'
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* opsu! - an open-source osu! client
|
||||
* Copyright (C) 2014, 2015 Jeffrey Han
|
||||
*
|
||||
* opsu! is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu! is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package itdelatrisu.opsu;
|
||||
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.audio.SoundController;
|
||||
import itdelatrisu.opsu.beatmap.Beatmap;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapGroup;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapSetList;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapSortOrder;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapWatchService;
|
||||
import itdelatrisu.opsu.downloads.DownloadList;
|
||||
import itdelatrisu.opsu.downloads.Updater;
|
||||
import itdelatrisu.opsu.render.CurveRenderState;
|
||||
import itdelatrisu.opsu.ui.UI;
|
||||
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.newdawn.slick.AppGameContainer;
|
||||
import org.newdawn.slick.Game;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.opengl.InternalTextureLoader;
|
||||
|
||||
/**
|
||||
* AppGameContainer extension that sends critical errors to ErrorHandler.
|
||||
*/
|
||||
public class Container extends AppGameContainer {
|
||||
/** Exception causing game failure. */
|
||||
protected Exception e = null;
|
||||
|
||||
public static Container instance;
|
||||
|
||||
/**
|
||||
* Create a new container wrapping a game
|
||||
*
|
||||
* @param game The game to be wrapped
|
||||
* @throws SlickException Indicates a failure to initialise the display
|
||||
*/
|
||||
public Container(Game game) throws SlickException {
|
||||
super(game);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new container wrapping a game
|
||||
*
|
||||
* @param game The game to be wrapped
|
||||
* @param width The width of the display required
|
||||
* @param height The height of the display required
|
||||
* @param fullscreen True if we want fullscreen mode
|
||||
* @throws SlickException Indicates a failure to initialise the display
|
||||
*/
|
||||
public Container(Game game, int width, int height, boolean fullscreen) throws SlickException {
|
||||
super(game, width, height, fullscreen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws SlickException {
|
||||
try {
|
||||
setup();
|
||||
ErrorHandler.setGlString();
|
||||
getDelta();
|
||||
while (running())
|
||||
gameLoop();
|
||||
} catch (Exception e) {
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
// destroy the game container
|
||||
try {
|
||||
close_sub();
|
||||
} catch (Exception e) {
|
||||
if (this.e == null) // suppress if caused by a previous exception
|
||||
this.e = e;
|
||||
}
|
||||
destroy();
|
||||
|
||||
// report any critical errors
|
||||
if (e != null) {
|
||||
ErrorHandler.error(null, e, true);
|
||||
e = null;
|
||||
forceExit = true;
|
||||
}
|
||||
|
||||
if (forceExit) {
|
||||
Opsu.close();
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void gameLoop() throws SlickException {
|
||||
int delta = getDelta();
|
||||
if (!Display.isVisible() && updateOnlyOnVisible) {
|
||||
try { Thread.sleep(100); } catch (Exception e) {}
|
||||
} else {
|
||||
try {
|
||||
updateAndRender(delta);
|
||||
} catch (SlickException e) {
|
||||
this.e = e; // store exception to display later
|
||||
running = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
updateFPS();
|
||||
Display.update();
|
||||
if (Display.isCloseRequested()) {
|
||||
if (game.closeRequested())
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions to perform before destroying the game container.
|
||||
*/
|
||||
private void close_sub() {
|
||||
// save user options
|
||||
Options.saveOptions();
|
||||
|
||||
// reset cursor
|
||||
UI.getCursor().reset();
|
||||
|
||||
// destroy images
|
||||
InternalTextureLoader.get().clear();
|
||||
|
||||
// reset image references
|
||||
GameImage.clearReferences();
|
||||
GameData.Grade.clearReferences();
|
||||
Beatmap.clearBackgroundImageCache();
|
||||
|
||||
// prevent loading tracks from re-initializing OpenAL
|
||||
MusicController.reset();
|
||||
|
||||
// stop any playing track
|
||||
SoundController.stopTrack();
|
||||
|
||||
// reset BeatmapSetList data
|
||||
BeatmapGroup.set(BeatmapGroup.ALL);
|
||||
BeatmapSortOrder.set(BeatmapSortOrder.TITLE);
|
||||
if (BeatmapSetList.get() != null)
|
||||
BeatmapSetList.get().reset();
|
||||
|
||||
// delete OpenGL objects involved in the Curve rendering
|
||||
CurveRenderState.shutdown();
|
||||
|
||||
// destroy watch service
|
||||
if (!Options.isWatchServiceEnabled())
|
||||
BeatmapWatchService.destroy();
|
||||
BeatmapWatchService.removeListeners();
|
||||
|
||||
// delete temporary directory
|
||||
Utils.deleteDirectory(Options.TEMP_DIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit() {
|
||||
// show confirmation dialog if any downloads are active
|
||||
if (forceExit) {
|
||||
if (DownloadList.get().hasActiveDownloads() &&
|
||||
UI.showExitConfirmation(DownloadList.EXIT_CONFIRMATION))
|
||||
return;
|
||||
if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING &&
|
||||
UI.showExitConfirmation(Updater.EXIT_CONFIRMATION))
|
||||
return;
|
||||
}
|
||||
|
||||
super.exit();
|
||||
}
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
/*
|
||||
* opsu! - an open-source osu! client
|
||||
* Copyright (C) 2014, 2015 Jeffrey Han
|
||||
*
|
||||
* opsu! is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu! is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package itdelatrisu.opsu;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Desktop;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
/**
|
||||
* Error handler to log and display errors.
|
||||
*/
|
||||
public class ErrorHandler {
|
||||
/** Error popup title. */
|
||||
private static final String title = "Error";
|
||||
|
||||
/** Error popup description text. */
|
||||
private static final String
|
||||
desc = "opsu! has encountered an error.",
|
||||
descReport = "opsu! has encountered an error. Please report this!";
|
||||
|
||||
/** Error popup button options. */
|
||||
private static final String[]
|
||||
optionsLog = {"View Error Log", "Close"},
|
||||
optionsReport = {"Send Report", "Close"},
|
||||
optionsLogReport = {"Send Report", "View Error Log", "Close"};
|
||||
|
||||
/** Text area for Exception. */
|
||||
private static final JTextArea textArea = new JTextArea(15, 100);
|
||||
static {
|
||||
textArea.setEditable(false);
|
||||
textArea.setBackground(UIManager.getColor("Panel.background"));
|
||||
textArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
|
||||
textArea.setTabSize(2);
|
||||
textArea.setLineWrap(false);
|
||||
textArea.setWrapStyleWord(true);
|
||||
}
|
||||
|
||||
/** Scroll pane holding JTextArea. */
|
||||
private static final JScrollPane scroll = new JScrollPane(textArea);
|
||||
|
||||
/** Error popup objects. */
|
||||
private static final Object[]
|
||||
message = { desc, scroll },
|
||||
messageReport = { descReport, scroll };
|
||||
|
||||
/** OpenGL string (if any). */
|
||||
private static String glString = null;
|
||||
|
||||
// This class should not be instantiated.
|
||||
private ErrorHandler() {}
|
||||
|
||||
/**
|
||||
* Sets the OpenGL version string.
|
||||
*/
|
||||
public static void setGlString() {
|
||||
try {
|
||||
String glVersion = GL11.glGetString(GL11.GL_VERSION);
|
||||
String glVendor = GL11.glGetString(GL11.GL_VENDOR);
|
||||
glString = String.format("%s (%s)", glVersion, glVendor);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an error popup and logs the given error.
|
||||
* @param error a description of the error
|
||||
* @param e the exception causing the error
|
||||
* @param report whether to ask to report the error
|
||||
*/
|
||||
public static void error(String error, Throwable e, boolean report) {
|
||||
if (error == null && e == null)
|
||||
return;
|
||||
|
||||
// log the error
|
||||
if (error == null)
|
||||
Log.error(e);
|
||||
else if (e == null)
|
||||
Log.error(error);
|
||||
else
|
||||
Log.error(error, e);
|
||||
|
||||
// set the textArea to the error message
|
||||
textArea.setText(null);
|
||||
if (error != null) {
|
||||
textArea.append(error);
|
||||
textArea.append("\n");
|
||||
}
|
||||
String trace = null;
|
||||
if (e != null) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
trace = sw.toString();
|
||||
textArea.append(trace);
|
||||
}
|
||||
|
||||
// display popup
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
Desktop desktop = null;
|
||||
boolean isBrowseSupported = false, isOpenSupported = false;
|
||||
if (Desktop.isDesktopSupported()) {
|
||||
desktop = Desktop.getDesktop();
|
||||
isBrowseSupported = desktop.isSupported(Desktop.Action.BROWSE);
|
||||
isOpenSupported = desktop.isSupported(Desktop.Action.OPEN);
|
||||
}
|
||||
if (desktop != null && (isOpenSupported || (report && isBrowseSupported))) { // try to open the log file and/or issues webpage
|
||||
if (report && isBrowseSupported) { // ask to report the error
|
||||
if (isOpenSupported) { // also ask to open the log
|
||||
int n = JOptionPane.showOptionDialog(null, messageReport, title,
|
||||
JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE,
|
||||
null, optionsLogReport, optionsLogReport[2]);
|
||||
if (n == 0)
|
||||
desktop.browse(getIssueURI(error, e, trace));
|
||||
else if (n == 1)
|
||||
desktop.open(Options.LOG_FILE);
|
||||
} else { // only ask to report the error
|
||||
int n = JOptionPane.showOptionDialog(null, message, title,
|
||||
JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE,
|
||||
null, optionsReport, optionsReport[1]);
|
||||
if (n == 0)
|
||||
desktop.browse(getIssueURI(error, e, trace));
|
||||
}
|
||||
} else { // don't report the error
|
||||
int n = JOptionPane.showOptionDialog(null, message, title,
|
||||
JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE,
|
||||
null, optionsLog, optionsLog[1]);
|
||||
if (n == 0)
|
||||
desktop.open(Options.LOG_FILE);
|
||||
}
|
||||
} else { // display error only
|
||||
JOptionPane.showMessageDialog(null, report ? messageReport : message,
|
||||
title, JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
Log.error("An error occurred in the crash popup.", e1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the issue reporting URI.
|
||||
* This will auto-fill the report with the relevant information if possible.
|
||||
* @param error a description of the error
|
||||
* @param e the exception causing the error
|
||||
* @param trace the stack trace
|
||||
* @return the created URI
|
||||
*/
|
||||
private static URI getIssueURI(String error, Throwable e, String trace) {
|
||||
// generate report information
|
||||
String issueTitle = (error != null) ? error : e.getMessage();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
// read version and build date from version file, if possible
|
||||
Properties props = new Properties();
|
||||
props.load(ResourceLoader.getResourceAsStream(Options.VERSION_FILE));
|
||||
String version = props.getProperty("version");
|
||||
if (version != null && !version.equals("${pom.version}")) {
|
||||
sb.append("**Version:** ");
|
||||
sb.append(version);
|
||||
String hash = Utils.getGitHash();
|
||||
if (hash != null) {
|
||||
sb.append(" (");
|
||||
sb.append(hash.substring(0, 12));
|
||||
sb.append(')');
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
String timestamp = props.getProperty("build.date");
|
||||
if (timestamp != null &&
|
||||
!timestamp.equals("${maven.build.timestamp}") && !timestamp.equals("${timestamp}")) {
|
||||
sb.append("**Build date:** ");
|
||||
sb.append(timestamp);
|
||||
sb.append('\n');
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
Log.warn("Could not read version file.", e1);
|
||||
}
|
||||
sb.append("**OS:** ");
|
||||
sb.append(System.getProperty("os.name"));
|
||||
sb.append(" (");
|
||||
sb.append(System.getProperty("os.arch"));
|
||||
sb.append(")\n");
|
||||
sb.append("**JRE:** ");
|
||||
sb.append(System.getProperty("java.version"));
|
||||
sb.append('\n');
|
||||
if (glString != null) {
|
||||
sb.append("**OpenGL Version:** ");
|
||||
sb.append(glString);
|
||||
sb.append('\n');
|
||||
}
|
||||
if (error != null) {
|
||||
sb.append("**Error:** `");
|
||||
sb.append(error);
|
||||
sb.append("`\n");
|
||||
}
|
||||
if (trace != null) {
|
||||
sb.append("**Stack trace:**");
|
||||
sb.append("\n```\n");
|
||||
sb.append(trace);
|
||||
sb.append("```");
|
||||
}
|
||||
|
||||
// return auto-filled URI
|
||||
try {
|
||||
return URI.create(String.format(Options.ISSUES_URL,
|
||||
URLEncoder.encode(issueTitle, "UTF-8"),
|
||||
URLEncoder.encode(sb.toString(), "UTF-8")));
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
Log.warn("URLEncoder failed to encode the auto-filled issue report URL.");
|
||||
return URI.create(String.format(Options.ISSUES_URL, "", ""));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ import org.newdawn.slick.Animation;
|
|||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import yugecin.opsudance.Dancer;
|
||||
import yugecin.opsudance.utils.SlickUtil;
|
||||
|
||||
/**
|
||||
* Holds game data and renders all related elements.
|
||||
|
@ -101,9 +101,17 @@ public class GameData {
|
|||
* This does NOT destroy images, so be careful of memory leaks!
|
||||
*/
|
||||
public static void clearReferences() {
|
||||
for (Grade grade : Grade.values())
|
||||
for (Grade grade : Grade.values()) {
|
||||
grade.menuImage = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void destroyImages() {
|
||||
for (Grade grade : Grade.values()) {
|
||||
SlickUtil.destroyImage(grade.menuImage);
|
||||
grade.menuImage = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
|
|
@ -27,7 +27,12 @@ import java.util.List;
|
|||
import org.newdawn.slick.Animation;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
import yugecin.opsudance.utils.SlickUtil;
|
||||
|
||||
/**
|
||||
* Game images.
|
||||
|
@ -461,6 +466,18 @@ public enum GameImage {
|
|||
}
|
||||
}
|
||||
|
||||
public static void destroyImages() {
|
||||
for (GameImage img : GameImage.values()) {
|
||||
SlickUtil.destroyImages(img.defaultImages);
|
||||
SlickUtil.destroyImage(img.defaultImage);
|
||||
SlickUtil.destroyImages(img.skinImages);
|
||||
SlickUtil.destroyImage(img.skinImage);
|
||||
img.isSkinned = false;
|
||||
img.defaultImages = img.skinImages = null;
|
||||
img.defaultImage = img.skinImage = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bitmask image type from a type string.
|
||||
* @param type the type string
|
||||
|
@ -693,7 +710,9 @@ public enum GameImage {
|
|||
return;
|
||||
}
|
||||
|
||||
ErrorHandler.error(String.format("Could not find default image '%s'.", filename), null, false);
|
||||
String err = String.format("Could not find default image '%s'.", filename);
|
||||
Log.warn(err);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -752,7 +771,7 @@ public enum GameImage {
|
|||
img = img.getScaledCopy(0.5f);
|
||||
list.add(img);
|
||||
} catch (SlickException e) {
|
||||
ErrorHandler.error(String.format("Failed to set image '%s'.", name), null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", name), BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -766,7 +785,7 @@ public enum GameImage {
|
|||
img = img.getScaledCopy(0.5f);
|
||||
list.add(img);
|
||||
} catch (SlickException e) {
|
||||
ErrorHandler.error(String.format("Failed to set image '%s'.", name), null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", name), BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -793,7 +812,7 @@ public enum GameImage {
|
|||
img = img.getScaledCopy(0.5f);
|
||||
return img;
|
||||
} catch (SlickException e) {
|
||||
ErrorHandler.error(String.format("Failed to set image '%s'.", filename), null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", filename), BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -838,7 +857,7 @@ public enum GameImage {
|
|||
skinImages = null;
|
||||
}
|
||||
} catch (SlickException e) {
|
||||
ErrorHandler.error(String.format("Failed to destroy beatmap skin images for '%s'.", this.name()), e, true);
|
||||
ErrorHandler.error(String.format("Failed to destroy beatmap skin images for '%s'.", this.name()), e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,309 +0,0 @@
|
|||
/*
|
||||
* opsu! - an open-source osu! client
|
||||
* Copyright (C) 2014, 2015 Jeffrey Han
|
||||
*
|
||||
* opsu! is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu! is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package itdelatrisu.opsu;
|
||||
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.db.DBController;
|
||||
import itdelatrisu.opsu.downloads.DownloadList;
|
||||
import itdelatrisu.opsu.downloads.Updater;
|
||||
import itdelatrisu.opsu.states.ButtonMenu;
|
||||
import itdelatrisu.opsu.states.DownloadsMenu;
|
||||
import itdelatrisu.opsu.states.Game;
|
||||
import itdelatrisu.opsu.states.GamePauseMenu;
|
||||
import itdelatrisu.opsu.states.GameRanking;
|
||||
import itdelatrisu.opsu.states.MainMenu;
|
||||
import itdelatrisu.opsu.states.OptionsMenu;
|
||||
import itdelatrisu.opsu.states.SongMenu;
|
||||
import itdelatrisu.opsu.states.Splash;
|
||||
import itdelatrisu.opsu.ui.UI;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import org.newdawn.slick.state.transition.FadeInTransition;
|
||||
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
|
||||
import org.newdawn.slick.util.DefaultLogSystem;
|
||||
import org.newdawn.slick.util.FileSystemLocation;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
/**
|
||||
* Main class.
|
||||
* <p>
|
||||
* Creates game container, adds all other states, and initializes song data.
|
||||
*/
|
||||
public class Opsu extends StateBasedGame {
|
||||
/** Game states. */
|
||||
public static final int
|
||||
STATE_SPLASH = 0,
|
||||
STATE_MAINMENU = 1,
|
||||
STATE_BUTTONMENU = 2,
|
||||
STATE_SONGMENU = 3,
|
||||
STATE_GAME = 4,
|
||||
STATE_GAMEPAUSEMENU = 5,
|
||||
STATE_GAMERANKING = 6,
|
||||
STATE_OPTIONSMENU = 7,
|
||||
STATE_DOWNLOADSMENU = 8;
|
||||
|
||||
/** Server socket for restricting the program to a single instance. */
|
||||
private static ServerSocket SERVER_SOCKET;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param name the program name
|
||||
*/
|
||||
public Opsu(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initStatesList(GameContainer container) throws SlickException {
|
||||
addState(new Splash(STATE_SPLASH));
|
||||
addState(new MainMenu(STATE_MAINMENU));
|
||||
addState(new ButtonMenu(STATE_BUTTONMENU));
|
||||
addState(new SongMenu(STATE_SONGMENU));
|
||||
addState(new Game(STATE_GAME));
|
||||
addState(new GamePauseMenu(STATE_GAMEPAUSEMENU));
|
||||
addState(new GameRanking(STATE_GAMERANKING));
|
||||
addState(new OptionsMenu(STATE_OPTIONSMENU));
|
||||
addState(new DownloadsMenu(STATE_DOWNLOADSMENU));
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches opsu!.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// log all errors to a file
|
||||
Log.setVerbose(false);
|
||||
try {
|
||||
DefaultLogSystem.out = new PrintStream(new FileOutputStream(Options.LOG_FILE, true));
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.error(e);
|
||||
}
|
||||
|
||||
// set default exception handler
|
||||
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
ErrorHandler.error("** Uncaught Exception! **", e, true);
|
||||
System.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// parse configuration file
|
||||
Options.parseOptions();
|
||||
|
||||
// only allow a single instance
|
||||
if (!Options.noSingleInstance()) {
|
||||
try {
|
||||
SERVER_SOCKET = new ServerSocket(Options.getPort(), 1, InetAddress.getLocalHost());
|
||||
} catch (UnknownHostException e) {
|
||||
// shouldn't happen
|
||||
} catch (IOException e) {
|
||||
errorAndExit(
|
||||
null,
|
||||
String.format(
|
||||
"opsu! could not be launched for one of these reasons:\n" +
|
||||
"- An instance of opsu! is already running.\n" +
|
||||
"- Another program is bound to port %d. " +
|
||||
"You can change the port opsu! uses by editing the \"Port\" field in the configuration file.",
|
||||
Options.getPort()
|
||||
),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// load natives
|
||||
File nativeDir;
|
||||
if (!Utils.isJarRunning() && (
|
||||
(nativeDir = new File("./target/natives/")).isDirectory() ||
|
||||
(nativeDir = new File("./build/natives/")).isDirectory()))
|
||||
;
|
||||
else {
|
||||
nativeDir = Options.NATIVE_DIR;
|
||||
try {
|
||||
new NativeLoader(nativeDir).loadNatives();
|
||||
} catch (IOException e) {
|
||||
Log.error("Error loading natives.", e);
|
||||
}
|
||||
}
|
||||
System.setProperty("org.lwjgl.librarypath", nativeDir.getAbsolutePath());
|
||||
System.setProperty("java.library.path", nativeDir.getAbsolutePath());
|
||||
try {
|
||||
// Workaround for "java.library.path" property being read-only.
|
||||
// http://stackoverflow.com/a/24988095
|
||||
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
|
||||
fieldSysPath.setAccessible(true);
|
||||
fieldSysPath.set(null, null);
|
||||
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
|
||||
Log.warn("Failed to set 'sys_paths' field.", e);
|
||||
}
|
||||
|
||||
// set the resource paths
|
||||
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
|
||||
|
||||
// initialize databases
|
||||
try {
|
||||
DBController.init();
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
errorAndExit(e, "The databases could not be initialized.", true);
|
||||
}
|
||||
|
||||
// check if just updated
|
||||
if (args.length >= 2)
|
||||
Updater.get().setUpdateInfo(args[0], args[1]);
|
||||
|
||||
// check for updates
|
||||
if (!Options.isUpdaterDisabled()) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Updater.get().checkForUpdates();
|
||||
} catch (IOException e) {
|
||||
Log.warn("Check for updates failed.", e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
// disable jinput
|
||||
Input.disableControllers();
|
||||
|
||||
// start the game
|
||||
try {
|
||||
// loop until force exit
|
||||
while (true) {
|
||||
Opsu opsu = new Opsu("opsu!");
|
||||
Container app = new Container(opsu);
|
||||
|
||||
// basic game settings
|
||||
Options.setDisplayMode(app);
|
||||
String[] icons = { "icon16.png", "icon32.png" };
|
||||
try {
|
||||
app.setIcons(icons);
|
||||
} catch (Exception e) {
|
||||
Log.error("could not set icon");
|
||||
}
|
||||
app.setForceExit(true);
|
||||
|
||||
app.start();
|
||||
|
||||
// run update if available
|
||||
if (Updater.get().getStatus() == Updater.Status.UPDATE_FINAL) {
|
||||
close();
|
||||
Updater.get().runUpdate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (SlickException e) {
|
||||
errorAndExit(e, "An error occurred while creating the game container.", true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeRequested() {
|
||||
int id = this.getCurrentStateID();
|
||||
|
||||
// intercept close requests in game-related states and return to song menu
|
||||
if (id == STATE_GAME || id == STATE_GAMEPAUSEMENU || id == STATE_GAMERANKING) {
|
||||
// start playing track at preview position
|
||||
SongMenu songMenu = (SongMenu) this.getState(Opsu.STATE_SONGMENU);
|
||||
if (id == STATE_GAMERANKING) {
|
||||
GameData data = ((GameRanking) this.getState(Opsu.STATE_GAMERANKING)).getGameData();
|
||||
if (data != null && data.isGameplay())
|
||||
songMenu.resetTrackOnLoad();
|
||||
} else {
|
||||
if (id == STATE_GAME) {
|
||||
MusicController.pause();
|
||||
MusicController.setPitch(1.0f);
|
||||
MusicController.resume();
|
||||
} else
|
||||
songMenu.resetTrackOnLoad();
|
||||
}
|
||||
|
||||
// reset game data
|
||||
if (UI.getCursor().isBeatmapSkinned())
|
||||
UI.getCursor().reset();
|
||||
songMenu.resetGameDataOnLoad();
|
||||
|
||||
this.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
return false;
|
||||
}
|
||||
|
||||
// show confirmation dialog if any downloads are active
|
||||
if (DownloadList.get().hasActiveDownloads() &&
|
||||
UI.showExitConfirmation(DownloadList.EXIT_CONFIRMATION))
|
||||
return false;
|
||||
if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING &&
|
||||
UI.showExitConfirmation(Updater.EXIT_CONFIRMATION))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all resources.
|
||||
*/
|
||||
public static void close() {
|
||||
// close databases
|
||||
DBController.closeConnections();
|
||||
|
||||
// cancel all downloads
|
||||
DownloadList.get().cancelAllDownloads();
|
||||
|
||||
// close server socket
|
||||
if (SERVER_SOCKET != null) {
|
||||
try {
|
||||
SERVER_SOCKET.close();
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error("Failed to close server socket.", e, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an error and exits the application with the given message.
|
||||
* @param e the exception that caused the crash
|
||||
* @param message the message to display
|
||||
* @param report whether to ask to report the error
|
||||
*/
|
||||
private static void errorAndExit(Throwable e, String message, boolean report) {
|
||||
// JARs will not run properly inside directories containing '!'
|
||||
// http://bugs.java.com/view_bug.do?bug_id=4523159
|
||||
if (Utils.isJarRunning() && Utils.getRunningDirectory() != null &&
|
||||
Utils.getRunningDirectory().getAbsolutePath().indexOf('!') != -1)
|
||||
ErrorHandler.error("JARs cannot be run from some paths containing '!'. Please move or rename the file and try again.", null, false);
|
||||
else
|
||||
ErrorHandler.error(message, e, report);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
|
@ -59,6 +59,10 @@ import com.sun.jna.platform.win32.Advapi32Util;
|
|||
import com.sun.jna.platform.win32.Win32Exception;
|
||||
import com.sun.jna.platform.win32.WinReg;
|
||||
import yugecin.opsudance.*;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
import yugecin.opsudance.movers.factories.ExgonMoverFactory;
|
||||
import yugecin.opsudance.movers.factories.QuadraticBezierMoverFactory;
|
||||
import yugecin.opsudance.movers.slidermovers.DefaultSliderMoverController;
|
||||
|
@ -195,7 +199,7 @@ public class Options {
|
|||
}
|
||||
File dir = new File(rootPath, "opsu");
|
||||
if (!dir.isDirectory() && !dir.mkdir())
|
||||
ErrorHandler.error(String.format("Failed to create configuration folder at '%s/opsu'.", rootPath), null, false);
|
||||
ErrorHandler.error(String.format("Failed to create configuration folder at '%s/opsu'.", rootPath), new Exception("empty")).preventReport().show();
|
||||
return dir;
|
||||
} else
|
||||
return workingDir;
|
||||
|
@ -393,8 +397,7 @@ public class Options {
|
|||
@Override
|
||||
public void clickListItem(int index) {
|
||||
targetFPSindex = index;
|
||||
Container.instance.setTargetFrameRate(targetFPS[index]);
|
||||
Container.instance.setVSync(targetFPS[index] == 60);
|
||||
displayContainer.setFPS(targetFPS[index]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -414,8 +417,7 @@ public class Options {
|
|||
SHOW_FPS ("Show FPS Counter", "FpsCounter", "Show an FPS counter in the bottom-right hand corner.", true),
|
||||
SHOW_UNICODE ("Prefer Non-English Metadata", "ShowUnicode", "Where available, song titles will be shown in their native language.", false) {
|
||||
@Override
|
||||
public void click(GameContainer container) {
|
||||
super.click(container);
|
||||
public void click() {
|
||||
if (bool) {
|
||||
try {
|
||||
Fonts.LARGE.loadGlyphs();
|
||||
|
@ -465,13 +467,7 @@ public class Options {
|
|||
val = i;
|
||||
}
|
||||
},
|
||||
NEW_CURSOR ("Enable New Cursor", "NewCursor", "Use the new cursor style (may cause higher CPU usage).", true) {
|
||||
@Override
|
||||
public void click(GameContainer container) {
|
||||
super.click(container);
|
||||
UI.getCursor().reset();
|
||||
}
|
||||
},
|
||||
NEW_CURSOR ("Enable New Cursor", "NewCursor", "Use the new cursor style (may cause higher CPU usage).", true),
|
||||
DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "DynamicBackground", "The song background will be used as the main menu background.", true),
|
||||
LOAD_VERBOSE ("Show Detailed Loading Progress", "LoadVerbose", "Display more specific loading information in the splash screen.", false),
|
||||
COLOR_MAIN_MENU_LOGO ("Use cursor color as main menu logo tint", "ColorMainMenuLogo", "Colorful main menu logo", false),
|
||||
|
@ -983,6 +979,7 @@ public class Options {
|
|||
PIPPI_SLIDER_FOLLOW_EXPAND ("Followcircle expand", "PippiFollowExpand", "Increase radius in followcircles", false),
|
||||
PIPPI_PREVENT_WOBBLY_STREAMS ("Prevent wobbly streams", "PippiPreventWobblyStreams", "Force linear mover while doing streams to prevent wobbly pippi", true);
|
||||
|
||||
public static DisplayContainer displayContainer;
|
||||
|
||||
/** Option name. */
|
||||
private final String name;
|
||||
|
@ -1129,9 +1126,8 @@ public class Options {
|
|||
* Processes a mouse click action (via override).
|
||||
* <p>
|
||||
* By default, this inverts the current {@code bool} field.
|
||||
* @param container the game container
|
||||
*/
|
||||
public void click(GameContainer container) { bool = !bool; }
|
||||
public void click() { bool = !bool; }
|
||||
|
||||
/**
|
||||
* Get a list of values to choose from
|
||||
|
@ -1279,9 +1275,9 @@ public class Options {
|
|||
/**
|
||||
* Sets the target frame rate to the next available option, and sends a
|
||||
* bar notification about the action.
|
||||
* @param container the game container
|
||||
*/
|
||||
public static void setNextFPS(GameContainer container) {
|
||||
public static void setNextFPS(DisplayContainer displayContainer) {
|
||||
GameOption.displayContainer = displayContainer; // TODO dirty shit
|
||||
GameOption.TARGET_FPS.clickListItem((targetFPSindex + 1) % targetFPS.length);
|
||||
UI.sendBarNotification(String.format("Frame limiter: %s", GameOption.TARGET_FPS.getValueString()));
|
||||
}
|
||||
|
@ -1294,10 +1290,9 @@ public class Options {
|
|||
|
||||
/**
|
||||
* Sets the master volume level (if within valid range).
|
||||
* @param container the game container
|
||||
* @param volume the volume [0, 1]
|
||||
*/
|
||||
public static void setMasterVolume(GameContainer container, float volume) {
|
||||
public static void setMasterVolume(float volume) {
|
||||
if (volume >= 0f && volume <= 1f) {
|
||||
GameOption.MASTER_VOLUME.setValue((int) (volume * 100f));
|
||||
MusicController.setVolume(getMasterVolume() * getMusicVolume());
|
||||
|
@ -1346,11 +1341,10 @@ public class Options {
|
|||
* <p>
|
||||
* If the configured resolution is larger than the screen size, the smallest
|
||||
* available resolution will be used.
|
||||
* @param app the game container
|
||||
*/
|
||||
public static void setDisplayMode(Container app) {
|
||||
int screenWidth = app.getScreenWidth();
|
||||
int screenHeight = app.getScreenHeight();
|
||||
public static void setDisplayMode(DisplayContainer container) {
|
||||
int screenWidth = container.nativeDisplayMode.getWidth();
|
||||
int screenHeight = container.nativeDisplayMode.getHeight();
|
||||
|
||||
resolutions[0] = screenWidth + "x" + screenHeight;
|
||||
if (resolutionIdx < 0 || resolutionIdx > resolutions.length) {
|
||||
|
@ -1370,9 +1364,10 @@ public class Options {
|
|||
}
|
||||
|
||||
try {
|
||||
app.setDisplayMode(width, height, isFullscreen());
|
||||
} catch (SlickException e) {
|
||||
ErrorHandler.error("Failed to set display mode.", e, true);
|
||||
container.setDisplayMode(width, height, isFullscreen());
|
||||
} catch (Exception e) {
|
||||
container.eventBus.post(new BubbleNotificationEvent("Failed to change resolution", BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
Log.error("Failed to set display mode.", e);
|
||||
}
|
||||
|
||||
if (!isFullscreen()) {
|
||||
|
@ -1696,7 +1691,7 @@ public class Options {
|
|||
* sends a bar notification about the action.
|
||||
*/
|
||||
public static void toggleMouseDisabled() {
|
||||
GameOption.DISABLE_MOUSE_BUTTONS.click(null);
|
||||
GameOption.DISABLE_MOUSE_BUTTONS.click();
|
||||
UI.sendBarNotification((GameOption.DISABLE_MOUSE_BUTTONS.getBooleanValue()) ?
|
||||
"Mouse buttons are disabled." : "Mouse buttons are enabled.");
|
||||
}
|
||||
|
@ -1787,7 +1782,7 @@ public class Options {
|
|||
// use default directory
|
||||
beatmapDir = BEATMAP_DIR;
|
||||
if (!beatmapDir.isDirectory() && !beatmapDir.mkdir())
|
||||
ErrorHandler.error(String.format("Failed to create beatmap directory at '%s'.", beatmapDir.getAbsolutePath()), null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create beatmap directory at '%s'.", beatmapDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
return beatmapDir;
|
||||
}
|
||||
|
||||
|
@ -1802,7 +1797,7 @@ public class Options {
|
|||
|
||||
oszDir = new File(DATA_DIR, "SongPacks/");
|
||||
if (!oszDir.isDirectory() && !oszDir.mkdir())
|
||||
ErrorHandler.error(String.format("Failed to create song packs directory at '%s'.", oszDir.getAbsolutePath()), null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create song packs directory at '%s'.", oszDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
return oszDir;
|
||||
}
|
||||
|
||||
|
@ -1817,7 +1812,7 @@ public class Options {
|
|||
|
||||
replayImportDir = new File(DATA_DIR, "ReplayImport/");
|
||||
if (!replayImportDir.isDirectory() && !replayImportDir.mkdir())
|
||||
ErrorHandler.error(String.format("Failed to create replay import directory at '%s'.", replayImportDir.getAbsolutePath()), null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create replay import directory at '%s'.", replayImportDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
return replayImportDir;
|
||||
}
|
||||
|
||||
|
@ -1867,7 +1862,7 @@ public class Options {
|
|||
// use default directory
|
||||
skinRootDir = SKIN_ROOT_DIR;
|
||||
if (!skinRootDir.isDirectory() && !skinRootDir.mkdir())
|
||||
ErrorHandler.error(String.format("Failed to create skins directory at '%s'.", skinRootDir.getAbsolutePath()), null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create skins directory at '%s'.", skinRootDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
return skinRootDir;
|
||||
}
|
||||
|
||||
|
@ -2000,7 +1995,9 @@ public class Options {
|
|||
}
|
||||
GameOption.DANCE_HIDE_WATERMARK.setValue(false);
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error(String.format("Failed to read file '%s'.", OPTIONS_FILE.getAbsolutePath()), e, false);
|
||||
String err = String.format("Failed to read file '%s'.", OPTIONS_FILE.getAbsolutePath());
|
||||
Log.error(err, e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2029,7 +2026,9 @@ public class Options {
|
|||
}
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error(String.format("Failed to write to file '%s'.", OPTIONS_FILE.getAbsolutePath()), e, false);
|
||||
String err = String.format("Failed to write to file '%s'.", OPTIONS_FILE.getAbsolutePath());
|
||||
Log.error(err, e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package itdelatrisu.opsu;
|
||||
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.audio.SoundController;
|
||||
import itdelatrisu.opsu.audio.SoundEffect;
|
||||
import itdelatrisu.opsu.beatmap.HitObject;
|
||||
|
@ -71,6 +72,10 @@ import org.newdawn.slick.state.StateBasedGame;
|
|||
import org.newdawn.slick.util.Log;
|
||||
|
||||
import com.sun.jna.platform.FileUtils;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* Contains miscellaneous utilities.
|
||||
|
@ -89,40 +94,18 @@ public class Utils {
|
|||
Arrays.sort(illegalChars);
|
||||
}
|
||||
|
||||
// game-related variables
|
||||
private static Input input;
|
||||
|
||||
// This class should not be instantiated.
|
||||
private Utils() {}
|
||||
|
||||
/**
|
||||
* Initializes game settings and class data.
|
||||
* @param container the game container
|
||||
* @param game the game object
|
||||
*/
|
||||
public static void init(GameContainer container, StateBasedGame game) {
|
||||
input = container.getInput();
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
public static void init(DisplayContainer displayContainer) {
|
||||
// TODO clean this up
|
||||
|
||||
// game settings
|
||||
container.setTargetFrameRate(Options.getTargetFPS());
|
||||
container.setVSync(Options.getTargetFPS() == 60);
|
||||
container.setMusicVolume(Options.getMusicVolume() * Options.getMasterVolume());
|
||||
container.setShowFPS(false);
|
||||
container.getInput().enableKeyRepeat();
|
||||
container.setAlwaysRender(true);
|
||||
container.setUpdateOnlyWhenVisible(false);
|
||||
|
||||
// calculate UI scale
|
||||
GameImage.init(width, height);
|
||||
|
||||
// create fonts
|
||||
try {
|
||||
Fonts.init();
|
||||
} catch (Exception e) {
|
||||
ErrorHandler.error("Failed to load fonts.", e, true);
|
||||
}
|
||||
displayContainer.setFPS(Options.getTargetFPS()); // TODO move this elsewhere
|
||||
MusicController.setMusicVolume(Options.getMusicVolume() * Options.getMasterVolume());
|
||||
|
||||
// load skin
|
||||
Options.loadSkin();
|
||||
|
@ -134,19 +117,19 @@ public class Utils {
|
|||
}
|
||||
|
||||
// initialize game mods
|
||||
GameMod.init(width, height);
|
||||
GameMod.init(displayContainer.width, displayContainer.height);
|
||||
|
||||
// initialize playback buttons
|
||||
PlaybackSpeed.init(width, height);
|
||||
PlaybackSpeed.init(displayContainer.width, displayContainer.height);
|
||||
|
||||
// initialize hit objects
|
||||
HitObject.init(width, height);
|
||||
HitObject.init(displayContainer.width, displayContainer.height);
|
||||
|
||||
// initialize download nodes
|
||||
DownloadNode.init(width, height);
|
||||
DownloadNode.init(displayContainer.width, displayContainer.height);
|
||||
|
||||
// initialize UI components
|
||||
UI.init(container, game);
|
||||
UI.init(displayContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -246,12 +229,15 @@ public class Utils {
|
|||
* @return true if pressed
|
||||
*/
|
||||
public static boolean isGameKeyPressed() {
|
||||
/*
|
||||
boolean mouseDown = !Options.isMouseDisabled() && (
|
||||
input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) ||
|
||||
input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON));
|
||||
return (mouseDown ||
|
||||
input.isKeyDown(Options.getGameKeyLeft()) ||
|
||||
input.isKeyDown(Options.getGameKeyRight()));
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -262,14 +248,14 @@ public class Utils {
|
|||
// create the screenshot directory
|
||||
File dir = Options.getScreenshotDir();
|
||||
if (!dir.isDirectory() && !dir.mkdir()) {
|
||||
ErrorHandler.error(String.format("Failed to create screenshot directory at '%s'.", dir.getAbsolutePath()), null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create screenshot directory at '%s'.", dir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
return;
|
||||
}
|
||||
|
||||
// create file name
|
||||
SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss");
|
||||
final File file = new File(dir, String.format("screenshot_%s.%s",
|
||||
date.format(new Date()), Options.getScreenshotFormat()));
|
||||
final String fileName = String.format("screenshot_%s.%s", date.format(new Date()), Options.getScreenshotFormat());
|
||||
final File file = new File(dir, fileName);
|
||||
|
||||
SoundController.playSound(SoundEffect.SHUTTER);
|
||||
|
||||
|
@ -296,8 +282,9 @@ public class Utils {
|
|||
}
|
||||
}
|
||||
ImageIO.write(image, Options.getScreenshotFormat(), file);
|
||||
EventBus.instance.post(new BubbleNotificationEvent("Created " + fileName, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
|
||||
} catch (Exception e) {
|
||||
ErrorHandler.error("Failed to take a screenshot.", e, true);
|
||||
ErrorHandler.error("Failed to take a screenshot.", e).show();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
@ -446,7 +433,7 @@ public class Utils {
|
|||
try {
|
||||
json = new JSONObject(s);
|
||||
} catch (JSONException e) {
|
||||
ErrorHandler.error("Failed to create JSON object.", e, true);
|
||||
ErrorHandler.error("Failed to create JSON object.", e).show();
|
||||
}
|
||||
}
|
||||
return json;
|
||||
|
@ -465,7 +452,7 @@ public class Utils {
|
|||
try {
|
||||
json = new JSONArray(s);
|
||||
} catch (JSONException e) {
|
||||
ErrorHandler.error("Failed to create JSON array.", e, true);
|
||||
ErrorHandler.error("Failed to create JSON array.", e).show();
|
||||
}
|
||||
}
|
||||
return json;
|
||||
|
@ -507,7 +494,7 @@ public class Utils {
|
|||
result.append(String.format("%02x", b));
|
||||
return result.toString();
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
ErrorHandler.error("Failed to calculate MD5 hash.", e, true);
|
||||
ErrorHandler.error("Failed to calculate MD5 hash.", e).show();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -531,7 +518,7 @@ public class Utils {
|
|||
* @return true if JAR, false if file
|
||||
*/
|
||||
public static boolean isJarRunning() {
|
||||
return Opsu.class.getResource(String.format("%s.class", Opsu.class.getSimpleName())).toString().startsWith("jar:");
|
||||
return Utils.class.getResource(String.format("%s.class", Utils.class.getSimpleName())).toString().startsWith("jar:");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -543,7 +530,7 @@ public class Utils {
|
|||
return null;
|
||||
|
||||
try {
|
||||
return new JarFile(new File(Opsu.class.getProtectionDomain().getCodeSource().getLocation().toURI()), false);
|
||||
return new JarFile(new File(Utils.class.getProtectionDomain().getCodeSource().getLocation().toURI()), false);
|
||||
} catch (URISyntaxException | IOException e) {
|
||||
Log.error("Could not determine the JAR file.", e);
|
||||
return null;
|
||||
|
@ -556,7 +543,7 @@ public class Utils {
|
|||
*/
|
||||
public static File getRunningDirectory() {
|
||||
try {
|
||||
return new File(Opsu.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
|
||||
return new File(Utils.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
|
||||
} catch (URISyntaxException e) {
|
||||
Log.error("Could not get the running directory.", e);
|
||||
return null;
|
||||
|
@ -590,6 +577,8 @@ public class Utils {
|
|||
if (isJarRunning())
|
||||
return null;
|
||||
File f = new File(".git/refs/remotes/origin/master");
|
||||
if (!f.isFile())
|
||||
f = new File("../.git/refs/remotes/origin/master");
|
||||
if (!f.isFile())
|
||||
return null;
|
||||
try (BufferedReader in = new BufferedReader(new FileReader(f))) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package itdelatrisu.opsu.audio;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
|
@ -194,7 +194,7 @@ public class MultiClip {
|
|||
try {
|
||||
audioIn.close();
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error(String.format("Could not close AudioInputStream for MultiClip %s.", name), e, true);
|
||||
ErrorHandler.error(String.format("Could not close AudioInputStream for MultiClip %s.", name), e).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.audio;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.beatmap.Beatmap;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapParser;
|
||||
|
@ -44,8 +43,12 @@ import org.newdawn.slick.MusicListener;
|
|||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.openal.Audio;
|
||||
import org.newdawn.slick.openal.SoundStore;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
import org.tritonus.share.sampled.file.TAudioFileFormat;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* Controller for all music.
|
||||
|
@ -152,7 +155,9 @@ public class MusicController {
|
|||
});
|
||||
playAt(position, loop);
|
||||
} catch (Exception e) {
|
||||
ErrorHandler.error(String.format("Could not play track '%s'.", file.getName()), e, false);
|
||||
String err = String.format("Could not play track '%s'.", file.getName());
|
||||
Log.error(err, e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,9 +501,7 @@ public class MusicController {
|
|||
trackLoader.interrupt();
|
||||
try {
|
||||
trackLoader.join();
|
||||
} catch (InterruptedException e) {
|
||||
ErrorHandler.error(null, e, true);
|
||||
}
|
||||
} catch (InterruptedException ignored) { }
|
||||
}
|
||||
trackLoader = null;
|
||||
|
||||
|
@ -575,7 +578,16 @@ public class MusicController {
|
|||
|
||||
player = null;
|
||||
} catch (Exception e) {
|
||||
ErrorHandler.error("Failed to destroy OpenAL.", e, true);
|
||||
ErrorHandler.error("Failed to destroy OpenAL.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default volume for music
|
||||
* @param volume the new default value for music volume
|
||||
*/
|
||||
public static void setMusicVolume(float volume) {
|
||||
SoundStore.get().setMusicVolume(volume);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.audio;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.audio.HitSound.SampleSet;
|
||||
import itdelatrisu.opsu.beatmap.HitObject;
|
||||
|
@ -41,6 +40,9 @@ import javax.sound.sampled.LineUnavailableException;
|
|||
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* Controller for all (non-music) sound components.
|
||||
|
@ -99,7 +101,7 @@ public class SoundController {
|
|||
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
|
||||
return loadClip(ref, audioIn, isMP3);
|
||||
} catch (Exception e) {
|
||||
ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e, true);
|
||||
ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e).show();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +216,7 @@ public class SoundController {
|
|||
// menu and game sounds
|
||||
for (SoundEffect s : SoundEffect.values()) {
|
||||
if ((currentFileName = getSoundFileName(s.getFileName())) == null) {
|
||||
ErrorHandler.error(String.format("Could not find sound file '%s'.", s.getFileName()), null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent("Could not find sound file " + s.getFileName(), BubbleNotificationEvent.COLOR_ORANGE));
|
||||
continue;
|
||||
}
|
||||
MultiClip newClip = loadClip(currentFileName, currentFileName.endsWith(".mp3"));
|
||||
|
@ -233,7 +235,7 @@ public class SoundController {
|
|||
for (HitSound s : HitSound.values()) {
|
||||
String filename = String.format("%s-%s", ss.getName(), s.getFileName());
|
||||
if ((currentFileName = getSoundFileName(filename)) == null) {
|
||||
ErrorHandler.error(String.format("Could not find hit sound file '%s'.", filename), null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent("Could not find hit sound file " + filename, BubbleNotificationEvent.COLOR_ORANGE));
|
||||
continue;
|
||||
}
|
||||
MultiClip newClip = loadClip(currentFileName, false);
|
||||
|
@ -277,7 +279,7 @@ public class SoundController {
|
|||
try {
|
||||
clip.start(volume, listener);
|
||||
} catch (LineUnavailableException e) {
|
||||
ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e, true);
|
||||
ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import itdelatrisu.opsu.Options;
|
|||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -59,6 +60,14 @@ public class Beatmap implements Comparable<Beatmap> {
|
|||
*/
|
||||
public static void clearBackgroundImageCache() { bgImageCache.clear(); }
|
||||
|
||||
public static void destroyBackgroundImageCache() {
|
||||
Collection<ImageLoader> values = bgImageCache.values();
|
||||
for (ImageLoader value : values) {
|
||||
value.destroy();
|
||||
}
|
||||
bgImageCache.clear();
|
||||
}
|
||||
|
||||
/** The OSU File object associated with this beatmap. */
|
||||
private File file;
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.beatmap;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.db.BeatmapDB;
|
||||
|
@ -35,6 +34,9 @@ import java.util.Map;
|
|||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* Parser for beatmaps.
|
||||
|
@ -243,9 +245,11 @@ public class BeatmapParser {
|
|||
}
|
||||
map.timingPoints.trimToSize();
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error(String.format("Failed to read file '%s'.", map.getFile().getAbsolutePath()), e, false);
|
||||
String err = String.format("Failed to read file '%s'.", map.getFile().getAbsolutePath());
|
||||
Log.error(err, e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
ErrorHandler.error("Failed to get MD5 hash stream.", e, true);
|
||||
ErrorHandler.error("Failed to get MD5 hash stream.", e).show();
|
||||
|
||||
// retry without MD5
|
||||
hasNoMD5Algorithm = true;
|
||||
|
@ -646,9 +650,11 @@ public class BeatmapParser {
|
|||
if (md5stream != null)
|
||||
beatmap.md5Hash = md5stream.getMD5();
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error(String.format("Failed to read file '%s'.", file.getAbsolutePath()), e, false);
|
||||
String err = String.format("Failed to read file '%s'.", file.getAbsolutePath());
|
||||
Log.error(err, e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
ErrorHandler.error("Failed to get MD5 hash stream.", e, true);
|
||||
ErrorHandler.error("Failed to get MD5 hash stream.", e).show();
|
||||
|
||||
// retry without MD5
|
||||
hasNoMD5Algorithm = true;
|
||||
|
@ -727,7 +733,9 @@ public class BeatmapParser {
|
|||
}
|
||||
beatmap.timingPoints.trimToSize();
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error(String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath()), e, false);
|
||||
String err = String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath());
|
||||
Log.error(err, e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -803,9 +811,11 @@ public class BeatmapParser {
|
|||
// check that all objects were parsed
|
||||
if (objectIndex != beatmap.objects.length)
|
||||
ErrorHandler.error(String.format("Parsed %d objects for beatmap '%s', %d objects expected.",
|
||||
objectIndex, beatmap.toString(), beatmap.objects.length), null, true);
|
||||
objectIndex, beatmap.toString(), beatmap.objects.length), new Exception("no")).show();
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error(String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath()), e, false);
|
||||
String err = String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath());
|
||||
Log.error(err, e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
|
||||
package itdelatrisu.opsu.beatmap;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.db.BeatmapDB;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -215,7 +216,7 @@ public class BeatmapSetList {
|
|||
try {
|
||||
Utils.deleteToTrash(dir);
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error("Could not delete song group.", e, true);
|
||||
EventBus.instance.post(new BubbleNotificationEvent("Could not delete song group", BubbleNotificationEvent.COLOR_ORANGE));
|
||||
}
|
||||
if (ws != null)
|
||||
ws.resume();
|
||||
|
@ -270,7 +271,7 @@ public class BeatmapSetList {
|
|||
try {
|
||||
Utils.deleteToTrash(file);
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error("Could not delete song.", e, true);
|
||||
EventBus.instance.post(new BubbleNotificationEvent("Could not delete song", BubbleNotificationEvent.COLOR_ORANGE));
|
||||
}
|
||||
if (ws != null)
|
||||
ws.resume();
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.beatmap;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Options;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -42,6 +41,8 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
|
@ -96,7 +97,8 @@ public class BeatmapWatchService {
|
|||
ws = new BeatmapWatchService();
|
||||
ws.register(Options.getBeatmapDir().toPath());
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error("An I/O exception occurred while creating the watch service.", e, true);
|
||||
Log.error("Could not create watch service", e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent("Could not create watch service", BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -117,8 +119,9 @@ public class BeatmapWatchService {
|
|||
ws.service.shutdownNow();
|
||||
ws = null;
|
||||
} catch (IOException e) {
|
||||
Log.error("An I/O exception occurred while closing the previous watch service.", e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent("An I/O exception occurred while closing the previous watch service.", BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
ws = null;
|
||||
ErrorHandler.error("An I/O exception occurred while closing the previous watch service.", e, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.beatmap;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Options;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -28,6 +27,9 @@ import java.util.List;
|
|||
|
||||
import net.lingala.zip4j.core.ZipFile;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* Unpacker for OSZ (ZIP) archives.
|
||||
|
@ -97,8 +99,9 @@ public class OszUnpacker {
|
|||
ZipFile zipFile = new ZipFile(file);
|
||||
zipFile.extractAll(dest.getAbsolutePath());
|
||||
} catch (ZipException e) {
|
||||
ErrorHandler.error(String.format("Failed to unzip file %s to dest %s.",
|
||||
file.getAbsolutePath(), dest.getAbsolutePath()), e, false);
|
||||
String err = String.format("Failed to unzip file %s to dest %s.", file.getAbsolutePath(), dest.getAbsolutePath());
|
||||
Log.error(err, e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.db;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.beatmap.Beatmap;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapParser;
|
||||
|
@ -35,6 +34,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
/**
|
||||
* Handles connections and queries with the cached beatmap database.
|
||||
|
@ -110,7 +110,7 @@ public class BeatmapDB {
|
|||
try {
|
||||
updateSizeStmt = connection.prepareStatement("REPLACE INTO info (key, value) VALUES ('size', ?)");
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to prepare beatmap statements.", e, true);
|
||||
ErrorHandler.error("Failed to prepare beatmap statements.", e).show();
|
||||
}
|
||||
|
||||
// retrieve the cache size
|
||||
|
@ -132,7 +132,7 @@ public class BeatmapDB {
|
|||
updatePlayStatsStmt = connection.prepareStatement("UPDATE beatmaps SET playCount = ?, lastPlayed = ? WHERE dir = ? AND file = ?");
|
||||
setFavoriteStmt = connection.prepareStatement("UPDATE beatmaps SET favorite = ? WHERE dir = ? AND file = ?");
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to prepare beatmap statements.", e, true);
|
||||
ErrorHandler.error("Failed to prepare beatmap statements.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,7 +170,7 @@ public class BeatmapDB {
|
|||
sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', '%s')", DATABASE_VERSION);
|
||||
stmt.executeUpdate(sql);
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Could not create beatmap database.", e, true);
|
||||
ErrorHandler.error("Coudl not create beatmap database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +222,7 @@ public class BeatmapDB {
|
|||
ps.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to update beatmap database.", e, true);
|
||||
ErrorHandler.error("Failed to update beatmap database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,7 +240,7 @@ public class BeatmapDB {
|
|||
}
|
||||
rs.close();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Could not get beatmap cache size.", e, true);
|
||||
ErrorHandler.error("Could not get beatmap cache size.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,7 +255,7 @@ public class BeatmapDB {
|
|||
updateSizeStmt.setString(1, Integer.toString(Math.max(cacheSize, 0)));
|
||||
updateSizeStmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Could not update beatmap cache size.", e, true);
|
||||
ErrorHandler.error("Could not update beatmap cache size.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,7 +273,7 @@ public class BeatmapDB {
|
|||
cacheSize = 0;
|
||||
updateCacheSize();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Could not drop beatmap database.", e, true);
|
||||
ErrorHandler.error("Could not drop beatmap database.", e).show();
|
||||
}
|
||||
createDatabase();
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ public class BeatmapDB {
|
|||
cacheSize += insertStmt.executeUpdate();
|
||||
updateCacheSize();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to add beatmap to database.", e, true);
|
||||
ErrorHandler.error("Failed to add beatmap to database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,7 +344,7 @@ public class BeatmapDB {
|
|||
// update cache size
|
||||
updateCacheSize();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to add beatmaps to database.", e, true);
|
||||
ErrorHandler.error("Failed to add beatmaps to database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -432,7 +432,7 @@ public class BeatmapDB {
|
|||
}
|
||||
rs.close();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to load Beatmap from database.", e, true);
|
||||
ErrorHandler.error("Failed to load Beatmap from database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -495,7 +495,7 @@ public class BeatmapDB {
|
|||
}
|
||||
rs.close();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to load beatmaps from database.", e, true);
|
||||
ErrorHandler.error("Failed to load beatmaps from database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -596,7 +596,7 @@ public class BeatmapDB {
|
|||
rs.close();
|
||||
return map;
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to get last modified map from database.", e, true);
|
||||
ErrorHandler.error("Failed to get last modified map from database.", e).show();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -616,7 +616,7 @@ public class BeatmapDB {
|
|||
cacheSize -= deleteMapStmt.executeUpdate();
|
||||
updateCacheSize();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to delete beatmap entry from database.", e, true);
|
||||
ErrorHandler.error("Failed to delete beatmap entry from database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -633,7 +633,7 @@ public class BeatmapDB {
|
|||
cacheSize -= deleteGroupStmt.executeUpdate();
|
||||
updateCacheSize();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to delete beatmap group entry from database.", e, true);
|
||||
ErrorHandler.error("Failed to delete beatmap group entry from database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -652,7 +652,7 @@ public class BeatmapDB {
|
|||
setStarsStmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error(String.format("Failed to save star rating '%.4f' for beatmap '%s' in database.",
|
||||
beatmap.starRating, beatmap.toString()), e, true);
|
||||
beatmap.starRating, beatmap.toString()), e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -672,7 +672,7 @@ public class BeatmapDB {
|
|||
updatePlayStatsStmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error(String.format("Failed to update play statistics for beatmap '%s' in database.",
|
||||
beatmap.toString()), e, true);
|
||||
beatmap.toString()), e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -691,7 +691,7 @@ public class BeatmapDB {
|
|||
setFavoriteStmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error(String.format("Failed to update favorite status for beatmap '%s' in database.",
|
||||
beatmap.toString()), e, true);
|
||||
beatmap.toString()), e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -711,7 +711,7 @@ public class BeatmapDB {
|
|||
connection.close();
|
||||
connection = null;
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to close beatmap database.", e, true);
|
||||
ErrorHandler.error("Failed to close beatmap database.", e).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package itdelatrisu.opsu.db;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
|
@ -39,7 +39,7 @@ public class DBController {
|
|||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
} catch (ClassNotFoundException e) {
|
||||
ErrorHandler.error("Could not load sqlite-JDBC driver.", e, true);
|
||||
ErrorHandler.error("Could not load sqlite-JDBC driver.", e).show();
|
||||
}
|
||||
|
||||
// initialize the databases
|
||||
|
@ -65,7 +65,7 @@ public class DBController {
|
|||
return DriverManager.getConnection(String.format("jdbc:sqlite:%s", path));
|
||||
} catch (SQLException e) {
|
||||
// if the error message is "out of memory", it probably means no database file is found
|
||||
ErrorHandler.error(String.format("Could not connect to database: '%s'.", path), e, true);
|
||||
ErrorHandler.error(String.format("Could not connect to database: '%s'.", path), e).show();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
|
||||
package itdelatrisu.opsu.db;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.ScoreData;
|
||||
import itdelatrisu.opsu.beatmap.Beatmap;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
|
@ -124,7 +124,7 @@ public class ScoreDB {
|
|||
// TODO: extra playerName checks not needed if name is guaranteed not null
|
||||
);
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to prepare score statements.", e, true);
|
||||
ErrorHandler.error("Failed to prepare score statements.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ public class ScoreDB {
|
|||
sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', %d)", DATABASE_VERSION);
|
||||
stmt.executeUpdate(sql);
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Could not create score database.", e, true);
|
||||
ErrorHandler.error("Could not create score database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ public class ScoreDB {
|
|||
ps.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to update score database.", e, true);
|
||||
ErrorHandler.error("Failed to update score database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,7 @@ public class ScoreDB {
|
|||
insertStmt.setString(19, data.playerName);
|
||||
insertStmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to save score to database.", e, true);
|
||||
ErrorHandler.error("Failed to save score to database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,7 +247,7 @@ public class ScoreDB {
|
|||
deleteScoreStmt.setString(21, data.playerName);
|
||||
deleteScoreStmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to delete score from database.", e, true);
|
||||
ErrorHandler.error("Failed to delete score from database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,7 +267,7 @@ public class ScoreDB {
|
|||
deleteSongStmt.setString(5, beatmap.version);
|
||||
deleteSongStmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to delete scores from database.", e, true);
|
||||
ErrorHandler.error("Failed to delete scores from database.", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,7 @@ public class ScoreDB {
|
|||
}
|
||||
rs.close();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to read scores from database.", e, true);
|
||||
ErrorHandler.error("Failed to read scores from database.", e).show();
|
||||
return null;
|
||||
}
|
||||
return getSortedArray(list);
|
||||
|
@ -377,7 +377,7 @@ public class ScoreDB {
|
|||
map.put(version, getSortedArray(list));
|
||||
rs.close();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to read scores from database.", e, true);
|
||||
ErrorHandler.error("Failed to read scores from database.", e).show();
|
||||
return null;
|
||||
}
|
||||
return map;
|
||||
|
@ -406,7 +406,7 @@ public class ScoreDB {
|
|||
connection.close();
|
||||
connection = null;
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to close score database.", e, true);
|
||||
ErrorHandler.error("Failed to close score database.", e).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.downloads;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -35,6 +34,9 @@ import java.nio.file.Path;
|
|||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* File download.
|
||||
|
@ -142,7 +144,7 @@ public class Download {
|
|||
this.url = new URL(remoteURL);
|
||||
} catch (MalformedURLException e) {
|
||||
this.status = Status.ERROR;
|
||||
ErrorHandler.error(String.format("Bad download URL: '%s'", remoteURL), e, true);
|
||||
ErrorHandler.error(String.format("Bad download URL: '%s'", remoteURL), e).show();
|
||||
return;
|
||||
}
|
||||
this.localPath = localPath;
|
||||
|
@ -215,7 +217,7 @@ public class Download {
|
|||
else if (redirectCount > MAX_REDIRECTS)
|
||||
error = String.format("Download for URL '%s' is attempting too many redirects (over %d).", base.toString(), MAX_REDIRECTS);
|
||||
if (error != null) {
|
||||
ErrorHandler.error(error, null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(error, BubbleNotificationEvent.COLOR_ORANGE));
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
|
@ -417,7 +419,7 @@ public class Download {
|
|||
}
|
||||
} catch (IOException e) {
|
||||
this.status = Status.ERROR;
|
||||
ErrorHandler.error("Failed to cancel download.", e, true);
|
||||
ErrorHandler.error("Failed to cancel download.", e).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.downloads;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
|
@ -35,6 +34,8 @@ import java.io.File;
|
|||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* Node containing song data and a Download object.
|
||||
|
@ -402,7 +403,7 @@ public class DownloadNode {
|
|||
public void drawDownload(Graphics g, float position, int id, boolean hover) {
|
||||
Download download = this.download; // in case clearDownload() is called asynchronously
|
||||
if (download == null) {
|
||||
ErrorHandler.error("Trying to draw download information for button without Download object.", null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent("Trying to draw download information for button without Download object", BubbleNotificationEvent.COLOR_ORANGE));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.downloads;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.downloads.Download.DownloadListener;
|
||||
|
@ -38,6 +37,7 @@ import java.util.Properties;
|
|||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
/**
|
||||
* Handles automatic program updates.
|
||||
|
@ -298,7 +298,7 @@ public class Updater {
|
|||
pb.start();
|
||||
} catch (IOException e) {
|
||||
status = Status.INTERNAL_ERROR;
|
||||
ErrorHandler.error("Failed to start new process.", e, true);
|
||||
ErrorHandler.error("Failed to start new process.", e).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.downloads.servers;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.downloads.DownloadNode;
|
||||
|
||||
|
@ -34,6 +33,7 @@ import java.util.Date;
|
|||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
/**
|
||||
* Download server: http://bloodcat.com/osu/
|
||||
|
@ -97,7 +97,7 @@ public class BloodcatServer extends DownloadServer {
|
|||
resultCount++;
|
||||
this.totalResults = resultCount;
|
||||
} catch (MalformedURLException | UnsupportedEncodingException e) {
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true);
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.downloads.servers;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.downloads.DownloadNode;
|
||||
|
||||
|
@ -30,6 +29,7 @@ import java.net.URLEncoder;
|
|||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
/**
|
||||
* Download server: https://osu.hexide.com/
|
||||
|
@ -128,7 +128,7 @@ public class HexideServer extends DownloadServer {
|
|||
// all results at once; this approach just gets pagination correct.
|
||||
this.totalResults = arr.length() + resultIndex;
|
||||
} catch (MalformedURLException | UnsupportedEncodingException e) {
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true);
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.downloads.servers;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.downloads.DownloadNode;
|
||||
|
||||
|
@ -30,6 +29,7 @@ import java.net.URLEncoder;
|
|||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
/**
|
||||
* Download server: http://osu.mengsky.net/
|
||||
|
@ -101,7 +101,7 @@ public class MengSkyServer extends DownloadServer {
|
|||
resultCount = 1 + (pageTotal - 1) * PAGE_LIMIT;
|
||||
this.totalResults = resultCount;
|
||||
} catch (MalformedURLException | UnsupportedEncodingException e) {
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true);
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
|
||||
package itdelatrisu.opsu.downloads.servers;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.downloads.DownloadNode;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
@ -120,7 +120,7 @@ public class MnetworkServer extends DownloadServer {
|
|||
// store total result count
|
||||
this.totalResults = nodes.length;
|
||||
} catch (MalformedURLException | UnsupportedEncodingException e) {
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true);
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.downloads.servers;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.downloads.DownloadNode;
|
||||
|
||||
|
@ -36,6 +35,7 @@ import java.util.TimeZone;
|
|||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
/**
|
||||
* Download server: http://loli.al/
|
||||
|
@ -123,7 +123,7 @@ public class OsuMirrorServer extends DownloadServer {
|
|||
else
|
||||
this.totalResults = maxServerID;
|
||||
} catch (MalformedURLException | UnsupportedEncodingException e) {
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true);
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
package itdelatrisu.opsu.downloads.servers;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.downloads.DownloadNode;
|
||||
|
||||
|
@ -34,6 +33,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
/**
|
||||
* Download server: http://osu.yas-online.net/
|
||||
|
@ -114,7 +114,7 @@ public class YaSOnlineServer extends DownloadServer {
|
|||
String downloadLink = item.getString("downloadLink");
|
||||
return String.format(DOWNLOAD_FETCH_URL, downloadLink);
|
||||
} catch (MalformedURLException | UnsupportedEncodingException e) {
|
||||
ErrorHandler.error(String.format("Problem retrieving download URL for beatmap '%d'.", beatmapSetID), e, true);
|
||||
ErrorHandler.error(String.format("Problem retrieving download URL for beatmap '%d'.", beatmapSetID), e).show();
|
||||
return null;
|
||||
} finally {
|
||||
Utils.setSSLCertValidation(true);
|
||||
|
@ -186,7 +186,7 @@ public class YaSOnlineServer extends DownloadServer {
|
|||
else
|
||||
this.totalResults = maxServerID;
|
||||
} catch (MalformedURLException | UnsupportedEncodingException e) {
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true);
|
||||
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
|
||||
} finally {
|
||||
Utils.setSSLCertValidation(true);
|
||||
}
|
||||
|
|
|
@ -64,10 +64,9 @@ public class Circle extends GameObject {
|
|||
|
||||
/**
|
||||
* Initializes the Circle data type with map modifiers, images, and dimensions.
|
||||
* @param container the game container
|
||||
* @param circleDiameter the circle diameter
|
||||
*/
|
||||
public static void init(GameContainer container, float circleDiameter) {
|
||||
public static void init(float circleDiameter) {
|
||||
diameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480)
|
||||
int diameterInt = (int) diameter;
|
||||
GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(diameterInt, diameterInt));
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.newdawn.slick.GameContainer;
|
|||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import yugecin.opsudance.Dancer;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
|
||||
/**
|
||||
* Data type representing a slider object.
|
||||
|
@ -127,13 +128,12 @@ public class Slider extends GameObject {
|
|||
|
||||
/**
|
||||
* Initializes the Slider data type with images and dimensions.
|
||||
* @param container the game container
|
||||
* @param circleDiameter the circle diameter
|
||||
* @param beatmap the associated beatmap
|
||||
*/
|
||||
public static void init(GameContainer container, float circleDiameter, Beatmap beatmap) {
|
||||
containerWidth = container.getWidth();
|
||||
containerHeight = container.getHeight();
|
||||
public static void init(DisplayContainer displayContainer, float circleDiameter, Beatmap beatmap) {
|
||||
containerWidth = displayContainer.width;
|
||||
containerHeight = displayContainer.height;
|
||||
|
||||
diameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480)
|
||||
int diameterInt = (int) diameter;
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.newdawn.slick.Color;
|
|||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
|
||||
/**
|
||||
* Data type representing a spinner object.
|
||||
|
@ -109,12 +110,11 @@ public class Spinner extends GameObject {
|
|||
|
||||
/**
|
||||
* Initializes the Spinner data type with images and dimensions.
|
||||
* @param container the game container
|
||||
* @param difficulty the map's overall difficulty value
|
||||
*/
|
||||
public static void init(GameContainer container, float difficulty) {
|
||||
width = container.getWidth();
|
||||
height = container.getHeight();
|
||||
public static void init(DisplayContainer displayContainer, float difficulty) {
|
||||
width = displayContainer.width;
|
||||
height = displayContainer.height;
|
||||
overallDifficulty = difficulty;
|
||||
}
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ public class CurveRenderState {
|
|||
*/
|
||||
public static void shutdown() {
|
||||
staticState.shutdown();
|
||||
FrameBufferCache.shutdown();
|
||||
//FrameBufferCache.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -128,6 +128,7 @@ public class FrameBufferCache {
|
|||
* <p>
|
||||
* This is necessary for cases when the game gets restarted with a
|
||||
* different resolution without closing the process.
|
||||
* // TODO d do we still need this
|
||||
*/
|
||||
public static void shutdown() {
|
||||
FrameBufferCache fbcInstance = FrameBufferCache.getInstance();
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.replay;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.ScoreData;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
|
@ -45,6 +44,9 @@ import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream;
|
|||
import org.newdawn.slick.util.Log;
|
||||
|
||||
import lzma.streams.LzmaOutputStream;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* Captures osu! replay data.
|
||||
|
@ -273,7 +275,7 @@ public class Replay {
|
|||
File dir = Options.getReplayDir();
|
||||
if (!dir.isDirectory()) {
|
||||
if (!dir.mkdir()) {
|
||||
ErrorHandler.error("Failed to create replay directory.", null, false);
|
||||
EventBus.instance.post(new BubbleNotificationEvent("Failed to create replay directory.", BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +345,7 @@ public class Replay {
|
|||
compressedOut.write(bytes);
|
||||
} catch (IOException e) {
|
||||
// possible OOM: https://github.com/jponge/lzma-java/issues/9
|
||||
ErrorHandler.error("LZMA compression failed (possible out-of-memory error).", e, true);
|
||||
ErrorHandler.error("LZMA compression failed (possible out-of-memory error).", e).show();
|
||||
}
|
||||
compressedOut.close();
|
||||
bout.close();
|
||||
|
@ -357,7 +359,7 @@ public class Replay {
|
|||
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error("Could not save replay data.", e, true);
|
||||
ErrorHandler.error("Could not save replay data.", e).show();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package itdelatrisu.opsu.replay;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import com.sun.deploy.security.EnhancedJarVerifier;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.beatmap.Beatmap;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapSetList;
|
||||
|
@ -31,6 +31,8 @@ import java.nio.file.Files;
|
|||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* Importer for replay files.
|
||||
|
@ -70,7 +72,9 @@ public class ReplayImporter {
|
|||
File replayDir = Options.getReplayDir();
|
||||
if (!replayDir.isDirectory()) {
|
||||
if (!replayDir.mkdir()) {
|
||||
ErrorHandler.error(String.format("Failed to create replay directory '%s'.", replayDir.getAbsolutePath()), null, false);
|
||||
String err = String.format("Failed to create replay directory '%s'.", replayDir.getAbsolutePath());
|
||||
Log.error(err);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +87,9 @@ public class ReplayImporter {
|
|||
r.loadHeader();
|
||||
} catch (IOException e) {
|
||||
moveToFailedDirectory(file);
|
||||
ErrorHandler.error(String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName()), e, false);
|
||||
String err = String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName());
|
||||
Log.error(err, e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
continue;
|
||||
}
|
||||
Beatmap beatmap = BeatmapSetList.get().getBeatmapFromHash(r.beatmapHash);
|
||||
|
@ -100,8 +106,9 @@ public class ReplayImporter {
|
|||
}
|
||||
} else {
|
||||
moveToFailedDirectory(file);
|
||||
ErrorHandler.error(String.format("Failed to import replay '%s'. The associated beatmap could not be found.", file.getName()), null, false);
|
||||
continue;
|
||||
String err = String.format("Failed to import replay '%s'. The associated beatmap could not be found.", file.getName());
|
||||
Log.error(err);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.skins;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
|
||||
|
@ -32,6 +31,8 @@ import java.util.LinkedList;
|
|||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* Loads skin configuration files.
|
||||
|
@ -290,7 +291,9 @@ public class SkinLoader {
|
|||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error(String.format("Failed to read file '%s'.", skinFile.getAbsolutePath()), e, false);
|
||||
String err = String.format("Failed to read file '%s'.", skinFile.getAbsolutePath());
|
||||
Log.error(err, e);
|
||||
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
}
|
||||
|
||||
return skin;
|
||||
|
|
|
@ -20,7 +20,6 @@ package itdelatrisu.opsu.states;
|
|||
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.GameMod;
|
||||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.ScoreData;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
|
@ -39,114 +38,112 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.state.BasicGameState;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import org.newdawn.slick.state.transition.EmptyTransition;
|
||||
import org.newdawn.slick.state.transition.FadeInTransition;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.inject.InstanceContainer;
|
||||
import yugecin.opsudance.core.state.BaseOpsuState;
|
||||
|
||||
/**
|
||||
* Generic button menu state.
|
||||
* <p>
|
||||
* Displays a header and a set of defined options to the player.
|
||||
*/
|
||||
public class ButtonMenu extends BasicGameState {
|
||||
public class ButtonMenu extends BaseOpsuState {
|
||||
|
||||
/** Menu states. */
|
||||
public enum MenuState {
|
||||
/** The exit confirmation screen. */
|
||||
EXIT (new Button[] { Button.YES, Button.NO }) {
|
||||
@Override
|
||||
public String[] getTitle(GameContainer container, StateBasedGame game) {
|
||||
public String[] getTitle() {
|
||||
return new String[] { "Are you sure you want to exit opsu!?" };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game) {
|
||||
Button.NO.click(container, game);
|
||||
public void leave() {
|
||||
Button.NO.click();
|
||||
}
|
||||
},
|
||||
/** The initial beatmap management screen (for a non-"favorite" beatmap). */
|
||||
BEATMAP (new Button[] { Button.CLEAR_SCORES, Button.FAVORITE_ADD, Button.DELETE, Button.CANCEL }) {
|
||||
@Override
|
||||
public String[] getTitle(GameContainer container, StateBasedGame game) {
|
||||
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
|
||||
public String[] getTitle() {
|
||||
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
|
||||
String beatmapString = (node != null) ? BeatmapSetList.get().getBaseNode(node.index).toString() : "";
|
||||
return new String[] { beatmapString, "What do you want to do with this beatmap?" };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game) {
|
||||
Button.CANCEL.click(container, game);
|
||||
public void leave() {
|
||||
Button.CANCEL.click();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scroll(GameContainer container, StateBasedGame game, int newValue) {
|
||||
Input input = container.getInput();
|
||||
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT))
|
||||
super.scroll(container, game, newValue);
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) {
|
||||
super.mouseWheelMoved(newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
/** The initial beatmap management screen (for a "favorite" beatmap). */
|
||||
BEATMAP_FAVORITE (new Button[] { Button.CLEAR_SCORES, Button.FAVORITE_REMOVE, Button.DELETE, Button.CANCEL }) {
|
||||
@Override
|
||||
public String[] getTitle(GameContainer container, StateBasedGame game) {
|
||||
return BEATMAP.getTitle(container, game);
|
||||
public String[] getTitle() {
|
||||
return BEATMAP.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game) {
|
||||
BEATMAP.leave(container, game);
|
||||
public void leave() {
|
||||
BEATMAP.leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scroll(GameContainer container, StateBasedGame game, int newValue) {
|
||||
BEATMAP.scroll(container, game, newValue);
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
BEATMAP.mouseWheelMoved(newValue);
|
||||
}
|
||||
},
|
||||
/** The beatmap deletion screen for a beatmap set with multiple beatmaps. */
|
||||
BEATMAP_DELETE_SELECT (new Button[] { Button.DELETE_GROUP, Button.DELETE_SONG, Button.CANCEL_DELETE }) {
|
||||
@Override
|
||||
public String[] getTitle(GameContainer container, StateBasedGame game) {
|
||||
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
|
||||
public String[] getTitle() {
|
||||
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
|
||||
String beatmapString = (node != null) ? node.toString() : "";
|
||||
return new String[] { String.format("Are you sure you wish to delete '%s' from disk?", beatmapString) };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game) {
|
||||
Button.CANCEL_DELETE.click(container, game);
|
||||
public void leave() {
|
||||
Button.CANCEL_DELETE.click();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scroll(GameContainer container, StateBasedGame game, int newValue) {
|
||||
MenuState.BEATMAP.scroll(container, game, newValue);
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
MenuState.BEATMAP.mouseWheelMoved(newValue);
|
||||
}
|
||||
},
|
||||
/** The beatmap deletion screen for a single beatmap. */
|
||||
BEATMAP_DELETE_CONFIRM (new Button[] { Button.DELETE_CONFIRM, Button.CANCEL_DELETE }) {
|
||||
@Override
|
||||
public String[] getTitle(GameContainer container, StateBasedGame game) {
|
||||
return BEATMAP_DELETE_SELECT.getTitle(container, game);
|
||||
public String[] getTitle() {
|
||||
return BEATMAP_DELETE_SELECT.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game) {
|
||||
Button.CANCEL_DELETE.click(container, game);
|
||||
public void leave() {
|
||||
Button.CANCEL_DELETE.click();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scroll(GameContainer container, StateBasedGame game, int newValue) {
|
||||
MenuState.BEATMAP.scroll(container, game, newValue);
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
MenuState.BEATMAP.mouseWheelMoved(newValue);
|
||||
}
|
||||
},
|
||||
/** The beatmap reloading confirmation screen. */
|
||||
RELOAD (new Button[] { Button.RELOAD_CONFIRM, Button.RELOAD_CANCEL }) {
|
||||
@Override
|
||||
public String[] getTitle(GameContainer container, StateBasedGame game) {
|
||||
public String[] getTitle() {
|
||||
return new String[] {
|
||||
"You have requested a full process of your beatmaps.",
|
||||
"This could take a few minutes.",
|
||||
|
@ -155,70 +152,68 @@ public class ButtonMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game) {
|
||||
Button.RELOAD_CANCEL.click(container, game);
|
||||
public void leave() {
|
||||
Button.RELOAD_CANCEL.click();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scroll(GameContainer container, StateBasedGame game, int newValue) {
|
||||
MenuState.BEATMAP.scroll(container, game, newValue);
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
MenuState.BEATMAP.mouseWheelMoved(newValue);
|
||||
}
|
||||
},
|
||||
/** The score management screen. */
|
||||
SCORE (new Button[] { Button.DELETE_SCORE, Button.CLOSE }) {
|
||||
@Override
|
||||
public String[] getTitle(GameContainer container, StateBasedGame game) {
|
||||
public String[] getTitle() {
|
||||
return new String[] { "Score Management" };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game) {
|
||||
Button.CLOSE.click(container, game);
|
||||
public void leave() {
|
||||
Button.CLOSE.click();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scroll(GameContainer container, StateBasedGame game, int newValue) {
|
||||
MenuState.BEATMAP.scroll(container, game, newValue);
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
MenuState.BEATMAP.mouseWheelMoved(newValue);
|
||||
}
|
||||
},
|
||||
/** The game mod selection screen. */
|
||||
MODS (new Button[] { Button.RESET_MODS, Button.CLOSE }) {
|
||||
@Override
|
||||
public String[] getTitle(GameContainer container, StateBasedGame game) {
|
||||
public String[] getTitle() {
|
||||
return new String[] {
|
||||
"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getBaseY(GameContainer container, StateBasedGame game) {
|
||||
return container.getHeight() * 2f / 3;
|
||||
protected float getBaseY(DisplayContainer displayContainer) {
|
||||
return displayContainer.height * 2f / 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(GameContainer container, StateBasedGame game) {
|
||||
super.enter(container, game);
|
||||
for (GameMod mod : GameMod.values())
|
||||
public void enter() {
|
||||
super.enter();
|
||||
for (GameMod mod : GameMod.values()) {
|
||||
mod.resetHover();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game) {
|
||||
Button.CLOSE.click(container, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GameContainer container, StateBasedGame game, Graphics g) {
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
public void leave() {
|
||||
Button.CLOSE.click();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Graphics g) {
|
||||
// score multiplier (TODO: fade in color changes)
|
||||
float mult = GameMod.getScoreMultiplier();
|
||||
String multString = String.format("Score Multiplier: %.2fx", mult);
|
||||
Color multColor = (mult == 1f) ? Color.white : (mult > 1f) ? Color.green : Color.red;
|
||||
float multY = Fonts.LARGE.getLineHeight() * 2 + height * 0.06f;
|
||||
float multY = Fonts.LARGE.getLineHeight() * 2 + displayContainer.height * 0.06f;
|
||||
Fonts.LARGE.drawString(
|
||||
(width - Fonts.LARGE.getWidth(multString)) / 2f,
|
||||
(displayContainer.width - Fonts.LARGE.getWidth(multString)) / 2f,
|
||||
multY, multString, multColor);
|
||||
|
||||
// category text
|
||||
|
@ -232,27 +227,28 @@ public class ButtonMenu extends BasicGameState {
|
|||
for (GameMod mod : GameMod.values())
|
||||
mod.draw();
|
||||
|
||||
super.draw(container, game, g);
|
||||
super.render(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer container, int delta, int mouseX, int mouseY) {
|
||||
super.update(container, delta, mouseX, mouseY);
|
||||
public void preRenderUpdate() {
|
||||
super.preRenderUpdate();
|
||||
GameMod hoverMod = null;
|
||||
for (GameMod mod : GameMod.values()) {
|
||||
mod.hoverUpdate(delta, mod.isActive());
|
||||
if (hoverMod == null && mod.contains(mouseX, mouseY))
|
||||
mod.hoverUpdate(displayContainer.renderDelta, mod.isActive());
|
||||
if (hoverMod == null && mod.contains(displayContainer.mouseX, displayContainer.mouseY))
|
||||
hoverMod = mod;
|
||||
}
|
||||
|
||||
// tooltips
|
||||
if (hoverMod != null)
|
||||
UI.updateTooltip(delta, hoverMod.getDescription(), true);
|
||||
if (hoverMod != null) {
|
||||
UI.updateTooltip(displayContainer.renderDelta, hoverMod.getDescription(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPress(GameContainer container, StateBasedGame game, int key, char c) {
|
||||
super.keyPress(container, game, key, c);
|
||||
public void keyPressed(int key, char c) {
|
||||
super.keyPressed(key, c);
|
||||
for (GameMod mod : GameMod.values()) {
|
||||
if (key == mod.getKey()) {
|
||||
mod.toggle(true);
|
||||
|
@ -262,8 +258,8 @@ public class ButtonMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game, int cx, int cy) {
|
||||
super.click(container, game, cx, cy);
|
||||
public void mousePressed(int cx, int cy) {
|
||||
super.mousePressed(cx, cy);
|
||||
for (GameMod mod : GameMod.values()) {
|
||||
if (mod.contains(cx, cy)) {
|
||||
boolean prevState = mod.isActive();
|
||||
|
@ -276,11 +272,14 @@ public class ButtonMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void scroll(GameContainer container, StateBasedGame game, int newValue) {
|
||||
MenuState.BEATMAP.scroll(container, game, newValue);
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
MenuState.BEATMAP.mouseWheelMoved(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
public static DisplayContainer displayContainer;
|
||||
public static InstanceContainer instanceContainer;
|
||||
|
||||
/** The buttons in the state. */
|
||||
private final Button[] buttons;
|
||||
|
||||
|
@ -306,15 +305,10 @@ public class ButtonMenu extends BasicGameState {
|
|||
|
||||
/**
|
||||
* Initializes the menu state.
|
||||
* @param container the game container
|
||||
* @param game the game
|
||||
* @param button the center button image
|
||||
* @param buttonL the left button image
|
||||
* @param buttonR the right button image
|
||||
*/
|
||||
public void init(GameContainer container, StateBasedGame game, Image button, Image buttonL, Image buttonR) {
|
||||
float center = container.getWidth() / 2f;
|
||||
float baseY = getBaseY(container, game);
|
||||
public void revalidate(Image button, Image buttonL, Image buttonR) {
|
||||
float center = displayContainer.width / 2;
|
||||
float baseY = getBaseY(displayContainer);
|
||||
float offsetY = button.getHeight() * 1.25f;
|
||||
|
||||
menuButtons = new MenuButton[buttons.length];
|
||||
|
@ -328,25 +322,21 @@ public class ButtonMenu extends BasicGameState {
|
|||
|
||||
/**
|
||||
* Returns the base Y coordinate for the buttons.
|
||||
* @param container the game container
|
||||
* @param game the game
|
||||
*/
|
||||
protected float getBaseY(GameContainer container, StateBasedGame game) {
|
||||
float baseY = container.getHeight() * 0.2f;
|
||||
baseY += ((getTitle(container, game).length - 1) * Fonts.LARGE.getLineHeight());
|
||||
protected float getBaseY(DisplayContainer displayContainer) {
|
||||
float baseY = displayContainer.height * 0.2f;
|
||||
baseY += ((getTitle().length - 1) * Fonts.LARGE.getLineHeight());
|
||||
return baseY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the title and buttons to the graphics context.
|
||||
* @param container the game container
|
||||
* @param game the game
|
||||
* @param g the graphics context
|
||||
*/
|
||||
public void draw(GameContainer container, StateBasedGame game, Graphics g) {
|
||||
public void render(Graphics g) {
|
||||
// draw title
|
||||
if (actualTitle != null) {
|
||||
float marginX = container.getWidth() * 0.015f, marginY = container.getHeight() * 0.01f;
|
||||
float marginX = displayContainer.width * 0.015f, marginY = displayContainer.height * 0.01f;
|
||||
int lineHeight = Fonts.LARGE.getLineHeight();
|
||||
for (int i = 0, size = actualTitle.size(); i < size; i++)
|
||||
Fonts.LARGE.drawString(marginX, marginY + (i * lineHeight), actualTitle.get(i), Color.white);
|
||||
|
@ -361,17 +351,13 @@ public class ButtonMenu extends BasicGameState {
|
|||
|
||||
/**
|
||||
* Updates the menu state.
|
||||
* @param container the game container
|
||||
* @param delta the delta interval
|
||||
* @param mouseX the mouse x coordinate
|
||||
* @param mouseY the mouse y coordinate
|
||||
*/
|
||||
public void update(GameContainer container, int delta, int mouseX, int mouseY) {
|
||||
float center = container.getWidth() / 2f;
|
||||
boolean centerOffsetUpdated = centerOffset.update(delta);
|
||||
public void preRenderUpdate() {
|
||||
float center = displayContainer.width / 2f;
|
||||
boolean centerOffsetUpdated = centerOffset.update(displayContainer.renderDelta);
|
||||
float centerOffsetX = centerOffset.getValue();
|
||||
for (int i = 0; i < buttons.length; i++) {
|
||||
menuButtons[i].hoverUpdate(delta, mouseX, mouseY);
|
||||
menuButtons[i].hoverUpdate(displayContainer.renderDelta, displayContainer.mouseX, displayContainer.mouseY);
|
||||
|
||||
// move button to center
|
||||
if (centerOffsetUpdated)
|
||||
|
@ -381,15 +367,11 @@ public class ButtonMenu extends BasicGameState {
|
|||
|
||||
/**
|
||||
* Processes a mouse click action.
|
||||
* @param container the game container
|
||||
* @param game the game
|
||||
* @param cx the x coordinate
|
||||
* @param cy the y coordinate
|
||||
*/
|
||||
public void click(GameContainer container, StateBasedGame game, int cx, int cy) {
|
||||
public void mousePressed(int x, int y) {
|
||||
for (int i = 0; i < buttons.length; i++) {
|
||||
if (menuButtons[i].contains(cx, cy)) {
|
||||
buttons[i].click(container, game);
|
||||
if (menuButtons[i].contains(x, y)) {
|
||||
buttons[i].click();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -397,42 +379,34 @@ public class ButtonMenu extends BasicGameState {
|
|||
|
||||
/**
|
||||
* Processes a key press action.
|
||||
* @param container the game container
|
||||
* @param game the game
|
||||
* @param key the key code that was pressed (see {@link org.newdawn.slick.Input})
|
||||
* @param c the character of the key that was pressed
|
||||
*/
|
||||
public void keyPress(GameContainer container, StateBasedGame game, int key, char c) {
|
||||
public void keyPressed(int key, char c) {
|
||||
int index = Character.getNumericValue(c) - 1;
|
||||
if (index >= 0 && index < buttons.length)
|
||||
buttons[index].click(container, game);
|
||||
buttons[index].click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the title strings for the menu state (via override).
|
||||
* @param container the game container
|
||||
* @param game the game
|
||||
*/
|
||||
public String[] getTitle(GameContainer container, StateBasedGame game) { return new String[0]; }
|
||||
public String[] getTitle() { return new String[0]; }
|
||||
|
||||
/**
|
||||
* Processes a mouse wheel movement.
|
||||
* @param container the game container
|
||||
* @param game the game
|
||||
* @param newValue the amount that the mouse wheel moved
|
||||
*/
|
||||
public void scroll(GameContainer container, StateBasedGame game, int newValue) {
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
UI.changeVolume((newValue < 0) ? -1 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a state enter request.
|
||||
* @param container the game container
|
||||
* @param game the game
|
||||
*/
|
||||
public void enter(GameContainer container, StateBasedGame game) {
|
||||
float center = container.getWidth() / 2f;
|
||||
float centerOffsetX = container.getWidth() * OFFSET_WIDTH_RATIO;
|
||||
public void enter() {
|
||||
float center = displayContainer.width / 2f;
|
||||
float centerOffsetX = displayContainer.width * OFFSET_WIDTH_RATIO;
|
||||
centerOffset = new AnimatedValue(700, centerOffsetX, 0, AnimationEquation.OUT_BOUNCE);
|
||||
for (int i = 0; i < buttons.length; i++) {
|
||||
menuButtons[i].setX(center + ((i % 2 == 0) ? centerOffsetX : centerOffsetX * -1));
|
||||
|
@ -440,150 +414,149 @@ public class ButtonMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
// create title string list
|
||||
actualTitle = new ArrayList<String>();
|
||||
String[] title = getTitle(container, game);
|
||||
int maxLineWidth = (int) (container.getWidth() * 0.96f);
|
||||
for (int i = 0; i < title.length; i++) {
|
||||
actualTitle = new ArrayList<>();
|
||||
String[] title = getTitle();
|
||||
int maxLineWidth = (int) (displayContainer.width * 0.96f);
|
||||
for (String aTitle : title) {
|
||||
// wrap text if too long
|
||||
if (Fonts.LARGE.getWidth(title[i]) > maxLineWidth) {
|
||||
List<String> list = Fonts.wrap(Fonts.LARGE, title[i], maxLineWidth);
|
||||
if (Fonts.LARGE.getWidth(aTitle) > maxLineWidth) {
|
||||
List<String> list = Fonts.wrap(Fonts.LARGE, aTitle, maxLineWidth, false);
|
||||
actualTitle.addAll(list);
|
||||
} else
|
||||
actualTitle.add(title[i]);
|
||||
} else {
|
||||
actualTitle.add(aTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a state exit request (via override).
|
||||
* @param container the game container
|
||||
* @param game the game
|
||||
*/
|
||||
public void leave(GameContainer container, StateBasedGame game) {}
|
||||
};
|
||||
public void leave() {}
|
||||
}
|
||||
|
||||
/** Button types. */
|
||||
private enum Button {
|
||||
YES ("Yes", Color.green) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
container.exit();
|
||||
public void click() {
|
||||
displayContainer.exitRequested = true;
|
||||
}
|
||||
},
|
||||
NO ("No", Color.red) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
game.enterState(Opsu.STATE_MAINMENU, new EmptyTransition(), new FadeInTransition());
|
||||
displayContainer.switchState(MainMenu.class);
|
||||
}
|
||||
},
|
||||
CLEAR_SCORES ("Clear local scores", Color.magenta) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP, node);
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition());
|
||||
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
|
||||
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP, node);
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
}
|
||||
},
|
||||
FAVORITE_ADD ("Add to Favorites", Color.blue) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
|
||||
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
|
||||
node.getBeatmapSet().setFavorite(true);
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition());
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
}
|
||||
},
|
||||
FAVORITE_REMOVE ("Remove from Favorites", Color.blue) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
|
||||
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
|
||||
node.getBeatmapSet().setFavorite(false);
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_FAVORITE);
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition());
|
||||
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_FAVORITE);
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
}
|
||||
},
|
||||
DELETE ("Delete...", Color.red) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
|
||||
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
|
||||
MenuState ms = (node.beatmapIndex == -1 || node.getBeatmapSet().size() == 1) ?
|
||||
MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT;
|
||||
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(ms, node);
|
||||
game.enterState(Opsu.STATE_BUTTONMENU);
|
||||
instanceContainer.provide(ButtonMenu.class).setMenuState(ms, node);
|
||||
displayContainer.switchState(ButtonMenu.class);
|
||||
}
|
||||
},
|
||||
CANCEL ("Cancel", Color.gray) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition());
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
}
|
||||
},
|
||||
DELETE_CONFIRM ("Yes, delete this beatmap!", Color.red) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_DELETE_CONFIRM, node);
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition());
|
||||
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
|
||||
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_DELETE_CONFIRM, node);
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
}
|
||||
},
|
||||
DELETE_GROUP ("Yes, delete all difficulties!", Color.red) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
DELETE_CONFIRM.click(container, game);
|
||||
public void click() {
|
||||
DELETE_CONFIRM.click();
|
||||
}
|
||||
},
|
||||
DELETE_SONG ("Yes, but only this difficulty", Color.red) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_DELETE_SELECT, node);
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition());
|
||||
BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
|
||||
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_DELETE_SELECT, node);
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
}
|
||||
},
|
||||
CANCEL_DELETE ("Nooooo! I didn't mean to!", Color.gray) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
CANCEL.click(container, game);
|
||||
public void click() {
|
||||
CANCEL.click();
|
||||
}
|
||||
},
|
||||
RELOAD_CONFIRM ("Let's do it!", Color.green) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.RELOAD);
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition());
|
||||
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.RELOAD);
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
}
|
||||
},
|
||||
RELOAD_CANCEL ("Cancel", Color.red) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
CANCEL.click(container, game);
|
||||
public void click() {
|
||||
CANCEL.click();
|
||||
}
|
||||
},
|
||||
DELETE_SCORE ("Delete score", Color.green) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
ScoreData scoreData = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getScoreData();
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.SCORE, scoreData);
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition());
|
||||
ScoreData scoreData = instanceContainer.provide(ButtonMenu.class).getScoreData();
|
||||
instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.SCORE, scoreData);
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
}
|
||||
},
|
||||
CLOSE ("Close", Color.gray) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
CANCEL.click(container, game);
|
||||
public void click() {
|
||||
CANCEL.click();
|
||||
}
|
||||
},
|
||||
RESET_MODS ("Reset All Mods", Color.red) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
public void click() {
|
||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||
for (GameMod mod : GameMod.values()) {
|
||||
if (mod.isActive())
|
||||
|
@ -592,6 +565,9 @@ public class ButtonMenu extends BasicGameState {
|
|||
}
|
||||
};
|
||||
|
||||
public static DisplayContainer displayContainer;
|
||||
public static InstanceContainer instanceContainer;
|
||||
|
||||
/** The text to show on the button. */
|
||||
private final String text;
|
||||
|
||||
|
@ -620,10 +596,8 @@ public class ButtonMenu extends BasicGameState {
|
|||
|
||||
/**
|
||||
* Processes a mouse click action (via override).
|
||||
* @param container the game container
|
||||
* @param game the game
|
||||
*/
|
||||
public void click(GameContainer container, StateBasedGame game) {}
|
||||
public void click() {}
|
||||
}
|
||||
|
||||
/** The current menu state. */
|
||||
|
@ -635,97 +609,83 @@ public class ButtonMenu extends BasicGameState {
|
|||
/** The score data to process in the state. */
|
||||
private ScoreData scoreData;
|
||||
|
||||
// game-related variables
|
||||
private GameContainer container;
|
||||
private StateBasedGame game;
|
||||
private Input input;
|
||||
private final int state;
|
||||
|
||||
public ButtonMenu(int state) {
|
||||
this.state = state;
|
||||
public ButtonMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
|
||||
super(displayContainer);
|
||||
Button.displayContainer = MenuState.displayContainer = displayContainer;
|
||||
Button.instanceContainer = MenuState.instanceContainer = instanceContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
this.container = container;
|
||||
this.game = game;
|
||||
this.input = container.getInput();
|
||||
public void revalidate() {
|
||||
super.revalidate();
|
||||
|
||||
// initialize buttons
|
||||
Image button = GameImage.MENU_BUTTON_MID.getImage();
|
||||
button = button.getScaledCopy(container.getWidth() / 2, button.getHeight());
|
||||
button = button.getScaledCopy(displayContainer.width / 2, button.getHeight());
|
||||
Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage();
|
||||
Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage();
|
||||
for (MenuState ms : MenuState.values())
|
||||
ms.init(container, game, button, buttonL, buttonR);
|
||||
for (MenuState ms : MenuState.values()) {
|
||||
ms.revalidate(button, buttonL, buttonR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer container, StateBasedGame game, Graphics g)
|
||||
throws SlickException {
|
||||
public void render(Graphics g) {
|
||||
super.render(g);
|
||||
|
||||
g.setBackground(Color.black);
|
||||
if (menuState != null)
|
||||
menuState.draw(container, game, g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer container, StateBasedGame game, int delta)
|
||||
throws SlickException {
|
||||
UI.update(delta);
|
||||
MusicController.loopTrackIfEnded(false);
|
||||
if (menuState != null)
|
||||
menuState.update(container, delta, input.getMouseX(), input.getMouseY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID() { return state; }
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
// check mouse button
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON)
|
||||
if (menuState == null) {
|
||||
return;
|
||||
|
||||
if (menuState != null)
|
||||
menuState.click(container, game, x, y);
|
||||
}
|
||||
menuState.render(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
if (menuState != null)
|
||||
menuState.scroll(container, game, newValue);
|
||||
public void preRenderUpdate() {
|
||||
super.preRenderUpdate();
|
||||
|
||||
UI.update(displayContainer.renderDelta);
|
||||
MusicController.loopTrackIfEnded(false);
|
||||
menuState.preRenderUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
switch (key) {
|
||||
case Input.KEY_ESCAPE:
|
||||
if (menuState != null)
|
||||
menuState.leave(container, game);
|
||||
break;
|
||||
case Input.KEY_F7:
|
||||
Options.setNextFPS(container);
|
||||
break;
|
||||
case Input.KEY_F10:
|
||||
Options.toggleMouseDisabled();
|
||||
break;
|
||||
case Input.KEY_F12:
|
||||
Utils.takeScreenShot();
|
||||
break;
|
||||
default:
|
||||
if (menuState != null)
|
||||
menuState.keyPress(container, game, key, c);
|
||||
break;
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
menuState.mousePressed(x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
public boolean mouseWheelMoved(int newValue) {
|
||||
menuState.mouseWheelMoved(newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int key, char c) {
|
||||
if (super.keyPressed(key, c)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Input.KEY_ESCAPE) {
|
||||
menuState.leave();
|
||||
return true;
|
||||
}
|
||||
|
||||
menuState.keyPressed(key, c);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
super.enter();
|
||||
|
||||
UI.enter();
|
||||
if (menuState != null)
|
||||
menuState.enter(container, game);
|
||||
menuState.enter();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
package itdelatrisu.opsu.states;
|
||||
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
|
@ -51,17 +50,15 @@ import javax.sound.sampled.LineEvent;
|
|||
import javax.sound.sampled.LineListener;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.gui.TextField;
|
||||
import org.newdawn.slick.state.BasicGameState;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
|
||||
import org.newdawn.slick.state.transition.FadeInTransition;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.inject.InstanceContainer;
|
||||
import yugecin.opsudance.core.state.ComplexOpsuState;
|
||||
|
||||
/**
|
||||
* Downloads menu.
|
||||
|
@ -69,7 +66,10 @@ import org.newdawn.slick.util.Log;
|
|||
* Players are able to download beatmaps off of various servers and import them
|
||||
* from this state.
|
||||
*/
|
||||
public class DownloadsMenu extends BasicGameState {
|
||||
public class DownloadsMenu extends ComplexOpsuState {
|
||||
|
||||
private final InstanceContainer instanceContainer;
|
||||
|
||||
/** Delay time, in milliseconds, between each search. */
|
||||
private static final int SEARCH_DELAY = 700;
|
||||
|
||||
|
@ -288,45 +288,42 @@ public class DownloadsMenu extends BasicGameState {
|
|||
}
|
||||
}
|
||||
|
||||
// game-related variables
|
||||
private GameContainer container;
|
||||
private StateBasedGame game;
|
||||
private Input input;
|
||||
private final int state;
|
||||
|
||||
public DownloadsMenu(int state) {
|
||||
this.state = state;
|
||||
public DownloadsMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
|
||||
super(displayContainer);
|
||||
this.instanceContainer = instanceContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
this.container = container;
|
||||
this.game = game;
|
||||
this.input = container.getInput();
|
||||
public void revalidate() {
|
||||
super.revalidate();
|
||||
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
float baseX = width * 0.024f;
|
||||
float searchY = (height * 0.04f) + Fonts.LARGE.getLineHeight();
|
||||
float searchWidth = width * 0.3f;
|
||||
components.clear();
|
||||
|
||||
int width = displayContainer.width;
|
||||
int height = displayContainer.height;
|
||||
int baseX = (int) (width * 0.024f);
|
||||
int searchY = (int) (height * 0.04f + Fonts.LARGE.getLineHeight());
|
||||
int searchWidth = (int) (width * 0.3f);
|
||||
|
||||
// search
|
||||
searchTimer = SEARCH_DELAY;
|
||||
searchResultString = "Loading data from server...";
|
||||
search = new TextField(
|
||||
container, Fonts.DEFAULT, (int) baseX, (int) searchY,
|
||||
(int) searchWidth, Fonts.MEDIUM.getLineHeight()
|
||||
);
|
||||
search = new TextField(displayContainer, Fonts.DEFAULT, baseX, searchY, searchWidth, Fonts.MEDIUM.getLineHeight()) {
|
||||
@Override
|
||||
public boolean isFocusable() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
search.setFocused(true);
|
||||
search.setBackgroundColor(Colors.BLACK_BG_NORMAL);
|
||||
search.setBorderColor(Color.white);
|
||||
search.setTextColor(Color.white);
|
||||
search.setConsumeEvents(false);
|
||||
search.setMaxLength(255);
|
||||
components.add(search);
|
||||
|
||||
// page buttons
|
||||
float pageButtonY = height * 0.2f;
|
||||
float pageButtonWidth = width * 0.7f;
|
||||
int pageButtonY = (int) (height * 0.2f);
|
||||
int pageButtonWidth = (int) (width * 0.7f);
|
||||
Image prevImg = GameImage.MUSIC_PREVIOUS.getImage();
|
||||
Image nextImg = GameImage.MUSIC_NEXT.getImage();
|
||||
prevPage = new MenuButton(prevImg, baseX + prevImg.getWidth() / 2f,
|
||||
|
@ -337,25 +334,25 @@ public class DownloadsMenu extends BasicGameState {
|
|||
nextPage.setHoverExpand(1.5f);
|
||||
|
||||
// buttons
|
||||
float buttonMarginX = width * 0.004f;
|
||||
float buttonHeight = height * 0.038f;
|
||||
float resetWidth = width * 0.085f;
|
||||
float rankedWidth = width * 0.15f;
|
||||
float lowerWidth = width * 0.12f;
|
||||
float topButtonY = searchY + Fonts.MEDIUM.getLineHeight() / 2f;
|
||||
float lowerButtonY = height * 0.995f - searchY - buttonHeight / 2f;
|
||||
int buttonMarginX = (int) (width * 0.004f);
|
||||
int buttonHeight = (int) (height * 0.038f);
|
||||
int resetWidth = (int) (width * 0.085f);
|
||||
int rankedWidth = (int) (width * 0.15f);
|
||||
int lowerWidth = (int) (width * 0.12f);
|
||||
int topButtonY = (int) (searchY + Fonts.MEDIUM.getLineHeight() / 2f);
|
||||
int lowerButtonY = (int) (height * 0.995f - searchY - buttonHeight / 2f);
|
||||
Image button = GameImage.MENU_BUTTON_MID.getImage();
|
||||
Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage();
|
||||
Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage();
|
||||
buttonL = buttonL.getScaledCopy(buttonHeight / buttonL.getHeight());
|
||||
buttonR = buttonR.getScaledCopy(buttonHeight / buttonR.getHeight());
|
||||
int lrButtonWidth = buttonL.getWidth() + buttonR.getWidth();
|
||||
Image resetButtonImage = button.getScaledCopy((int) resetWidth - lrButtonWidth, (int) buttonHeight);
|
||||
Image rankedButtonImage = button.getScaledCopy((int) rankedWidth - lrButtonWidth, (int) buttonHeight);
|
||||
Image lowerButtonImage = button.getScaledCopy((int) lowerWidth - lrButtonWidth, (int) buttonHeight);
|
||||
float resetButtonWidth = resetButtonImage.getWidth() + lrButtonWidth;
|
||||
float rankedButtonWidth = rankedButtonImage.getWidth() + lrButtonWidth;
|
||||
float lowerButtonWidth = lowerButtonImage.getWidth() + lrButtonWidth;
|
||||
Image resetButtonImage = button.getScaledCopy(resetWidth - lrButtonWidth, buttonHeight);
|
||||
Image rankedButtonImage = button.getScaledCopy(rankedWidth - lrButtonWidth, buttonHeight);
|
||||
Image lowerButtonImage = button.getScaledCopy(lowerWidth - lrButtonWidth, buttonHeight);
|
||||
int resetButtonWidth = resetButtonImage.getWidth() + lrButtonWidth;
|
||||
int rankedButtonWidth = rankedButtonImage.getWidth() + lrButtonWidth;
|
||||
int lowerButtonWidth = lowerButtonImage.getWidth() + lrButtonWidth;
|
||||
clearButton = new MenuButton(lowerButtonImage, buttonL, buttonR,
|
||||
width * 0.75f + buttonMarginX + lowerButtonWidth / 2f, lowerButtonY);
|
||||
importButton = new MenuButton(lowerButtonImage, buttonL, buttonR,
|
||||
|
@ -374,8 +371,8 @@ public class DownloadsMenu extends BasicGameState {
|
|||
|
||||
// dropdown menu
|
||||
int serverWidth = (int) (width * 0.12f);
|
||||
serverMenu = new DropdownMenu<DownloadServer>(container, SERVERS,
|
||||
baseX + searchWidth + buttonMarginX * 3f + resetButtonWidth + rankedButtonWidth, searchY, serverWidth) {
|
||||
int x = baseX + searchWidth + buttonMarginX * 3 + resetButtonWidth + rankedButtonWidth;
|
||||
serverMenu = new DropdownMenu<DownloadServer>(displayContainer, SERVERS, x, searchY, serverWidth) {
|
||||
@Override
|
||||
public void itemSelected(int index, DownloadServer item) {
|
||||
resultList = null;
|
||||
|
@ -388,13 +385,14 @@ public class DownloadsMenu extends BasicGameState {
|
|||
searchResultString = "Loading data from server...";
|
||||
lastQuery = null;
|
||||
pageDir = Page.RESET;
|
||||
if (searchQuery != null)
|
||||
if (searchQuery != null) {
|
||||
searchQuery.interrupt();
|
||||
}
|
||||
resetSearchTimer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean menuClicked(int index) {
|
||||
public boolean canSelect(int index) {
|
||||
// block input during beatmap importing
|
||||
if (importThread != null)
|
||||
return false;
|
||||
|
@ -406,28 +404,25 @@ public class DownloadsMenu extends BasicGameState {
|
|||
serverMenu.setBackgroundColor(Colors.BLACK_BG_HOVER);
|
||||
serverMenu.setBorderColor(Color.black);
|
||||
serverMenu.setChevronRightColor(Color.white);
|
||||
components.add(serverMenu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer container, StateBasedGame game, Graphics g)
|
||||
throws SlickException {
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
boolean inDropdownMenu = serverMenu.contains(mouseX, mouseY);
|
||||
public void render(Graphics g) {
|
||||
super.render(g);
|
||||
|
||||
// background
|
||||
GameImage.SEARCH_BG.getImage().draw();
|
||||
|
||||
// title
|
||||
Fonts.LARGE.drawString(width * 0.024f, height * 0.03f, "Download Beatmaps!", Color.white);
|
||||
Fonts.LARGE.drawString(displayContainer.width * 0.024f, displayContainer.height * 0.03f, "Download Beatmaps!", Color.white);
|
||||
|
||||
// search
|
||||
g.setColor(Color.white);
|
||||
g.setLineWidth(2f);
|
||||
search.render(container, g);
|
||||
search.render(g);
|
||||
Fonts.BOLD.drawString(
|
||||
search.getX() + search.getWidth() * 0.01f, search.getY() + search.getHeight() * 1.3f,
|
||||
search.x + search.width * 0.01f, search.y + search.height * 1.3f,
|
||||
searchResultString, Color.white
|
||||
);
|
||||
|
||||
|
@ -446,7 +441,7 @@ public class DownloadsMenu extends BasicGameState {
|
|||
if (index >= nodes.length)
|
||||
break;
|
||||
nodes[index].drawResult(g, offset + i * DownloadNode.getButtonOffset(),
|
||||
DownloadNode.resultContains(mouseX, mouseY - offset, i) && !inDropdownMenu,
|
||||
DownloadNode.resultContains(displayContainer.mouseX, displayContainer.mouseY - offset, i) && !serverMenu.isHovered(),
|
||||
(index == focusResult), (previewID == nodes[index].getID()));
|
||||
}
|
||||
g.clearClip();
|
||||
|
@ -457,9 +452,9 @@ public class DownloadsMenu extends BasicGameState {
|
|||
|
||||
// pages
|
||||
if (nodes.length > 0) {
|
||||
float baseX = width * 0.024f;
|
||||
float buttonY = height * 0.2f;
|
||||
float buttonWidth = width * 0.7f;
|
||||
float baseX = displayContainer.width * 0.024f;
|
||||
float buttonY = displayContainer.height * 0.2f;
|
||||
float buttonWidth = displayContainer.width * 0.7f;
|
||||
Fonts.BOLD.drawString(
|
||||
baseX + (buttonWidth - Fonts.BOLD.getWidth("Page 1")) / 2f,
|
||||
buttonY - Fonts.BOLD.getLineHeight() * 1.3f,
|
||||
|
@ -473,11 +468,11 @@ public class DownloadsMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
// downloads
|
||||
float downloadsX = width * 0.75f, downloadsY = search.getY();
|
||||
float downloadsX = displayContainer.width * 0.75f, downloadsY = search.y;
|
||||
g.setColor(Colors.BLACK_BG_NORMAL);
|
||||
g.fillRect(downloadsX, downloadsY,
|
||||
width * 0.25f, height - downloadsY * 2f);
|
||||
Fonts.LARGE.drawString(downloadsX + width * 0.015f, downloadsY + height * 0.015f, "Downloads", Color.white);
|
||||
displayContainer.width * 0.25f, displayContainer.height - downloadsY * 2f);
|
||||
Fonts.LARGE.drawString(downloadsX + displayContainer.width * 0.015f, downloadsY + displayContainer.height * 0.015f, "Downloads", Color.white);
|
||||
int downloadsSize = DownloadList.get().size();
|
||||
if (downloadsSize > 0) {
|
||||
int maxDownloadsShown = DownloadNode.maxDownloadsShown();
|
||||
|
@ -493,7 +488,7 @@ public class DownloadsMenu extends BasicGameState {
|
|||
if (node == null)
|
||||
break;
|
||||
node.drawDownload(g, i * DownloadNode.getInfoHeight() + offset, index,
|
||||
DownloadNode.downloadContains(mouseX, mouseY - offset, i));
|
||||
DownloadNode.downloadContains(displayContainer.mouseX, displayContainer.mouseY - offset, i));
|
||||
}
|
||||
g.clearClip();
|
||||
|
||||
|
@ -510,13 +505,13 @@ public class DownloadsMenu extends BasicGameState {
|
|||
rankedButton.draw(Color.magenta);
|
||||
|
||||
// dropdown menu
|
||||
serverMenu.render(container, g);
|
||||
serverMenu.render(g);
|
||||
|
||||
// importing beatmaps
|
||||
if (importThread != null) {
|
||||
// darken the screen
|
||||
g.setColor(Colors.BLACK_ALPHA);
|
||||
g.fillRect(0, 0, width, height);
|
||||
g.fillRect(0, 0, displayContainer.width, displayContainer.height);
|
||||
|
||||
UI.drawLoadingProgress(g);
|
||||
}
|
||||
|
@ -529,8 +524,10 @@ public class DownloadsMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer container, StateBasedGame game, int delta)
|
||||
throws SlickException {
|
||||
public void preRenderUpdate() {
|
||||
super.preRenderUpdate();
|
||||
|
||||
int delta = displayContainer.renderDelta;
|
||||
UI.update(delta);
|
||||
if (importThread == null)
|
||||
MusicController.loopTrackIfEnded(false);
|
||||
|
@ -547,11 +544,12 @@ public class DownloadsMenu extends BasicGameState {
|
|||
|
||||
// focus new beatmap
|
||||
// NOTE: This can't be called in another thread because it makes OpenGL calls.
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(importedNode, -1, true, true);
|
||||
instanceContainer.provide(SongMenu.class).setFocus(importedNode, -1, true, true);
|
||||
}
|
||||
importThread = null;
|
||||
}
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
int mouseX = displayContainer.mouseX;
|
||||
int mouseY = displayContainer.mouseY;
|
||||
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
|
||||
prevPage.hoverUpdate(delta, mouseX, mouseY);
|
||||
nextPage.hoverUpdate(delta, mouseX, mouseY);
|
||||
|
@ -572,7 +570,6 @@ public class DownloadsMenu extends BasicGameState {
|
|||
focusTimer += delta;
|
||||
|
||||
// search
|
||||
search.setFocus(true);
|
||||
searchTimer += delta;
|
||||
if (searchTimer >= SEARCH_DELAY && importThread == null) {
|
||||
searchTimer = 0;
|
||||
|
@ -608,24 +605,25 @@ public class DownloadsMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getID() { return state; }
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
if (super.mousePressed(button, x, y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
// check mouse button
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON)
|
||||
return;
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// block input during beatmap importing
|
||||
if (importThread != null)
|
||||
return;
|
||||
if (importThread != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// back
|
||||
if (UI.getBackButton().contains(x, y)) {
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset();
|
||||
game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
return;
|
||||
displayContainer.switchState(MainMenu.class);
|
||||
return true;
|
||||
}
|
||||
|
||||
// search results
|
||||
|
@ -694,11 +692,12 @@ public class DownloadsMenu extends BasicGameState {
|
|||
}
|
||||
}.start();
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isLoaded)
|
||||
return;
|
||||
if (isLoaded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||
if (index == focusResult) {
|
||||
|
@ -725,7 +724,7 @@ public class DownloadsMenu extends BasicGameState {
|
|||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// pages
|
||||
|
@ -741,7 +740,7 @@ public class DownloadsMenu extends BasicGameState {
|
|||
searchQuery.interrupt();
|
||||
resetSearchTimer();
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (pageResultTotal < totalResults && nextPage.contains(x, y)) {
|
||||
if (lastQueryDir == Page.NEXT && searchQuery != null && !searchQuery.isComplete())
|
||||
|
@ -753,7 +752,7 @@ public class DownloadsMenu extends BasicGameState {
|
|||
if (searchQuery != null)
|
||||
searchQuery.interrupt();
|
||||
resetSearchTimer();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -763,7 +762,7 @@ public class DownloadsMenu extends BasicGameState {
|
|||
if (clearButton.contains(x, y)) {
|
||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||
DownloadList.get().clearInactiveDownloads();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (importButton.contains(x, y)) {
|
||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||
|
@ -771,7 +770,7 @@ public class DownloadsMenu extends BasicGameState {
|
|||
// import songs in new thread
|
||||
importThread = new BeatmapImportThread();
|
||||
importThread.start();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (resetButton.contains(x, y)) {
|
||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||
|
@ -781,7 +780,7 @@ public class DownloadsMenu extends BasicGameState {
|
|||
if (searchQuery != null)
|
||||
searchQuery.interrupt();
|
||||
resetSearchTimer();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (rankedButton.contains(x, y)) {
|
||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||
|
@ -791,7 +790,7 @@ public class DownloadsMenu extends BasicGameState {
|
|||
if (searchQuery != null)
|
||||
searchQuery.interrupt();
|
||||
resetSearchTimer();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// downloads
|
||||
|
@ -807,8 +806,9 @@ public class DownloadsMenu extends BasicGameState {
|
|||
if (DownloadNode.downloadIconContains(x, y - offset, i)) {
|
||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||
DownloadNode node = DownloadList.get().getNode(index);
|
||||
if (node == null)
|
||||
return;
|
||||
if (node == null) {
|
||||
return true;
|
||||
}
|
||||
Download dl = node.getDownload();
|
||||
switch (dl.getStatus()) {
|
||||
case CANCELLED:
|
||||
|
@ -822,62 +822,78 @@ public class DownloadsMenu extends BasicGameState {
|
|||
dl.cancel();
|
||||
break;
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
// check mouse button
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON)
|
||||
return;
|
||||
public boolean mouseReleased(int button, int x, int y) {
|
||||
if (super.mouseReleased(button, x, y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
startDownloadIndexPos.released();
|
||||
startResultPos.released();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
public boolean mouseWheelMoved(int newValue) {
|
||||
// change volume
|
||||
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) {
|
||||
if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) {
|
||||
UI.changeVolume((newValue < 0) ? -1 : 1);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// block input during beatmap importing
|
||||
if (importThread != null)
|
||||
return;
|
||||
if (importThread != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int shift = (newValue < 0) ? 1 : -1;
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
scrollLists(mouseX, mouseY, shift);
|
||||
scrollLists(displayContainer.mouseX, displayContainer.mouseY, shift);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
// block input during beatmap importing
|
||||
if (importThread != null)
|
||||
return;
|
||||
|
||||
// check mouse button
|
||||
if (!input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON) &&
|
||||
!input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON))
|
||||
return;
|
||||
if (importThread != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int diff = newy - oldy;
|
||||
if (diff == 0)
|
||||
return;
|
||||
if (diff == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
startDownloadIndexPos.dragged(-diff);
|
||||
startResultPos.dragged(-diff);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
public boolean keyReleased(int key, char c) {
|
||||
return super.keyReleased(key, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int key, char c) {
|
||||
if (super.keyPressed(key, c)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// block input during beatmap importing
|
||||
if (importThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12))
|
||||
return;
|
||||
if (importThread != null && key != Input.KEY_ESCAPE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case Input.KEY_ESCAPE:
|
||||
|
@ -892,16 +908,15 @@ public class DownloadsMenu extends BasicGameState {
|
|||
} else {
|
||||
// return to main menu
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset();
|
||||
game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
displayContainer.switchState(MainMenu.class);
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_ENTER:
|
||||
if (!search.getText().isEmpty()) {
|
||||
pageDir = Page.RESET;
|
||||
resetSearchTimer();
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_F5:
|
||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||
lastQuery = null;
|
||||
|
@ -909,29 +924,21 @@ public class DownloadsMenu extends BasicGameState {
|
|||
if (searchQuery != null)
|
||||
searchQuery.interrupt();
|
||||
resetSearchTimer();
|
||||
break;
|
||||
case Input.KEY_F7:
|
||||
Options.setNextFPS(container);
|
||||
break;
|
||||
case Input.KEY_F10:
|
||||
Options.toggleMouseDisabled();
|
||||
break;
|
||||
case Input.KEY_F12:
|
||||
Utils.takeScreenShot();
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
// wait for user to finish typing
|
||||
if (Character.isLetterOrDigit(c) || key == Input.KEY_BACK) {
|
||||
search.keyPressed(key, c);
|
||||
searchTimer = 0;
|
||||
pageDir = Page.RESET;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
public void enter() {
|
||||
super.enter();
|
||||
|
||||
UI.enter();
|
||||
prevPage.resetHover();
|
||||
nextPage.resetHover();
|
||||
|
@ -939,7 +946,6 @@ public class DownloadsMenu extends BasicGameState {
|
|||
importButton.resetHover();
|
||||
resetButton.resetHover();
|
||||
rankedButton.resetHover();
|
||||
serverMenu.activate();
|
||||
serverMenu.reset();
|
||||
focusResult = -1;
|
||||
startResultPos.setPosition(0);
|
||||
|
@ -953,10 +959,10 @@ public class DownloadsMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
search.setFocus(false);
|
||||
serverMenu.deactivate();
|
||||
public void leave() {
|
||||
super.leave();
|
||||
|
||||
focusComponent(search);
|
||||
SoundController.stopTrack();
|
||||
MusicController.resume();
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -19,9 +19,7 @@
|
|||
package itdelatrisu.opsu.states;
|
||||
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.audio.SoundController;
|
||||
import itdelatrisu.opsu.audio.SoundEffect;
|
||||
|
@ -31,14 +29,11 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
|||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.state.BasicGameState;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import org.newdawn.slick.state.transition.FadeInTransition;
|
||||
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.inject.InstanceContainer;
|
||||
import yugecin.opsudance.core.state.BaseOpsuState;
|
||||
|
||||
/**
|
||||
* "Game Pause/Fail" state.
|
||||
|
@ -46,33 +41,22 @@ import org.newdawn.slick.state.transition.EasedFadeOutTransition;
|
|||
* Players are able to continue the game (if applicable), retry the beatmap,
|
||||
* or return to the song menu from this state.
|
||||
*/
|
||||
public class GamePauseMenu extends BasicGameState {
|
||||
/** "Continue", "Retry", and "Back" buttons. */
|
||||
public class GamePauseMenu extends BaseOpsuState {
|
||||
|
||||
private final InstanceContainer instanceContainer;
|
||||
|
||||
private MenuButton continueButton, retryButton, backButton;
|
||||
|
||||
// game-related variables
|
||||
private GameContainer container;
|
||||
private StateBasedGame game;
|
||||
private Input input;
|
||||
private final int state;
|
||||
private Game gameState;
|
||||
private final Game gameState;
|
||||
|
||||
public GamePauseMenu(int state) {
|
||||
this.state = state;
|
||||
public GamePauseMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer, Game gameState) {
|
||||
super(displayContainer);
|
||||
this.instanceContainer = instanceContainer;
|
||||
this.gameState = gameState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
this.container = container;
|
||||
this.game = game;
|
||||
this.input = container.getInput();
|
||||
this.gameState = (Game) game.getState(Opsu.STATE_GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer container, StateBasedGame game, Graphics g)
|
||||
throws SlickException {
|
||||
public void render(Graphics g) {
|
||||
// get background image
|
||||
GameImage bg = (gameState.getRestart() == Game.Restart.LOSE) ?
|
||||
GameImage.FAIL_BACKGROUND : GameImage.PAUSE_OVERLAY;
|
||||
|
@ -97,102 +81,107 @@ public class GamePauseMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer container, StateBasedGame game, int delta)
|
||||
throws SlickException {
|
||||
public void preRenderUpdate() {
|
||||
int delta = displayContainer.renderDelta;
|
||||
UI.update(delta);
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
continueButton.hoverUpdate(delta, mouseX, mouseY);
|
||||
retryButton.hoverUpdate(delta, mouseX, mouseY);
|
||||
backButton.hoverUpdate(delta, mouseX, mouseY);
|
||||
continueButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
|
||||
retryButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
|
||||
backButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID() { return state; }
|
||||
public boolean keyPressed(int key, char c) {
|
||||
if (super.keyPressed(key, c)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
// game keys
|
||||
if (!Keyboard.isRepeatEvent()) {
|
||||
if (key == Options.getGameKeyLeft())
|
||||
mousePressed(Input.MOUSE_LEFT_BUTTON, input.getMouseX(), input.getMouseY());
|
||||
else if (key == Options.getGameKeyRight())
|
||||
mousePressed(Input.MOUSE_RIGHT_BUTTON, input.getMouseX(), input.getMouseY());
|
||||
if (key == Options.getGameKeyLeft()) {
|
||||
mousePressed(Input.MOUSE_LEFT_BUTTON, displayContainer.mouseX, displayContainer.mouseY);
|
||||
} else if (key == Options.getGameKeyRight()) {
|
||||
mousePressed(Input.MOUSE_RIGHT_BUTTON, displayContainer.mouseX, displayContainer.mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case Input.KEY_ESCAPE:
|
||||
if (key == Input.KEY_ESCAPE) {
|
||||
// 'esc' will normally unpause, but will return to song menu if health is zero
|
||||
if (gameState.getRestart() == Game.Restart.LOSE) {
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad();
|
||||
instanceContainer.provide(SongMenu.class).resetGameDataOnLoad();
|
||||
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
|
||||
if (UI.getCursor().isBeatmapSkinned())
|
||||
UI.getCursor().reset();
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
} else {
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
gameState.setRestart(Game.Restart.FALSE);
|
||||
game.enterState(Opsu.STATE_GAME);
|
||||
displayContainer.switchState(Game.class);
|
||||
}
|
||||
break;
|
||||
case Input.KEY_R:
|
||||
// restart
|
||||
if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Input.KEY_R && (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL))) {
|
||||
gameState.setRestart(Game.Restart.MANUAL);
|
||||
game.enterState(Opsu.STATE_GAME);
|
||||
}
|
||||
break;
|
||||
case Input.KEY_F7:
|
||||
Options.setNextFPS(container);
|
||||
break;
|
||||
case Input.KEY_F10:
|
||||
Options.toggleMouseDisabled();
|
||||
break;
|
||||
case Input.KEY_F12:
|
||||
Utils.takeScreenShot();
|
||||
break;
|
||||
displayContainer.switchState(Game.class);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON)
|
||||
return;
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
if (super.mousePressed(button, x, y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean loseState = (gameState.getRestart() == Game.Restart.LOSE);
|
||||
if (continueButton.contains(x, y) && !loseState) {
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
gameState.setRestart(Game.Restart.FALSE);
|
||||
game.enterState(Opsu.STATE_GAME);
|
||||
displayContainer.switchState(Game.class);
|
||||
} else if (retryButton.contains(x, y)) {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
gameState.setRestart(Game.Restart.MANUAL);
|
||||
game.enterState(Opsu.STATE_GAME);
|
||||
displayContainer.switchState(Game.class);
|
||||
} else if (backButton.contains(x, y)) {
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad();
|
||||
instanceContainer.provide(SongMenu.class).resetGameDataOnLoad();
|
||||
if (loseState)
|
||||
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
|
||||
else
|
||||
MusicController.resume();
|
||||
if (UI.getCursor().isBeatmapSkinned())
|
||||
UI.getCursor().reset();
|
||||
MusicController.setPitch(1.0f);
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
if (displayContainer.cursor.isBeatmapSkinned()) {
|
||||
displayContainer.resetCursor();
|
||||
}
|
||||
MusicController.setPitch(1.0f);
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
if (Options.isMouseWheelDisabled())
|
||||
return;
|
||||
public boolean mouseWheelMoved(int newValue) {
|
||||
if (super.mouseWheelMoved(newValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Options.isMouseWheelDisabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
UI.changeVolume((newValue < 0) ? -1 : 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
public void enter() {
|
||||
super.enter();
|
||||
|
||||
UI.enter();
|
||||
MusicController.pause();
|
||||
continueButton.resetHover();
|
||||
|
@ -200,17 +189,23 @@ public class GamePauseMenu extends BasicGameState {
|
|||
backButton.resetHover();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCloseRequest() {
|
||||
SongMenu songmenu = instanceContainer.provide(SongMenu.class);
|
||||
songmenu.resetTrackOnLoad();
|
||||
songmenu.resetGameDataOnLoad();
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all game pause/fail menu images.
|
||||
*/
|
||||
public void loadImages() {
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
|
||||
// initialize buttons
|
||||
continueButton = new MenuButton(GameImage.PAUSE_CONTINUE.getImage(), width / 2f, height * 0.25f);
|
||||
retryButton = new MenuButton(GameImage.PAUSE_RETRY.getImage(), width / 2f, height * 0.5f);
|
||||
backButton = new MenuButton(GameImage.PAUSE_BACK.getImage(), width / 2f, height * 0.75f);
|
||||
continueButton = new MenuButton(GameImage.PAUSE_CONTINUE.getImage(), displayContainer.width / 2f, displayContainer.height * 0.25f);
|
||||
retryButton = new MenuButton(GameImage.PAUSE_RETRY.getImage(), displayContainer.width / 2f, displayContainer.height * 0.5f);
|
||||
backButton = new MenuButton(GameImage.PAUSE_BACK.getImage(), displayContainer.width / 2f, displayContainer.height * 0.75f);
|
||||
final int buttonAnimationDuration = 300;
|
||||
continueButton.setHoverAnimationDuration(buttonAnimationDuration);
|
||||
retryButton.setHoverAnimationDuration(buttonAnimationDuration);
|
||||
|
@ -223,4 +218,5 @@ public class GamePauseMenu extends BasicGameState {
|
|||
retryButton.setHoverExpand();
|
||||
backButton.setHoverExpand();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,9 +21,6 @@ package itdelatrisu.opsu.states;
|
|||
import itdelatrisu.opsu.GameData;
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.GameMod;
|
||||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.audio.SoundController;
|
||||
import itdelatrisu.opsu.audio.SoundEffect;
|
||||
|
@ -35,17 +32,13 @@ import itdelatrisu.opsu.ui.UI;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.state.BasicGameState;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import org.newdawn.slick.state.transition.FadeInTransition;
|
||||
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.inject.InstanceContainer;
|
||||
import yugecin.opsudance.core.state.BaseOpsuState;
|
||||
|
||||
/**
|
||||
* "Game Ranking" (score card) state.
|
||||
|
@ -54,7 +47,10 @@ import org.newdawn.slick.util.Log;
|
|||
* or watch a replay of the game from this state.
|
||||
* </ul>
|
||||
*/
|
||||
public class GameRanking extends BasicGameState {
|
||||
public class GameRanking extends BaseOpsuState {
|
||||
|
||||
private final InstanceContainer instanceContainer;
|
||||
|
||||
/** Associated GameData object. */
|
||||
private GameData data;
|
||||
|
||||
|
@ -64,48 +60,35 @@ public class GameRanking extends BasicGameState {
|
|||
/** Button coordinates. */
|
||||
private float retryY, replayY;
|
||||
|
||||
// game-related variables
|
||||
private GameContainer container;
|
||||
private StateBasedGame game;
|
||||
private final int state;
|
||||
private Input input;
|
||||
public GameRanking(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
|
||||
super(displayContainer);
|
||||
this.instanceContainer = instanceContainer;
|
||||
|
||||
public GameRanking(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
this.container = container;
|
||||
this.game = game;
|
||||
this.input = container.getInput();
|
||||
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
public void revalidate() {
|
||||
super.revalidate();
|
||||
|
||||
// buttons
|
||||
Image retry = GameImage.PAUSE_RETRY.getImage();
|
||||
Image replay = GameImage.PAUSE_REPLAY.getImage();
|
||||
replayY = (height * 0.985f) - replay.getHeight() / 2f;
|
||||
replayY = (displayContainer.height * 0.985f) - replay.getHeight() / 2f;
|
||||
retryY = replayY - (replay.getHeight() / 2f) - (retry.getHeight() / 1.975f);
|
||||
retryButton = new MenuButton(retry, width - (retry.getWidth() / 2f), retryY);
|
||||
replayButton = new MenuButton(replay, width - (replay.getWidth() / 2f), replayY);
|
||||
retryButton = new MenuButton(retry, displayContainer.width - (retry.getWidth() / 2f), retryY);
|
||||
replayButton = new MenuButton(replay, displayContainer.width - (replay.getWidth() / 2f), replayY);
|
||||
retryButton.setHoverFade();
|
||||
replayButton.setHoverFade();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer container, StateBasedGame game, Graphics g)
|
||||
throws SlickException {
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
|
||||
public void render(Graphics g) {
|
||||
Beatmap beatmap = MusicController.getBeatmap();
|
||||
|
||||
// background
|
||||
if (!beatmap.drawBackground(width, height, 0.7f, true))
|
||||
GameImage.PLAYFIELD.getImage().draw(0,0);
|
||||
if (!beatmap.drawBackground(displayContainer.width, displayContainer.height, 0.7f, true)) {
|
||||
GameImage.PLAYFIELD.getImage().draw(0, 0);
|
||||
}
|
||||
|
||||
// ranking screen elements
|
||||
data.drawRankingElements(g, beatmap);
|
||||
|
@ -117,62 +100,62 @@ public class GameRanking extends BasicGameState {
|
|||
UI.getBackButton().draw();
|
||||
|
||||
UI.draw(g);
|
||||
|
||||
super.render(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer container, StateBasedGame game, int delta)
|
||||
throws SlickException {
|
||||
public void preRenderUpdate() {
|
||||
int delta = displayContainer.renderDelta;
|
||||
UI.update(delta);
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
replayButton.hoverUpdate(delta, mouseX, mouseY);
|
||||
if (data.isGameplay())
|
||||
retryButton.hoverUpdate(delta, mouseX, mouseY);
|
||||
else
|
||||
replayButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
|
||||
if (data.isGameplay()) {
|
||||
retryButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
|
||||
} else {
|
||||
MusicController.loopTrackIfEnded(true);
|
||||
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
|
||||
}
|
||||
UI.getBackButton().hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID() { return state; }
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT))
|
||||
public boolean mouseWheelMoved(int newValue) {
|
||||
if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) {
|
||||
UI.changeVolume((newValue < 0) ? -1 : 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
switch (key) {
|
||||
case Input.KEY_ESCAPE:
|
||||
public boolean keyPressed(int key, char c) {
|
||||
if (super.keyPressed(key, c)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Input.KEY_ESCAPE) {
|
||||
returnToSongMenu();
|
||||
break;
|
||||
case Input.KEY_F7:
|
||||
Options.setNextFPS(container);
|
||||
break;
|
||||
case Input.KEY_F10:
|
||||
Options.toggleMouseDisabled();
|
||||
break;
|
||||
case Input.KEY_F12:
|
||||
Utils.takeScreenShot();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
if (super.mousePressed(button, x, y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check mouse button
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON)
|
||||
return;
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// back to menu
|
||||
if (UI.getBackButton().contains(x, y)) {
|
||||
returnToSongMenu();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// replay
|
||||
Game gameState = (Game) game.getState(Opsu.STATE_GAME);
|
||||
Game gameState = instanceContainer.provide(Game.class);
|
||||
boolean returnToGame = false;
|
||||
boolean replayButtonPressed = replayButton.contains(x, y);
|
||||
if (replayButtonPressed && !(data.isGameplay() && GameMod.AUTO.isActive())) {
|
||||
|
@ -206,16 +189,16 @@ public class GameRanking extends BasicGameState {
|
|||
Beatmap beatmap = MusicController.getBeatmap();
|
||||
gameState.loadBeatmap(beatmap);
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
game.enterState(Opsu.STATE_GAME, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
return;
|
||||
// TODO d displayContainer.switchState(Game.class);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
public void enter() {
|
||||
super.enter();
|
||||
|
||||
UI.enter();
|
||||
Display.setTitle(game.getTitle());
|
||||
if (!data.isGameplay()) {
|
||||
if (!MusicController.isTrackDimmed())
|
||||
MusicController.toggleTrackDimmed(0.5f);
|
||||
|
@ -229,12 +212,25 @@ public class GameRanking extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
public void leave() {
|
||||
super.leave();
|
||||
|
||||
this.data = null;
|
||||
if (MusicController.isTrackDimmed())
|
||||
if (MusicController.isTrackDimmed()) {
|
||||
MusicController.toggleTrackDimmed(1f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCloseRequest() {
|
||||
SongMenu songmenu = instanceContainer.provide(SongMenu.class);
|
||||
if (data != null && data.isGameplay()) {
|
||||
songmenu.resetTrackOnLoad();
|
||||
}
|
||||
songmenu.resetGameDataOnLoad();
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns to the song menu.
|
||||
|
@ -242,13 +238,15 @@ public class GameRanking extends BasicGameState {
|
|||
private void returnToSongMenu() {
|
||||
SoundController.muteSoundComponent();
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
SongMenu songMenu = (SongMenu) game.getState(Opsu.STATE_SONGMENU);
|
||||
if (data.isGameplay())
|
||||
SongMenu songMenu = instanceContainer.provide(SongMenu.class);
|
||||
if (data.isGameplay()) {
|
||||
songMenu.resetTrackOnLoad();
|
||||
}
|
||||
songMenu.resetGameDataOnLoad();
|
||||
if (UI.getCursor().isBeatmapSkinned())
|
||||
UI.getCursor().reset();
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
if (displayContainer.cursor.isBeatmapSkinned()) {
|
||||
displayContainer.resetCursor();
|
||||
}
|
||||
displayContainer.switchState(SongMenu.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,9 +18,7 @@
|
|||
|
||||
package itdelatrisu.opsu.states;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
|
@ -29,7 +27,6 @@ import itdelatrisu.opsu.audio.SoundEffect;
|
|||
import itdelatrisu.opsu.beatmap.Beatmap;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapSetList;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapSetNode;
|
||||
import itdelatrisu.opsu.beatmap.TimingPoint;
|
||||
import itdelatrisu.opsu.downloads.Updater;
|
||||
import itdelatrisu.opsu.states.ButtonMenu.MenuState;
|
||||
import itdelatrisu.opsu.ui.*;
|
||||
|
@ -43,23 +40,28 @@ import java.text.SimpleDateFormat;
|
|||
import java.util.Date;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.state.BasicGameState;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
|
||||
import org.newdawn.slick.state.transition.FadeInTransition;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.core.inject.InstanceContainer;
|
||||
import yugecin.opsudance.core.state.BaseOpsuState;
|
||||
import yugecin.opsudance.core.state.OpsuState;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
/**
|
||||
* "Main Menu" state.
|
||||
* <p>
|
||||
* Players are able to enter the song menu or downloads menu from this state.
|
||||
*/
|
||||
public class MainMenu extends BasicGameState {
|
||||
public class MainMenu extends BaseOpsuState {
|
||||
|
||||
private final InstanceContainer instanceContainer;
|
||||
|
||||
/** Idle time, in milliseconds, before returning the logo to its original position. */
|
||||
private static final short LOGO_IDLE_DELAY = 10000;
|
||||
|
||||
|
@ -123,40 +125,27 @@ public class MainMenu extends BasicGameState {
|
|||
/** The star fountain. */
|
||||
private StarFountain starFountain;
|
||||
|
||||
// game-related variables
|
||||
private GameContainer container;
|
||||
private StateBasedGame game;
|
||||
private Input input;
|
||||
private final int state;
|
||||
|
||||
public MainMenu(int state) {
|
||||
this.state = state;
|
||||
public MainMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
|
||||
super(displayContainer);
|
||||
this.instanceContainer = instanceContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
this.container = container;
|
||||
this.game = game;
|
||||
this.input = container.getInput();
|
||||
|
||||
protected void revalidate() {
|
||||
programStartTime = System.currentTimeMillis();
|
||||
previous = new Stack<Integer>();
|
||||
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
previous = new Stack<>();
|
||||
|
||||
// initialize menu buttons
|
||||
Image logoImg = GameImage.MENU_LOGO.getImage();
|
||||
Image playImg = GameImage.MENU_PLAY.getImage();
|
||||
Image exitImg = GameImage.MENU_EXIT.getImage();
|
||||
float exitOffset = (playImg.getWidth() - exitImg.getWidth()) / 3f;
|
||||
logo = new MenuButton(logoImg, width / 2f, height / 2f);
|
||||
logo = new MenuButton(logoImg, displayContainer.width / 2f, displayContainer.height / 2f);
|
||||
playButton = new MenuButton(playImg,
|
||||
width * 0.75f, (height / 2) - (logoImg.getHeight() / 5f)
|
||||
displayContainer.width * 0.75f, (displayContainer.height / 2) - (logoImg.getHeight() / 5f)
|
||||
);
|
||||
exitButton = new MenuButton(exitImg,
|
||||
width * 0.75f - exitOffset, (height / 2) + (exitImg.getHeight() / 2f)
|
||||
displayContainer.width * 0.75f - exitOffset, (displayContainer.height / 2) + (exitImg.getHeight() / 2f)
|
||||
);
|
||||
final int logoAnimationDuration = 350;
|
||||
logo.setHoverAnimationDuration(logoAnimationDuration);
|
||||
|
@ -174,30 +163,30 @@ public class MainMenu extends BasicGameState {
|
|||
// initialize music buttons
|
||||
int musicWidth = GameImage.MUSIC_PLAY.getImage().getWidth();
|
||||
int musicHeight = GameImage.MUSIC_PLAY.getImage().getHeight();
|
||||
musicPlay = new MenuButton(GameImage.MUSIC_PLAY.getImage(), width - (2 * musicWidth), musicHeight / 1.5f);
|
||||
musicPause = new MenuButton(GameImage.MUSIC_PAUSE.getImage(), width - (2 * musicWidth), musicHeight / 1.5f);
|
||||
musicNext = new MenuButton(GameImage.MUSIC_NEXT.getImage(), width - musicWidth, musicHeight / 1.5f);
|
||||
musicPrevious = new MenuButton(GameImage.MUSIC_PREVIOUS.getImage(), width - (3 * musicWidth), musicHeight / 1.5f);
|
||||
musicPlay = new MenuButton(GameImage.MUSIC_PLAY.getImage(), displayContainer.width - (2 * musicWidth), musicHeight / 1.5f);
|
||||
musicPause = new MenuButton(GameImage.MUSIC_PAUSE.getImage(), displayContainer.width - (2 * musicWidth), musicHeight / 1.5f);
|
||||
musicNext = new MenuButton(GameImage.MUSIC_NEXT.getImage(), displayContainer.width - musicWidth, musicHeight / 1.5f);
|
||||
musicPrevious = new MenuButton(GameImage.MUSIC_PREVIOUS.getImage(), displayContainer.width - (3 * musicWidth), musicHeight / 1.5f);
|
||||
musicPlay.setHoverExpand(1.5f);
|
||||
musicPause.setHoverExpand(1.5f);
|
||||
musicNext.setHoverExpand(1.5f);
|
||||
musicPrevious.setHoverExpand(1.5f);
|
||||
|
||||
// initialize music position bar location
|
||||
musicBarX = width - musicWidth * 3.5f;
|
||||
musicBarX = displayContainer.width - musicWidth * 3.5f;
|
||||
musicBarY = musicHeight * 1.25f;
|
||||
musicBarWidth = musicWidth * 3f;
|
||||
musicBarHeight = musicHeight * 0.11f;
|
||||
|
||||
// initialize downloads button
|
||||
Image dlImg = GameImage.DOWNLOADS.getImage();
|
||||
downloadsButton = new MenuButton(dlImg, width - dlImg.getWidth() / 2f, height / 2f);
|
||||
downloadsButton = new MenuButton(dlImg, displayContainer.width - dlImg.getWidth() / 2f, displayContainer.height / 2f);
|
||||
downloadsButton.setHoverAnimationDuration(350);
|
||||
downloadsButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK);
|
||||
downloadsButton.setHoverExpand(1.03f, Expand.LEFT);
|
||||
|
||||
// initialize repository button
|
||||
float startX = width * 0.997f, startY = height * 0.997f;
|
||||
float startX = displayContainer.width * 0.997f, startY = displayContainer.height * 0.997f;
|
||||
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { // only if a webpage can be opened
|
||||
Image repoImg;
|
||||
repoImg = GameImage.REPOSITORY.getImage();
|
||||
|
@ -217,7 +206,7 @@ public class MainMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
// initialize update buttons
|
||||
float updateX = width / 2f, updateY = height * 17 / 18f;
|
||||
float updateX = displayContainer.width / 2f, updateY = displayContainer.height * 17 / 18f;
|
||||
Image downloadImg = GameImage.DOWNLOAD.getImage();
|
||||
updateButton = new MenuButton(downloadImg, updateX, updateY);
|
||||
updateButton.setHoverAnimationDuration(400);
|
||||
|
@ -230,22 +219,19 @@ public class MainMenu extends BasicGameState {
|
|||
restartButton.setHoverRotate(360);
|
||||
|
||||
// initialize star fountain
|
||||
starFountain = new StarFountain(width, height);
|
||||
starFountain = new StarFountain(displayContainer.width, displayContainer.height);
|
||||
|
||||
// logo animations
|
||||
float centerOffsetX = width / 6.5f;
|
||||
float centerOffsetX = displayContainer.width / 6.5f;
|
||||
logoOpen = new AnimatedValue(100, 0, centerOffsetX, AnimationEquation.OUT_QUAD);
|
||||
logoClose = new AnimatedValue(2200, centerOffsetX, 0, AnimationEquation.OUT_QUAD);
|
||||
logoButtonAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer container, StateBasedGame game, Graphics g)
|
||||
throws SlickException {
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
public void render(Graphics g) {
|
||||
int width = displayContainer.width;
|
||||
int height = displayContainer.height;
|
||||
|
||||
// draw background
|
||||
Beatmap beatmap = MusicController.getBeatmap();
|
||||
|
@ -305,7 +291,8 @@ public class MainMenu extends BasicGameState {
|
|||
musicPrevious.draw();
|
||||
|
||||
// draw music position bar
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
int mouseX = displayContainer.mouseX;
|
||||
int mouseY = displayContainer.mouseY;
|
||||
g.setColor((musicPositionBarContains(mouseX, mouseY)) ? Colors.BLACK_BG_HOVER : Colors.BLACK_BG_NORMAL);
|
||||
g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4);
|
||||
g.setColor(Color.white);
|
||||
|
@ -366,12 +353,14 @@ public class MainMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer container, StateBasedGame game, int delta)
|
||||
throws SlickException {
|
||||
public void preRenderUpdate() {
|
||||
int delta = displayContainer.renderDelta;
|
||||
|
||||
UI.update(delta);
|
||||
if (MusicController.trackEnded())
|
||||
nextTrack(false); // end of track: go to next track
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
int mouseX = displayContainer.mouseX;
|
||||
int mouseY = displayContainer.mouseY;
|
||||
logo.hoverUpdate(delta, mouseX, mouseY, 0.25f);
|
||||
playButton.hoverUpdate(delta, mouseX, mouseY, 0.25f);
|
||||
exitButton.hoverUpdate(delta, mouseX, mouseY, 0.25f);
|
||||
|
@ -396,7 +385,7 @@ public class MainMenu extends BasicGameState {
|
|||
|
||||
// window focus change: increase/decrease theme song volume
|
||||
if (MusicController.isThemePlaying() &&
|
||||
MusicController.isTrackDimmed() == container.hasFocus())
|
||||
MusicController.isTrackDimmed() == Display.isActive())
|
||||
MusicController.toggleTrackDimmed(0.33f);
|
||||
|
||||
// fade in background
|
||||
|
@ -413,7 +402,7 @@ public class MainMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
// buttons
|
||||
int centerX = container.getWidth() / 2;
|
||||
int centerX = displayContainer.width / 2;
|
||||
float currentLogoButtonAlpha;
|
||||
switch (logoState) {
|
||||
case DEFAULT:
|
||||
|
@ -468,11 +457,16 @@ public class MainMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getID() { return state; }
|
||||
public void enter() {
|
||||
super.enter();
|
||||
|
||||
logo.setX(displayContainer.width / 2);
|
||||
logoOpen.setTime(0);
|
||||
logoClose.setTime(0);
|
||||
logoButtonAlpha.setTime(0);
|
||||
logoTimer = 0;
|
||||
logoState = LogoState.DEFAULT;
|
||||
|
||||
@Override
|
||||
public void enter(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
UI.enter();
|
||||
if (!enterNotification) {
|
||||
if (Updater.get().getStatus() == Updater.Status.UPDATE_AVAILABLE) {
|
||||
|
@ -489,7 +483,8 @@ public class MainMenu extends BasicGameState {
|
|||
starFountain.clear();
|
||||
|
||||
// reset button hover states if mouse is not currently hovering over the button
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
int mouseX = displayContainer.mouseX;
|
||||
int mouseY = displayContainer.mouseY;
|
||||
if (!logo.contains(mouseX, mouseY, 0.25f))
|
||||
logo.resetHover();
|
||||
if (!playButton.contains(mouseX, mouseY, 0.25f))
|
||||
|
@ -515,17 +510,17 @@ public class MainMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
public void leave() {
|
||||
super.leave();
|
||||
if (MusicController.isTrackDimmed())
|
||||
MusicController.toggleTrackDimmed(1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
// check mouse button
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON)
|
||||
return;
|
||||
return false;
|
||||
|
||||
// music position bar
|
||||
if (MusicController.isPlaying()) {
|
||||
|
@ -533,7 +528,7 @@ public class MainMenu extends BasicGameState {
|
|||
lastMeasureProgress = 0f;
|
||||
float pos = (x - musicBarX) / musicBarWidth;
|
||||
MusicController.setPosition((int) (pos * MusicController.getDuration()));
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -546,29 +541,28 @@ public class MainMenu extends BasicGameState {
|
|||
MusicController.resume();
|
||||
UI.sendBarNotification("Play");
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
} else if (musicNext.contains(x, y)) {
|
||||
nextTrack(true);
|
||||
UI.sendBarNotification(">> Next");
|
||||
return;
|
||||
return true;
|
||||
} else if (musicPrevious.contains(x, y)) {
|
||||
lastMeasureProgress = 0f;
|
||||
if (!previous.isEmpty()) {
|
||||
SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU);
|
||||
menu.setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false);
|
||||
instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false);
|
||||
if (Options.isDynamicBackgroundEnabled())
|
||||
bgAlpha.setTime(0);
|
||||
} else
|
||||
MusicController.setPosition(0);
|
||||
UI.sendBarNotification("<< Previous");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// downloads button actions
|
||||
if (downloadsButton.contains(x, y)) {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
return;
|
||||
displayContainer.switchState(DownloadsMenu.class);
|
||||
return true;
|
||||
}
|
||||
|
||||
// repository button actions
|
||||
|
@ -578,9 +572,10 @@ public class MainMenu extends BasicGameState {
|
|||
} catch (UnsupportedOperationException e) {
|
||||
UI.sendBarNotification("The repository web page could not be opened.");
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error("Could not browse to repository URI.", e, false);
|
||||
Log.error("could not browse to repo", e);
|
||||
displayContainer.eventBus.post(new BubbleNotificationEvent("Could not browse to repo", BubbleNotificationEvent.COLOR_ORANGE));
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (danceRepoButton != null && danceRepoButton.contains(x, y)) {
|
||||
|
@ -589,9 +584,10 @@ public class MainMenu extends BasicGameState {
|
|||
} catch (UnsupportedOperationException e) {
|
||||
UI.sendBarNotification("The repository web page could not be opened.");
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error("Could not browse to repository URI.", e, false);
|
||||
Log.error("could not browse to repo", e);
|
||||
displayContainer.eventBus.post(new BubbleNotificationEvent("Could not browse to repo", BubbleNotificationEvent.COLOR_ORANGE));
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// update button actions
|
||||
|
@ -604,13 +600,12 @@ public class MainMenu extends BasicGameState {
|
|||
updateButton.setHoverAnimationDuration(800);
|
||||
updateButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_QUAD);
|
||||
updateButton.setHoverFade(0.6f);
|
||||
return;
|
||||
return true;
|
||||
} else if (restartButton.contains(x, y) && status == Updater.Status.UPDATE_DOWNLOADED) {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
Updater.get().prepareUpdate();
|
||||
container.setForceExit(false);
|
||||
container.exit();
|
||||
return;
|
||||
displayContainer.exitRequested = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,7 +618,7 @@ public class MainMenu extends BasicGameState {
|
|||
playButton.getImage().setAlpha(0f);
|
||||
exitButton.getImage().setAlpha(0f);
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -632,21 +627,27 @@ public class MainMenu extends BasicGameState {
|
|||
if (logo.contains(x, y, 0.25f) || playButton.contains(x, y, 0.25f)) {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
enterSongMenu();
|
||||
return;
|
||||
return true;
|
||||
} else if (exitButton.contains(x, y, 0.25f)) {
|
||||
container.exit();
|
||||
return;
|
||||
displayContainer.exitRequested = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
public boolean mouseWheelMoved(int newValue) {
|
||||
UI.changeVolume((newValue < 0) ? -1 : 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
public boolean keyPressed(int key, char c) {
|
||||
if (super.keyPressed(key, c)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case Input.KEY_ESCAPE:
|
||||
case Input.KEY_Q:
|
||||
|
@ -656,9 +657,9 @@ public class MainMenu extends BasicGameState {
|
|||
logoTimer = 0;
|
||||
break;
|
||||
}
|
||||
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.EXIT);
|
||||
game.enterState(Opsu.STATE_BUTTONMENU);
|
||||
break;
|
||||
instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.EXIT);
|
||||
displayContainer.switchState(ButtonMenu.class);
|
||||
return true;
|
||||
case Input.KEY_P:
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
if (logoState == LogoState.DEFAULT || logoState == LogoState.CLOSING) {
|
||||
|
@ -669,30 +670,22 @@ public class MainMenu extends BasicGameState {
|
|||
exitButton.getImage().setAlpha(0f);
|
||||
} else
|
||||
enterSongMenu();
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_D:
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
break;
|
||||
displayContainer.switchState(DownloadsMenu.class);
|
||||
return true;
|
||||
case Input.KEY_R:
|
||||
nextTrack(true);
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_UP:
|
||||
UI.changeVolume(1);
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_DOWN:
|
||||
UI.changeVolume(-1);
|
||||
break;
|
||||
case Input.KEY_F7:
|
||||
Options.setNextFPS(container);
|
||||
break;
|
||||
case Input.KEY_F10:
|
||||
Options.toggleMouseDisabled();
|
||||
break;
|
||||
case Input.KEY_F12:
|
||||
Utils.takeScreenShot();
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -705,34 +698,6 @@ public class MainMenu extends BasicGameState {
|
|||
(cy > musicBarY && cy < musicBarY + musicBarHeight));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the button states.
|
||||
*/
|
||||
public void reset() {
|
||||
// reset logo
|
||||
logo.setX(container.getWidth() / 2);
|
||||
logoOpen.setTime(0);
|
||||
logoClose.setTime(0);
|
||||
logoButtonAlpha.setTime(0);
|
||||
logoTimer = 0;
|
||||
logoState = LogoState.DEFAULT;
|
||||
|
||||
logo.resetHover();
|
||||
playButton.resetHover();
|
||||
exitButton.resetHover();
|
||||
musicPlay.resetHover();
|
||||
musicPause.resetHover();
|
||||
musicNext.resetHover();
|
||||
musicPrevious.resetHover();
|
||||
if (repoButton != null)
|
||||
repoButton.resetHover();
|
||||
if (danceRepoButton != null)
|
||||
danceRepoButton.resetHover();
|
||||
updateButton.resetHover();
|
||||
restartButton.resetHover();
|
||||
downloadsButton.resetHover();
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the next track, and adds the previous one to the stack.
|
||||
* @param user {@code true} if this was user-initiated, false otherwise (track end)
|
||||
|
@ -746,8 +711,7 @@ public class MainMenu extends BasicGameState {
|
|||
MusicController.playAt(0, false);
|
||||
return;
|
||||
}
|
||||
SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU);
|
||||
BeatmapSetNode node = menu.setFocus(BeatmapSetList.get().getRandomNode(), -1, true, false);
|
||||
BeatmapSetNode node = instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, false);
|
||||
boolean sameAudio = false;
|
||||
if (node != null) {
|
||||
sameAudio = MusicController.getBeatmap().audioFilename.equals(node.getBeatmapSet().get(0).audioFilename);
|
||||
|
@ -762,11 +726,11 @@ public class MainMenu extends BasicGameState {
|
|||
* Enters the song menu, or the downloads menu if no beatmaps are loaded.
|
||||
*/
|
||||
private void enterSongMenu() {
|
||||
int state = Opsu.STATE_SONGMENU;
|
||||
Class<? extends OpsuState> state = SongMenu.class;
|
||||
if (BeatmapSetList.get().getMapSetCount() == 0) {
|
||||
((DownloadsMenu) game.getState(Opsu.STATE_DOWNLOADSMENU)).notifyOnLoad("Download some beatmaps to get started!");
|
||||
state = Opsu.STATE_DOWNLOADSMENU;
|
||||
instanceContainer.provide(DownloadsMenu.class).notifyOnLoad("Download some beatmaps to get started!");
|
||||
state = DownloadsMenu.class;
|
||||
}
|
||||
game.enterState(state, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
displayContainer.switchState(state);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,35 +18,14 @@
|
|||
|
||||
package itdelatrisu.opsu.states;
|
||||
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Options.GameOption;
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.audio.SoundController;
|
||||
import itdelatrisu.opsu.audio.SoundEffect;
|
||||
import itdelatrisu.opsu.ui.UI;
|
||||
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.state.BasicGameState;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import org.newdawn.slick.state.transition.FadeInTransition;
|
||||
import org.newdawn.slick.state.transition.EmptyTransition;
|
||||
import yugecin.opsudance.ui.OptionsOverlay;
|
||||
import yugecin.opsudance.ui.OptionsOverlay.OptionTab;
|
||||
|
||||
/**
|
||||
* "Game Options" state.
|
||||
* <p>
|
||||
* Players are able to view and change various game settings in this state.
|
||||
*/
|
||||
public class OptionsMenu extends BasicGameState implements OptionsOverlay.Parent {
|
||||
public class OptionsMenu {
|
||||
|
||||
/** Option tabs. */
|
||||
private static final OptionTab[] options = new OptionsOverlay.OptionTab[]{
|
||||
public static final OptionTab[] normalOptions = new OptionsOverlay.OptionTab[]{
|
||||
new OptionTab("Display", new GameOption[]{
|
||||
GameOption.SCREEN_RESOLUTION,
|
||||
GameOption.FULLSCREEN,
|
||||
|
@ -154,104 +133,63 @@ public class OptionsMenu extends BasicGameState implements OptionsOverlay.Parent
|
|||
})
|
||||
};
|
||||
|
||||
private StateBasedGame game;
|
||||
private Input input;
|
||||
private final int state;
|
||||
|
||||
private OptionsOverlay optionsOverlay;
|
||||
|
||||
public OptionsMenu(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
this.game = game;
|
||||
this.input = container.getInput();
|
||||
|
||||
optionsOverlay = new OptionsOverlay(this, options, 5, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException {
|
||||
// background
|
||||
GameImage.OPTIONS_BG.getImage().draw();
|
||||
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
optionsOverlay.render(g, mouseX, mouseY);
|
||||
UI.draw(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException {
|
||||
UI.update(delta);
|
||||
MusicController.loopTrackIfEnded(false);
|
||||
optionsOverlay.update(delta, input.getMouseX(), input.getMouseY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
optionsOverlay.mouseReleased(button, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
optionsOverlay.mousePressed(button, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
optionsOverlay.mouseDragged(oldx, oldy, newx, newy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
optionsOverlay.mouseWheelMoved(newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
optionsOverlay.keyPressed(key, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* This string is built with option values when entering the options menu.
|
||||
* When leaving the options menu, this string is checked against the new optionstring with the same options.
|
||||
* If those do not match, it means some option has change which requires a restart
|
||||
*/
|
||||
private String restartOptions;
|
||||
|
||||
@Override
|
||||
public void enter(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
UI.enter();
|
||||
restartOptions = "" + Options.getResolutionIdx() + Options.isFullscreen() + Options.allowLargeResolutions() + Options.getSkinName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game) throws SlickException {
|
||||
if (!("" + Options.getResolutionIdx() + Options.isFullscreen() + Options.allowLargeResolutions() + Options.getSkinName()).equals(restartOptions)) {
|
||||
container.setForceExit(false);
|
||||
container.exit();
|
||||
return;
|
||||
}
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLeave() {
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveOption(GameOption option) {
|
||||
|
||||
}
|
||||
public static final OptionTab[] storyboardOptions = new OptionsOverlay.OptionTab[]{
|
||||
new OptionTab("Gameplay", new GameOption[] {
|
||||
GameOption.BACKGROUND_DIM,
|
||||
GameOption.DANCE_REMOVE_BG,
|
||||
GameOption.SNAKING_SLIDERS,
|
||||
GameOption.SHRINKING_SLIDERS,
|
||||
GameOption.SHOW_HIT_LIGHTING,
|
||||
GameOption.SHOW_HIT_ANIMATIONS,
|
||||
GameOption.SHOW_COMBO_BURSTS,
|
||||
GameOption.SHOW_PERFECT_HIT,
|
||||
GameOption.SHOW_FOLLOW_POINTS,
|
||||
}),
|
||||
new OptionTab("Input", new GameOption[] {
|
||||
GameOption.CURSOR_SIZE,
|
||||
GameOption.NEW_CURSOR,
|
||||
GameOption.DISABLE_CURSOR
|
||||
}),
|
||||
new OptionTab("Dance", new GameOption[] {
|
||||
GameOption.DANCE_MOVER,
|
||||
GameOption.DANCE_EXGON_DELAY,
|
||||
GameOption.DANCE_QUAD_BEZ_AGGRESSIVENESS,
|
||||
GameOption.DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR,
|
||||
GameOption.DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS,
|
||||
GameOption.DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR,
|
||||
GameOption.DANCE_MOVER_DIRECTION,
|
||||
GameOption.DANCE_SLIDER_MOVER_TYPE,
|
||||
GameOption.DANCE_SPINNER,
|
||||
GameOption.DANCE_SPINNER_DELAY,
|
||||
GameOption.DANCE_LAZY_SLIDERS,
|
||||
GameOption.DANCE_CIRCLE_STREAMS,
|
||||
GameOption.DANCE_ONLY_CIRCLE_STACKS,
|
||||
GameOption.DANCE_CIRLCE_IN_SLOW_SLIDERS,
|
||||
GameOption.DANCE_CIRLCE_IN_LAZY_SLIDERS,
|
||||
GameOption.DANCE_MIRROR,
|
||||
}),
|
||||
new OptionTab("Dance display", new GameOption[] {
|
||||
GameOption.DANCE_DRAW_APPROACH,
|
||||
GameOption.DANCE_OBJECT_COLOR_OVERRIDE,
|
||||
GameOption.DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED,
|
||||
GameOption.DANCE_RGB_OBJECT_INC,
|
||||
GameOption.DANCE_CURSOR_COLOR_OVERRIDE,
|
||||
GameOption.DANCE_CURSOR_MIRROR_COLOR_OVERRIDE,
|
||||
GameOption.DANCE_CURSOR_ONLY_COLOR_TRAIL,
|
||||
GameOption.DANCE_RGB_CURSOR_INC,
|
||||
GameOption.DANCE_CURSOR_TRAIL_OVERRIDE,
|
||||
GameOption.DANCE_HIDE_OBJECTS,
|
||||
GameOption.DANCE_HIDE_UI,
|
||||
GameOption.DANCE_HIDE_WATERMARK,
|
||||
}),
|
||||
new OptionTab ("Pippi", new GameOption[] {
|
||||
GameOption.PIPPI_ENABLE,
|
||||
GameOption.PIPPI_RADIUS_PERCENT,
|
||||
GameOption.PIPPI_ANGLE_INC_MUL,
|
||||
GameOption.PIPPI_ANGLE_INC_MUL_SLIDER,
|
||||
GameOption.PIPPI_SLIDER_FOLLOW_EXPAND,
|
||||
GameOption.PIPPI_PREVENT_WOBBLY_STREAMS,
|
||||
})
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import itdelatrisu.opsu.GameData;
|
|||
import itdelatrisu.opsu.GameData.Grade;
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.GameMod;
|
||||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.ScoreData;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
|
@ -62,21 +61,17 @@ import java.nio.file.WatchEvent.Kind;
|
|||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.newdawn.slick.Animation;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.SpriteSheet;
|
||||
import org.newdawn.slick.gui.TextField;
|
||||
import org.newdawn.slick.state.BasicGameState;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
|
||||
import org.newdawn.slick.state.transition.EmptyTransition;
|
||||
import org.newdawn.slick.state.transition.FadeInTransition;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.inject.InstanceContainer;
|
||||
import yugecin.opsudance.core.state.ComplexOpsuState;
|
||||
import yugecin.opsudance.ui.OptionsOverlay;
|
||||
|
||||
/**
|
||||
* "Song Selection" state.
|
||||
|
@ -84,7 +79,10 @@ import org.newdawn.slick.state.transition.FadeInTransition;
|
|||
* Players are able to select a beatmap to play, view previous scores, choose game mods,
|
||||
* manage beatmaps, or change game options from this state.
|
||||
*/
|
||||
public class SongMenu extends BasicGameState {
|
||||
public class SongMenu extends ComplexOpsuState {
|
||||
|
||||
private final InstanceContainer instanceContainer;
|
||||
|
||||
/** The max number of song buttons to be shown on each screen. */
|
||||
public static final int MAX_SONG_BUTTONS = 6;
|
||||
|
||||
|
@ -169,7 +167,7 @@ public class SongMenu extends BasicGameState {
|
|||
private MenuButton selectModsButton, selectRandomButton, selectMapOptionsButton, selectOptionsButton;
|
||||
|
||||
/** The search textfield. */
|
||||
private TextField search;
|
||||
private TextField searchTextField;
|
||||
|
||||
/**
|
||||
* Delay timer, in milliseconds, before running another search.
|
||||
|
@ -230,7 +228,7 @@ public class SongMenu extends BasicGameState {
|
|||
} finally {
|
||||
finished = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Reloads all beatmaps. */
|
||||
private void reloadBeatmaps() {
|
||||
|
@ -323,45 +321,41 @@ public class SongMenu extends BasicGameState {
|
|||
/** Sort order dropdown menu. */
|
||||
private DropdownMenu<BeatmapSortOrder> sortMenu;
|
||||
|
||||
// game-related variables
|
||||
private GameContainer container;
|
||||
private StateBasedGame game;
|
||||
private Input input;
|
||||
private final int state;
|
||||
private final OptionsOverlay optionsOverlay;
|
||||
|
||||
public SongMenu(int state) {
|
||||
this.state = state;
|
||||
public SongMenu(final DisplayContainer displayContainer, InstanceContainer instanceContainer) {
|
||||
super(displayContainer);
|
||||
this.instanceContainer = instanceContainer;
|
||||
optionsOverlay = new OptionsOverlay(displayContainer, OptionsMenu.normalOptions, 0);
|
||||
overlays.add(optionsOverlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
this.container = container;
|
||||
this.game = game;
|
||||
this.input = container.getInput();
|
||||
public void revalidate() {
|
||||
super.revalidate();
|
||||
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
components.clear();
|
||||
|
||||
// header/footer coordinates
|
||||
headerY = height * 0.0075f + GameImage.MENU_MUSICNOTE.getImage().getHeight() +
|
||||
headerY = displayContainer.height * 0.0075f + GameImage.MENU_MUSICNOTE.getImage().getHeight() +
|
||||
Fonts.BOLD.getLineHeight() + Fonts.DEFAULT.getLineHeight() +
|
||||
Fonts.SMALL.getLineHeight();
|
||||
footerY = height - GameImage.SELECTION_MODS.getImage().getHeight();
|
||||
footerY = displayContainer.height - GameImage.SELECTION_MODS.getImage().getHeight();
|
||||
|
||||
// footer logo coordinates
|
||||
float footerHeight = height - footerY;
|
||||
float footerHeight = displayContainer.height - footerY;
|
||||
footerLogoSize = footerHeight * 3.25f;
|
||||
Image logo = GameImage.MENU_LOGO.getImage();
|
||||
logo = logo.getScaledCopy(footerLogoSize / logo.getWidth());
|
||||
footerLogoButton = new MenuButton(logo, width - footerHeight * 0.8f, height - footerHeight * 0.65f);
|
||||
footerLogoButton = new MenuButton(logo, displayContainer.width - footerHeight * 0.8f, displayContainer.height - footerHeight * 0.65f);
|
||||
footerLogoButton.setHoverAnimationDuration(1);
|
||||
footerLogoButton.setHoverExpand(1.2f);
|
||||
|
||||
// initialize sorts
|
||||
int sortWidth = (int) (width * 0.12f);
|
||||
sortMenu = new DropdownMenu<BeatmapSortOrder>(container, BeatmapSortOrder.values(),
|
||||
width * 0.87f, headerY - GameImage.MENU_TAB.getImage().getHeight() * 2.25f, sortWidth) {
|
||||
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) {
|
||||
@Override
|
||||
public void itemSelected(int index, BeatmapSortOrder item) {
|
||||
BeatmapSortOrder.set(item);
|
||||
|
@ -375,7 +369,7 @@ public class SongMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean menuClicked(int index) {
|
||||
public boolean canSelect(int index) {
|
||||
if (isInputBlocked())
|
||||
return false;
|
||||
|
||||
|
@ -386,36 +380,40 @@ public class SongMenu extends BasicGameState {
|
|||
sortMenu.setBackgroundColor(Colors.BLACK_BG_HOVER);
|
||||
sortMenu.setBorderColor(Colors.BLUE_DIVIDER);
|
||||
sortMenu.setChevronRightColor(Color.white);
|
||||
components.add(sortMenu);
|
||||
|
||||
// initialize group tabs
|
||||
for (BeatmapGroup group : BeatmapGroup.values())
|
||||
group.init(width, headerY - DIVIDER_LINE_WIDTH / 2);
|
||||
group.init(displayContainer.width, headerY - DIVIDER_LINE_WIDTH / 2);
|
||||
|
||||
// initialize score data buttons
|
||||
ScoreData.init(width, headerY + height * 0.01f);
|
||||
ScoreData.init(displayContainer.width, headerY + displayContainer.height * 0.01f);
|
||||
|
||||
// song button background & graphics context
|
||||
Image menuBackground = GameImage.MENU_BUTTON_BG.getImage();
|
||||
|
||||
// song button coordinates
|
||||
buttonX = width * 0.6f;
|
||||
buttonX = displayContainer.width * 0.6f;
|
||||
//buttonY = headerY;
|
||||
buttonWidth = menuBackground.getWidth();
|
||||
buttonHeight = menuBackground.getHeight();
|
||||
buttonOffset = (footerY - headerY - DIVIDER_LINE_WIDTH) / MAX_SONG_BUTTONS;
|
||||
|
||||
// search
|
||||
int textFieldX = (int) (width * 0.7125f + Fonts.BOLD.getWidth("Search: "));
|
||||
int textFieldX = (int) (displayContainer.width * 0.7125f + Fonts.BOLD.getWidth("Search: "));
|
||||
int textFieldY = (int) (headerY + Fonts.BOLD.getLineHeight() / 2);
|
||||
search = new TextField(
|
||||
container, Fonts.BOLD, textFieldX, textFieldY,
|
||||
(int) (width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight()
|
||||
);
|
||||
search.setBackgroundColor(Color.transparent);
|
||||
search.setBorderColor(Color.transparent);
|
||||
search.setTextColor(Color.white);
|
||||
search.setConsumeEvents(false);
|
||||
search.setMaxLength(60);
|
||||
searchTextField = new TextField(displayContainer, Fonts.BOLD, textFieldX, textFieldY, (int) (displayContainer.width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight()) {
|
||||
@Override
|
||||
public boolean isFocusable() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
searchTextField.setBackgroundColor(Color.transparent);
|
||||
searchTextField.setBorderColor(Color.transparent);
|
||||
searchTextField.setTextColor(Color.white);
|
||||
searchTextField.setMaxLength(60);
|
||||
searchTextField.setFocused(true);
|
||||
components.add(searchTextField);
|
||||
|
||||
// selection buttons
|
||||
Image selectionMods = GameImage.SELECTION_MODS.getImage();
|
||||
|
@ -427,8 +425,8 @@ public class SongMenu extends BasicGameState {
|
|||
if (selectButtonsWidth < 20) {
|
||||
selectButtonsWidth = 100;
|
||||
}
|
||||
float selectX = width * 0.183f + selectButtonsWidth / 2f;
|
||||
float selectY = height - selectButtonsHeight / 2f;
|
||||
float selectX = displayContainer.width * 0.183f + selectButtonsWidth / 2f;
|
||||
float selectY = displayContainer.height - selectButtonsHeight / 2f;
|
||||
float selectOffset = selectButtonsWidth * 1.05f;
|
||||
selectModsButton = new MenuButton(GameImage.SELECTION_MODS_OVERLAY.getImage(),
|
||||
selectX, selectY);
|
||||
|
@ -449,33 +447,32 @@ public class SongMenu extends BasicGameState {
|
|||
loader = new Animation(spr, 50);
|
||||
|
||||
// beatmap watch service listener
|
||||
final StateBasedGame game_ = game;
|
||||
BeatmapWatchService.addListener(new BeatmapWatchServiceListener() {
|
||||
@Override
|
||||
public void eventReceived(Kind<?> kind, Path child) {
|
||||
if (!songFolderChanged && kind != StandardWatchEventKinds.ENTRY_MODIFY) {
|
||||
songFolderChanged = true;
|
||||
if (game_.getCurrentStateID() == Opsu.STATE_SONGMENU)
|
||||
if (displayContainer.isInState(SongMenu.class)) {
|
||||
UI.sendBarNotification("Changes in Songs folder detected. Hit F5 to refresh.");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// star stream
|
||||
starStream = new StarStream(width, (height - GameImage.STAR.getImage().getHeight()) / 2, -width, 0, MAX_STREAM_STARS);
|
||||
starStream.setPositionSpread(height / 20f);
|
||||
starStream = new StarStream(displayContainer.width, (displayContainer.height - GameImage.STAR.getImage().getHeight()) / 2, -displayContainer.width, 0, MAX_STREAM_STARS);
|
||||
starStream.setPositionSpread(displayContainer.height / 20f);
|
||||
starStream.setDirectionSpread(10f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer container, StateBasedGame game, Graphics g)
|
||||
throws SlickException {
|
||||
public void render(Graphics g) {
|
||||
g.setBackground(Color.black);
|
||||
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
boolean inDropdownMenu = sortMenu.contains(mouseX, mouseY);
|
||||
int width = displayContainer.width;
|
||||
int height = displayContainer.height;
|
||||
int mouseX = displayContainer.mouseX;
|
||||
int mouseY = displayContainer.mouseY;
|
||||
|
||||
// background
|
||||
if (focusNode != null) {
|
||||
|
@ -547,9 +544,10 @@ public class SongMenu extends BasicGameState {
|
|||
g.clearClip();
|
||||
|
||||
// scroll bar
|
||||
if (focusScores.length > MAX_SCORE_BUTTONS && ScoreData.areaContains(mouseX, mouseY) && !inDropdownMenu)
|
||||
if (focusScores.length > MAX_SCORE_BUTTONS && ScoreData.areaContains(mouseX, mouseY) && !isAnyComponentFocused()) {
|
||||
ScoreData.drawScrollbar(g, startScorePos.getPosition(), focusScores.length * ScoreData.getButtonOffset());
|
||||
}
|
||||
}
|
||||
|
||||
// top/bottom bars
|
||||
g.setColor(Colors.BLACK_ALPHA);
|
||||
|
@ -565,7 +563,7 @@ public class SongMenu extends BasicGameState {
|
|||
Float position = MusicController.getBeatProgress();
|
||||
if (position == null) // default to 60bpm
|
||||
position = System.currentTimeMillis() % 1000 / 1000f;
|
||||
if (footerLogoButton.contains(mouseX, mouseY, 0.25f) && !inDropdownMenu) {
|
||||
if (footerLogoButton.contains(mouseX, mouseY, 0.25f)) {
|
||||
// hovering over logo: stop pulsing
|
||||
footerLogoButton.draw();
|
||||
} else {
|
||||
|
@ -658,7 +656,7 @@ public class SongMenu extends BasicGameState {
|
|||
// group tabs
|
||||
BeatmapGroup currentGroup = BeatmapGroup.current();
|
||||
BeatmapGroup hoverGroup = null;
|
||||
if (!inDropdownMenu) {
|
||||
if (!isAnyComponentFocused()) {
|
||||
for (BeatmapGroup group : BeatmapGroup.values()) {
|
||||
if (group.contains(mouseX, mouseY)) {
|
||||
hoverGroup = group;
|
||||
|
@ -673,8 +671,9 @@ public class SongMenu extends BasicGameState {
|
|||
currentGroup.draw(true, false);
|
||||
|
||||
// search
|
||||
boolean searchEmpty = search.getText().isEmpty();
|
||||
int searchX = search.getX(), searchY = search.getY();
|
||||
boolean searchEmpty = searchTextField.getText().isEmpty();
|
||||
int searchX = searchTextField.x;
|
||||
int searchY = searchTextField.y;
|
||||
float searchBaseX = width * 0.7f;
|
||||
float searchTextX = width * 0.7125f;
|
||||
float searchRectHeight = Fonts.BOLD.getLineHeight() * 2;
|
||||
|
@ -693,20 +692,15 @@ public class SongMenu extends BasicGameState {
|
|||
g.fillRect(searchBaseX, headerY + DIVIDER_LINE_WIDTH / 2, width - searchBaseX, searchRectHeight);
|
||||
Colors.BLACK_ALPHA.a = oldAlpha;
|
||||
Fonts.BOLD.drawString(searchTextX, searchY, "Search:", Colors.GREEN_SEARCH);
|
||||
if (searchEmpty)
|
||||
if (searchEmpty) {
|
||||
Fonts.BOLD.drawString(searchX, searchY, "Type to search!", Color.white);
|
||||
else {
|
||||
} else {
|
||||
g.setColor(Color.white);
|
||||
// TODO: why is this needed to correctly position the TextField?
|
||||
search.setLocation(searchX - 3, searchY - 1);
|
||||
search.render(container, g);
|
||||
search.setLocation(searchX, searchY);
|
||||
Fonts.DEFAULT.drawString(searchTextX, searchY + Fonts.BOLD.getLineHeight(),
|
||||
(searchResultString == null) ? "Searching..." : searchResultString, Color.white);
|
||||
searchTextField.render(g);
|
||||
Fonts.DEFAULT.drawString(searchTextX, searchY + Fonts.BOLD.getLineHeight(), (searchResultString == null) ? "Searching..." : searchResultString, Color.white);
|
||||
}
|
||||
|
||||
// sorting options
|
||||
sortMenu.render(container, g);
|
||||
sortMenu.render(g);
|
||||
|
||||
// reloading beatmaps
|
||||
if (reloadThread != null) {
|
||||
|
@ -722,11 +716,15 @@ public class SongMenu extends BasicGameState {
|
|||
UI.getBackButton().draw();
|
||||
|
||||
UI.draw(g);
|
||||
|
||||
super.render(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer container, StateBasedGame game, int delta)
|
||||
throws SlickException {
|
||||
public void preRenderUpdate() {
|
||||
super.preRenderUpdate();
|
||||
|
||||
int delta = displayContainer.renderDelta;
|
||||
UI.update(delta);
|
||||
if (reloadThread == null)
|
||||
MusicController.loopTrackIfEnded(true);
|
||||
|
@ -742,8 +740,8 @@ public class SongMenu extends BasicGameState {
|
|||
MusicController.playThemeSong();
|
||||
reloadThread = null;
|
||||
}
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
boolean inDropdownMenu = sortMenu.contains(mouseX, mouseY);
|
||||
int mouseX = displayContainer.mouseX;
|
||||
int mouseY = displayContainer.mouseY;
|
||||
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
|
||||
selectModsButton.hoverUpdate(delta, mouseX, mouseY);
|
||||
selectRandomButton.hoverUpdate(delta, mouseX, mouseY);
|
||||
|
@ -759,8 +757,8 @@ public class SongMenu extends BasicGameState {
|
|||
if (focusNode != null) {
|
||||
MenuState state = focusNode.getBeatmapSet().isFavorite() ?
|
||||
MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP;
|
||||
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(state, focusNode);
|
||||
game.enterState(Opsu.STATE_BUTTONMENU);
|
||||
instanceContainer.provide(ButtonMenu.class).setMenuState(state, focusNode);
|
||||
displayContainer.switchState(ButtonMenu.class);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -782,7 +780,6 @@ public class SongMenu extends BasicGameState {
|
|||
starStream.update(delta);
|
||||
|
||||
// search
|
||||
search.setFocus(true);
|
||||
searchTimer += delta;
|
||||
if (searchTimer >= SEARCH_DELAY && reloadThread == null && beatmapMenuTimer == -1) {
|
||||
searchTimer = 0;
|
||||
|
@ -791,12 +788,12 @@ public class SongMenu extends BasicGameState {
|
|||
if (focusNode != null)
|
||||
oldFocusNode = new SongNode(BeatmapSetList.get().getBaseNode(focusNode.index), focusNode.beatmapIndex);
|
||||
|
||||
if (BeatmapSetList.get().search(search.getText())) {
|
||||
if (BeatmapSetList.get().search(searchTextField.getText())) {
|
||||
// reset song stack
|
||||
randomStack = new Stack<SongNode>();
|
||||
randomStack = new Stack<>();
|
||||
|
||||
// empty search
|
||||
if (search.getText().isEmpty())
|
||||
if (searchTextField.getText().isEmpty())
|
||||
searchResultString = null;
|
||||
|
||||
// search produced new list: re-initialize it
|
||||
|
@ -805,7 +802,7 @@ public class SongMenu extends BasicGameState {
|
|||
focusScores = null;
|
||||
if (BeatmapSetList.get().size() > 0) {
|
||||
BeatmapSetList.get().init();
|
||||
if (search.getText().isEmpty()) { // cleared search
|
||||
if (searchTextField.getText().isEmpty()) { // cleared search
|
||||
// use previous start/focus if possible
|
||||
if (oldFocusNode != null)
|
||||
setFocus(oldFocusNode.getNode(), oldFocusNode.getIndex(), true, true);
|
||||
|
@ -818,7 +815,7 @@ public class SongMenu extends BasicGameState {
|
|||
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
|
||||
}
|
||||
oldFocusNode = null;
|
||||
} else if (!search.getText().isEmpty())
|
||||
} else if (!searchTextField.getText().isEmpty())
|
||||
searchResultString = "No matches found. Hit ESC to reset.";
|
||||
}
|
||||
}
|
||||
|
@ -848,7 +845,7 @@ public class SongMenu extends BasicGameState {
|
|||
|
||||
// mouse hover
|
||||
BeatmapSetNode node = getNodeAtPosition(mouseX, mouseY);
|
||||
if (node != null && !inDropdownMenu) {
|
||||
if (node != null && !isAnyComponentFocused()) {
|
||||
if (node == hoverIndex)
|
||||
hoverOffset.update(delta);
|
||||
else {
|
||||
|
@ -880,66 +877,65 @@ public class SongMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getID() { return state; }
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
if (super.mousePressed(button, x, y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
// check mouse button
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON)
|
||||
return;
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isScrollingToFocusNode)
|
||||
return;
|
||||
if (isScrollingToFocusNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
songScrolling.pressed();
|
||||
startScorePos.pressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
// check mouse button
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON)
|
||||
return;
|
||||
public boolean mouseReleased(int button, int x, int y) {
|
||||
if (super.mouseReleased(button, x, y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isScrollingToFocusNode)
|
||||
return;
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isScrollingToFocusNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
songScrolling.released();
|
||||
startScorePos.released();
|
||||
|
||||
if (isInputBlocked()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(int button, int x, int y, int clickCount) {
|
||||
// check mouse button
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON)
|
||||
return;
|
||||
|
||||
// block input
|
||||
if (isInputBlocked())
|
||||
return;
|
||||
|
||||
// back
|
||||
if (UI.getBackButton().contains(x, y)) {
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset();
|
||||
game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
return;
|
||||
displayContainer.switchState(MainMenu.class);
|
||||
return true;
|
||||
}
|
||||
|
||||
// selection buttons
|
||||
if (selectModsButton.contains(x, y)) {
|
||||
this.keyPressed(Input.KEY_F1, '\0');
|
||||
return;
|
||||
return true;
|
||||
} else if (selectRandomButton.contains(x, y)) {
|
||||
this.keyPressed(Input.KEY_F2, '\0');
|
||||
return;
|
||||
return true;
|
||||
} else if (selectMapOptionsButton.contains(x, y)) {
|
||||
this.keyPressed(Input.KEY_F3, '\0');
|
||||
return;
|
||||
return true;
|
||||
} else if (selectOptionsButton.contains(x, y)) {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition());
|
||||
return;
|
||||
optionsOverlay.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
// group tabs
|
||||
|
@ -954,7 +950,7 @@ public class SongMenu extends BasicGameState {
|
|||
songInfo = null;
|
||||
scoreMap = null;
|
||||
focusScores = null;
|
||||
search.setText("");
|
||||
searchTextField.setText("");
|
||||
searchTimer = SEARCH_DELAY;
|
||||
searchTransitionTimer = SEARCH_TRANSITION_TIME;
|
||||
searchResultString = null;
|
||||
|
@ -965,17 +961,18 @@ public class SongMenu extends BasicGameState {
|
|||
if (BeatmapSetList.get().size() < 1 && group.getEmptyMessage() != null)
|
||||
UI.sendBarNotification(group.getEmptyMessage());
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (focusNode == null)
|
||||
return;
|
||||
if (focusNode == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// logo: start game
|
||||
if (footerLogoButton.contains(x, y, 0.25f)) {
|
||||
startGame();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// song buttons
|
||||
|
@ -1014,7 +1011,7 @@ public class SongMenu extends BasicGameState {
|
|||
if (button == Input.MOUSE_RIGHT_BUTTON)
|
||||
beatmapMenuTimer = (node.index == expandedIndex) ? BEATMAP_MENU_DELAY * 4 / 5 : 0;
|
||||
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// score buttons
|
||||
|
@ -1027,49 +1024,55 @@ public class SongMenu extends BasicGameState {
|
|||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
if (button != Input.MOUSE_RIGHT_BUTTON) {
|
||||
// view score
|
||||
GameData data = new GameData(focusScores[rank], container.getWidth(), container.getHeight());
|
||||
((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data);
|
||||
game.enterState(Opsu.STATE_GAMERANKING, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
instanceContainer.provide(GameRanking.class).setGameData(new GameData(focusScores[rank], displayContainer.width, displayContainer.height));
|
||||
displayContainer.switchState(GameRanking.class);
|
||||
} else {
|
||||
// score management
|
||||
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.SCORE, focusScores[rank]);
|
||||
game.enterState(Opsu.STATE_BUTTONMENU);
|
||||
instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.SCORE, focusScores[rank]);
|
||||
displayContainer.switchState(ButtonMenu.class);
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
public boolean keyPressed(int key, char c) {
|
||||
if (super.keyPressed(key, c)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// block input
|
||||
if ((reloadThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) || beatmapMenuTimer > -1 || isScrollingToFocusNode)
|
||||
return;
|
||||
if ((reloadThread != null && key != Input.KEY_ESCAPE) || beatmapMenuTimer > -1 || isScrollingToFocusNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Input input = displayContainer.input;
|
||||
|
||||
switch (key) {
|
||||
case Input.KEY_ESCAPE:
|
||||
if (reloadThread != null) {
|
||||
// beatmap reloading: stop parsing beatmaps by sending interrupt to BeatmapParser
|
||||
reloadThread.interrupt();
|
||||
} else if (!search.getText().isEmpty()) {
|
||||
} else if (!searchTextField.getText().isEmpty()) {
|
||||
// clear search text
|
||||
search.setText("");
|
||||
searchTextField.setText("");
|
||||
searchTimer = SEARCH_DELAY;
|
||||
searchTransitionTimer = 0;
|
||||
searchResultString = null;
|
||||
} else {
|
||||
// return to main menu
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset();
|
||||
game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
displayContainer.switchState(MainMenu.class);
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_F1:
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.MODS);
|
||||
game.enterState(Opsu.STATE_BUTTONMENU);
|
||||
break;
|
||||
instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.MODS);
|
||||
displayContainer.switchState(ButtonMenu.class);
|
||||
return true;
|
||||
case Input.KEY_F2:
|
||||
if (focusNode == null)
|
||||
break;
|
||||
|
@ -1089,25 +1092,25 @@ public class SongMenu extends BasicGameState {
|
|||
randomStack.push(new SongNode(BeatmapSetList.get().getBaseNode(focusNode.index), focusNode.beatmapIndex));
|
||||
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_F3:
|
||||
if (focusNode == null)
|
||||
break;
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
MenuState state = focusNode.getBeatmapSet().isFavorite() ?
|
||||
MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP;
|
||||
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(state, focusNode);
|
||||
game.enterState(Opsu.STATE_BUTTONMENU);
|
||||
break;
|
||||
instanceContainer.provide(ButtonMenu.class).setMenuState(state, focusNode);
|
||||
displayContainer.switchState(ButtonMenu.class);
|
||||
return true;
|
||||
case Input.KEY_F5:
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
if (songFolderChanged)
|
||||
reloadBeatmaps(false);
|
||||
else {
|
||||
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.RELOAD);
|
||||
game.enterState(Opsu.STATE_BUTTONMENU);
|
||||
instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.RELOAD);
|
||||
displayContainer.switchState(ButtonMenu.class);
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_DELETE:
|
||||
if (focusNode == null)
|
||||
break;
|
||||
|
@ -1115,30 +1118,21 @@ public class SongMenu extends BasicGameState {
|
|||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
MenuState ms = (focusNode.beatmapIndex == -1 || focusNode.getBeatmapSet().size() == 1) ?
|
||||
MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT;
|
||||
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(ms, focusNode);
|
||||
game.enterState(Opsu.STATE_BUTTONMENU);
|
||||
instanceContainer.provide(ButtonMenu.class).setMenuState(ms, focusNode);
|
||||
displayContainer.switchState(ButtonMenu.class);
|
||||
}
|
||||
break;
|
||||
case Input.KEY_F7:
|
||||
Options.setNextFPS(container);
|
||||
break;
|
||||
case Input.KEY_F10:
|
||||
Options.toggleMouseDisabled();
|
||||
break;
|
||||
case Input.KEY_F12:
|
||||
Utils.takeScreenShot();
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_ENTER:
|
||||
if (focusNode == null)
|
||||
break;
|
||||
startGame();
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_DOWN:
|
||||
changeIndex(1);
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_UP:
|
||||
changeIndex(-1);
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_RIGHT:
|
||||
if (focusNode == null)
|
||||
break;
|
||||
|
@ -1154,7 +1148,7 @@ public class SongMenu extends BasicGameState {
|
|||
hoverIndex = oldHoverIndex;
|
||||
}
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_LEFT:
|
||||
if (focusNode == null)
|
||||
break;
|
||||
|
@ -1170,24 +1164,24 @@ public class SongMenu extends BasicGameState {
|
|||
hoverIndex = oldHoverIndex;
|
||||
}
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_NEXT:
|
||||
changeIndex(MAX_SONG_BUTTONS);
|
||||
break;
|
||||
return true;
|
||||
case Input.KEY_PRIOR:
|
||||
changeIndex(-MAX_SONG_BUTTONS);
|
||||
break;
|
||||
case Input.KEY_O:
|
||||
if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) {
|
||||
game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition());
|
||||
return true;
|
||||
}
|
||||
if (key == Input.KEY_O && (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL))) {
|
||||
optionsOverlay.show();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// wait for user to finish typing
|
||||
// TODO: accept all characters (current conditions are from TextField class)
|
||||
if ((c > 31 && c < 127) || key == Input.KEY_BACK) {
|
||||
searchTimer = 0;
|
||||
int textLength = search.getText().length();
|
||||
searchTextField.keyPressed(key, c);
|
||||
int textLength = searchTextField.getText().length();
|
||||
if (lastSearchTextLength != textLength) {
|
||||
if (key == Input.KEY_BACK) {
|
||||
if (textLength == 0)
|
||||
|
@ -1197,49 +1191,58 @@ public class SongMenu extends BasicGameState {
|
|||
lastSearchTextLength = textLength;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
// block input
|
||||
if (isInputBlocked())
|
||||
return;
|
||||
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
if (super.mouseDragged(oldx, oldy, newx, newy)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isInputBlocked()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int diff = newy - oldy;
|
||||
if (diff == 0)
|
||||
return;
|
||||
if (diff == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check mouse button (right click scrolls faster on songs)
|
||||
int multiplier;
|
||||
if (input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON))
|
||||
if (displayContainer.input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) {
|
||||
multiplier = 10;
|
||||
else if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON))
|
||||
} else if (displayContainer.input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
|
||||
multiplier = 1;
|
||||
else
|
||||
return;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// score buttons
|
||||
if (focusScores != null && focusScores.length >= MAX_SCORE_BUTTONS && ScoreData.areaContains(oldx, oldy))
|
||||
if (focusScores != null && focusScores.length >= MAX_SCORE_BUTTONS && ScoreData.areaContains(oldx, oldy)) {
|
||||
startScorePos.dragged(-diff * multiplier);
|
||||
|
||||
// song buttons
|
||||
else
|
||||
} else {
|
||||
songScrolling.dragged(-diff * multiplier);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int newValue) {
|
||||
// change volume
|
||||
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) {
|
||||
UI.changeVolume((newValue < 0) ? -1 : 1);
|
||||
return;
|
||||
public boolean mouseWheelMoved(int newValue) {
|
||||
if (super.mouseWheelMoved(newValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// block input
|
||||
if (isInputBlocked())
|
||||
return;
|
||||
Input input = displayContainer.input;
|
||||
|
||||
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) {
|
||||
UI.changeVolume((newValue < 0) ? -1 : 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isInputBlocked()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int shift = (newValue < 0) ? 1 : -1;
|
||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||
|
@ -1251,13 +1254,14 @@ public class SongMenu extends BasicGameState {
|
|||
// song buttons
|
||||
else
|
||||
changeIndex(shift);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
public void enter() {
|
||||
super.enter();
|
||||
|
||||
UI.enter();
|
||||
Display.setTitle(game.getTitle());
|
||||
selectModsButton.resetHover();
|
||||
selectRandomButton.resetHover();
|
||||
selectMapOptionsButton.resetHover();
|
||||
|
@ -1275,11 +1279,10 @@ public class SongMenu extends BasicGameState {
|
|||
songChangeTimer.setTime(songChangeTimer.getDuration());
|
||||
musicIconBounceTimer.setTime(musicIconBounceTimer.getDuration());
|
||||
starStream.clear();
|
||||
sortMenu.activate();
|
||||
sortMenu.reset();
|
||||
|
||||
// reset song stack
|
||||
randomStack = new Stack<SongNode>();
|
||||
randomStack = new Stack<>();
|
||||
|
||||
// reload beatmaps if song folder changed
|
||||
if (songFolderChanged && stateAction != MenuState.RELOAD)
|
||||
|
@ -1307,7 +1310,7 @@ public class SongMenu extends BasicGameState {
|
|||
|
||||
// reset game data
|
||||
if (resetGame) {
|
||||
((Game) game.getState(Opsu.STATE_GAME)).resetGameData();
|
||||
instanceContainer.provide(Game.class).resetGameData();
|
||||
|
||||
// destroy extra Clips
|
||||
MultiClip.destroyExtraClips();
|
||||
|
@ -1439,13 +1442,6 @@ public class SongMenu extends BasicGameState {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
search.setFocus(false);
|
||||
sortMenu.deactivate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts the startNode forward (+) or backwards (-) by a given number of nodes.
|
||||
* Initiates sliding "animation" by shifting the button Y position.
|
||||
|
@ -1568,9 +1564,9 @@ public class SongMenu extends BasicGameState {
|
|||
|
||||
// change the focus node
|
||||
if (changeStartNode || (startNode.index == 0 && startNode.beatmapIndex == -1 && startNode.prev == null)) {
|
||||
if (startNode == null || game.getCurrentStateID() != Opsu.STATE_SONGMENU)
|
||||
if (startNode == null || displayContainer.isInState(SongMenu.class)) {
|
||||
songScrolling.setPosition((node.index - 1) * buttonOffset);
|
||||
else {
|
||||
} else {
|
||||
isScrollingToFocusNode = true;
|
||||
songScrolling.setSpeedMultiplier(2f);
|
||||
songScrolling.released();
|
||||
|
@ -1703,7 +1699,7 @@ public class SongMenu extends BasicGameState {
|
|||
songInfo = null;
|
||||
hoverOffset.setTime(0);
|
||||
hoverIndex = null;
|
||||
search.setText("");
|
||||
searchTextField.setText("");
|
||||
searchTimer = SEARCH_DELAY;
|
||||
searchTransitionTimer = SEARCH_TRANSITION_TIME;
|
||||
searchResultString = null;
|
||||
|
@ -1784,17 +1780,17 @@ public class SongMenu extends BasicGameState {
|
|||
}
|
||||
|
||||
// turn on "auto" mod if holding "ctrl" key
|
||||
if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) {
|
||||
if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) {
|
||||
if (!GameMod.AUTO.isActive())
|
||||
GameMod.AUTO.toggle(true);
|
||||
}
|
||||
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
MultiClip.destroyExtraClips();
|
||||
Game gameState = (Game) game.getState(Opsu.STATE_GAME);
|
||||
Game gameState = instanceContainer.provide(Game.class);
|
||||
gameState.loadBeatmap(beatmap);
|
||||
gameState.setRestart(Game.Restart.NEW);
|
||||
gameState.setReplay(null);
|
||||
game.enterState(Opsu.STATE_GAME, new EasedFadeOutTransition(), new FadeInTransition());
|
||||
displayContainer.switchState(Game.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
package itdelatrisu.opsu.states;
|
||||
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
|
@ -36,19 +35,22 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
|||
import java.io.File;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.state.BasicGameState;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.inject.InstanceContainer;
|
||||
import yugecin.opsudance.core.state.BaseOpsuState;
|
||||
|
||||
/**
|
||||
* "Splash Screen" state.
|
||||
* <p>
|
||||
* Loads game resources and enters "Main Menu" state.
|
||||
*/
|
||||
public class Splash extends BasicGameState {
|
||||
public class Splash extends BaseOpsuState {
|
||||
|
||||
private final InstanceContainer instanceContainer;
|
||||
|
||||
/** Minimum time, in milliseconds, to display the splash screen (and fade in the logo). */
|
||||
private static final int MIN_SPLASH_TIME = 400;
|
||||
|
||||
|
@ -71,18 +73,16 @@ public class Splash extends BasicGameState {
|
|||
private AnimatedValue logoAlpha;
|
||||
|
||||
// game-related variables
|
||||
private final int state;
|
||||
private GameContainer container;
|
||||
private boolean init = false;
|
||||
|
||||
public Splash(int state) {
|
||||
this.state = state;
|
||||
public Splash(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
|
||||
super(displayContainer);
|
||||
this.instanceContainer = instanceContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
this.container = container;
|
||||
protected void revalidate() {
|
||||
super.revalidate();
|
||||
|
||||
// check if skin changed
|
||||
if (Options.getSkin() != null)
|
||||
|
@ -92,7 +92,7 @@ public class Splash extends BasicGameState {
|
|||
this.watchServiceChange = Options.isWatchServiceEnabled() && BeatmapWatchService.get() == null;
|
||||
|
||||
// load Utils class first (needed in other 'init' methods)
|
||||
Utils.init(container, game);
|
||||
Utils.init(displayContainer);
|
||||
|
||||
// fade in logo
|
||||
this.logoAlpha = new AnimatedValue(MIN_SPLASH_TIME, 0f, 1f, AnimationEquation.LINEAR);
|
||||
|
@ -100,16 +100,14 @@ public class Splash extends BasicGameState {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer container, StateBasedGame game, Graphics g)
|
||||
throws SlickException {
|
||||
public void render(Graphics g) {
|
||||
g.setBackground(Color.black);
|
||||
GameImage.MENU_LOGO.getImage().drawCentered(container.getWidth() / 2, container.getHeight() / 2);
|
||||
GameImage.MENU_LOGO.getImage().drawCentered(displayContainer.width / 2, displayContainer.height / 2);
|
||||
UI.drawLoadingProgress(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer container, StateBasedGame game, int delta)
|
||||
throws SlickException {
|
||||
public void preRenderUpdate() {
|
||||
if (!init) {
|
||||
init = true;
|
||||
|
||||
|
@ -165,7 +163,7 @@ public class Splash extends BasicGameState {
|
|||
}
|
||||
|
||||
// fade in logo
|
||||
if (logoAlpha.update(delta))
|
||||
if (logoAlpha.update(displayContainer.renderDelta))
|
||||
GameImage.MENU_LOGO.getImage().setAlpha(logoAlpha.getValue());
|
||||
|
||||
// change states when loading complete
|
||||
|
@ -173,33 +171,41 @@ public class Splash extends BasicGameState {
|
|||
// initialize song list
|
||||
if (BeatmapSetList.get().size() > 0) {
|
||||
BeatmapSetList.get().init();
|
||||
if (Options.isThemeSongEnabled())
|
||||
if (Options.isThemeSongEnabled()) {
|
||||
MusicController.playThemeSong();
|
||||
else
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
|
||||
} else {
|
||||
instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
|
||||
}
|
||||
|
||||
// play the theme song
|
||||
else
|
||||
} else {
|
||||
MusicController.playThemeSong();
|
||||
|
||||
game.enterState(Opsu.STATE_MAINMENU);
|
||||
}
|
||||
displayContainer.switchState(MainMenu.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID() { return state; }
|
||||
public boolean onCloseRequest() {
|
||||
if (thread != null && thread.isAlive()) {
|
||||
thread.interrupt();
|
||||
try {
|
||||
thread.join();
|
||||
} catch (InterruptedException e) {
|
||||
Log.warn("InterruptedException while waiting for splash thread to die", e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
if (key == Input.KEY_ESCAPE) {
|
||||
// close program
|
||||
if (++escapeCount >= 3)
|
||||
container.exit();
|
||||
|
||||
// stop parsing beatmaps by sending interrupt to BeatmapParser
|
||||
else if (thread != null)
|
||||
public boolean keyPressed(int key, char c) {
|
||||
if (key != Input.KEY_ESCAPE) {
|
||||
return false;
|
||||
}
|
||||
if (++escapeCount >= 3) {
|
||||
displayContainer.exitRequested = true;
|
||||
} else if (thread != null) {
|
||||
thread.interrupt();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,31 +18,22 @@
|
|||
|
||||
package itdelatrisu.opsu.ui;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.skins.Skin;
|
||||
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.newdawn.slick.*;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import yugecin.opsudance.Dancer;
|
||||
|
||||
/**
|
||||
* Updates and draws the cursor.
|
||||
*/
|
||||
public class Cursor {
|
||||
/** Empty cursor. */
|
||||
private static org.lwjgl.input.Cursor emptyCursor;
|
||||
|
||||
/** Last cursor coordinates. */
|
||||
private Point lastPosition;
|
||||
|
@ -63,15 +54,10 @@ public class Cursor {
|
|||
private static final float CURSOR_SCALE_TIME = 125;
|
||||
|
||||
/** Stores all previous cursor locations to display a trail. */
|
||||
private LinkedList<Point> trail = new LinkedList<Point>();
|
||||
private LinkedList<Point> trail = new LinkedList<>();
|
||||
|
||||
private boolean newStyle;
|
||||
|
||||
// game-related variables
|
||||
private static GameContainer container;
|
||||
private static StateBasedGame game;
|
||||
private static Input input;
|
||||
|
||||
public static Color lastObjColor = Color.white;
|
||||
public static Color lastMirroredObjColor = Color.white;
|
||||
public static Color nextObjColor = Color.white;
|
||||
|
@ -80,26 +66,6 @@ public class Cursor {
|
|||
|
||||
private boolean isMirrored;
|
||||
|
||||
/**
|
||||
* Initializes the class.
|
||||
* @param container the game container
|
||||
* @param game the game object
|
||||
*/
|
||||
public static void init(GameContainer container, StateBasedGame game) {
|
||||
Cursor.container = container;
|
||||
Cursor.game = game;
|
||||
Cursor.input = container.getInput();
|
||||
|
||||
// create empty cursor to simulate hiding the cursor
|
||||
try {
|
||||
int min = org.lwjgl.input.Cursor.getMinCursorSize();
|
||||
IntBuffer tmp = BufferUtils.createIntBuffer(min * min);
|
||||
emptyCursor = new org.lwjgl.input.Cursor(min, min, min/2, min/2, 1, tmp, null);
|
||||
} catch (LWJGLException e) {
|
||||
ErrorHandler.error("Failed to create hidden cursor.", e, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
|
@ -108,29 +74,15 @@ public class Cursor {
|
|||
}
|
||||
|
||||
public Cursor(boolean isMirrored) {
|
||||
resetLocations();
|
||||
resetLocations(0, 0);
|
||||
this.isMirrored = isMirrored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the cursor.
|
||||
*/
|
||||
public void draw() {
|
||||
int state = game.getCurrentStateID();
|
||||
boolean mousePressed =
|
||||
(((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && Utils.isGameKeyPressed()) ||
|
||||
((input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) &&
|
||||
!(state == Opsu.STATE_GAME && Options.isMouseDisabled())));
|
||||
draw(input.getMouseX(), input.getMouseY(), mousePressed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the cursor.
|
||||
* @param mouseX the mouse x coordinate
|
||||
* @param mouseY the mouse y coordinate
|
||||
* @param mousePressed whether or not the mouse button is pressed
|
||||
*/
|
||||
public void draw(int mouseX, int mouseY, boolean mousePressed) {
|
||||
public void draw(boolean mousePressed) {
|
||||
if (Options.isCursorDisabled())
|
||||
return;
|
||||
|
||||
|
@ -172,8 +124,6 @@ public class Cursor {
|
|||
cursorTrail = cursorTrail.getScaledCopy(cursorScale);
|
||||
}
|
||||
|
||||
setCursorPosition(mouseX, mouseY);
|
||||
|
||||
Color filter;
|
||||
if (isMirrored) {
|
||||
filter = Dancer.cursorColorMirrorOverride.getMirrorColor();
|
||||
|
@ -195,16 +145,16 @@ public class Cursor {
|
|||
cursorTrailWidth, cursorTrailHeight, cursorTrailRotation);
|
||||
}
|
||||
cursorTrail.drawEmbedded(
|
||||
mouseX - (cursorTrailWidth / 2f), mouseY - (cursorTrailHeight / 2f),
|
||||
lastPosition.x - (cursorTrailWidth / 2f), lastPosition.y - (cursorTrailHeight / 2f),
|
||||
cursorTrailWidth, cursorTrailHeight, cursorTrailRotation);
|
||||
cursorTrail.endUse();
|
||||
|
||||
// draw the other components
|
||||
if (newStyle && skin.isCursorRotated())
|
||||
cursor.setRotation(cursorAngle);
|
||||
cursor.drawCentered(mouseX, mouseY, Options.isCursorOnlyColorTrail() ? Color.white : filter);
|
||||
cursor.drawCentered(lastPosition.x, lastPosition.y, Options.isCursorOnlyColorTrail() ? Color.white : filter);
|
||||
if (hasMiddle)
|
||||
cursorMiddle.drawCentered(mouseX, mouseY, Options.isCursorOnlyColorTrail() ? Color.white : filter);
|
||||
cursorMiddle.drawCentered(lastPosition.x, lastPosition.y, Options.isCursorOnlyColorTrail() ? Color.white : filter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,10 +162,10 @@ public class Cursor {
|
|||
* @param mouseX x coordinate to set position to
|
||||
* @param mouseY y coordinate to set position to
|
||||
*/
|
||||
public void setCursorPosition(int mouseX, int mouseY) {
|
||||
public void setCursorPosition(int delta, int mouseX, int mouseY) {
|
||||
// TODO: use an image buffer
|
||||
int removeCount = 0;
|
||||
float FPSmod = Math.max(container.getFPS(), 1) / 30f;
|
||||
float FPSmod = Math.max(1000 / Math.max(delta, 1), 1) / 30f; // TODO
|
||||
if (newStyle) {
|
||||
// new style: add all points between cursor movements
|
||||
if ((lastPosition.x == 0 && lastPosition.y == 0) || !addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY)) {
|
||||
|
@ -301,7 +251,7 @@ public class Cursor {
|
|||
* If the old style cursor is being used, this will do nothing.
|
||||
* @param delta the delta interval since the last call
|
||||
*/
|
||||
public void update(int delta) {
|
||||
public void updateAngle(int delta) {
|
||||
cursorAngle += delta / 40f;
|
||||
cursorAngle %= 360;
|
||||
}
|
||||
|
@ -309,14 +259,14 @@ public class Cursor {
|
|||
/**
|
||||
* Resets all cursor data and beatmap skins.
|
||||
*/
|
||||
public void reset() {
|
||||
public void reset(int mouseX, int mouseY) {
|
||||
// destroy skin images
|
||||
GameImage.CURSOR.destroyBeatmapSkinImage();
|
||||
GameImage.CURSOR_MIDDLE.destroyBeatmapSkinImage();
|
||||
GameImage.CURSOR_TRAIL.destroyBeatmapSkinImage();
|
||||
|
||||
// reset locations
|
||||
resetLocations();
|
||||
resetLocations(mouseX, mouseY);
|
||||
|
||||
// reset angles
|
||||
cursorAngle = 0f;
|
||||
|
@ -325,15 +275,13 @@ public class Cursor {
|
|||
/**
|
||||
* Resets all cursor location data.
|
||||
*/
|
||||
public void resetLocations() {
|
||||
public void resetLocations(int mouseX, int mouseY) {
|
||||
trail.clear();
|
||||
if (lastPosition != null) {
|
||||
lastPosition = new Point(mouseX, mouseY);
|
||||
for (int i = 0; i < 50; i++) {
|
||||
trail.add(new Point(lastPosition));
|
||||
}
|
||||
}
|
||||
lastPosition = new Point(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the cursor is skinned.
|
||||
|
@ -344,23 +292,4 @@ public class Cursor {
|
|||
GameImage.CURSOR_TRAIL.hasBeatmapSkinImage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the cursor, if possible.
|
||||
*/
|
||||
public void hide() {
|
||||
if (emptyCursor != null) {
|
||||
try {
|
||||
container.setMouseCursor(emptyCursor, 0, 0);
|
||||
} catch (SlickException e) {
|
||||
ErrorHandler.error("Failed to hide the cursor.", e, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhides the cursor.
|
||||
*/
|
||||
public void show() {
|
||||
container.setDefaultMouseCursor();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,152 +27,62 @@ import org.newdawn.slick.Font;
|
|||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.UnicodeFont;
|
||||
import org.newdawn.slick.gui.AbstractComponent;
|
||||
import org.newdawn.slick.gui.GUIContext;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.components.Component;
|
||||
|
||||
public class DropdownMenu<E> extends Component {
|
||||
|
||||
/**
|
||||
* Simple dropdown menu.
|
||||
* <p>
|
||||
* Basic usage:
|
||||
* <ul>
|
||||
* <li>Override {@link #menuClicked(int)} to perform actions when the menu is clicked
|
||||
* (e.g. play a sound effect, block input under certain conditions).
|
||||
* <li>Override {@link #itemSelected(int, Object)} to perform actions when a new item is selected.
|
||||
* <li>Call {@link #activate()}/{@link #deactivate()} whenever the component is needed
|
||||
* (e.g. in a state's {@code enter} and {@code leave} events.
|
||||
* </ul>
|
||||
*
|
||||
* @param <E> the type of the elements in the menu
|
||||
*/
|
||||
public class DropdownMenu<E> extends AbstractComponent {
|
||||
/** Padding ratios for drawing. */
|
||||
private static final float PADDING_Y = 0.1f, CHEVRON_X = 0.03f;
|
||||
|
||||
/** Whether this component is active. */
|
||||
private boolean active;
|
||||
private final DisplayContainer displayContainer;
|
||||
|
||||
/** The menu items. */
|
||||
private E[] items;
|
||||
|
||||
/** The menu item names. */
|
||||
private String[] itemNames;
|
||||
private int selectedItemIndex = 0;
|
||||
private boolean expanded;
|
||||
|
||||
/** The index of the selected item. */
|
||||
private int itemIndex = 0;
|
||||
|
||||
/** Whether the menu is expanded. */
|
||||
private boolean expanded = false;
|
||||
|
||||
/** The expanding animation progress. */
|
||||
private AnimatedValue expandProgress = new AnimatedValue(300, 0f, 1f, AnimationEquation.LINEAR);
|
||||
|
||||
/** The last update time, in milliseconds. */
|
||||
private long lastUpdateTime;
|
||||
|
||||
/** The top-left coordinates. */
|
||||
private float x, y;
|
||||
|
||||
/** The width and height of the dropdown menu. */
|
||||
private int width, height;
|
||||
|
||||
/** The height of the base item. */
|
||||
private int baseHeight;
|
||||
|
||||
/** The vertical offset between items. */
|
||||
private float offsetY;
|
||||
|
||||
/** The colors to use. */
|
||||
private Color
|
||||
textColor = Color.white, backgroundColor = Color.black,
|
||||
highlightColor = Colors.BLUE_DIVIDER, borderColor = Colors.BLUE_DIVIDER,
|
||||
chevronDownColor = textColor, chevronRightColor = backgroundColor;
|
||||
private Color textColor = Color.white;
|
||||
private Color backgroundColor = Color.black;
|
||||
private Color highlightColor = Colors.BLUE_DIVIDER;
|
||||
private Color borderColor = Colors.BLUE_DIVIDER;
|
||||
private Color chevronDownColor = textColor;
|
||||
private Color chevronRightColor = backgroundColor;
|
||||
|
||||
/** The fonts to use. */
|
||||
private UnicodeFont fontNormal = Fonts.MEDIUM, fontSelected = Fonts.MEDIUMBOLD;
|
||||
private UnicodeFont fontNormal = Fonts.MEDIUM;
|
||||
private UnicodeFont fontSelected = Fonts.MEDIUMBOLD;
|
||||
|
||||
/** The chevron images. */
|
||||
private Image chevronDown, chevronRight;
|
||||
private Image chevronDown;
|
||||
private Image chevronRight;
|
||||
|
||||
/** Should the next click be blocked? */
|
||||
private boolean blockClick = false;
|
||||
|
||||
/**
|
||||
* Creates a new dropdown menu.
|
||||
* @param container the container rendering this menu
|
||||
* @param items the list of items (with names given as their {@code toString()} methods)
|
||||
* @param x the top-left x coordinate
|
||||
* @param y the top-left y coordinate
|
||||
*/
|
||||
public DropdownMenu(GUIContext container, E[] items, float x, float y) {
|
||||
this(container, items, x, y, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dropdown menu with the given fonts.
|
||||
* @param container the container rendering this menu
|
||||
* @param items the list of items (with names given as their {@code toString()} methods)
|
||||
* @param x the top-left x coordinate
|
||||
* @param y the top-left y coordinate
|
||||
* @param normal the normal font
|
||||
* @param selected the font for the selected item
|
||||
*/
|
||||
public DropdownMenu(GUIContext container, E[] items, float x, float y, UnicodeFont normal, UnicodeFont selected) {
|
||||
this(container, items, x, y, 0, normal, selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dropdown menu with the given width.
|
||||
* @param container the container rendering this menu
|
||||
* @param items the list of items (with names given as their {@code toString()} methods)
|
||||
* @param x the top-left x coordinate
|
||||
* @param y the top-left y coordinate
|
||||
* @param width the menu width
|
||||
*/
|
||||
public DropdownMenu(GUIContext container, E[] items, float x, float y, int width) {
|
||||
super(container);
|
||||
public DropdownMenu(DisplayContainer displayContainer, E[] items, int x, int y, int width) {
|
||||
this.displayContainer = displayContainer;
|
||||
init(items, x, y, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dropdown menu with the given width and fonts.
|
||||
* @param container the container rendering this menu
|
||||
* @param items the list of items (with names given as their {@code toString()} methods)
|
||||
* @param x the top-left x coordinate
|
||||
* @param y the top-left y coordinate
|
||||
* @param width the menu width
|
||||
* @param normal the normal font
|
||||
* @param selected the font for the selected item
|
||||
*/
|
||||
public DropdownMenu(GUIContext container, E[] items, float x, float y, int width, UnicodeFont normal, UnicodeFont selected) {
|
||||
super(container);
|
||||
this.fontNormal = normal;
|
||||
this.fontSelected = selected;
|
||||
init(items, x, y, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum item width from the list.
|
||||
*/
|
||||
private int getMaxItemWidth() {
|
||||
int maxWidth = 0;
|
||||
for (int i = 0; i < itemNames.length; i++) {
|
||||
int w = fontSelected.getWidth(itemNames[i]);
|
||||
if (w > maxWidth)
|
||||
for (String itemName : itemNames) {
|
||||
int w = fontSelected.getWidth(itemName);
|
||||
if (w > maxWidth) {
|
||||
maxWidth = w;
|
||||
}
|
||||
}
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the component.
|
||||
*/
|
||||
private void init(E[] items, float x, float y, int width) {
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
private void init(E[] items, int x, int y, int width) {
|
||||
this.items = items;
|
||||
this.itemNames = new String[items.length];
|
||||
for (int i = 0; i < itemNames.length; i++)
|
||||
for (int i = 0; i < itemNames.length; i++) {
|
||||
itemNames[i] = items[i].toString();
|
||||
}
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.baseHeight = fontNormal.getLineHeight();
|
||||
|
@ -188,77 +98,27 @@ public class DropdownMenu<E> extends AbstractComponent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
public void updateHover(int x, int y) {
|
||||
this.hovered = this.x <= x && x <= this.x + width && this.y <= y && y <= this.y + (expanded ? height : baseHeight);
|
||||
}
|
||||
|
||||
public boolean baseContains(int x, int y) {
|
||||
return (x > this.x && x < this.x + width && y > this.y && y < this.y + baseHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX() { return (int) x; }
|
||||
public void render(Graphics g) {
|
||||
int delta = displayContainer.renderDelta;
|
||||
|
||||
@Override
|
||||
public int getY() { return (int) y; }
|
||||
|
||||
@Override
|
||||
public int getWidth() { return width; }
|
||||
|
||||
@Override
|
||||
public int getHeight() { return (expanded) ? height : baseHeight; }
|
||||
|
||||
/** Activates the component. */
|
||||
public void activate() { this.active = true; }
|
||||
|
||||
/** Deactivates the component. */
|
||||
public void deactivate() { this.active = false; }
|
||||
|
||||
/**
|
||||
* Returns whether the dropdown menu is currently open.
|
||||
* @return true if open, false otherwise
|
||||
*/
|
||||
public boolean isOpen() { return expanded; }
|
||||
|
||||
/**
|
||||
* Opens or closes the dropdown menu.
|
||||
* @param flag true to open, false to close
|
||||
*/
|
||||
public void open(boolean flag) { this.expanded = flag; }
|
||||
|
||||
/**
|
||||
* Returns true if the coordinates are within the menu bounds.
|
||||
* @param cx the x coordinate
|
||||
* @param cy the y coordinate
|
||||
*/
|
||||
public boolean contains(float cx, float cy) {
|
||||
return (cx > x && cx < x + width && (
|
||||
(cy > y && cy < y + baseHeight) ||
|
||||
(expanded && cy > y + offsetY && cy < y + height)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the coordinates are within the base item bounds.
|
||||
* @param cx the x coordinate
|
||||
* @param cy the y coordinate
|
||||
*/
|
||||
public boolean baseContains(float cx, float cy) {
|
||||
return (cx > x && cx < x + width && cy > y && cy < y + baseHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GUIContext container, Graphics g) throws SlickException {
|
||||
// update animation
|
||||
long time = container.getTime();
|
||||
if (lastUpdateTime > 0) {
|
||||
int delta = (int) (time - lastUpdateTime);
|
||||
expandProgress.update((expanded) ? delta : -delta * 2);
|
||||
}
|
||||
this.lastUpdateTime = time;
|
||||
|
||||
// get parameters
|
||||
Input input = container.getInput();
|
||||
int idx = getIndexAt(input.getMouseX(), input.getMouseY());
|
||||
int idx = getIndexAt(displayContainer.mouseY);
|
||||
float t = expandProgress.getValue();
|
||||
if (expanded)
|
||||
if (expanded) {
|
||||
t = AnimationEquation.OUT_CUBIC.calc(t);
|
||||
}
|
||||
|
||||
// background and border
|
||||
Color oldGColor = g.getColor();
|
||||
|
@ -293,12 +153,12 @@ public class DropdownMenu<E> extends AbstractComponent {
|
|||
|
||||
// text
|
||||
chevronDown.draw(x + width - chevronDown.getWidth() - width * CHEVRON_X, y + (baseHeight - chevronDown.getHeight()) / 2f, chevronDownColor);
|
||||
fontNormal.drawString(x + (width * 0.03f), y + (fontNormal.getPaddingTop() + fontNormal.getPaddingBottom()) / 2f, itemNames[itemIndex], textColor);
|
||||
fontNormal.drawString(x + (width * 0.03f), y + (fontNormal.getPaddingTop() + fontNormal.getPaddingBottom()) / 2f, itemNames[selectedItemIndex], textColor);
|
||||
float oldTextAlpha = textColor.a;
|
||||
textColor.a *= t;
|
||||
if (expanded || t >= 0.0001) {
|
||||
for (int i = 0; i < itemNames.length; i++) {
|
||||
Font f = (i == itemIndex) ? fontSelected : fontNormal;
|
||||
Font f = (i == selectedItemIndex) ? fontSelected : fontNormal;
|
||||
if (i == idx && t >= 0.999)
|
||||
chevronRight.draw(x, y + offsetY + (offsetY * i) + (offsetY - chevronRight.getHeight()) / 2f, chevronRightColor);
|
||||
f.drawString(x + chevronRight.getWidth(), y + offsetY + (offsetY * i * t), itemNames[i], textColor);
|
||||
|
@ -310,131 +170,89 @@ public class DropdownMenu<E> extends AbstractComponent {
|
|||
/**
|
||||
* Returns the index of the item at the given location, -1 for the base item,
|
||||
* and -2 if there is no item at the location.
|
||||
* @param cx the x coordinate
|
||||
* @param cy the y coordinate
|
||||
* @param y the y coordinate
|
||||
*/
|
||||
private int getIndexAt(float cx, float cy) {
|
||||
if (!contains(cx, cy))
|
||||
private int getIndexAt(int y) {
|
||||
if (!hovered) {
|
||||
return -2;
|
||||
if (cy <= y + baseHeight)
|
||||
}
|
||||
if (y <= this.y + baseHeight) {
|
||||
return -1;
|
||||
if (!expanded)
|
||||
}
|
||||
if (!expanded) {
|
||||
return -2;
|
||||
return (int) ((cy - (y + offsetY)) / offsetY);
|
||||
}
|
||||
return (int) ((y - (this.y + offsetY)) / offsetY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the menu state.
|
||||
*/
|
||||
public void reset() {
|
||||
this.expanded = false;
|
||||
this.lastUpdateTime = 0;
|
||||
expandProgress.setTime(0);
|
||||
blockClick = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setFocused(boolean focused) {
|
||||
super.setFocused(focused);
|
||||
expanded = focused;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
if (!active)
|
||||
return;
|
||||
public boolean isFocusable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON)
|
||||
return;
|
||||
@Override
|
||||
public void mouseReleased(int button) {
|
||||
super.mouseReleased(button);
|
||||
|
||||
int idx = getIndexAt(x, y);
|
||||
if (button == Input.MOUSE_MIDDLE_BUTTON) {
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = getIndexAt(displayContainer.mouseY);
|
||||
if (idx == -2) {
|
||||
this.expanded = false;
|
||||
return;
|
||||
}
|
||||
if (!menuClicked(idx))
|
||||
if (!canSelect(selectedItemIndex)) {
|
||||
return;
|
||||
this.expanded = (idx == -1) ? !expanded : false;
|
||||
if (idx >= 0 && itemIndex != idx) {
|
||||
this.itemIndex = idx;
|
||||
itemSelected(idx, items[idx]);
|
||||
}
|
||||
blockClick = true;
|
||||
consumeEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(int button, int x, int y, int clickCount) {
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
if (blockClick) {
|
||||
blockClick = false;
|
||||
consumeEvent();
|
||||
this.expanded = (idx == -1) && !expanded;
|
||||
if (idx >= 0 && selectedItemIndex != idx) {
|
||||
this.selectedItemIndex = idx;
|
||||
itemSelected(idx, items[selectedItemIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that a new item was selected (via override).
|
||||
* @param index the index of the item selected
|
||||
* @param item the item selected
|
||||
*/
|
||||
public void itemSelected(int index, E item) {}
|
||||
|
||||
/**
|
||||
* Notification that the menu was clicked (via override).
|
||||
* @param index the index of the item clicked, or -1 for the base item
|
||||
* @return true to process the click, or false to block/intercept it
|
||||
*/
|
||||
public boolean menuClicked(int index) { return true; }
|
||||
|
||||
@Override
|
||||
public void setFocus(boolean focus) { /* does not currently use the "focus" concept */ }
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int button, int x, int y) { /* does not currently use the "focus" concept */ }
|
||||
|
||||
/**
|
||||
* Selects the item at the given index.
|
||||
* @param index the list item index
|
||||
* @throws IllegalArgumentException if {@code index} is negative or greater than or equal to size
|
||||
*/
|
||||
public void setSelectedIndex(int index) {
|
||||
if (index < 0 || index >= items.length)
|
||||
throw new IllegalArgumentException();
|
||||
this.itemIndex = index;
|
||||
protected boolean canSelect(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the selected item.
|
||||
*/
|
||||
public int getSelectedIndex() { return itemIndex; }
|
||||
protected void itemSelected(int index, E item) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected item.
|
||||
*/
|
||||
public E getSelectedItem() { return items[itemIndex]; }
|
||||
public E getSelectedItem() {
|
||||
return items[selectedItemIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item at the given index.
|
||||
* @param index the list item index
|
||||
*/
|
||||
public E getItemAt(int index) { return items[index]; }
|
||||
public void setBackgroundColor(Color c) {
|
||||
this.backgroundColor = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of items in the list.
|
||||
*/
|
||||
public int getItemCount() { return items.length; }
|
||||
public void setHighlightColor(Color c) {
|
||||
this.highlightColor = c;
|
||||
}
|
||||
|
||||
/** Sets the text color. */
|
||||
public void setTextColor(Color c) { this.textColor = c; }
|
||||
public void setBorderColor(Color c) {
|
||||
this.borderColor = c;
|
||||
}
|
||||
|
||||
/** Sets the background color. */
|
||||
public void setBackgroundColor(Color c) { this.backgroundColor = c; }
|
||||
public void setChevronDownColor(Color c) {
|
||||
this.chevronDownColor = c;
|
||||
}
|
||||
|
||||
/** Sets the highlight color. */
|
||||
public void setHighlightColor(Color c) { this.highlightColor = c; }
|
||||
|
||||
/** Sets the border color. */
|
||||
public void setBorderColor(Color c) { this.borderColor = c; }
|
||||
|
||||
/** Sets the down chevron color. */
|
||||
public void setChevronDownColor(Color c) { this.chevronDownColor = c; }
|
||||
|
||||
/** Sets the right chevron color. */
|
||||
public void setChevronRightColor(Color c) { this.chevronRightColor = c; }
|
||||
public void setChevronRightColor(Color c) {
|
||||
this.chevronRightColor = c;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ import org.newdawn.slick.util.ResourceLoader;
|
|||
* Fonts used for drawing.
|
||||
*/
|
||||
public class Fonts {
|
||||
public static UnicodeFont DEFAULT, BOLD, XLARGE, LARGE, MEDIUM, MEDIUMBOLD, SMALL;
|
||||
public static UnicodeFont DEFAULT, BOLD, XLARGE, LARGE, MEDIUM, MEDIUMBOLD, SMALL, SMALLBOLD;
|
||||
|
||||
/** Set of all Unicode strings already loaded per font. */
|
||||
private static HashMap<UnicodeFont, HashSet<String>> loadedGlyphs = new HashMap<UnicodeFont, HashSet<String>>();
|
||||
|
@ -65,6 +65,7 @@ public class Fonts {
|
|||
MEDIUM = new UnicodeFont(font.deriveFont(fontBase * 3 / 2));
|
||||
MEDIUMBOLD = new UnicodeFont(font.deriveFont(Font.BOLD, fontBase * 3 / 2));
|
||||
SMALL = new UnicodeFont(font.deriveFont(fontBase));
|
||||
SMALLBOLD = new UnicodeFont(font.deriveFont(Font.BOLD, fontBase));
|
||||
ColorEffect colorEffect = new ColorEffect();
|
||||
loadFont(DEFAULT, colorEffect);
|
||||
loadFont(BOLD, colorEffect);
|
||||
|
@ -73,6 +74,7 @@ public class Fonts {
|
|||
loadFont(MEDIUM, colorEffect);
|
||||
loadFont(MEDIUMBOLD, colorEffect);
|
||||
loadFont(SMALL, colorEffect);
|
||||
loadFont(SMALLBOLD, colorEffect);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,7 +125,7 @@ public class Fonts {
|
|||
* @return the list of split strings
|
||||
* @author davedes (http://slick.ninjacave.com/forum/viewtopic.php?t=3778)
|
||||
*/
|
||||
public static List<String> wrap(org.newdawn.slick.Font font, String text, int width) {
|
||||
public static List<String> wrap(org.newdawn.slick.Font font, String text, int width, boolean newlines) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
String str = text;
|
||||
String line = "";
|
||||
|
@ -134,7 +136,7 @@ public class Fonts {
|
|||
if (Character.isWhitespace(c))
|
||||
lastSpace = i;
|
||||
String append = line + c;
|
||||
if (font.getWidth(append) > width) {
|
||||
if (font.getWidth(append) > width || (newlines && c == '\n')) {
|
||||
int split = (lastSpace != -1) ? lastSpace : i;
|
||||
int splitTrimmed = split;
|
||||
if (lastSpace != -1 && split < str.length() - 1)
|
||||
|
@ -153,4 +155,5 @@ public class Fonts {
|
|||
list.add(str);
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package itdelatrisu.opsu.ui;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
|
@ -29,23 +28,16 @@ import itdelatrisu.opsu.replay.ReplayImporter;
|
|||
import itdelatrisu.opsu.ui.animations.AnimatedValue;
|
||||
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import org.newdawn.slick.Animation;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
|
||||
/**
|
||||
* Draws common UI components.
|
||||
*/
|
||||
public class UI {
|
||||
/** Cursor. */
|
||||
private static Cursor cursor = new Cursor();
|
||||
|
||||
/** Back button. */
|
||||
private static MenuButton backButton;
|
||||
|
@ -75,32 +67,24 @@ public class UI {
|
|||
private static AnimatedValue tooltipAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR);
|
||||
|
||||
// game-related variables
|
||||
private static GameContainer container;
|
||||
private static Input input;
|
||||
private static DisplayContainer displayContainer;
|
||||
|
||||
// This class should not be instantiated.
|
||||
private UI() {}
|
||||
|
||||
/**
|
||||
* Initializes UI data.
|
||||
* @param container the game container
|
||||
* @param game the game object
|
||||
*/
|
||||
public static void init(GameContainer container, StateBasedGame game) {
|
||||
UI.container = container;
|
||||
UI.input = container.getInput();
|
||||
|
||||
// initialize cursor
|
||||
Cursor.init(container, game);
|
||||
cursor.hide();
|
||||
public static void init(DisplayContainer displayContainer) {
|
||||
UI.displayContainer = displayContainer;
|
||||
|
||||
// back button
|
||||
if (GameImage.MENU_BACK.getImages() != null) {
|
||||
Animation back = GameImage.MENU_BACK.getAnimation(120);
|
||||
backButton = new MenuButton(back, back.getWidth() / 2f, container.getHeight() - (back.getHeight() / 2f));
|
||||
backButton = new MenuButton(back, back.getWidth() / 2f, displayContainer.height - (back.getHeight() / 2f));
|
||||
} else {
|
||||
Image back = GameImage.MENU_BACK.getImage();
|
||||
backButton = new MenuButton(back, back.getWidth() / 2f, container.getHeight() - (back.getHeight() / 2f));
|
||||
backButton = new MenuButton(back, back.getWidth() / 2f, displayContainer.height - (back.getHeight() / 2f));
|
||||
}
|
||||
backButton.setHoverAnimationDuration(350);
|
||||
backButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK);
|
||||
|
@ -112,7 +96,6 @@ public class UI {
|
|||
* @param delta the delta interval since the last call.
|
||||
*/
|
||||
public static void update(int delta) {
|
||||
cursor.update(delta);
|
||||
updateVolumeDisplay(delta);
|
||||
updateBarNotification(delta);
|
||||
tooltipAlpha.update(-delta);
|
||||
|
@ -125,24 +108,6 @@ public class UI {
|
|||
public static void draw(Graphics g) {
|
||||
drawBarNotification(g);
|
||||
drawVolume(g);
|
||||
drawFPS();
|
||||
cursor.draw();
|
||||
drawTooltip(g);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the global UI components: cursor, FPS, volume bar, tooltips, bar notifications.
|
||||
* @param g the graphics context
|
||||
* @param mouseX the mouse x coordinate
|
||||
* @param mouseY the mouse y coordinate
|
||||
* @param mousePressed whether or not the mouse button is pressed
|
||||
*/
|
||||
public static void draw(Graphics g, int mouseX, int mouseY, boolean mousePressed) {
|
||||
drawBarNotification(g);
|
||||
drawVolume(g);
|
||||
drawFPS();
|
||||
cursor.draw(mouseX, mouseY, mousePressed);
|
||||
drawTooltip(g);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,16 +115,10 @@ public class UI {
|
|||
*/
|
||||
public static void enter() {
|
||||
backButton.resetHover();
|
||||
cursor.resetLocations();
|
||||
resetBarNotification();
|
||||
resetTooltip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the game cursor.
|
||||
*/
|
||||
public static Cursor getCursor() { return cursor; }
|
||||
|
||||
/**
|
||||
* Returns the 'menu-back' MenuButton.
|
||||
*/
|
||||
|
@ -189,27 +148,6 @@ public class UI {
|
|||
Fonts.MEDIUM.drawString(tabTextX, tabTextY, text, textColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the FPS at the bottom-right corner of the game container.
|
||||
* If the option is not activated, this will do nothing.
|
||||
*/
|
||||
public static void drawFPS() {
|
||||
if (!Options.isFPSCounterEnabled())
|
||||
return;
|
||||
|
||||
String fps = String.format("%dFPS", container.getFPS());
|
||||
Fonts.BOLD.drawString(
|
||||
container.getWidth() * 0.997f - Fonts.BOLD.getWidth(fps),
|
||||
container.getHeight() * 0.997f - Fonts.BOLD.getHeight(fps),
|
||||
Integer.toString(container.getFPS()), Color.white
|
||||
);
|
||||
Fonts.DEFAULT.drawString(
|
||||
container.getWidth() * 0.997f - Fonts.BOLD.getWidth("FPS"),
|
||||
container.getHeight() * 0.997f - Fonts.BOLD.getHeight("FPS"),
|
||||
"FPS", Color.white
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the volume bar on the middle right-hand side of the game container.
|
||||
* Only draws if the volume has recently been changed using with {@link #changeVolume(int)}.
|
||||
|
@ -219,7 +157,6 @@ public class UI {
|
|||
if (volumeDisplay == -1)
|
||||
return;
|
||||
|
||||
int width = container.getWidth(), height = container.getHeight();
|
||||
Image img = GameImage.VOLUME.getImage();
|
||||
|
||||
// move image in/out
|
||||
|
@ -230,13 +167,13 @@ public class UI {
|
|||
else if (ratio >= 0.9f)
|
||||
xOffset = img.getWidth() * (1 - ((1 - ratio) * 10f));
|
||||
|
||||
img.drawCentered(width - img.getWidth() / 2f + xOffset, height / 2f);
|
||||
img.drawCentered(displayContainer.width - img.getWidth() / 2f + xOffset, displayContainer.height / 2f);
|
||||
float barHeight = img.getHeight() * 0.9f;
|
||||
float volume = Options.getMasterVolume();
|
||||
g.setColor(Color.white);
|
||||
g.fillRoundRect(
|
||||
width - (img.getWidth() * 0.368f) + xOffset,
|
||||
(height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)),
|
||||
displayContainer.width - (img.getWidth() * 0.368f) + xOffset,
|
||||
(displayContainer.height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)),
|
||||
img.getWidth() * 0.15f, barHeight * volume, 3
|
||||
);
|
||||
}
|
||||
|
@ -260,7 +197,7 @@ public class UI {
|
|||
*/
|
||||
public static void changeVolume(int units) {
|
||||
final float UNIT_OFFSET = 0.05f;
|
||||
Options.setMasterVolume(container, Utils.clamp(Options.getMasterVolume() + (UNIT_OFFSET * units), 0f, 1f));
|
||||
Options.setMasterVolume(Utils.clamp(Options.getMasterVolume() + (UNIT_OFFSET * units), 0f, 1f));
|
||||
if (volumeDisplay == -1)
|
||||
volumeDisplay = 0;
|
||||
else if (volumeDisplay >= VOLUME_DISPLAY_TIME / 10)
|
||||
|
@ -294,8 +231,8 @@ public class UI {
|
|||
return;
|
||||
|
||||
// draw loading info
|
||||
float marginX = container.getWidth() * 0.02f, marginY = container.getHeight() * 0.02f;
|
||||
float lineY = container.getHeight() - marginY;
|
||||
float marginX = displayContainer.width * 0.02f, marginY = displayContainer.height * 0.02f;
|
||||
float lineY = displayContainer.height - marginY;
|
||||
int lineOffsetY = Fonts.MEDIUM.getLineHeight();
|
||||
if (Options.isLoadVerbose()) {
|
||||
// verbose: display percentages and file names
|
||||
|
@ -308,7 +245,7 @@ public class UI {
|
|||
Fonts.MEDIUM.drawString(marginX, lineY - (lineOffsetY * 2), text, Color.white);
|
||||
g.setColor(Color.white);
|
||||
g.fillRoundRect(marginX, lineY - (lineOffsetY / 2f),
|
||||
(container.getWidth() - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4
|
||||
(displayContainer.width - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -332,7 +269,7 @@ public class UI {
|
|||
float unitBaseX, float unitBaseY, float unitWidth, float scrollAreaHeight,
|
||||
Color bgColor, Color scrollbarColor, boolean right
|
||||
) {
|
||||
float scrollbarWidth = container.getWidth() * 0.00347f;
|
||||
float scrollbarWidth = displayContainer.width * 0.00347f;
|
||||
float scrollbarHeight = scrollAreaHeight * lengthShown / totalLength;
|
||||
float offsetY = (scrollAreaHeight - scrollbarHeight) * (position / (totalLength - lengthShown));
|
||||
float scrollbarX = unitBaseX + unitWidth - ((right) ? scrollbarWidth : 0);
|
||||
|
@ -368,8 +305,7 @@ public class UI {
|
|||
if (tooltipAlpha.getTime() == 0 || tooltip == null)
|
||||
return;
|
||||
|
||||
int containerWidth = container.getWidth(), containerHeight = container.getHeight();
|
||||
int margin = containerWidth / 100, textMarginX = 2;
|
||||
int margin = displayContainer.width / 100, textMarginX = 2;
|
||||
int offset = GameImage.CURSOR_MIDDLE.getImage().getWidth() / 2;
|
||||
int lineHeight = Fonts.SMALL.getLineHeight();
|
||||
int textWidth = textMarginX * 2, textHeight = lineHeight;
|
||||
|
@ -387,13 +323,14 @@ public class UI {
|
|||
textWidth += Fonts.SMALL.getWidth(tooltip);
|
||||
|
||||
// get drawing coordinates
|
||||
int x = input.getMouseX() + offset, y = input.getMouseY() + offset;
|
||||
if (x + textWidth > containerWidth - margin)
|
||||
x = containerWidth - margin - textWidth;
|
||||
int x = displayContainer.mouseX + offset;
|
||||
int y = displayContainer.mouseY + offset;
|
||||
if (x + textWidth > displayContainer.width - margin)
|
||||
x = displayContainer.width - margin - textWidth;
|
||||
else if (x < margin)
|
||||
x = margin;
|
||||
if (y + textHeight > containerHeight - margin)
|
||||
y = containerHeight - margin - textHeight;
|
||||
if (y + textHeight > displayContainer.height - margin)
|
||||
y = displayContainer.height - margin - textHeight;
|
||||
else if (y < margin)
|
||||
y = margin;
|
||||
|
||||
|
@ -467,13 +404,13 @@ public class UI {
|
|||
float alpha = 1f;
|
||||
if (barNotifTimer >= BAR_NOTIFICATION_TIME * 0.9f)
|
||||
alpha -= 1 - ((BAR_NOTIFICATION_TIME - barNotifTimer) / (BAR_NOTIFICATION_TIME * 0.1f));
|
||||
int midX = container.getWidth() / 2, midY = container.getHeight() / 2;
|
||||
int midX = displayContainer.width / 2, midY = displayContainer.height / 2;
|
||||
float barHeight = Fonts.LARGE.getLineHeight() * (1f + 0.6f * Math.min(barNotifTimer * 15f / BAR_NOTIFICATION_TIME, 1f));
|
||||
float oldAlphaB = Colors.BLACK_ALPHA.a, oldAlphaW = Colors.WHITE_ALPHA.a;
|
||||
Colors.BLACK_ALPHA.a *= alpha;
|
||||
Colors.WHITE_ALPHA.a = alpha;
|
||||
g.setColor(Colors.BLACK_ALPHA);
|
||||
g.fillRect(0, midY - barHeight / 2f, container.getWidth(), barHeight);
|
||||
g.fillRect(0, midY - barHeight / 2f, displayContainer.width, barHeight);
|
||||
Fonts.LARGE.drawString(
|
||||
midX - Fonts.LARGE.getWidth(barNotif) / 2f,
|
||||
midY - Fonts.LARGE.getLineHeight() / 2.2f,
|
||||
|
@ -482,19 +419,4 @@ public class UI {
|
|||
Colors.WHITE_ALPHA.a = oldAlphaW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a confirmation dialog (used before exiting the game).
|
||||
* @param message the message to display
|
||||
* @return true if user selects "yes", false otherwise
|
||||
*/
|
||||
public static boolean showExitConfirmation(String message) {
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception e) {
|
||||
ErrorHandler.error("Could not set system look and feel for exit confirmation.", e, true);
|
||||
}
|
||||
int n = JOptionPane.showConfirmDialog(null, message, "Warning",
|
||||
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
|
||||
return (n != JOptionPane.YES_OPTION);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,907 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Slick2D
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the Slick2D nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.newdawn.slick;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.Sys;
|
||||
import org.lwjgl.input.Cursor;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.Drawable;
|
||||
import org.lwjgl.opengl.Pbuffer;
|
||||
import org.lwjgl.opengl.PixelFormat;
|
||||
import org.newdawn.slick.gui.GUIContext;
|
||||
import org.newdawn.slick.openal.SoundStore;
|
||||
import org.newdawn.slick.opengl.CursorLoader;
|
||||
import org.newdawn.slick.opengl.ImageData;
|
||||
import org.newdawn.slick.opengl.renderer.Renderer;
|
||||
import org.newdawn.slick.opengl.renderer.SGL;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
/**
|
||||
* A generic game container that handles the game loop, fps recording and
|
||||
* managing the input system
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public abstract class GameContainer implements GUIContext {
|
||||
/** The renderer to use for all GL operations */
|
||||
protected static SGL GL = Renderer.get();
|
||||
/** The shared drawable if any */
|
||||
protected static Drawable SHARED_DRAWABLE;
|
||||
|
||||
/** The time the last frame was rendered */
|
||||
protected long lastFrame;
|
||||
/** The last time the FPS recorded */
|
||||
protected long lastFPS;
|
||||
/** The last recorded FPS */
|
||||
protected int recordedFPS;
|
||||
/** The current count of FPS */
|
||||
protected int fps;
|
||||
/** True if we're currently running the game loop */
|
||||
protected boolean running = true;
|
||||
|
||||
/** The width of the display */
|
||||
protected int width;
|
||||
/** The height of the display */
|
||||
protected int height;
|
||||
/** The game being managed */
|
||||
protected Game game;
|
||||
|
||||
/** The default font to use in the graphics context */
|
||||
private Font defaultFont;
|
||||
/** The graphics context to be passed to the game */
|
||||
private Graphics graphics;
|
||||
|
||||
/** The input system to pass to the game */
|
||||
protected Input input;
|
||||
/** The FPS we want to lock to */
|
||||
protected int targetFPS = -1;
|
||||
/** True if we should show the fps */
|
||||
private boolean showFPS = true;
|
||||
/** The minimum logic update interval */
|
||||
protected long minimumLogicInterval = 1;
|
||||
/** The stored delta */
|
||||
protected long storedDelta;
|
||||
/** The maximum logic update interval */
|
||||
protected long maximumLogicInterval = 0;
|
||||
/** The last game started */
|
||||
protected Game lastGame;
|
||||
/** True if we should clear the screen each frame */
|
||||
protected boolean clearEachFrame = true;
|
||||
|
||||
/** True if the game is paused */
|
||||
protected boolean paused;
|
||||
/** True if we should force exit */
|
||||
protected boolean forceExit = true;
|
||||
/** True if vsync has been requested */
|
||||
protected boolean vsync;
|
||||
/** Smoothed deltas requested */
|
||||
protected boolean smoothDeltas;
|
||||
/** The number of samples we'll attempt through hardware */
|
||||
protected int samples;
|
||||
|
||||
/** True if this context supports multisample */
|
||||
protected boolean supportsMultiSample;
|
||||
|
||||
/** True if we should render when not focused */
|
||||
protected boolean alwaysRender;
|
||||
/** True if we require stencil bits */
|
||||
protected static boolean stencil;
|
||||
|
||||
/**
|
||||
* Create a new game container wrapping a given game
|
||||
*
|
||||
* @param game The game to be wrapped
|
||||
*/
|
||||
protected GameContainer(Game game) {
|
||||
this.game = game;
|
||||
lastFrame = getTime();
|
||||
|
||||
getBuildVersion();
|
||||
Log.checkVerboseLogSetting();
|
||||
}
|
||||
|
||||
public static void enableStencil() {
|
||||
stencil = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default font that will be intialised in the graphics held in this container
|
||||
*
|
||||
* @param font The font to use as default
|
||||
*/
|
||||
public void setDefaultFont(Font font) {
|
||||
if (font != null) {
|
||||
this.defaultFont = font;
|
||||
} else {
|
||||
Log.warn("Please provide a non null font");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether we want to try to use fullscreen multisampling. This will
|
||||
* give antialiasing across the whole scene using a hardware feature.
|
||||
*
|
||||
* @param samples The number of samples to attempt (2 is safe)
|
||||
*/
|
||||
public void setMultiSample(int samples) {
|
||||
this.samples = samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this hardware can support multi-sampling
|
||||
*
|
||||
* @return True if the hardware supports multi-sampling
|
||||
*/
|
||||
public boolean supportsMultiSample() {
|
||||
return supportsMultiSample;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of samples we're attempting to performing using
|
||||
* hardware multisampling
|
||||
*
|
||||
* @return The number of samples requested
|
||||
*/
|
||||
public int getSamples() {
|
||||
return samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if we should force exitting the VM at the end
|
||||
* of the game (default = true)
|
||||
*
|
||||
* @param forceExit True if we should force the VM exit
|
||||
*/
|
||||
public void setForceExit(boolean forceExit) {
|
||||
this.forceExit = forceExit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if we want to smooth deltas. This feature will report
|
||||
* a delta based on the FPS not the time passed. This works well with
|
||||
* vsync.
|
||||
*
|
||||
* @param smoothDeltas True if we should report smooth deltas
|
||||
*/
|
||||
public void setSmoothDeltas(boolean smoothDeltas) {
|
||||
this.smoothDeltas = smoothDeltas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the display is in fullscreen mode
|
||||
*
|
||||
* @return True if the display is in fullscreen mode
|
||||
*/
|
||||
public boolean isFullscreen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the aspect ratio of the screen
|
||||
*
|
||||
* @return The aspect ratio of the display
|
||||
*/
|
||||
public float getAspectRatio() {
|
||||
return getWidth() / getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether we want to be in fullscreen mode. Note that the current
|
||||
* display mode must be valid as a fullscreen mode for this to work
|
||||
*
|
||||
* @param fullscreen True if we want to be in fullscreen mode
|
||||
* @throws SlickException Indicates we failed to change the display mode
|
||||
*/
|
||||
public void setFullscreen(boolean fullscreen) throws SlickException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable shared OpenGL context. After calling this all containers created
|
||||
* will shared a single parent context
|
||||
*
|
||||
* @throws SlickException Indicates a failure to create the shared drawable
|
||||
*/
|
||||
public static void enableSharedContext() throws SlickException {
|
||||
try {
|
||||
SHARED_DRAWABLE = new Pbuffer(64, 64, new PixelFormat(8, 0, 0), null);
|
||||
} catch (LWJGLException e) {
|
||||
throw new SlickException("Unable to create the pbuffer used for shard context, buffers not supported", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the context shared by all containers
|
||||
*
|
||||
* @return The context shared by all the containers or null if shared context isn't enabled
|
||||
*/
|
||||
public static Drawable getSharedContext() {
|
||||
return SHARED_DRAWABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if we should clear the screen at the beginning of each frame. If you're
|
||||
* rendering to the whole screen each frame then setting this to false can give
|
||||
* some performance improvements
|
||||
*
|
||||
* @param clear True if the the screen should be cleared each frame
|
||||
*/
|
||||
public void setClearEachFrame(boolean clear) {
|
||||
this.clearEachFrame = clear;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renitialise the game and the context in which it's being rendered
|
||||
*
|
||||
* @throws SlickException Indicates a failure rerun initialisation routines
|
||||
*/
|
||||
public void reinit() throws SlickException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the game - i.e. suspend updates
|
||||
*/
|
||||
public void pause()
|
||||
{
|
||||
setPaused(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumt the game - i.e. continue updates
|
||||
*/
|
||||
public void resume()
|
||||
{
|
||||
setPaused(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the container is currently paused.
|
||||
*
|
||||
* @return True if the container is paused
|
||||
*/
|
||||
public boolean isPaused() {
|
||||
return paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the game should be paused, i.e. if updates
|
||||
* should be propogated through to the game.
|
||||
*
|
||||
* @param paused True if the game should be paused
|
||||
*/
|
||||
public void setPaused(boolean paused)
|
||||
{
|
||||
this.paused = paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if this container should render when it has focus
|
||||
*
|
||||
* @return True if this container should render when it has focus
|
||||
*/
|
||||
public boolean getAlwaysRender () {
|
||||
return alwaysRender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether we want this container to render when it has focus
|
||||
*
|
||||
* @param alwaysRender True if this container should render when it has focus
|
||||
*/
|
||||
public void setAlwaysRender (boolean alwaysRender) {
|
||||
this.alwaysRender = alwaysRender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the build number of slick
|
||||
*
|
||||
* @return The build number of slick
|
||||
*/
|
||||
public static int getBuildVersion() {
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
props.load(ResourceLoader.getResourceAsStream("version"));
|
||||
|
||||
int build = Integer.parseInt(props.getProperty("build"));
|
||||
Log.info("Slick Build #"+build);
|
||||
|
||||
return build;
|
||||
} catch (Exception e) {
|
||||
Log.info("Unable to determine Slick build number");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default system font
|
||||
*
|
||||
* @return The default system font
|
||||
*/
|
||||
@Override
|
||||
public Font getDefaultFont() {
|
||||
return defaultFont;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if sound effects are enabled
|
||||
*
|
||||
* @return True if sound effects are enabled
|
||||
*/
|
||||
public boolean isSoundOn() {
|
||||
return SoundStore.get().soundsOn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if music is enabled
|
||||
*
|
||||
* @return True if music is enabled
|
||||
*/
|
||||
public boolean isMusicOn() {
|
||||
return SoundStore.get().musicOn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether music should be enabled
|
||||
*
|
||||
* @param on True if music should be enabled
|
||||
*/
|
||||
public void setMusicOn(boolean on) {
|
||||
SoundStore.get().setMusicOn(on);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether sound effects should be enabled
|
||||
*
|
||||
* @param on True if sound effects should be enabled
|
||||
*/
|
||||
public void setSoundOn(boolean on) {
|
||||
SoundStore.get().setSoundsOn(on);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current default volume for music
|
||||
* @return the current default volume for music
|
||||
*/
|
||||
public float getMusicVolume() {
|
||||
return SoundStore.get().getMusicVolume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current default volume for sound fx
|
||||
* @return the current default volume for sound fx
|
||||
*/
|
||||
public float getSoundVolume() {
|
||||
return SoundStore.get().getSoundVolume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default volume for sound fx
|
||||
* @param volume the new default value for sound fx volume
|
||||
*/
|
||||
public void setSoundVolume(float volume) {
|
||||
SoundStore.get().setSoundVolume(volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default volume for music
|
||||
* @param volume the new default value for music volume
|
||||
*/
|
||||
public void setMusicVolume(float volume) {
|
||||
SoundStore.get().setMusicVolume(volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the standard screen resolution
|
||||
*
|
||||
* @return The screen width
|
||||
*/
|
||||
@Override
|
||||
public abstract int getScreenWidth();
|
||||
|
||||
/**
|
||||
* Get the height of the standard screen resolution
|
||||
*
|
||||
* @return The screen height
|
||||
*/
|
||||
@Override
|
||||
public abstract int getScreenHeight();
|
||||
|
||||
/**
|
||||
* Get the width of the game canvas
|
||||
*
|
||||
* @return The width of the game canvas
|
||||
*/
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the game canvas
|
||||
*
|
||||
* @return The height of the game canvas
|
||||
*/
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the icon to be displayed if possible in this type of
|
||||
* container
|
||||
*
|
||||
* @param ref The reference to the icon to be displayed
|
||||
* @throws SlickException Indicates a failure to load the icon
|
||||
*/
|
||||
public abstract void setIcon(String ref) throws SlickException;
|
||||
|
||||
/**
|
||||
* Set the icons to be used for this application. Note that the size of the icon
|
||||
* defines how it will be used. Important ones to note
|
||||
*
|
||||
* Windows window icon must be 16x16
|
||||
* Windows alt-tab icon must be 24x24 or 32x32 depending on Windows version (XP=32)
|
||||
*
|
||||
* @param refs The reference to the icon to be displayed
|
||||
* @throws SlickException Indicates a failure to load the icon
|
||||
*/
|
||||
public abstract void setIcons(String[] refs) throws SlickException;
|
||||
|
||||
/**
|
||||
* Get the accurate system time
|
||||
*
|
||||
* @return The system time in milliseconds
|
||||
*/
|
||||
@Override
|
||||
public long getTime() {
|
||||
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep for a given period
|
||||
*
|
||||
* @param milliseconds The period to sleep for in milliseconds
|
||||
*/
|
||||
public void sleep(int milliseconds) {
|
||||
long target = getTime()+milliseconds;
|
||||
while (getTime() < target) {
|
||||
try { Thread.sleep(1); } catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mouse cursor to be displayed - this is a hardware cursor and hence
|
||||
* shouldn't have any impact on FPS.
|
||||
*
|
||||
* @param ref The location of the image to be loaded for the cursor
|
||||
* @param hotSpotX The x coordinate of the hotspot within the cursor image
|
||||
* @param hotSpotY The y coordinate of the hotspot within the cursor image
|
||||
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
|
||||
*/
|
||||
@Override
|
||||
public abstract void setMouseCursor(String ref, int hotSpotX, int hotSpotY) throws SlickException;
|
||||
|
||||
/**
|
||||
* Set the mouse cursor to be displayed - this is a hardware cursor and hence
|
||||
* shouldn't have any impact on FPS.
|
||||
*
|
||||
* @param data The image data from which the cursor can be construted
|
||||
* @param hotSpotX The x coordinate of the hotspot within the cursor image
|
||||
* @param hotSpotY The y coordinate of the hotspot within the cursor image
|
||||
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
|
||||
*/
|
||||
@Override
|
||||
public abstract void setMouseCursor(ImageData data, int hotSpotX, int hotSpotY) throws SlickException;
|
||||
|
||||
/**
|
||||
* Set the mouse cursor based on the contents of the image. Note that this will not take
|
||||
* account of render state type changes to images (rotation and such). If these effects
|
||||
* are required it is recommended that an offscreen buffer be used to produce an appropriate
|
||||
* image. An offscreen buffer will always be used to produce the new cursor and as such
|
||||
* this operation an be very expensive
|
||||
*
|
||||
* @param image The image to use as the cursor
|
||||
* @param hotSpotX The x coordinate of the hotspot within the cursor image
|
||||
* @param hotSpotY The y coordinate of the hotspot within the cursor image
|
||||
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
|
||||
*/
|
||||
public abstract void setMouseCursor(Image image, int hotSpotX, int hotSpotY) throws SlickException;
|
||||
|
||||
/**
|
||||
* Set the mouse cursor to be displayed - this is a hardware cursor and hence
|
||||
* shouldn't have any impact on FPS.
|
||||
*
|
||||
* @param cursor The cursor to use
|
||||
* @param hotSpotX The x coordinate of the hotspot within the cursor image
|
||||
* @param hotSpotY The y coordinate of the hotspot within the cursor image
|
||||
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
|
||||
*/
|
||||
@Override
|
||||
public abstract void setMouseCursor(Cursor cursor, int hotSpotX, int hotSpotY) throws SlickException;
|
||||
|
||||
/**
|
||||
* Get a cursor based on a image reference on the classpath. The image
|
||||
* is assumed to be a set/strip of cursor animation frames running from top to
|
||||
* bottom.
|
||||
*
|
||||
* @param ref The reference to the image to be loaded
|
||||
* @param x The x-coordinate of the cursor hotspot (left {@literal ->} right)
|
||||
* @param y The y-coordinate of the cursor hotspot (bottom {@literal ->} top)
|
||||
* @param width The x width of the cursor
|
||||
* @param height The y height of the cursor
|
||||
* @param cursorDelays image delays between changing frames in animation
|
||||
*
|
||||
* @throws SlickException Indicates a failure to load the image or a failure to create the hardware cursor
|
||||
*/
|
||||
public void setAnimatedMouseCursor(String ref, int x, int y, int width, int height, int[] cursorDelays) throws SlickException
|
||||
{
|
||||
try {
|
||||
Cursor cursor;
|
||||
cursor = CursorLoader.get().getAnimatedCursor(ref, x, y, width, height, cursorDelays);
|
||||
setMouseCursor(cursor, x, y);
|
||||
} catch (IOException e) {
|
||||
throw new SlickException("Failed to set mouse cursor", e);
|
||||
} catch (LWJGLException e) {
|
||||
throw new SlickException("Failed to set mouse cursor", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default mouse cursor - i.e. the original cursor before any native
|
||||
* cursor was set
|
||||
*/
|
||||
@Override
|
||||
public abstract void setDefaultMouseCursor();
|
||||
|
||||
/**
|
||||
* Get the input system
|
||||
*
|
||||
* @return The input system available to this game container
|
||||
*/
|
||||
@Override
|
||||
public Input getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current recorded FPS (frames per second)
|
||||
*
|
||||
* @return The current FPS
|
||||
*/
|
||||
public int getFPS() {
|
||||
return recordedFPS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether mouse cursor should be grabbed or not
|
||||
*
|
||||
* @param grabbed True if mouse cursor should be grabbed
|
||||
*/
|
||||
public abstract void setMouseGrabbed(boolean grabbed);
|
||||
|
||||
/**
|
||||
* Check if the mouse cursor is current grabbed. This will cause it not
|
||||
* to be seen.
|
||||
*
|
||||
* @return True if the mouse is currently grabbed
|
||||
*/
|
||||
public abstract boolean isMouseGrabbed();
|
||||
|
||||
/**
|
||||
* Retrieve the time taken to render the last frame, i.e. the change in time - delta.
|
||||
*
|
||||
* @return The time taken to render the last frame
|
||||
*/
|
||||
protected int getDelta() {
|
||||
long time = getTime();
|
||||
int delta = (int) (time - lastFrame);
|
||||
lastFrame = time;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updated the FPS counter
|
||||
*/
|
||||
protected void updateFPS() {
|
||||
if (getTime() - lastFPS > 1000) {
|
||||
lastFPS = getTime();
|
||||
recordedFPS = fps;
|
||||
fps = 0;
|
||||
}
|
||||
fps++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum amount of time in milliseonds that has to
|
||||
* pass before update() is called on the container game. This gives
|
||||
* a way to limit logic updates compared to renders.
|
||||
*
|
||||
* @param interval The minimum interval between logic updates
|
||||
*/
|
||||
public void setMinimumLogicUpdateInterval(int interval) {
|
||||
minimumLogicInterval = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum amount of time in milliseconds that can passed
|
||||
* into the update method. Useful for collision detection without
|
||||
* sweeping.
|
||||
*
|
||||
* @param interval The maximum interval between logic updates
|
||||
*/
|
||||
public void setMaximumLogicUpdateInterval(int interval) {
|
||||
maximumLogicInterval = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update and render the game
|
||||
*
|
||||
* @param delta The change in time since last update and render
|
||||
* @throws SlickException Indicates an internal fault to the game.
|
||||
*/
|
||||
protected void updateAndRender(int delta) throws SlickException {
|
||||
if (smoothDeltas) {
|
||||
if (getFPS() != 0) {
|
||||
delta = 1000 / getFPS();
|
||||
}
|
||||
}
|
||||
|
||||
input.poll(width, height);
|
||||
|
||||
Music.poll(delta);
|
||||
if (!paused) {
|
||||
storedDelta += delta;
|
||||
|
||||
if (storedDelta >= minimumLogicInterval) {
|
||||
try {
|
||||
if (maximumLogicInterval != 0) {
|
||||
long cycles = storedDelta / maximumLogicInterval;
|
||||
for (int i=0;i<cycles;i++) {
|
||||
game.update(this, (int) maximumLogicInterval);
|
||||
}
|
||||
|
||||
int remainder = (int) (storedDelta % maximumLogicInterval);
|
||||
if (remainder > minimumLogicInterval) {
|
||||
game.update(this, (int) (remainder % maximumLogicInterval));
|
||||
storedDelta = 0;
|
||||
} else {
|
||||
storedDelta = remainder;
|
||||
}
|
||||
} else {
|
||||
game.update(this, (int) storedDelta);
|
||||
storedDelta = 0;
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
// Log.error(e);
|
||||
throw new SlickException("Game.update() failure.", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
game.update(this, 0);
|
||||
}
|
||||
|
||||
if (hasFocus() || getAlwaysRender()) {
|
||||
if (clearEachFrame) {
|
||||
GL.glClear(SGL.GL_COLOR_BUFFER_BIT | SGL.GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
GL.glLoadIdentity();
|
||||
|
||||
graphics.resetTransform();
|
||||
graphics.resetFont();
|
||||
graphics.resetLineWidth();
|
||||
graphics.setAntiAlias(false);
|
||||
try {
|
||||
game.render(this, graphics);
|
||||
} catch (Throwable e) {
|
||||
// Log.error(e);
|
||||
throw new SlickException("Game.render() failure.", e);
|
||||
}
|
||||
graphics.resetTransform();
|
||||
|
||||
if (showFPS) {
|
||||
defaultFont.drawString(10, 10, "FPS: "+recordedFPS);
|
||||
}
|
||||
|
||||
GL.flush();
|
||||
}
|
||||
|
||||
if (targetFPS != -1) {
|
||||
Display.sync(targetFPS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if the display should update only when the game is visible
|
||||
* (the default is true)
|
||||
*
|
||||
* @param updateOnlyWhenVisible True if we should updated only when the display is visible
|
||||
*/
|
||||
public void setUpdateOnlyWhenVisible(boolean updateOnlyWhenVisible) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this game is only updating when visible to the user (default = true)
|
||||
*
|
||||
* @return True if the game is only updated when the display is visible
|
||||
*/
|
||||
public boolean isUpdatingOnlyWhenVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the GL context
|
||||
*/
|
||||
protected void initGL() {
|
||||
Log.info("Starting display "+width+"x"+height);
|
||||
GL.initDisplay(width, height);
|
||||
|
||||
if (input == null) {
|
||||
input = new Input(height);
|
||||
}
|
||||
input.init(height);
|
||||
// no need to remove listeners?
|
||||
//input.removeAllListeners();
|
||||
if (game instanceof InputListener) {
|
||||
input.removeListener((InputListener) game);
|
||||
input.addListener((InputListener) game);
|
||||
}
|
||||
|
||||
if (graphics != null) {
|
||||
graphics.setDimensions(getWidth(), getHeight());
|
||||
}
|
||||
lastGame = game;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the system components, OpenGL and OpenAL.
|
||||
*
|
||||
* @throws SlickException Indicates a failure to create a native handler
|
||||
*/
|
||||
protected void initSystem() throws SlickException {
|
||||
initGL();
|
||||
setMusicVolume(1.0f);
|
||||
setSoundVolume(1.0f);
|
||||
|
||||
graphics = new Graphics(width, height);
|
||||
defaultFont = graphics.getFont();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter the orthographic mode
|
||||
*/
|
||||
protected void enterOrtho() {
|
||||
enterOrtho(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether the container should show the FPS
|
||||
*
|
||||
* @param show True if the container should show the FPS
|
||||
*/
|
||||
public void setShowFPS(boolean show) {
|
||||
showFPS = show;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the FPS is currently showing
|
||||
*
|
||||
* @return True if the FPS is showing
|
||||
*/
|
||||
public boolean isShowingFPS() {
|
||||
return showFPS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target fps we're hoping to get
|
||||
*
|
||||
* @param fps The target fps we're hoping to get
|
||||
*/
|
||||
public void setTargetFrameRate(int fps) {
|
||||
targetFPS = fps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether the display should be synced to the
|
||||
* vertical refresh (stops tearing)
|
||||
*
|
||||
* @param vsync True if we want to sync to vertical refresh
|
||||
*/
|
||||
public void setVSync(boolean vsync) {
|
||||
this.vsync = vsync;
|
||||
Display.setVSyncEnabled(vsync);
|
||||
}
|
||||
|
||||
/**
|
||||
* True if vsync is requested
|
||||
*
|
||||
* @return True if vsync is requested
|
||||
*/
|
||||
public boolean isVSyncRequested() {
|
||||
return vsync;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the game is running
|
||||
*
|
||||
* @return True if the game is running
|
||||
*/
|
||||
protected boolean running() {
|
||||
return running;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inidcate we want verbose logging
|
||||
*
|
||||
* @param verbose True if we want verbose logging (INFO and DEBUG)
|
||||
*/
|
||||
public void setVerbose(boolean verbose) {
|
||||
Log.setVerbose(verbose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cause the game to exit and shutdown cleanly
|
||||
*/
|
||||
public void exit() {
|
||||
running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the game currently has focus
|
||||
*
|
||||
* @return True if the game currently has focus
|
||||
*/
|
||||
public abstract boolean hasFocus();
|
||||
|
||||
/**
|
||||
* Get the graphics context used by this container. Note that this
|
||||
* value may vary over the life time of the game.
|
||||
*
|
||||
* @return The graphics context used by this container
|
||||
*/
|
||||
public Graphics getGraphics() {
|
||||
return graphics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter the orthographic mode
|
||||
*
|
||||
* @param xsize The size of the panel being used
|
||||
* @param ysize The size of the panel being used
|
||||
*/
|
||||
protected void enterOrtho(int xsize, int ysize) {
|
||||
GL.enterOrtho(xsize, ysize);
|
||||
}
|
||||
}
|
|
@ -34,232 +34,70 @@ import org.newdawn.slick.Font;
|
|||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.geom.Rectangle;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.components.ActionListener;
|
||||
import yugecin.opsudance.core.components.Component;
|
||||
|
||||
/**
|
||||
* A single text field supporting text entry
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class TextField extends AbstractComponent {
|
||||
/** The key repeat interval */
|
||||
public class TextField extends Component {
|
||||
|
||||
private static final int INITIAL_KEY_REPEAT_INTERVAL = 400;
|
||||
/** The key repeat interval */
|
||||
private static final int KEY_REPEAT_INTERVAL = 50;
|
||||
|
||||
/** The width of the field */
|
||||
private int width;
|
||||
private final DisplayContainer displayContainer;
|
||||
|
||||
/** The height of the field */
|
||||
private int height;
|
||||
|
||||
/** The location in the X coordinate */
|
||||
protected int x;
|
||||
|
||||
/** The location in the Y coordinate */
|
||||
protected int y;
|
||||
|
||||
/** The maximum number of characters allowed to be input */
|
||||
private String value = "";
|
||||
private Font font;
|
||||
private int maxCharacter = 10000;
|
||||
|
||||
/** The value stored in the text field */
|
||||
private String value = "";
|
||||
private Color borderCol = Color.white;
|
||||
private Color textCol = Color.white;
|
||||
private Color backgroundCol = new Color(0, 0, 0, 0.5f);
|
||||
|
||||
/** The font used to render text in the field */
|
||||
private Font font;
|
||||
|
||||
/** The border color - null if no border */
|
||||
private Color border = Color.white;
|
||||
|
||||
/** The text color */
|
||||
private Color text = Color.white;
|
||||
|
||||
/** The background color - null if no background */
|
||||
private Color background = new Color(0, 0, 0, 0.5f);
|
||||
|
||||
/** The current cursor position */
|
||||
private int cursorPos;
|
||||
|
||||
/** True if the cursor should be visible */
|
||||
private boolean visibleCursor = true;
|
||||
|
||||
/** The last key pressed */
|
||||
private int lastKey = -1;
|
||||
|
||||
/** The last character pressed */
|
||||
private char lastChar = 0;
|
||||
|
||||
/** The time since last key repeat */
|
||||
private long repeatTimer;
|
||||
|
||||
/** The text before the paste in */
|
||||
private String oldText;
|
||||
|
||||
/** The cursor position before the paste */
|
||||
private int oldCursorPos;
|
||||
|
||||
/** True if events should be consumed by the field */
|
||||
private boolean consume = true;
|
||||
|
||||
/**
|
||||
* Create a new text field
|
||||
*
|
||||
* @param container
|
||||
* The container rendering this field
|
||||
* @param font
|
||||
* The font to use in the text field
|
||||
* @param x
|
||||
* The x coordinate of the top left corner of the text field
|
||||
* @param y
|
||||
* The y coordinate of the top left corner of the text field
|
||||
* @param width
|
||||
* The width of the text field
|
||||
* @param height
|
||||
* The height of the text field
|
||||
* @param listener
|
||||
* The listener to add to the text field
|
||||
*/
|
||||
public TextField(GUIContext container, Font font, int x, int y, int width,
|
||||
int height, ComponentListener listener) {
|
||||
this(container,font,x,y,width,height);
|
||||
addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new text field
|
||||
*
|
||||
* @param container
|
||||
* The container rendering this field
|
||||
* @param font
|
||||
* The font to use in the text field
|
||||
* @param x
|
||||
* The x coordinate of the top left corner of the text field
|
||||
* @param y
|
||||
* The y coordinate of the top left corner of the text field
|
||||
* @param width
|
||||
* The width of the text field
|
||||
* @param height
|
||||
* The height of the text field
|
||||
*/
|
||||
public TextField(GUIContext container, Font font, int x, int y, int width,
|
||||
int height) {
|
||||
super(container);
|
||||
private ActionListener listener;
|
||||
|
||||
public TextField(DisplayContainer displayContainer, Font font, int x, int y, int width, int height) {
|
||||
this.displayContainer = displayContainer;
|
||||
this.font = font;
|
||||
|
||||
setLocation(x, y);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if the input events should be consumed by this field
|
||||
*
|
||||
* @param consume True if events should be consumed by this field
|
||||
*/
|
||||
public void setConsumeEvents(boolean consume) {
|
||||
this.consume = consume;
|
||||
public void setListener(ActionListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the key input handling for this field
|
||||
*/
|
||||
public void deactivate() {
|
||||
setFocus(false);
|
||||
public void setBorderColor(Color border) {
|
||||
this.borderCol = border;
|
||||
}
|
||||
|
||||
public void setTextColor(Color text) {
|
||||
this.textCol = text;
|
||||
}
|
||||
|
||||
public void setBackgroundColor(Color background) {
|
||||
this.backgroundCol = background;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the component.
|
||||
*
|
||||
* @param x
|
||||
* X coordinate
|
||||
* @param y
|
||||
* Y coordinate
|
||||
*/
|
||||
@Override
|
||||
public void setLocation(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
public boolean isFocusable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position in the X coordinate
|
||||
*
|
||||
* @return x
|
||||
*/
|
||||
@Override
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position in the Y coordinate
|
||||
*
|
||||
* @return y
|
||||
*/
|
||||
@Override
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the component
|
||||
*
|
||||
* @return The width of the component
|
||||
*/
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the component
|
||||
*
|
||||
* @return The height of the component
|
||||
*/
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the background color. Set to null to disable the background
|
||||
*
|
||||
* @param color
|
||||
* The color to use for the background
|
||||
*/
|
||||
public void setBackgroundColor(Color color) {
|
||||
background = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the border color. Set to null to disable the border
|
||||
*
|
||||
* @param color
|
||||
* The color to use for the border
|
||||
*/
|
||||
public void setBorderColor(Color color) {
|
||||
border = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text color.
|
||||
*
|
||||
* @param color
|
||||
* The color to use for the text
|
||||
*/
|
||||
public void setTextColor(Color color) {
|
||||
text = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.gui.AbstractComponent#render(org.newdawn.slick.gui.GUIContext,
|
||||
* org.newdawn.slick.Graphics)
|
||||
*/
|
||||
@Override
|
||||
public void render(GUIContext container, Graphics g) {
|
||||
public void render(Graphics g) {
|
||||
if (lastKey != -1) {
|
||||
if (input.isKeyDown(lastKey)) {
|
||||
if (displayContainer.input.isKeyDown(lastKey)) {
|
||||
if (repeatTimer < System.currentTimeMillis()) {
|
||||
repeatTimer = System.currentTimeMillis() + KEY_REPEAT_INTERVAL;
|
||||
keyPressed(lastKey, lastChar);
|
||||
|
@ -274,11 +112,11 @@ public class TextField extends AbstractComponent {
|
|||
// Someone could have set a color for me to blend...
|
||||
Color clr = g.getColor();
|
||||
|
||||
if (background != null) {
|
||||
g.setColor(background.multiply(clr));
|
||||
if (backgroundCol != null) {
|
||||
g.setColor(backgroundCol.multiply(clr));
|
||||
g.fillRect(x, y, width, height);
|
||||
}
|
||||
g.setColor(text.multiply(clr));
|
||||
g.setColor(textCol.multiply(clr));
|
||||
Font temp = g.getFont();
|
||||
|
||||
int cpos = font.getWidth(value.substring(0, cursorPos));
|
||||
|
@ -291,14 +129,14 @@ public class TextField extends AbstractComponent {
|
|||
g.setFont(font);
|
||||
g.drawString(value, x + 1, y + 1);
|
||||
|
||||
if (hasFocus() && visibleCursor) {
|
||||
g.drawString("_", x + 1 + cpos + 2, y + 1);
|
||||
if (focused) {
|
||||
g.drawString("|", x + 1 + cpos + 2, y + 1);
|
||||
}
|
||||
|
||||
g.translate(-tx - 2, 0);
|
||||
|
||||
if (border != null) {
|
||||
g.setColor(border.multiply(clr));
|
||||
if (borderCol != null) {
|
||||
g.setColor(borderCol.multiply(clr));
|
||||
g.drawRect(x, y, width, height);
|
||||
}
|
||||
g.setColor(clr);
|
||||
|
@ -307,21 +145,10 @@ public class TextField extends AbstractComponent {
|
|||
g.setClip(oldClip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value in the text field
|
||||
*
|
||||
* @return The value in the text field
|
||||
*/
|
||||
public String getText() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value to be displayed in the text field
|
||||
*
|
||||
* @param value
|
||||
* The value to be displayed in the text field
|
||||
*/
|
||||
public void setText(String value) {
|
||||
this.value = value;
|
||||
if (cursorPos > value.length()) {
|
||||
|
@ -329,35 +156,6 @@ public class TextField extends AbstractComponent {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position of the cursor
|
||||
*
|
||||
* @param pos
|
||||
* The new position of the cursor
|
||||
*/
|
||||
public void setCursorPos(int pos) {
|
||||
cursorPos = pos;
|
||||
if (cursorPos > value.length()) {
|
||||
cursorPos = value.length();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether the mouse cursor should be visible or not
|
||||
*
|
||||
* @param visibleCursor
|
||||
* True if the mouse cursor should be visible
|
||||
*/
|
||||
public void setCursorVisible(boolean visibleCursor) {
|
||||
this.visibleCursor = visibleCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the length of the allowed input
|
||||
*
|
||||
* @param length
|
||||
* The length of the allowed input
|
||||
*/
|
||||
public void setMaxLength(int length) {
|
||||
maxCharacter = length;
|
||||
if (value.length() > maxCharacter) {
|
||||
|
@ -365,71 +163,23 @@ public class TextField extends AbstractComponent {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the paste into the field, overrideable for custom behaviour
|
||||
*
|
||||
* @param text The text to be pasted in
|
||||
*/
|
||||
protected void doPaste(String text) {
|
||||
recordOldPosition();
|
||||
|
||||
for (int i=0;i<text.length();i++) {
|
||||
keyPressed(-1, text.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the old position and content
|
||||
*/
|
||||
protected void recordOldPosition() {
|
||||
oldText = getText();
|
||||
oldCursorPos = cursorPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the undo of the paste, overrideable for custom behaviour
|
||||
*
|
||||
* @param oldCursorPos before the paste
|
||||
* @param oldText The text before the last paste
|
||||
*/
|
||||
protected void doUndo(int oldCursorPos, String oldText) {
|
||||
if (oldText != null) {
|
||||
setText(oldText);
|
||||
setCursorPos(oldCursorPos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.gui.AbstractComponent#keyPressed(int, char)
|
||||
*/
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
if (hasFocus()) {
|
||||
if (key != -1)
|
||||
{
|
||||
if ((key == Input.KEY_V) &&
|
||||
((input.isKeyDown(Input.KEY_LCONTROL)) || (input.isKeyDown(Input.KEY_RCONTROL)))) {
|
||||
((displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) || (displayContainer.input.isKeyDown(Input.KEY_RCONTROL)))) {
|
||||
String text = Sys.getClipboard();
|
||||
if (text != null) {
|
||||
doPaste(text);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* if ((key == Input.KEY_Z) &&
|
||||
((input.isKeyDown(Input.KEY_LCONTROL)) || (input.isKeyDown(Input.KEY_RCONTROL)))) {
|
||||
if (oldText != null) {
|
||||
doUndo(oldCursorPos, oldText);
|
||||
}
|
||||
return;
|
||||
} */
|
||||
|
||||
// alt and control keys don't come through here
|
||||
/* if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) {
|
||||
return;
|
||||
} */
|
||||
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastKey != key) {
|
||||
|
@ -458,7 +208,7 @@ public class TextField extends AbstractComponent {
|
|||
}
|
||||
*/ } else if (key == Input.KEY_BACK) {
|
||||
if ((cursorPos > 0) && (value.length() > 0)) {
|
||||
if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) {
|
||||
if (displayContainer.input.isKeyDown(Input.KEY_LCONTROL) || displayContainer.input.isKeyDown(Input.KEY_RCONTROL)) {
|
||||
int sp = 0;
|
||||
boolean startSpace = Character.isWhitespace(value.charAt(cursorPos - 1));
|
||||
boolean charSeen = false;
|
||||
|
@ -490,18 +240,10 @@ public class TextField extends AbstractComponent {
|
|||
cursorPos--;
|
||||
}
|
||||
}
|
||||
// Nobody more will be notified
|
||||
if (consume) {
|
||||
container.getInput().consumeEvent();
|
||||
}
|
||||
} else if (key == Input.KEY_DELETE) {
|
||||
if (value.length() > cursorPos) {
|
||||
value = value.substring(0,cursorPos) + value.substring(cursorPos+1);
|
||||
}
|
||||
// Nobody more will be notified
|
||||
if (consume) {
|
||||
container.getInput().consumeEvent();
|
||||
}
|
||||
} else if ((c < 127) && (c > 31) && (value.length() < maxCharacter)) {
|
||||
if (cursorPos < value.length()) {
|
||||
value = value.substring(0, cursorPos) + c
|
||||
|
@ -510,28 +252,12 @@ public class TextField extends AbstractComponent {
|
|||
value = value.substring(0, cursorPos) + c;
|
||||
}
|
||||
cursorPos++;
|
||||
// Nobody more will be notified
|
||||
if (consume) {
|
||||
container.getInput().consumeEvent();
|
||||
}
|
||||
} else if (key == Input.KEY_RETURN) {
|
||||
notifyListeners();
|
||||
// Nobody more will be notified
|
||||
if (consume) {
|
||||
container.getInput().consumeEvent();
|
||||
if (listener != null) {
|
||||
listener.onAction();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.gui.AbstractComponent#setFocus(boolean)
|
||||
*/
|
||||
@Override
|
||||
public void setFocus(boolean focus) {
|
||||
lastKey = -1;
|
||||
|
||||
super.setFocus(focus);
|
||||
}
|
||||
}
|
||||
|
|
184
src/yugecin/opsudance/OpsuDance.java
Normal file
184
src/yugecin/opsudance/OpsuDance.java
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance;
|
||||
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapWatchService;
|
||||
import itdelatrisu.opsu.db.DBController;
|
||||
import itdelatrisu.opsu.downloads.DownloadList;
|
||||
import itdelatrisu.opsu.downloads.Updater;
|
||||
import itdelatrisu.opsu.states.Splash;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
import yugecin.opsudance.states.EmptyState;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import static yugecin.opsudance.core.Entrypoint.sout;
|
||||
|
||||
/*
|
||||
* loosely based on itdelatrisu.opsu.Opsu
|
||||
*/
|
||||
public class OpsuDance {
|
||||
|
||||
private final DisplayContainer container;
|
||||
|
||||
private ServerSocket singleInstanceSocket;
|
||||
|
||||
public OpsuDance(DisplayContainer container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
public void start(String[] args) {
|
||||
try {
|
||||
sout("initialized");
|
||||
|
||||
checkRunningDirectory();
|
||||
Options.parseOptions();
|
||||
ensureSingleInstance();
|
||||
sout("prechecks done and options parsed");
|
||||
|
||||
initDatabase();
|
||||
initUpdater(args);
|
||||
sout("database & updater initialized");
|
||||
|
||||
//container.init(EmptyState.class);
|
||||
container.init(Splash.class);
|
||||
} catch (Exception e) {
|
||||
errorAndExit("startup failure", e);
|
||||
}
|
||||
|
||||
while (rungame());
|
||||
container.teardownAL();
|
||||
|
||||
Options.saveOptions();
|
||||
closeSingleInstanceSocket();
|
||||
DBController.closeConnections();
|
||||
DownloadList.get().cancelAllDownloads();
|
||||
Utils.deleteDirectory(Options.TEMP_DIR);
|
||||
if (!Options.isWatchServiceEnabled()) {
|
||||
BeatmapWatchService.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean rungame() {
|
||||
try {
|
||||
container.setup();
|
||||
container.resume();
|
||||
} catch (Exception e) {
|
||||
ErrorHandler.error("could not initialize GL", e).allowTerminate().preventContinue().show();
|
||||
return false;
|
||||
}
|
||||
Exception caughtException = null;
|
||||
try {
|
||||
container.run();
|
||||
} catch (Exception e) {
|
||||
caughtException = e;
|
||||
}
|
||||
container.teardown();
|
||||
container.pause();
|
||||
return caughtException != null && ErrorHandler.error("update/render error", caughtException).allowTerminate().show().shouldIgnoreAndContinue();
|
||||
}
|
||||
|
||||
private void initDatabase() {
|
||||
try {
|
||||
DBController.init();
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
errorAndExit("Could not initialize database.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void initUpdater(String[] args) {
|
||||
// check if just updated
|
||||
if (args.length >= 2)
|
||||
Updater.get().setUpdateInfo(args[0], args[1]);
|
||||
|
||||
// check for updates
|
||||
if (Options.isUpdaterDisabled()) {
|
||||
return;
|
||||
}
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Updater.get().checkForUpdates();
|
||||
} catch (IOException e) {
|
||||
Log.warn("updatecheck failed.", e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private void checkRunningDirectory() {
|
||||
if (!Utils.isJarRunning()) {
|
||||
return;
|
||||
}
|
||||
File runningDir = Utils.getRunningDirectory();
|
||||
if (runningDir == null) {
|
||||
return;
|
||||
}
|
||||
if (runningDir.getAbsolutePath().indexOf('!') == -1) {
|
||||
return;
|
||||
}
|
||||
errorAndExit("Cannot run from a path that contains a '!'. Please move or rename the jar and try again.");
|
||||
}
|
||||
|
||||
private void ensureSingleInstance() {
|
||||
if (Options.noSingleInstance()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
singleInstanceSocket = new ServerSocket(Options.getPort(), 1, InetAddress.getLocalHost());
|
||||
} catch (UnknownHostException e) {
|
||||
// shouldn't happen
|
||||
} catch (IOException e) {
|
||||
errorAndExit(String.format(
|
||||
"Could not launch. Either opsu! is already running or a different program uses port %d.\n" +
|
||||
"You can change the port opsu! uses by editing the 'Port' field in the .opsu.cfg configuration file.\n" +
|
||||
"If that still does not resolve the problem, you can set 'NoSingleInstance' to 'true', but this is not recommended.", Options.getPort()), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeSingleInstanceSocket() {
|
||||
if (singleInstanceSocket == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
singleInstanceSocket.close();
|
||||
} catch (IOException e) {
|
||||
Log.error("Single instance socket was not closed!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void errorAndExit(String errstr) {
|
||||
ErrorHandler.error(errstr, new Throwable()).allowTerminate().preventContinue().show();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
private void errorAndExit(String errstr, Throwable cause) {
|
||||
ErrorHandler.error(errstr, cause).preventContinue().show();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
}
|
68
src/yugecin/opsudance/PreStartupInitializer.java
Normal file
68
src/yugecin/opsudance/PreStartupInitializer.java
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance;
|
||||
|
||||
import itdelatrisu.opsu.NativeLoader;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import org.newdawn.slick.util.FileSystemLocation;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class PreStartupInitializer {
|
||||
|
||||
public PreStartupInitializer() {
|
||||
loadNatives();
|
||||
setResourcePath();
|
||||
}
|
||||
|
||||
private void loadNatives() {
|
||||
File nativeDir = loadNativesUsingOptionsPath();
|
||||
|
||||
System.setProperty("org.lwjgl.librarypath", nativeDir.getAbsolutePath());
|
||||
System.setProperty("java.library.path", nativeDir.getAbsolutePath());
|
||||
|
||||
try {
|
||||
// Workaround for "java.library.path" property being read-only.
|
||||
// http://stackoverflow.com/a/24988095
|
||||
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
|
||||
fieldSysPath.setAccessible(true);
|
||||
fieldSysPath.set(null, null);
|
||||
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
|
||||
Log.warn("Failed to set 'sys_paths' field.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private File loadNativesUsingOptionsPath() {
|
||||
File nativeDir = Options.NATIVE_DIR;
|
||||
try {
|
||||
new NativeLoader(nativeDir).loadNatives();
|
||||
} catch (IOException e) {
|
||||
Log.error("Error loading natives.", e);
|
||||
}
|
||||
return nativeDir;
|
||||
}
|
||||
|
||||
private void setResourcePath() {
|
||||
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
|
||||
}
|
||||
|
||||
}
|
460
src/yugecin/opsudance/core/DisplayContainer.java
Normal file
460
src/yugecin/opsudance/core/DisplayContainer.java
Normal file
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core;
|
||||
|
||||
import itdelatrisu.opsu.GameData;
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.beatmap.Beatmap;
|
||||
import itdelatrisu.opsu.downloads.DownloadList;
|
||||
import itdelatrisu.opsu.downloads.Updater;
|
||||
import itdelatrisu.opsu.render.CurveRenderState;
|
||||
import itdelatrisu.opsu.ui.Cursor;
|
||||
import itdelatrisu.opsu.ui.Fonts;
|
||||
import itdelatrisu.opsu.ui.UI;
|
||||
import org.lwjgl.Sys;
|
||||
import org.lwjgl.openal.AL;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.DisplayMode;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.newdawn.slick.*;
|
||||
import org.newdawn.slick.opengl.InternalTextureLoader;
|
||||
import org.newdawn.slick.opengl.renderer.Renderer;
|
||||
import org.newdawn.slick.opengl.renderer.SGL;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorDumpable;
|
||||
import yugecin.opsudance.core.inject.InstanceContainer;
|
||||
import yugecin.opsudance.core.state.OpsuState;
|
||||
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
|
||||
import yugecin.opsudance.core.state.specialstates.BubbleNotificationState;
|
||||
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
|
||||
import yugecin.opsudance.core.state.transitions.*;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
import yugecin.opsudance.events.ResolutionChangedEvent;
|
||||
import yugecin.opsudance.utils.GLHelper;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
import static yugecin.opsudance.core.Entrypoint.sout;
|
||||
|
||||
/**
|
||||
* based on org.newdawn.slick.AppGameContainer
|
||||
*/
|
||||
public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListener {
|
||||
|
||||
private static SGL GL = Renderer.get();
|
||||
|
||||
public final EventBus eventBus;
|
||||
private final InstanceContainer instanceContainer;
|
||||
|
||||
private FpsRenderState fpsState;
|
||||
private BarNotificationState barNotifState;
|
||||
private BubbleNotificationState bubNotifState;
|
||||
|
||||
private TransitionState outTransitionState;
|
||||
private TransitionState inTransitionState;
|
||||
|
||||
private final TransitionFinishedListener outTransitionListener;
|
||||
private final TransitionFinishedListener inTransitionListener;
|
||||
|
||||
private OpsuState state;
|
||||
|
||||
public final DisplayMode nativeDisplayMode;
|
||||
|
||||
private Graphics graphics;
|
||||
public Input input;
|
||||
|
||||
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;
|
||||
public int delta;
|
||||
|
||||
public boolean exitRequested;
|
||||
|
||||
public int timeSinceLastRender;
|
||||
|
||||
private long lastFrame;
|
||||
|
||||
private boolean wasMusicPlaying;
|
||||
|
||||
private String glVersion;
|
||||
private String glVendor;
|
||||
|
||||
private long exitconfirmation;
|
||||
|
||||
public final Cursor cursor;
|
||||
public boolean drawCursor;
|
||||
|
||||
public DisplayContainer(InstanceContainer instanceContainer, EventBus eventBus) {
|
||||
this.instanceContainer = instanceContainer;
|
||||
this.eventBus = eventBus;
|
||||
this.cursor = new Cursor();
|
||||
drawCursor = true;
|
||||
|
||||
outTransitionListener = new TransitionFinishedListener() {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
state.leave();
|
||||
outTransitionState.getApplicableState().leave();
|
||||
state = inTransitionState;
|
||||
state.enter();
|
||||
inTransitionState.getApplicableState().enter();
|
||||
}
|
||||
};
|
||||
|
||||
inTransitionListener = new TransitionFinishedListener() {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
state.leave();
|
||||
state = inTransitionState.getApplicableState();
|
||||
}
|
||||
};
|
||||
|
||||
this.nativeDisplayMode = Display.getDisplayMode();
|
||||
setUPS(1000);
|
||||
setFPS(60);
|
||||
targetBackgroundRenderInterval = 41; // ~24 fps
|
||||
lastFrame = getTime();
|
||||
delta = 1;
|
||||
renderDelta = 1;
|
||||
}
|
||||
|
||||
public void setUPS(int ups) {
|
||||
targetUpdatesPerSecond = ups;
|
||||
targetUpdateInterval = 1000 / targetUpdatesPerSecond;
|
||||
}
|
||||
|
||||
public void setFPS(int fps) {
|
||||
targetRendersPerSecond = fps;
|
||||
targetRenderInterval = 1000 / targetRendersPerSecond;
|
||||
}
|
||||
|
||||
public void init(Class<? extends OpsuState> startingState) {
|
||||
state = instanceContainer.provide(startingState);
|
||||
state.enter();
|
||||
|
||||
fpsState = instanceContainer.provide(FpsRenderState.class);
|
||||
bubNotifState = instanceContainer.provide(BubbleNotificationState.class);
|
||||
barNotifState = instanceContainer.provide(BarNotificationState.class);
|
||||
}
|
||||
|
||||
|
||||
public void run() throws Exception {
|
||||
while(!exitRequested && !(Display.isCloseRequested() && state.onCloseRequest()) || !confirmExit()) {
|
||||
delta = getDelta();
|
||||
|
||||
timeSinceLastRender += delta;
|
||||
|
||||
input.poll(width, height);
|
||||
Music.poll(delta);
|
||||
mouseX = input.getMouseX();
|
||||
mouseY = input.getMouseY();
|
||||
|
||||
state.update();
|
||||
if (drawCursor) {
|
||||
cursor.setCursorPosition(delta, mouseX, mouseY);
|
||||
}
|
||||
|
||||
int maxRenderInterval;
|
||||
if (Display.isVisible() && Display.isActive()) {
|
||||
maxRenderInterval = targetRenderInterval;
|
||||
} else {
|
||||
maxRenderInterval = targetBackgroundRenderInterval;
|
||||
}
|
||||
|
||||
if (timeSinceLastRender >= maxRenderInterval) {
|
||||
GL.glClear(SGL.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
/*
|
||||
graphics.resetTransform();
|
||||
graphics.resetFont();
|
||||
graphics.resetLineWidth();
|
||||
graphics.resetTransform();
|
||||
*/
|
||||
|
||||
renderDelta = timeSinceLastRender;
|
||||
|
||||
state.preRenderUpdate();
|
||||
state.render(graphics);
|
||||
fpsState.render(graphics);
|
||||
bubNotifState.render(graphics);
|
||||
barNotifState.render(graphics);
|
||||
|
||||
cursor.updateAngle(renderDelta);
|
||||
if (drawCursor) {
|
||||
cursor.draw(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON));
|
||||
}
|
||||
UI.drawTooltip(graphics);
|
||||
|
||||
timeSinceLastRender = 0;
|
||||
|
||||
Display.update(false);
|
||||
}
|
||||
|
||||
Display.processMessages();
|
||||
Display.sync(targetUpdatesPerSecond);
|
||||
}
|
||||
}
|
||||
|
||||
public void setup() throws Exception {
|
||||
width = height = -1;
|
||||
Input.disableControllers();
|
||||
Display.setTitle("opsu!dance");
|
||||
Options.setDisplayMode(this);
|
||||
Display.create();
|
||||
GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" });
|
||||
initGL();
|
||||
glVersion = GL11.glGetString(GL11.GL_VERSION);
|
||||
glVendor = GL11.glGetString(GL11.GL_VENDOR);
|
||||
GLHelper.hideNativeCursor();
|
||||
}
|
||||
|
||||
public void teardown() {
|
||||
InternalTextureLoader.get().clear();
|
||||
GameImage.destroyImages();
|
||||
GameData.Grade.destroyImages();
|
||||
Beatmap.destroyBackgroundImageCache();
|
||||
CurveRenderState.shutdown();
|
||||
Display.destroy();
|
||||
}
|
||||
|
||||
public void teardownAL() {
|
||||
AL.destroy();
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
wasMusicPlaying = MusicController.isPlaying();
|
||||
if (wasMusicPlaying) {
|
||||
MusicController.pause();
|
||||
}
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
if (wasMusicPlaying) {
|
||||
MusicController.resume();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean confirmExit() {
|
||||
if (System.currentTimeMillis() - exitconfirmation < 10000) {
|
||||
return true;
|
||||
}
|
||||
if (DownloadList.get().hasActiveDownloads()) {
|
||||
eventBus.post(new BubbleNotificationEvent(DownloadList.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
|
||||
exitRequested = false;
|
||||
exitconfirmation = System.currentTimeMillis();
|
||||
return false;
|
||||
}
|
||||
if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING) {
|
||||
eventBus.post(new BubbleNotificationEvent(Updater.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
|
||||
exitRequested = false;
|
||||
exitconfirmation = System.currentTimeMillis();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception {
|
||||
if (this.width == width && this.height == height) {
|
||||
Display.setFullscreen(fullscreen);
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayMode displayMode = null;
|
||||
if (fullscreen) {
|
||||
displayMode = GLHelper.findFullscreenDisplayMode(nativeDisplayMode.getBitsPerPixel(), nativeDisplayMode.getFrequency(), width, height);
|
||||
}
|
||||
|
||||
if (displayMode == null) {
|
||||
displayMode = new DisplayMode(width, height);
|
||||
if (fullscreen) {
|
||||
fullscreen = false;
|
||||
Log.warn("could not find fullscreen displaymode for " + width + "x" + height);
|
||||
eventBus.post(new BubbleNotificationEvent("Fullscreen mode is not supported for " + width + "x" + height, BubbleNotificationEvent.COLOR_ORANGE));
|
||||
}
|
||||
}
|
||||
|
||||
this.width = displayMode.getWidth();
|
||||
this.height = displayMode.getHeight();
|
||||
|
||||
Display.setDisplayMode(displayMode);
|
||||
Display.setFullscreen(fullscreen);
|
||||
|
||||
if (Display.isCreated()) {
|
||||
initGL();
|
||||
}
|
||||
|
||||
if (displayMode.getBitsPerPixel() == 16) {
|
||||
InternalTextureLoader.get().set16BitMode();
|
||||
}
|
||||
}
|
||||
|
||||
private void initGL() throws Exception {
|
||||
GL.initDisplay(width, height);
|
||||
GL.enterOrtho(width, height);
|
||||
|
||||
graphics = new Graphics(width, height);
|
||||
graphics.setAntiAlias(false);
|
||||
|
||||
input = new Input(height);
|
||||
input.enableKeyRepeat();
|
||||
input.addKeyListener(this);
|
||||
input.addMouseListener(this);
|
||||
|
||||
sout("GL ready");
|
||||
|
||||
GameImage.init(width, height);
|
||||
Fonts.init();
|
||||
|
||||
eventBus.post(new ResolutionChangedEvent(this.width, this.height));
|
||||
}
|
||||
|
||||
public void resetCursor() {
|
||||
cursor.reset(mouseX, mouseY);
|
||||
}
|
||||
|
||||
private int getDelta() {
|
||||
long time = getTime();
|
||||
int delta = (int) (time - lastFrame);
|
||||
lastFrame = time;
|
||||
return delta;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeErrorDump(StringWriter dump) {
|
||||
dump.append("> DisplayContainer dump\n");
|
||||
dump.append("OpenGL version: ").append(glVersion).append( "(").append(glVendor).append(")\n");
|
||||
if (isTransitioning()) {
|
||||
dump.append("doing a transition\n");
|
||||
dump.append("using out transition ").append(outTransitionState.getClass().getSimpleName()).append('\n');
|
||||
dump.append("using in transition ").append(inTransitionState.getClass().getSimpleName()).append('\n');
|
||||
if (state == inTransitionState) {
|
||||
dump.append("currently doing the in transition\n");
|
||||
} else {
|
||||
dump.append("currently doing the out transition\n");
|
||||
}
|
||||
}
|
||||
state.writeErrorDump(dump);
|
||||
}
|
||||
|
||||
public boolean isInState(Class<? extends OpsuState> state) {
|
||||
return state.isInstance(state);
|
||||
}
|
||||
|
||||
public boolean isTransitioning() {
|
||||
return state instanceof TransitionState;
|
||||
}
|
||||
|
||||
public void switchState(Class<? extends OpsuState> newState) {
|
||||
switchState(newState, FadeOutTransitionState.class, 200, FadeInTransitionState.class, 300);
|
||||
}
|
||||
|
||||
public void switchStateNow(Class<? extends OpsuState> newState) {
|
||||
switchState(newState, EmptyTransitionState.class, 0, FadeInTransitionState.class, 300);
|
||||
}
|
||||
|
||||
public void switchStateInstantly(Class<? extends OpsuState> newState) {
|
||||
state.leave();
|
||||
state = instanceContainer.provide(newState);
|
||||
state.enter();
|
||||
}
|
||||
|
||||
public void switchState(Class<? extends OpsuState> newState, Class<? extends TransitionState> outTransition, int outTime, Class<? extends TransitionState> inTransition, int inTime) {
|
||||
if (isTransitioning()) {
|
||||
return;
|
||||
}
|
||||
outTransitionState = instanceContainer.provide(outTransition).set(state, outTime, outTransitionListener);
|
||||
inTransitionState = instanceContainer.provide(inTransition).set(instanceContainer.provide(newState), inTime, inTransitionListener);
|
||||
state = outTransitionState;
|
||||
state.enter();
|
||||
}
|
||||
|
||||
/*
|
||||
* input events below, see org.newdawn.slick.KeyListener & org.newdawn.slick.MouseListener
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
state.keyPressed(key, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(int key, char c) {
|
||||
state.keyReleased(key, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int change) {
|
||||
state.mouseWheelMoved(change);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(int button, int x, int y, int clickCount) { }
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
state.mousePressed(button, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
if (bubNotifState.mouseReleased(x, y)) {
|
||||
return;
|
||||
}
|
||||
state.mouseReleased(button, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(int oldx, int oldy, int newx, int newy) { }
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
state.mouseDragged(oldx, oldy, newx, newy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInput(Input input) { }
|
||||
|
||||
@Override
|
||||
public boolean isAcceptingInput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inputEnded() { }
|
||||
|
||||
@Override
|
||||
public void inputStarted() { }
|
||||
|
||||
}
|
45
src/yugecin/opsudance/core/Entrypoint.java
Normal file
45
src/yugecin/opsudance/core/Entrypoint.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core;
|
||||
|
||||
import itdelatrisu.opsu.downloads.Updater;
|
||||
import yugecin.opsudance.OpsuDance;
|
||||
import yugecin.opsudance.core.inject.OpsuDanceInjector;
|
||||
|
||||
public class Entrypoint {
|
||||
|
||||
public static final long startTime = System.currentTimeMillis();
|
||||
|
||||
public static void main(String[] args) {
|
||||
sout("launched");
|
||||
(new OpsuDanceInjector()).provide(OpsuDance.class).start(args);
|
||||
|
||||
if (Updater.get().getStatus() == Updater.Status.UPDATE_FINAL) {
|
||||
Updater.get().runUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public static long runtime() {
|
||||
return System.currentTimeMillis() - startTime;
|
||||
}
|
||||
|
||||
public static void sout(String message) {
|
||||
System.out.println(String.format("[%7d] %s", runtime(), message));
|
||||
}
|
||||
|
||||
}
|
24
src/yugecin/opsudance/core/components/ActionListener.java
Normal file
24
src/yugecin/opsudance/core/components/ActionListener.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.components;
|
||||
|
||||
public interface ActionListener {
|
||||
|
||||
void onAction();
|
||||
|
||||
}
|
60
src/yugecin/opsudance/core/components/Component.java
Normal file
60
src/yugecin/opsudance/core/components/Component.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.components;
|
||||
|
||||
import org.newdawn.slick.Graphics;
|
||||
|
||||
public abstract class Component {
|
||||
|
||||
public int width;
|
||||
public int height;
|
||||
public int x;
|
||||
public int y;
|
||||
|
||||
protected boolean focused;
|
||||
protected boolean hovered;
|
||||
|
||||
public abstract boolean isFocusable();
|
||||
|
||||
public boolean isHovered() {
|
||||
return hovered;
|
||||
}
|
||||
|
||||
public void updateHover(int x, int y) {
|
||||
this.hovered = this.x <= x && x <= this.x + width && this.y <= y && y <= this.y + height;
|
||||
}
|
||||
|
||||
public void mouseReleased(int button) {
|
||||
}
|
||||
|
||||
public void preRenderUpdate() {
|
||||
}
|
||||
|
||||
public abstract void render(Graphics g);
|
||||
|
||||
public void keyPressed(int key, char c) {
|
||||
}
|
||||
|
||||
public void keyReleased(int key, char c) {
|
||||
}
|
||||
|
||||
public void setFocused(boolean focused) {
|
||||
this.focused = focused;
|
||||
}
|
||||
|
||||
}
|
26
src/yugecin/opsudance/core/errorhandling/ErrorDumpable.java
Normal file
26
src/yugecin/opsudance/core/errorhandling/ErrorDumpable.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.errorhandling;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
public interface ErrorDumpable {
|
||||
|
||||
void writeErrorDump(StringWriter dump);
|
||||
|
||||
}
|
248
src/yugecin/opsudance/core/errorhandling/ErrorHandler.java
Normal file
248
src/yugecin/opsudance/core/errorhandling/ErrorHandler.java
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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.errorhandling;
|
||||
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.utils.MiscUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* based on itdelatrisu.opsu.ErrorHandler
|
||||
*/
|
||||
public class ErrorHandler {
|
||||
|
||||
private static ErrorHandler instance;
|
||||
|
||||
private final DisplayContainer displayContainer;
|
||||
|
||||
private String customMessage;
|
||||
private Throwable cause;
|
||||
private String errorDump;
|
||||
private String messageBody;
|
||||
|
||||
private boolean preventContinue;
|
||||
private boolean preventReport;
|
||||
private boolean ignoreAndContinue;
|
||||
private boolean allowTerminate;
|
||||
|
||||
public ErrorHandler(DisplayContainer displayContainer) {
|
||||
this.displayContainer = displayContainer;
|
||||
instance = this;
|
||||
}
|
||||
|
||||
private ErrorHandler init(String customMessage, Throwable cause) {
|
||||
this.customMessage = customMessage;
|
||||
this.cause = cause;
|
||||
|
||||
StringWriter dump = new StringWriter();
|
||||
try {
|
||||
displayContainer.writeErrorDump(dump);
|
||||
} catch (Exception e) {
|
||||
dump
|
||||
.append("### ")
|
||||
.append(e.getClass().getSimpleName())
|
||||
.append(" while creating errordump");
|
||||
e.printStackTrace(new PrintWriter(dump));
|
||||
}
|
||||
errorDump = dump.toString();
|
||||
|
||||
dump = new StringWriter();
|
||||
dump.append(customMessage).append("\n");
|
||||
cause.printStackTrace(new PrintWriter(dump));
|
||||
dump.append("\n").append(errorDump);
|
||||
messageBody = dump.toString();
|
||||
|
||||
Log.error("====== start unhandled exception dump");
|
||||
Log.error(messageBody);
|
||||
Log.error("====== end unhandled exception dump");
|
||||
return this;
|
||||
}
|
||||
|
||||
public static ErrorHandler error(String message, Throwable cause) {
|
||||
return instance.init(message, cause);
|
||||
}
|
||||
|
||||
public ErrorHandler preventReport() {
|
||||
preventReport = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorHandler allowTerminate() {
|
||||
allowTerminate = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorHandler preventContinue() {
|
||||
preventContinue = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorHandler show() {
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception e) {
|
||||
Log.warn("Unable to set look and feel for error dialog");
|
||||
}
|
||||
|
||||
String title = "opsu!dance error - " + customMessage;
|
||||
|
||||
String messageText = "opsu!dance has encountered an error.";
|
||||
if (!preventReport) {
|
||||
messageText += " Please report this!";
|
||||
}
|
||||
JLabel message = new JLabel(messageText);
|
||||
|
||||
JTextArea textArea = new JTextArea(15, 100);
|
||||
textArea.setEditable(false);
|
||||
textArea.setBackground(UIManager.getColor("Panel.background"));
|
||||
textArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
|
||||
textArea.setTabSize(2);
|
||||
textArea.setLineWrap(false);
|
||||
textArea.setWrapStyleWord(true);
|
||||
textArea.setText(messageBody);
|
||||
|
||||
Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(), createReportButton() };
|
||||
|
||||
String[] buttons;
|
||||
if (!allowTerminate && !preventContinue) {
|
||||
buttons = new String[] { "Ignore & continue" };
|
||||
} else if (preventContinue) {
|
||||
buttons = new String[] { "Terminate" };
|
||||
} else {
|
||||
buttons = new String[] { "Terminate", "Ignore & continue" };
|
||||
}
|
||||
|
||||
JFrame frame = new JFrame(title);
|
||||
frame.setUndecorated(true);
|
||||
frame.setVisible(true);
|
||||
frame.setLocationRelativeTo(null);
|
||||
int result = JOptionPane.showOptionDialog(frame,
|
||||
messageComponents,
|
||||
title,
|
||||
JOptionPane.DEFAULT_OPTION,
|
||||
JOptionPane.ERROR_MESSAGE,
|
||||
null,
|
||||
buttons,
|
||||
buttons[buttons.length - 1]);
|
||||
ignoreAndContinue = !allowTerminate || result == 1;
|
||||
frame.dispose();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private JComponent createViewLogButton() {
|
||||
return createButton("View log", Desktop.Action.OPEN, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
try {
|
||||
Desktop.getDesktop().open(Options.LOG_FILE);
|
||||
} catch (IOException e) {
|
||||
Log.warn("Could not open log file", e);
|
||||
JOptionPane.showMessageDialog(null, "whoops could not open log file", "errorception", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private JComponent createReportButton() {
|
||||
if (preventReport) {
|
||||
return new JLabel();
|
||||
}
|
||||
return createButton("Report error", Desktop.Action.BROWSE, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(createGithubIssueUrl());
|
||||
} catch (IOException e) {
|
||||
Log.warn("Could not open browser to report issue", e);
|
||||
JOptionPane.showMessageDialog(null, "whoops could not launch a browser", "errorception", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private JButton createButton(String buttonText, Desktop.Action action, ActionListener listener) {
|
||||
JButton button = new JButton(buttonText);
|
||||
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(action)) {
|
||||
button.addActionListener(listener);
|
||||
return button;
|
||||
}
|
||||
button.setEnabled(false);
|
||||
return button;
|
||||
}
|
||||
|
||||
private URI createGithubIssueUrl() {
|
||||
StringWriter dump = new StringWriter();
|
||||
|
||||
dump.append(customMessage).append("\n");
|
||||
|
||||
dump.append("**ver** ").append(MiscUtils.buildProperties.get().getProperty("version")).append('\n');
|
||||
String gitHash = Utils.getGitHash();
|
||||
if (gitHash != null) {
|
||||
dump.append("**git hash** ").append(gitHash.substring(0, 12)).append('\n');
|
||||
}
|
||||
|
||||
dump.append("**os** ").append(System.getProperty("os.name"))
|
||||
.append(" (").append(System.getProperty("os.arch")).append(")\n");
|
||||
dump.append("**jre** ").append(System.getProperty("java.version")).append('\n');
|
||||
|
||||
dump.append("**trace**").append("\n```\n");
|
||||
cause.printStackTrace(new PrintWriter(dump));
|
||||
dump.append("\n```\n");
|
||||
|
||||
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(Options.ISSUES_URL, issueTitle, issueBody));
|
||||
}
|
||||
|
||||
private String truncateGithubIssueBody(String body) {
|
||||
if (body.replaceAll("[^a-zA-Z+-]", "").length() < 1750) {
|
||||
return body;
|
||||
}
|
||||
Log.warn("error dump too long to fit into github issue url, truncating");
|
||||
return body.substring(0, 1640) + "** TRUNCATED **\n```";
|
||||
}
|
||||
|
||||
public boolean shouldIgnoreAndContinue() {
|
||||
return ignoreAndContinue;
|
||||
}
|
||||
|
||||
}
|
59
src/yugecin/opsudance/core/events/EventBus.java
Normal file
59
src/yugecin/opsudance/core/events/EventBus.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.util.*;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class EventBus {
|
||||
|
||||
@Deprecated
|
||||
public static EventBus instance; // TODO get rid of this
|
||||
|
||||
private final List<Subscriber> subscribers;
|
||||
|
||||
public EventBus() {
|
||||
subscribers = new LinkedList<>();
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public <T> void subscribe(Class<T> eventType, EventListener<T> eventListener) {
|
||||
subscribers.add(new Subscriber<>(eventType, eventListener));
|
||||
}
|
||||
|
||||
public void post(Object event) {
|
||||
for (Subscriber s : subscribers) {
|
||||
if (s.eventType.isInstance(event)) {
|
||||
s.listener.onEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Subscriber<T> {
|
||||
|
||||
private final Class<T> eventType;
|
||||
private final EventListener<T> listener;
|
||||
|
||||
private Subscriber(Class<T> eventType, EventListener<T> listener) {
|
||||
this.eventType = eventType;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
24
src/yugecin/opsudance/core/events/EventListener.java
Normal file
24
src/yugecin/opsudance/core/events/EventListener.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.events;
|
||||
|
||||
public interface EventListener<T> {
|
||||
|
||||
void onEvent(T event);
|
||||
|
||||
}
|
25
src/yugecin/opsudance/core/inject/Binder.java
Normal file
25
src/yugecin/opsudance/core/inject/Binder.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
99
src/yugecin/opsudance/core/inject/Injector.java
Normal file
99
src/yugecin/opsudance/core/inject/Injector.java
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract class Injector implements InstanceContainer, Binder {
|
||||
|
||||
private final HashMap<Class<?>, Object> instances;
|
||||
private final LinkedList<Class<?>> lazyInstances;
|
||||
|
||||
private Class<?> lastType;
|
||||
|
||||
public Injector() {
|
||||
instances = new HashMap<>();
|
||||
lazyInstances = new LinkedList<>();
|
||||
instances.put(InstanceContainer.class, this);
|
||||
configure();
|
||||
}
|
||||
|
||||
protected abstract void configure();
|
||||
|
||||
public final <T> T provide(Class<T> type) {
|
||||
Object instance = instances.get(type);
|
||||
if (instance != null) {
|
||||
return (T) instance;
|
||||
}
|
||||
ListIterator<Class<?>> iter = lazyInstances.listIterator();
|
||||
while (iter.hasNext()) {
|
||||
Class<?> l = iter.next();
|
||||
if (l == type) {
|
||||
iter.remove();
|
||||
instance = createInstance(type);
|
||||
instances.put(type, instance);
|
||||
return (T) instance;
|
||||
}
|
||||
}
|
||||
return createInstance(type);
|
||||
}
|
||||
|
||||
private <T> T createInstance(Class<T> type) {
|
||||
Constructor<?>[] constructors = type.getDeclaredConstructors();
|
||||
if (constructors.length == 0) {
|
||||
throw new RuntimeException("Cannot provide " + type.getSimpleName());
|
||||
}
|
||||
Constructor constructor = constructors[0];
|
||||
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||
Object[] params = new Object[parameterTypes.length];
|
||||
for (int i = parameterTypes.length - 1; i >= 0; i--) {
|
||||
params[i] = provide(parameterTypes[i]);
|
||||
}
|
||||
try {
|
||||
return (T) constructor.newInstance(params);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public final <T> Binder<T> bind(Class<T> type) {
|
||||
lastType = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void asEagerSingleton() {
|
||||
instances.put(lastType, createInstance(lastType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void asLazySingleton() {
|
||||
lazyInstances.add(lastType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void to(Class type) {
|
||||
instances.put(lastType, createInstance(type));
|
||||
}
|
||||
|
||||
}
|
24
src/yugecin/opsudance/core/inject/InstanceContainer.java
Normal file
24
src/yugecin/opsudance/core/inject/InstanceContainer.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.inject;
|
||||
|
||||
public interface InstanceContainer {
|
||||
|
||||
<T> T provide(Class<T> type);
|
||||
|
||||
}
|
65
src/yugecin/opsudance/core/inject/OpsuDanceInjector.java
Normal file
65
src/yugecin/opsudance/core/inject/OpsuDanceInjector.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.inject;
|
||||
|
||||
import itdelatrisu.opsu.states.*;
|
||||
import yugecin.opsudance.PreStartupInitializer;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
|
||||
import yugecin.opsudance.core.state.specialstates.BubbleNotificationState;
|
||||
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
|
||||
import yugecin.opsudance.core.state.transitions.EmptyTransitionState;
|
||||
import yugecin.opsudance.core.state.transitions.FadeInTransitionState;
|
||||
import yugecin.opsudance.core.state.transitions.FadeOutTransitionState;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
import yugecin.opsudance.states.EmptyRedState;
|
||||
import yugecin.opsudance.states.EmptyState;
|
||||
|
||||
public class OpsuDanceInjector extends Injector {
|
||||
|
||||
protected void configure() {
|
||||
bind(EventBus.class).asEagerSingleton();
|
||||
|
||||
bind(PreStartupInitializer.class).asEagerSingleton();
|
||||
bind(DisplayContainer.class).asEagerSingleton();
|
||||
|
||||
bind(ErrorHandler.class).asEagerSingleton();
|
||||
|
||||
bind(FpsRenderState.class).asEagerSingleton();
|
||||
bind(BarNotificationState.class).asEagerSingleton();
|
||||
bind(BubbleNotificationState.class).asEagerSingleton();
|
||||
|
||||
bind(EmptyTransitionState.class).asEagerSingleton();
|
||||
bind(FadeInTransitionState.class).asEagerSingleton();
|
||||
bind(FadeOutTransitionState.class).asEagerSingleton();
|
||||
|
||||
bind(EmptyRedState.class).asEagerSingleton();
|
||||
bind(EmptyState.class).asEagerSingleton();
|
||||
|
||||
bind(Splash.class).asEagerSingleton();
|
||||
bind(MainMenu.class).asEagerSingleton();
|
||||
bind(ButtonMenu.class).asEagerSingleton();
|
||||
bind(SongMenu.class).asEagerSingleton();
|
||||
bind(DownloadsMenu.class).asEagerSingleton();
|
||||
bind(Game.class).asEagerSingleton();
|
||||
bind(GameRanking.class).asEagerSingleton();
|
||||
bind(GamePauseMenu.class).asEagerSingleton();
|
||||
}
|
||||
|
||||
}
|
136
src/yugecin/opsudance/core/state/BaseOpsuState.java
Normal file
136
src/yugecin/opsudance/core/state/BaseOpsuState.java
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state;
|
||||
|
||||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.events.EventListener;
|
||||
import yugecin.opsudance.events.ResolutionChangedEvent;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
public abstract class BaseOpsuState implements OpsuState, EventListener<ResolutionChangedEvent> {
|
||||
|
||||
protected final DisplayContainer displayContainer;
|
||||
|
||||
/**
|
||||
* state is dirty when resolution or skin changed but hasn't rendered yet
|
||||
*/
|
||||
private boolean isDirty;
|
||||
private boolean isCurrentState;
|
||||
|
||||
public BaseOpsuState(DisplayContainer displayContainer) {
|
||||
this.displayContainer = displayContainer;
|
||||
displayContainer.eventBus.subscribe(ResolutionChangedEvent.class, this);
|
||||
}
|
||||
|
||||
protected void revalidate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRenderUpdate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Graphics g) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ResolutionChangedEvent event) {
|
||||
if (isCurrentState) {
|
||||
revalidate();
|
||||
return;
|
||||
}
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
isCurrentState = true;
|
||||
if (isDirty) {
|
||||
revalidate();
|
||||
isDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave() {
|
||||
isCurrentState = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCloseRequest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int key, char c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyReleased(int key, char c) {
|
||||
if (key == Input.KEY_F7) {
|
||||
Options.setNextFPS(displayContainer);
|
||||
return true;
|
||||
}
|
||||
if (key == Input.KEY_F10) {
|
||||
Options.toggleMouseDisabled();
|
||||
return true;
|
||||
}
|
||||
if (key == Input.KEY_F12) {
|
||||
Utils.takeScreenShot();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseWheelMoved(int delta) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased(int button, int x, int y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeErrorDump(StringWriter dump) {
|
||||
dump.append("> BaseOpsuState dump\n");
|
||||
dump.append("isDirty: ").append(String.valueOf(isDirty)).append('\n');
|
||||
}
|
||||
|
||||
}
|
194
src/yugecin/opsudance/core/state/ComplexOpsuState.java
Normal file
194
src/yugecin/opsudance/core/state/ComplexOpsuState.java
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state;
|
||||
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.components.Component;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public abstract class ComplexOpsuState extends BaseOpsuState {
|
||||
|
||||
protected final LinkedList<Component> components;
|
||||
protected final LinkedList<OverlayOpsuState> overlays;
|
||||
|
||||
private Component focusedComponent;
|
||||
|
||||
public ComplexOpsuState(DisplayContainer displayContainer) {
|
||||
super(displayContainer);
|
||||
this.components = new LinkedList<>();
|
||||
this.overlays = new LinkedList<>();
|
||||
}
|
||||
|
||||
public final void focusComponent(Component component) {
|
||||
if (!component.isFocusable()) {
|
||||
return;
|
||||
}
|
||||
if (focusedComponent != null) {
|
||||
focusedComponent.setFocused(false);
|
||||
}
|
||||
focusedComponent = component;
|
||||
component.setFocused(true);
|
||||
}
|
||||
|
||||
public boolean isAnyComponentFocused() {
|
||||
return focusedComponent != null || isAnyOverlayActive();
|
||||
}
|
||||
|
||||
public boolean isAnyOverlayActive() {
|
||||
for (OverlayOpsuState overlay : overlays) {
|
||||
if (overlay.active) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseWheelMoved(int delta) {
|
||||
for (OverlayOpsuState overlay : overlays) {
|
||||
if (overlay.mouseWheelMoved(delta)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
for (OverlayOpsuState overlay : overlays) {
|
||||
if (overlay.mousePressed(button, x, y)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
for (OverlayOpsuState overlay : overlays) {
|
||||
if (overlay.mouseDragged(oldx, oldy, newx, newy)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased(int button, int x, int y) {
|
||||
for (OverlayOpsuState overlay : overlays) {
|
||||
if (overlay.mouseReleased(button, x, y)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (focusedComponent == null) {
|
||||
for (Component component : components) {
|
||||
if (!component.isFocusable()) {
|
||||
continue;
|
||||
}
|
||||
component.updateHover(x, y);
|
||||
if (component.isHovered()) {
|
||||
focusedComponent = component;
|
||||
focusedComponent.setFocused(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
focusedComponent.updateHover(x, y);
|
||||
if (focusedComponent.isHovered()) {
|
||||
focusedComponent.mouseReleased(button);
|
||||
return true;
|
||||
}
|
||||
focusedComponent.setFocused(false);
|
||||
focusedComponent = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRenderUpdate() {
|
||||
super.preRenderUpdate();
|
||||
for (Component component : components) {
|
||||
component.updateHover(displayContainer.mouseX, displayContainer.mouseY);
|
||||
component.preRenderUpdate();
|
||||
}
|
||||
for (OverlayOpsuState overlay : overlays) {
|
||||
overlay.preRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void revalidate() {
|
||||
super.revalidate();
|
||||
for (OverlayOpsuState overlay : overlays) {
|
||||
overlay.revalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Graphics g) {
|
||||
for (OverlayOpsuState overlay : overlays) {
|
||||
overlay.render(g);
|
||||
}
|
||||
super.render(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyReleased(int key, char c) {
|
||||
if (super.keyReleased(key, c)) {
|
||||
return true;
|
||||
}
|
||||
for (OverlayOpsuState overlay : overlays) {
|
||||
if (overlay.keyReleased(key, c)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (focusedComponent != null) {
|
||||
if (key == Input.KEY_ESCAPE) {
|
||||
focusedComponent.setFocused(false);
|
||||
focusedComponent = null;
|
||||
return true;
|
||||
}
|
||||
focusedComponent.keyReleased(key, c);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int key, char c) {
|
||||
for (OverlayOpsuState overlay : overlays) {
|
||||
if (overlay.keyPressed(key, c)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (focusedComponent != null) {
|
||||
if (key == Input.KEY_ESCAPE) {
|
||||
focusedComponent.setFocused(false);
|
||||
focusedComponent = null;
|
||||
return true;
|
||||
}
|
||||
focusedComponent.keyPressed(key, c);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
66
src/yugecin/opsudance/core/state/OpsuState.java
Normal file
66
src/yugecin/opsudance/core/state/OpsuState.java
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state;
|
||||
|
||||
import org.newdawn.slick.Graphics;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorDumpable;
|
||||
|
||||
public interface OpsuState extends ErrorDumpable {
|
||||
|
||||
void update();
|
||||
void preRenderUpdate();
|
||||
void render(Graphics g);
|
||||
void enter();
|
||||
void leave();
|
||||
|
||||
/**
|
||||
* @return true if closing is allowed
|
||||
*/
|
||||
boolean onCloseRequest();
|
||||
|
||||
/**
|
||||
* @return false to stop event bubbling
|
||||
*/
|
||||
boolean keyPressed(int key, char c);
|
||||
|
||||
/**
|
||||
* @return false to stop event bubbling
|
||||
*/
|
||||
boolean keyReleased(int key, char c);
|
||||
|
||||
/**
|
||||
* @return false to stop event bubbling
|
||||
*/
|
||||
boolean mouseWheelMoved(int delta);
|
||||
|
||||
/**
|
||||
* @return false to stop event bubbling
|
||||
*/
|
||||
boolean mousePressed(int button, int x, int y);
|
||||
|
||||
/**
|
||||
* @return false to stop event bubbling
|
||||
*/
|
||||
boolean mouseReleased(int button, int x, int y);
|
||||
|
||||
/**
|
||||
* @return false to stop event bubbling
|
||||
*/
|
||||
boolean mouseDragged(int oldx, int oldy, int newx, int newy);
|
||||
|
||||
}
|
122
src/yugecin/opsudance/core/state/OverlayOpsuState.java
Normal file
122
src/yugecin/opsudance/core/state/OverlayOpsuState.java
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state;
|
||||
|
||||
import org.newdawn.slick.Graphics;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
public abstract class OverlayOpsuState implements OpsuState {
|
||||
|
||||
protected boolean active;
|
||||
protected boolean acceptInput;
|
||||
|
||||
public void hide() {
|
||||
acceptInput = active = false;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
acceptInput = active = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void update() {
|
||||
}
|
||||
|
||||
public void revalidate() {
|
||||
}
|
||||
|
||||
protected abstract void onPreRenderUpdate();
|
||||
|
||||
@Override
|
||||
public final void preRenderUpdate() {
|
||||
if (active) {
|
||||
onPreRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void onRender(Graphics g);
|
||||
|
||||
@Override
|
||||
public final void render(Graphics g) {
|
||||
if (active) {
|
||||
onRender(g);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void enter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void leave() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onCloseRequest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract boolean onKeyPressed(int key, char c);
|
||||
|
||||
@Override
|
||||
public final boolean keyPressed(int key, char c) {
|
||||
return acceptInput && onKeyPressed(key, c);
|
||||
}
|
||||
|
||||
protected abstract boolean onKeyReleased(int key, char c);
|
||||
|
||||
@Override
|
||||
public final boolean keyReleased(int key, char c) {
|
||||
return acceptInput && onKeyReleased(key, c);
|
||||
}
|
||||
|
||||
protected abstract boolean onMouseWheelMoved(int delta);
|
||||
|
||||
@Override
|
||||
public final boolean mouseWheelMoved(int delta) {
|
||||
return acceptInput && onMouseWheelMoved(delta);
|
||||
}
|
||||
|
||||
protected abstract boolean onMousePressed(int button, int x, int y);
|
||||
|
||||
@Override
|
||||
public final boolean mousePressed(int button, int x, int y) {
|
||||
return acceptInput && onMousePressed(button, x, y);
|
||||
}
|
||||
|
||||
protected abstract boolean onMouseReleased(int button, int x, int y);
|
||||
|
||||
@Override
|
||||
public final boolean mouseReleased(int button, int x, int y) {
|
||||
return acceptInput && onMouseReleased(button, x, y);
|
||||
}
|
||||
|
||||
protected abstract boolean onMouseDragged(int oldx, int oldy, int newx, int newy);
|
||||
|
||||
@Override
|
||||
public final boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
return acceptInput && onMouseDragged(oldx, oldy, newx, newy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeErrorDump(StringWriter dump) {
|
||||
dump.append("> OverlayOpsuState dump\n");
|
||||
dump.append("accepts input: ").append(String.valueOf(acceptInput)).append(" is active: ").append(String.valueOf(active));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state.specialstates;
|
||||
|
||||
import itdelatrisu.opsu.ui.Fonts;
|
||||
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.core.events.EventListener;
|
||||
import yugecin.opsudance.events.BarNotificationEvent;
|
||||
import yugecin.opsudance.events.ResolutionChangedEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BarNotificationState implements EventListener<BarNotificationEvent> {
|
||||
|
||||
private final int IN_TIME = 200;
|
||||
private final int DISPLAY_TIME = 5700 + IN_TIME;
|
||||
private final int OUT_TIME = 200;
|
||||
private final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME;
|
||||
|
||||
private final DisplayContainer displayContainer;
|
||||
private final Color bgcol;
|
||||
private final Color textCol;
|
||||
|
||||
private int timeShown;
|
||||
|
||||
private String message;
|
||||
private List<String> lines;
|
||||
private int textY;
|
||||
|
||||
private int barHalfTargetHeight;
|
||||
private int barHalfHeight;
|
||||
|
||||
public BarNotificationState(DisplayContainer displayContainer, EventBus eventBus) {
|
||||
this.displayContainer = displayContainer;
|
||||
this.bgcol = new Color(Color.black);
|
||||
this.textCol = new Color(Color.white);
|
||||
this.timeShown = TOTAL_TIME;
|
||||
eventBus.subscribe(BarNotificationEvent.class, this);
|
||||
eventBus.subscribe(ResolutionChangedEvent.class, new EventListener<ResolutionChangedEvent>() {
|
||||
@Override
|
||||
public void onEvent(ResolutionChangedEvent event) {
|
||||
if (timeShown >= TOTAL_TIME) {
|
||||
return;
|
||||
}
|
||||
calculatePosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void render(Graphics g) {
|
||||
if (timeShown >= TOTAL_TIME) {
|
||||
return;
|
||||
}
|
||||
timeShown += displayContainer.renderDelta;
|
||||
processAnimations();
|
||||
g.setColor(bgcol);
|
||||
g.fillRect(0, displayContainer.height / 2 - barHalfHeight, displayContainer.width, barHalfHeight * 2);
|
||||
int y = textY;
|
||||
for (String line : lines) {
|
||||
Fonts.LARGE.drawString((displayContainer.width - Fonts.LARGE.getWidth(line)) / 2, y, line, textCol);
|
||||
y += Fonts.LARGE.getLineHeight();
|
||||
}
|
||||
}
|
||||
|
||||
private void processAnimations() {
|
||||
if (timeShown < IN_TIME) {
|
||||
float progress = (float) timeShown / IN_TIME;
|
||||
barHalfHeight = (int) (barHalfTargetHeight * AnimationEquation.OUT_BACK.calc(progress));
|
||||
textCol.a = progress;
|
||||
bgcol.a = 0.4f * progress;
|
||||
return;
|
||||
}
|
||||
if (timeShown > DISPLAY_TIME) {
|
||||
float progress = 1f - (float) (timeShown - DISPLAY_TIME) / OUT_TIME;
|
||||
textCol.a = progress;
|
||||
bgcol.a = 0.4f * progress;
|
||||
return;
|
||||
}
|
||||
barHalfHeight = barHalfTargetHeight;
|
||||
textCol.a = 1f;
|
||||
bgcol.a = 0.4f;
|
||||
}
|
||||
|
||||
private void calculatePosition() {
|
||||
this.lines = Fonts.wrap(Fonts.LARGE, message, (int) (displayContainer.width * 0.96f), true);
|
||||
int textHeight = (int) (Fonts.LARGE.getLineHeight() * (lines.size() + 0.5f));
|
||||
textY = (displayContainer.height - textHeight) / 2 + Fonts.LARGE.getLineHeight() / 8;
|
||||
barHalfTargetHeight = textHeight / 2 + Fonts.LARGE.getLineHeight() / 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(BarNotificationEvent event) {
|
||||
this.message = event.message;
|
||||
calculatePosition();
|
||||
timeShown = 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state.specialstates;
|
||||
|
||||
import itdelatrisu.opsu.ui.Fonts;
|
||||
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.events.EventBus;
|
||||
import yugecin.opsudance.core.events.EventListener;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
import yugecin.opsudance.events.ResolutionChangedEvent;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class BubbleNotificationState implements EventListener<BubbleNotificationEvent> {
|
||||
|
||||
public static final int IN_TIME = 633;
|
||||
public static final int DISPLAY_TIME = 7000 + IN_TIME;
|
||||
public static final int OUT_TIME = 433;
|
||||
public static final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME;
|
||||
|
||||
private final DisplayContainer displayContainer;
|
||||
private final LinkedList<Notification> bubbles;
|
||||
|
||||
private int addAnimationTime;
|
||||
private int addAnimationHeight;
|
||||
|
||||
public BubbleNotificationState(DisplayContainer displayContainer, EventBus eventBus) {
|
||||
this.displayContainer = displayContainer;
|
||||
this.bubbles = new LinkedList<>();
|
||||
this.addAnimationTime = IN_TIME;
|
||||
eventBus.subscribe(BubbleNotificationEvent.class, this);
|
||||
eventBus.subscribe(ResolutionChangedEvent.class, new EventListener<ResolutionChangedEvent>() {
|
||||
@Override
|
||||
public void onEvent(ResolutionChangedEvent event) {
|
||||
calculatePositions();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void render(Graphics g) {
|
||||
ListIterator<Notification> iter = bubbles.listIterator();
|
||||
if (!iter.hasNext()) {
|
||||
return;
|
||||
}
|
||||
addAnimationTime += displayContainer.renderDelta;
|
||||
if (addAnimationTime > IN_TIME) {
|
||||
finishAddAnimation();
|
||||
}
|
||||
boolean animateUp = false;
|
||||
do {
|
||||
Notification next = iter.next();
|
||||
if (animateUp && addAnimationTime < IN_TIME) {
|
||||
next.y = next.baseY - (int) (addAnimationHeight * AnimationEquation.OUT_QUINT.calc((float) addAnimationTime / IN_TIME));
|
||||
}
|
||||
if (next.render(g, displayContainer.mouseX, displayContainer.mouseY, displayContainer.renderDelta)) {
|
||||
iter.remove();
|
||||
}
|
||||
animateUp = true;
|
||||
} while (iter.hasNext());
|
||||
}
|
||||
|
||||
public boolean mouseReleased(int x, int y) {
|
||||
if (x < Notification.finalX) {
|
||||
return false;
|
||||
}
|
||||
for (Notification bubble : bubbles) {
|
||||
if (bubble.mouseReleased(x, y)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void calculatePositions() {
|
||||
Notification.width = (int) (displayContainer.width * 0.1703125f);
|
||||
Notification.baseLine = (int) (displayContainer.height * 0.9645f);
|
||||
Notification.paddingY = (int) (displayContainer.height * 0.0144f);
|
||||
Notification.finalX = displayContainer.width - Notification.width - (int) (displayContainer.width * 0.01);
|
||||
Notification.fontPaddingX = (int) (Notification.width * 0.02f);
|
||||
Notification.fontPaddingY = (int) (Fonts.SMALLBOLD.getLineHeight() / 4f);
|
||||
Notification.lineHeight = Fonts.SMALLBOLD.getLineHeight();
|
||||
if (bubbles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
finishAddAnimation();
|
||||
int y = Notification.baseLine;
|
||||
for (Notification bubble : bubbles) {
|
||||
bubble.recalculateDimensions();
|
||||
y -= bubble.height;
|
||||
bubble.baseY = bubble.y = y;
|
||||
y -= Notification.paddingY;
|
||||
}
|
||||
}
|
||||
|
||||
private void finishAddAnimation() {
|
||||
if (bubbles.isEmpty()) {
|
||||
addAnimationHeight = 0;
|
||||
addAnimationTime = IN_TIME;
|
||||
return;
|
||||
}
|
||||
ListIterator<Notification> iter = bubbles.listIterator();
|
||||
iter.next();
|
||||
while (iter.hasNext()) {
|
||||
Notification bubble = iter.next();
|
||||
bubble.y = bubble.baseY - addAnimationHeight;
|
||||
bubble.baseY = bubble.y;
|
||||
}
|
||||
addAnimationHeight = 0;
|
||||
addAnimationTime = IN_TIME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(BubbleNotificationEvent event) {
|
||||
finishAddAnimation();
|
||||
Notification newBubble = new Notification(event.message, event.borderColor);
|
||||
bubbles.add(0, newBubble);
|
||||
addAnimationTime = 0;
|
||||
addAnimationHeight = newBubble.height + Notification.paddingY;
|
||||
ListIterator<Notification> iter = bubbles.listIterator();
|
||||
iter.next();
|
||||
while (iter.hasNext()) {
|
||||
Notification next = iter.next();
|
||||
next.baseY = next.y;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Notification {
|
||||
|
||||
private final static int HOVER_ANIM_TIME = 150;
|
||||
|
||||
private static int width;
|
||||
private static int finalX;
|
||||
private static int baseLine;
|
||||
private static int fontPaddingX;
|
||||
private static int fontPaddingY;
|
||||
private static int lineHeight;
|
||||
private static int paddingY;
|
||||
|
||||
private final Color bgcol;
|
||||
private final Color textColor;
|
||||
private final Color borderColor;
|
||||
private final Color targetBorderColor;
|
||||
|
||||
private int timeShown;
|
||||
private int x;
|
||||
private int y;
|
||||
private int baseY;
|
||||
private int height;
|
||||
|
||||
private final String message;
|
||||
private List<String> lines;
|
||||
|
||||
private boolean isFading;
|
||||
private int hoverTime;
|
||||
|
||||
private Notification(String message, Color borderColor) {
|
||||
this.message = message;
|
||||
recalculateDimensions();
|
||||
this.targetBorderColor = borderColor;
|
||||
this.borderColor = new Color(borderColor);
|
||||
this.textColor = new Color(Color.white);
|
||||
this.bgcol = new Color(Color.black);
|
||||
this.y = baseLine - height;
|
||||
this.baseY = this.y;
|
||||
}
|
||||
|
||||
private void recalculateDimensions() {
|
||||
this.lines = Fonts.wrap(Fonts.SMALLBOLD, message, (int) (width * 0.96f), true);
|
||||
this.height = (int) (Fonts.SMALLBOLD.getLineHeight() * (lines.size() + 0.5f));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this notification expired
|
||||
*/
|
||||
private boolean render(Graphics g, int mouseX, int mouseY, int delta) {
|
||||
timeShown += delta;
|
||||
processAnimations(isMouseHovered(mouseX, mouseY), delta);
|
||||
g.setColor(bgcol);
|
||||
g.fillRoundRect(x, y, width, height, 6);
|
||||
g.setLineWidth(2f);
|
||||
g.setColor(borderColor);
|
||||
g.drawRoundRect(x, y, width, height, 6);
|
||||
int y = this.y + fontPaddingY;
|
||||
for (String line : lines) {
|
||||
Fonts.SMALLBOLD.drawString(x + fontPaddingX, y, line, textColor);
|
||||
y += lineHeight;
|
||||
}
|
||||
return timeShown > BubbleNotificationState.TOTAL_TIME;
|
||||
}
|
||||
|
||||
private void processAnimations(boolean mouseHovered, int delta) {
|
||||
if (mouseHovered) {
|
||||
hoverTime = Math.min(HOVER_ANIM_TIME, hoverTime + delta);
|
||||
} else {
|
||||
hoverTime = Math.max(0, hoverTime - delta);
|
||||
}
|
||||
float hoverProgress = (float) hoverTime / HOVER_ANIM_TIME;
|
||||
borderColor.r = targetBorderColor.r + (0.977f - targetBorderColor.r) * hoverProgress;
|
||||
borderColor.g = targetBorderColor.g + (0.977f - targetBorderColor.g) * hoverProgress;
|
||||
borderColor.b = targetBorderColor.b + (0.977f - targetBorderColor.b) * hoverProgress;
|
||||
if (timeShown < BubbleNotificationState.IN_TIME) {
|
||||
float progress = (float) timeShown / BubbleNotificationState.IN_TIME;
|
||||
this.x = finalX + (int) ((1 - AnimationEquation.OUT_BACK.calc(progress)) * width / 2);
|
||||
textColor.a = borderColor.a = bgcol.a = progress;
|
||||
return;
|
||||
}
|
||||
x = Notification.finalX;
|
||||
if (timeShown > BubbleNotificationState.DISPLAY_TIME) {
|
||||
isFading = true;
|
||||
float progress = (float) (timeShown - BubbleNotificationState.DISPLAY_TIME) / BubbleNotificationState.OUT_TIME;
|
||||
textColor.a = borderColor.a = bgcol.a = 1f - progress;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean mouseReleased(int x, int y) {
|
||||
if (!isFading && isMouseHovered(x, y)) {
|
||||
timeShown = BubbleNotificationState.DISPLAY_TIME;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isMouseHovered(int x, int y) {
|
||||
return this.x <= x && x < this.x + width && this.y <= y && y <= this.y + this.height;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state.specialstates;
|
||||
|
||||
import itdelatrisu.opsu.ui.Fonts;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.events.EventListener;
|
||||
import yugecin.opsudance.events.ResolutionChangedEvent;
|
||||
|
||||
public class FpsRenderState implements EventListener<ResolutionChangedEvent> {
|
||||
|
||||
private final DisplayContainer displayContainer;
|
||||
|
||||
private final static Color GREEN = new Color(171, 218, 25);
|
||||
private final static Color ORANGE = new Color(255, 204, 34);
|
||||
private final static Color DARKORANGE = new Color(255, 149, 24);
|
||||
|
||||
private int x;
|
||||
private int y;
|
||||
private int singleHeight;
|
||||
|
||||
public FpsRenderState(DisplayContainer displayContainer) {
|
||||
this.displayContainer = displayContainer;
|
||||
displayContainer.eventBus.subscribe(ResolutionChangedEvent.class, this);
|
||||
}
|
||||
|
||||
public void render(Graphics g) {
|
||||
int x = this.x;
|
||||
int target = displayContainer.targetRenderInterval + (displayContainer.targetUpdateInterval % displayContainer.targetRenderInterval);
|
||||
x = drawText(g, getColor(target, displayContainer.renderDelta), (1000 / displayContainer.renderDelta) + " fps", x, this.y);
|
||||
drawText(g, getColor(displayContainer.targetUpdateInterval, displayContainer.delta), (1000 / displayContainer.delta) + " ups", x, this.y);
|
||||
}
|
||||
|
||||
private Color getColor(int targetValue, int realValue) {
|
||||
if (realValue <= targetValue) {
|
||||
return GREEN;
|
||||
}
|
||||
if (realValue <= targetValue * 1.15f) {
|
||||
return ORANGE;
|
||||
}
|
||||
return DARKORANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return x position where the next block can be drawn (right aligned)
|
||||
*/
|
||||
private int drawText(Graphics g, Color color, String text, int x, int y) {
|
||||
int width = Fonts.SMALL.getWidth(text) + 10;
|
||||
g.setColor(color);
|
||||
g.fillRoundRect(x - width, y, width, singleHeight + 6, 2);
|
||||
Fonts.SMALL.drawString(x - width + 3, y + 3, text, Color.black);
|
||||
return x - width - 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ResolutionChangedEvent event) {
|
||||
singleHeight = Fonts.SMALL.getLineHeight();
|
||||
x = event.width - 3;
|
||||
y = event.height - 3 - singleHeight - 10;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state.transitions;
|
||||
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
|
||||
public class EmptyTransitionState extends TransitionState {
|
||||
|
||||
public EmptyTransitionState(DisplayContainer displayContainer) {
|
||||
super(displayContainer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
finish();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state.transitions;
|
||||
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
|
||||
public class FadeInTransitionState extends FadeTransitionState {
|
||||
|
||||
public FadeInTransitionState(DisplayContainer container) {
|
||||
super(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getMaskAlphaLevel(float fadeProgress) {
|
||||
return 1f - fadeProgress;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state.transitions;
|
||||
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
|
||||
public class FadeOutTransitionState extends FadeTransitionState {
|
||||
|
||||
public FadeOutTransitionState(DisplayContainer container) {
|
||||
super(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getMaskAlphaLevel(float fadeProgress) {
|
||||
return fadeProgress;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state.transitions;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
|
||||
public abstract class FadeTransitionState extends TransitionState {
|
||||
|
||||
private final Color black;
|
||||
|
||||
public FadeTransitionState(DisplayContainer displayContainer) {
|
||||
super(displayContainer);
|
||||
black = new Color(Color.black);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Graphics g) {
|
||||
applicableState.render(g);
|
||||
black.a = getMaskAlphaLevel((float) transitionTime / transitionTargetTime);
|
||||
g.setColor(black);
|
||||
g.fillRect(0, 0, displayContainer.width, displayContainer.height);
|
||||
}
|
||||
|
||||
protected abstract float getMaskAlphaLevel(float fadeProgress);
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state.transitions;
|
||||
|
||||
public interface TransitionFinishedListener {
|
||||
|
||||
void onFinish();
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.core.state.transitions;
|
||||
|
||||
import org.newdawn.slick.Graphics;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.state.BaseOpsuState;
|
||||
import yugecin.opsudance.core.state.OpsuState;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
public abstract class TransitionState extends BaseOpsuState {
|
||||
|
||||
protected OpsuState applicableState;
|
||||
|
||||
protected int transitionTargetTime;
|
||||
protected int transitionTime;
|
||||
|
||||
private TransitionFinishedListener listener;
|
||||
|
||||
public TransitionState(DisplayContainer displayContainer) {
|
||||
super(displayContainer);
|
||||
}
|
||||
|
||||
public final TransitionState set(OpsuState applicableState, int targetTime, TransitionFinishedListener listener) {
|
||||
this.applicableState = applicableState;
|
||||
this.transitionTargetTime = targetTime;
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public final OpsuState getApplicableState() {
|
||||
return applicableState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
applicableState.update();
|
||||
transitionTime += displayContainer.delta;
|
||||
if (transitionTime >= transitionTargetTime) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRenderUpdate() {
|
||||
applicableState.preRenderUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Graphics g) {
|
||||
applicableState.render(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
super.enter();
|
||||
transitionTime = 0;
|
||||
}
|
||||
|
||||
protected final void finish() {
|
||||
listener.onFinish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCloseRequest() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeErrorDump(StringWriter dump) {
|
||||
dump.append("> TransitionState dump\n");
|
||||
dump.append("progress: ").append(String.valueOf(transitionTime)).append("/").append(String.valueOf(transitionTargetTime)).append('\n');
|
||||
dump.append("applicable state: ");
|
||||
if (applicableState == null) {
|
||||
dump.append("IS NULL");
|
||||
return;
|
||||
}
|
||||
dump.append(applicableState.getClass().getSimpleName()).append('\n');
|
||||
applicableState.writeErrorDump(dump);
|
||||
}
|
||||
|
||||
}
|
28
src/yugecin/opsudance/events/BarNotificationEvent.java
Normal file
28
src/yugecin/opsudance/events/BarNotificationEvent.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.events;
|
||||
|
||||
public class BarNotificationEvent {
|
||||
|
||||
public final String message;
|
||||
|
||||
public BarNotificationEvent(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
38
src/yugecin/opsudance/events/BubbleNotificationEvent.java
Normal file
38
src/yugecin/opsudance/events/BubbleNotificationEvent.java
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.events;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
|
||||
public class BubbleNotificationEvent {
|
||||
|
||||
public static final Color COMMONCOLOR_GREEN = new Color(98, 131, 59);
|
||||
public static final Color COMMONCOLOR_WHITE = new Color(220, 220, 220);
|
||||
public static final Color COMMONCOLOR_PURPLE = new Color(94, 46, 149);
|
||||
public static final Color COMMONCOLOR_RED = new Color(141, 49, 16);
|
||||
public static final Color COLOR_ORANGE = new Color(138, 72, 51);
|
||||
|
||||
public final String message;
|
||||
public final Color borderColor;
|
||||
|
||||
public BubbleNotificationEvent(String message, Color borderColor) {
|
||||
this.message = message;
|
||||
this.borderColor = borderColor;
|
||||
}
|
||||
|
||||
}
|
30
src/yugecin/opsudance/events/ResolutionChangedEvent.java
Normal file
30
src/yugecin/opsudance/events/ResolutionChangedEvent.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.events;
|
||||
|
||||
public class ResolutionChangedEvent {
|
||||
|
||||
public final int width;
|
||||
public final int height;
|
||||
|
||||
public ResolutionChangedEvent(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
}
|
|
@ -25,6 +25,8 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
|||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.state.OverlayOpsuState;
|
||||
import yugecin.opsudance.sbv2.movers.CubicStoryboardMover;
|
||||
import yugecin.opsudance.sbv2.movers.LinearStoryboardMover;
|
||||
import yugecin.opsudance.sbv2.movers.QuadraticStoryboardMover;
|
||||
|
@ -34,20 +36,20 @@ import java.lang.reflect.InvocationHandler;
|
|||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
public class MoveStoryboard {
|
||||
public class MoveStoryboard extends OverlayOpsuState{
|
||||
|
||||
private final SimpleButton btnAddLinear;
|
||||
private final SimpleButton btnAddQuadratic;
|
||||
private final SimpleButton btnAddCubic;
|
||||
private final DisplayContainer displayContainer;
|
||||
|
||||
private final SimpleButton btnAnimLin;
|
||||
private final SimpleButton btnAnimMid;
|
||||
private final SimpleButton btnAnimCub;
|
||||
private SimpleButton btnAddLinear;
|
||||
private SimpleButton btnAddQuadratic;
|
||||
private SimpleButton btnAddCubic;
|
||||
|
||||
private SimpleButton btnAnimLin;
|
||||
private SimpleButton btnAnimMid;
|
||||
private SimpleButton btnAnimCub;
|
||||
|
||||
private final StoryboardMove dummyMove;
|
||||
|
||||
private int width;
|
||||
|
||||
private StoryboardMove[] moves;
|
||||
|
||||
private GameObject[] gameObjects;
|
||||
|
@ -55,14 +57,8 @@ public class MoveStoryboard {
|
|||
|
||||
private int trackPosition;
|
||||
|
||||
public MoveStoryboard(GameContainer container) {
|
||||
this.width = container.getWidth();
|
||||
btnAddLinear = new SimpleButton(width - 205, 50, 200, 25, Fonts.SMALL, "add linear", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
|
||||
btnAddQuadratic = new SimpleButton(width - 205, 80, 200, 25, Fonts.SMALL, "add quadratic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
|
||||
btnAddCubic = new SimpleButton(width - 205, 110, 200, 25, Fonts.SMALL, "add cubic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
|
||||
btnAnimLin = new SimpleButton(width - 250, 50, 40, 25, Fonts.SMALL, "lin", Color.blue, Color.white, Color.white, Color.orange);
|
||||
btnAnimMid = new SimpleButton(width - 250, 80, 40, 25, Fonts.SMALL, "mid", Color.blue, Color.white, Color.white, Color.orange);
|
||||
btnAnimCub = new SimpleButton(width - 250, 110, 40, 25, Fonts.SMALL, "cub", Color.blue, Color.white, Color.white, Color.orange);
|
||||
public MoveStoryboard(DisplayContainer displayContainer) {
|
||||
this.displayContainer = displayContainer;
|
||||
dummyMove = (StoryboardMove) Proxy.newProxyInstance(StoryboardMove.class.getClassLoader(), new Class<?>[]{StoryboardMove.class}, new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
|
@ -71,6 +67,18 @@ public class MoveStoryboard {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revalidate() {
|
||||
super.revalidate();
|
||||
|
||||
btnAddLinear = new SimpleButton(displayContainer.width - 205, 50, 200, 25, Fonts.SMALL, "add linear", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
|
||||
btnAddQuadratic = new SimpleButton(displayContainer.width - 205, 80, 200, 25, Fonts.SMALL, "add quadratic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
|
||||
btnAddCubic = new SimpleButton(displayContainer.width - 205, 110, 200, 25, Fonts.SMALL, "add cubic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
|
||||
btnAnimLin = new SimpleButton(displayContainer.width - 250, 50, 40, 25, Fonts.SMALL, "lin", Color.blue, Color.white, Color.white, Color.orange);
|
||||
btnAnimMid = new SimpleButton(displayContainer.width - 250, 80, 40, 25, Fonts.SMALL, "mid", Color.blue, Color.white, Color.white, Color.orange);
|
||||
btnAnimCub = new SimpleButton(displayContainer.width - 250, 110, 40, 25, Fonts.SMALL, "cub", Color.blue, Color.white, Color.white, Color.orange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the point at the current time
|
||||
* @param trackPosition current time in ms
|
||||
|
@ -88,7 +96,23 @@ public class MoveStoryboard {
|
|||
return moves[objectIndex].getPointAt(t);
|
||||
}
|
||||
|
||||
public void render(Graphics g) {
|
||||
@Override
|
||||
protected void onPreRenderUpdate() {
|
||||
int x = displayContainer.mouseX;
|
||||
int y = displayContainer.mouseY;
|
||||
btnAddLinear.update(x, y);
|
||||
btnAddQuadratic.update(x, y);
|
||||
btnAddCubic.update(x, y);
|
||||
btnAnimLin.update(x, y);
|
||||
btnAnimMid.update(x, y);
|
||||
btnAnimCub.update(x, y);
|
||||
if (moves[objectIndex] != null) {
|
||||
moves[objectIndex].update(displayContainer.renderDelta, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRender(Graphics g) {
|
||||
btnAddLinear.render(g);
|
||||
btnAddQuadratic.render(g);
|
||||
btnAddCubic.render(g);
|
||||
|
@ -100,13 +124,31 @@ public class MoveStoryboard {
|
|||
}
|
||||
}
|
||||
|
||||
public void mousePressed(int x, int y) {
|
||||
@Override
|
||||
protected boolean onKeyPressed(int key, char c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onKeyReleased(int key, char c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMouseWheelMoved(int delta) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMousePressed(int button, int x, int y) {
|
||||
if (moves[objectIndex] != null) {
|
||||
moves[objectIndex].mousePressed(x, y);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void mouseReleased(int x, int y) {
|
||||
@Override
|
||||
protected boolean onMouseReleased(int button, int x, int y) {
|
||||
if (moves[objectIndex] != null) {
|
||||
moves[objectIndex].mouseReleased(x, y);
|
||||
if (moves[objectIndex].getAmountOfMovers() == 0) {
|
||||
|
@ -114,7 +156,7 @@ public class MoveStoryboard {
|
|||
}
|
||||
}
|
||||
if (objectIndex == 0) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (btnAddLinear.isHovered()) {
|
||||
getCurrentMoveOrCreateNew().add(new LinearStoryboardMover());
|
||||
|
@ -134,6 +176,12 @@ public class MoveStoryboard {
|
|||
if (btnAnimCub.isHovered()) {
|
||||
getCurrentMoveOrDummy().setAnimationEquation(AnimationEquation.IN_OUT_EASE_MIDDLE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private StoryboardMove getCurrentMoveOrCreateNew() {
|
||||
|
@ -142,7 +190,7 @@ public class MoveStoryboard {
|
|||
return dummyMove;
|
||||
}
|
||||
if (moves[objectIndex] == null) {
|
||||
return moves[objectIndex] = new StoryboardMoveImpl(gameObjects[objectIndex - 1].end, gameObjects[objectIndex].start, width);
|
||||
return moves[objectIndex] = new StoryboardMoveImpl(gameObjects[objectIndex - 1].end, gameObjects[objectIndex].start, displayContainer.width);
|
||||
}
|
||||
return moves[objectIndex];
|
||||
}
|
||||
|
@ -154,18 +202,6 @@ public class MoveStoryboard {
|
|||
return moves[objectIndex];
|
||||
}
|
||||
|
||||
public void update(int delta, int x, int y) {
|
||||
btnAddLinear.update(x, y);
|
||||
btnAddQuadratic.update(x, y);
|
||||
btnAddCubic.update(x, y);
|
||||
btnAnimLin.update(x, y);
|
||||
btnAnimMid.update(x, y);
|
||||
btnAnimCub.update(x, y);
|
||||
if (moves[objectIndex] != null) {
|
||||
moves[objectIndex].update(delta, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
public void setGameObjects(GameObject[] gameObjects) {
|
||||
this.gameObjects = gameObjects;
|
||||
this.moves = new StoryboardMove[gameObjects.length];
|
||||
|
|
114
src/yugecin/opsudance/states/EmptyRedState.java
Normal file
114
src/yugecin/opsudance/states/EmptyRedState.java
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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.states;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.state.OpsuState;
|
||||
import yugecin.opsudance.events.BarNotificationEvent;
|
||||
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class EmptyRedState implements OpsuState {
|
||||
|
||||
private int counter;
|
||||
private long start;
|
||||
|
||||
private final DisplayContainer displayContainer;
|
||||
|
||||
public EmptyRedState(DisplayContainer displayContainer) {
|
||||
this.displayContainer = displayContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
counter -= displayContainer.delta;
|
||||
if (counter < 0) {
|
||||
counter = 10000; // to prevent more calls to switch, as this will keep rendering until state transitioned
|
||||
System.out.println(System.currentTimeMillis() - start);
|
||||
displayContainer.switchState(EmptyState.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRenderUpdate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Graphics g) {
|
||||
g.setColor(Color.red);
|
||||
g.fillRect(0, 0, 100, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
counter = 5000;
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCloseRequest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int key, char c) {
|
||||
displayContainer.eventBus.post(new BubbleNotificationEvent("this is a bubble notification... bubbly bubbly bubbly linewraaaaaaaaaap", BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyReleased(int key, char c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseWheelMoved(int delta) {
|
||||
displayContainer.eventBus.post(new BubbleNotificationEvent("Life is like a box of chocolates. It's all going to melt by the end of the day.\n-Emily", BubbleNotificationEvent.COMMONCOLOR_PURPLE));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased(int button, int x, int y) {
|
||||
displayContainer.eventBus.post(new BarNotificationEvent("this is a\nbar notification"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeErrorDump(StringWriter dump) {
|
||||
dump.append("> EmptyRedState dump\n");
|
||||
dump.append("its red\n");
|
||||
}
|
||||
|
||||
}
|
106
src/yugecin/opsudance/states/EmptyState.java
Normal file
106
src/yugecin/opsudance/states/EmptyState.java
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.states;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.state.OpsuState;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class EmptyState implements OpsuState {
|
||||
|
||||
private int counter;
|
||||
|
||||
private final DisplayContainer displayContainer;
|
||||
|
||||
public EmptyState(DisplayContainer displayContainer) {
|
||||
this.displayContainer = displayContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
counter -= displayContainer.delta;
|
||||
if (counter < 0) {
|
||||
counter = 10000; // to prevent more calls to switch, as this will keep rending until state transitioned
|
||||
displayContainer.switchState(EmptyRedState.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRenderUpdate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Graphics g) {
|
||||
g.setColor(Color.green);
|
||||
g.fillRect(0, 0, 100, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
counter = 2000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCloseRequest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int key, char c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyReleased(int key, char c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseWheelMoved(int delta) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased(int button, int x, int y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeErrorDump(StringWriter dump) {
|
||||
dump.append("> EmptyState dump\n");
|
||||
dump.append("its green\n");
|
||||
}
|
||||
|
||||
}
|
|
@ -29,16 +29,18 @@ import itdelatrisu.opsu.ui.Fonts;
|
|||
import itdelatrisu.opsu.ui.MenuButton;
|
||||
import itdelatrisu.opsu.ui.UI;
|
||||
import org.newdawn.slick.*;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.state.OverlayOpsuState;
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public class OptionsOverlay {
|
||||
public class OptionsOverlay extends OverlayOpsuState {
|
||||
|
||||
private Parent parent;
|
||||
private GameContainer container;
|
||||
private final DisplayContainer displayContainer;
|
||||
|
||||
private final Image sliderBallImg;
|
||||
private final Image checkOnImg;
|
||||
private final Image checkOffImg;
|
||||
private Listener listener;
|
||||
|
||||
private Image sliderBallImg;
|
||||
private Image checkOnImg;
|
||||
private Image checkOffImg;
|
||||
|
||||
private OptionTab[] tabs;
|
||||
private int selectedTab;
|
||||
|
@ -79,21 +81,29 @@ public class OptionsOverlay {
|
|||
|
||||
private int sliderSoundDelay;
|
||||
|
||||
public OptionsOverlay(Parent parent, OptionTab[] tabs, int defaultSelectedTabIndex, GameContainer container) {
|
||||
this.parent = parent;
|
||||
this.container = container;
|
||||
public OptionsOverlay(DisplayContainer displayContainer, OptionTab[] tabs, int defaultSelectedTabIndex) {
|
||||
this.displayContainer = displayContainer;
|
||||
|
||||
this.tabs = tabs;
|
||||
selectedTab = defaultSelectedTabIndex;
|
||||
|
||||
listHoverIndex = -1;
|
||||
}
|
||||
|
||||
public void setListener(Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revalidate() {
|
||||
super.revalidate();
|
||||
|
||||
sliderBallImg = GameImage.CONTROL_SLIDER_BALL.getImage().getScaledCopy(20, 20);
|
||||
checkOnImg = GameImage.CONTROL_CHECK_ON.getImage().getScaledCopy(20, 20);
|
||||
checkOffImg = GameImage.CONTROL_CHECK_OFF.getImage().getScaledCopy(20, 20);
|
||||
|
||||
width = container.getWidth();
|
||||
height = container.getHeight();
|
||||
width = displayContainer.width;
|
||||
height = displayContainer.height;
|
||||
|
||||
// calculate positions
|
||||
optionWidth = width / 2;
|
||||
|
@ -109,10 +119,12 @@ public class OptionsOverlay {
|
|||
maxScrollOffset = Fonts.MEDIUM.getLineHeight() * 2 * tabs.length;
|
||||
scrollOffset = 0;
|
||||
for (OptionTab tab : tabs) {
|
||||
/*
|
||||
if (defaultSelectedTabIndex-- > 0) {
|
||||
scrollOffset += Fonts.MEDIUM.getLineHeight() * 2;
|
||||
scrollOffset += tab.options.length * optionHeight;
|
||||
}
|
||||
*/
|
||||
maxScrollOffset += tab.options.length * optionHeight;
|
||||
tab.button = new MenuButton(tabImage, tabX, tabY);
|
||||
tabX += tabOffset;
|
||||
|
@ -127,7 +139,8 @@ public class OptionsOverlay {
|
|||
optionStartY = (int) (tabY + tabImage.getHeight() / 2 + 2); // +2 for the separator line
|
||||
}
|
||||
|
||||
public void render(Graphics g, int mouseX, int mouseY) {
|
||||
@Override
|
||||
public void onRender(Graphics g) {
|
||||
// bg
|
||||
g.setColor(Colors.BLACK_ALPHA_75);
|
||||
g.fillRect(0, 0, width, height);
|
||||
|
@ -136,7 +149,7 @@ public class OptionsOverlay {
|
|||
renderTitle();
|
||||
|
||||
// option tabs
|
||||
renderTabs(mouseX, mouseY);
|
||||
renderTabs();
|
||||
|
||||
// line separator
|
||||
g.setColor(Color.white);
|
||||
|
@ -159,7 +172,7 @@ public class OptionsOverlay {
|
|||
UI.getBackButton().draw();
|
||||
|
||||
// tooltip
|
||||
renderTooltip(g, mouseX, mouseY);
|
||||
renderTooltip(g);
|
||||
|
||||
// key input options
|
||||
if (keyEntryLeft || keyEntryRight) {
|
||||
|
@ -175,15 +188,10 @@ public class OptionsOverlay {
|
|||
Fonts.LARGE.drawString((width - Fonts.LARGE.getWidth(prompt)) / 2, (height - Fonts.LARGE.getLineHeight()) / 2, prompt);
|
||||
}
|
||||
|
||||
private void renderTooltip(Graphics g, int mouseX, int mouseY) {
|
||||
private void renderTooltip(Graphics g) {
|
||||
if (hoverOption != null) {
|
||||
String optionDescription = hoverOption.getDescription();
|
||||
float textWidth = Fonts.SMALL.getWidth(optionDescription);
|
||||
Color.black.a = 0.7f;
|
||||
g.setColor(Color.black);
|
||||
g.fillRoundRect(mouseX + 10, mouseY + 10, 10 + textWidth, 10 + Fonts.SMALL.getLineHeight(), 4);
|
||||
Fonts.SMALL.drawString(mouseX + 15, mouseY + 15, optionDescription, Color.white);
|
||||
Color.black.a = 1f;
|
||||
UI.updateTooltip(displayContainer.renderDelta, hoverOption.getDescription(), false);
|
||||
UI.drawTooltip(g);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,10 +330,10 @@ public class OptionsOverlay {
|
|||
Fonts.MEDIUM.drawString(optionStartX + optionWidth - valueLen, y, value, Colors.BLUE_BACKGROUND);
|
||||
}
|
||||
|
||||
public void renderTabs(int mouseX, int mouseY) {
|
||||
public void renderTabs() {
|
||||
for (int i = 0; i < tabs.length; i++) {
|
||||
OptionTab tab = tabs[i];
|
||||
boolean hovering = tab.button.contains(mouseX, mouseY);
|
||||
boolean hovering = tab.button.contains(displayContainer.mouseX, displayContainer.mouseY);
|
||||
UI.drawTab(tab.button.getX(), tab.button.getY(), tab.name, i == selectedTab, hovering);
|
||||
}
|
||||
}
|
||||
|
@ -339,7 +347,25 @@ public class OptionsOverlay {
|
|||
Fonts.DEFAULT.drawString(marginX, marginY, "Change the way opsu! behaves", Color.white);
|
||||
}
|
||||
|
||||
public void update(int delta, int mouseX, int mouseY) {
|
||||
@Override
|
||||
public void hide() {
|
||||
acceptInput = false;
|
||||
SoundController.playSound(SoundEffect.MENUBACK);
|
||||
active = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
acceptInput = true;
|
||||
active = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreRenderUpdate() {
|
||||
int mouseX = displayContainer.mouseX;
|
||||
int mouseY = displayContainer.mouseY;
|
||||
int delta = displayContainer.renderDelta;
|
||||
|
||||
if (sliderSoundDelay > 0) {
|
||||
sliderSoundDelay -= delta;
|
||||
}
|
||||
|
@ -352,7 +378,7 @@ public class OptionsOverlay {
|
|||
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
|
||||
if (isAdjustingSlider) {
|
||||
int sliderValue = hoverOption.getIntegerValue();
|
||||
updateSliderOption(mouseX, mouseY);
|
||||
updateSliderOption();
|
||||
if (hoverOption.getIntegerValue() - sliderValue != 0 && sliderSoundDelay <= 0) {
|
||||
sliderSoundDelay = 90;
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
|
@ -366,22 +392,27 @@ public class OptionsOverlay {
|
|||
}
|
||||
}
|
||||
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
@Override
|
||||
public boolean onMousePressed(int button, int x, int y) {
|
||||
if (keyEntryLeft || keyEntryRight) {
|
||||
keyEntryLeft = keyEntryRight = false;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isListOptionOpen) {
|
||||
if (y > optionStartY && listStartX <= x && x < listStartX + listWidth && listStartY <= y && y < listStartY + listHeight) {
|
||||
if (0 <= listHoverIndex && listHoverIndex < hoverOption.getListItems().length) {
|
||||
hoverOption.clickListItem(listHoverIndex);
|
||||
parent.onSaveOption(hoverOption);
|
||||
if (listener != null) {
|
||||
listener.onSaveOption(hoverOption);
|
||||
}
|
||||
}
|
||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||
}
|
||||
isListOptionOpen = false;
|
||||
listHoverIndex = -1;
|
||||
updateHoverOption(x, y);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
mousePressY = y;
|
||||
|
@ -393,35 +424,39 @@ public class OptionsOverlay {
|
|||
} else if (hoverOption.getType() == OptionType.NUMERIC) {
|
||||
isAdjustingSlider = sliderOptionStartX <= x && x < sliderOptionStartX + sliderOptionLength;
|
||||
if (isAdjustingSlider) {
|
||||
updateSliderOption(x, y);
|
||||
updateSliderOption();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (UI.getBackButton().contains(x, y)) {
|
||||
parent.onLeave();
|
||||
hide();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
@Override
|
||||
public boolean onMouseReleased(int button, int x, int y) {
|
||||
selectedOption = null;
|
||||
if (isAdjustingSlider) {
|
||||
parent.onSaveOption(hoverOption);
|
||||
if (isAdjustingSlider && listener != null) {
|
||||
listener.onSaveOption(hoverOption);
|
||||
}
|
||||
isAdjustingSlider = false;
|
||||
sliderOptionLength = 0;
|
||||
|
||||
// check if clicked, not dragged
|
||||
if (Math.abs(y - mousePressY) >= 5) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hoverOption != null) {
|
||||
if (hoverOption.getType() == OptionType.BOOLEAN) {
|
||||
hoverOption.click(container);
|
||||
parent.onSaveOption(hoverOption);
|
||||
hoverOption.click();
|
||||
if (listener != null) {
|
||||
listener.onSaveOption(hoverOption);
|
||||
}
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
return;
|
||||
return true;
|
||||
} else if (hoverOption == GameOption.KEY_LEFT) {
|
||||
keyEntryLeft = true;
|
||||
} else if (hoverOption == GameOption.KEY_RIGHT) {
|
||||
|
@ -435,27 +470,38 @@ public class OptionsOverlay {
|
|||
if (tab.button.contains(x, y)) {
|
||||
scrollOffset = tScrollOffset;
|
||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
tScrollOffset += Fonts.MEDIUM.getLineHeight() * 2;
|
||||
tScrollOffset += tab.options.length * optionHeight;
|
||||
}
|
||||
|
||||
if (UI.getBackButton().contains(x, y) && listener != null) {
|
||||
listener.onLeaveOptionsMenu();
|
||||
}
|
||||
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
if (!isAdjustingSlider) {
|
||||
scrollOffset = Utils.clamp(scrollOffset + oldy - newy, 0, maxScrollOffset);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void mouseWheelMoved(int delta) {
|
||||
@Override
|
||||
public boolean onMouseWheelMoved(int delta) {
|
||||
if (!isAdjustingSlider) {
|
||||
scrollOffset = Utils.clamp(scrollOffset - delta, 0, maxScrollOffset);
|
||||
}
|
||||
updateHoverOption(prevMouseX, prevMouseY);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean keyPressed(int key, char c) {
|
||||
@Override
|
||||
public boolean onKeyPressed(int key, char c) {
|
||||
if (keyEntryRight) {
|
||||
Options.setGameKeyRight(key);
|
||||
keyEntryRight = false;
|
||||
|
@ -468,23 +514,31 @@ public class OptionsOverlay {
|
|||
return true;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case Input.KEY_ESCAPE:
|
||||
if (key == Input.KEY_ESCAPE) {
|
||||
if (isListOptionOpen) {
|
||||
isListOptionOpen = false;
|
||||
listHoverIndex = -1;
|
||||
return true;
|
||||
}
|
||||
parent.onLeave();
|
||||
hide();
|
||||
if (listener != null) {
|
||||
listener.onLeaveOptionsMenu();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateSliderOption(int mouseX, int mouseY) {
|
||||
@Override
|
||||
public boolean onKeyReleased(int key, char c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateSliderOption() {
|
||||
int min = hoverOption.getMinValue();
|
||||
int max = hoverOption.getMaxValue();
|
||||
int value = min + Math.round((float) (max - min) * (mouseX - sliderOptionStartX) / (sliderOptionLength));
|
||||
int value = min + Math.round((float) (max - min) * (displayContainer.mouseX - sliderOptionStartX) / (sliderOptionLength));
|
||||
hoverOption.setValue(Utils.clamp(value, min, max));
|
||||
}
|
||||
|
||||
|
@ -533,10 +587,9 @@ public class OptionsOverlay {
|
|||
|
||||
}
|
||||
|
||||
public interface Parent {
|
||||
|
||||
void onLeave();
|
||||
public interface Listener {
|
||||
|
||||
void onLeaveOptionsMenu();
|
||||
void onSaveOption(GameOption option);
|
||||
|
||||
}
|
||||
|
|
|
@ -22,86 +22,27 @@ import itdelatrisu.opsu.Options.GameOption;
|
|||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.objects.GameObject;
|
||||
import itdelatrisu.opsu.states.Game;
|
||||
import itdelatrisu.opsu.states.OptionsMenu;
|
||||
import itdelatrisu.opsu.ui.Fonts;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
import yugecin.opsudance.ObjectColorOverrides;
|
||||
import yugecin.opsudance.core.DisplayContainer;
|
||||
import yugecin.opsudance.core.state.OverlayOpsuState;
|
||||
import yugecin.opsudance.sbv2.MoveStoryboard;
|
||||
import yugecin.opsudance.ui.OptionsOverlay.OptionTab;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class SBOverlay implements OptionsOverlay.Parent {
|
||||
|
||||
private static final OptionTab[] options = new OptionsOverlay.OptionTab[]{
|
||||
new OptionTab("Gameplay", new GameOption[] {
|
||||
GameOption.BACKGROUND_DIM,
|
||||
GameOption.DANCE_REMOVE_BG,
|
||||
GameOption.SNAKING_SLIDERS,
|
||||
GameOption.SHRINKING_SLIDERS,
|
||||
GameOption.SHOW_HIT_LIGHTING,
|
||||
GameOption.SHOW_HIT_ANIMATIONS,
|
||||
GameOption.SHOW_COMBO_BURSTS,
|
||||
GameOption.SHOW_PERFECT_HIT,
|
||||
GameOption.SHOW_FOLLOW_POINTS,
|
||||
}),
|
||||
new OptionTab("Input", new GameOption[] {
|
||||
GameOption.CURSOR_SIZE,
|
||||
GameOption.NEW_CURSOR,
|
||||
GameOption.DISABLE_CURSOR
|
||||
}),
|
||||
new OptionTab("Dance", new GameOption[] {
|
||||
GameOption.DANCE_MOVER,
|
||||
GameOption.DANCE_EXGON_DELAY,
|
||||
GameOption.DANCE_QUAD_BEZ_AGGRESSIVENESS,
|
||||
GameOption.DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR,
|
||||
GameOption.DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS,
|
||||
GameOption.DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR,
|
||||
GameOption.DANCE_MOVER_DIRECTION,
|
||||
GameOption.DANCE_SLIDER_MOVER_TYPE,
|
||||
GameOption.DANCE_SPINNER,
|
||||
GameOption.DANCE_SPINNER_DELAY,
|
||||
GameOption.DANCE_LAZY_SLIDERS,
|
||||
GameOption.DANCE_CIRCLE_STREAMS,
|
||||
GameOption.DANCE_ONLY_CIRCLE_STACKS,
|
||||
GameOption.DANCE_CIRLCE_IN_SLOW_SLIDERS,
|
||||
GameOption.DANCE_CIRLCE_IN_LAZY_SLIDERS,
|
||||
GameOption.DANCE_MIRROR,
|
||||
}),
|
||||
new OptionTab("Dance display", new GameOption[] {
|
||||
GameOption.DANCE_DRAW_APPROACH,
|
||||
GameOption.DANCE_OBJECT_COLOR_OVERRIDE,
|
||||
GameOption.DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED,
|
||||
GameOption.DANCE_RGB_OBJECT_INC,
|
||||
GameOption.DANCE_CURSOR_COLOR_OVERRIDE,
|
||||
GameOption.DANCE_CURSOR_MIRROR_COLOR_OVERRIDE,
|
||||
GameOption.DANCE_CURSOR_ONLY_COLOR_TRAIL,
|
||||
GameOption.DANCE_RGB_CURSOR_INC,
|
||||
GameOption.DANCE_CURSOR_TRAIL_OVERRIDE,
|
||||
GameOption.DANCE_HIDE_OBJECTS,
|
||||
GameOption.DANCE_HIDE_UI,
|
||||
GameOption.DANCE_HIDE_WATERMARK,
|
||||
}),
|
||||
new OptionTab ("Pippi", new GameOption[] {
|
||||
GameOption.PIPPI_ENABLE,
|
||||
GameOption.PIPPI_RADIUS_PERCENT,
|
||||
GameOption.PIPPI_ANGLE_INC_MUL,
|
||||
GameOption.PIPPI_ANGLE_INC_MUL_SLIDER,
|
||||
GameOption.PIPPI_SLIDER_FOLLOW_EXPAND,
|
||||
GameOption.PIPPI_PREVENT_WOBBLY_STREAMS,
|
||||
})
|
||||
};
|
||||
public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverlay.Listener {
|
||||
|
||||
private final static List<GameOption> optionList = new ArrayList<>();
|
||||
|
||||
private boolean hide;
|
||||
private boolean menu;
|
||||
private final DisplayContainer displayContainer;
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
private boolean hide;
|
||||
|
||||
private int speed;
|
||||
private GameObject[] gameObjects;
|
||||
|
@ -112,43 +53,42 @@ public class SBOverlay implements OptionsOverlay.Parent {
|
|||
|
||||
private final Game game;
|
||||
private final MoveStoryboard msb;
|
||||
private final OptionsOverlay overlay;
|
||||
private final OptionsOverlay optionsOverlay;
|
||||
|
||||
static {
|
||||
for (OptionTab tab : options) {
|
||||
for (OptionTab tab : OptionsMenu.storyboardOptions) {
|
||||
optionList.addAll(Arrays.asList(tab.options));
|
||||
}
|
||||
}
|
||||
|
||||
public SBOverlay(Game game, MoveStoryboard msb, GameContainer container) {
|
||||
this.game = game;
|
||||
public StoryboardOverlay(DisplayContainer displayContainer, MoveStoryboard msb, OptionsOverlay optionsOverlay, Game game) {
|
||||
this.displayContainer = displayContainer;
|
||||
this.msb = msb;
|
||||
this.optionsOverlay = optionsOverlay;
|
||||
this.game = game;
|
||||
initialOptions = new HashMap<>();
|
||||
overlay = new OptionsOverlay(this, options, 2, container);
|
||||
this.width = container.getWidth();
|
||||
this.height = container.getHeight();
|
||||
speed = 10;
|
||||
gameObjects = new GameObject[0];
|
||||
}
|
||||
|
||||
public void render(GameContainer container, Graphics g) {
|
||||
@Override
|
||||
public void onRender(Graphics g) {
|
||||
if (!Options.isEnableSB() || hide) {
|
||||
return;
|
||||
}
|
||||
msb.render(g);
|
||||
int lh = Fonts.SMALL.getLineHeight();
|
||||
Fonts.SMALL.drawString(10, height - 50 + lh, "save position: ctrl+s, load position: ctrl+l", Color.cyan);
|
||||
Fonts.SMALL.drawString(10, height - 50, "speed: C " + (speed / 10f) + " V", Color.cyan);
|
||||
Fonts.SMALL.drawString(10, height - 50 - lh, "Menu: N", Color.cyan);
|
||||
Fonts.SMALL.drawString(10, height - 50 - lh * 2, "HIDE: H", Color.cyan);
|
||||
Fonts.SMALL.drawString(10, height - 50 - lh * 3, "obj: J " + index + " K", Color.cyan);
|
||||
Fonts.SMALL.drawString(10, displayContainer.height - 50 + lh, "save position: ctrl+s, load position: ctrl+l", Color.cyan);
|
||||
Fonts.SMALL.drawString(10, displayContainer.height - 50, "speed: C " + (speed / 10f) + " V", Color.cyan);
|
||||
Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh, "Menu: N", Color.cyan);
|
||||
Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh * 2, "HIDE: H", Color.cyan);
|
||||
Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh * 3, "obj: J " + index + " K", Color.cyan);
|
||||
g.setColor(Color.red);
|
||||
if (index < optionsMap.length && optionsMap[index] != null) {
|
||||
int i = 0;
|
||||
for (Object o : optionsMap[index].entrySet()) {
|
||||
Map.Entry<Options.GameOption, String> option = (Map.Entry<Options.GameOption, String>) o;
|
||||
Fonts.SMALL.drawString(10, 50 + i * lh, option.getKey().getName(), Color.cyan);
|
||||
Fonts.SMALL.drawString(width / 5, 50 + i * lh, option.getKey().getValueString(), Color.cyan);
|
||||
Fonts.SMALL.drawString(displayContainer.width / 5, 50 + i * lh, option.getKey().getValueString(), Color.cyan);
|
||||
g.fillRect(0, 50 + i * lh + lh / 4, 10, 10);
|
||||
i++;
|
||||
}
|
||||
|
@ -157,27 +97,16 @@ public class SBOverlay implements OptionsOverlay.Parent {
|
|||
int start = gameObjects[0].getTime();
|
||||
int end = gameObjects[gameObjects.length - 1].getEndTime();
|
||||
float curtime = (float) (MusicController.getPosition() - start) / (end - start);
|
||||
g.fillRect(curtime * width, height - 10f, 10f, 10f);
|
||||
}
|
||||
if (menu) {
|
||||
overlay.render(g, container.getInput().getMouseX(), container.getInput().getMouseY());
|
||||
g.fillRect(curtime * displayContainer.width, displayContainer.height - 10f, 10f, 10f);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(int delta, int mouseX, int mouseY) {
|
||||
if (Options.isEnableSB() && menu) {
|
||||
overlay.update(delta, mouseX, mouseY);
|
||||
}
|
||||
msb.update(delta, mouseX, mouseY);
|
||||
@Override
|
||||
public void onPreRenderUpdate() {
|
||||
}
|
||||
|
||||
public boolean keyPressed(int key, char c) {
|
||||
if (!Options.isEnableSB()) {
|
||||
return false;
|
||||
}
|
||||
if (menu && overlay.keyPressed(key, c)) {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean onKeyPressed(int key, char c) {
|
||||
if (key == Input.KEY_C) {
|
||||
if (speed > 0) {
|
||||
speed -= 1;
|
||||
|
@ -196,11 +125,9 @@ public class SBOverlay implements OptionsOverlay.Parent {
|
|||
} else if (key == Input.KEY_H) {
|
||||
hide = !hide;
|
||||
} else if (key == Input.KEY_N) {
|
||||
menu = !menu;
|
||||
if (menu && speed != 0) {
|
||||
optionsOverlay.show();
|
||||
if (speed != 0) {
|
||||
MusicController.pause();
|
||||
} else if (!menu && speed != 0) {
|
||||
MusicController.resume();
|
||||
}
|
||||
} else if (key == Input.KEY_J && index > 0) {
|
||||
index--;
|
||||
|
@ -214,6 +141,11 @@ public class SBOverlay implements OptionsOverlay.Parent {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onKeyReleased(int key, char c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void goBackOneSBIndex() {
|
||||
if (index + 1 < optionsMap.length) {
|
||||
// new options on previous index, so to revert then we have to reload them all to this point..
|
||||
|
@ -252,20 +184,13 @@ public class SBOverlay implements OptionsOverlay.Parent {
|
|||
this.gameObjects = gameObjects;
|
||||
}
|
||||
|
||||
public boolean mousePressed(int button, int x, int y) {
|
||||
msb.mousePressed(x, y);
|
||||
if (!menu) {
|
||||
return false;
|
||||
}
|
||||
overlay.mousePressed(button, x, y);
|
||||
@Override
|
||||
public boolean onMousePressed(int button, int x, int y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
if (!menu) {
|
||||
return false;
|
||||
}
|
||||
overlay.mouseDragged(oldx, oldy, newx, newy);
|
||||
@Override
|
||||
public boolean onMouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -293,12 +218,8 @@ public class SBOverlay implements OptionsOverlay.Parent {
|
|||
this.index--;
|
||||
}
|
||||
|
||||
public boolean mouseReleased(int button, int x, int y) {
|
||||
if (menu) {
|
||||
overlay.mouseReleased(button, x, y);
|
||||
return true;
|
||||
}
|
||||
msb.mouseReleased(x, y);
|
||||
@Override
|
||||
public boolean onMouseReleased(int button, int x, int y) {
|
||||
if (x > 10 || index >= optionsMap.length || optionsMap[index] == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -318,15 +239,12 @@ public class SBOverlay implements OptionsOverlay.Parent {
|
|||
return true;
|
||||
}
|
||||
|
||||
public boolean mouseWheelMoved(int delta) {
|
||||
if (!menu) {
|
||||
return false;
|
||||
}
|
||||
overlay.mouseWheelMoved(delta);
|
||||
@Override
|
||||
public boolean onMouseWheelMoved(int delta) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void enter() {
|
||||
public void onEnter() {
|
||||
// enter, save current settings
|
||||
for (Options.GameOption o : optionList) {
|
||||
initialOptions.put(o, o.write());
|
||||
|
@ -334,7 +252,7 @@ public class SBOverlay implements OptionsOverlay.Parent {
|
|||
speed = 10;
|
||||
}
|
||||
|
||||
public void leave() {
|
||||
public void onLeave() {
|
||||
// leave, revert the settings saved before entering
|
||||
for (Options.GameOption o : optionList) {
|
||||
if (initialOptions.containsKey(o)) {
|
||||
|
@ -358,13 +276,8 @@ public class SBOverlay implements OptionsOverlay.Parent {
|
|||
}
|
||||
}
|
||||
|
||||
public float[] getPoint(int trackPosition) {
|
||||
return msb.getPoint(trackPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLeave() {
|
||||
menu = false;
|
||||
public void onLeaveOptionsMenu() {
|
||||
if (speed != 0) {
|
||||
MusicController.resume();
|
||||
}
|
41
src/yugecin/opsudance/utils/CachedVariable.java
Normal file
41
src/yugecin/opsudance/utils/CachedVariable.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.utils;
|
||||
|
||||
public class CachedVariable<T> {
|
||||
|
||||
private T value;
|
||||
private Getter<T> getter;
|
||||
|
||||
public CachedVariable(Getter<T> getter) {
|
||||
this.getter = getter;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
if (getter != null) {
|
||||
value = getter.get();
|
||||
getter = null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public interface Getter<T> {
|
||||
T get();
|
||||
}
|
||||
|
||||
}
|
111
src/yugecin/opsudance/utils/GLHelper.java
Normal file
111
src/yugecin/opsudance/utils/GLHelper.java
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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.utils;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.input.Cursor;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.DisplayMode;
|
||||
import org.newdawn.slick.opengl.ImageIOImageData;
|
||||
import org.newdawn.slick.opengl.LoadableImageData;
|
||||
import org.newdawn.slick.opengl.TGAImageData;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
import yugecin.opsudance.core.errorhandling.ErrorHandler;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public class GLHelper {
|
||||
|
||||
/**
|
||||
* from org.newdawn.slick.AppGameContainer#setDisplayMode
|
||||
*/
|
||||
public static DisplayMode findFullscreenDisplayMode(int targetBPP, int targetFrequency, int width, int height) throws LWJGLException {
|
||||
DisplayMode[] modes = Display.getAvailableDisplayModes();
|
||||
DisplayMode foundMode = null;
|
||||
int freq = 0;
|
||||
int bpp = 0;
|
||||
|
||||
for (DisplayMode current : modes) {
|
||||
if (current.getWidth() != width || current.getHeight() != height) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current.getBitsPerPixel() == targetBPP && current.getFrequency() == targetFrequency) {
|
||||
return current;
|
||||
}
|
||||
|
||||
if (current.getFrequency() >= freq && (foundMode == null || current.getBitsPerPixel() >= bpp)) {
|
||||
foundMode = current;
|
||||
freq = foundMode.getFrequency();
|
||||
bpp = foundMode.getBitsPerPixel();
|
||||
}
|
||||
}
|
||||
return foundMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* from org.newdawn.slick.AppGameContainer#setDisplayMode
|
||||
*/
|
||||
public static void setIcons(String[] refs) {
|
||||
ByteBuffer[] bufs = new ByteBuffer[refs.length];
|
||||
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
LoadableImageData data;
|
||||
boolean flip = true;
|
||||
|
||||
if (refs[i].endsWith(".tga")) {
|
||||
data = new TGAImageData();
|
||||
} else {
|
||||
flip = false;
|
||||
data = new ImageIOImageData();
|
||||
}
|
||||
|
||||
try {
|
||||
bufs[i] = data.loadImage(ResourceLoader.getResourceAsStream(refs[i]), flip, false, null);
|
||||
} catch (Exception e) {
|
||||
Log.error("failed to set the icon", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Display.setIcon(bufs);
|
||||
}
|
||||
|
||||
public static void hideNativeCursor() {
|
||||
try {
|
||||
int min = Cursor.getMinCursorSize();
|
||||
IntBuffer tmp = BufferUtils.createIntBuffer(min * min);
|
||||
Mouse.setNativeCursor(new Cursor(min, min, min / 2, min / 2, 1, tmp, null));
|
||||
} catch (LWJGLException e) {
|
||||
ErrorHandler.error("Cannot hide native cursor", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
public static void showNativeCursor() {
|
||||
try {
|
||||
Mouse.setNativeCursor(null);
|
||||
} catch (LWJGLException e) {
|
||||
ErrorHandler.error("Cannot show native cursor", e).show();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
42
src/yugecin/opsudance/utils/MiscUtils.java
Normal file
42
src/yugecin/opsudance/utils/MiscUtils.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.utils;
|
||||
|
||||
import itdelatrisu.opsu.Options;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MiscUtils {
|
||||
|
||||
public static final CachedVariable<Properties> buildProperties = new CachedVariable<>(new CachedVariable.Getter<Properties>() {
|
||||
@Override
|
||||
public Properties get() {
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
props.load(ResourceLoader.getResourceAsStream(Options.VERSION_FILE));
|
||||
} catch (IOException e) {
|
||||
Log.error("Could not read version file", e);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
44
src/yugecin/opsudance/utils/SlickUtil.java
Normal file
44
src/yugecin/opsudance/utils/SlickUtil.java
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* opsu!dance - fork of opsu! with cursordance auto
|
||||
* Copyright (C) 2017 yugecin
|
||||
*
|
||||
* opsu!dance is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu!dance is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package yugecin.opsudance.utils;
|
||||
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.SlickException;
|
||||
|
||||
public class SlickUtil {
|
||||
|
||||
public static void destroyImages(Image[] imgs) {
|
||||
if (imgs == null) {
|
||||
return;
|
||||
}
|
||||
for (Image i : imgs) {
|
||||
destroyImage(i);
|
||||
}
|
||||
}
|
||||
|
||||
public static void destroyImage(Image image) {
|
||||
if (image == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
image.destroy();
|
||||
} catch (SlickException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user