Skip to content

Commit 118d94c

Browse files
committed
Merge remote-tracking branch 'droid/master' into GLES
# Conflicts: # src/com/rian/andengine/HUD.kt
2 parents 24e2a2a + b2f879d commit 118d94c

11 files changed

Lines changed: 240 additions & 90 deletions

File tree

src/com/osudroid/ui/v2/CalibrationScene.kt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import androidx.annotation.IdRes
55
import com.edlplan.framework.easing.Easing
66
import com.osudroid.ui.v1.SettingsFragment
77
import com.osudroid.utils.mainThread
8-
import com.osudroid.utils.standardDeviation
98
import com.osudroid.utils.updateThread
109
import com.reco1l.andengine.Anchor
1110
import com.reco1l.andengine.UIEngine
@@ -38,8 +37,11 @@ import com.reco1l.framework.math.Vec4
3837
import com.reco1l.osu.ui.PromptDialog
3938
import com.osudroid.beatmaps.DroidHitWindow
4039
import com.osudroid.math.Interpolation
40+
import com.osudroid.utils.median
41+
import com.osudroid.utils.standardDeviation
4142
import com.rian.andengine.modifier.ModifierType
4243
import kotlin.math.abs
44+
import kotlin.math.exp
4345
import kotlin.math.roundToInt
4446
import org.andengine.input.touch.TouchEvent
4547
import ru.nsu.ccfit.zuev.audio.Status
@@ -547,7 +549,7 @@ object CalibrationScene : UIScene() {
547549
tapOffsets.removeAt(0)
548550
}
549551

550-
pendingOffset = tapOffsets.standardDeviation().roundToInt().coerceIn(OFFSET_MIN, OFFSET_MAX)
552+
pendingOffset = computePendingOffset()
551553

552554
val judgement = when {
553555
absErr < judgementHitWindow.greatWindow -> Judgement.PERFECT
@@ -784,4 +786,21 @@ object CalibrationScene : UIScene() {
784786
Config.setOffset(pendingOffset.toFloat())
785787
Config.setInt("offset", pendingOffset)
786788
}
789+
790+
private fun computePendingOffset(): Int {
791+
if (tapOffsets.isEmpty()) {
792+
return 0
793+
}
794+
795+
val unstableRate = tapOffsets.standardDeviation()
796+
var offset = tapOffsets.median()
797+
798+
if (unstableRate >= 90) {
799+
// A demonstrative graph of this algorithm is embedded in https://github.com/ppy/osu/discussions/30521.
800+
// This prevents high unstable rate from suggesting potentially invalid offsets.
801+
offset *= exp(-0.0116 * (unstableRate - 90))
802+
}
803+
804+
return offset.roundToInt().coerceIn(OFFSET_MIN, OFFSET_MAX)
805+
}
787806
}

src/com/osudroid/ui/v2/hud/elements/HUDAverageOffsetCounter.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ class HUDAverageOffsetCounter : HUDElement() {
1515
text = "Avg offset: 0ms"
1616
}
1717

18-
private var value = 0f
18+
private var value = 0.0
1919
set(value) {
2020
if (field != value) {
2121
field = value
22-
text.text = "Avg offset: ${(value * 1000).roundToInt()}ms"
22+
text.text = "Avg offset: ${value.roundToInt()}ms"
2323
}
2424
}
2525

@@ -28,6 +28,6 @@ class HUDAverageOffsetCounter : HUDElement() {
2828
}
2929

3030
override fun onGameplayUpdate(game: GameScene, secondsElapsed: Float) {
31-
value = if (game.offsetRegs > 0) game.offsetSum / game.offsetRegs else 0f
31+
value = game.stat.averageHitOffset
3232
}
3333
}

src/com/osudroid/ui/v2/modmenu/ModIcon.kt

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,44 @@ class ModIcon(val mod: Mod) : UIContainer(), ISkinnable {
3737
return ResourceManager.getInstance().getTexture(mod.iconTextureName)?.takeUnless { it is BlankTextureRegion }
3838
}
3939

40+
private fun setupContent() {
41+
detachChildren()
42+
43+
val texture = fetchTextureRegion()
44+
45+
if (texture != null) {
46+
background = null
47+
48+
attachChild(OsuSkinnableSprite(mod.iconTextureName).apply {
49+
width = FillParent
50+
height = FillParent
51+
buffer = sharedSpriteVBO
52+
})
53+
} else {
54+
background = UIBox().apply {
55+
applyTheme = { color = it.accentColor * 0.1f }
56+
}
57+
58+
attachChild(UIText().apply {
59+
anchor = Anchor.Center
60+
origin = Anchor.Center
61+
text = mod.acronym
62+
font = ResourceManager.getInstance().getFont("smallFont")
63+
applyTheme = { color = it.accentColor }
64+
})
65+
}
66+
67+
shouldUpdateTexture = false
68+
}
69+
70+
71+
override fun onAttached() {
72+
if (shouldUpdateTexture) {
73+
setupContent()
74+
}
75+
76+
super.onAttached()
77+
}
4078

4179
override fun onManagedDraw(pGLState: GLState, pCamera: Camera) {
4280

@@ -52,33 +90,7 @@ class ModIcon(val mod: Mod) : UIContainer(), ISkinnable {
5290

5391
override fun onManagedUpdate(deltaTimeSec: Float) {
5492
if (shouldUpdateTexture) {
55-
detachChildren()
56-
57-
val texture = fetchTextureRegion()
58-
59-
if (texture != null) {
60-
background = null
61-
62-
attachChild(OsuSkinnableSprite(mod.iconTextureName).apply {
63-
width = FillParent
64-
height = FillParent
65-
buffer = sharedSpriteVBO
66-
})
67-
} else {
68-
background = UIBox().apply {
69-
applyTheme = { color = it.accentColor * 0.1f }
70-
}
71-
72-
attachChild(UIText().apply {
73-
anchor = Anchor.Center
74-
origin = Anchor.Center
75-
text = mod.acronym
76-
font = ResourceManager.getInstance().getFont("smallFont")
77-
applyTheme = { color = it.accentColor }
78-
})
79-
}
80-
81-
shouldUpdateTexture = false
93+
setupContent()
8294
}
8395

8496
super.onManagedUpdate(deltaTimeSec)

src/com/osudroid/utils/Collections.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,19 @@ fun Collection<Double>.standardDeviation(): Double {
1414
val mean = average()
1515

1616
return sqrt(sumOf { (it - mean).pow(2) } / size)
17+
}
18+
19+
/**
20+
* Returns the median of the elements in this [Collection]. If the collection is empty, returns 0.
21+
*/
22+
fun Collection<Double>.median(): Double {
23+
if (isEmpty()) {
24+
return 0.0
25+
}
26+
27+
val sorted = sorted()
28+
val center = sorted.size / 2
29+
30+
// Use average of the 2 central values if the length is even.
31+
return if (sorted.size % 2 == 0) (sorted[center - 1] + sorted[center]) / 2 else sorted[center]
1732
}

src/com/rian/andengine/HUD.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import com.rian.andengine.timing.IClockReceiver
99
import com.rian.andengine.timing.IFrameBasedClock
1010
import org.andengine.engine.camera.hud.HUD as AndEngineHUD
1111
import org.andengine.entity.scene.Scene
12+
import org.andengine.engine.camera.Camera
13+
import javax.microedition.khronos.opengles.GL10
1214

1315
/**
1416
* An [AndEngineHUD] that provides an [IFrameBasedClock] to its [UIComponent] children.
@@ -125,6 +127,34 @@ open class HUD : AndEngineHUD(), IClockProvider<IFrameBasedClock?>, IClockReceiv
125127

126128
//endregion
127129

130+
private var hudCamera: Camera? = null
131+
132+
override fun onDraw(pGL: GL10, pCamera: Camera) {
133+
val mc = mCamera
134+
var cam = pCamera
135+
136+
// HUD should not be affected by camera zoom since it is displayed across all scenes (meaning it should fill the
137+
// entire screen). However, CameraScene (which is what AndEngine's HUD extends) passes the zoomed main camera
138+
// to children.
139+
// We need to give children an un-zoomed camera, not just due to that, but to also ensure that clipping computes
140+
// correct scissor regions in this HUD's space.
141+
if (mc != null) {
142+
val rawWidth = mc.widthRaw
143+
val rawHeight = mc.heightRaw
144+
var hudCamera = hudCamera
145+
146+
if (hudCamera == null || hudCamera.widthRaw != rawWidth || hudCamera.heightRaw != rawHeight) {
147+
hudCamera = Camera(0f, 0f, rawWidth, rawHeight)
148+
this.hudCamera = hudCamera
149+
}
150+
151+
hudCamera.setSurfaceSize(0, 0, pCamera.surfaceWidth, pCamera.surfaceHeight)
152+
cam = hudCamera
153+
}
154+
155+
super.onDraw(pGL, cam)
156+
}
157+
128158
override fun setChildScene(childScene: Scene?, modalDraw: Boolean, modalUpdate: Boolean, modalTouch: Boolean) {
129159
this.childScene?.onDetached()
130160
super.setChildScene(childScene, modalDraw, modalUpdate, modalTouch)

src/ru/nsu/ccfit/zuev/osu/game/GameObjectListener.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ void onSliderHit(int id, int score, PointF judgementPos,
4949
float getElapsedTime();
5050

5151
/**
52-
* Whether the game has just seeked and is in the first frame where active objects run their first update.
53-
* Used to suppress hitsounds for nested slider objects that were already passed at the seek target.
52+
* Whether the game has recently seeked and active objects are still in the catch-up frames.
53+
* Used to suppress hitsounds for slider objects (head, ticks, repeats) that were already passed at the seek target.
5454
*/
5555
boolean isAfterSeek();
5656
}

0 commit comments

Comments
 (0)