From 40933cfa8606e28c6be74d7bfb5587608bfaf234 Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Wed, 18 Mar 2015 22:20:37 -0400 Subject: [PATCH] Implemented follow points. Added new followpoint image from "Vocaloid Project Skin" by noClue. Signed-off-by: Jeffrey Han --- CREDITS.md | 1 + res/followpoint.png | Bin 4275 -> 592 bytes src/itdelatrisu/opsu/GameImage.java | 1 + src/itdelatrisu/opsu/states/Game.java | 62 ++++++++++++++++++++++++-- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 9bb6a463..9f622802 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -15,6 +15,7 @@ The images included in opsu! belong to their respective authors. * Misaki Louize - "Yukino II" * Alic1a - "AL's IA -Blue-" * Xiaounlimited - "Nexus Ivory" +* noClue - "Vocaloid Project Skin [LIGHT]" * pictuga - "osu! web" * sherrie__fay * kouyang diff --git a/res/followpoint.png b/res/followpoint.png index 45a9f5aecbccd8e2df8e7ba46c2ad213a53b9e83..403d83300f20278889530ddb9c05874536bf87bf 100644 GIT binary patch literal 592 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 VNY6xg69WJM002ovPDHLkV1jrEJnsMi diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index e8de7423..12f9a814 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -75,6 +75,7 @@ public enum GameImage { }, HITCIRCLE_SELECT ("hitcircleselect", "png"), UNRANKED ("play-unranked", "png"), + FOLLOWPOINT ("followpoint", "png"), PLAYFIELD ("playfield", "png|jpg", false, false) { @Override protected Image process_sub(Image img, int w, int h) { diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index 6a6e11b1..7923f102 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -1178,15 +1178,71 @@ public class Game extends BasicGameState { } /** - * Draws hit objects and hit results. + * Draws hit objects, hit results, and follow points. * @param g the graphics context * @param trackPosition the track position */ private void drawHitObjects(Graphics g, int trackPosition) { + // include previous object in follow points + int lastObjectIndex = -1; + if (objectIndex > 0 && objectIndex < osu.objects.length && + trackPosition < osu.objects[objectIndex].getTime() && !osu.objects[objectIndex - 1].isSpinner()) + lastObjectIndex = objectIndex - 1; + // draw hit objects in reverse order, or else overlapping objects are unreadable Stack stack = new Stack(); - for (int i = objectIndex; i < hitObjects.length && osu.objects[i].getTime() < trackPosition + approachTime; i++) - stack.add(i); + for (int index = objectIndex; index < hitObjects.length && osu.objects[index].getTime() < trackPosition + approachTime; index++) { + stack.add(index); + + // draw follow points + if (osu.objects[index].isSpinner()) { + lastObjectIndex = -1; + continue; + } + if (lastObjectIndex != -1 && !osu.objects[index].isNewCombo()) { + // calculate points + final int followPointInterval = container.getHeight() / 14; + int lastObjectEndTime = hitObjects[lastObjectIndex].getEndTime() + 1; + int objectStartTime = osu.objects[index].getTime(); + float[] startXY = hitObjects[lastObjectIndex].getPointAt(lastObjectEndTime); + float[] endXY = hitObjects[index].getPointAt(objectStartTime); + float xDiff = endXY[0] - startXY[0]; + float yDiff = endXY[1] - startXY[1]; + float dist = (float) Math.hypot(xDiff, yDiff); + int numPoints = (int) ((dist - GameImage.HITCIRCLE.getImage().getWidth()) / followPointInterval); + if (numPoints > 0) { + // set the image angle + Image followPoint = GameImage.FOLLOWPOINT.getImage(); + float angle = (float) Math.toDegrees(Math.atan2(yDiff, xDiff)); + followPoint.setRotation(angle); + + // draw points + float progress = 0f; + if (lastObjectIndex < objectIndex) + progress = (float) (trackPosition - lastObjectEndTime) / (objectStartTime - lastObjectEndTime); + float step = 1f / (numPoints + 1); + float t = step; + for (int i = 0; i < numPoints; i++) { + float x = startXY[0] + xDiff * t; + float y = startXY[1] + yDiff * t; + float nextT = t + step; + if (lastObjectIndex < objectIndex) { // fade the previous trail + if (progress < nextT) { + if (progress > t) + followPoint.setAlpha(1f - ((progress - t + step) / (step * 2f))); + else if (progress > t - step) + followPoint.setAlpha(1f - ((progress - (t - step)) / (step * 2f))); + followPoint.draw(x, y); + followPoint.setAlpha(1f); + } + } else + followPoint.draw(x, y); + t = nextT; + } + } + } + lastObjectIndex = index; + } while (!stack.isEmpty()) hitObjects[stack.pop()].draw(g, trackPosition);