Added "Easy" mod.

- Halves all difficulty values and grants 3 "lives" instead of 1, with a score multiplier of 0.5x.

Other changes:
- Fixed score display in game state if score exceeds 8 digits.
- Added a "HP_DRAIN_MULTIPLIER" constant for steady HP drain/increase (to replace numeric constants).

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2014-07-16 16:56:23 -04:00
parent 7a187c4e4f
commit cb396e0d20
5 changed files with 73 additions and 16 deletions

View File

@ -29,11 +29,12 @@ import org.newdawn.slick.util.Log;
* Game mods.
*/
public enum GameMod {
NO_FAIL (0, "selection-mod-nofail.png"),
HARD_ROCK (1, "selection-mod-hardrock.png"),
SUDDEN_DEATH (2, "selection-mod-suddendeath.png"),
SPUN_OUT (3, "selection-mod-spunout.png"),
AUTO (4, "selection-mod-autoplay.png");
EASY (0, "selection-mod-easy.png"),
NO_FAIL (1, "selection-mod-nofail.png"),
HARD_ROCK (2, "selection-mod-hardrock.png"),
SUDDEN_DEATH (3, "selection-mod-suddendeath.png"),
SPUN_OUT (4, "selection-mod-spunout.png"),
AUTO (5, "selection-mod-autoplay.png");
/**
* The ID of the mod (used for positioning).
@ -130,12 +131,19 @@ public enum GameMod {
if (active)
toggle(false);
}
} else if (SUDDEN_DEATH.isActive() && NO_FAIL.isActive()) {
}
if (SUDDEN_DEATH.isActive() && NO_FAIL.isActive()) {
if (this == SUDDEN_DEATH)
NO_FAIL.toggle(false);
else
SUDDEN_DEATH.toggle(false);
}
if (EASY.isActive() && HARD_ROCK.isActive()) {
if (this == EASY)
HARD_ROCK.toggle(false);
else
EASY.toggle(false);
}
}
}

View File

@ -48,6 +48,11 @@ public class GameScore {
GRADE_D = 7,
GRADE_MAX = 8; // not a grade
/**
* Delta multiplier for steady HP drain.
*/
public static final float HP_DRAIN_MULTIPLIER = 1 / 200f;
/**
* Hit result types.
*/
@ -437,6 +442,7 @@ public class GameScore {
n /= 10;
}
}
/**
* Draws a string of scoreSymbols.
* @param str the string to draw
@ -478,7 +484,7 @@ public class GameScore {
*/
public void drawGameElements(Graphics g, int mapLength, boolean breakPeriod, boolean firstObject) {
// score
drawSymbolString(String.format("%08d", scoreDisplay),
drawSymbolString((scoreDisplay < 100000000) ? String.format("%08d", scoreDisplay) : Long.toString(scoreDisplay),
width - 2, 0, 1.0f, true);
// score percentage
@ -594,7 +600,7 @@ public class GameScore {
float rankResultScale = (height * 0.03f) / hitResults[HIT_300].getHeight();
// score
drawSymbolString((score / 100000000 == 0) ? String.format("%08d", score) : Long.toString(score),
drawSymbolString((score < 100000000) ? String.format("%08d", score) : Long.toString(score),
(int) (width * 0.18f), height / 6, symbolTextScale, false);
// result counts
@ -688,7 +694,7 @@ public class GameScore {
health += percent;
if (health > 100f)
health = 100f;
if (health < 0f)
else if (health < 0f)
health = 0f;
}
@ -904,6 +910,8 @@ public class GameScore {
// game mod score multipliers
float modMultiplier = 1f;
if (GameMod.EASY.isActive())
modMultiplier *= 0.5f;
if (GameMod.NO_FAIL.isActive())
modMultiplier *= 0.5f;
if (GameMod.HARD_ROCK.isActive())
@ -911,8 +919,7 @@ public class GameScore {
if (GameMod.SPUN_OUT.isActive())
modMultiplier *= 0.9f;
// not implemented:
// EASY (0.5x), HALF_TIME (0.3x),
// DOUBLE_TIME (1.12x), HIDDEN (1.06x), FLASHLIGHT (1.12x)
// HALF_TIME (0.3x), DOUBLE_TIME (1.12x), HIDDEN (1.06x), FLASHLIGHT (1.12x)
/**
* [SCORE FORMULA]

View File

@ -575,7 +575,7 @@ public class Slider {
if ((game.isInputKeyPressed() && distance < followCircleRadius) || isAutoMod) {
// mouse pressed and within follow circle
followCircleActive = true;
score.changeHealth(delta / 200f);
score.changeHealth(delta * GameScore.HP_DRAIN_MULTIPLIER);
// held during new repeat
if (isNewRepeat) {

View File

@ -191,12 +191,12 @@ public class Spinner {
// spin automatically (TODO: correct rotation angles)
if (GameMod.AUTO.isActive()) {
// "auto" mod (fast)
score.changeHealth(delta / 200f); // maintain health (TODO)
score.changeHealth(delta * GameScore.HP_DRAIN_MULTIPLIER);
rotate(delta / 20f);
return false;
} else if (GameMod.SPUN_OUT.isActive()) {
// "spun out" mod (slow)
score.changeHealth(delta / 200f); // maintain health (TODO)
score.changeHealth(delta * GameScore.HP_DRAIN_MULTIPLIER);
rotate(delta / 32f);
return false;
}
@ -215,7 +215,7 @@ public class Spinner {
if (lastAngle >= 0f) { // skip initial clicks
float angleDiff = Math.abs(lastAngle - angle);
if (angleDiff < Math.PI / 2) { // skip huge angle changes...
score.changeHealth(delta / 200f); // maintain health (TODO)
score.changeHealth(delta * GameScore.HP_DRAIN_MULTIPLIER);
rotate(angleDiff);
}
}

View File

@ -187,6 +187,16 @@ public class Game extends BasicGameState {
*/
private boolean checkpointLoaded = false;
/**
* Number of deaths, used if "Easy" mod is enabled.
*/
private byte deaths = 0;
/**
* Track position at death, used if "Easy" mod is enabled.
*/
private int deathTime = -1;
// game-related variables
private GameContainer container;
private StateBasedGame game;
@ -238,6 +248,8 @@ public class Game extends BasicGameState {
int trackPosition = MusicController.getPosition();
if (pauseTime > -1) // returning from pause screen
trackPosition = pauseTime;
else if (deathTime > -1) // "Easy" mod: health bar increasing
trackPosition = deathTime;
// checkpoint
if (checkpointLoaded) {
@ -442,6 +454,16 @@ public class Game extends BasicGameState {
return;
}
// "Easy" mod: multiple "lives"
if (GameMod.EASY.isActive() && deathTime > -1) {
if (score.getHealth() < 99f)
score.changeHealth(delta / 10f);
else {
MusicController.resume();
deathTime = -1;
}
}
score.updateScoreDisplay(delta);
// map complete!
@ -509,8 +531,18 @@ public class Game extends BasicGameState {
score.updateComboBurst(delta);
// drain health
score.changeHealth(delta / -200f);
score.changeHealth(delta * -1 * GameScore.HP_DRAIN_MULTIPLIER);
if (!score.isAlive()) {
// "Easy" mod
if (GameMod.EASY.isActive()) {
deaths++;
if (deaths < 3) {
deathTime = trackPosition;
MusicController.pause();
return;
}
}
// game over, force a restart
restart = RESTART_LOSE;
game.enterState(Opsu.STATE_GAMEPAUSEMENU);
@ -748,6 +780,8 @@ public class Game extends BasicGameState {
countdown2Sound = false;
countdownGoSound = false;
checkpointLoaded = false;
deaths = 0;
deathTime = -1;
// load the first timingPoint
if (!osu.timingPoints.isEmpty()) {
@ -892,6 +926,14 @@ public class Game extends BasicGameState {
HPDrainRate = Math.min(HPDrainRate * 1.4f, 10);
}
// "Easy" modifiers
else if (GameMod.EASY.isActive()) {
circleSize /= 2f;
approachRate /= 2f;
overallDifficulty /= 2f;
HPDrainRate /= 2f;
}
// initialize objects
Circle.init(container, circleSize);
Slider.init(container, circleSize, osu);