Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some corrections to HUD editor #492

Merged
merged 13 commits into from
Mar 6, 2025
Binary file removed assets/one-one.png
Binary file not shown.
Binary file added assets/restore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 41 additions & 3 deletions src/com/reco1l/osu/hud/GameplayHUD.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import java.io.File
import kotlin.reflect.full.primaryConstructor
import ru.nsu.ccfit.zuev.osu.helper.StringTable
import ru.nsu.ccfit.zuev.osuplus.R
import kotlin.reflect.*

class GameplayHUD : Container(), IGameplayEvents {

Expand All @@ -38,6 +39,12 @@ class GameplayHUD : Container(), IGameplayEvents {
// Move to front
if (value != null) {
setChildIndex(value, mChildren.size - 1)

// Preserve overlay on top of the element.
if (value.editorOverlay != null) {
setChildIndex(value.editorOverlay, mChildren.size - 1)
}

elementSelector?.collapse()
}
}
Expand Down Expand Up @@ -67,16 +74,27 @@ class GameplayHUD : Container(), IGameplayEvents {
}


/**
* Checks if the HUD has an element of the specified type.
*/
fun hasElement(type: KClass<out HUDElement>): Boolean {
return mChildren?.any { type.isInstance(it) } ?: false
}

/**
* Adds an element to the HUD.
*/
fun addElement(data: HUDElementSkinData, inEditMode: Boolean = isInEditMode) {
val element = data.type.primaryConstructor!!.call()
attachChild(element)
element.restoreData = data
element.setSkinData(data)
element.setEditMode(inEditMode)
}

/**
* Called when the back button is pressed.
*/
fun onBackPress() {

if (elementSelector?.isExpanded == true) {
Expand Down Expand Up @@ -116,6 +134,9 @@ class GameplayHUD : Container(), IGameplayEvents {

//region Skinning

/**
* Saves the current HUD layout to the skin JSON file.
*/
fun saveToSkinJSON() {

val data = getSkinData()
Expand All @@ -133,7 +154,7 @@ class GameplayHUD : Container(), IGameplayEvents {
}

json.put("HUD", HUDSkinData.writeToJSON(data))
jsonFile.writeText(json.toString())
jsonFile.writeText(json.toString(4))

SkinJsonReader.getReader().currentData = json
OsuSkin.get().hudSkinData = data
Expand Down Expand Up @@ -182,18 +203,29 @@ class GameplayHUD : Container(), IGameplayEvents {

pieSongProgress.y = accuracyCounter.y + accuracyCounter.heightScaled / 2f
pieSongProgress.x = accuracyCounter.x - accuracyCounter.widthScaled - 18f

accuracyCounter.restoreData = accuracyCounter.getSkinData()
pieSongProgress.restoreData = pieSongProgress.getSkinData()
}

//endregion

//region Elements events

private fun loadEditModeAssets() {
ResourceManager.getInstance().loadHighQualityAsset("delete", "delete.png")
ResourceManager.getInstance().loadHighQualityAsset("restore", "restore.png")
}

/**
* Sets the editor mode of the HUD.
*/
fun setEditMode(value: Boolean) {
isInEditMode = value
GlobalManager.getInstance().gameScene.isHUDEditorMode = value

if (value) {
ResourceManager.getInstance().loadHighQualityAsset("delete", "delete.png")
ResourceManager.getInstance().loadHighQualityAsset("oneone", "one-one.png")
loadEditModeAssets()

elementSelector = HUDElementSelector(this)

Expand All @@ -215,6 +247,12 @@ class GameplayHUD : Container(), IGameplayEvents {

//region Gameplay Events

/**
* Iterates over all the elements in the HUD.
*
* Note: If you need to remove elements you have to copy the list temporarily
* using another method such as `filterIsInstance<HUDElement>()`.
*/
fun forEachElement(action: (HUDElement) -> Unit) {
mChildren?.fastForEach {
(it as? HUDElement)?.let(action)
Expand Down
75 changes: 56 additions & 19 deletions src/com/reco1l/osu/hud/HUDElement.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,7 @@ import com.reco1l.andengine.shape.*
import com.reco1l.framework.ColorARGB
import com.reco1l.framework.math.Vec2
import com.reco1l.osu.hud.editor.HUDElementOverlay
import com.reco1l.osu.hud.elements.HUDAccuracyCounter
import com.reco1l.osu.hud.elements.HUDAverageOffsetCounter
import com.reco1l.osu.hud.elements.HUDComboCounter
import com.reco1l.osu.hud.elements.HUDHealthBar
import com.reco1l.osu.hud.elements.HUDHitErrorMeter
import com.reco1l.osu.hud.elements.HUDLinearSongProgress
import com.reco1l.osu.hud.elements.HUDPPCounter
import com.reco1l.osu.hud.elements.HUDPieSongProgress
import com.reco1l.osu.hud.elements.HUDScoreCounter
import com.reco1l.osu.hud.elements.HUDUnstableRateCounter
import com.reco1l.osu.hud.elements.*
import com.reco1l.osu.ui.entity.GameplayLeaderboard
import com.reco1l.toolkt.kotlin.capitalize
import org.anddev.andengine.input.touch.TouchEvent
Expand All @@ -39,6 +30,10 @@ enum class HUDElements(val type: KClass<out HUDElement>) {
avg_offset_counter(HUDAverageOffsetCounter::class),
hit_error_meter(HUDHitErrorMeter::class),
linear_song_progress(HUDLinearSongProgress::class),
great_counter(HUDGreatCounter::class),
ok_counter(HUDOkCounter::class),
meh_counter(HUDMehCounter::class),
miss_counter(HUDMissCounter::class),
leaderboard(GameplayLeaderboard::class);

companion object {
Expand All @@ -53,7 +48,8 @@ abstract class HUDElement : Container(), IGameplayEvents {
/**
* Returns the name of this element.
*/
open val name = HUDElements[this::class].name.replace('_', ' ').capitalize()
open val name
get() = HUDElements[this::class].name.replace('_', ' ').capitalize()

/**
* Indicates whether the element is selected.
Expand All @@ -62,7 +58,16 @@ abstract class HUDElement : Container(), IGameplayEvents {
get() = (parent as? GameplayHUD)?.selected == this


private var editorOverlay: HUDElementOverlay? = null
/**
* The overlay for this element to be used in edit mode.
*/
var editorOverlay: HUDElementOverlay? = null

/**
* The restore data of the element.
*/
var restoreData: HUDElementSkinData? = null


private var connectionLine: Line? = null

Expand All @@ -71,27 +76,51 @@ abstract class HUDElement : Container(), IGameplayEvents {

//region Skinning

/**
* Sets the skin data of the element.
*/
open fun setSkinData(data: HUDElementSkinData?) {
if (data != null) {
anchor = data.anchor
origin = data.origin
setScale(data.scale.x, data.scale.y)
setPosition(data.position.x, data.position.y)
if (data == null) {
return
}

anchor = data.anchor
origin = data.origin
setScale(data.scale)
setPosition(data.position.x, data.position.y)

// When the element is restored it's usually selected so we need to update the connection line.
if (isSelected) {
editorOverlay?.updateOutline()
updateConnectionLine()
}
}

/**
* Creates a [HUDElementSkinData] object with the current element's skin data.
*/
open fun getSkinData() = HUDElementSkinData(
type = this::class,
anchor = anchor,
origin = origin,
scale = scale,
scale = (mScaleX + mScaleY) / 2f,
position = Vec2(x, y)
)

/**
* Restores the element to its original state before any changes were made.
*/
fun restore() {
setSkinData(restoreData)
}

//endregion

//region Element events

/**
* Sets the edit mode of the element.
*/
open fun setEditMode(value: Boolean) {
isInEditMode = value

Expand All @@ -113,6 +142,9 @@ abstract class HUDElement : Container(), IGameplayEvents {
}
}

/**
* Called when the selection state of the element changes.
*/
open fun onSelectionStateChange(isSelected: Boolean) {

background?.clearEntityModifiers()
Expand Down Expand Up @@ -248,7 +280,7 @@ abstract class HUDElement : Container(), IGameplayEvents {
}

connectionLine!!.fromPoint = anchorOffset
connectionLine!!.toPoint = (editorOverlay?.outline?.drawPosition ?: Vec2.Zero) + drawSize * scale.absolute() * origin
connectionLine!!.toPoint = (editorOverlay?.outlinePosition ?: Vec2.Zero) + drawSize * scale * origin
}

override fun setScaleX(pScaleX: Float) {
Expand All @@ -266,6 +298,11 @@ abstract class HUDElement : Container(), IGameplayEvents {
updateConnectionLine()
}

override fun setScale(pScaleX: Float, pScaleY: Float) {
super.setScale(pScaleX, pScaleY)
updateConnectionLine()
}

/**
* Moves the element by the specified delta.
*/
Expand Down
16 changes: 6 additions & 10 deletions src/com/reco1l/osu/hud/HUDSkinData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ data class HUDSkinData(val elements: List<HUDElementSkinData>) {
type = HUDAccuracyCounter::class,
anchor = Anchor.TopRight,
origin = Anchor.TopRight,
scale = Vec2(0.6f * 0.96f),
scale = 0.6f * 0.96f,
position = Vec2(-17f, 9f)
),
HUDElementSkinData(
type = HUDComboCounter::class,
anchor = Anchor.BottomLeft,
origin = Anchor.BottomLeft,
position = Vec2(10f, -10f),
scale = Vec2(1.28f)
scale = 1.28f
),
HUDElementSkinData(
type = HUDPieSongProgress::class,
Expand All @@ -54,7 +54,7 @@ data class HUDSkinData(val elements: List<HUDElementSkinData>) {
type = HUDScoreCounter::class,
anchor = Anchor.TopRight,
origin = Anchor.TopRight,
scale = Vec2(0.96f),
scale = 0.96f,
position = Vec2(-10f, 0f)
)
)
Expand All @@ -70,8 +70,7 @@ data class HUDSkinData(val elements: List<HUDElementSkinData>) {
put("y", it.position.y)
put("anchor", Anchor.getName(it.anchor))
put("origin", Anchor.getName(it.origin))
put("scaleX", it.scale.x)
put("scaleY", it.scale.y)
put("scale", it.scale)
}
}
}
Expand All @@ -90,10 +89,7 @@ data class HUDSkinData(val elements: List<HUDElementSkinData>) {
),
anchor = Anchor.getFromName(element.optString("anchor", "Center")),
origin = Anchor.getFromName(element.optString("origin", "Center")),
scale = Vec2(
element.optDouble("scaleX", 1.0).toFloat(),
element.optDouble("scaleY", 1.0).toFloat()
)
scale = element.optDouble("scale", 1.0).toFloat(),
)
}.filterNotNull()
)
Expand Down Expand Up @@ -128,5 +124,5 @@ data class HUDElementSkinData(
/**
* The scale applied to the element.
*/
val scale: Vec2 = Vec2.One
val scale: Float = 1f
)
Loading
Loading