package starshipfights.game
-import kotlinx.coroutines.currentCoroutineContext
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.isActive
import kotlinx.html.*
import kotlinx.serialization.json.Json
import kotlin.math.abs
fun <T : Enum<T>> T.toUrlSlug() = name.replace('_', '-').lowercase()
-inline fun <T> pollFlow(intervalMs: Long = 50, crossinline poll: () -> T) = flow {
- var prev = poll()
- emit(prev)
-
- while (currentCoroutineContext().isActive) {
- delay(intervalMs)
- val curr = poll()
- if (curr != prev) {
- prev = curr
- emit(prev)
- }
- }
-}
-
fun Double.toPercent() = "${(this * 100).roundToInt()}%"
fun smoothMinus1To1(x: Double, exponent: Double = 1.0) = x / (1 + abs(x).pow(exponent)).pow(1 / exponent)
}
launch {
+ val doneDeploying = Job()
+
+ launch {
+ doneDeploying.join()
+
+ val pickContext = pickContextDeferred.await()
+ beginSelecting(pickContext)
+ handleSelections(pickContext)
+ }
+
gameState.collect { state ->
GameRender.renderGameState(scene, state)
GameUI.drawGameUI(state)
if (state.phase != GamePhase.Deploy)
- launch {
- val pickContext = pickContextDeferred.await()
- beginSelecting(pickContext)
- handleSelections(pickContext)
- }
+ doneDeploying.complete()
}
}
}
package starshipfights.game
import kotlinx.browser.document
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
import kotlinx.dom.addClass
import kotlinx.dom.clear
-import kotlinx.dom.hasClass
import kotlinx.dom.removeClass
import kotlinx.html.*
import kotlinx.html.dom.append
consumer.render(context, callback)
}
- suspend fun display(): T {
- pollFlow(100) { popup.hasClass("hide") }.takeWhile { !it }.collect()
-
- popupBox.clear()
-
- return suspendCancellableCoroutine { continuation ->
+ suspend fun display(): T = popupMutex.withLock {
+ suspendCancellableCoroutine { continuation ->
+ popupBox.clear()
+
popupBox.append {
renderInto(this, continuation.context) {
hide()
}
companion object {
+ private val popupMutex = Mutex()
+
private val popup by lazy {
document.getElementById("popup").unsafeCast<HTMLDivElement>()
}
import io.ktor.http.cio.websocket.*
import kotlinx.browser.window
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.isActive
-import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationStrategy
addEventListener(eventName, listener)
}
-suspend fun EventTarget.eventFlow(eventName: String, shouldPreventDefault: Boolean = false): Flow<Event> = callbackFlow {
- val listener = object : EventListener {
- override fun handleEvent(event: Event) {
- if (shouldPreventDefault)
- event.preventDefault()
-
- launch { send(event) }
- }
- }
-
- addEventListener(eventName, listener)
-
- awaitClose {
- removeEventListener(eventName, listener)
- }
-}
-
suspend fun awaitAnimationFrame(): Double = suspendCancellableCoroutine { continuation ->
val handle = window.requestAnimationFrame { t ->
continuation.resume(t)