2014-06-30 04:17:04 +02:00
|
|
|
/*
|
|
|
|
* opsu! - an open-source osu! client
|
2015-01-16 18:05:44 +01:00
|
|
|
* Copyright (C) 2014, 2015 Jeffrey Han
|
2014-06-30 04:17:04 +02:00
|
|
|
*
|
|
|
|
* 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.states;
|
|
|
|
|
2015-01-16 03:55:26 +01:00
|
|
|
import itdelatrisu.opsu.ErrorHandler;
|
2015-01-30 02:36:23 +01:00
|
|
|
import itdelatrisu.opsu.GameData;
|
2014-07-04 22:41:52 +02:00
|
|
|
import itdelatrisu.opsu.GameImage;
|
2014-07-16 22:01:36 +02:00
|
|
|
import itdelatrisu.opsu.GameMod;
|
2015-01-08 01:29:51 +01:00
|
|
|
import itdelatrisu.opsu.MenuButton;
|
2014-06-30 04:17:04 +02:00
|
|
|
import itdelatrisu.opsu.Opsu;
|
2015-01-21 05:56:10 +01:00
|
|
|
import itdelatrisu.opsu.Options;
|
2014-06-30 04:17:04 +02:00
|
|
|
import itdelatrisu.opsu.OsuFile;
|
|
|
|
import itdelatrisu.opsu.OsuHitObject;
|
|
|
|
import itdelatrisu.opsu.OsuTimingPoint;
|
2015-01-30 02:36:23 +01:00
|
|
|
import itdelatrisu.opsu.ScoreData;
|
2015-03-05 19:27:45 +01:00
|
|
|
import itdelatrisu.opsu.UI;
|
2014-07-02 01:32:03 +02:00
|
|
|
import itdelatrisu.opsu.Utils;
|
2015-01-08 01:29:51 +01:00
|
|
|
import itdelatrisu.opsu.audio.HitSound;
|
|
|
|
import itdelatrisu.opsu.audio.MusicController;
|
|
|
|
import itdelatrisu.opsu.audio.SoundController;
|
|
|
|
import itdelatrisu.opsu.audio.SoundEffect;
|
2015-03-05 03:03:06 +01:00
|
|
|
import itdelatrisu.opsu.db.ScoreDB;
|
2014-06-30 04:17:04 +02:00
|
|
|
import itdelatrisu.opsu.objects.Circle;
|
2015-01-16 09:47:37 +01:00
|
|
|
import itdelatrisu.opsu.objects.HitObject;
|
2014-06-30 04:17:04 +02:00
|
|
|
import itdelatrisu.opsu.objects.Slider;
|
|
|
|
import itdelatrisu.opsu.objects.Spinner;
|
|
|
|
|
2014-07-04 22:41:52 +02:00
|
|
|
import java.io.File;
|
2014-06-30 04:17:04 +02:00
|
|
|
import java.util.Stack;
|
2014-12-20 21:30:20 +01:00
|
|
|
import java.util.concurrent.TimeUnit;
|
2014-06-30 04:17:04 +02:00
|
|
|
|
|
|
|
import org.lwjgl.input.Keyboard;
|
2015-02-18 04:03:11 +01:00
|
|
|
import org.newdawn.slick.Animation;
|
2014-06-30 04:17:04 +02:00
|
|
|
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 org.newdawn.slick.state.transition.FadeOutTransition;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* "Game" state.
|
|
|
|
*/
|
|
|
|
public class Game extends BasicGameState {
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Game restart states. */
|
2015-01-15 06:56:30 +01:00
|
|
|
public enum Restart {
|
2015-02-13 09:45:38 +01:00
|
|
|
/** No restart. */
|
|
|
|
FALSE,
|
|
|
|
/** First time loading the song. */
|
|
|
|
NEW,
|
|
|
|
/** Manual retry. */
|
|
|
|
MANUAL,
|
|
|
|
/** Health is zero: no-continue/force restart. */
|
|
|
|
LOSE;
|
2015-01-15 06:56:30 +01:00
|
|
|
}
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Minimum time before start of song, in milliseconds, to process skip-related actions. */
|
2015-01-15 05:57:50 +01:00
|
|
|
private static final int SKIP_OFFSET = 2000;
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** The associated OsuFile object. */
|
2014-07-09 19:36:42 +02:00
|
|
|
private OsuFile osu;
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2015-01-27 09:19:39 +01:00
|
|
|
/** The associated GameData object. */
|
|
|
|
private GameData data;
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Current hit object index in OsuHitObject[] array. */
|
2014-06-30 04:17:04 +02:00
|
|
|
private int objectIndex = 0;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** The map's HitObjects, indexed by objectIndex. */
|
2015-01-16 09:47:37 +01:00
|
|
|
private HitObject[] hitObjects;
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Delay time, in milliseconds, before song starts. */
|
2014-07-09 19:36:42 +02:00
|
|
|
private int leadInTime;
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Hit object approach time, in milliseconds. */
|
2014-06-30 04:17:04 +02:00
|
|
|
private int approachTime;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Time offsets for obtaining each hit result (indexed by HIT_* constants). */
|
2014-06-30 04:17:04 +02:00
|
|
|
private int[] hitResultOffset;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Current restart state. */
|
2015-01-15 06:56:30 +01:00
|
|
|
private Restart restart;
|
2015-01-15 05:57:50 +01:00
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Current break index in breaks ArrayList. */
|
2014-06-30 04:17:04 +02:00
|
|
|
private int breakIndex;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Break start time (0 if not in break). */
|
2014-06-30 04:17:04 +02:00
|
|
|
private int breakTime = 0;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Whether the break sound has been played. */
|
2014-07-01 07:14:03 +02:00
|
|
|
private boolean breakSound;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Skip button (displayed at song start, when necessary). */
|
2014-12-30 06:00:58 +01:00
|
|
|
private MenuButton skipButton;
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Current timing point index in timingPoints ArrayList. */
|
2014-06-30 04:17:04 +02:00
|
|
|
private int timingPointIndex;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Current beat lengths (base value and inherited value). */
|
2014-06-30 04:17:04 +02:00
|
|
|
private float beatLengthBase, beatLength;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Whether the countdown sound has been played. */
|
2014-07-01 07:14:03 +02:00
|
|
|
private boolean
|
|
|
|
countdownReadySound, countdown3Sound, countdown1Sound,
|
|
|
|
countdown2Sound, countdownGoSound;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Mouse coordinates before game paused. */
|
2014-06-30 04:17:04 +02:00
|
|
|
private int pausedMouseX = -1, pausedMouseY = -1;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Track position when game paused. */
|
2014-06-30 04:17:04 +02:00
|
|
|
private int pauseTime = -1;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Value for handling hitCircleSelect pulse effect (expanding, alpha level). */
|
2014-06-30 04:17:04 +02:00
|
|
|
private float pausePulse;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Whether a checkpoint has been loaded during this game. */
|
2014-07-09 04:17:48 +02:00
|
|
|
private boolean checkpointLoaded = false;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Number of deaths, used if "Easy" mod is enabled. */
|
2014-07-16 22:56:23 +02:00
|
|
|
private byte deaths = 0;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Track position at death, used if "Easy" mod is enabled. */
|
2014-07-16 22:56:23 +02:00
|
|
|
private int deathTime = -1;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Number of retries. */
|
2014-12-30 05:35:31 +01:00
|
|
|
private int retries = 0;
|
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
// game-related variables
|
|
|
|
private GameContainer container;
|
|
|
|
private StateBasedGame game;
|
|
|
|
private Input input;
|
|
|
|
private int state;
|
|
|
|
|
|
|
|
public Game(int state) {
|
|
|
|
this.state = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void init(GameContainer container, StateBasedGame game)
|
|
|
|
throws SlickException {
|
|
|
|
this.container = container;
|
|
|
|
this.game = game;
|
|
|
|
input = container.getInput();
|
|
|
|
|
|
|
|
int width = container.getWidth();
|
|
|
|
int height = container.getHeight();
|
|
|
|
|
2015-01-27 09:19:39 +01:00
|
|
|
// create the associated GameData object
|
|
|
|
data = new GameData(width, height);
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void render(GameContainer container, StateBasedGame game, Graphics g)
|
|
|
|
throws SlickException {
|
|
|
|
int width = container.getWidth();
|
|
|
|
int height = container.getHeight();
|
|
|
|
|
|
|
|
// background
|
2014-07-03 00:24:19 +02:00
|
|
|
g.setBackground(Color.black);
|
2014-07-03 07:05:23 +02:00
|
|
|
float dimLevel = Options.getBackgroundDim();
|
2015-02-10 03:40:38 +01:00
|
|
|
if (Options.isDefaultPlayfieldForced() || !osu.drawBG(width, height, dimLevel, false)) {
|
2015-01-21 01:01:18 +01:00
|
|
|
Image playfield = GameImage.PLAYFIELD.getImage();
|
2014-07-03 07:05:23 +02:00
|
|
|
playfield.setAlpha(dimLevel);
|
|
|
|
playfield.draw();
|
2015-02-11 08:56:02 +01:00
|
|
|
playfield.setAlpha(1f);
|
2014-07-03 07:05:23 +02:00
|
|
|
}
|
2014-06-30 04:17:04 +02:00
|
|
|
|
|
|
|
int trackPosition = MusicController.getPosition();
|
|
|
|
if (pauseTime > -1) // returning from pause screen
|
|
|
|
trackPosition = pauseTime;
|
2014-07-16 22:56:23 +02:00
|
|
|
else if (deathTime > -1) // "Easy" mod: health bar increasing
|
|
|
|
trackPosition = deathTime;
|
2014-12-30 05:35:31 +01:00
|
|
|
int firstObjectTime = osu.objects[0].getTime();
|
|
|
|
int timeDiff = firstObjectTime - trackPosition;
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2014-07-09 04:17:48 +02:00
|
|
|
// checkpoint
|
|
|
|
if (checkpointLoaded) {
|
2014-12-20 21:30:20 +01:00
|
|
|
int checkpoint = Options.getCheckpoint();
|
|
|
|
String checkpointText = String.format(
|
2014-12-24 06:25:26 +01:00
|
|
|
"Playing from checkpoint at %02d:%02d.",
|
2014-12-20 21:30:20 +01:00
|
|
|
TimeUnit.MILLISECONDS.toMinutes(checkpoint),
|
|
|
|
TimeUnit.MILLISECONDS.toSeconds(checkpoint) -
|
|
|
|
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(checkpoint))
|
|
|
|
);
|
2014-07-09 04:17:48 +02:00
|
|
|
Utils.FONT_MEDIUM.drawString(
|
2014-12-30 05:35:31 +01:00
|
|
|
(width - Utils.FONT_MEDIUM.getWidth(checkpointText)) / 2,
|
|
|
|
height - 15 - Utils.FONT_MEDIUM.getLineHeight(),
|
2014-07-09 04:17:48 +02:00
|
|
|
checkpointText, Color.white
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
// break periods
|
|
|
|
if (osu.breaks != null && breakIndex < osu.breaks.size()) {
|
|
|
|
if (breakTime > 0) {
|
|
|
|
int endTime = osu.breaks.get(breakIndex);
|
|
|
|
int breakLength = endTime - breakTime;
|
|
|
|
|
|
|
|
// letterbox effect (black bars on top/bottom)
|
|
|
|
if (osu.letterboxInBreaks && breakLength >= 4000) {
|
|
|
|
g.setColor(Color.black);
|
|
|
|
g.fillRect(0, 0, width, height * 0.125f);
|
|
|
|
g.fillRect(0, height * 0.875f, width, height * 0.125f);
|
|
|
|
}
|
|
|
|
|
2015-01-27 09:19:39 +01:00
|
|
|
data.drawGameElements(g, true, objectIndex == 0);
|
2014-06-30 04:17:04 +02:00
|
|
|
|
|
|
|
if (breakLength >= 8000 &&
|
|
|
|
trackPosition - breakTime > 2000 &&
|
|
|
|
trackPosition - breakTime < 5000) {
|
|
|
|
// show break start
|
2015-01-27 09:19:39 +01:00
|
|
|
if (data.getHealth() >= 50) {
|
2014-07-04 22:41:52 +02:00
|
|
|
GameImage.SECTION_PASS.getImage().drawCentered(width / 2f, height / 2f);
|
2014-07-01 07:14:03 +02:00
|
|
|
if (!breakSound) {
|
2015-01-08 01:29:51 +01:00
|
|
|
SoundController.playSound(SoundEffect.SECTIONPASS);
|
2014-07-01 07:14:03 +02:00
|
|
|
breakSound = true;
|
|
|
|
}
|
|
|
|
} else {
|
2014-07-04 22:41:52 +02:00
|
|
|
GameImage.SECTION_FAIL.getImage().drawCentered(width / 2f, height / 2f);
|
2014-07-01 07:14:03 +02:00
|
|
|
if (!breakSound) {
|
2015-01-08 01:29:51 +01:00
|
|
|
SoundController.playSound(SoundEffect.SECTIONFAIL);
|
2014-07-01 07:14:03 +02:00
|
|
|
breakSound = true;
|
|
|
|
}
|
|
|
|
}
|
2014-06-30 04:17:04 +02:00
|
|
|
} else if (breakLength >= 4000) {
|
|
|
|
// show break end (flash twice for 500ms)
|
|
|
|
int endTimeDiff = endTime - trackPosition;
|
|
|
|
if ((endTimeDiff > 1500 && endTimeDiff < 2000) ||
|
|
|
|
(endTimeDiff > 500 && endTimeDiff < 1000)) {
|
2014-07-04 22:41:52 +02:00
|
|
|
Image arrow = GameImage.WARNINGARROW.getImage();
|
|
|
|
arrow.setRotation(0);
|
|
|
|
arrow.draw(width * 0.15f, height * 0.15f);
|
|
|
|
arrow.draw(width * 0.15f, height * 0.75f);
|
|
|
|
arrow.setRotation(180);
|
|
|
|
arrow.draw(width * 0.75f, height * 0.15f);
|
|
|
|
arrow.draw(width * 0.75f, height * 0.75f);
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
|
|
|
}
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2014-07-16 22:01:36 +02:00
|
|
|
if (GameMod.AUTO.isActive())
|
2014-07-04 22:41:52 +02:00
|
|
|
GameImage.UNRANKED.getImage().drawCentered(width / 2, height * 0.077f);
|
2015-03-05 19:27:45 +01:00
|
|
|
UI.draw(g);
|
2014-06-30 04:17:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// game elements
|
2015-01-27 09:19:39 +01:00
|
|
|
data.drawGameElements(g, false, objectIndex == 0);
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2014-07-03 05:38:30 +02:00
|
|
|
// skip beginning
|
|
|
|
if (objectIndex == 0 &&
|
2014-12-30 05:35:31 +01:00
|
|
|
trackPosition < osu.objects[0].getTime() - SKIP_OFFSET)
|
2014-07-03 05:38:30 +02:00
|
|
|
skipButton.draw();
|
|
|
|
|
2014-12-30 05:35:31 +01:00
|
|
|
// show retries
|
|
|
|
if (retries >= 2 && timeDiff >= -1000) {
|
|
|
|
int retryHeight = Math.max(
|
|
|
|
GameImage.SCOREBAR_BG.getImage().getHeight(),
|
|
|
|
GameImage.SCOREBAR_KI.getImage().getHeight()
|
|
|
|
);
|
2015-01-18 18:39:30 +01:00
|
|
|
float oldAlpha = Utils.COLOR_WHITE_FADE.a;
|
2014-12-30 05:35:31 +01:00
|
|
|
if (timeDiff < -500)
|
|
|
|
Utils.COLOR_WHITE_FADE.a = (1000 + timeDiff) / 500f;
|
|
|
|
Utils.FONT_MEDIUM.drawString(
|
|
|
|
2 + (width / 100), retryHeight,
|
|
|
|
String.format("%d retries and counting...", retries),
|
|
|
|
Utils.COLOR_WHITE_FADE
|
|
|
|
);
|
2015-01-18 18:39:30 +01:00
|
|
|
Utils.COLOR_WHITE_FADE.a = oldAlpha;
|
2014-12-30 05:35:31 +01:00
|
|
|
}
|
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
if (isLeadIn())
|
2015-02-09 05:48:55 +01:00
|
|
|
trackPosition = (leadInTime - Options.getMusicOffset()) * -1; // render approach circles during song lead-in
|
2014-06-30 04:17:04 +02:00
|
|
|
|
|
|
|
// countdown
|
|
|
|
if (osu.countdown > 0) { // TODO: implement half/double rate settings
|
2015-01-15 04:31:48 +01:00
|
|
|
timeDiff = firstObjectTime - trackPosition;
|
2014-06-30 04:17:04 +02:00
|
|
|
if (timeDiff >= 500 && timeDiff < 3000) {
|
2014-07-01 07:14:03 +02:00
|
|
|
if (timeDiff >= 1500) {
|
2014-07-04 22:41:52 +02:00
|
|
|
GameImage.COUNTDOWN_READY.getImage().drawCentered(width / 2, height / 2);
|
2014-07-01 07:14:03 +02:00
|
|
|
if (!countdownReadySound) {
|
2015-01-08 01:29:51 +01:00
|
|
|
SoundController.playSound(SoundEffect.READY);
|
2014-07-01 07:14:03 +02:00
|
|
|
countdownReadySound = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (timeDiff < 2000) {
|
2014-07-04 22:41:52 +02:00
|
|
|
GameImage.COUNTDOWN_3.getImage().draw(0, 0);
|
2014-07-01 07:14:03 +02:00
|
|
|
if (!countdown3Sound) {
|
2015-01-08 01:29:51 +01:00
|
|
|
SoundController.playSound(SoundEffect.COUNT3);
|
2014-07-01 07:14:03 +02:00
|
|
|
countdown3Sound = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (timeDiff < 1500) {
|
2014-07-04 22:41:52 +02:00
|
|
|
GameImage.COUNTDOWN_2.getImage().draw(width - GameImage.COUNTDOWN_2.getImage().getWidth(), 0);
|
2014-07-01 07:14:03 +02:00
|
|
|
if (!countdown2Sound) {
|
2015-01-08 01:29:51 +01:00
|
|
|
SoundController.playSound(SoundEffect.COUNT2);
|
2014-07-01 07:14:03 +02:00
|
|
|
countdown2Sound = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (timeDiff < 1000) {
|
2014-07-04 22:41:52 +02:00
|
|
|
GameImage.COUNTDOWN_1.getImage().drawCentered(width / 2, height / 2);
|
2014-07-01 07:14:03 +02:00
|
|
|
if (!countdown1Sound) {
|
2015-01-08 01:29:51 +01:00
|
|
|
SoundController.playSound(SoundEffect.COUNT1);
|
2014-07-01 07:14:03 +02:00
|
|
|
countdown1Sound = true;
|
|
|
|
}
|
|
|
|
}
|
2014-06-30 04:17:04 +02:00
|
|
|
} else if (timeDiff >= -500 && timeDiff < 500) {
|
2014-07-04 22:41:52 +02:00
|
|
|
Image go = GameImage.COUNTDOWN_GO.getImage();
|
|
|
|
go.setAlpha((timeDiff < 0) ? 1 - (timeDiff / -1000f) : 1);
|
|
|
|
go.drawCentered(width / 2, height / 2);
|
2014-07-01 07:14:03 +02:00
|
|
|
if (!countdownGoSound) {
|
2015-01-08 01:29:51 +01:00
|
|
|
SoundController.playSound(SoundEffect.GO);
|
2014-07-01 07:14:03 +02:00
|
|
|
countdownGoSound = true;
|
|
|
|
}
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// draw hit objects in reverse order, or else overlapping objects are unreadable
|
|
|
|
Stack<Integer> stack = new Stack<Integer>();
|
2015-01-30 03:24:21 +01:00
|
|
|
for (int i = objectIndex; i < hitObjects.length && osu.objects[i].getTime() < trackPosition + approachTime; i++)
|
2014-06-30 04:17:04 +02:00
|
|
|
stack.add(i);
|
|
|
|
|
2015-01-16 09:47:37 +01:00
|
|
|
while (!stack.isEmpty())
|
|
|
|
hitObjects[stack.pop()].draw(trackPosition, stack.isEmpty(), g);
|
2014-06-30 04:17:04 +02:00
|
|
|
|
|
|
|
// draw OsuHitObjectResult objects
|
2015-01-27 09:19:39 +01:00
|
|
|
data.drawHitResults(trackPosition);
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2014-07-16 22:01:36 +02:00
|
|
|
if (GameMod.AUTO.isActive())
|
2014-07-04 22:41:52 +02:00
|
|
|
GameImage.UNRANKED.getImage().drawCentered(width / 2, height * 0.077f);
|
2014-07-03 07:05:23 +02:00
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
// returning from pause screen
|
|
|
|
if (pauseTime > -1 && pausedMouseX > -1 && pausedMouseY > -1) {
|
|
|
|
// darken the screen
|
2014-07-02 01:32:03 +02:00
|
|
|
g.setColor(Utils.COLOR_BLACK_ALPHA);
|
2014-06-30 04:17:04 +02:00
|
|
|
g.fillRect(0, 0, width, height);
|
|
|
|
|
|
|
|
// draw glowing hit select circle and pulse effect
|
2014-07-04 22:41:52 +02:00
|
|
|
int circleRadius = GameImage.HITCIRCLE.getImage().getWidth();
|
|
|
|
Image cursorCircle = GameImage.HITCIRCLE_SELECT.getImage().getScaledCopy(circleRadius, circleRadius);
|
2014-06-30 04:17:04 +02:00
|
|
|
cursorCircle.setAlpha(1.0f);
|
|
|
|
cursorCircle.drawCentered(pausedMouseX, pausedMouseY);
|
|
|
|
Image cursorCirclePulse = cursorCircle.getScaledCopy(1f + pausePulse);
|
|
|
|
cursorCirclePulse.setAlpha(1f - pausePulse);
|
|
|
|
cursorCirclePulse.drawCentered(pausedMouseX, pausedMouseY);
|
|
|
|
}
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2015-03-05 19:27:45 +01:00
|
|
|
UI.draw(g);
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void update(GameContainer container, StateBasedGame game, int delta)
|
|
|
|
throws SlickException {
|
2015-03-05 19:27:45 +01:00
|
|
|
UI.update(delta);
|
2014-12-24 08:45:43 +01:00
|
|
|
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
|
|
|
skipButton.hoverUpdate(delta, mouseX, mouseY);
|
2014-07-07 05:13:33 +02:00
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
if (isLeadIn()) { // stop updating during song lead-in
|
|
|
|
leadInTime -= delta;
|
|
|
|
if (!isLeadIn())
|
2015-02-09 05:48:55 +01:00
|
|
|
MusicController.resume();
|
2014-06-30 04:17:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// returning from pause screen: must click previous mouse position
|
|
|
|
if (pauseTime > -1) {
|
2015-03-06 06:25:48 +01:00
|
|
|
// paused during lead-in or break, or "relax" or "autopilot": continue immediately
|
|
|
|
if ((pausedMouseX < 0 && pausedMouseY < 0) ||
|
|
|
|
(GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive())) {
|
2014-06-30 04:17:04 +02:00
|
|
|
pauseTime = -1;
|
|
|
|
if (!isLeadIn())
|
|
|
|
MusicController.resume();
|
|
|
|
}
|
|
|
|
|
|
|
|
// focus lost: go back to pause screen
|
|
|
|
else if (!container.hasFocus()) {
|
|
|
|
game.enterState(Opsu.STATE_GAMEPAUSEMENU);
|
|
|
|
pausePulse = 0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// advance pulse animation
|
|
|
|
else {
|
|
|
|
pausePulse += delta / 750f;
|
|
|
|
if (pausePulse > 1f)
|
|
|
|
pausePulse = 0f;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-16 22:56:23 +02:00
|
|
|
// "Easy" mod: multiple "lives"
|
|
|
|
if (GameMod.EASY.isActive() && deathTime > -1) {
|
2015-01-27 09:19:39 +01:00
|
|
|
if (data.getHealth() < 99f)
|
|
|
|
data.changeHealth(delta / 10f);
|
2014-07-16 22:56:23 +02:00
|
|
|
else {
|
|
|
|
MusicController.resume();
|
|
|
|
deathTime = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-27 09:19:39 +01:00
|
|
|
data.updateDisplays(delta);
|
2014-07-03 08:03:56 +02:00
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
// map complete!
|
2015-01-30 03:24:21 +01:00
|
|
|
if (objectIndex >= hitObjects.length || (MusicController.trackEnded() && objectIndex > 0)) {
|
|
|
|
// track ended before last object was processed: force a hit result
|
|
|
|
if (MusicController.trackEnded() && objectIndex < hitObjects.length)
|
|
|
|
hitObjects[objectIndex].update(true, delta, mouseX, mouseY);
|
|
|
|
|
2015-01-16 20:10:42 +01:00
|
|
|
if (checkpointLoaded) // if checkpoint used, skip ranking screen
|
|
|
|
game.closeRequested();
|
2015-01-28 09:47:24 +01:00
|
|
|
else { // go to ranking screen
|
|
|
|
((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data);
|
2015-01-30 02:36:23 +01:00
|
|
|
ScoreData score = data.getScoreData(osu);
|
2015-03-06 06:25:48 +01:00
|
|
|
if (!GameMod.AUTO.isActive() && !GameMod.RELAX.isActive() && !GameMod.AUTOPILOT.isActive())
|
2015-01-30 02:36:23 +01:00
|
|
|
ScoreDB.addScore(score);
|
2015-01-16 20:10:42 +01:00
|
|
|
game.enterState(Opsu.STATE_GAMERANKING, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
2015-01-28 09:47:24 +01:00
|
|
|
}
|
2014-06-30 04:17:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int trackPosition = MusicController.getPosition();
|
|
|
|
|
|
|
|
// timing points
|
|
|
|
if (timingPointIndex < osu.timingPoints.size()) {
|
|
|
|
OsuTimingPoint timingPoint = osu.timingPoints.get(timingPointIndex);
|
2014-07-09 19:36:42 +02:00
|
|
|
if (trackPosition >= timingPoint.getTime()) {
|
|
|
|
if (!timingPoint.isInherited())
|
|
|
|
beatLengthBase = beatLength = timingPoint.getBeatLength();
|
2014-06-30 04:17:04 +02:00
|
|
|
else
|
2014-07-09 19:36:42 +02:00
|
|
|
beatLength = beatLengthBase * timingPoint.getSliderMultiplier();
|
2015-03-01 17:06:46 +01:00
|
|
|
HitSound.setDefaultSampleSet(timingPoint.getSampleType());
|
2014-07-09 19:36:42 +02:00
|
|
|
SoundController.setSampleVolume(timingPoint.getSampleVolume());
|
2014-06-30 04:17:04 +02:00
|
|
|
timingPointIndex++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// song beginning
|
2014-12-30 07:17:05 +01:00
|
|
|
if (objectIndex == 0 && trackPosition < osu.objects[0].getTime())
|
|
|
|
return; // nothing to do here
|
2014-06-30 04:17:04 +02:00
|
|
|
|
|
|
|
// break periods
|
|
|
|
if (osu.breaks != null && breakIndex < osu.breaks.size()) {
|
|
|
|
int breakValue = osu.breaks.get(breakIndex);
|
|
|
|
if (breakTime > 0) { // in a break period
|
|
|
|
if (trackPosition < breakValue)
|
|
|
|
return;
|
|
|
|
else {
|
|
|
|
// break is over
|
|
|
|
breakTime = 0;
|
|
|
|
breakIndex++;
|
|
|
|
}
|
|
|
|
} else if (trackPosition >= breakValue) {
|
|
|
|
// start a break
|
|
|
|
breakTime = breakValue;
|
2014-07-01 07:14:03 +02:00
|
|
|
breakSound = false;
|
2014-06-30 04:17:04 +02:00
|
|
|
breakIndex++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// pause game if focus lost
|
2014-07-16 22:01:36 +02:00
|
|
|
if (!container.hasFocus() && !GameMod.AUTO.isActive()) {
|
2014-06-30 04:17:04 +02:00
|
|
|
if (pauseTime < 0) {
|
2014-12-24 08:45:43 +01:00
|
|
|
pausedMouseX = mouseX;
|
|
|
|
pausedMouseY = mouseY;
|
2014-06-30 04:17:04 +02:00
|
|
|
pausePulse = 0f;
|
|
|
|
}
|
|
|
|
if (MusicController.isPlaying() || isLeadIn())
|
|
|
|
pauseTime = trackPosition;
|
|
|
|
game.enterState(Opsu.STATE_GAMEPAUSEMENU);
|
|
|
|
}
|
|
|
|
|
|
|
|
// drain health
|
2015-01-27 09:19:39 +01:00
|
|
|
data.changeHealth(delta * -1 * GameData.HP_DRAIN_MULTIPLIER);
|
|
|
|
if (!data.isAlive()) {
|
2014-07-16 22:56:23 +02:00
|
|
|
// "Easy" mod
|
2015-01-29 07:50:26 +01:00
|
|
|
if (GameMod.EASY.isActive() && !GameMod.SUDDEN_DEATH.isActive()) {
|
2014-07-16 22:56:23 +02:00
|
|
|
deaths++;
|
|
|
|
if (deaths < 3) {
|
|
|
|
deathTime = trackPosition;
|
|
|
|
MusicController.pause();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
// game over, force a restart
|
2015-01-15 06:56:30 +01:00
|
|
|
restart = Restart.LOSE;
|
2014-06-30 04:17:04 +02:00
|
|
|
game.enterState(Opsu.STATE_GAMEPAUSEMENU);
|
|
|
|
}
|
|
|
|
|
|
|
|
// update objects (loop in unlikely event of any skipped indexes)
|
2015-01-30 03:24:21 +01:00
|
|
|
while (objectIndex < hitObjects.length && trackPosition > osu.objects[objectIndex].getTime()) {
|
2014-06-30 04:17:04 +02:00
|
|
|
// check if we've already passed the next object's start time
|
2015-01-30 03:24:21 +01:00
|
|
|
boolean overlap = (objectIndex + 1 < hitObjects.length &&
|
2015-01-27 09:19:39 +01:00
|
|
|
trackPosition > osu.objects[objectIndex + 1].getTime() - hitResultOffset[GameData.HIT_300]);
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2015-01-16 09:47:37 +01:00
|
|
|
// update hit object and check completion status
|
|
|
|
if (hitObjects[objectIndex].update(overlap, delta, mouseX, mouseY))
|
|
|
|
objectIndex++; // done, so increment object index
|
2014-06-30 04:17:04 +02:00
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getID() { return state; }
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void keyPressed(int key, char c) {
|
2014-12-30 05:35:31 +01:00
|
|
|
int trackPosition = MusicController.getPosition();
|
|
|
|
|
2014-07-18 05:58:37 +02:00
|
|
|
// game keys
|
|
|
|
if (!Keyboard.isRepeatEvent()) {
|
|
|
|
if (key == Options.getGameKeyLeft())
|
2015-03-03 04:12:57 +01:00
|
|
|
gameKeyPressed(Input.MOUSE_LEFT_BUTTON, input.getMouseX(), input.getMouseY());
|
2014-07-18 05:58:37 +02:00
|
|
|
else if (key == Options.getGameKeyRight())
|
2015-03-03 04:12:57 +01:00
|
|
|
gameKeyPressed(Input.MOUSE_RIGHT_BUTTON, input.getMouseX(), input.getMouseY());
|
2014-07-18 05:58:37 +02:00
|
|
|
}
|
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
switch (key) {
|
|
|
|
case Input.KEY_ESCAPE:
|
2015-01-16 08:00:42 +01:00
|
|
|
// "auto" mod: go back to song menu
|
|
|
|
if (GameMod.AUTO.isActive()) {
|
|
|
|
game.closeRequested();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
// pause game
|
2015-01-16 08:00:42 +01:00
|
|
|
if (pauseTime < 0 && breakTime <= 0 && trackPosition >= osu.objects[0].getTime()) {
|
2014-06-30 04:17:04 +02:00
|
|
|
pausedMouseX = input.getMouseX();
|
|
|
|
pausedMouseY = input.getMouseY();
|
|
|
|
pausePulse = 0f;
|
|
|
|
}
|
|
|
|
if (MusicController.isPlaying() || isLeadIn())
|
|
|
|
pauseTime = trackPosition;
|
|
|
|
game.enterState(Opsu.STATE_GAMEPAUSEMENU, new EmptyTransition(), new FadeInTransition(Color.black));
|
|
|
|
break;
|
|
|
|
case Input.KEY_SPACE:
|
2014-07-09 04:17:48 +02:00
|
|
|
// skip intro
|
2014-06-30 04:17:04 +02:00
|
|
|
skipIntro();
|
|
|
|
break;
|
2014-07-05 20:29:48 +02:00
|
|
|
case Input.KEY_R:
|
2014-07-09 04:17:48 +02:00
|
|
|
// restart
|
2014-07-05 20:29:48 +02:00
|
|
|
if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) {
|
|
|
|
try {
|
2014-12-30 05:35:31 +01:00
|
|
|
if (trackPosition < osu.objects[0].getTime())
|
|
|
|
retries--; // don't count this retry (cancel out later increment)
|
2015-01-15 06:56:30 +01:00
|
|
|
restart = Restart.MANUAL;
|
2014-07-05 20:29:48 +02:00
|
|
|
enter(container, game);
|
|
|
|
skipIntro();
|
|
|
|
} catch (SlickException e) {
|
2015-01-16 03:55:26 +01:00
|
|
|
ErrorHandler.error("Failed to restart game.", e, false);
|
2014-07-05 20:29:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2014-07-09 04:17:48 +02:00
|
|
|
case Input.KEY_S:
|
|
|
|
// save checkpoint
|
|
|
|
if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) {
|
|
|
|
if (isLeadIn())
|
|
|
|
break;
|
|
|
|
|
2014-12-30 05:35:31 +01:00
|
|
|
int position = (pauseTime > -1) ? pauseTime : trackPosition;
|
2015-03-05 20:40:57 +01:00
|
|
|
if (Options.setCheckpoint(position / 1000)) {
|
2015-01-08 01:29:51 +01:00
|
|
|
SoundController.playSound(SoundEffect.MENUCLICK);
|
2015-03-05 20:40:57 +01:00
|
|
|
UI.sendBarNotification("Checkpoint saved.");
|
|
|
|
}
|
2014-07-09 04:17:48 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Input.KEY_L:
|
|
|
|
// load checkpoint
|
|
|
|
if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) {
|
|
|
|
int checkpoint = Options.getCheckpoint();
|
2014-07-18 03:16:15 +02:00
|
|
|
if (checkpoint == 0 || checkpoint > osu.endTime)
|
2014-07-09 04:17:48 +02:00
|
|
|
break; // invalid checkpoint
|
|
|
|
try {
|
2015-01-15 06:56:30 +01:00
|
|
|
restart = Restart.MANUAL;
|
2014-07-09 04:17:48 +02:00
|
|
|
enter(container, game);
|
|
|
|
checkpointLoaded = true;
|
|
|
|
if (isLeadIn()) {
|
|
|
|
leadInTime = 0;
|
|
|
|
MusicController.resume();
|
|
|
|
}
|
2015-01-08 01:29:51 +01:00
|
|
|
SoundController.playSound(SoundEffect.MENUHIT);
|
2015-03-05 20:40:57 +01:00
|
|
|
UI.sendBarNotification("Checkpoint loaded.");
|
2014-07-09 04:17:48 +02:00
|
|
|
|
|
|
|
// skip to checkpoint
|
|
|
|
MusicController.setPosition(checkpoint);
|
2015-01-30 03:24:21 +01:00
|
|
|
while (objectIndex < hitObjects.length &&
|
2015-01-16 20:10:42 +01:00
|
|
|
osu.objects[objectIndex++].getTime() <= checkpoint)
|
2014-07-09 04:17:48 +02:00
|
|
|
;
|
|
|
|
objectIndex--;
|
|
|
|
} catch (SlickException e) {
|
2015-01-16 03:55:26 +01:00
|
|
|
ErrorHandler.error("Failed to load checkpoint.", e, false);
|
2014-07-09 04:17:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2015-01-21 04:19:14 +01:00
|
|
|
case Input.KEY_UP:
|
2015-03-05 19:27:45 +01:00
|
|
|
UI.changeVolume(1);
|
2015-01-21 04:19:14 +01:00
|
|
|
break;
|
|
|
|
case Input.KEY_DOWN:
|
2015-03-05 19:27:45 +01:00
|
|
|
UI.changeVolume(-1);
|
2015-01-21 04:19:14 +01:00
|
|
|
break;
|
2015-03-05 20:40:57 +01:00
|
|
|
case Input.KEY_F7:
|
|
|
|
Options.setNextFPS(container);
|
|
|
|
break;
|
|
|
|
case Input.KEY_F10:
|
|
|
|
Options.toggleMouseDisabled();
|
|
|
|
break;
|
2014-06-30 04:17:04 +02:00
|
|
|
case Input.KEY_F12:
|
2014-07-02 01:32:03 +02:00
|
|
|
Utils.takeScreenShot();
|
2014-06-30 04:17:04 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void mousePressed(int button, int x, int y) {
|
2015-03-03 04:12:57 +01:00
|
|
|
if (Options.isMouseDisabled())
|
2014-06-30 04:17:04 +02:00
|
|
|
return;
|
|
|
|
|
2015-03-03 04:12:57 +01:00
|
|
|
// mouse wheel: pause the game
|
|
|
|
if (button == Input.MOUSE_MIDDLE_BUTTON && !Options.isMouseWheelDisabled()) {
|
|
|
|
int trackPosition = MusicController.getPosition();
|
|
|
|
if (pauseTime < 0 && breakTime <= 0 && trackPosition >= osu.objects[0].getTime()) {
|
|
|
|
pausedMouseX = x;
|
|
|
|
pausedMouseY = y;
|
|
|
|
pausePulse = 0f;
|
|
|
|
}
|
|
|
|
if (MusicController.isPlaying() || isLeadIn())
|
|
|
|
pauseTime = trackPosition;
|
|
|
|
game.enterState(Opsu.STATE_GAMEPAUSEMENU, new EmptyTransition(), new FadeInTransition(Color.black));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gameKeyPressed(button, x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles a game key pressed event.
|
|
|
|
* @param button the index of the button pressed
|
|
|
|
* @param x the mouse x coordinate
|
|
|
|
* @param y the mouse y coordinate
|
|
|
|
*/
|
|
|
|
private void gameKeyPressed(int button, int x, int y) {
|
2014-06-30 04:17:04 +02:00
|
|
|
// returning from pause screen
|
|
|
|
if (pauseTime > -1) {
|
|
|
|
double distance = Math.hypot(pausedMouseX - x, pausedMouseY - y);
|
2014-07-04 22:41:52 +02:00
|
|
|
int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
|
2014-06-30 04:17:04 +02:00
|
|
|
if (distance < circleRadius) {
|
|
|
|
// unpause the game
|
|
|
|
pauseTime = -1;
|
|
|
|
pausedMouseX = -1;
|
|
|
|
pausedMouseY = -1;
|
2014-07-09 19:36:42 +02:00
|
|
|
if (!isLeadIn())
|
2014-06-30 04:17:04 +02:00
|
|
|
MusicController.resume();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-30 03:24:21 +01:00
|
|
|
if (objectIndex >= hitObjects.length) // nothing left to do here
|
2014-06-30 04:17:04 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
OsuHitObject hitObject = osu.objects[objectIndex];
|
|
|
|
|
|
|
|
// skip beginning
|
|
|
|
if (skipButton.contains(x, y)) {
|
|
|
|
if (skipIntro())
|
|
|
|
return; // successfully skipped
|
|
|
|
}
|
|
|
|
|
2015-03-06 06:25:48 +01:00
|
|
|
// "auto" and "relax" mods: ignore user actions
|
|
|
|
if (GameMod.AUTO.isActive() || GameMod.RELAX.isActive())
|
2014-06-30 04:17:04 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
// circles
|
2015-01-16 09:47:37 +01:00
|
|
|
if (hitObject.isCircle() && hitObjects[objectIndex].mousePressed(x, y))
|
|
|
|
objectIndex++; // circle hit
|
2014-06-30 04:17:04 +02:00
|
|
|
|
|
|
|
// sliders
|
2015-02-15 04:15:41 +01:00
|
|
|
else if (hitObject.isSlider())
|
|
|
|
hitObjects[objectIndex].mousePressed(x, y);
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
|
|
|
|
2015-01-20 20:52:02 +01:00
|
|
|
@Override
|
|
|
|
public void mouseWheelMoved(int newValue) {
|
2015-03-03 04:12:57 +01:00
|
|
|
if (Options.isMouseWheelDisabled() || Options.isMouseDisabled())
|
|
|
|
return;
|
|
|
|
|
2015-03-05 19:27:45 +01:00
|
|
|
UI.changeVolume((newValue < 0) ? -1 : 1);
|
2015-01-20 20:52:02 +01:00
|
|
|
}
|
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
@Override
|
|
|
|
public void enter(GameContainer container, StateBasedGame game)
|
|
|
|
throws SlickException {
|
2015-03-05 19:27:45 +01:00
|
|
|
UI.enter();
|
2015-01-15 06:56:30 +01:00
|
|
|
if (restart == Restart.NEW)
|
2014-07-02 09:02:11 +02:00
|
|
|
osu = MusicController.getOsuFile();
|
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
if (osu == null || osu.objects == null)
|
|
|
|
throw new RuntimeException("Running game with no OsuFile loaded.");
|
|
|
|
|
2014-07-02 07:53:42 +02:00
|
|
|
// grab the mouse (not working for touchscreen)
|
|
|
|
// container.setMouseGrabbed(true);
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
// restart the game
|
2015-01-15 06:56:30 +01:00
|
|
|
if (restart != Restart.FALSE) {
|
|
|
|
if (restart == Restart.NEW) {
|
2015-01-15 22:47:55 +01:00
|
|
|
// new game
|
2014-07-04 22:41:52 +02:00
|
|
|
loadImages();
|
2014-06-30 04:17:04 +02:00
|
|
|
setMapModifiers();
|
2014-12-30 05:35:31 +01:00
|
|
|
retries = 0;
|
2015-01-15 22:47:55 +01:00
|
|
|
} else {
|
|
|
|
// retry
|
2014-12-30 05:35:31 +01:00
|
|
|
retries++;
|
2015-01-15 22:47:55 +01:00
|
|
|
}
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2015-01-15 22:47:55 +01:00
|
|
|
// reset game data
|
|
|
|
resetGameData();
|
2015-02-14 07:07:17 +01:00
|
|
|
|
2015-02-12 09:52:19 +01:00
|
|
|
// needs to play before setting position to resume without lag later
|
2015-02-16 07:24:22 +01:00
|
|
|
MusicController.play(false);
|
2015-01-15 22:47:55 +01:00
|
|
|
MusicController.setPosition(0);
|
|
|
|
MusicController.pause();
|
2014-06-30 04:17:04 +02:00
|
|
|
|
2015-01-15 22:47:55 +01:00
|
|
|
// initialize object maps
|
2014-06-30 04:17:04 +02:00
|
|
|
for (int i = 0; i < osu.objects.length; i++) {
|
|
|
|
OsuHitObject hitObject = osu.objects[i];
|
|
|
|
|
|
|
|
// is this the last note in the combo?
|
|
|
|
boolean comboEnd = false;
|
2014-07-18 21:11:57 +02:00
|
|
|
if (i + 1 < osu.objects.length && osu.objects[i + 1].isNewCombo())
|
2014-06-30 04:17:04 +02:00
|
|
|
comboEnd = true;
|
|
|
|
|
2014-07-18 21:11:57 +02:00
|
|
|
Color color = osu.combo[hitObject.getComboIndex()];
|
2015-01-16 09:47:37 +01:00
|
|
|
if (hitObject.isCircle())
|
2015-01-27 09:19:39 +01:00
|
|
|
hitObjects[i] = new Circle(hitObject, this, data, color, comboEnd);
|
2015-01-16 09:47:37 +01:00
|
|
|
else if (hitObject.isSlider())
|
2015-01-27 09:19:39 +01:00
|
|
|
hitObjects[i] = new Slider(hitObject, this, data, color, comboEnd);
|
2015-01-16 09:47:37 +01:00
|
|
|
else if (hitObject.isSpinner())
|
2015-01-27 09:19:39 +01:00
|
|
|
hitObjects[i] = new Spinner(hitObject, this, data);
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// load the first timingPoint
|
2014-07-01 07:14:03 +02:00
|
|
|
if (!osu.timingPoints.isEmpty()) {
|
|
|
|
OsuTimingPoint timingPoint = osu.timingPoints.get(0);
|
2014-07-09 19:36:42 +02:00
|
|
|
if (!timingPoint.isInherited()) {
|
|
|
|
beatLengthBase = beatLength = timingPoint.getBeatLength();
|
2015-03-01 17:06:46 +01:00
|
|
|
HitSound.setDefaultSampleSet(timingPoint.getSampleType());
|
2014-07-09 19:36:42 +02:00
|
|
|
SoundController.setSampleVolume(timingPoint.getSampleVolume());
|
2014-07-01 07:14:03 +02:00
|
|
|
timingPointIndex++;
|
|
|
|
}
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
leadInTime = osu.audioLeadIn + approachTime;
|
2015-01-15 06:56:30 +01:00
|
|
|
restart = Restart.FALSE;
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
2014-12-24 08:45:43 +01:00
|
|
|
|
2015-02-02 06:15:16 +01:00
|
|
|
skipButton.resetHover();
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
|
|
|
|
2014-07-02 07:53:42 +02:00
|
|
|
// @Override
|
|
|
|
// public void leave(GameContainer container, StateBasedGame game)
|
|
|
|
// throws SlickException {
|
|
|
|
// container.setMouseGrabbed(false);
|
|
|
|
// }
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2015-01-15 22:47:55 +01:00
|
|
|
/**
|
|
|
|
* Resets all game data and structures.
|
|
|
|
*/
|
|
|
|
public void resetGameData() {
|
2015-01-16 09:47:37 +01:00
|
|
|
hitObjects = new HitObject[osu.objects.length];
|
2015-01-27 09:19:39 +01:00
|
|
|
data.clear();
|
2015-01-15 22:47:55 +01:00
|
|
|
objectIndex = 0;
|
|
|
|
breakIndex = 0;
|
|
|
|
breakTime = 0;
|
|
|
|
breakSound = false;
|
|
|
|
timingPointIndex = 0;
|
|
|
|
beatLengthBase = beatLength = 1;
|
|
|
|
pauseTime = -1;
|
|
|
|
pausedMouseX = -1;
|
|
|
|
pausedMouseY = -1;
|
|
|
|
countdownReadySound = false;
|
|
|
|
countdown3Sound = false;
|
|
|
|
countdown1Sound = false;
|
|
|
|
countdown2Sound = false;
|
|
|
|
countdownGoSound = false;
|
|
|
|
checkpointLoaded = false;
|
|
|
|
deaths = 0;
|
|
|
|
deathTime = -1;
|
|
|
|
|
|
|
|
System.gc();
|
|
|
|
}
|
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
/**
|
|
|
|
* Skips the beginning of a track.
|
|
|
|
* @return true if skipped, false otherwise
|
|
|
|
*/
|
|
|
|
private boolean skipIntro() {
|
2014-07-18 21:11:57 +02:00
|
|
|
int firstObjectTime = osu.objects[0].getTime();
|
2014-06-30 04:17:04 +02:00
|
|
|
int trackPosition = MusicController.getPosition();
|
|
|
|
if (objectIndex == 0 &&
|
2014-07-18 21:11:57 +02:00
|
|
|
trackPosition < firstObjectTime - SKIP_OFFSET) {
|
2014-06-30 04:17:04 +02:00
|
|
|
if (isLeadIn()) {
|
|
|
|
leadInTime = 0;
|
|
|
|
MusicController.resume();
|
|
|
|
}
|
2014-07-18 21:11:57 +02:00
|
|
|
MusicController.setPosition(firstObjectTime - SKIP_OFFSET);
|
2015-01-08 01:29:51 +01:00
|
|
|
SoundController.playSound(SoundEffect.MENUHIT);
|
2014-06-30 04:17:04 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-07-04 22:41:52 +02:00
|
|
|
/**
|
|
|
|
* Loads all game images.
|
|
|
|
*/
|
2015-01-18 23:01:13 +01:00
|
|
|
private void loadImages() {
|
2014-07-04 22:41:52 +02:00
|
|
|
int width = container.getWidth();
|
|
|
|
int height = container.getHeight();
|
|
|
|
|
|
|
|
// set images
|
|
|
|
File parent = osu.getFile().getParentFile();
|
2015-01-08 04:36:39 +01:00
|
|
|
for (GameImage img : GameImage.values()) {
|
2015-03-06 20:39:49 +01:00
|
|
|
if (img.isSkinnable()) {
|
|
|
|
img.setDefaultImage();
|
2015-01-15 05:10:19 +01:00
|
|
|
img.setSkinImage(parent);
|
2015-03-06 20:39:49 +01:00
|
|
|
}
|
2015-01-08 04:36:39 +01:00
|
|
|
}
|
2014-07-04 22:41:52 +02:00
|
|
|
|
|
|
|
// skip button
|
2015-02-19 01:35:26 +01:00
|
|
|
if (GameImage.SKIP.getImages() != null) {
|
2015-02-19 06:33:32 +01:00
|
|
|
Animation skip = GameImage.SKIP.getAnimation(120);
|
2015-02-19 01:35:26 +01:00
|
|
|
skipButton = new MenuButton(skip, width - skip.getWidth() / 2f, height - (skip.getHeight() / 2f));
|
|
|
|
} else {
|
|
|
|
Image skip = GameImage.SKIP.getImage();
|
|
|
|
skipButton = new MenuButton(skip, width - skip.getWidth() / 2f, height - (skip.getHeight() / 2f));
|
2015-02-18 04:03:11 +01:00
|
|
|
}
|
2015-02-19 01:35:26 +01:00
|
|
|
skipButton.setHoverExpand(1.1f, MenuButton.Expand.UP_LEFT);
|
|
|
|
|
2014-07-05 01:59:57 +02:00
|
|
|
// load other images...
|
2014-07-04 22:41:52 +02:00
|
|
|
((GamePauseMenu) game.getState(Opsu.STATE_GAMEPAUSEMENU)).loadImages();
|
2015-01-28 09:47:24 +01:00
|
|
|
data.loadImages();
|
2014-07-04 22:41:52 +02:00
|
|
|
}
|
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
/**
|
|
|
|
* Set map modifiers.
|
|
|
|
*/
|
|
|
|
private void setMapModifiers() {
|
2015-01-22 00:56:53 +01:00
|
|
|
// map-based properties, re-initialized each game
|
|
|
|
float circleSize = osu.circleSize;
|
|
|
|
float approachRate = osu.approachRate;
|
|
|
|
float overallDifficulty = osu.overallDifficulty;
|
|
|
|
float HPDrainRate = osu.HPDrainRate;
|
|
|
|
|
|
|
|
// "Hard Rock" modifiers
|
|
|
|
if (GameMod.HARD_ROCK.isActive()) {
|
|
|
|
circleSize = Math.min(circleSize * 1.4f, 10);
|
|
|
|
approachRate = Math.min(approachRate * 1.4f, 10);
|
|
|
|
overallDifficulty = Math.min(overallDifficulty * 1.4f, 10);
|
|
|
|
HPDrainRate = Math.min(HPDrainRate * 1.4f, 10);
|
|
|
|
}
|
2014-07-16 22:56:23 +02:00
|
|
|
|
2015-01-22 00:56:53 +01:00
|
|
|
// "Easy" modifiers
|
|
|
|
else if (GameMod.EASY.isActive()) {
|
|
|
|
circleSize /= 2f;
|
|
|
|
approachRate /= 2f;
|
|
|
|
overallDifficulty /= 2f;
|
|
|
|
HPDrainRate /= 2f;
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
2015-01-22 00:56:53 +01:00
|
|
|
|
|
|
|
// fixed difficulty overrides
|
|
|
|
if (Options.getFixedCS() > 0f)
|
|
|
|
circleSize = Options.getFixedCS();
|
|
|
|
if (Options.getFixedAR() > 0f)
|
|
|
|
approachRate = Options.getFixedAR();
|
|
|
|
if (Options.getFixedOD() > 0f)
|
|
|
|
overallDifficulty = Options.getFixedOD();
|
|
|
|
if (Options.getFixedHP() > 0f)
|
|
|
|
HPDrainRate = Options.getFixedHP();
|
|
|
|
|
|
|
|
// initialize objects
|
|
|
|
Circle.init(container, circleSize);
|
|
|
|
Slider.init(container, circleSize, osu);
|
|
|
|
Spinner.init(container);
|
|
|
|
|
|
|
|
// approachRate (hit object approach time)
|
|
|
|
if (approachRate < 5)
|
|
|
|
approachTime = (int) (1800 - (approachRate * 120));
|
|
|
|
else
|
|
|
|
approachTime = (int) (1200 - ((approachRate - 5) * 150));
|
|
|
|
|
|
|
|
// overallDifficulty (hit result time offsets)
|
2015-01-27 09:19:39 +01:00
|
|
|
hitResultOffset = new int[GameData.HIT_MAX];
|
|
|
|
hitResultOffset[GameData.HIT_300] = (int) (78 - (overallDifficulty * 6));
|
|
|
|
hitResultOffset[GameData.HIT_100] = (int) (138 - (overallDifficulty * 8));
|
|
|
|
hitResultOffset[GameData.HIT_50] = (int) (198 - (overallDifficulty * 10));
|
|
|
|
hitResultOffset[GameData.HIT_MISS] = (int) (500 - (overallDifficulty * 10));
|
2015-01-22 00:56:53 +01:00
|
|
|
|
|
|
|
// HPDrainRate (health change), overallDifficulty (scoring)
|
2015-01-27 09:19:39 +01:00
|
|
|
data.setDrainRate(HPDrainRate);
|
|
|
|
data.setDifficulty(overallDifficulty);
|
2015-02-14 19:45:14 +01:00
|
|
|
data.setHitResultOffset(hitResultOffset);
|
2014-06-30 04:17:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets/returns whether entering the state will restart it.
|
|
|
|
*/
|
2015-01-15 06:56:30 +01:00
|
|
|
public void setRestart(Restart restart) { this.restart = restart; }
|
|
|
|
public Restart getRestart() { return restart; }
|
2014-06-30 04:17:04 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether or not the track is in the lead-in time state.
|
|
|
|
*/
|
2014-07-09 19:36:42 +02:00
|
|
|
public boolean isLeadIn() { return leadInTime > 0; }
|
2014-06-30 04:17:04 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the object approach time, in milliseconds.
|
|
|
|
*/
|
|
|
|
public int getApproachTime() { return approachTime; }
|
2015-01-16 21:44:13 +01:00
|
|
|
|
2014-06-30 04:17:04 +02:00
|
|
|
/**
|
2015-01-27 09:19:39 +01:00
|
|
|
* Returns an array of hit result offset times, in milliseconds (indexed by GameData.HIT_* constants).
|
2014-06-30 04:17:04 +02:00
|
|
|
*/
|
|
|
|
public int[] getHitResultOffsets() { return hitResultOffset; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the beat length.
|
|
|
|
*/
|
|
|
|
public float getBeatLength() { return beatLength; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the slider multiplier given by the current timing point.
|
|
|
|
*/
|
|
|
|
public float getTimingPointMultiplier() { return beatLength / beatLengthBase; }
|
|
|
|
}
|