Merge pull request #20 from fluddokt/omaster

Visual changes:
- Larger hit circles
- Circle overlay after drawing circle
- Fade in approach circles
- Fade in sliders
- Fade out hit lighting
- Hit Error Bar
- Reverse Slider ball rotation on repeats
- Fill bg image to screen while keeping aspect ratio
- Added a few more resolutions
This commit is contained in:
Jeffrey Han 2015-02-15 00:45:43 -05:00
commit b7f86f1962
7 changed files with 167 additions and 23 deletions

View File

@ -153,6 +153,27 @@ public class GameData {
/** List of hit result objects associated with hit objects. */
private LinkedList<OsuHitObjectResult> hitResultList;
/**
* class to store Hit Error information.
* @author fluddokt
*/
class HitErrorInfo {
int time, x, y, timeDiff;
public HitErrorInfo(int time, int x, int y, int timeDiff) {
this.time = time;
this.x = x;
this.y = y;
this.timeDiff = timeDiff;
}
}
/**
* List of stored Hit Error Info
*/
private LinkedList<HitErrorInfo> hitErrorList = new LinkedList<HitErrorInfo>();
/**
* Hit result helper class.
*/
@ -225,6 +246,10 @@ public class GameData {
/** Container dimensions. */
private int width, height;
/** Time offsets for obtaining each hit result (indexed by HIT_* constants). */
private int[] hitResultOffset;
/**
* Constructor for gameplay.
* @param width container width
@ -283,6 +308,7 @@ public class GameData {
comboEnd = 0;
comboBurstIndex = -1;
scoreData = null;
hitErrorList.clear();
}
/**
@ -364,6 +390,10 @@ public class GameData {
public void setDifficulty(float difficulty) { this.difficulty = difficulty; }
public float getDifficulty() { return difficulty; }
/** Sets the HitResultOffset */
public void setHitResultOffset(int[] hitResultOffset) {this.hitResultOffset = hitResultOffset; }
/**
* Draws a number with defaultSymbols.
* @param n the number to draw
@ -517,6 +547,50 @@ public class GameData {
// combo count
if (combo > 0) // 0 isn't a combo
drawSymbolString(String.format("%dx", combo), 10, height - 10 - symbolHeight, 1.0f, false);
//*
//Draw Hit Error bar
final int fadeDelay = 5000;
int hitErrorY = 30;
Iterator<HitErrorInfo> iter2 = hitErrorList.iterator();
g.setColor(Color.black);
g.fillRect(width / 2f - 3 - hitResultOffset[HIT_50],
height - marginX - hitErrorY - 10,
hitResultOffset[HIT_50] * 2,
20);
g.setColor(Utils.COLOR_LIGHT_ORANGE);
g.fillRect(width / 2f - 3 - hitResultOffset[HIT_50],
height - marginX - hitErrorY - 3,
hitResultOffset[HIT_50] * 2,
6);
g.setColor(Utils.COLOR_LIGHT_GREEN);
g.fillRect(width / 2f - 3 - hitResultOffset[HIT_100],
height - marginX - hitErrorY - 3,
hitResultOffset[HIT_100] * 2,
6);
g.setColor(Utils.COLOR_LIGHT_BLUE);
g.fillRect(width / 2f - 3 - hitResultOffset[HIT_300],
height- marginX - hitErrorY - 3,
hitResultOffset[HIT_300] * 2,
6);
g.setColor(Color.white);
g.drawRect(width / 2f - 3, height - marginX - hitErrorY - 10, 6, 20);
while (iter2.hasNext()) {
HitErrorInfo info = iter2.next();
int time = info.time;
if (Math.abs(info.timeDiff) < hitResultOffset[GameData.HIT_50]
&& time + fadeDelay > trackPosition) {
float alpha = 1 - ((float) (trackPosition - time) / fadeDelay);
Color col = new Color(Color.white);
col.a = alpha;
g.setColor(col);
g.fillRect(width / 2 + info.timeDiff - 1, height - marginX
- hitErrorY - 10, 2, 20);
} else {
iter2.remove();
}
}
//*/
} else {
// grade
Grade grade = getGrade();
@ -528,6 +602,9 @@ public class GameData {
);
}
}
}
/**
@ -649,6 +726,9 @@ public class GameData {
float scale = 1f + ((trackPosition - hitResult.time) / (float) fadeDelay);
Image scaledLighting = GameImage.LIGHTING.getImage().getScaledCopy(scale);
Image scaledLighting1 = GameImage.LIGHTING1.getImage().getScaledCopy(scale);
scaledLighting.setAlpha(hitResult.alpha);
scaledLighting1.setAlpha(hitResult.alpha);
scaledLighting.draw(hitResult.x - (scaledLighting.getWidth() / 2f),
hitResult.y - (scaledLighting.getHeight() / 2f), hitResult.color);
scaledLighting1.draw(hitResult.x - (scaledLighting1.getWidth() / 2f),
@ -1008,4 +1088,15 @@ public class GameData {
* @return true if gameplay, false if score viewing
*/
public boolean isGameplay() { return gameplay; }
/**
* add a Hit Error Info to the list.
* @param time when it should have been hit
* @param x, y the location that it hit
* @param timeDiff the difference from the time from when it was actually hit
*/
public void addHitError(int time, int x, int y, int timeDiff) {
hitErrorList.add(new HitErrorInfo(time, x, y, timeDiff));
}
}

View File

@ -418,8 +418,10 @@ public class Options {
RES_800_600 (800, 600),
RES_1024_600 (1024, 600),
RES_1024_768 (1024, 768),
RES_1280_720 (1280, 720),
RES_1280_800 (1280, 800),
RES_1280_960 (1280, 960),
RES_1280_1024 (1280, 1024),
RES_1366_768 (1366, 768),
RES_1440_900 (1440, 900),
RES_1600_900 (1600, 900),
@ -428,7 +430,9 @@ public class Options {
RES_1920_1080 (1920, 1080),
RES_1920_1200 (1920, 1200),
RES_2560_1440 (2560, 1440),
RES_2560_1600 (2560, 1600);
RES_2560_1600 (2560, 1600),
RES_3840_2160 (3840, 2160);
/** Screen dimensions. */
private int width, height;

View File

@ -268,14 +268,20 @@ public class OsuFile implements Comparable<OsuFile> {
bgImageMap.put(this, bgImage);
}
// fit image to screen
int swidth = width;
int sheight = height;
if (!stretch) {
// fit image to screen
if (bgImage.getWidth() / (float) bgImage.getHeight() > width / (float) height) // x > y
sheight = (int) (width * bgImage.getHeight() / (float) bgImage.getWidth());
else
swidth = (int) (height * bgImage.getWidth() / (float) bgImage.getHeight());
} else {
//fill image to screen while keeping aspect ratio
if (bgImage.getWidth() / (float) bgImage.getHeight() > width / (float) height) // x > y
swidth = (int) (height * bgImage.getWidth() / (float) bgImage.getHeight());
else
sheight = (int) (width * bgImage.getHeight() / (float) bgImage.getWidth());
}
bgImage = bgImage.getScaledCopy(swidth, sheight);

View File

@ -79,7 +79,10 @@ public class Utils {
COLOR_YELLOW_ALPHA = new Color(255, 255, 0, 0.4f),
COLOR_WHITE_FADE = new Color(255, 255, 255, 1f),
COLOR_RED_HOVER = new Color(255, 112, 112),
COLOR_GREEN = new Color(137, 201, 79);
COLOR_GREEN = new Color(137, 201, 79),
COLOR_LIGHT_ORANGE = new Color(255,192,128),
COLOR_LIGHT_GREEN = new Color(128,255,128),
COLOR_LIGHT_BLUE = new Color(128,128,255);
/** The default map colors, used when a map does not provide custom colors. */
public static final Color[] DEFAULT_COMBO = {
@ -872,4 +875,20 @@ public class Utils {
list.add(str);
return list;
}
/**
* Clamps the value to be in between low and high
* @param val
* @param low
* @param high
* @return val clamped between low and high
* @author fluddokt (https://github.com/fluddokt)
*/
public static float clamp(float val, int low, int high) {
if(val < low)
return low;
if(val > high)
return high;
return val;
}
}

View File

@ -55,7 +55,7 @@ public class Circle implements HitObject {
* @param circleSize the map's circleSize value
*/
public static void init(GameContainer container, float circleSize) {
int diameter = (int) (96 - (circleSize * 8));
int diameter = (int) (104 - (circleSize * 8));
diameter = (int) (diameter * OsuHitObject.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));
@ -83,15 +83,20 @@ public class Circle implements HitObject {
int timeDiff = hitObject.getTime() - trackPosition;
if (timeDiff >= 0) {
float x = hitObject.getX(), y = hitObject.getY();
float approachScale = 1 + (timeDiff * 2f / game.getApproachTime());
Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color);
float alpha = (approachScale > 3.3f) ? 0f : 1f - (approachScale - 1f) / 2.7f;
float oldAlpha = color.a;
float x = hitObject.getX(), y = hitObject.getY();
float scale = timeDiff / (float)game.getApproachTime();
float approachScale = 1 + scale * 3;
color.a = 1 - scale;
Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color);
float alpha = Utils.clamp((1 - scale) * 2, 0, 1);
color.a = alpha;
Utils.COLOR_WHITE_FADE.a = alpha;
Utils.drawCentered(GameImage.HITCIRCLE_OVERLAY.getImage(), x, y, Utils.COLOR_WHITE_FADE);
Utils.drawCentered(GameImage.HITCIRCLE.getImage(), x, y, color);
Utils.drawCentered(GameImage.HITCIRCLE_OVERLAY.getImage(), x, y, Utils.COLOR_WHITE_FADE);
color.a = oldAlpha;
Utils.COLOR_WHITE_FADE.a = 1f;
data.drawSymbolNumber(hitObject.getComboNumber(), x, y,
@ -105,8 +110,7 @@ public class Circle implements HitObject {
* @return the hit result (GameData.HIT_* constants)
*/
private int hitResult(int time) {
int trackPosition = MusicController.getPosition();
int timeDiff = Math.abs(trackPosition - time);
int timeDiff = Math.abs(time);
int[] hitResultOffset = game.getHitResultOffsets();
int result = -1;
@ -128,8 +132,12 @@ public class Circle implements HitObject {
double distance = Math.hypot(hitObject.getX() - x, hitObject.getY() - y);
int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
if (distance < circleRadius) {
int result = hitResult(hitObject.getTime());
int trackPosition = MusicController.getPosition();
int timeDiff = trackPosition - hitObject.getTime();
int result = hitResult(timeDiff);
if (result > -1) {
data.addHitError(hitObject.getTime(), x, y, timeDiff);
data.hitResult(
hitObject.getTime(), result,
hitObject.getX(), hitObject.getY(),

View File

@ -98,7 +98,7 @@ public class Slider implements HitObject {
* @param osu the associated OsuFile object
*/
public static void init(GameContainer container, float circleSize, OsuFile osu) {
int diameter = (int) (96 - (circleSize * 8));
int diameter = (int) (104 - (circleSize * 8));
diameter = (int) (diameter * OsuHitObject.getXMultiplier()); // convert from Osupixels (640x480)
// slider ball
@ -110,7 +110,7 @@ public class Slider implements HitObject {
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);
sliderBall = new Animation(sliderBallImages, 60);
sliderBall = new Animation(sliderBallImages, 30);
GameImage.SLIDER_FOLLOWCIRCLE.setImage(GameImage.SLIDER_FOLLOWCIRCLE.getImage().getScaledCopy(diameter * 259 / 128, diameter * 259 / 128));
GameImage.REVERSEARROW.setImage(GameImage.REVERSEARROW.getImage().getScaledCopy(diameter, diameter));
@ -143,14 +143,19 @@ public class Slider implements HitObject {
@Override
public void draw(int trackPosition, boolean currentObject, Graphics g) {
float x = hitObject.getX(), y = hitObject.getY();
int timeDiff = hitObject.getTime() - trackPosition;
float approachScale = (timeDiff >= 0) ? 1 + (timeDiff * 2f / game.getApproachTime()) : 1f;
float alpha = (approachScale > 3.3f) ? 0f : 1f - (approachScale - 1f) / 2.7f;
float oldAlpha = color.a;
float oldAlphaFade = Utils.COLOR_WHITE_FADE.a;
color.a = alpha;
float x = hitObject.getX(), y = hitObject.getY();
float scale = timeDiff / (float)game.getApproachTime();
float approachScale = 1 + scale*3 ;
float alpha = (1 - scale);//(approachScale > 3.3f) ? 0f : 1f - (approachScale - 1f) / 2.7f;
color.a = Utils.clamp(alpha* 0.5f, 0, 1);
alpha = Utils.clamp(alpha, 0, 1);
Utils.COLOR_WHITE_FADE.a = alpha;
// curve
@ -165,6 +170,8 @@ public class Slider implements HitObject {
}
}
color.a = alpha;
Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
Image hitCircle = GameImage.HITCIRCLE.getImage();
@ -174,8 +181,8 @@ public class Slider implements HitObject {
Utils.drawCentered(hitCircleOverlay, endPos[0], endPos[1], Utils.COLOR_WHITE_FADE);
// start circle
Utils.drawCentered(hitCircleOverlay, x, y, Utils.COLOR_WHITE_FADE);
Utils.drawCentered(hitCircle, x, y, color);
Utils.drawCentered(hitCircleOverlay, x, y, Utils.COLOR_WHITE_FADE);
if (sliderClicked)
; // don't draw current combo number if already clicked
else
@ -208,23 +215,30 @@ public class Slider implements HitObject {
if (timeDiff >= 0) {
// approach circle
color.a = 1 - scale;
Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color);
} else {
float[] c = curve.pointAt(getT(trackPosition, false));
float[] c2 = curve.pointAt(getT(trackPosition, false) + 0.01f);
// slider ball
sliderBall.updateNoDraw();//TODO ..... I can't thing of anything else
//It might be better to make your own animation class or something
//also it might be better to update the animation based on the distance traveled
Image sliderBallFrame = sliderBall.getCurrentFrame();
float angle = (float) (Math.atan2(c2[1] - c[1], c2[0] - c[0]) * 180 / Math.PI);
float angle = (float) (Math.atan2(c2[1] - c[1], c2[0] - c[0]) * 180 / Math.PI)
+ (currentRepeats % 2 == 1 ? 180 : 0);
sliderBallFrame.setRotation(angle);
sliderBallFrame.drawCentered(c[0], c[1]);
// follow circle
if (followCircleActive)
GameImage.SLIDER_FOLLOWCIRCLE.getImage().drawCentered(c[0], c[1]);
}
}
/**
* Calculates the slider hit result.
* @return the hit result (GameData.HIT_* constants)
@ -275,6 +289,7 @@ public class Slider implements HitObject {
//else not a hit
if (result > -1) {
data.addHitError(hitObject.getTime(), x,y,trackPosition - hitObject.getTime());
sliderClicked = true;
data.sliderTickResult(hitObject.getTime(), result,
hitObject.getX(), hitObject.getY(), hitObject.getHitSoundType());

View File

@ -878,6 +878,7 @@ public class Game extends BasicGameState {
// HPDrainRate (health change), overallDifficulty (scoring)
data.setDrainRate(HPDrainRate);
data.setDifficulty(overallDifficulty);
data.setHitResultOffset(hitResultOffset);
}
/**