Skip to content

Commit

Permalink
Implemented osu! HP algorithms. (base: ppy/osu-iPhone@e72787f)
Browse files Browse the repository at this point in the history
Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
  • Loading branch information
itdelatrisu committed Jan 7, 2017
1 parent 8655794 commit e62822a
Show file tree
Hide file tree
Showing 6 changed files with 470 additions and 89 deletions.
119 changes: 58 additions & 61 deletions src/itdelatrisu/opsu/GameData.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.Health;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.objects.curves.Curve;
Expand All @@ -47,9 +48,6 @@
* Holds game data and renders all related elements.
*/
public class GameData {
/** Delta multiplier for steady HP drain. */
public static final float HP_DRAIN_MULTIPLIER = 1 / 200f;

/** Time, in milliseconds, for a hit result to remain existent. */
public static final int HITRESULT_TIME = 833;

Expand Down Expand Up @@ -149,9 +147,11 @@ public Image getMenuImage() {
HIT_300G = 6, // Geki
HIT_SLIDER10 = 7,
HIT_SLIDER30 = 8,
HIT_MAX = 9, // not a hit result
HIT_SLIDER_REPEAT = 10, // not a hit result
HIT_ANIMATION_RESULT = 11; // not a hit result
HIT_MAX = 9,
HIT_SLIDER_REPEAT = 10,
HIT_ANIMATION_RESULT = 11,
HIT_SPINNERSPIN = 12,
HIT_SPINNERBONUS = 13;

/** Hit result-related images (indexed by HIT_* constants to HIT_MAX). */
private Image[] hitResults;
Expand Down Expand Up @@ -298,19 +298,12 @@ public HitObjectResult(int time, int result, float x, float y, Color color,
/** Displayed game score percent (for animation, slightly behind score percent). */
private float scorePercentDisplay;

/** Current health bar percentage. */
private float health;

/** Displayed health (for animation, slightly behind health). */
private float healthDisplay;
/** Health. */
private Health health = new Health();

/** The difficulty multiplier used in the score formula. */
private int difficultyMultiplier = 2;

/** Beatmap HPDrainRate value. (0:easy ~ 10:hard) */
@SuppressWarnings("unused")
private float drainRate = 5f;

/** Default text symbol images. */
private Image[] defaultSymbols;

Expand Down Expand Up @@ -383,10 +376,8 @@ public void clear() {
score = 0;
scoreDisplay = 0;
scorePercentDisplay = 0f;
health = 100f;
healthDisplay = 100f;
health.reset();
hitResultCount = new int[HIT_MAX];
drainRate = 5f;
if (hitResultList != null) {
for (HitObjectResult hitResult : hitResultList) {
if (hitResult.curve != null)
Expand Down Expand Up @@ -477,12 +468,6 @@ public void loadImages() {
*/
public Image getScoreSymbolImage(char c) { return scoreSymbols.get(c); }

/**
* Sets the health drain rate.
* @param drainRate the new drain rate [0-10]
*/
public void setDrainRate(float drainRate) { this.drainRate = drainRate; }

/**
* Sets the array of hit result offsets.
* @param hitResultOffset the time offset array (of size {@link #HIT_MAX})
Expand Down Expand Up @@ -713,7 +698,7 @@ public void drawGameElements(Graphics g, boolean breakPeriod, boolean firstObjec

if (!breakPeriod && !relaxAutoPilot) {
// scorebar
float healthRatio = healthDisplay / 100f;
float healthRatio = health.getHealthDisplay() / 100f;
if (firstObject) { // gradually move ki before map begins
if (firstObjectTime >= 1500 && trackPosition < firstObjectTime - 500)
healthRatio = (float) trackPosition / (firstObjectTime - 500);
Expand All @@ -736,9 +721,9 @@ public void drawGameElements(Graphics g, boolean breakPeriod, boolean firstObjec
colourCropped.setAlpha(1f);

Image ki = null;
if (health >= 50f)
if (health.getHealth() >= 50f)
ki = GameImage.SCOREBAR_KI.getImage();
else if (health >= 25f)
else if (health.getHealth() >= 25f)
ki = GameImage.SCOREBAR_KI_DANGER.getImage();
else
ki = GameImage.SCOREBAR_KI_DANGER2.getImage();
Expand Down Expand Up @@ -1104,32 +1089,35 @@ private void drawHitAnimations(HitObjectResult hitResult, int trackPosition) {
}

/**
* Changes health by a given percentage, modified by drainRate.
* @param percent the health percentage
* Returns the current health percentage.
*/
public void changeHealth(float percent) {
// TODO: drainRate formula
health += percent;
if (health > 100f)
health = 100f;
else if (health < 0f)
health = 0f;
}
public float getHealthPercent() { return health.getHealth(); }

/**
* Returns the current health percentage.
* Sets the health modifiers.
* @param hpDrainRate the HP drain rate
* @param hpMultiplierNormal the normal HP multiplier
* @param hpMultiplierComboEnd the combo-end HP multiplier
*/
public float getHealth() { return health; }
public void setHealthModifiers(float hpDrainRate, float hpMultiplierNormal, float hpMultiplierComboEnd) {
health.setModifiers(hpDrainRate, hpMultiplierNormal, hpMultiplierComboEnd);
}

/**
* Returns false if health is zero.
* If "No Fail" or "Auto" mods are active, this will always return true.
*/
public boolean isAlive() {
return (health > 0f || GameMod.NO_FAIL.isActive() || GameMod.AUTO.isActive() ||
return (health.getHealth() > 0f || GameMod.NO_FAIL.isActive() || GameMod.AUTO.isActive() ||
GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive());
}

/**
* Changes health by a raw value.
* @param value the health value
*/
public void changeHealth(float value) { health.changeHealth(value); }

/**
* Changes score by a raw value (not affected by other modifiers).
* @param value the score value
Expand Down Expand Up @@ -1237,18 +1225,7 @@ public void updateDisplays(int delta) {
}

// health display
if (healthDisplay != health) {
float shift = delta / 15f;
if (healthDisplay < health) {
healthDisplay += shift;
if (healthDisplay > health)
healthDisplay = health;
} else {
healthDisplay -= shift;
if (healthDisplay < health)
healthDisplay = health;
}
}
health.update(delta);

// combo burst
if (comboBurstIndex > -1 && Options.isComboBurstEnabled()) {
Expand Down Expand Up @@ -1328,7 +1305,7 @@ private void resetComboStreak() {
SoundController.playSound(SoundEffect.COMBOBREAK);
combo = 0;
if (GameMod.SUDDEN_DEATH.isActive())
health = 0f;
health.setHealth(0f);
}

/**
Expand Down Expand Up @@ -1370,15 +1347,13 @@ public void sendSliderTickResult(int time, int result, float x, float y, HitObje
switch (result) {
case HIT_SLIDER30:
hitValue = 30;
changeHealth(2f);
SoundController.playHitSound(
hitObject.getEdgeHitSoundType(repeat),
hitObject.getSampleSet(repeat),
hitObject.getAdditionSampleSet(repeat));
break;
case HIT_SLIDER10:
hitValue = 10;
changeHealth(1f);
SoundController.playHitSound(HitSound.SLIDERTICK);
break;
case HIT_MISS:
Expand All @@ -1392,6 +1367,7 @@ public void sendSliderTickResult(int time, int result, float x, float y, HitObje
// calculate score and increment combo streak
score += hitValue;
incrementComboStreak();
health.changeHealthForHit(result);

if (!Options.isPerfectHitBurstEnabled())
; // hide perfect hit results
Expand All @@ -1401,6 +1377,29 @@ public void sendSliderTickResult(int time, int result, float x, float y, HitObje
fullObjectCount++;
}

/**
* Handles a spinner spin result.
* @param result the hit result (HIT_* constants)
*/
public void sendSpinnerSpinResult(int result) {
int hitValue = 0;
switch (result) {
case HIT_SPINNERSPIN:
hitValue = 100;
SoundController.playSound(SoundEffect.SPINNERSPIN);
break;
case HIT_SPINNERBONUS:
hitValue = 1100;
SoundController.playSound(SoundEffect.SPINNERBONUS);
break;
default:
return;
}

score += hitValue;
health.changeHealthForHit(result);
}

/**
* Returns the score for a hit based on the following score formula:
* <p>
Expand Down Expand Up @@ -1474,11 +1473,9 @@ private int handleHitResult(int time, int result, float x, float y, Color color,
switch (result) {
case HIT_300:
hitValue = 300;
changeHealth(5f);
break;
case HIT_100:
hitValue = 100;
changeHealth(2f);
comboEnd |= 1;
break;
case HIT_50:
Expand All @@ -1487,7 +1484,6 @@ private int handleHitResult(int time, int result, float x, float y, Color color,
break;
case HIT_MISS:
hitValue = 0;
changeHealth(-10f);
comboEnd |= 2;
resetComboStreak();
break;
Expand All @@ -1505,23 +1501,24 @@ private int handleHitResult(int time, int result, float x, float y, Color color,
if (!noIncrementCombo)
incrementComboStreak();
}
health.changeHealthForHit(result);
hitResultCount[result]++;
fullObjectCount++;

// last element in combo: check for Geki/Katu
if (end) {
if (comboEnd == 0) {
result = HIT_300G;
changeHealth(15f);
health.changeHealthForHit(HIT_300G);
hitResultCount[result]++;
} else if ((comboEnd & 2) == 0) {
if (result == HIT_100) {
result = HIT_100K;
changeHealth(10f);
health.changeHealthForHit(HIT_100K);
hitResultCount[result]++;
} else if (result == HIT_300) {
result = HIT_300K;
changeHealth(10f);
health.changeHealthForHit(HIT_300K);
hitResultCount[result]++;
}
}
Expand Down
17 changes: 17 additions & 0 deletions src/itdelatrisu/opsu/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,23 @@ public static float lerp(float a, float b, float t) {
return a * (1 - t) + b * t;
}

/**
* Maps a difficulty value to the given range.
* @param difficulty the difficulty value
* @param min the min
* @param mid the mid
* @param max the max
* @author peppy (ppy/osu-iPhone:OsuFunctions.m)
*/
public static float mapDifficultyRange(float difficulty, float min, float mid, float max) {
if (difficulty > 5f)
return mid + (max - mid) * (difficulty - 5f) / 5f;
else if (difficulty < 5f)
return mid - (mid - min) * (5f - difficulty) / 5f;
else
return mid;
}

/**
* Returns true if a game input key is pressed (mouse/keyboard left/right).
* @return true if pressed
Expand Down
Loading

0 comments on commit e62822a

Please # to comment.