Follow-up to #211: fix slider hit results not appearing; code cleanup.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2016-12-19 16:33:20 -05:00
parent d111fc0efe
commit e161d0180e
5 changed files with 106 additions and 82 deletions

View File

@ -140,23 +140,23 @@ public class GameData {
/** Hit result types. */
public static final int
HIT_MISS = 0,
HIT_50 = 1,
HIT_100 = 2,
HIT_300 = 3,
HIT_100K = 4, // 100-Katu
HIT_300K = 5, // 300-Katu
HIT_300G = 6, // Geki
HIT_SLIDER10 = 7,
HIT_SLIDER30 = 8,
HIT_MAX = 9, // not a hit result
HIT_SLIDER_REPEAT = 10, // not a hit result
HIT_MISS = 0,
HIT_50 = 1,
HIT_100 = 2,
HIT_300 = 3,
HIT_100K = 4, // 100-Katu
HIT_300K = 5, // 300-Katu
HIT_300G = 6, // Geki
HIT_SLIDER10 = 7,
HIT_SLIDER30 = 8,
HIT_MAX = 9, // not a hit result
HIT_SLIDER_REPEAT = 10, // not a hit result
HIT_ANIMATION_RESULT = 11; // not a hit result
/** Hit result-related images (indexed by HIT_* constants). */
/** Hit result-related images (indexed by HIT_* constants to HIT_MAX). */
private Image[] hitResults;
/** Counts of each hit result so far. */
/** Counts of each hit result so far (indexed by HIT_* constants to HIT_MAX). */
private int[] hitResultCount;
/** Total objects including slider hits/ticks (for determining Full Combo status). */
@ -192,7 +192,7 @@ public class GameData {
/** Current x coordinate of the combo burst image (for sliding animation). */
private float comboBurstX;
/** Time offsets for obtaining each hit result (indexed by HIT_* constants). */
/** Time offsets for obtaining each hit result (indexed by HIT_* constants to HIT_MAX). */
private int[] hitResultOffset;
/** List of hit result objects associated with hit objects. */
@ -232,7 +232,7 @@ public class GameData {
private LinkedBlockingDeque<HitErrorInfo> hitErrorList;
/** Hit object types, used for drawing results. */
public enum HitObjectType { CIRCLE, SLIDERTICK, SLIDER_FIRST, SLIDER_CURVE, SLIDER_LAST, SPINNER }
public enum HitObjectType { CIRCLE, SLIDERTICK, SLIDER_FIRST, SLIDER_LAST, SPINNER }
/** Hit result helper class. */
private class HitObjectResult {
@ -892,16 +892,17 @@ public class GameData {
lighting.drawCentered(hitResult.x, hitResult.y, hitResult.color);
}
// hit animations, only draw when the 'hidden' mod is not enabled
// hit animations (only draw when the "Hidden" mod is not enabled)
if (!GameMod.HIDDEN.isActive()) {
drawHitAnimations(hitResult, trackPosition);
}
// hit result
if (!hitResult.hideResult && (
hitResult.hitResultType == HitObjectType.CIRCLE ||
hitResult.hitResultType == HitObjectType.SPINNER ||
hitResult.curve != null)) {
hitResult.hitResultType == HitObjectType.CIRCLE ||
hitResult.hitResultType == HitObjectType.SLIDER_FIRST ||
hitResult.hitResultType == HitObjectType.SLIDER_LAST ||
hitResult.hitResultType == HitObjectType.SPINNER)) {
float scaleProgress = AnimationEquation.IN_OUT_BOUNCE.calc(
(float) Utils.clamp(trackPosition - hitResult.time, 0, HITCIRCLE_TEXT_BOUNCE_TIME) / HITCIRCLE_TEXT_BOUNCE_TIME);
float scale = 1f + (HITCIRCLE_TEXT_ANIM_SCALE - 1f) * scaleProgress;
@ -923,43 +924,42 @@ public class GameData {
}
/**
* Draw the hit animations: circles, reversearrows, slider curves fading out and/or expanding
* @param hitResult the hitresult which holds information about the kind of animation to draw
* Draw the hit animations:
* circles, reverse arrows, slider curves (fading out and/or expanding).
* @param hitResult the hit result
* @param trackPosition the current track position (in ms)
*/
private void drawHitAnimations(HitObjectResult hitResult, int trackPosition) {
if (hitResult.hitResultType == HitObjectType.SLIDER_CURVE && hitResult.curve != null) {
// fade out slider curve
if (hitResult.result != HIT_SLIDER_REPEAT && hitResult.curve != null) {
float progress = AnimationEquation.OUT_CUBIC.calc(
(float) Utils.clamp(trackPosition - hitResult.time, 0, HITCIRCLE_FADE_TIME) / HITCIRCLE_FADE_TIME);
float alpha = 1f - progress;
// slider curve
float oldWhiteAlpha = Colors.WHITE_FADE.a;
float oldColorAlpha = hitResult.color.a;
Colors.WHITE_FADE.a = alpha;
hitResult.color.a = alpha;
Colors.WHITE_FADE.a = hitResult.color.a = alpha;
hitResult.curve.draw(hitResult.color);
Colors.WHITE_FADE.a = oldWhiteAlpha;
hitResult.color.a = oldColorAlpha;
return;
}
// miss, don't draw an animation
if (hitResult.result == HIT_MISS) {
return;
}
if (hitResult.hitResultType != HitObjectType.CIRCLE
&& hitResult.hitResultType != HitObjectType.SLIDER_FIRST
&& hitResult.hitResultType != HitObjectType.SLIDER_LAST) {
// not a circle?
if (hitResult.hitResultType != HitObjectType.CIRCLE &&
hitResult.hitResultType != HitObjectType.SLIDER_FIRST &&
hitResult.hitResultType != HitObjectType.SLIDER_LAST) {
return;
}
// circles
// hit circles
float progress = AnimationEquation.OUT_CUBIC.calc(
(float) Utils.clamp(trackPosition - hitResult.time, 0, HITCIRCLE_FADE_TIME) / HITCIRCLE_FADE_TIME);
float scale = (!hitResult.expand) ? 1f : 1f + (HITCIRCLE_ANIM_SCALE - 1f) * progress;
float alpha = 1f - progress;
if (hitResult.result == HIT_SLIDER_REPEAT) {
// repeats
Image scaledRepeat = GameImage.REVERSEARROW.getImage().getScaledCopy(scale);
@ -973,7 +973,6 @@ public class GameData {
scaledRepeat.rotate(ang);
scaledRepeat.drawCentered(hitResult.x, hitResult.y, hitResult.color);
}
// hit circles
Image scaledHitCircle = GameImage.HITCIRCLE.getImage().getScaledCopy(scale);
Image scaledHitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(scale);
scaledHitCircle.setAlpha(alpha);
@ -1210,15 +1209,28 @@ public class GameData {
health = 0f;
}
public void sendRepeatSliderResult(int time, float x, float y, Color color, Curve curve, HitObjectType type) {
/**
* Handles a slider repeat result (animation only: arrow).
* @param time the repeat time
* @param x the x coordinate
* @param y the y coordinate
* @param color the arrow color
* @param curve the slider curve
* @param type the hit object type
*/
public void sendSliderRepeatResult(int time, float x, float y, Color color, Curve curve, HitObjectType type) {
hitResultList.add(new HitObjectResult(time, HIT_SLIDER_REPEAT, x, y, color, type, curve, true, true));
}
public void sendSliderCurveResult(int time, Color color, Curve curve) {
hitResultList.add(new HitObjectResult(time, HIT_SLIDER_REPEAT, 0f, 0f, color, HitObjectType.SLIDER_CURVE, curve, true, true));
}
public void sendAnimationResult(int time, float x, float y, Color color, boolean expand) {
/**
* Handles a slider start result (animation only: initial circle).
* @param time the hit time
* @param x the x coordinate
* @param y the y coordinate
* @param color the slider color
* @param expand whether or not the hit result animation should expand
*/
public void sendSliderStartResult(int time, float x, float y, Color color, boolean expand) {
hitResultList.add(new HitObjectResult(time, HIT_ANIMATION_RESULT, x, y, color, HitObjectType.CIRCLE, null, expand, true));
}
@ -1231,7 +1243,7 @@ public class GameData {
* @param hitObject the hit object
* @param repeat the current repeat number
*/
public void sliderTickResult(int time, int result, float x, float y, HitObject hitObject, int repeat) {
public void sendSliderTickResult(int time, int result, float x, float y, HitObject hitObject, int repeat) {
int hitValue = 0;
switch (result) {
case HIT_SLIDER30:
@ -1412,11 +1424,12 @@ public class GameData {
* @param curve the slider curve (or null if not applicable)
* @param sliderHeldToEnd whether or not the slider was held to the end (if applicable)
*/
public void hitResult(int time, int result, float x, float y, Color color,
boolean end, HitObject hitObject, HitObjectType hitResultType,
boolean expand, int repeat, Curve curve, boolean sliderHeldToEnd) {
int hitResult = handleHitResult(time, result, x, y, color, end, hitObject,
hitResultType, repeat, (curve != null && !sliderHeldToEnd));
public void sendHitResult(
int time, int result, float x, float y, Color color,
boolean end, HitObject hitObject, HitObjectType hitResultType,
boolean expand, int repeat, Curve curve, boolean sliderHeldToEnd
) {
int hitResult = handleHitResult(time, result, x, y, color, end, hitObject, hitResultType, repeat, (curve != null && !sliderHeldToEnd));
if (hitResult == HIT_MISS && (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()))
return; // "relax" and "autopilot" mods: hide misses

View File

@ -63,6 +63,7 @@ import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;
import org.newdawn.slick.state.StateBasedGame;
@ -157,6 +158,14 @@ public class Utils {
anim.draw(x - (anim.getWidth() / 2f), y - (anim.getHeight() / 2f));
}
/**
* Returns the luminance of a color.
* @param c the color
*/
public static float getLuminance(Color c) {
return 0.299f*c.r + 0.587f*c.g + 0.114f*c.b;
}
/**
* Clamps a value between a lower and upper bound.
* @param val the value to clamp

View File

@ -156,7 +156,7 @@ public class Circle implements GameObject {
if (result > -1) {
data.addHitError(hitObject.getTime(), x, y, timeDiff);
data.hitResult(trackPosition, result, this.x, this.y, color, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false);
data.sendHitResult(trackPosition, result, this.x, this.y, color, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false);
return true;
}
}
@ -172,17 +172,17 @@ public class Circle implements GameObject {
if (trackPosition > time + hitResultOffset[GameData.HIT_50]) {
if (isAutoMod) // "auto" mod: catch any missed notes due to lag
data.hitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false);
data.sendHitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false);
else // no more points can be scored, so send a miss
data.hitResult(trackPosition, GameData.HIT_MISS, x, y, null, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false);
data.sendHitResult(trackPosition, GameData.HIT_MISS, x, y, null, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false);
return true;
}
// "auto" mod: send a perfect hit result
else if (isAutoMod) {
if (Math.abs(trackPosition - time) < hitResultOffset[GameData.HIT_300]) {
data.hitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false);
data.sendHitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false);
return true;
}
}

View File

@ -180,7 +180,7 @@ public class Slider implements GameObject {
@Override
public void draw(Graphics g, int trackPosition) {
int timeDiff = hitObject.getTime() - trackPosition;
final int repeats = hitObject.getRepeatCount();
final int repeatCount = hitObject.getRepeatCount();
final int approachTime = game.getApproachTime();
final int fadeInTime = game.getFadeInTime();
float scale = timeDiff / (float) approachTime;
@ -198,7 +198,7 @@ public class Slider implements GameObject {
float oldWhiteFadeAlpha = Colors.WHITE_FADE.a;
float sliderAlpha = 1f;
if (GameMod.HIDDEN.isActive() && trackPosition > hitObject.getTime()) {
// "hidden" mod: fade out sliders
// "Hidden" mod: fade out sliders
Colors.WHITE_FADE.a = color.a = sliderAlpha =
Math.max(0f, 1f - ((float) (trackPosition - hitObject.getTime()) / (getEndTime() - hitObject.getTime())) * 1.05f);
}
@ -206,8 +206,8 @@ public class Slider implements GameObject {
float curveInterval = Options.isSliderSnaking() ? alpha : 1f;
curve.draw(color,curveInterval);
// end circle, only draw if ball still has to go there
if (curveInterval == 1f && currentRepeats < repeats - (repeats % 2 == 0 ? 1 : 0)) {
// end circle (only draw if ball still has to go there)
if (curveInterval == 1f && currentRepeats < repeatCount - (repeatCount % 2 == 0 ? 1 : 0)) {
Color circleColor = new Color(color);
Color overlayColor = new Color(Colors.WHITE_FADE);
if (currentRepeats == 0) {
@ -233,7 +233,7 @@ public class Slider implements GameObject {
}
// start circle, only draw if ball still has to go there
if (!sliderClickedInitial || currentRepeats < repeats - (repeats % 2 == 1 ? 1 : 0)) {
if (!sliderClickedInitial || currentRepeats < repeatCount - (repeatCount % 2 == 1 ? 1 : 0)) {
hitCircle.drawCentered(x, y, firstCircleColor);
if (!overlayAboveNumber || sliderClickedInitial)
hitCircleOverlay.drawCentered(x, y, startCircleOverlayColor);
@ -256,7 +256,7 @@ public class Slider implements GameObject {
}
}
// draw combonumber and overlay if not initially clicked
// draw combo number and overlay if not initially clicked
if (!sliderClickedInitial) {
data.drawSymbolNumber(hitObject.getComboNumber(), x, y,
hitCircle.getWidth() * 0.40f / data.getDefaultSymbolImage(0).getHeight(), alpha);
@ -269,11 +269,11 @@ public class Slider implements GameObject {
// repeats
if (curveInterval == 1.0f) {
for (int tcurRepeat = currentRepeats; tcurRepeat <= currentRepeats + 1 && tcurRepeat < repeats - 1; tcurRepeat++) {
for (int tcurRepeat = currentRepeats; tcurRepeat <= currentRepeats + 1 && tcurRepeat < repeatCount - 1; tcurRepeat++) {
Image arrow = GameImage.REVERSEARROW.getImage();
// bouncing animation
//arrow = arrow.getScaledCopy((float) (1 + 0.2d * ((trackPosition + sliderTime * tcurRepeat) % 292) / 292));
float colorLuminance = 0.299f*color.r + 0.587f*color.g + 0.114f*color.b;
float colorLuminance = Utils.getLuminance(color);
Color arrowColor = colorLuminance < 0.8f ? Color.white : Color.black;
if (tcurRepeat == 0) {
arrow.setAlpha(Options.isSliderSnaking() ? decorationsAlpha : 1f);
@ -469,19 +469,21 @@ public class Slider implements GameObject {
float cx, cy;
HitObjectType type;
if (currentRepeats % 2 == 0) { // last circle
if (currentRepeats % 2 == 0) {
// last circle
Vec2f lastPos = curve.pointAt(1);
cx = lastPos.x;
cy = lastPos.y;
type = HitObjectType.SLIDER_LAST;
} else { // first circle
} else {
// first circle
cx = x;
cy = y;
type = HitObjectType.SLIDER_FIRST;
}
data.hitResult(hitObject.getTime() + (int) sliderTimeTotal, result,
data.sendHitResult(hitObject.getTime() + (int) sliderTimeTotal, result,
cx, cy, color, comboEnd, hitObject, type, sliderHeldToEnd,
currentRepeats + 1, null, sliderHeldToEnd);
currentRepeats + 1, curve, sliderHeldToEnd);
return result;
}
@ -500,17 +502,17 @@ public class Slider implements GameObject {
if (timeDiff < hitResultOffset[GameData.HIT_50]) {
result = GameData.HIT_SLIDER30;
ticksHit++;
data.sendAnimationResult(trackPosition, this.x, this.y, color, true);
data.sendSliderStartResult(trackPosition, this.x, this.y, color, true);
} else if (timeDiff < hitResultOffset[GameData.HIT_MISS]) {
result = GameData.HIT_MISS;
data.sendAnimationResult(trackPosition, this.x, this.y, color, false);
data.sendSliderStartResult(trackPosition, this.x, this.y, color, false);
}
//else not a hit
if (result > -1) {
data.addHitError(hitObject.getTime(), x,y,trackPosition - hitObject.getTime());
sliderClickedInitial = true;
data.sliderTickResult(hitObject.getTime(), result, this.x, this.y, hitObject, currentRepeats);
data.sendSliderTickResult(hitObject.getTime(), result, this.x, this.y, hitObject, currentRepeats);
return true;
}
}
@ -531,11 +533,11 @@ public class Slider implements GameObject {
sliderClickedInitial = true;
if (isAutoMod) { // "auto" mod: catch any missed notes due to lag
ticksHit++;
data.sliderTickResult(time, GameData.HIT_SLIDER30, x, y, hitObject, currentRepeats);
data.sendAnimationResult(time, x, y, color, true);
data.sendSliderTickResult(time, GameData.HIT_SLIDER30, x, y, hitObject, currentRepeats);
data.sendSliderStartResult(time, x, y, color, true);
} else {
data.sliderTickResult(time, GameData.HIT_MISS, x, y, hitObject, currentRepeats);
data.sendAnimationResult(trackPosition, x, y, color, false);
data.sendSliderTickResult(time, GameData.HIT_MISS, x, y, hitObject, currentRepeats);
data.sendSliderStartResult(trackPosition, x, y, color, false);
}
}
@ -544,8 +546,8 @@ public class Slider implements GameObject {
if (Math.abs(trackPosition - time) < hitResultOffset[GameData.HIT_300]) {
ticksHit++;
sliderClickedInitial = true;
data.sliderTickResult(time, GameData.HIT_SLIDER30, x, y, hitObject, currentRepeats);
data.sendAnimationResult(time, x, y, color, true);
data.sendSliderTickResult(time, GameData.HIT_SLIDER30, x, y, hitObject, currentRepeats);
data.sendSliderStartResult(time, x, y, color, true);
}
}
@ -578,8 +580,6 @@ public class Slider implements GameObject {
// calculate and send slider result
hitResult();
// send 'curve fade out' hit result
data.sendSliderCurveResult(getEndTime(), color, curve);
return true;
}
@ -629,29 +629,31 @@ public class Slider implements GameObject {
HitObjectType type;
float posX, posY;
if (currentRepeats % 2 > 0) { // last circle
if (currentRepeats % 2 > 0) {
// last circle
type = HitObjectType.SLIDER_LAST;
Vec2f endPos = curve.pointAt(1f);
posX = endPos.x;
posY = endPos.y;
} else { // first circle
} else {
// first circle
type = HitObjectType.SLIDER_FIRST;
posX = this.x;
posY = this.y;
}
data.sliderTickResult(trackPosition, GameData.HIT_SLIDER30,
data.sendSliderTickResult(trackPosition, GameData.HIT_SLIDER30,
posX, posY, hitObject, currentRepeats);
// send hit result, to fade out reversearrow
float colorLuminance = 0.299f*color.r + 0.587f*color.g + 0.114f*color.b;
// fade out reverse arrow
float colorLuminance = Utils.getLuminance(color);
Color arrowColor = colorLuminance < 0.8f ? Color.white : Color.black;
data.sendRepeatSliderResult(trackPosition, posX, posY, arrowColor, curve, type);
data.sendSliderRepeatResult(trackPosition, posX, posY, arrowColor, curve, type);
}
// held during new tick
if (isNewTick) {
ticksHit++;
data.sliderTickResult(trackPosition, GameData.HIT_SLIDER10,
data.sendSliderTickResult(trackPosition, GameData.HIT_SLIDER10,
c.x, c.y, hitObject, currentRepeats);
}
@ -662,9 +664,9 @@ public class Slider implements GameObject {
followCircleActive = false;
if (isNewRepeat)
data.sliderTickResult(trackPosition, GameData.HIT_MISS, 0, 0, hitObject, currentRepeats);
data.sendSliderTickResult(trackPosition, GameData.HIT_MISS, 0, 0, hitObject, currentRepeats);
if (isNewTick)
data.sliderTickResult(trackPosition, GameData.HIT_MISS, 0, 0, hitObject, currentRepeats);
data.sendSliderTickResult(trackPosition, GameData.HIT_MISS, 0, 0, hitObject, currentRepeats);
}
return false;

View File

@ -252,7 +252,7 @@ public class Spinner implements GameObject {
else
result = GameData.HIT_MISS;
data.hitResult(hitObject.getEndTime(), result, width / 2, height / 2,
data.sendHitResult(hitObject.getEndTime(), result, width / 2, height / 2,
Color.transparent, true, hitObject, HitObjectType.SPINNER, true, 0, null, false);
return result;
}