Score formula update - still very wrong, but slightly closer. (see #43)

Added the "difficulty multiplier" as given by the osu! wiki.  Please note that this will invalidate all of your saved scores.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2015-06-14 11:59:12 -05:00
parent 85c2328877
commit 81d9421d74
4 changed files with 53 additions and 37 deletions

View File

@ -293,11 +293,12 @@ public class GameData {
/** Displayed health (for animation, slightly behind health). */ /** Displayed health (for animation, slightly behind health). */
private float healthDisplay; private float healthDisplay;
/** Beatmap HPDrainRate value. (0:easy ~ 10:hard) */ /** The difficulty multiplier used in the score formula. */
private float drainRate = 5f; private int difficultyMultiplier = 2;
/** Beatmap OverallDifficulty value. (0:easy ~ 10:hard) */ /** Beatmap HPDrainRate value. (0:easy ~ 10:hard) */
private float difficulty = 5f; @SuppressWarnings("unused")
private float drainRate = 5f;
/** Default text symbol images. */ /** Default text symbol images. */
private Image[] defaultSymbols; private Image[] defaultSymbols;
@ -374,6 +375,7 @@ public class GameData {
health = 100f; health = 100f;
healthDisplay = 100f; healthDisplay = 100f;
hitResultCount = new int[HIT_MAX]; hitResultCount = new int[HIT_MAX];
drainRate = 5f;
if (hitResultList != null) { if (hitResultList != null) {
for (HitObjectResult hitResult : hitResultList) { for (HitObjectResult hitResult : hitResultList) {
if (hitResult.curve != null) if (hitResult.curve != null)
@ -470,22 +472,6 @@ public class GameData {
*/ */
public void setDrainRate(float drainRate) { this.drainRate = drainRate; } public void setDrainRate(float drainRate) { this.drainRate = drainRate; }
/**
* Returns the health drain rate.
*/
public float getDrainRate() { return drainRate; }
/**
* Sets the overall difficulty level.
* @param difficulty the new difficulty [0-10]
*/
public void setDifficulty(float difficulty) { this.difficulty = difficulty; }
/**
* Returns the overall difficulty level.
*/
public float getDifficulty() { return difficulty; }
/** /**
* Sets the array of hit result offsets. * Sets the array of hit result offsets.
*/ */
@ -1192,7 +1178,6 @@ public class GameData {
switch (result) { switch (result) {
case HIT_SLIDER30: case HIT_SLIDER30:
hitValue = 30; hitValue = 30;
incrementComboStreak();
changeHealth(1f); changeHealth(1f);
SoundController.playHitSound( SoundController.playHitSound(
hitObject.getEdgeHitSoundType(repeat), hitObject.getEdgeHitSoundType(repeat),
@ -1201,7 +1186,6 @@ public class GameData {
break; break;
case HIT_SLIDER10: case HIT_SLIDER10:
hitValue = 10; hitValue = 10;
incrementComboStreak();
SoundController.playHitSound(HitSound.SLIDERTICK); SoundController.playHitSound(HitSound.SLIDERTICK);
break; break;
case HIT_MISS: case HIT_MISS:
@ -1210,15 +1194,18 @@ public class GameData {
default: default:
return; return;
} }
fullObjectCount++;
if (hitValue > 0) { if (hitValue > 0) {
// calculate score and increment combo streak
score += hitValue; score += hitValue;
incrementComboStreak();
if (!Options.isPerfectHitBurstEnabled()) if (!Options.isPerfectHitBurstEnabled())
; // hide perfect hit results ; // hide perfect hit results
else else
hitResultList.add(new HitObjectResult(time, result, x, y, null, HitObjectType.SLIDERTICK, null, false)); hitResultList.add(new HitObjectResult(time, result, x, y, null, HitObjectType.SLIDERTICK, null, false));
} }
fullObjectCount++;
} }
/** /**
@ -1228,14 +1215,34 @@ public class GameData {
* <ul> * <ul>
* <li><strong>Hit Value:</strong> hit result (50, 100, 300), slider ticks, spinner bonus * <li><strong>Hit Value:</strong> hit result (50, 100, 300), slider ticks, spinner bonus
* <li><strong>Combo:</strong> combo before this hit - 1 (minimum 0) * <li><strong>Combo:</strong> combo before this hit - 1 (minimum 0)
* <li><strong>Difficulty:</strong> the beatmap difficulty * <li><strong>Difficulty:</strong> the difficulty setting (see {@link #calculateDifficultyMultiplier(float, float, float)})
* <li><strong>Mod:</strong> mod multipliers * <li><strong>Mod:</strong> mod multipliers
* </ul> * </ul>
* @param hitValue the hit value * @param hitValue the hit value
* @return the score value * @return the score value
*/ */
private int getScoreForHit(int hitValue) { private int getScoreForHit(int hitValue) {
return hitValue + (int) (hitValue * (Math.max(combo - 1, 0) * difficulty * GameMod.getScoreMultiplier()) / 25); return hitValue + (int) (hitValue * (Math.max(combo - 1, 0) * difficultyMultiplier * GameMod.getScoreMultiplier()) / 25);
}
/**
* 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) {
float sum = drainRate + circleSize + overallDifficulty; // typically 2~27
if (sum <= 5f)
difficultyMultiplier = 2;
else if (sum <= 12f)
difficultyMultiplier = 3;
else if (sum <= 17f)
difficultyMultiplier = 4;
else if (sum <= 24f)
difficultyMultiplier = 5;
else //if (sum <= 30f)
difficultyMultiplier = 6;
} }
/** /**
@ -1331,21 +1338,21 @@ public class GameData {
public void hitResult(int time, int result, float x, float y, Color color, public void hitResult(int time, int result, float x, float y, Color color,
boolean end, HitObject hitObject, int repeat, boolean end, HitObject hitObject, int repeat,
HitObjectType hitResultType, Curve curve, boolean expand) { HitObjectType hitResultType, Curve curve, boolean expand) {
result = handleHitResult(time, result, x, y, color, end, hitObject, repeat, hitResultType); int hitResult = handleHitResult(time, result, x, y, color, end, hitObject, repeat, hitResultType);
if ((result == HIT_300 || result == HIT_300G || result == HIT_300K) && !Options.isPerfectHitBurstEnabled()) if ((hitResult == HIT_300 || hitResult == HIT_300G || hitResult == HIT_300K) && !Options.isPerfectHitBurstEnabled())
; // hide perfect hit results ; // hide perfect hit results
else if (result == HIT_MISS && (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive())) else if (hitResult == HIT_MISS && (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()))
; // "relax" and "autopilot" mods: hide misses ; // "relax" and "autopilot" mods: hide misses
else { else {
hitResultList.add(new HitObjectResult(time, result, x, y, color, hitResultType, curve, expand)); hitResultList.add(new HitObjectResult(time, hitResult, x, y, color, hitResultType, curve, expand));
// sliders: add the other curve endpoint for the hit animation // sliders: add the other curve endpoint for the hit animation
if (curve != null) { if (curve != null) {
boolean isFirst = (hitResultType == HitObjectType.SLIDER_FIRST); boolean isFirst = (hitResultType == HitObjectType.SLIDER_FIRST);
float[] p = curve.pointAt((isFirst) ? 1f : 0f); float[] p = curve.pointAt((isFirst) ? 1f : 0f);
HitObjectType type = (isFirst) ? HitObjectType.SLIDER_LAST : HitObjectType.SLIDER_FIRST; HitObjectType type = (isFirst) ? HitObjectType.SLIDER_LAST : HitObjectType.SLIDER_FIRST;
hitResultList.add(new HitObjectResult(time, result, p[0], p[1], color, type, null, expand)); hitResultList.add(new HitObjectResult(time, hitResult, p[0], p[1], color, type, null, expand));
} }
} }
} }

View File

@ -260,6 +260,8 @@ public class DownloadNode {
UI.sendBarNotification("Download failed due to a connection error."); UI.sendBarNotification("Download failed due to a connection error.");
} }
}); });
if (Options.useUnicodeMetadata()) // load glyphs
Utils.loadGlyphs(Utils.FONT_LARGE, getTitle(), null);
} }
/** /**

View File

@ -41,6 +41,9 @@ public class Spinner implements GameObject {
/** Container dimensions. */ /** Container dimensions. */
private static int width, height; private static int width, height;
/** The map's overall difficulty value. */
private static float overallDifficulty = 5f;
/** The number of rotation velocities to store. */ /** The number of rotation velocities to store. */
// note: currently takes about 200ms to spin up (4 * 50) // note: currently takes about 200ms to spin up (4 * 50)
private static final int MAX_ROTATION_VELOCITIES = 50; private static final int MAX_ROTATION_VELOCITIES = 50;
@ -97,10 +100,12 @@ public class Spinner implements GameObject {
/** /**
* Initializes the Spinner data type with images and dimensions. * Initializes the Spinner data type with images and dimensions.
* @param container the game container * @param container the game container
* @param difficulty the map's overall difficulty value
*/ */
public static void init(GameContainer container) { public static void init(GameContainer container, float difficulty) {
width = container.getWidth(); width = container.getWidth();
height = container.getHeight(); height = container.getHeight();
overallDifficulty = difficulty;
} }
/** /**
@ -114,7 +119,7 @@ public class Spinner implements GameObject {
this.data = data; this.data = data;
// calculate rotations needed // calculate rotations needed
float spinsPerMinute = 100 + (data.getDifficulty() * 15); float spinsPerMinute = 100 + (overallDifficulty * 15);
rotationsNeeded = spinsPerMinute * (hitObject.getEndTime() - hitObject.getTime()) / 60000f; rotationsNeeded = spinsPerMinute * (hitObject.getEndTime() - hitObject.getTime()) / 60000f;
} }

View File

@ -1393,7 +1393,7 @@ public class Game extends BasicGameState {
// initialize objects // initialize objects
Circle.init(container, circleSize); Circle.init(container, circleSize);
Slider.init(container, circleSize, beatmap); Slider.init(container, circleSize, beatmap);
Spinner.init(container); Spinner.init(container, overallDifficulty);
Curve.init(container.getWidth(), container.getHeight(), circleSize, (Options.isBeatmapSkinIgnored()) ? Curve.init(container.getWidth(), container.getHeight(), circleSize, (Options.isBeatmapSkinIgnored()) ?
Options.getSkin().getSliderBorderColor() : beatmap.getSliderBorderColor()); Options.getSkin().getSliderBorderColor() : beatmap.getSliderBorderColor());
@ -1409,11 +1409,13 @@ public class Game extends BasicGameState {
hitResultOffset[GameData.HIT_100] = (int) (138 - (overallDifficulty * 8)); hitResultOffset[GameData.HIT_100] = (int) (138 - (overallDifficulty * 8));
hitResultOffset[GameData.HIT_50] = (int) (198 - (overallDifficulty * 10)); hitResultOffset[GameData.HIT_50] = (int) (198 - (overallDifficulty * 10));
hitResultOffset[GameData.HIT_MISS] = (int) (500 - (overallDifficulty * 10)); hitResultOffset[GameData.HIT_MISS] = (int) (500 - (overallDifficulty * 10));
// HPDrainRate (health change), overallDifficulty (scoring)
data.setDrainRate(HPDrainRate);
data.setDifficulty(overallDifficulty);
data.setHitResultOffset(hitResultOffset); data.setHitResultOffset(hitResultOffset);
// HPDrainRate (health change)
data.setDrainRate(HPDrainRate);
// difficulty multiplier (scoring)
data.calculateDifficultyMultiplier(beatmap.HPDrainRate, beatmap.circleSize, beatmap.overallDifficulty);
} }
/** /**