mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-22 10:54:19 -04:00
Merge 63755fd8db79b13f7f12c27b66983f86181a0b50 into d51ef24c205b6b05330b3c4d7ce79c402db44447
This commit is contained in:
commit
af1d890725
52
core/src/com/unciv/logic/AlternatingStateManager.kt
Normal file
52
core/src/com/unciv/logic/AlternatingStateManager.kt
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package com.unciv.logic
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx
|
||||||
|
import com.unciv.utils.Concurrency
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlin.time.Clock
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
|
||||||
|
class AlternatingStateManager(
|
||||||
|
private val name: String,
|
||||||
|
private val originalState: () -> Unit,
|
||||||
|
private val alternateState: () -> Unit
|
||||||
|
) {
|
||||||
|
private var job: Job? = null
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTime::class)
|
||||||
|
fun start(
|
||||||
|
duration: Duration = 5.seconds, interval: Duration = 500.milliseconds
|
||||||
|
) {
|
||||||
|
job?.cancel()
|
||||||
|
job = Concurrency.run(name) {
|
||||||
|
val startTime = Clock.System.now()
|
||||||
|
|
||||||
|
var isAlternateState = true
|
||||||
|
while (true) {
|
||||||
|
if (isAlternateState) {
|
||||||
|
Gdx.app.postRunnable(alternateState)
|
||||||
|
isAlternateState = false
|
||||||
|
} else {
|
||||||
|
Gdx.app.postRunnable(originalState)
|
||||||
|
isAlternateState = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Clock.System.now() - startTime >= duration) {
|
||||||
|
Gdx.app.postRunnable(originalState)
|
||||||
|
return@run
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
job?.cancel()
|
||||||
|
Gdx.app.postRunnable(originalState)
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
package com.unciv.logic.multiplayer.chat
|
package com.unciv.logic.multiplayer.chat
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx
|
import com.badlogic.gdx.Gdx
|
||||||
|
import com.unciv.UncivGame
|
||||||
import com.unciv.ui.screens.worldscreen.chat.ChatPopup
|
import com.unciv.ui.screens.worldscreen.chat.ChatPopup
|
||||||
|
import com.unciv.utils.toUUIDOrNull
|
||||||
import java.util.Collections.synchronizedMap
|
import java.util.Collections.synchronizedMap
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
import java.util.Queue
|
import java.util.Queue
|
||||||
@ -10,6 +12,8 @@ import java.util.UUID
|
|||||||
data class Chat(
|
data class Chat(
|
||||||
val gameId: UUID,
|
val gameId: UUID,
|
||||||
) {
|
) {
|
||||||
|
var unreadCount = 0
|
||||||
|
|
||||||
// <civName, message> pairs
|
// <civName, message> pairs
|
||||||
private val messages: MutableList<Pair<String, String>> = mutableListOf(INITIAL_MESSAGE)
|
private val messages: MutableList<Pair<String, String>> = mutableListOf(INITIAL_MESSAGE)
|
||||||
|
|
||||||
@ -52,6 +56,8 @@ object ChatStore {
|
|||||||
*/
|
*/
|
||||||
var chatPopup: ChatPopup? = null
|
var chatPopup: ChatPopup? = null
|
||||||
|
|
||||||
|
var hasGlobalMessage = false
|
||||||
|
|
||||||
private var gameIdToChat: MutableMap<UUID, Chat> = synchronizedMap(mutableMapOf())
|
private var gameIdToChat: MutableMap<UUID, Chat> = synchronizedMap(mutableMapOf())
|
||||||
|
|
||||||
/** When no [ChatPopup] is open to receive these oddities, we keep them here.
|
/** When no [ChatPopup] is open to receive these oddities, we keep them here.
|
||||||
@ -72,21 +78,36 @@ object ChatStore {
|
|||||||
globalMessages = LinkedList()
|
globalMessages = LinkedList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun relayChatMessage(chat: Response.Chat) {
|
fun relayChatMessage(incomingChatMsg: Response.Chat) {
|
||||||
Gdx.app.postRunnable {
|
Gdx.app.postRunnable {
|
||||||
if (chat.gameId == null || chat.gameId.isBlank()) {
|
if (incomingChatMsg.gameId == null || incomingChatMsg.gameId.isBlank()) {
|
||||||
relayGlobalMessage(chat.message, chat.civName)
|
relayGlobalMessage(incomingChatMsg.message, incomingChatMsg.civName)
|
||||||
} else {
|
} else {
|
||||||
val gameId = try {
|
val gameId = try {
|
||||||
UUID.fromString(chat.gameId)
|
UUID.fromString(incomingChatMsg.gameId)
|
||||||
} catch (_: Throwable) {
|
} catch (_: Throwable) {
|
||||||
// Discard messages with invalid UUID
|
// Discard messages with invalid UUID
|
||||||
return@postRunnable
|
return@postRunnable
|
||||||
}
|
}
|
||||||
|
|
||||||
getChatByGameId(gameId).addMessage(chat.civName, chat.message)
|
val chat = chatPopup?.chat ?: getChatByGameId(gameId)
|
||||||
if (chatPopup?.chat?.gameId == gameId) {
|
chat.addMessage(incomingChatMsg.civName, incomingChatMsg.message)
|
||||||
chatPopup?.addMessage(chat.civName, chat.message)
|
if (gameId.equals(chatPopup?.chat?.gameId)) {
|
||||||
|
chatPopup?.addMessage(incomingChatMsg.civName, incomingChatMsg.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chatPopup == null && incomingChatMsg.civName != "System") {
|
||||||
|
if (gameId.equals(UncivGame.Current.worldScreen?.gameInfo?.gameId?.toUUIDOrNull())) {
|
||||||
|
// ensures that you are not getting notified for your own messages
|
||||||
|
if (UncivGame.Current.worldScreen?.gameInfo?.currentPlayer != incomingChatMsg.civName) {
|
||||||
|
chat.unreadCount++
|
||||||
|
UncivGame.Current.worldScreen?.chatButton?.triggerChatIndication()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// user is out of world screen or
|
||||||
|
// some other game not currently on screen has a message
|
||||||
|
chat.unreadCount++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +124,13 @@ object ChatStore {
|
|||||||
|
|
||||||
fun relayGlobalMessage(message: String, civName: String = "System") {
|
fun relayGlobalMessage(message: String, civName: String = "System") {
|
||||||
Gdx.app.postRunnable {
|
Gdx.app.postRunnable {
|
||||||
chatPopup?.addMessage(civName, message, suffix = "one time") ?: globalMessages.add(Pair(civName, message))
|
if (civName != "System") {
|
||||||
|
hasGlobalMessage = chatPopup == null
|
||||||
|
if (hasGlobalMessage) UncivGame.Current.worldScreen?.chatButton?.triggerChatIndication()
|
||||||
|
}
|
||||||
|
|
||||||
|
chatPopup?.addMessage(civName, message, suffix = "one time")
|
||||||
|
?: globalMessages.add(Pair(civName, message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ import com.badlogic.gdx.scenes.scene2d.ui.Cell
|
|||||||
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
||||||
import com.badlogic.gdx.utils.Align
|
import com.badlogic.gdx.utils.Align
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.ui.components.fonts.Fonts
|
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
|
import com.unciv.ui.components.fonts.Fonts
|
||||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,10 +23,11 @@ open class IconTextButton(
|
|||||||
text: String,
|
text: String,
|
||||||
val icon: Actor? = null,
|
val icon: Actor? = null,
|
||||||
fontSize: Int = Constants.defaultFontSize,
|
fontSize: Int = Constants.defaultFontSize,
|
||||||
fontColor: Color = Color.WHITE
|
val fontColor: Color = Color.WHITE
|
||||||
): Button(BaseScreen.skin) {
|
) : Button(BaseScreen.skin) {
|
||||||
/** [Label] instance produced by, and with content and formatting as specified in [String.toLabel]. */
|
/** [Label] instance produced by, and with content and formatting as specified in [String.toLabel]. */
|
||||||
val label = text.toLabel(fontColor, fontSize, hideIcons = true) // Since by definition we already have an icon
|
val label = text.toLabel(fontColor, fontSize, hideIcons = true) // Since by definition we already have an icon
|
||||||
|
|
||||||
/** Table cell containing the [icon] if any, or `null` (that is, when no [icon] was supplied, the Cell will exist but have no Actor). */
|
/** Table cell containing the [icon] if any, or `null` (that is, when no [icon] was supplied, the Cell will exist but have no Actor). */
|
||||||
val iconCell: Cell<Actor> =
|
val iconCell: Cell<Actor> =
|
||||||
if (icon != null) {
|
if (icon != null) {
|
||||||
@ -37,6 +38,7 @@ open class IconTextButton(
|
|||||||
} else {
|
} else {
|
||||||
add().padRight(fontSize / 2f)
|
add().padRight(fontSize / 2f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Table cell instance containing the [label]. */
|
/** Table cell instance containing the [label]. */
|
||||||
val labelCell: Cell<Label> = add(label)
|
val labelCell: Cell<Label> = add(label)
|
||||||
|
|
||||||
|
@ -1,22 +1,80 @@
|
|||||||
package com.unciv.ui.screens.worldscreen.chat
|
package com.unciv.ui.screens.worldscreen.chat
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import com.badlogic.gdx.utils.Align
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
|
import com.unciv.logic.AlternatingStateManager
|
||||||
|
import com.unciv.logic.multiplayer.chat.ChatStore
|
||||||
import com.unciv.logic.multiplayer.chat.ChatWebSocket
|
import com.unciv.logic.multiplayer.chat.ChatWebSocket
|
||||||
import com.unciv.logic.multiplayer.chat.Message
|
import com.unciv.logic.multiplayer.chat.Message
|
||||||
|
import com.unciv.ui.components.SmallButtonStyle
|
||||||
|
import com.unciv.ui.components.extensions.disable
|
||||||
|
import com.unciv.ui.components.extensions.toTextButton
|
||||||
import com.unciv.ui.components.input.onClick
|
import com.unciv.ui.components.input.onClick
|
||||||
import com.unciv.ui.images.IconTextButton
|
import com.unciv.ui.images.IconTextButton
|
||||||
import com.unciv.ui.images.ImageGetter
|
import com.unciv.ui.images.ImageGetter
|
||||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||||
|
|
||||||
|
private val smallButtonStyle = SmallButtonStyle()
|
||||||
|
|
||||||
class ChatButton(val worldScreen: WorldScreen) : IconTextButton(
|
class ChatButton(val worldScreen: WorldScreen) : IconTextButton(
|
||||||
"Chat", ImageGetter.getImage("OtherIcons/Chat"), 23
|
"Chat", ImageGetter.getImage("OtherIcons/Chat"), 23
|
||||||
) {
|
) {
|
||||||
|
private val chat = ChatStore.getChatByGameId(worldScreen.gameInfo.gameId)
|
||||||
|
|
||||||
|
private val badge = "".toTextButton(smallButtonStyle).apply {
|
||||||
|
disable()
|
||||||
|
label.setColor(Color.WHITE)
|
||||||
|
label.setAlignment(Align.center)
|
||||||
|
label.setFontScale(0.2f)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val flash = AlternatingStateManager(
|
||||||
|
name = "ChatButton color flash",
|
||||||
|
originalState = {
|
||||||
|
icon?.color = fontColor
|
||||||
|
label.color = fontColor
|
||||||
|
}, alternateState = {
|
||||||
|
icon?.color = Color.ORANGE
|
||||||
|
label.color = Color.ORANGE
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun updateBadge() {
|
||||||
|
badge.height = height / 3
|
||||||
|
badge.setPosition(
|
||||||
|
width - badge.width / 1.5f,
|
||||||
|
height - badge.height / 1.5f
|
||||||
|
)
|
||||||
|
|
||||||
|
badge.isVisible = chat.unreadCount > 0 || ChatStore.hasGlobalMessage
|
||||||
|
|
||||||
|
if (badge.isVisible) {
|
||||||
|
var text = chat.unreadCount.toString()
|
||||||
|
if (ChatStore.hasGlobalMessage) {
|
||||||
|
text += '+'
|
||||||
|
}
|
||||||
|
badge.setText(text)
|
||||||
|
} else flash.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun triggerChatIndication() {
|
||||||
|
updateBadge()
|
||||||
|
flash.start()
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
width = 95f
|
width = 95f
|
||||||
iconCell.pad(3f).center()
|
iconCell.pad(3f).center()
|
||||||
|
addActor(badge)
|
||||||
|
updateBadge()
|
||||||
|
|
||||||
onClick {
|
onClick {
|
||||||
ChatPopup(worldScreen).open()
|
chat.unreadCount = 0
|
||||||
|
ChatStore.hasGlobalMessage = false
|
||||||
|
updateBadge()
|
||||||
|
|
||||||
|
ChatPopup(chat, worldScreen).open()
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshVisibility()
|
refreshVisibility()
|
||||||
@ -34,6 +92,7 @@ class ChatButton(val worldScreen: WorldScreen) : IconTextButton(
|
|||||||
Message.Join(listOf(worldScreen.gameInfo.gameId)),
|
Message.Join(listOf(worldScreen.gameInfo.gameId)),
|
||||||
)
|
)
|
||||||
updatePosition()
|
updatePosition()
|
||||||
|
updateBadge()
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
ChatWebSocket.stop()
|
ChatWebSocket.stop()
|
||||||
|
@ -11,6 +11,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
|
|||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
import com.badlogic.gdx.utils.Align
|
import com.badlogic.gdx.utils.Align
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
|
import com.unciv.logic.multiplayer.chat.Chat
|
||||||
import com.unciv.logic.multiplayer.chat.ChatStore
|
import com.unciv.logic.multiplayer.chat.ChatStore
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.ui.components.extensions.coerceLightnessAtLeast
|
import com.unciv.ui.components.extensions.coerceLightnessAtLeast
|
||||||
@ -28,6 +29,7 @@ private val civChatColorsMap = mapOf<String, Color>(
|
|||||||
)
|
)
|
||||||
|
|
||||||
class ChatPopup(
|
class ChatPopup(
|
||||||
|
val chat: Chat,
|
||||||
private val worldScreen: WorldScreen,
|
private val worldScreen: WorldScreen,
|
||||||
) : Popup(screen = worldScreen, scrollable = Scrollability.None) {
|
) : Popup(screen = worldScreen, scrollable = Scrollability.None) {
|
||||||
companion object {
|
companion object {
|
||||||
@ -35,8 +37,6 @@ class ChatPopup(
|
|||||||
const val CIVNAME_COLOR_MIN_LIGHTNESS = 0.60f
|
const val CIVNAME_COLOR_MIN_LIGHTNESS = 0.60f
|
||||||
}
|
}
|
||||||
|
|
||||||
val chat = ChatStore.getChatByGameId(worldScreen.gameInfo.gameId)
|
|
||||||
|
|
||||||
private val chatTable = Table(skin)
|
private val chatTable = Table(skin)
|
||||||
private val scrollPane = ScrollPane(chatTable, skin)
|
private val scrollPane = ScrollPane(chatTable, skin)
|
||||||
private val messageField = UncivTextField(
|
private val messageField = UncivTextField(
|
||||||
|
@ -5,17 +5,17 @@ import com.badlogic.gdx.scenes.scene2d.Actor
|
|||||||
import com.badlogic.gdx.scenes.scene2d.ui.Button
|
import com.badlogic.gdx.scenes.scene2d.ui.Button
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Cell
|
import com.badlogic.gdx.scenes.scene2d.ui.Cell
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.HorizontalGroup
|
import com.badlogic.gdx.scenes.scene2d.ui.HorizontalGroup
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Image
|
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
||||||
import com.badlogic.gdx.utils.Disposable
|
import com.badlogic.gdx.utils.Disposable
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
|
import com.unciv.logic.AlternatingStateManager
|
||||||
import com.unciv.logic.event.EventBus
|
import com.unciv.logic.event.EventBus
|
||||||
import com.unciv.logic.multiplayer.HasMultiplayerGameName
|
import com.unciv.logic.multiplayer.HasMultiplayerGameName
|
||||||
import com.unciv.logic.multiplayer.MultiplayerGameNameChanged
|
import com.unciv.logic.multiplayer.MultiplayerGameNameChanged
|
||||||
|
import com.unciv.logic.multiplayer.MultiplayerGamePreview
|
||||||
import com.unciv.logic.multiplayer.MultiplayerGameUpdateEnded
|
import com.unciv.logic.multiplayer.MultiplayerGameUpdateEnded
|
||||||
import com.unciv.logic.multiplayer.MultiplayerGameUpdateStarted
|
import com.unciv.logic.multiplayer.MultiplayerGameUpdateStarted
|
||||||
import com.unciv.logic.multiplayer.MultiplayerGameUpdated
|
import com.unciv.logic.multiplayer.MultiplayerGameUpdated
|
||||||
import com.unciv.logic.multiplayer.MultiplayerGamePreview
|
|
||||||
import com.unciv.logic.multiplayer.isUsersTurn
|
import com.unciv.logic.multiplayer.isUsersTurn
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.ui.components.extensions.setSize
|
import com.unciv.ui.components.extensions.setSize
|
||||||
@ -24,20 +24,20 @@ import com.unciv.ui.components.widgets.LoadingImage
|
|||||||
import com.unciv.ui.images.ImageGetter
|
import com.unciv.ui.images.ImageGetter
|
||||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||||
import com.unciv.utils.Concurrency
|
import com.unciv.utils.Concurrency
|
||||||
import com.unciv.utils.launchOnGLThread
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
|
|
||||||
class MultiplayerStatusButton(
|
class MultiplayerStatusButton(
|
||||||
screen: BaseScreen,
|
screen: BaseScreen,
|
||||||
curGame: MultiplayerGamePreview?
|
curGame: MultiplayerGamePreview?
|
||||||
) : Button(BaseScreen.skin), Disposable {
|
) : Button(BaseScreen.skin), Disposable {
|
||||||
private var curGameName = curGame?.name
|
private var curGameName = curGame?.name
|
||||||
private val loadingImage = LoadingImage(style = LoadingImage.Style(
|
private val loadingImage = LoadingImage(
|
||||||
|
style = LoadingImage.Style(
|
||||||
idleImageName = "OtherIcons/Multiplayer",
|
idleImageName = "OtherIcons/Multiplayer",
|
||||||
idleIconColor = Color.WHITE,
|
idleIconColor = Color.WHITE,
|
||||||
minShowTime = 500
|
minShowTime = 500
|
||||||
))
|
)
|
||||||
|
)
|
||||||
private val turnIndicator = TurnIndicator()
|
private val turnIndicator = TurnIndicator()
|
||||||
private val turnIndicatorCell: Cell<Actor>
|
private val turnIndicatorCell: Cell<Actor>
|
||||||
private val gameNamesWithCurrentTurn = getInitialGamesWithCurrentTurn()
|
private val gameNamesWithCurrentTurn = getInitialGamesWithCurrentTurn()
|
||||||
@ -96,7 +96,7 @@ class MultiplayerStatusButton(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTurnIndicator(flash: Boolean = true) {
|
private fun updateTurnIndicator(flash: Boolean = true) {
|
||||||
if (gameNamesWithCurrentTurn.size == 0) {
|
if (gameNamesWithCurrentTurn.isEmpty()) {
|
||||||
turnIndicatorCell.clearActor()
|
turnIndicatorCell.clearActor()
|
||||||
} else {
|
} else {
|
||||||
turnIndicatorCell.setActor(turnIndicator)
|
turnIndicatorCell.setActor(turnIndicator)
|
||||||
@ -105,7 +105,7 @@ class MultiplayerStatusButton(
|
|||||||
|
|
||||||
// flash so the user sees an better update
|
// flash so the user sees an better update
|
||||||
if (flash) {
|
if (flash) {
|
||||||
turnIndicator.flash()
|
turnIndicator.flash.start(3.seconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,10 +118,9 @@ class MultiplayerStatusButton(
|
|||||||
|
|
||||||
private class TurnIndicator : HorizontalGroup(), Disposable {
|
private class TurnIndicator : HorizontalGroup(), Disposable {
|
||||||
val gameAmount = Label("2", BaseScreen.skin)
|
val gameAmount = Label("2", BaseScreen.skin)
|
||||||
val image: Image
|
val image = ImageGetter.getImage("OtherIcons/ExclamationMark")
|
||||||
private var job: Job? = null
|
|
||||||
init {
|
init {
|
||||||
image = ImageGetter.getImage("OtherIcons/ExclamationMark")
|
|
||||||
image.setSize(30f)
|
image.setSize(30f)
|
||||||
addActor(image)
|
addActor(image)
|
||||||
}
|
}
|
||||||
@ -135,23 +134,18 @@ private class TurnIndicator : HorizontalGroup(), Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun flash() {
|
val flash = AlternatingStateManager(
|
||||||
// using a gdx Action would be nicer, but we don't necessarily have continuousRendering on and we still want to flash
|
name = "StatusButton color flash",
|
||||||
flash(6, Color.WHITE, Color.ORANGE)
|
originalState = {
|
||||||
}
|
image.color = Color.WHITE
|
||||||
private fun flash(alternations: Int, curColor: Color, nextColor: Color) {
|
gameAmount.color = Color.WHITE
|
||||||
if (alternations == 0) return
|
}, alternateState = {
|
||||||
gameAmount.color = nextColor
|
image.color = Color.ORANGE
|
||||||
image.color = nextColor
|
gameAmount.color = Color.ORANGE
|
||||||
job = Concurrency.run("StatusButton color flash") {
|
|
||||||
delay(500)
|
|
||||||
launchOnGLThread {
|
|
||||||
flash(alternations - 1, nextColor, curColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
job?.cancel()
|
flash.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,18 @@ import yairm210.purity.annotations.Pure
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a [String] is a valid UUID
|
* Tries to convert a [String] to a valid [UUID]
|
||||||
|
* and returns `null` upon failure
|
||||||
*/
|
*/
|
||||||
@Pure
|
@Pure
|
||||||
fun String.isUUID(): Boolean = try {
|
fun String.toUUIDOrNull(): UUID? = try {
|
||||||
UUID.fromString(this)
|
UUID.fromString(this)
|
||||||
true
|
|
||||||
} catch (_: Throwable) {
|
} catch (_: Throwable) {
|
||||||
false
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a [String] is a valid [UUID]
|
||||||
|
*/
|
||||||
|
@Pure
|
||||||
|
fun String.isUUID(): Boolean = toUUIDOrNull() != null
|
||||||
|
Loading…
x
Reference in New Issue
Block a user