From 0ae010ef5b3c72116c28ced02e0285d4ed215be2 Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Fri, 3 Feb 2017 19:13:36 -0500 Subject: [PATCH] Fixed remaining issues involving missed game objects due to overlap. - 2B maps are now playable. Note that "Auto" mode will still count "perfect" circles/sliders/spinners even though it obviously misses them. - Combo counts for normal (non-2B) maps now finally match osu!, from what I can tell. (Turns out this wasn't the slider issue I had initially thought.) Signed-off-by: Jeffrey Han --- src/itdelatrisu/opsu/objects/Circle.java | 2 +- src/itdelatrisu/opsu/objects/DummyObject.java | 2 +- src/itdelatrisu/opsu/objects/GameObject.java | 3 +- src/itdelatrisu/opsu/objects/Slider.java | 7 +- src/itdelatrisu/opsu/objects/Spinner.java | 4 +- src/itdelatrisu/opsu/states/Game.java | 72 +++++++++++++++---- 6 files changed, 68 insertions(+), 22 deletions(-) diff --git a/src/itdelatrisu/opsu/objects/Circle.java b/src/itdelatrisu/opsu/objects/Circle.java index 4f478c18..05e29237 100644 --- a/src/itdelatrisu/opsu/objects/Circle.java +++ b/src/itdelatrisu/opsu/objects/Circle.java @@ -164,7 +164,7 @@ public boolean mousePressed(int x, int y, int trackPosition) { } @Override - public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) { + public boolean update(int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) { int time = hitObject.getTime(); int[] hitResultOffset = game.getHitResultOffsets(); diff --git a/src/itdelatrisu/opsu/objects/DummyObject.java b/src/itdelatrisu/opsu/objects/DummyObject.java index f8472543..146f8e8e 100644 --- a/src/itdelatrisu/opsu/objects/DummyObject.java +++ b/src/itdelatrisu/opsu/objects/DummyObject.java @@ -46,7 +46,7 @@ public DummyObject(HitObject hitObject) { public void draw(Graphics g, int trackPosition) {} @Override - public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) { + public boolean update(int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) { return (trackPosition > hitObject.getTime()); } diff --git a/src/itdelatrisu/opsu/objects/GameObject.java b/src/itdelatrisu/opsu/objects/GameObject.java index 0e496de9..43ca764e 100644 --- a/src/itdelatrisu/opsu/objects/GameObject.java +++ b/src/itdelatrisu/opsu/objects/GameObject.java @@ -35,7 +35,6 @@ public interface GameObject { /** * Updates the hit object. - * @param overlap true if the next object's start time has already passed * @param delta the delta interval since the last call * @param mouseX the x coordinate of the mouse * @param mouseY the y coordinate of the mouse @@ -43,7 +42,7 @@ public interface GameObject { * @param trackPosition the track position * @return true if object ended */ - public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition); + public boolean update(int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition); /** * Processes a mouse click. diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java index b0ccba8c..96a75acd 100644 --- a/src/itdelatrisu/opsu/objects/Slider.java +++ b/src/itdelatrisu/opsu/objects/Slider.java @@ -541,7 +541,7 @@ public boolean mousePressed(int x, int y, int trackPosition) { } @Override - public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) { + public boolean update(int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) { int repeatCount = hitObject.getRepeatCount(); int[] hitResultOffset = game.getHitResultOffsets(); boolean isAutoMod = GameMod.AUTO.isActive(); @@ -582,8 +582,9 @@ else if (GameMod.RELAX.isActive() && trackPosition >= time) initialExpand.update(delta); releaseExpand.update(delta); - // TODO: Some ticks/repeats get missed if delta updates skip past them. - // The code tries to catch this, but doesn't completely work... + // NOTE: + // The loops below are used to catch any missed ticks/repeats if slower + // delta updates skip past them. // repeats int newRepeats = 0; diff --git a/src/itdelatrisu/opsu/objects/Spinner.java b/src/itdelatrisu/opsu/objects/Spinner.java index 129e2958..3976881c 100644 --- a/src/itdelatrisu/opsu/objects/Spinner.java +++ b/src/itdelatrisu/opsu/objects/Spinner.java @@ -264,9 +264,9 @@ public boolean mousePressed(int x, int y, int trackPosition) { } @Override - public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) { + public boolean update(int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) { // end of spinner - if (overlap || trackPosition > hitObject.getEndTime()) { + if (trackPosition > hitObject.getEndTime()) { hitResult(); return true; } diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index a85742f0..b41c2ee3 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -63,7 +63,9 @@ import java.io.File; import java.io.IOException; import java.util.IdentityHashMap; +import java.util.Iterator; import java.util.LinkedList; +import java.util.List; import java.util.Stack; import org.lwjgl.input.Keyboard; @@ -140,6 +142,9 @@ public enum Restart { /** The map's game objects, indexed by objectIndex. */ private GameObject[] gameObjects; + /** Any passed, unfinished hit object indices before objectIndex. */ + private List passedObjects; + /** Delay time, in milliseconds, before song starts. */ private int leadInTime; @@ -578,7 +583,7 @@ else if (breakIndex > 1) { skipButton.draw(); // show retries - if (retries >= 2 && timeDiff >= -1000) { + if (objectIndex == 0 && retries >= 2 && timeDiff >= -1000) { int retryHeight = Math.max( GameImage.SCOREBAR_BG.getImage().getHeight(), GameImage.SCOREBAR_KI.getImage().getHeight() @@ -905,10 +910,16 @@ else if (!gameFinished) { */ private void updateGame(int mouseX, int mouseY, int delta, int trackPosition, int keys) { // map complete! - if (objectIndex >= gameObjects.length || (MusicController.trackEnded() && objectIndex > 0)) { - // track ended before last object was processed: force a hit result - if (MusicController.trackEnded() && objectIndex < gameObjects.length) - gameObjects[objectIndex].update(true, delta, mouseX, mouseY, false, trackPosition); + if (!hasMoreObjects() || (MusicController.trackEnded() && objectIndex > 0)) { + // track ended before last object(s) was processed: force a hit result + if (MusicController.trackEnded() && hasMoreObjects()) { + for (int index : passedObjects) + gameObjects[index].update(delta, mouseX, mouseY, false, trackPosition); + passedObjects.clear(); + for (int i = objectIndex; i < gameObjects.length; i++) + gameObjects[i].update(delta, mouseX, mouseY, false, trackPosition); + objectIndex = gameObjects.length; + } // save score and replay if (!checkpointLoaded) { @@ -1035,17 +1046,32 @@ else if (replayFrames != null) { // don't process hit results when already lost if (restart != Restart.LOSE) { - // update objects (loop over any skipped indexes) boolean keyPressed = keys != ReplayFrame.KEY_NONE; + + // update passed objects + Iterator iter = passedObjects.iterator(); + while (iter.hasNext()) { + int index = iter.next(); + if (gameObjects[index].update(delta, mouseX, mouseY, keyPressed, trackPosition)) + iter.remove(); + } + + // update objects (loop over any skipped indexes) while (objectIndex < gameObjects.length && trackPosition > beatmap.objects[objectIndex].getTime()) { // check if we've already passed the next object's start time - boolean overlap = (objectIndex + 1 < gameObjects.length && - trackPosition > beatmap.objects[objectIndex + 1].getTime() - hitResultOffset[GameData.HIT_50]); + boolean overlap = + (objectIndex + 1 < gameObjects.length && + trackPosition > beatmap.objects[objectIndex + 1].getTime() - hitResultOffset[GameData.HIT_50]); // update hit object and check completion status - if (gameObjects[objectIndex].update(overlap, delta, mouseX, mouseY, keyPressed, trackPosition)) - objectIndex++; // done, so increment object index - else + if (gameObjects[objectIndex].update(delta, mouseX, mouseY, keyPressed, trackPosition)) { + // done, so increment object index + objectIndex++; + } else if (overlap) { + // overlap, so save the current object and increment object index + passedObjects.add(objectIndex); + objectIndex++; + } else break; } } @@ -1585,13 +1611,27 @@ private void drawHitObjects(Graphics g, int trackPosition) { // get hit objects in reverse order, or else overlapping objects are unreadable Stack stack = new Stack(); + int spinnerIndex = -1; // draw spinner first (assume there can only be 1...) + for (int index : passedObjects) { + if (beatmap.objects[index].isSpinner()) { + if (spinnerIndex == -1) + spinnerIndex = index; + } else + stack.add(index); + } for (int index = objectIndex; index < gameObjects.length && beatmap.objects[index].getTime() < trackPosition + approachTime; index++) { - stack.add(index); + if (beatmap.objects[index].isSpinner()) { + if (spinnerIndex == -1) + spinnerIndex = index; + } else + stack.add(index); // draw follow points if (Options.isFollowPointEnabled() && !loseState) lastObjectIndex = drawFollowPointsBetween(objectIndex, lastObjectIndex, trackPosition); } + if (spinnerIndex != -1) + stack.add(spinnerIndex); // draw hit objects while (!stack.isEmpty()){ @@ -1722,6 +1762,7 @@ public void loadBeatmap(Beatmap beatmap) { public void resetGameData() { data.clear(); objectIndex = 0; + passedObjects = new LinkedList(); breakIndex = 0; breakTime = 0; breakSound = false; @@ -2038,7 +2079,7 @@ else if (keys != lastReplayKeys) * @param keys the keys that are pressed */ private void sendGameKeyPress(int keys, int x, int y, int trackPosition) { - if (objectIndex >= gameObjects.length) // nothing to do here + if (!hasMoreObjects()) // nothing to do here return; HitObject hitObject = beatmap.objects[objectIndex]; @@ -2217,6 +2258,11 @@ private void calculateStacks() { } } + /** Returns whether there are any more objects remaining in the map. */ + private boolean hasMoreObjects() { + return objectIndex < gameObjects.length || !passedObjects.isEmpty(); + } + /** * Returns true if the coordinates are within the music position bar bounds. * @param cx the x coordinate