Skip to content

Commit

Permalink
Fixed "Hidden" mod timing issues. (fixes #121)
Browse files Browse the repository at this point in the history
This introduces more accurate timing formulas associated with the "Hidden" mod (previously, in #115, these values were hardcoded).  The values seem somewhat close to the values in osu!, but were not extensively tested.

Also set an upper bound on the fade in time for hit objects proportional to the approach time, or else the timing values in the "Hidden" mod would be too inconsistent.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
  • Loading branch information
itdelatrisu committed Aug 29, 2015
1 parent c4f54ec commit 34c7942
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 50 deletions.
18 changes: 10 additions & 8 deletions src/itdelatrisu/opsu/objects/Circle.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@
* Data type representing a circle object.
*/
public class Circle implements GameObject {
/** The amount of time, in milliseconds, to fade in the circle. */
private static final int FADE_IN_TIME = 375;

/** The diameter of hit circles. */
private static float diameter;

Expand Down Expand Up @@ -94,15 +91,20 @@ public Circle(HitObject hitObject, Game game, GameData data, Color color, boolea
@Override
public void draw(Graphics g, int trackPosition) {
int timeDiff = hitObject.getTime() - trackPosition;
float scale = timeDiff / (float) game.getApproachTime();
float fadeinScale = (timeDiff - game.getApproachTime() + FADE_IN_TIME) / (float) FADE_IN_TIME;
final int approachTime = game.getApproachTime();
final int fadeInTime = game.getFadeInTime();
float scale = timeDiff / (float) approachTime;
float approachScale = 1 + scale * 3;
float fadeinScale = (timeDiff - approachTime + fadeInTime) / (float) fadeInTime;
float alpha = Utils.clamp(1 - fadeinScale, 0, 1);

if (GameMod.HIDDEN.isActive()) {
float fadeOutScale = -(float)(timeDiff-game.getApproachTime())/game.getDecayTime();
float fadeOutAlpha = Utils.clamp(1-fadeOutScale, 0, 1);
alpha = Math.min(alpha, fadeOutAlpha);
final int hiddenDecayTime = game.getHiddenDecayTime();
final int hiddenTimeDiff = game.getHiddenTimeDiff();
if (fadeinScale <= 0f && timeDiff < hiddenTimeDiff + hiddenDecayTime) {
float hiddenAlpha = (timeDiff < hiddenTimeDiff) ? 0f : (timeDiff - hiddenTimeDiff) / (float) hiddenDecayTime;
alpha = Math.min(alpha, hiddenAlpha);
}
}

float oldAlpha = Colors.WHITE_FADE.a;
Expand Down
18 changes: 10 additions & 8 deletions src/itdelatrisu/opsu/objects/Slider.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ public class Slider implements GameObject {
/** The diameter of hit circles. */
private static float diameter;

/** The amount of time, in milliseconds, to fade in the slider. */
private static final int FADE_IN_TIME = 375;

/** The associated HitObject. */
private HitObject hitObject;

Expand Down Expand Up @@ -179,9 +176,11 @@ public Slider(HitObject hitObject, Game game, GameData data, Color color, boolea
@Override
public void draw(Graphics g, int trackPosition) {
int timeDiff = hitObject.getTime() - trackPosition;
float scale = timeDiff / (float) game.getApproachTime();
float fadeinScale = (timeDiff - game.getApproachTime() + FADE_IN_TIME) / (float) FADE_IN_TIME;
final int approachTime = game.getApproachTime();
final int fadeInTime = game.getFadeInTime();
float scale = timeDiff / (float) approachTime;
float approachScale = 1 + scale * 3;
float fadeinScale = (timeDiff - approachTime + fadeInTime) / (float) fadeInTime;
float alpha = Utils.clamp(1 - fadeinScale, 0, 1);
boolean overlayAboveNumber = Options.getSkin().isHitCircleOverlayAboveNumber();

Expand Down Expand Up @@ -212,9 +211,12 @@ public void draw(Graphics g, int trackPosition) {
}
}
if (GameMod.HIDDEN.isActive()) {
float fadeOutScale = -(float) (timeDiff - game.getApproachTime()) / game.getDecayTime();
float fadeOutAlpha = Utils.clamp(1 - fadeOutScale, 0, 1);
alpha = Math.min(alpha, fadeOutAlpha);
final int hiddenDecayTime = game.getHiddenDecayTime();
final int hiddenTimeDiff = game.getHiddenTimeDiff();
if (fadeinScale <= 0f && timeDiff < hiddenTimeDiff + hiddenDecayTime) {
float hiddenAlpha = (timeDiff < hiddenTimeDiff) ? 0f : (timeDiff - hiddenTimeDiff) / (float) hiddenDecayTime;
alpha = Math.min(alpha, hiddenAlpha);
}
}
if (sliderClickedInitial)
; // don't draw current combo number if already clicked
Expand Down
12 changes: 7 additions & 5 deletions src/itdelatrisu/opsu/objects/Spinner.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ public class Spinner implements GameObject {
/** The amount of time, in milliseconds, before another velocity is stored. */
private static final float DELTA_UPDATE_TIME = 1000 / 60f;

/** The amount of time, in milliseconds, to fade in the spinner. */
private static final int FADE_IN_TIME = 500;

/** Angle mod multipliers: "auto" (477rpm), "spun out" (287rpm) */
private static final float
AUTO_MULTIPLIER = 1 / 20f, // angle = 477/60f * delta/1000f * TWO_PI;
Expand All @@ -70,6 +67,9 @@ public class Spinner implements GameObject {
/** The associated HitObject. */
private HitObject hitObject;

/** The associated Game object. */
private Game game;

/** The associated GameData object. */
private GameData data;

Expand Down Expand Up @@ -125,6 +125,7 @@ public static void init(GameContainer container, float difficulty) {
*/
public Spinner(HitObject hitObject, Game game, GameData data) {
this.hitObject = hitObject;
this.game = game;
this.data = data;

/*
Expand Down Expand Up @@ -176,11 +177,12 @@ public Spinner(HitObject hitObject, Game game, GameData data) {
public void draw(Graphics g, int trackPosition) {
// only draw spinners shortly before start time
int timeDiff = hitObject.getTime() - trackPosition;
if (timeDiff - FADE_IN_TIME > 0)
final int fadeInTime = game.getFadeInTime();
if (timeDiff - fadeInTime > 0)
return;

boolean spinnerComplete = (rotations >= rotationsNeeded);
float alpha = Utils.clamp(1 - (float) timeDiff / FADE_IN_TIME, 0f, 1f);
float alpha = Utils.clamp(1 - (float) timeDiff / fadeInTime, 0f, 1f);

// darken screen
if (Options.getSkin().isSpinnerFadePlayfield()) {
Expand Down
82 changes: 53 additions & 29 deletions src/itdelatrisu/opsu/states/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,25 @@

package itdelatrisu.opsu.states;

import java.io.File;
import java.util.LinkedList;
import java.util.Stack;

import org.lwjgl.input.Keyboard;
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.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;

import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameImage;
Expand Down Expand Up @@ -52,25 +71,6 @@
import itdelatrisu.opsu.ui.UI;
import itdelatrisu.opsu.ui.animations.AnimationEquation;

import java.io.File;
import java.util.LinkedList;
import java.util.Stack;

import org.lwjgl.input.Keyboard;
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.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.
*/
Expand Down Expand Up @@ -119,9 +119,14 @@ public enum Restart {
/** Hit object approach time, in milliseconds. */
private int approachTime;

/** Decay time for elements in "Hidden" mod, in milliseconds. */
//TODO: figure out actual formula for decay time
private int decayTime = 800;
/** The amount of time for hit objects to fade in, in milliseconds. */
private int fadeInTime;

/** Decay time for hit objects in the "Hidden" mod, in milliseconds. */
private int hiddenDecayTime;

/** Time before the hit object time by which the objects have completely faded in the "Hidden" mod, in milliseconds. */
private int hiddenTimeDiff;

/** Time offsets for obtaining each hit result (indexed by HIT_* constants). */
private int[] hitResultOffset;
Expand Down Expand Up @@ -1480,19 +1485,19 @@ private void setMapModifiers() {
int diameter = (int) (104 - (circleSize * 8));
HitObject.setStackOffset(diameter * STACK_OFFSET_MODIFIER);

// approachRate (hit object approach time)
if (approachRate < 5)
approachTime = (int) (1800 - (approachRate * 120));
else
approachTime = (int) (1200 - ((approachRate - 5) * 150));

// initialize objects
Circle.init(container, circleSize);
Slider.init(container, circleSize, beatmap);
Spinner.init(container, overallDifficulty);
Curve.init(container.getWidth(), container.getHeight(), circleSize, (Options.isBeatmapSkinIgnored()) ?
Options.getSkin().getSliderBorderColor() : beatmap.getSliderBorderColor());

// 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)
hitResultOffset = new int[GameData.HIT_MAX];
hitResultOffset[GameData.HIT_300] = (int) (78 - (overallDifficulty * 6));
Expand All @@ -1511,6 +1516,14 @@ private void setMapModifiers() {

// difficulty multiplier (scoring)
data.calculateDifficultyMultiplier(beatmap.HPDrainRate, beatmap.circleSize, beatmap.overallDifficulty);

// hit object fade-in time (TODO: formula)
fadeInTime = Math.min(375, (int) (approachTime / 2.5f));

// fade times ("Hidden" mod)
// TODO: find the actual formulas for this
hiddenDecayTime = (int) (approachTime / 3.6f);
hiddenTimeDiff = (int) (approachTime / 3.3f);
}

/**
Expand All @@ -1534,10 +1547,21 @@ private void setMapModifiers() {
*/
public int getApproachTime() { return approachTime; }

/**
* Returns the amount of time for hit objects to fade in, in milliseconds.
*/
public int getFadeInTime() { return fadeInTime; }

/**
* Returns the object decay time in the "Hidden" mod, in milliseconds.
*/
public int getDecayTime() { return decayTime; }
public int getHiddenDecayTime() { return hiddenDecayTime; }

/**
* Returns the time before the hit object time by which the objects have
* completely faded in the "Hidden" mod, in milliseconds.
*/
public int getHiddenTimeDiff() { return hiddenTimeDiff; }

/**
* Returns an array of hit result offset times, in milliseconds (indexed by GameData.HIT_* constants).
Expand Down

0 comments on commit 34c7942

Please sign in to comment.