Fixed "Hidden" mod timing issues. (fixes #121)
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>
This commit is contained in:
parent
c4f54ecc05
commit
34c7942f4b
|
@ -36,9 +36,6 @@ import org.newdawn.slick.Graphics;
|
||||||
* Data type representing a circle object.
|
* Data type representing a circle object.
|
||||||
*/
|
*/
|
||||||
public class Circle implements GameObject {
|
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. */
|
/** The diameter of hit circles. */
|
||||||
private static float diameter;
|
private static float diameter;
|
||||||
|
|
||||||
|
@ -94,15 +91,20 @@ public class Circle implements GameObject {
|
||||||
@Override
|
@Override
|
||||||
public void draw(Graphics g, int trackPosition) {
|
public void draw(Graphics g, int trackPosition) {
|
||||||
int timeDiff = hitObject.getTime() - trackPosition;
|
int timeDiff = hitObject.getTime() - trackPosition;
|
||||||
float scale = timeDiff / (float) game.getApproachTime();
|
final int approachTime = game.getApproachTime();
|
||||||
float fadeinScale = (timeDiff - game.getApproachTime() + FADE_IN_TIME) / (float) FADE_IN_TIME;
|
final int fadeInTime = game.getFadeInTime();
|
||||||
|
float scale = timeDiff / (float) approachTime;
|
||||||
float approachScale = 1 + scale * 3;
|
float approachScale = 1 + scale * 3;
|
||||||
|
float fadeinScale = (timeDiff - approachTime + fadeInTime) / (float) fadeInTime;
|
||||||
float alpha = Utils.clamp(1 - fadeinScale, 0, 1);
|
float alpha = Utils.clamp(1 - fadeinScale, 0, 1);
|
||||||
|
|
||||||
if (GameMod.HIDDEN.isActive()) {
|
if (GameMod.HIDDEN.isActive()) {
|
||||||
float fadeOutScale = -(float)(timeDiff-game.getApproachTime())/game.getDecayTime();
|
final int hiddenDecayTime = game.getHiddenDecayTime();
|
||||||
float fadeOutAlpha = Utils.clamp(1-fadeOutScale, 0, 1);
|
final int hiddenTimeDiff = game.getHiddenTimeDiff();
|
||||||
alpha = Math.min(alpha, fadeOutAlpha);
|
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;
|
float oldAlpha = Colors.WHITE_FADE.a;
|
||||||
|
|
|
@ -57,9 +57,6 @@ public class Slider implements GameObject {
|
||||||
/** The diameter of hit circles. */
|
/** The diameter of hit circles. */
|
||||||
private static float diameter;
|
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. */
|
/** The associated HitObject. */
|
||||||
private HitObject hitObject;
|
private HitObject hitObject;
|
||||||
|
|
||||||
|
@ -179,9 +176,11 @@ public class Slider implements GameObject {
|
||||||
@Override
|
@Override
|
||||||
public void draw(Graphics g, int trackPosition) {
|
public void draw(Graphics g, int trackPosition) {
|
||||||
int timeDiff = hitObject.getTime() - trackPosition;
|
int timeDiff = hitObject.getTime() - trackPosition;
|
||||||
float scale = timeDiff / (float) game.getApproachTime();
|
final int approachTime = game.getApproachTime();
|
||||||
float fadeinScale = (timeDiff - game.getApproachTime() + FADE_IN_TIME) / (float) FADE_IN_TIME;
|
final int fadeInTime = game.getFadeInTime();
|
||||||
|
float scale = timeDiff / (float) approachTime;
|
||||||
float approachScale = 1 + scale * 3;
|
float approachScale = 1 + scale * 3;
|
||||||
|
float fadeinScale = (timeDiff - approachTime + fadeInTime) / (float) fadeInTime;
|
||||||
float alpha = Utils.clamp(1 - fadeinScale, 0, 1);
|
float alpha = Utils.clamp(1 - fadeinScale, 0, 1);
|
||||||
boolean overlayAboveNumber = Options.getSkin().isHitCircleOverlayAboveNumber();
|
boolean overlayAboveNumber = Options.getSkin().isHitCircleOverlayAboveNumber();
|
||||||
|
|
||||||
|
@ -212,9 +211,12 @@ public class Slider implements GameObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (GameMod.HIDDEN.isActive()) {
|
if (GameMod.HIDDEN.isActive()) {
|
||||||
float fadeOutScale = -(float) (timeDiff - game.getApproachTime()) / game.getDecayTime();
|
final int hiddenDecayTime = game.getHiddenDecayTime();
|
||||||
float fadeOutAlpha = Utils.clamp(1 - fadeOutScale, 0, 1);
|
final int hiddenTimeDiff = game.getHiddenTimeDiff();
|
||||||
alpha = Math.min(alpha, fadeOutAlpha);
|
if (fadeinScale <= 0f && timeDiff < hiddenTimeDiff + hiddenDecayTime) {
|
||||||
|
float hiddenAlpha = (timeDiff < hiddenTimeDiff) ? 0f : (timeDiff - hiddenTimeDiff) / (float) hiddenDecayTime;
|
||||||
|
alpha = Math.min(alpha, hiddenAlpha);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (sliderClickedInitial)
|
if (sliderClickedInitial)
|
||||||
; // don't draw current combo number if already clicked
|
; // don't draw current combo number if already clicked
|
||||||
|
|
|
@ -51,9 +51,6 @@ public class Spinner implements GameObject {
|
||||||
/** The amount of time, in milliseconds, before another velocity is stored. */
|
/** The amount of time, in milliseconds, before another velocity is stored. */
|
||||||
private static final float DELTA_UPDATE_TIME = 1000 / 60f;
|
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) */
|
/** Angle mod multipliers: "auto" (477rpm), "spun out" (287rpm) */
|
||||||
private static final float
|
private static final float
|
||||||
AUTO_MULTIPLIER = 1 / 20f, // angle = 477/60f * delta/1000f * TWO_PI;
|
AUTO_MULTIPLIER = 1 / 20f, // angle = 477/60f * delta/1000f * TWO_PI;
|
||||||
|
@ -70,6 +67,9 @@ public class Spinner implements GameObject {
|
||||||
/** The associated HitObject. */
|
/** The associated HitObject. */
|
||||||
private HitObject hitObject;
|
private HitObject hitObject;
|
||||||
|
|
||||||
|
/** The associated Game object. */
|
||||||
|
private Game game;
|
||||||
|
|
||||||
/** The associated GameData object. */
|
/** The associated GameData object. */
|
||||||
private GameData data;
|
private GameData data;
|
||||||
|
|
||||||
|
@ -125,6 +125,7 @@ public class Spinner implements GameObject {
|
||||||
*/
|
*/
|
||||||
public Spinner(HitObject hitObject, Game game, GameData data) {
|
public Spinner(HitObject hitObject, Game game, GameData data) {
|
||||||
this.hitObject = hitObject;
|
this.hitObject = hitObject;
|
||||||
|
this.game = game;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -176,11 +177,12 @@ public class Spinner implements GameObject {
|
||||||
public void draw(Graphics g, int trackPosition) {
|
public void draw(Graphics g, int trackPosition) {
|
||||||
// only draw spinners shortly before start time
|
// only draw spinners shortly before start time
|
||||||
int timeDiff = hitObject.getTime() - trackPosition;
|
int timeDiff = hitObject.getTime() - trackPosition;
|
||||||
if (timeDiff - FADE_IN_TIME > 0)
|
final int fadeInTime = game.getFadeInTime();
|
||||||
|
if (timeDiff - fadeInTime > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
boolean spinnerComplete = (rotations >= rotationsNeeded);
|
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
|
// darken screen
|
||||||
if (Options.getSkin().isSpinnerFadePlayfield()) {
|
if (Options.getSkin().isSpinnerFadePlayfield()) {
|
||||||
|
|
|
@ -18,6 +18,25 @@
|
||||||
|
|
||||||
package itdelatrisu.opsu.states;
|
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.ErrorHandler;
|
||||||
import itdelatrisu.opsu.GameData;
|
import itdelatrisu.opsu.GameData;
|
||||||
import itdelatrisu.opsu.GameImage;
|
import itdelatrisu.opsu.GameImage;
|
||||||
|
@ -52,25 +71,6 @@ import itdelatrisu.opsu.ui.MenuButton;
|
||||||
import itdelatrisu.opsu.ui.UI;
|
import itdelatrisu.opsu.ui.UI;
|
||||||
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
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.
|
* "Game" state.
|
||||||
*/
|
*/
|
||||||
|
@ -119,9 +119,14 @@ public class Game extends BasicGameState {
|
||||||
/** Hit object approach time, in milliseconds. */
|
/** Hit object approach time, in milliseconds. */
|
||||||
private int approachTime;
|
private int approachTime;
|
||||||
|
|
||||||
/** Decay time for elements in "Hidden" mod, in milliseconds. */
|
/** The amount of time for hit objects to fade in, in milliseconds. */
|
||||||
//TODO: figure out actual formula for decay time
|
private int fadeInTime;
|
||||||
private int decayTime = 800;
|
|
||||||
|
/** 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). */
|
/** Time offsets for obtaining each hit result (indexed by HIT_* constants). */
|
||||||
private int[] hitResultOffset;
|
private int[] hitResultOffset;
|
||||||
|
@ -1480,6 +1485,12 @@ public class Game extends BasicGameState {
|
||||||
int diameter = (int) (104 - (circleSize * 8));
|
int diameter = (int) (104 - (circleSize * 8));
|
||||||
HitObject.setStackOffset(diameter * STACK_OFFSET_MODIFIER);
|
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
|
// initialize objects
|
||||||
Circle.init(container, circleSize);
|
Circle.init(container, circleSize);
|
||||||
Slider.init(container, circleSize, beatmap);
|
Slider.init(container, circleSize, beatmap);
|
||||||
|
@ -1487,12 +1498,6 @@ public class Game extends BasicGameState {
|
||||||
Curve.init(container.getWidth(), container.getHeight(), circleSize, (Options.isBeatmapSkinIgnored()) ?
|
Curve.init(container.getWidth(), container.getHeight(), circleSize, (Options.isBeatmapSkinIgnored()) ?
|
||||||
Options.getSkin().getSliderBorderColor() : beatmap.getSliderBorderColor());
|
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)
|
// overallDifficulty (hit result time offsets)
|
||||||
hitResultOffset = new int[GameData.HIT_MAX];
|
hitResultOffset = new int[GameData.HIT_MAX];
|
||||||
hitResultOffset[GameData.HIT_300] = (int) (78 - (overallDifficulty * 6));
|
hitResultOffset[GameData.HIT_300] = (int) (78 - (overallDifficulty * 6));
|
||||||
|
@ -1511,6 +1516,14 @@ public class Game extends BasicGameState {
|
||||||
|
|
||||||
// difficulty multiplier (scoring)
|
// difficulty multiplier (scoring)
|
||||||
data.calculateDifficultyMultiplier(beatmap.HPDrainRate, beatmap.circleSize, beatmap.overallDifficulty);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1534,10 +1547,21 @@ public class Game extends BasicGameState {
|
||||||
*/
|
*/
|
||||||
public int getApproachTime() { return approachTime; }
|
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.
|
* 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).
|
* Returns an array of hit result offset times, in milliseconds (indexed by GameData.HIT_* constants).
|
||||||
|
|
Loading…
Reference in New Issue
Block a user