diff --git a/src/itdelatrisu/opsu/GameData.java b/src/itdelatrisu/opsu/GameData.java
index 9f52761a..7e14b995 100644
--- a/src/itdelatrisu/opsu/GameData.java
+++ b/src/itdelatrisu/opsu/GameData.java
@@ -1207,8 +1207,13 @@ public void sliderTickResult(int time, int result, float x, float y, HitObject h
}
fullObjectCount++;
}
+ public void sliderFinalResult(int time, int hitSlider30, float x, float y,
+ HitObject hitObject, int currentRepeats) {
+ score += 30;
+ }
/**
+ * https://osu.ppy.sh/wiki/Score
* Returns the score for a hit based on the following score formula:
*
* Score = Hit Value + Hit Value * (Combo * Difficulty * Mod) / 25
@@ -1219,19 +1224,26 @@ public void sliderTickResult(int time, int result, float x, float y, HitObject h
*
Mod: mod multipliers
*
* @param hitValue the hit value
+ * @param hitObject
* @return the score value
*/
- private int getScoreForHit(int hitValue) {
- return hitValue + (int) (hitValue * (Math.max(combo - 1, 0) * difficultyMultiplier * GameMod.getScoreMultiplier()) / 25);
+ private int getScoreForHit(int hitValue, HitObject hitObject) {
+ int comboMulti = Math.max(combo - 1, 0);
+ if(hitObject.isSlider()){
+ comboMulti += 1;
+ }
+ return (hitValue + (int)(hitValue * (comboMulti * difficultyMultiplier * GameMod.getScoreMultiplier()) / 25));
}
-
/**
+ * https://osu.ppy.sh/wiki/Score#How_to_calculate_the_Difficulty_multiplier
* Computes and stores the difficulty multiplier used in the score formula.
* @param drainRate the raw HP drain rate value
* @param circleSize the raw circle size value
* @param overallDifficulty the raw overall difficulty value
*/
public void calculateDifficultyMultiplier(float drainRate, float circleSize, float overallDifficulty) {
+ //TODO THE LIES ( difficultyMultiplier )
+ //*
float sum = drainRate + circleSize + overallDifficulty; // typically 2~27
if (sum <= 5f)
difficultyMultiplier = 2;
@@ -1243,8 +1255,21 @@ else if (sum <= 24f)
difficultyMultiplier = 5;
else //if (sum <= 30f)
difficultyMultiplier = 6;
+ //*/
+
+ /*
+ 924 3x1/4 beat notes 0.14stars
+ 924 3x1beat 0.28stars
+ 912 3x1beat wth 1 extra note 10 sec away 0.29stars
+
+ seems to be based on hitobject density? (Total Objects/Time)
+ */
+ /*
+ float mult = ((circleSize + overallDifficulty + drainRate) / 6) + 1.5f;
+ System.out.println("diffuculty Multiplier : "+ mult);
+ difficultyMultiplier = (int)mult;
+ */
}
-
/**
* Handles a hit result and performs all associated calculations.
* @param time the object start time
@@ -1292,7 +1317,7 @@ private int handleHitResult(int time, int result, float x, float y, Color color,
hitObject.getAdditionSampleSet(repeat));
// calculate score and increment combo streak
- changeScore(getScoreForHit(hitValue));
+ changeScore(getScoreForHit(hitValue, hitObject));
incrementComboStreak();
}
hitResultCount[result]++;
@@ -1357,6 +1382,7 @@ else if (hitResult == HIT_MISS && (GameMod.RELAX.isActive() || GameMod.AUTOPILOT
}
}
+
/**
* Returns a ScoreData object encapsulating all game data.
* If score data already exists, the existing object will be returned
@@ -1387,6 +1413,7 @@ public ScoreData getScoreData(Beatmap beatmap) {
scoreData.perfect = (comboMax == fullObjectCount);
scoreData.mods = GameMod.getModState();
scoreData.replayString = (replay == null) ? null : replay.getReplayFilename();
+ scoreData.playerName = "OpsuPlayer"; //TODO GameDataPlayerName?
return scoreData;
}
@@ -1407,7 +1434,7 @@ public Replay getReplay(ReplayFrame[] frames, Beatmap beatmap) {
replay = new Replay();
replay.mode = Beatmap.MODE_OSU;
replay.version = Updater.get().getBuildDate();
- replay.beatmapHash = (beatmap == null) ? "" : Utils.getMD5(beatmap.getFile());
+ replay.beatmapHash = (beatmap == null) ? "" : beatmap.md5Hash;//Utils.getMD5(beatmap.getFile());
replay.playerName = ""; // TODO
replay.replayHash = Long.toString(System.currentTimeMillis()); // TODO
replay.hit300 = (short) hitResultCount[HIT_300];
diff --git a/src/itdelatrisu/opsu/MD5InputStreamWrapper.java b/src/itdelatrisu/opsu/MD5InputStreamWrapper.java
new file mode 100644
index 00000000..530d50f7
--- /dev/null
+++ b/src/itdelatrisu/opsu/MD5InputStreamWrapper.java
@@ -0,0 +1,91 @@
+package itdelatrisu.opsu;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class MD5InputStreamWrapper extends InputStream {
+
+ InputStream in;
+ private boolean eof; // End Of File
+ MessageDigest md;
+ public MD5InputStreamWrapper(InputStream in) throws NoSuchAlgorithmException {
+ this.in = in;
+ md = MessageDigest.getInstance("MD5");
+ }
+
+ @Override
+ public int read() throws IOException {
+ int readed = in.read();
+ if(readed>=0)
+ md.update((byte) readed);
+ else
+ eof=true;
+ return readed;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return in.available();
+ }
+
+ @Override
+ public void close() throws IOException {
+ in.close();
+ }
+
+ @Override
+ public synchronized void mark(int readlimit) {
+ in.mark(readlimit);
+ }
+
+ @Override
+ public boolean markSupported() {
+ return in.markSupported();
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int readed = in.read(b, off, len);
+ if(readed>=0)
+ md.update(b, off, readed);
+ else
+ eof=true;
+
+ return readed;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ return read(b, 0 ,b.length);
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ throw new RuntimeException("MD5 stream not resetable");
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ throw new RuntimeException("MD5 stream not skipable");
+ }
+
+ public String getMD5() throws IOException {
+ byte[] buf = null;
+ if(!eof)
+ buf = new byte[0x1000];
+ while(!eof){
+ read(buf);
+ }
+
+ byte[] md5byte = md.digest();
+ StringBuilder result = new StringBuilder();
+ for (byte b : md5byte)
+ result.append(String.format("%02x", b));
+ //System.out.println("MD5 stream md5 " + result.toString());
+ return result.toString();
+
+ }
+
+}
diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java
index 59273f0d..5a39ab1f 100644
--- a/src/itdelatrisu/opsu/Options.java
+++ b/src/itdelatrisu/opsu/Options.java
@@ -110,6 +110,9 @@ public class Options {
/** The replay directory (created when needed). */
private static File replayDir;
+ /** The replay import directory. */
+ private static File replayImportDir;
+
/** The root skin directory. */
private static File skinRootDir;
@@ -1067,7 +1070,21 @@ public static File getOSZDir() {
oszDir.mkdir();
return oszDir;
}
+
+ /**
+ * Returns the replay import directory.
+ * If invalid, this will create and return a "ReplayImport" directory.
+ * @return the replay import directory
+ */
+ public static File getReplayImportDir() {
+ if (replayImportDir != null && replayImportDir.isDirectory())
+ return replayImportDir;
+ replayImportDir = new File(DATA_DIR, "ReplayImport/");
+ replayImportDir.mkdir();
+ return replayImportDir;
+ }
+
/**
* Returns the screenshot directory.
* If invalid, this will return a "Screenshot" directory.
diff --git a/src/itdelatrisu/opsu/ScoreData.java b/src/itdelatrisu/opsu/ScoreData.java
index 7cbe18ed..b6307031 100644
--- a/src/itdelatrisu/opsu/ScoreData.java
+++ b/src/itdelatrisu/opsu/ScoreData.java
@@ -77,6 +77,9 @@ public class ScoreData implements Comparable {
/** The tooltip string. */
private String tooltip;
+
+ /** The players Name. */
+ public String playerName;
/** Drawing values. */
private static float baseX, baseY, buttonWidth, buttonHeight, buttonOffset;
@@ -164,6 +167,7 @@ public ScoreData(ResultSet rs) throws SQLException {
this.perfect = rs.getBoolean(16);
this.mods = rs.getInt(17);
this.replayString = rs.getString(18);
+ this.playerName = rs.getString(19);
}
/**
@@ -260,7 +264,7 @@ public void draw(Graphics g, int index, int rank, long prevScore, boolean focus)
// hit counts (custom: osu! shows user instead, above score)
Utils.FONT_SMALL.drawString(
textX, y + textOffset + Utils.FONT_MEDIUM.getLineHeight(),
- String.format("300:%d 100:%d 50:%d Miss:%d", hit300, hit100, hit50, miss),
+ String.format("300:%d 100:%d 50:%d Miss:%d Name:%s", hit300, hit100, hit50, miss, getPlayerName()),
Color.white
);
@@ -331,6 +335,11 @@ public String toString() {
);
}
+ public String getPlayerName() {
+ if(playerName == null)
+ return "Null Name";
+ return playerName;
+ }
@Override
public int compareTo(ScoreData that) {
if (this.score != that.score)
diff --git a/src/itdelatrisu/opsu/beatmap/Beatmap.java b/src/itdelatrisu/opsu/beatmap/Beatmap.java
index ad8b5476..7216b0de 100644
--- a/src/itdelatrisu/opsu/beatmap/Beatmap.java
+++ b/src/itdelatrisu/opsu/beatmap/Beatmap.java
@@ -184,7 +184,10 @@ public class Beatmap implements Comparable {
/** Slider border color. If null, the skin value is used. */
public Color sliderBorder;
-
+
+ /** md5 hash of this file */
+ public String md5Hash;
+
/**
* [HitObjects]
*/
diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapParser.java b/src/itdelatrisu/opsu/beatmap/BeatmapParser.java
index 369ea19e..315d8f7a 100644
--- a/src/itdelatrisu/opsu/beatmap/BeatmapParser.java
+++ b/src/itdelatrisu/opsu/beatmap/BeatmapParser.java
@@ -19,16 +19,20 @@
package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.ErrorHandler;
+import itdelatrisu.opsu.MD5InputStreamWrapper;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.db.BeatmapDB;
+import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -205,7 +209,15 @@ private static Beatmap parseFile(File file, File dir, ArrayList beatmap
Beatmap beatmap = new Beatmap(file);
beatmap.timingPoints = new ArrayList();
- try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
+ try (InputStream inFileStream = new BufferedInputStream(new FileInputStream(file));){
+ MD5InputStreamWrapper md5stream = null;
+ try {
+ md5stream = new MD5InputStreamWrapper(inFileStream);
+ } catch (NoSuchAlgorithmException e1) {
+ ErrorHandler.error("Failed to get MD5 hash stream.", e1, true);
+ }
+ BufferedReader in = new BufferedReader(new InputStreamReader(md5stream!=null?md5stream:inFileStream, "UTF-8"));
+
String line = in.readLine();
String tokens[] = null;
while (line != null) {
@@ -578,6 +590,8 @@ else if ((type & HitObject.TYPE_SLIDER) > 0)
break;
}
}
+ if (md5stream != null)
+ beatmap.md5Hash = md5stream.getMD5();
} catch (IOException e) {
ErrorHandler.error(String.format("Failed to read file '%s'.", file.getAbsolutePath()), e, false);
}
diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java b/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java
index 7c5aab81..80362838 100644
--- a/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java
+++ b/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
@@ -57,6 +58,10 @@ public class BeatmapSetList {
/** Set of all beatmap set IDs for the parsed beatmaps. */
private HashSet MSIDdb;
+
+ /** Map of all hash to Beatmap . */
+ public HashMap beatmapHashesToFile;
+
/** Index of current expanded node (-1 if no node is expanded). */
private int expandedIndex;
@@ -83,6 +88,7 @@ public class BeatmapSetList {
private BeatmapSetList() {
parsedNodes = new ArrayList();
MSIDdb = new HashSet();
+ beatmapHashesToFile = new HashMap();
reset();
}
@@ -117,6 +123,10 @@ public BeatmapSetNode addSongGroup(ArrayList beatmaps) {
int msid = beatmaps.get(0).beatmapSetID;
if (msid > 0)
MSIDdb.add(msid);
+ for(Beatmap f : beatmaps) {
+ beatmapHashesToFile.put(f.md5Hash, f);
+ }
+
return node;
}
@@ -501,4 +511,8 @@ public boolean search(String query) {
* @return true if id is in the list
*/
public boolean containsBeatmapSetID(int id) { return MSIDdb.contains(id); }
+
+ public Beatmap getFileFromBeatmapHash(String beatmapHash) {
+ return beatmapHashesToFile.get(beatmapHash);
+ }
}
\ No newline at end of file
diff --git a/src/itdelatrisu/opsu/db/BeatmapDB.java b/src/itdelatrisu/opsu/db/BeatmapDB.java
index 3f85373e..57508975 100644
--- a/src/itdelatrisu/opsu/db/BeatmapDB.java
+++ b/src/itdelatrisu/opsu/db/BeatmapDB.java
@@ -43,7 +43,7 @@ public class BeatmapDB {
* Current database version.
* This value should be changed whenever the database format changes.
*/
- private static final String DATABASE_VERSION = "2014-06-08";
+ private static final String DATABASE_VERSION = "2015-06-11";
/** Minimum batch size ratio ({@code batchSize/cacheSize}) to invoke batch loading. */
private static final float LOAD_BATCH_MIN_RATIO = 0.2f;
@@ -96,7 +96,7 @@ public static void init() {
insertStmt = connection.prepareStatement(
"INSERT INTO beatmaps VALUES (" +
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " +
- "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
+ "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
);
selectStmt = connection.prepareStatement("SELECT * FROM beatmaps WHERE dir = ? AND file = ?");
deleteMapStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ? AND file = ?");
@@ -122,7 +122,8 @@ private static void createDatabase() {
"bpmMin INTEGER, bpmMax INTEGER, endTime INTEGER, " +
"audioFile TEXT, audioLeadIn INTEGER, previewTime INTEGER, countdown INTEGER, sampleSet TEXT, stackLeniency REAL, " +
"mode INTEGER, letterboxInBreaks BOOLEAN, widescreenStoryboard BOOLEAN, epilepsyWarning BOOLEAN, " +
- "bg TEXT, sliderBorder TEXT, timingPoints TEXT, breaks TEXT, combo TEXT" +
+ "bg TEXT, sliderBorder TEXT, timingPoints TEXT, breaks TEXT, combo TEXT," +
+ "md5hash TEXT" +
"); " +
"CREATE TABLE IF NOT EXISTS info (" +
"key TEXT NOT NULL UNIQUE, value TEXT" +
@@ -340,6 +341,7 @@ private static void setStatementFields(PreparedStatement stmt, Beatmap beatmap)
stmt.setString(38, beatmap.timingPointsToString());
stmt.setString(39, beatmap.breaksToString());
stmt.setString(40, beatmap.comboToString());
+ stmt.setString(41, beatmap.md5Hash);
} catch (SQLException e) {
throw e;
} catch (Exception e) {
@@ -481,6 +483,7 @@ private static void setBeatmapFields(ResultSet rs, Beatmap beatmap) throws SQLEx
if (bg != null)
beatmap.bg = new File(dir, BeatmapParser.getDBString(bg));
beatmap.sliderBorderFromString(rs.getString(37));
+ beatmap.md5Hash = BeatmapParser.getDBString(rs.getString(41));
} catch (SQLException e) {
throw e;
} catch (Exception e) {
diff --git a/src/itdelatrisu/opsu/db/ScoreDB.java b/src/itdelatrisu/opsu/db/ScoreDB.java
index 5ac5c300..4feee2da 100644
--- a/src/itdelatrisu/opsu/db/ScoreDB.java
+++ b/src/itdelatrisu/opsu/db/ScoreDB.java
@@ -45,7 +45,7 @@ public class ScoreDB {
* This value should be changed whenever the database format changes.
* Add any update queries to the {@link #getUpdateQueries(int)} method.
*/
- private static final int DATABASE_VERSION = 20140311;
+ private static final int DATABASE_VERSION = 20150401;
/**
* Returns a list of SQL queries to apply, in order, to update from
@@ -57,6 +57,8 @@ private static List getUpdateQueries(int version) {
List list = new LinkedList();
if (version < 20140311)
list.add("ALTER TABLE scores ADD COLUMN replay TEXT");
+ if (version < 20150401)
+ list.add("ALTER TABLE scores ADD COLUMN playerName TEXT");
/* add future updates here */
@@ -95,8 +97,13 @@ public static void init() {
// prepare sql statements
try {
+
+ //TODO timestamp as primary key should prevent importing the same replay multiple times
+ //but if for some magical reason two different replays has the same time stamp
+ //it will fail, such as copying replays from another drive? which will reset
+ //the last modified of the file.
insertStmt = connection.prepareStatement(
- "INSERT INTO scores VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
+ "INSERT OR IGNORE INTO scores VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
);
selectMapStmt = connection.prepareStatement(
"SELECT * FROM scores WHERE " +
@@ -114,7 +121,8 @@ public static void init() {
"DELETE FROM scores WHERE " +
"timestamp = ? AND MID = ? AND MSID = ? AND title = ? AND artist = ? AND " +
"creator = ? AND version = ? AND hit300 = ? AND hit100 = ? AND hit50 = ? AND " +
- "geki = ? AND katu = ? AND miss = ? AND score = ? AND combo = ? AND perfect = ? AND mods = ?"
+ "geki = ? AND katu = ? AND miss = ? AND score = ? AND combo = ? AND perfect = ? AND mods = ? AND " +
+ "replay = ? AND playerName = ?"
);
} catch (SQLException e) {
ErrorHandler.error("Failed to prepare score statements.", e, true);
@@ -137,7 +145,8 @@ private static void createDatabase() {
"combo INTEGER, " +
"perfect BOOLEAN, " +
"mods INTEGER, " +
- "replay TEXT" +
+ "replay TEXT," +
+ "playerName TEXT"+
");" +
"CREATE TABLE IF NOT EXISTS info (" +
"key TEXT NOT NULL UNIQUE, value TEXT" +
@@ -283,6 +292,9 @@ private static void setStatementFields(PreparedStatement stmt, ScoreData data)
stmt.setInt(15, data.combo);
stmt.setBoolean(16, data.perfect);
stmt.setInt(17, data.mods);
+ stmt.setString(18, data.replayString);
+ stmt.setString(19, data.playerName);
+
}
/**
diff --git a/src/itdelatrisu/opsu/io/OsuReader.java b/src/itdelatrisu/opsu/io/OsuReader.java
index c36a2b88..0cf84dcd 100644
--- a/src/itdelatrisu/opsu/io/OsuReader.java
+++ b/src/itdelatrisu/opsu/io/OsuReader.java
@@ -18,6 +18,7 @@
package itdelatrisu.opsu.io;
+import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -50,7 +51,7 @@ public OsuReader(File file) throws IOException {
* @param source the input stream to read from
*/
public OsuReader(InputStream source) {
- this.reader = new DataInputStream(source);
+ this.reader = new DataInputStream(new BufferedInputStream(source));
}
/**
diff --git a/src/itdelatrisu/opsu/io/OsuWriter.java b/src/itdelatrisu/opsu/io/OsuWriter.java
index eca07b3a..91b2efce 100644
--- a/src/itdelatrisu/opsu/io/OsuWriter.java
+++ b/src/itdelatrisu/opsu/io/OsuWriter.java
@@ -18,6 +18,7 @@
package itdelatrisu.opsu.io;
+import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
@@ -51,7 +52,7 @@ public OsuWriter(File file) throws FileNotFoundException {
* @param dest the output stream to write to
*/
public OsuWriter(OutputStream dest) {
- this.writer = new DataOutputStream(dest);
+ this.writer = new DataOutputStream(new BufferedOutputStream(dest));
}
/**
diff --git a/src/itdelatrisu/opsu/objects/Circle.java b/src/itdelatrisu/opsu/objects/Circle.java
index d52f32fd..c844dcc0 100644
--- a/src/itdelatrisu/opsu/objects/Circle.java
+++ b/src/itdelatrisu/opsu/objects/Circle.java
@@ -38,6 +38,9 @@ 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 Circle Hitobjects */
+ private static float diameter;
+
/** The associated HitObject. */
private HitObject hitObject;
@@ -62,11 +65,12 @@ public class Circle implements GameObject {
* @param circleSize the map's circleSize value
*/
public static void init(GameContainer container, float circleSize) {
- int diameter = (int) (104 - (circleSize * 8));
- diameter = (int) (diameter * HitObject.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));
- GameImage.APPROACHCIRCLE.setImage(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(diameter, diameter));
+ diameter = (104 - (circleSize * 8));
+ diameter = (diameter * HitObject.getXMultiplier()); // convert from Osupixels (640x480)
+ int diameterInt = (int)diameter;
+ GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(diameterInt, diameterInt));
+ GameImage.HITCIRCLE_OVERLAY.setImage(GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(diameterInt, diameterInt));
+ GameImage.APPROACHCIRCLE.setImage(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(diameterInt, diameterInt));
}
/**
@@ -119,15 +123,16 @@ public void draw(Graphics g, int trackPosition) {
private int hitResult(int time) {
int timeDiff = Math.abs(time);
+
int[] hitResultOffset = game.getHitResultOffsets();
int result = -1;
- if (timeDiff < hitResultOffset[GameData.HIT_300])
+ if (timeDiff <= hitResultOffset[GameData.HIT_300])
result = GameData.HIT_300;
- else if (timeDiff < hitResultOffset[GameData.HIT_100])
+ else if (timeDiff <= hitResultOffset[GameData.HIT_100])
result = GameData.HIT_100;
- else if (timeDiff < hitResultOffset[GameData.HIT_50])
+ else if (timeDiff <= hitResultOffset[GameData.HIT_50])
result = GameData.HIT_50;
- else if (timeDiff < hitResultOffset[GameData.HIT_MISS])
+ else if (timeDiff <= hitResultOffset[GameData.HIT_MISS])
result = GameData.HIT_MISS;
//else not a hit
@@ -137,8 +142,7 @@ else if (timeDiff < hitResultOffset[GameData.HIT_MISS])
@Override
public boolean mousePressed(int x, int y, int trackPosition) {
double distance = Math.hypot(this.x - x, this.y - y);
- int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
- if (distance < circleRadius) {
+ if (distance < diameter/2) {
int timeDiff = trackPosition - hitObject.getTime();
int result = hitResult(timeDiff);
@@ -158,7 +162,7 @@ public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolea
int[] hitResultOffset = game.getHitResultOffsets();
boolean isAutoMod = GameMod.AUTO.isActive();
- if (overlap || trackPosition > time + hitResultOffset[GameData.HIT_50]) {
+ 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, 0, HitObjectType.CIRCLE, null, true);
@@ -193,4 +197,9 @@ public void updatePosition() {
this.x = hitObject.getScaledX();
this.y = hitObject.getScaledY();
}
+
+ @Override
+ public void reset() {
+
+ }
}
diff --git a/src/itdelatrisu/opsu/objects/DummyObject.java b/src/itdelatrisu/opsu/objects/DummyObject.java
index 3f928c36..80f920a7 100644
--- a/src/itdelatrisu/opsu/objects/DummyObject.java
+++ b/src/itdelatrisu/opsu/objects/DummyObject.java
@@ -63,4 +63,9 @@ public void updatePosition() {
this.x = hitObject.getScaledX();
this.y = hitObject.getScaledY();
}
+
+ @Override
+ public void reset() {
+
+ }
}
diff --git a/src/itdelatrisu/opsu/objects/GameObject.java b/src/itdelatrisu/opsu/objects/GameObject.java
index ca1ab8e5..39c66020 100644
--- a/src/itdelatrisu/opsu/objects/GameObject.java
+++ b/src/itdelatrisu/opsu/objects/GameObject.java
@@ -69,4 +69,11 @@ public interface GameObject {
* Updates the position of the hit object.
*/
public void updatePosition();
+
+ /**
+ * Resets the hit object so that it can be reused.
+ */
+ public void reset();
+
+
}
diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java
index 414aff0e..ee81fb77 100644
--- a/src/itdelatrisu/opsu/objects/Slider.java
+++ b/src/itdelatrisu/opsu/objects/Slider.java
@@ -49,6 +49,10 @@ public class Slider implements GameObject {
/** Rate at which slider ticks are placed. */
private static float sliderTickRate = 1.0f;
+
+ private static float followRadius;
+
+ private static float diameter;
/** The amount of time, in milliseconds, to fade in the slider. */
private static final int FADE_IN_TIME = 375;
@@ -100,6 +104,8 @@ public class Slider implements GameObject {
/** Container dimensions. */
private static int containerWidth, containerHeight;
+
+
/**
* Initializes the Slider data type with images and dimensions.
@@ -110,10 +116,12 @@ public class Slider implements GameObject {
public static void init(GameContainer container, float circleSize, Beatmap beatmap) {
containerWidth = container.getWidth();
containerHeight = container.getHeight();
-
- int diameter = (int) (104 - (circleSize * 8));
- diameter = (int) (diameter * HitObject.getXMultiplier()); // convert from Osupixels (640x480)
-
+
+ diameter = (104 - (circleSize * 8));
+ diameter = (diameter * HitObject.getXMultiplier()); // convert from Osupixels (640x480)
+ int diameterInt = (int)diameter;
+
+ followRadius = diameter / 2 * 3f;
// slider ball
if (GameImage.SLIDER_BALL.hasSkinImages() ||
(!GameImage.SLIDER_BALL.hasSkinImage() && GameImage.SLIDER_BALL.getImages() != null))
@@ -121,11 +129,11 @@ public static void init(GameContainer container, float circleSize, Beatmap beatm
else
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);
+ sliderBallImages[i] = sliderBallImages[i].getScaledCopy(diameterInt * 118 / 128, diameterInt * 118 / 128);
- GameImage.SLIDER_FOLLOWCIRCLE.setImage(GameImage.SLIDER_FOLLOWCIRCLE.getImage().getScaledCopy(diameter * 259 / 128, diameter * 259 / 128));
- GameImage.REVERSEARROW.setImage(GameImage.REVERSEARROW.getImage().getScaledCopy(diameter, diameter));
- GameImage.SLIDER_TICK.setImage(GameImage.SLIDER_TICK.getImage().getScaledCopy(diameter / 4, diameter / 4));
+ GameImage.SLIDER_FOLLOWCIRCLE.setImage(GameImage.SLIDER_FOLLOWCIRCLE.getImage().getScaledCopy(diameterInt * 259 / 128, diameterInt * 259 / 128));
+ GameImage.REVERSEARROW.setImage(GameImage.REVERSEARROW.getImage().getScaledCopy(diameterInt, diameterInt));
+ GameImage.SLIDER_TICK.setImage(GameImage.SLIDER_TICK.getImage().getScaledCopy(diameterInt / 4, diameterInt / 4));
sliderMultiplier = beatmap.sliderMultiplier;
sliderTickRate = beatmap.sliderTickRate;
@@ -272,6 +280,55 @@ public void draw(Graphics g, int trackPosition) {
* @return the hit result (GameData.HIT_* constants)
*/
private int hitResult() {
+ /*
+ time scoredelta score-hit-initial-tick= unaccounted
+ (1/4 - 1) 396 - 300 - 30 46
+ (1+1/4 - 2) 442 - 300 - 30 - 10
+ (2+1/4 - 3) 488 - 300 - 30 - 2*10 896 (408)5x
+ (3+1/4 - 4) 534 - 300 - 30 - 3*10
+ (4+1/4 - 5) 580 - 300 - 30 - 4*10
+ (5+1/4 - 6) 626 - 300 - 30 - 5*10
+ (6+1/4 - 7) 672 - 300 - 30 - 6*10
+
+ difficultyMulti = 3 (+36 per combo)
+
+ score =
+ (t)ticks(10) * nticks +
+ (h)hitValue
+ (c)combo (hitValue/25 * difficultyMultiplier*(combo-1))
+ (i)initialHit (30) +
+ (f)finalHit(30) +
+
+ s t h c i f
+ 626 - 10*5 - 300 - 276(-216 - 30 - 30) (all)(7x)
+ 240 - 10*5 - 100 - 90 (-60 <- 30>) (no final or initial)(6x)
+
+ 218 - 10*4 - 100 - 78 (-36 - 30) (4 tick no initial)(5x)
+ 196 - 10*3 - 100 - 66 (-24 - 30 ) (3 tick no initial)(4x)
+ 112 - 10*2 - 50 - 42 (-12 - 30 ) (2 tick no initial)(3x)
+ 96 - 10 - 50 - 36 ( -6 - 30 ) (1 tick no initial)(2x)
+
+ 206 - 10*4 - 100 - 66 (-36 - 30 ) (4 tick no initial)(4x)
+ 184 - 10*3 - 100 - 54 (-24 - 30 ) (3 tick no initial)(3x)
+ 90 - 10 - 50 - 30 ( - 30 ) (1 tick no initial)(0x)
+
+ 194 - 10*4 - 100 - 54 (-24 - 30 ) (4 tick no initial)(3x)
+
+ 170 - 10*4 - 100 - 30 ( - 30 ) (4 tick no final)(0x)
+ 160 - 10*3 - 100 - 30 ( - 30 ) (3 tick no final)(0x)
+ 100 - 10*2 - 50 - 30 ( - 30 ) (2 tick no final)(0x)
+
+ 198 - 10*5 - 100 - 48 (-36 ) (no initial and final)(5x)
+ 110 - 50 - ( - 30 - 30 ) (final and initial no tick)(0x)
+ 80 - 50 - ( <- 30> ) (only final or initial)(0x)
+
+ 140 - 10*4 - 100 - 0 (4 ticks only)(0x)
+ 80 - 10*3 - 50 - 0 (3 tick only)(0x)
+ 70 - 10*2 - 50 - 0 (2 tick only)(0x)
+ 60 - 10 - 50 - 0 (1 tick only)(0x)
+
+
+ */
float tickRatio = (float) ticksHit / tickIntervals;
int result;
@@ -308,8 +365,7 @@ public boolean mousePressed(int x, int y, int trackPosition) {
return false;
double distance = Math.hypot(this.x - x, this.y - y);
- int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
- if (distance < circleRadius) {
+ if (distance < diameter / 2) {
int timeDiff = Math.abs(trackPosition - hitObject.getTime());
int[] hitResultOffset = game.getHitResultOffsets();
@@ -365,26 +421,29 @@ else if (GameMod.RELAX.isActive() && trackPosition >= time)
}
// end of slider
- if (overlap || trackPosition > hitObject.getTime() + sliderTimeTotal) {
+ if (trackPosition > hitObject.getTime() + sliderTimeTotal) {
tickIntervals++;
// check if cursor pressed and within end circle
if (keyPressed || GameMod.RELAX.isActive()) {
float[] c = curve.pointAt(getT(trackPosition, false));
double distance = Math.hypot(c[0] - mouseX, c[1] - mouseY);
- int followCircleRadius = GameImage.SLIDER_FOLLOWCIRCLE.getImage().getWidth() / 2;
- if (distance < followCircleRadius)
+ if (distance < followRadius)
sliderClickedFinal = true;
}
// final circle hit
- if (sliderClickedFinal)
+ if (sliderClickedFinal){
ticksHit++;
+ data.sliderFinalResult(hitObject.getTime(), GameData.HIT_SLIDER30, this.x, this.y, hitObject, currentRepeats);
+ }
// "auto" mod: always send a perfect hit result
if (isAutoMod)
ticksHit = tickIntervals;
+ //TODO missing the final shouldn't increment the combo
+
// calculate and send slider result
hitResult();
return true;
@@ -417,8 +476,7 @@ else if (GameMod.RELAX.isActive() && trackPosition >= time)
// holding slider...
float[] c = curve.pointAt(getT(trackPosition, false));
double distance = Math.hypot(c[0] - mouseX, c[1] - mouseY);
- int followCircleRadius = GameImage.SLIDER_FOLLOWCIRCLE.getImage().getWidth() / 2;
- if (((keyPressed || GameMod.RELAX.isActive()) && distance < followCircleRadius) || isAutoMod) {
+ if (((keyPressed || GameMod.RELAX.isActive()) && distance < followRadius) || isAutoMod) {
// mouse pressed and within follow circle
followCircleActive = true;
data.changeHealth(delta * GameData.HP_DRAIN_MULTIPLIER);
@@ -501,4 +559,16 @@ private float getT(int trackPosition, boolean raw) {
return (floor % 2 == 0) ? t - floor : floor + 1 - t;
}
}
+
+ @Override
+ public void reset() {
+ sliderClickedInitial = false;
+ sliderClickedFinal = false;
+ followCircleActive = false;
+ currentRepeats = 0;
+ tickIndex = 0;
+ ticksHit = 0;
+ tickIntervals = 1;
+ }
+
}
diff --git a/src/itdelatrisu/opsu/objects/Spinner.java b/src/itdelatrisu/opsu/objects/Spinner.java
index efea9112..9f0c18de 100644
--- a/src/itdelatrisu/opsu/objects/Spinner.java
+++ b/src/itdelatrisu/opsu/objects/Spinner.java
@@ -45,11 +45,10 @@ public class Spinner implements GameObject {
private static float overallDifficulty = 5f;
/** The number of rotation velocities to store. */
- // note: currently takes about 200ms to spin up (4 * 50)
- private static final int MAX_ROTATION_VELOCITIES = 50;
+ private int maxStoredDeltaAngles;
/** The amount of time, in milliseconds, before another velocity is stored. */
- private static final int DELTA_UPDATE_TIME = 4;
+ 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;
@@ -64,6 +63,8 @@ public class Spinner implements GameObject {
TWO_PI = (float) (Math.PI * 2),
HALF_PI = (float) (Math.PI / 2);
+ private static final float MAX_ANG_DIFF = DELTA_UPDATE_TIME * AUTO_MULTIPLIER; // ~95.3
+
/** The associated HitObject. */
private HitObject hitObject;
@@ -83,19 +84,25 @@ public class Spinner implements GameObject {
private float rotationsNeeded;
/** The remaining amount of time that was not used. */
- private int deltaOverflow;
+ private float deltaOverflow;
/** The sum of all the velocities in storedVelocities. */
- private float sumVelocity = 0f;
+ private float sumDeltaAngle = 0f;
/** Array holding the most recent rotation velocities. */
- private float[] storedVelocities = new float[MAX_ROTATION_VELOCITIES];
+ private float[] storedDeltaAngle;
/** True if the mouse cursor is pressed. */
private boolean isSpinning;
/** Current index of the stored velocities in rotations/second. */
- private int velocityIndex = 0;
+ private int deltaAngleIndex = 0;
+
+ /** The remaining amount of the angle that was not used. */
+ private float deltaAngleOverflow = 0;
+
+ /** The RPM that is drawn to the screen. */
+ private int drawnRPM = 0;
/**
* Initializes the Spinner data type with images and dimensions.
@@ -117,7 +124,46 @@ public static void init(GameContainer container, float difficulty) {
public Spinner(HitObject hitObject, Game game, GameData data) {
this.hitObject = hitObject;
this.data = data;
-
+/*
+ 1 beat = 731.707317073171ms
+ RPM at frame X with spinner Y beats long
+ 10 20 30 40 50 60 5sec ~ 48 ~ 800ms
+ final int minVel = 12;
+ final int maxVel = 48;
+ final int minTime = 2000;
+ final int maxTime = 5000;
+ maxStoredDeltaAngles = (int) Utils.clamp(
+ (hitObject.getEndTime() - hitObject.getTime() - minTime) * (maxVel-minVel)/(maxTime-minTime) + minVel
+ , minVel, maxVel);
+ storedDeltaAngle = new float[maxStoredDeltaAngles];
+
// calculate rotations needed
float spinsPerMinute = 100 + (overallDifficulty * 15);
rotationsNeeded = spinsPerMinute * (hitObject.getEndTime() - hitObject.getTime()) / 60000f;
@@ -144,12 +190,11 @@ public void draw(Graphics g, int trackPosition) {
}
// rpm
- int rpm = Math.abs(Math.round(sumVelocity / storedVelocities.length * 60));
Image rpmImg = GameImage.SPINNER_RPM.getImage();
rpmImg.setAlpha(alpha);
rpmImg.drawCentered(width / 2f, height - rpmImg.getHeight() / 2f);
if (timeDiff < 0)
- data.drawSymbolString(Integer.toString(rpm), (width + rpmImg.getWidth() * 0.95f) / 2f,
+ data.drawSymbolString(Integer.toString(drawnRPM), (width + rpmImg.getWidth() * 0.95f) / 2f,
height - data.getScoreSymbolImage('0').getHeight() * 1.025f, 1f, 1f, true);
// spinner meter (subimage)
@@ -205,7 +250,10 @@ else if (ratio >= 0.75f)
}
@Override
- public boolean mousePressed(int x, int y, int trackPosition) { return false; } // not used
+ public boolean mousePressed(int x, int y, int trackPosition) {
+ lastAngle = (float) Math.atan2(x - (height / 2), y - (width / 2));
+ return false;
+ }
@Override
public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) {
@@ -222,17 +270,18 @@ public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolea
// spin automatically
// http://osu.ppy.sh/wiki/FAQ#Spinners
- float angle;
+
+ deltaOverflow += delta;
+
+ float angleDiff = 0;
if (GameMod.AUTO.isActive()) {
- lastAngle = 0;
- angle = delta * AUTO_MULTIPLIER;
+ angleDiff = delta * AUTO_MULTIPLIER;
isSpinning = true;
} else if (GameMod.SPUN_OUT.isActive() || GameMod.AUTOPILOT.isActive()) {
- lastAngle = 0;
- angle = delta * SPUN_OUT_MULTIPLIER;
+ angleDiff = delta * SPUN_OUT_MULTIPLIER;
isSpinning = true;
} else {
- angle = (float) Math.atan2(mouseY - (height / 2), mouseX - (width / 2));
+ float angle = (float) Math.atan2(mouseY - (height / 2), mouseX - (width / 2));
// set initial angle to current mouse position to skip first click
if (!isSpinning && (keyPressed || GameMod.RELAX.isActive())) {
@@ -240,35 +289,52 @@ public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolea
isSpinning = true;
return false;
}
+ angleDiff = angle - lastAngle;
+ if(Math.abs(angleDiff) > 0.01f){
+ lastAngle = angle;
+ }else{
+ angleDiff = 0;
+ }
+
}
// make angleDiff the smallest angle change possible
// (i.e. 1/4 rotation instead of 3/4 rotation)
- float angleDiff = angle - lastAngle;
if (angleDiff < -Math.PI)
angleDiff += TWO_PI;
else if (angleDiff > Math.PI)
angleDiff -= TWO_PI;
-
- // spin caused by the cursor
- float cursorVelocity = 0;
+
+ //may be a problem at higher frame rate due to float point round off
if (isSpinning)
- cursorVelocity = Utils.clamp(angleDiff / TWO_PI / delta * 1000, -8f, 8f);
-
- deltaOverflow += delta;
+ deltaAngleOverflow += angleDiff;
+
while (deltaOverflow >= DELTA_UPDATE_TIME) {
- sumVelocity -= storedVelocities[velocityIndex];
- sumVelocity += cursorVelocity;
- storedVelocities[velocityIndex++] = cursorVelocity;
- velocityIndex %= storedVelocities.length;
+ // spin caused by the cursor
+ float deltaAngle = 0;
+ if (isSpinning){
+ deltaAngle = deltaAngleOverflow * DELTA_UPDATE_TIME / deltaOverflow;
+ deltaAngleOverflow -= deltaAngle;
+ deltaAngle = Utils.clamp(deltaAngle, -MAX_ANG_DIFF, MAX_ANG_DIFF);
+ }
+ sumDeltaAngle -= storedDeltaAngle[deltaAngleIndex];
+ sumDeltaAngle += deltaAngle;
+ storedDeltaAngle[deltaAngleIndex++] = deltaAngle;
+ deltaAngleIndex %= storedDeltaAngle.length;
deltaOverflow -= DELTA_UPDATE_TIME;
+
+ float rotationAngle = sumDeltaAngle / maxStoredDeltaAngles;
+ rotationAngle = Utils.clamp(rotationAngle, -MAX_ANG_DIFF, MAX_ANG_DIFF);
+ float rotationPerSec = rotationAngle * (1000/DELTA_UPDATE_TIME) / TWO_PI;
+
+ drawnRPM = (int)(Math.abs(rotationPerSec * 60));
+
+ rotate(rotationAngle);
+ if (Math.abs(rotationAngle) > 0.00001f)
+ data.changeHealth(DELTA_UPDATE_TIME * GameData.HP_DRAIN_MULTIPLIER);
+
}
- float rotationAngle = sumVelocity / storedVelocities.length * TWO_PI * delta / 1000;
- rotate(rotationAngle);
- if (Math.abs(rotationAngle) > 0.00001f)
- data.changeHealth(delta * GameData.HP_DRAIN_MULTIPLIER);
-
- lastAngle = angle;
+ //TODO may need to update 1 more time when the spinner ends?
return false;
}
@@ -311,15 +377,38 @@ private void rotate(float angle) {
// added one whole rotation...
if (Math.floor(newRotations) > rotations) {
+ //TODO seems to give 1100 points per spin but also an extra 100 for some spinners
if (newRotations > rotationsNeeded) { // extra rotations
data.changeScore(1000);
+
SoundController.playSound(SoundEffect.SPINNERBONUS);
- } else {
+ }
+ data.changeScore(100);
+ SoundController.playSound(SoundEffect.SPINNERSPIN);
+
+ }
+ /*
+ //The extra 100 for some spinners (mostly wrong)
+ if (Math.floor(newRotations + 0.5f) > rotations + 0.5f) {
+ if (newRotations + 0.5f > rotationsNeeded) { // extra rotations
data.changeScore(100);
- SoundController.playSound(SoundEffect.SPINNERSPIN);
}
}
+ //*/
rotations = newRotations;
}
+
+ @Override
+ public void reset() {
+ deltaAngleIndex = 0;
+ sumDeltaAngle = 0;
+ for(int i=0; i= replay.frames.length)
updateGame(replayX, replayY, delta, MusicController.getPosition(), lastKeysPressed);
+
+ //TODO probably should to disable sounds then reseek to the new position
+ if(seeking && replayIndex-1 >= 1 && replayIndex < replay.frames.length && trackPosition < replay.frames[replayIndex-1].getTime()){
+ replayIndex = 0;
+ while(objectIndex>=0){
+ gameObjects[objectIndex].reset();
+ objectIndex--;
+
+ }
+ // reset game data
+ resetGameData();
+
+ // load the first timingPoint
+ if (!beatmap.timingPoints.isEmpty()) {
+ TimingPoint timingPoint = beatmap.timingPoints.get(0);
+ if (!timingPoint.isInherited()) {
+ setBeatLength(timingPoint, true);
+ timingPointIndex++;
+ }
+ }
+ seeking = false;
+ }
// update and run replay frames
while (replayIndex < replay.frames.length && trackPosition >= replay.frames[replayIndex].getTime()) {
@@ -753,7 +781,7 @@ else if (replayFrames != null) {
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_300]);
+ 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))
@@ -897,6 +925,11 @@ else if (playbackSpeed.getButton().contains(x, y)) {
MusicController.setPitch(GameMod.getSpeedMultiplier() * playbackSpeed.getModifier());
}
+ if(!GameMod.AUTO.isActive() && y < 50){
+ float pos = (float)x / width * beatmap.endTime;
+ MusicController.setPosition((int)pos);
+ seeking = true;
+ }
return;
}
@@ -1044,6 +1077,9 @@ public void enter(GameContainer container, StateBasedGame game)
} else if (restart == Restart.REPLAY)
retries = 0;
+ gameObjects = new GameObject[beatmap.objects.length];
+ playbackSpeed = PlaybackSpeed.NORMAL;
+
// reset game data
resetGameData();
@@ -1063,7 +1099,7 @@ public void enter(GameContainer container, StateBasedGame game)
// is this the last note in the combo?
boolean comboEnd = false;
- if (i + 1 < beatmap.objects.length && beatmap.objects[i + 1].isNewCombo())
+ if (i + 1 >= beatmap.objects.length || beatmap.objects[i + 1].isNewCombo())
comboEnd = true;
Color color = combo[hitObject.getComboIndex()];
@@ -1278,7 +1314,6 @@ public void loadBeatmap(Beatmap beatmap) {
* Resets all game data and structures.
*/
public void resetGameData() {
- gameObjects = new GameObject[beatmap.objects.length];
data.clear();
objectIndex = 0;
breakIndex = 0;
@@ -1303,8 +1338,7 @@ public void resetGameData() {
autoMouseY = 0;
autoMousePressed = false;
flashlightRadius = container.getHeight() * 2 / 3;
- playbackSpeed = PlaybackSpeed.NORMAL;
-
+
System.gc();
}
@@ -1405,11 +1439,19 @@ private void setMapModifiers() {
// overallDifficulty (hit result time offsets)
hitResultOffset = new int[GameData.HIT_MAX];
+ /*
+ float mult = 0.608f;
+ hitResultOffset[GameData.HIT_300] = (int) ((128 - (overallDifficulty * 9.6))*mult);
+ hitResultOffset[GameData.HIT_100] = (int) ((224 - (overallDifficulty * 12.8))*mult);
+ hitResultOffset[GameData.HIT_50] = (int) ((320 - (overallDifficulty * 16))*mult);
+ hitResultOffset[GameData.HIT_MISS] = (int) ((1000 - (overallDifficulty * 10))*mult);
+ /*/
hitResultOffset[GameData.HIT_300] = (int) (78 - (overallDifficulty * 6));
hitResultOffset[GameData.HIT_100] = (int) (138 - (overallDifficulty * 8));
hitResultOffset[GameData.HIT_50] = (int) (198 - (overallDifficulty * 10));
hitResultOffset[GameData.HIT_MISS] = (int) (500 - (overallDifficulty * 10));
data.setHitResultOffset(hitResultOffset);
+ //*/
// HPDrainRate (health change)
data.setDrainRate(HPDrainRate);
diff --git a/src/itdelatrisu/opsu/states/Splash.java b/src/itdelatrisu/opsu/states/Splash.java
index 7b48729e..8d8ef388 100644
--- a/src/itdelatrisu/opsu/states/Splash.java
+++ b/src/itdelatrisu/opsu/states/Splash.java
@@ -25,6 +25,7 @@
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
+import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.ui.UI;
@@ -127,6 +128,9 @@ public void run() {
// parse song directory
BeatmapParser.parseAllFiles(beatmapDir);
+
+ // import replays
+ ReplayImporter.importAllReplaysFromDir(Options.getReplayImportDir());
// load sounds
SoundController.init();
diff --git a/src/org/newdawn/slick/openal/OpenALStreamPlayer.java b/src/org/newdawn/slick/openal/OpenALStreamPlayer.java
index 2e737886..7042407b 100644
--- a/src/org/newdawn/slick/openal/OpenALStreamPlayer.java
+++ b/src/org/newdawn/slick/openal/OpenALStreamPlayer.java
@@ -41,6 +41,7 @@
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
+
/**
* A generic tool to work on a supplied stream, pulling out PCM data and buffered it to OpenAL
* as required.
@@ -224,6 +225,7 @@ public synchronized void play(boolean loop) throws IOException {
*/
public void setup(float pitch) {
this.pitch = pitch;
+ syncPosition();
}
/**
@@ -354,7 +356,7 @@ public synchronized boolean setPosition(float position) {
}
playedPos = streamPos;
- syncStartTime = getTime() - playedPos * 1000 / sampleSize / sampleRate;
+ syncStartTime = (long) (getTime() - (playedPos * 1000 / sampleSize / sampleRate)/pitch);
startPlayback();
@@ -403,24 +405,34 @@ public float getPosition() {
float thisPosition = getALPosition();
long thisTime = getTime();
float dxPosition = thisPosition - lastUpdatePosition;
- long dxTime = thisTime - syncStartTime;
+ float dxTime = (thisTime - syncStartTime) * pitch;
// hard reset
if (Math.abs(thisPosition - dxTime / 1000f) > 1 / 2f) {
- syncStartTime = thisTime - ((long) (thisPosition * 1000));
- dxTime = thisTime - syncStartTime;
+ //System.out.println("Time HARD Reset"+" "+thisPosition+" "+(dxTime / 1000f));
+ syncPosition();
+ dxTime = (thisTime - syncStartTime) * pitch;
avgDiff = 0;
}
if ((int) (dxPosition * 1000) != 0) { // lastPosition != thisPosition
float diff = thisPosition * 1000 - (dxTime);
+
avgDiff = (diff + avgDiff * 9) / 10;
- syncStartTime -= (int) (avgDiff/2);
- dxTime = thisTime - syncStartTime;
+ if(Math.abs(avgDiff) >= 1){
+ syncStartTime -= (int)(avgDiff);
+ avgDiff -= (int)(avgDiff);
+ dxTime = (thisTime - syncStartTime) * pitch;
+ }
lastUpdatePosition = thisPosition;
}
+
return dxTime / 1000f;
}
+ private void syncPosition(){
+ syncStartTime = getTime() - (long) ( getALPosition() * 1000 / pitch);
+ avgDiff = 0;
+ }
/**
* Processes a track pause.
diff --git a/src/org/newdawn/slick/openal/SoundStore.java b/src/org/newdawn/slick/openal/SoundStore.java
index 30655b00..01e135b0 100644
--- a/src/org/newdawn/slick/openal/SoundStore.java
+++ b/src/org/newdawn/slick/openal/SoundStore.java
@@ -536,6 +536,7 @@ private int getMusicSource() {
*/
public void setMusicPitch(float pitch) {
if (soundWorks) {
+ stream.setup(pitch);
AL10.alSourcef(sources.get(0), AL10.AL_PITCH, pitch);
}
}