diff --git a/src/itdelatrisu/opsu/GameData.java b/src/itdelatrisu/opsu/GameData.java index 39ba2ac1..04cf78c1 100644 --- a/src/itdelatrisu/opsu/GameData.java +++ b/src/itdelatrisu/opsu/GameData.java @@ -153,6 +153,27 @@ public class GameData { /** List of hit result objects associated with hit objects. */ private LinkedList hitResultList; + /** + * class to store Hit Error information. + * @author fluddokt + */ + class HitErrorInfo { + int time, x, y, timeDiff; + + public HitErrorInfo(int time, int x, int y, int timeDiff) { + this.time = time; + this.x = x; + this.y = y; + this.timeDiff = timeDiff; + } + + } + + /** + * List of stored Hit Error Info + */ + private LinkedList hitErrorList = new LinkedList(); + /** * Hit result helper class. */ @@ -225,6 +246,10 @@ public class GameData { /** Container dimensions. */ private int width, height; + /** Time offsets for obtaining each hit result (indexed by HIT_* constants). */ + private int[] hitResultOffset; + + /** * Constructor for gameplay. * @param width container width @@ -283,6 +308,7 @@ public class GameData { comboEnd = 0; comboBurstIndex = -1; scoreData = null; + hitErrorList.clear(); } /** @@ -364,6 +390,10 @@ public class GameData { public void setDifficulty(float difficulty) { this.difficulty = difficulty; } public float getDifficulty() { return difficulty; } + /** Sets the HitResultOffset */ + public void setHitResultOffset(int[] hitResultOffset) {this.hitResultOffset = hitResultOffset; } + + /** * Draws a number with defaultSymbols. * @param n the number to draw @@ -517,6 +547,50 @@ public class GameData { // combo count if (combo > 0) // 0 isn't a combo drawSymbolString(String.format("%dx", combo), 10, height - 10 - symbolHeight, 1.0f, false); + + //* + //Draw Hit Error bar + final int fadeDelay = 5000; + int hitErrorY = 30; + Iterator iter2 = hitErrorList.iterator(); + g.setColor(Color.black); + g.fillRect(width / 2f - 3 - hitResultOffset[HIT_50], + height - marginX - hitErrorY - 10, + hitResultOffset[HIT_50] * 2, + 20); + g.setColor(Utils.COLOR_LIGHT_ORANGE); + g.fillRect(width / 2f - 3 - hitResultOffset[HIT_50], + height - marginX - hitErrorY - 3, + hitResultOffset[HIT_50] * 2, + 6); + g.setColor(Utils.COLOR_LIGHT_GREEN); + g.fillRect(width / 2f - 3 - hitResultOffset[HIT_100], + height - marginX - hitErrorY - 3, + hitResultOffset[HIT_100] * 2, + 6); + g.setColor(Utils.COLOR_LIGHT_BLUE); + g.fillRect(width / 2f - 3 - hitResultOffset[HIT_300], + height- marginX - hitErrorY - 3, + hitResultOffset[HIT_300] * 2, + 6); + g.setColor(Color.white); + g.drawRect(width / 2f - 3, height - marginX - hitErrorY - 10, 6, 20); + while (iter2.hasNext()) { + HitErrorInfo info = iter2.next(); + int time = info.time; + if (Math.abs(info.timeDiff) < hitResultOffset[GameData.HIT_50] + && time + fadeDelay > trackPosition) { + float alpha = 1 - ((float) (trackPosition - time) / fadeDelay); + Color col = new Color(Color.white); + col.a = alpha; + g.setColor(col); + g.fillRect(width / 2 + info.timeDiff - 1, height - marginX + - hitErrorY - 10, 2, 20); + } else { + iter2.remove(); + } + } + //*/ } else { // grade Grade grade = getGrade(); @@ -528,6 +602,9 @@ public class GameData { ); } } + + + } /** @@ -649,6 +726,9 @@ public class GameData { float scale = 1f + ((trackPosition - hitResult.time) / (float) fadeDelay); Image scaledLighting = GameImage.LIGHTING.getImage().getScaledCopy(scale); Image scaledLighting1 = GameImage.LIGHTING1.getImage().getScaledCopy(scale); + scaledLighting.setAlpha(hitResult.alpha); + scaledLighting1.setAlpha(hitResult.alpha); + scaledLighting.draw(hitResult.x - (scaledLighting.getWidth() / 2f), hitResult.y - (scaledLighting.getHeight() / 2f), hitResult.color); scaledLighting1.draw(hitResult.x - (scaledLighting1.getWidth() / 2f), @@ -1008,4 +1088,15 @@ public class GameData { * @return true if gameplay, false if score viewing */ public boolean isGameplay() { return gameplay; } + + /** + * add a Hit Error Info to the list. + * @param time when it should have been hit + * @param x, y the location that it hit + * @param timeDiff the difference from the time from when it was actually hit + */ + public void addHitError(int time, int x, int y, int timeDiff) { + hitErrorList.add(new HitErrorInfo(time, x, y, timeDiff)); + } + } \ No newline at end of file diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index ef2a446e..bbf666ad 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -418,8 +418,10 @@ public class Options { RES_800_600 (800, 600), RES_1024_600 (1024, 600), RES_1024_768 (1024, 768), + RES_1280_720 (1280, 720), RES_1280_800 (1280, 800), RES_1280_960 (1280, 960), + RES_1280_1024 (1280, 1024), RES_1366_768 (1366, 768), RES_1440_900 (1440, 900), RES_1600_900 (1600, 900), @@ -428,7 +430,9 @@ public class Options { RES_1920_1080 (1920, 1080), RES_1920_1200 (1920, 1200), RES_2560_1440 (2560, 1440), - RES_2560_1600 (2560, 1600); + RES_2560_1600 (2560, 1600), + RES_3840_2160 (3840, 2160); + /** Screen dimensions. */ private int width, height; diff --git a/src/itdelatrisu/opsu/OsuFile.java b/src/itdelatrisu/opsu/OsuFile.java index 39d888f7..11a8c03d 100644 --- a/src/itdelatrisu/opsu/OsuFile.java +++ b/src/itdelatrisu/opsu/OsuFile.java @@ -268,14 +268,20 @@ public class OsuFile implements Comparable { bgImageMap.put(this, bgImage); } - // fit image to screen int swidth = width; int sheight = height; if (!stretch) { + // fit image to screen if (bgImage.getWidth() / (float) bgImage.getHeight() > width / (float) height) // x > y sheight = (int) (width * bgImage.getHeight() / (float) bgImage.getWidth()); else swidth = (int) (height * bgImage.getWidth() / (float) bgImage.getHeight()); + } else { + //fill image to screen while keeping aspect ratio + if (bgImage.getWidth() / (float) bgImage.getHeight() > width / (float) height) // x > y + swidth = (int) (height * bgImage.getWidth() / (float) bgImage.getHeight()); + else + sheight = (int) (width * bgImage.getHeight() / (float) bgImage.getWidth()); } bgImage = bgImage.getScaledCopy(swidth, sheight); diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index 25bfd245..3d25faba 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -79,7 +79,10 @@ public class Utils { COLOR_YELLOW_ALPHA = new Color(255, 255, 0, 0.4f), COLOR_WHITE_FADE = new Color(255, 255, 255, 1f), COLOR_RED_HOVER = new Color(255, 112, 112), - COLOR_GREEN = new Color(137, 201, 79); + COLOR_GREEN = new Color(137, 201, 79), + COLOR_LIGHT_ORANGE = new Color(255,192,128), + COLOR_LIGHT_GREEN = new Color(128,255,128), + COLOR_LIGHT_BLUE = new Color(128,128,255); /** The default map colors, used when a map does not provide custom colors. */ public static final Color[] DEFAULT_COMBO = { @@ -872,4 +875,20 @@ public class Utils { list.add(str); return list; } + + /** + * Clamps the value to be in between low and high + * @param val + * @param low + * @param high + * @return val clamped between low and high + * @author fluddokt (https://github.com/fluddokt) + */ + public static float clamp(float val, int low, int high) { + if(val < low) + return low; + if(val > high) + return high; + return val; + } } \ No newline at end of file diff --git a/src/itdelatrisu/opsu/objects/Circle.java b/src/itdelatrisu/opsu/objects/Circle.java index 9a49a3af..d9bf1dcf 100644 --- a/src/itdelatrisu/opsu/objects/Circle.java +++ b/src/itdelatrisu/opsu/objects/Circle.java @@ -55,7 +55,7 @@ public class Circle implements HitObject { * @param circleSize the map's circleSize value */ public static void init(GameContainer container, float circleSize) { - int diameter = (int) (96 - (circleSize * 8)); + int diameter = (int) (104 - (circleSize * 8)); diameter = (int) (diameter * OsuHitObject.getXMultiplier()); // convert from Osupixels (640x480) GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(diameter, diameter)); GameImage.HITCIRCLE_OVERLAY.setImage(GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(diameter, diameter)); @@ -83,15 +83,20 @@ public class Circle implements HitObject { int timeDiff = hitObject.getTime() - trackPosition; if (timeDiff >= 0) { - float x = hitObject.getX(), y = hitObject.getY(); - float approachScale = 1 + (timeDiff * 2f / game.getApproachTime()); - Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color); - float alpha = (approachScale > 3.3f) ? 0f : 1f - (approachScale - 1f) / 2.7f; float oldAlpha = color.a; + float x = hitObject.getX(), y = hitObject.getY(); + float scale = timeDiff / (float)game.getApproachTime(); + + float approachScale = 1 + scale * 3; + color.a = 1 - scale; + Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color); + + float alpha = Utils.clamp((1 - scale) * 2, 0, 1); color.a = alpha; Utils.COLOR_WHITE_FADE.a = alpha; - Utils.drawCentered(GameImage.HITCIRCLE_OVERLAY.getImage(), x, y, Utils.COLOR_WHITE_FADE); Utils.drawCentered(GameImage.HITCIRCLE.getImage(), x, y, color); + Utils.drawCentered(GameImage.HITCIRCLE_OVERLAY.getImage(), x, y, Utils.COLOR_WHITE_FADE); + color.a = oldAlpha; Utils.COLOR_WHITE_FADE.a = 1f; data.drawSymbolNumber(hitObject.getComboNumber(), x, y, @@ -105,8 +110,7 @@ public class Circle implements HitObject { * @return the hit result (GameData.HIT_* constants) */ private int hitResult(int time) { - int trackPosition = MusicController.getPosition(); - int timeDiff = Math.abs(trackPosition - time); + int timeDiff = Math.abs(time); int[] hitResultOffset = game.getHitResultOffsets(); int result = -1; @@ -128,8 +132,12 @@ public class Circle implements HitObject { double distance = Math.hypot(hitObject.getX() - x, hitObject.getY() - y); int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2; if (distance < circleRadius) { - int result = hitResult(hitObject.getTime()); + int trackPosition = MusicController.getPosition(); + int timeDiff = trackPosition - hitObject.getTime(); + int result = hitResult(timeDiff); + if (result > -1) { + data.addHitError(hitObject.getTime(), x, y, timeDiff); data.hitResult( hitObject.getTime(), result, hitObject.getX(), hitObject.getY(), diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java index 1e5de886..f22822dd 100644 --- a/src/itdelatrisu/opsu/objects/Slider.java +++ b/src/itdelatrisu/opsu/objects/Slider.java @@ -98,7 +98,7 @@ public class Slider implements HitObject { * @param osu the associated OsuFile object */ public static void init(GameContainer container, float circleSize, OsuFile osu) { - int diameter = (int) (96 - (circleSize * 8)); + int diameter = (int) (104 - (circleSize * 8)); diameter = (int) (diameter * OsuHitObject.getXMultiplier()); // convert from Osupixels (640x480) // slider ball @@ -110,7 +110,7 @@ public class Slider implements HitObject { sliderBallImages = new Image[]{ GameImage.SLIDER_BALL.getImage() }; for (int i = 0; i < sliderBallImages.length; i++) sliderBallImages[i] = sliderBallImages[i].getScaledCopy(diameter * 118 / 128, diameter * 118 / 128); - sliderBall = new Animation(sliderBallImages, 60); + sliderBall = new Animation(sliderBallImages, 30); GameImage.SLIDER_FOLLOWCIRCLE.setImage(GameImage.SLIDER_FOLLOWCIRCLE.getImage().getScaledCopy(diameter * 259 / 128, diameter * 259 / 128)); GameImage.REVERSEARROW.setImage(GameImage.REVERSEARROW.getImage().getScaledCopy(diameter, diameter)); @@ -143,16 +143,21 @@ public class Slider implements HitObject { @Override public void draw(int trackPosition, boolean currentObject, Graphics g) { - float x = hitObject.getX(), y = hitObject.getY(); int timeDiff = hitObject.getTime() - trackPosition; - - float approachScale = (timeDiff >= 0) ? 1 + (timeDiff * 2f / game.getApproachTime()) : 1f; - float alpha = (approachScale > 3.3f) ? 0f : 1f - (approachScale - 1f) / 2.7f; + float oldAlpha = color.a; float oldAlphaFade = Utils.COLOR_WHITE_FADE.a; - color.a = alpha; + + float x = hitObject.getX(), y = hitObject.getY(); + float scale = timeDiff / (float)game.getApproachTime(); + + float approachScale = 1 + scale*3 ; + float alpha = (1 - scale);//(approachScale > 3.3f) ? 0f : 1f - (approachScale - 1f) / 2.7f; + color.a = Utils.clamp(alpha* 0.5f, 0, 1); + + alpha = Utils.clamp(alpha, 0, 1); Utils.COLOR_WHITE_FADE.a = alpha; - + // curve curve.draw(); @@ -164,6 +169,8 @@ public class Slider implements HitObject { tick.drawCentered(c[0], c[1]); } } + + color.a = alpha; Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage(); Image hitCircle = GameImage.HITCIRCLE.getImage(); @@ -174,8 +181,8 @@ public class Slider implements HitObject { Utils.drawCentered(hitCircleOverlay, endPos[0], endPos[1], Utils.COLOR_WHITE_FADE); // start circle - Utils.drawCentered(hitCircleOverlay, x, y, Utils.COLOR_WHITE_FADE); Utils.drawCentered(hitCircle, x, y, color); + Utils.drawCentered(hitCircleOverlay, x, y, Utils.COLOR_WHITE_FADE); if (sliderClicked) ; // don't draw current combo number if already clicked else @@ -208,22 +215,29 @@ public class Slider implements HitObject { if (timeDiff >= 0) { // approach circle + color.a = 1 - scale; + Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color); } else { float[] c = curve.pointAt(getT(trackPosition, false)); float[] c2 = curve.pointAt(getT(trackPosition, false) + 0.01f); // slider ball + sliderBall.updateNoDraw();//TODO ..... I can't thing of anything else + //It might be better to make your own animation class or something + //also it might be better to update the animation based on the distance traveled Image sliderBallFrame = sliderBall.getCurrentFrame(); - float angle = (float) (Math.atan2(c2[1] - c[1], c2[0] - c[0]) * 180 / Math.PI); + float angle = (float) (Math.atan2(c2[1] - c[1], c2[0] - c[0]) * 180 / Math.PI) + + (currentRepeats % 2 == 1 ? 180 : 0); sliderBallFrame.setRotation(angle); sliderBallFrame.drawCentered(c[0], c[1]); - // follow circle if (followCircleActive) GameImage.SLIDER_FOLLOWCIRCLE.getImage().drawCentered(c[0], c[1]); } } + + /** * Calculates the slider hit result. @@ -275,6 +289,7 @@ public class Slider implements HitObject { //else not a hit if (result > -1) { + data.addHitError(hitObject.getTime(), x,y,trackPosition - hitObject.getTime()); sliderClicked = true; data.sliderTickResult(hitObject.getTime(), result, hitObject.getX(), hitObject.getY(), hitObject.getHitSoundType()); diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index a7689a0d..1d5163b6 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -878,6 +878,7 @@ public class Game extends BasicGameState { // HPDrainRate (health change), overallDifficulty (scoring) data.setDrainRate(HPDrainRate); data.setDifficulty(overallDifficulty); + data.setHitResultOffset(hitResultOffset); } /**