Add a basic character sheet.
This commit is contained in:
parent
9ddd6018fd
commit
6e4f91e007
13 changed files with 871 additions and 329 deletions
|
|
@ -1,21 +1,24 @@
|
|||
package com.pixelized.desktop.lwa
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Window
|
||||
import androidx.compose.ui.window.rememberWindowState
|
||||
import com.pixelized.desktop.lwa.screen.CharacterSheet
|
||||
import com.pixelized.desktop.lwa.screen.CharacterSheetUio
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheet
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetViewModel
|
||||
import com.pixelized.desktop.lwa.screen.overlay.BlurOverlay
|
||||
import com.pixelized.desktop.lwa.screen.overlay.BlurOverlayViewModel
|
||||
import com.pixelized.desktop.lwa.screen.roll.RollPage
|
||||
import com.pixelized.desktop.lwa.screen.roll.RollViewModel
|
||||
import com.pixelized.desktop.lwa.theme.LwaTheme
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
|
||||
|
|
@ -23,31 +26,61 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
|
|||
@Preview
|
||||
fun App() {
|
||||
LwaTheme {
|
||||
val characterSheet = remember { mutableStateOf<CharacterSheetUio?>(null) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(all = 16.dp),
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
characterSheet.value = CharacterSheetUio.Koryas
|
||||
}
|
||||
) {
|
||||
Text(text = "Koryas Tissenpa")
|
||||
}
|
||||
}
|
||||
val sheetViewModel = viewModel { CharacterSheetViewModel() }
|
||||
val overlayViewModel = viewModel { BlurOverlayViewModel() }
|
||||
val rollViewModel = viewModel { RollViewModel() }
|
||||
|
||||
characterSheet.value?.let { sheet ->
|
||||
Window(
|
||||
onCloseRequest = { characterSheet.value = null },
|
||||
// state = rememberWindowState(size = DpSize(width = 320.dp + 32.dp, height = 800.dp)),
|
||||
title = "LwaCharacterSheet",
|
||||
Column(
|
||||
modifier = Modifier.padding(all = 16.dp),
|
||||
) {
|
||||
CharacterSheet(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
width = 320.dp,
|
||||
characterSheet = sheet
|
||||
)
|
||||
Button(
|
||||
onClick = sheetViewModel::showCharacterSheet,
|
||||
) {
|
||||
Text(text = "Koryas Tissenpa")
|
||||
}
|
||||
}
|
||||
|
||||
sheetViewModel.sheet.value?.let { sheet ->
|
||||
Window(
|
||||
onCloseRequest = sheetViewModel::hideCharacterSheet,
|
||||
state = rememberWindowState(
|
||||
width = 320.dp + 64.dp,
|
||||
height = 900.dp,
|
||||
),
|
||||
title = "LwaCharacterSheet",
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
BlurOverlay(
|
||||
viewModel = overlayViewModel,
|
||||
overlay = {
|
||||
RollPage(
|
||||
viewModel = rollViewModel,
|
||||
onDismissRequest = overlayViewModel::hide,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
CharacterSheet(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
width = 320.dp,
|
||||
characterSheet = sheet,
|
||||
onCharacteristic = { characteristic ->
|
||||
rollViewModel.prepareRoll(characteristic = characteristic)
|
||||
overlayViewModel.show()
|
||||
},
|
||||
onSkill = { node ->
|
||||
rollViewModel.prepareRoll(node = node)
|
||||
overlayViewModel.show()
|
||||
}
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package com.pixelized.desktop.lwa.business
|
||||
|
||||
object RollUseCase {
|
||||
private val d100 = (1..100)
|
||||
|
||||
/**
|
||||
* (Math.random() * 100 + 1).toInt()
|
||||
* TODO : test.
|
||||
*/
|
||||
fun rollD100(): Int {
|
||||
return d100.random()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package com.pixelized.desktop.lwa.business
|
||||
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
object SkillStepUseCase {
|
||||
|
||||
data class SkillStep(
|
||||
val criticalSuccessRange: IntRange,
|
||||
val specialSuccessRange: IntRange,
|
||||
val successRange: IntRange,
|
||||
val failureRange: IntRange,
|
||||
val criticalFailureRange: IntRange,
|
||||
) {
|
||||
constructor(
|
||||
criticalSuccess: Pair<Int, Int>?,
|
||||
specialSuccess: Pair<Int, Int>,
|
||||
success: Pair<Int, Int>,
|
||||
failure: Pair<Int, Int>?,
|
||||
criticalFailure: Pair<Int, Int>,
|
||||
) : this(
|
||||
criticalSuccessRange = criticalSuccess
|
||||
?.let { IntRange(it.first, it.second) }
|
||||
?: IntRange(-1, -1),
|
||||
specialSuccessRange = specialSuccess
|
||||
.let { IntRange(it.first, it.second) },
|
||||
successRange = success
|
||||
.let { IntRange(it.first, it.second) },
|
||||
failureRange = failure
|
||||
?.let { IntRange(it.first, it.second) } ?: IntRange(-1, -1),
|
||||
criticalFailureRange = criticalFailure
|
||||
.let { IntRange(it.first, it.second) },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to compute the range in which a roll is a either critical, special, success or failure.
|
||||
* TODO : test.
|
||||
*/
|
||||
fun computeSkillStep(skill: Int): SkillStep {
|
||||
val criticalSuccess: Pair<Int, Int>? = when (skill) {
|
||||
in (0..5) -> null
|
||||
in (10..25) -> 1 to 1
|
||||
in (30..45) -> 1 to 2
|
||||
in (50..65) -> 1 to 3
|
||||
in (70..85) -> 1 to 4
|
||||
in (90..100) -> 1 to 5
|
||||
else -> 1 to skill * 5 / 100
|
||||
}
|
||||
val specialSuccess: Pair<Int, Int> = when (skill) {
|
||||
0, 5 -> 1 to 1
|
||||
10 -> 2 to 2
|
||||
15 -> ((criticalSuccess?.second ?: 0) + 1) to 3
|
||||
20 -> ((criticalSuccess?.second ?: 0) + 1) to 4
|
||||
25 -> ((criticalSuccess?.second ?: 0) + 1) to 5
|
||||
30 -> ((criticalSuccess?.second ?: 0) + 1) to 6
|
||||
35 -> ((criticalSuccess?.second ?: 0) + 1) to 7
|
||||
40 -> ((criticalSuccess?.second ?: 0) + 1) to 8
|
||||
45 -> ((criticalSuccess?.second ?: 0) + 1) to 9
|
||||
50 -> ((criticalSuccess?.second ?: 0) + 1) to 10
|
||||
55 -> ((criticalSuccess?.second ?: 0) + 1) to 11
|
||||
60 -> ((criticalSuccess?.second ?: 0) + 1) to 12
|
||||
65 -> ((criticalSuccess?.second ?: 0) + 1) to 13
|
||||
70 -> ((criticalSuccess?.second ?: 0) + 1) to 15
|
||||
75 -> ((criticalSuccess?.second ?: 0) + 1) to 15
|
||||
80 -> ((criticalSuccess?.second ?: 0) + 1) to 16
|
||||
85 -> ((criticalSuccess?.second ?: 0) + 1) to 17
|
||||
90 -> ((criticalSuccess?.second ?: 0) + 1) to 18
|
||||
95 -> ((criticalSuccess?.second ?: 0) + 1) to 19
|
||||
100 -> ((criticalSuccess?.second ?: 0) + 1) to 20
|
||||
else -> ((criticalSuccess?.second ?: 0) + 1) to skill * 20 / 100
|
||||
}
|
||||
val success: Pair<Int, Int> = (specialSuccess.second + 1) to max(5, min(99, skill))
|
||||
val criticalFailure: Pair<Int, Int> = when (skill) {
|
||||
0, 5, 10 -> 96 to 100
|
||||
15, 20, 25, 30 -> 97 to 100
|
||||
35, 40, 45, 50 -> 98 to 100
|
||||
55, 60, 65, 70 -> 99 to 100
|
||||
else -> 100 to 100
|
||||
}
|
||||
val failure: Pair<Int, Int>? = if (skill >= 100) {
|
||||
null
|
||||
} else {
|
||||
success.second + 1 to criticalFailure.first - 1
|
||||
}
|
||||
|
||||
return SkillStep(
|
||||
criticalSuccess = criticalSuccess,
|
||||
specialSuccess = specialSuccess,
|
||||
success = success,
|
||||
failure = failure,
|
||||
criticalFailure = criticalFailure,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package com.pixelized.desktop.lwa.composable
|
|||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Surface
|
||||
|
|
@ -20,15 +19,14 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
|
|||
fun DecoratedBox(
|
||||
modifier: Modifier = Modifier,
|
||||
border: Color = Color(0xFFDFDFDF),
|
||||
paddingValues: PaddingValues = PaddingValues(all = 8.dp),
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.border(width = 1.dp, color = border, shape = RoundedCornerShape(16.dp))
|
||||
modifier = Modifier
|
||||
.border(width = 1.dp, color = border, shape = RoundedCornerShape(size = 16.dp))
|
||||
.padding(all = 2.dp)
|
||||
.border(width = 1.dp, color = border, shape = RectangleShape)
|
||||
.padding(paddingValues = paddingValues),
|
||||
.then(other = modifier),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,260 +0,0 @@
|
|||
package com.pixelized.desktop.lwa.screen
|
||||
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.composable.DecoratedBox
|
||||
|
||||
@Stable
|
||||
data class CharacterSheetUio(
|
||||
val name: String,
|
||||
val characteristics: List<Node>,
|
||||
val subCharacteristics: List<Node>,
|
||||
val skills: List<Node>,
|
||||
val occupations: List<Node>,
|
||||
val magics: List<Node>,
|
||||
) {
|
||||
@Stable
|
||||
class Node(
|
||||
val label: String,
|
||||
val value: Any,
|
||||
)
|
||||
|
||||
companion object {
|
||||
val Koryas = CharacterSheetUio(
|
||||
name = "Koryas Tissenpa",
|
||||
characteristics = listOf(
|
||||
Node(label = "Force", value = 10),
|
||||
Node(label = "Dextérité", value = 11),
|
||||
Node(label = "Constitution", value = 15),
|
||||
Node(label = "Taille", value = 13),
|
||||
Node(label = "Intelligence", value = 9),
|
||||
Node(label = "Pouvoir", value = 15),
|
||||
Node(label = "Charisme", value = 7),
|
||||
),
|
||||
subCharacteristics = listOf(
|
||||
Node(label = "Déplacement ", value = 10),
|
||||
Node(label = "Points de vie", value = "14/14"),
|
||||
Node(label = "Points de pouvoir", value = "13/13"),
|
||||
Node(label = "Bonus aux dégâts", value = "1d4"),
|
||||
Node(label = "Armure", value = 0),
|
||||
),
|
||||
skills = listOf(
|
||||
Node(label = "Bagarre", value = 75),
|
||||
Node(label = "Esquive", value = 60),
|
||||
Node(label = "Saisie", value = 20),
|
||||
Node(label = "Lancer", value = 20),
|
||||
Node(label = "Athlétisme", value = 60),
|
||||
Node(label = "Acrobatie", value = 50),
|
||||
Node(label = "Perception", value = 55),
|
||||
Node(label = "Recherche", value = 25),
|
||||
Node(label = "Empathie", value = 15),
|
||||
Node(label = "Persuasion", value = 20),
|
||||
Node(label = "Intimidation", value = 50),
|
||||
Node(label = "Baratin", value = 20),
|
||||
Node(label = "Marchandage", value = 10),
|
||||
Node(label = "Escamotage", value = 20),
|
||||
Node(label = "Premiers soins", value = 20),
|
||||
),
|
||||
occupations = listOf(
|
||||
Node(label = "Survie", value = 80),
|
||||
Node(label = "Empathie (Animal)", value = 60),
|
||||
Node(label = "Pistage", value = 60),
|
||||
Node(label = "Discrétion", value = 60),
|
||||
Node(label = "Connaissance (Herboristerie)", value = 40),
|
||||
Node(label = "Artisanat (Onguent)", value = 60),
|
||||
),
|
||||
magics = listOf(
|
||||
Node(label = "Métamorphose (Loup)", value = 100),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun CharacterSheet(
|
||||
modifier: Modifier,
|
||||
scrollState: ScrollState = rememberScrollState(),
|
||||
width: Dp = 320.dp,
|
||||
characterSheet: CharacterSheetUio = CharacterSheetUio.Koryas,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(state = scrollState)
|
||||
.padding(all = 16.dp)
|
||||
.then(other = modifier),
|
||||
verticalArrangement = Arrangement.spacedBy(space = 16.dp),
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.h4,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = characterSheet.name,
|
||||
)
|
||||
FlowRow(
|
||||
maxItemsInEachRow = 3,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
space = 16.dp,
|
||||
alignment = Alignment.CenterHorizontally,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(space = 16.dp),
|
||||
) {
|
||||
characterSheet.characteristics.forEach {
|
||||
Stat(
|
||||
modifier = Modifier
|
||||
.width(width = (width - 32.dp) / 3)
|
||||
.height(height = 120.dp),
|
||||
label = it.label,
|
||||
value = it.value as? Int ?: 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier.width(width = width),
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
text = "Charactéristiques dérivées"
|
||||
)
|
||||
characterSheet.subCharacteristics.forEach {
|
||||
Characteristics(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = it.label,
|
||||
value = it.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier.width(width = width),
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
text = "Compétences"
|
||||
)
|
||||
characterSheet.skills.forEach {
|
||||
Characteristics(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = it.label,
|
||||
value = it.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier.width(width = width),
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
text = "Occupations"
|
||||
)
|
||||
characterSheet.occupations.forEach {
|
||||
Characteristics(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = it.label,
|
||||
value = it.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier.width(width = width),
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
text = "Compétences magiques"
|
||||
)
|
||||
characterSheet.magics.forEach {
|
||||
Characteristics(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = it.label,
|
||||
value = it.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Stat(
|
||||
modifier: Modifier = Modifier,
|
||||
label: String,
|
||||
value: Int,
|
||||
) {
|
||||
DecoratedBox(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(alignment = Alignment.TopCenter),
|
||||
style = MaterialTheme.typography.caption,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = label,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.align(alignment = Alignment.Center),
|
||||
style = MaterialTheme.typography.h4,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = "$value"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Characteristics(
|
||||
modifier: Modifier = Modifier,
|
||||
label: String,
|
||||
value: Any,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.body1,
|
||||
text = label
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.body1,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = "$value",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
package com.pixelized.desktop.lwa.screen.characterSheet
|
||||
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Checkbox
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.composable.DecoratedBox
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetUio.Node.Type
|
||||
|
||||
@Stable
|
||||
data class CharacterSheetUio(
|
||||
val name: String,
|
||||
val characteristics: List<Characteristic>,
|
||||
val subCharacteristics: List<Characteristic>,
|
||||
val skills: List<Node>,
|
||||
val occupations: List<Node>,
|
||||
val magics: List<Node>,
|
||||
) {
|
||||
@Stable
|
||||
data class Characteristic(
|
||||
val label: String,
|
||||
val value: String,
|
||||
)
|
||||
|
||||
@Stable
|
||||
data class Node(
|
||||
val type: Type,
|
||||
val label: String,
|
||||
val value: Int,
|
||||
) {
|
||||
@Stable
|
||||
enum class Type {
|
||||
SKILLS,
|
||||
OCCUPATIONS,
|
||||
MAGICS,
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Koryas = CharacterSheetUio(
|
||||
name = "Koryas Tissenpa",
|
||||
characteristics = listOf(
|
||||
Characteristic(label = "Force", value = "10"),
|
||||
Characteristic(label = "Dextérité", value = "11"),
|
||||
Characteristic(label = "Constitution", value = "15"),
|
||||
Characteristic(label = "Taille", value = "13"),
|
||||
Characteristic(label = "Intelligence", value = "9"),
|
||||
Characteristic(label = "Pouvoir", value = "15"),
|
||||
Characteristic(label = "Charisme", value = "7"),
|
||||
),
|
||||
subCharacteristics = listOf(
|
||||
Characteristic(label = "Déplacement ", value = "10"),
|
||||
Characteristic(label = "Points de vie", value = "14/14"),
|
||||
Characteristic(label = "Points de pouvoir", value = "13/13"),
|
||||
Characteristic(label = "Bonus aux dégâts", value = "1d4"),
|
||||
Characteristic(label = "Armure", value = "0"),
|
||||
),
|
||||
skills = listOf(
|
||||
Node(type = Type.SKILLS, label = "Bagarre", value = 75),
|
||||
Node(type = Type.SKILLS, label = "Esquive", value = 60),
|
||||
Node(type = Type.SKILLS, label = "Saisie", value = 20),
|
||||
Node(type = Type.SKILLS, label = "Lancer", value = 20),
|
||||
Node(type = Type.SKILLS, label = "Athlétisme", value = 60),
|
||||
Node(type = Type.SKILLS, label = "Acrobatie", value = 50),
|
||||
Node(type = Type.SKILLS, label = "Perception", value = 55),
|
||||
Node(type = Type.SKILLS, label = "Recherche", value = 25),
|
||||
Node(type = Type.SKILLS, label = "Empathie", value = 15),
|
||||
Node(type = Type.SKILLS, label = "Persuasion", value = 20),
|
||||
Node(type = Type.SKILLS, label = "Intimidation", value = 50),
|
||||
Node(type = Type.SKILLS, label = "Baratin", value = 20),
|
||||
Node(type = Type.SKILLS, label = "Marchandage", value = 10),
|
||||
Node(type = Type.SKILLS, label = "Escamotage", value = 20),
|
||||
Node(type = Type.SKILLS, label = "Premiers soins", value = 20),
|
||||
),
|
||||
occupations = listOf(
|
||||
Node(type = Type.OCCUPATIONS, label = "Survie", value = 80),
|
||||
Node(type = Type.OCCUPATIONS, label = "Empathie (Animal)", value = 60),
|
||||
Node(type = Type.OCCUPATIONS, label = "Pistage", value = 60),
|
||||
Node(type = Type.OCCUPATIONS, label = "Discrétion", value = 60),
|
||||
Node(type = Type.OCCUPATIONS, label = "Connaissance (Herboristerie)", value = 40),
|
||||
Node(type = Type.OCCUPATIONS, label = "Artisanat (Onguent)", value = 60),
|
||||
),
|
||||
magics = listOf(
|
||||
Node(type = Type.MAGICS, label = "Métamorphose (Loup)", value = 100),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun CharacterSheet(
|
||||
modifier: Modifier,
|
||||
scrollState: ScrollState = rememberScrollState(),
|
||||
width: Dp = 320.dp,
|
||||
characterSheet: CharacterSheetUio = CharacterSheetUio.Koryas,
|
||||
onCharacteristic: (characteristic: CharacterSheetUio.Characteristic) -> Unit,
|
||||
onSkill: (skill: CharacterSheetUio.Node) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(state = scrollState)
|
||||
.padding(all = 16.dp)
|
||||
.then(other = modifier),
|
||||
verticalArrangement = Arrangement.spacedBy(space = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 16.dp),
|
||||
style = MaterialTheme.typography.h4,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = characterSheet.name,
|
||||
)
|
||||
FlowRow(
|
||||
maxItemsInEachRow = 3,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
space = 16.dp,
|
||||
alignment = Alignment.CenterHorizontally,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(space = 16.dp),
|
||||
) {
|
||||
characterSheet.characteristics.forEach {
|
||||
Stat(
|
||||
modifier = Modifier
|
||||
.width(width = width / 3 - 32.dp)
|
||||
.height(height = 112.dp),
|
||||
characteristic = it,
|
||||
onClick = { onCharacteristic(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier
|
||||
.width(width = width)
|
||||
.padding(vertical = 8.dp),
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
text = "Charactéristiques dérivées"
|
||||
)
|
||||
characterSheet.subCharacteristics.forEach {
|
||||
Characteristics(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
characteristic = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier
|
||||
.width(width = width)
|
||||
.padding(vertical = 8.dp),
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
text = "Compétences",
|
||||
)
|
||||
characterSheet.skills.forEach {
|
||||
Skill(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = it.label,
|
||||
value = it.value,
|
||||
onClick = { onSkill(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier
|
||||
.width(width = width)
|
||||
.padding(vertical = 8.dp),
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
text = "Occupations"
|
||||
)
|
||||
characterSheet.occupations.forEach {
|
||||
Skill(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = it.label,
|
||||
value = it.value,
|
||||
onClick = { onSkill(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier
|
||||
.width(width = width)
|
||||
.padding(vertical = 8.dp),
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
text = "Compétences magiques"
|
||||
)
|
||||
characterSheet.magics.forEach {
|
||||
Skill(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = it.label,
|
||||
value = it.value,
|
||||
onClick = { onSkill(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Stat(
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(all = 8.dp),
|
||||
characteristic: CharacterSheetUio.Characteristic,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
DecoratedBox(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onClick)
|
||||
.padding(paddingValues = paddingValues)
|
||||
.then(other = modifier),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(alignment = Alignment.TopCenter),
|
||||
style = MaterialTheme.typography.caption,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = characteristic.label,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.align(alignment = Alignment.Center),
|
||||
style = MaterialTheme.typography.h4,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = characteristic.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Characteristics(
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp),
|
||||
characteristic: CharacterSheetUio.Characteristic,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues = paddingValues)
|
||||
.then(other = modifier),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.body1,
|
||||
text = characteristic.label
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.body1,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = characteristic.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Skill(
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp),
|
||||
label: String,
|
||||
value: Any,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onClick)
|
||||
.padding(paddingValues = paddingValues)
|
||||
.then(other = modifier),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.body1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = label
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.body1,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = "$value",
|
||||
)
|
||||
Checkbox(
|
||||
modifier = Modifier.size(size = 32.dp),
|
||||
checked = false,
|
||||
onCheckedChange = { }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.pixelized.desktop.lwa.screen.characterSheet
|
||||
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class CharacterSheetViewModel : ViewModel() {
|
||||
|
||||
private val _sheet = mutableStateOf<CharacterSheetUio?>(null)
|
||||
val sheet: State<CharacterSheetUio?> get() = _sheet
|
||||
|
||||
fun showCharacterSheet() {
|
||||
_sheet.value = CharacterSheetUio.Koryas
|
||||
}
|
||||
|
||||
fun hideCharacterSheet() {
|
||||
_sheet.value = null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.pixelized.desktop.lwa.screen.overlay
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.blur
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
||||
@Composable
|
||||
fun BlurOverlay(
|
||||
viewModel: BlurOverlayViewModel = viewModel { BlurOverlayViewModel() },
|
||||
overlay: @Composable BoxScope.() -> Unit,
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
) {
|
||||
Box {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.blur(radius = viewModel.blur.value),
|
||||
content = content,
|
||||
)
|
||||
|
||||
AnimatedContent(
|
||||
targetState = viewModel.overlay.value,
|
||||
transitionSpec = {
|
||||
val enter = fadeIn() + slideInVertically { 64 }
|
||||
val exit = fadeOut() + slideOutVertically { 64 }
|
||||
enter togetherWith exit
|
||||
},
|
||||
) { roll ->
|
||||
when (roll) {
|
||||
true -> Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
content = overlay,
|
||||
)
|
||||
|
||||
else -> Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.pixelized.desktop.lwa.screen.overlay
|
||||
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class BlurOverlayViewModel : ViewModel() {
|
||||
private val _overlay = mutableStateOf(false)
|
||||
|
||||
val overlay: State<Boolean>
|
||||
get() = _overlay
|
||||
|
||||
val blur: State<Dp>
|
||||
@Composable
|
||||
get() = animateDpAsState(
|
||||
targetValue = when (overlay.value) {
|
||||
true -> 16.dp
|
||||
else -> 0.dp
|
||||
}
|
||||
)
|
||||
|
||||
fun show() {
|
||||
_overlay.value = true
|
||||
}
|
||||
|
||||
fun hide() {
|
||||
_overlay.value = false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
package com.pixelized.desktop.lwa.screen.roll
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Shadow
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_d20_32dp
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
|
||||
@Stable
|
||||
data class RollUio(
|
||||
val label: String,
|
||||
val value: Int,
|
||||
)
|
||||
|
||||
@Stable
|
||||
data class RollResultUio(
|
||||
val label: String,
|
||||
val value: Int,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun RollPage(
|
||||
viewModel: RollViewModel = viewModel { RollViewModel() },
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = onDismissRequest,
|
||||
)
|
||||
.padding(all = 32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
text = "Jet de :",
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.h5,
|
||||
textAlign = TextAlign.Center,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
text = viewModel.roll.value.label,
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
text = "Réussite en dessous de : ${viewModel.roll.value.value}",
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
space = 24.dp,
|
||||
alignment = Alignment.CenterVertically,
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.clip(shape = CircleShape)
|
||||
.clickable { scope.launch { viewModel.roll() } }
|
||||
.padding(all = 24.dp)
|
||||
.size(size = 128.dp)
|
||||
.graphicsLayer {
|
||||
this.alpha = 0.8f
|
||||
this.rotationZ = viewModel.rollRotation.value
|
||||
},
|
||||
tint = MaterialTheme.colors.onSurface,
|
||||
painter = painterResource(Res.drawable.ic_d20_32dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
AnimatedContent(
|
||||
targetState = viewModel.result.value?.value?.toString() ?: "",
|
||||
transitionSpec = {
|
||||
val enter = fadeIn() + slideInVertically { 32 }
|
||||
val exit = fadeOut() + slideOutVertically { -32 }
|
||||
enter togetherWith exit
|
||||
}
|
||||
) { label ->
|
||||
Text(
|
||||
modifier = Modifier.width(width = 128.dp),
|
||||
style = MaterialTheme.typography.h3.copy(
|
||||
shadow = Shadow(
|
||||
color = MaterialTheme.colors.surface,
|
||||
offset = Offset.Zero,
|
||||
blurRadius = 8f,
|
||||
)
|
||||
),
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = label,
|
||||
)
|
||||
}
|
||||
}
|
||||
AnimatedContent(
|
||||
targetState = viewModel.result.value?.label ?: "",
|
||||
transitionSpec = { fadeIn() togetherWith fadeOut() },
|
||||
) { value ->
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.h4,
|
||||
textAlign = TextAlign.Center,
|
||||
text = value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package com.pixelized.desktop.lwa.screen.roll
|
||||
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.pixelized.desktop.lwa.business.RollUseCase
|
||||
import com.pixelized.desktop.lwa.business.SkillStepUseCase
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetUio
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
class RollViewModel : ViewModel() {
|
||||
private val _roll = mutableStateOf(RollUio(label = "", value = 0))
|
||||
val roll: State<RollUio> get() = _roll
|
||||
|
||||
private var rollJob: Job? = null
|
||||
private lateinit var rollStep: SkillStepUseCase.SkillStep
|
||||
val rollRotation = Animatable(0f)
|
||||
|
||||
private val _result = mutableStateOf<RollResultUio?>(null)
|
||||
val result: State<RollResultUio?> get() = _result
|
||||
|
||||
fun prepareRoll(node: CharacterSheetUio.Node) {
|
||||
val step = SkillStepUseCase.computeSkillStep(
|
||||
skill = node.value,
|
||||
)
|
||||
prepareRoll(
|
||||
label = node.label,
|
||||
step = step,
|
||||
)
|
||||
}
|
||||
|
||||
fun prepareRoll(characteristic: CharacterSheetUio.Characteristic) {
|
||||
val step = SkillStepUseCase.computeSkillStep(
|
||||
skill = (characteristic.value.toIntOrNull() ?: 0) * 5
|
||||
)
|
||||
prepareRoll(
|
||||
label = characteristic.label,
|
||||
step = step,
|
||||
)
|
||||
}
|
||||
|
||||
private fun prepareRoll(
|
||||
label: String,
|
||||
step: SkillStepUseCase.SkillStep,
|
||||
) {
|
||||
runBlocking { rollRotation.snapTo(0f) }
|
||||
rollStep = step
|
||||
_result.value = null
|
||||
_roll.value = RollUio(
|
||||
label = label,
|
||||
value = step.successRange.last
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun roll() {
|
||||
coroutineScope {
|
||||
_result.value = null
|
||||
|
||||
rollJob?.cancel()
|
||||
rollJob = launch {
|
||||
launch {
|
||||
rollRotation.animateTo(
|
||||
targetValue = rollRotation.value.let { it - it % 360 } + 360f * 3,
|
||||
animationSpec = spring(
|
||||
dampingRatio = Spring.DampingRatioNoBouncy,
|
||||
stiffness = Spring.StiffnessLow,
|
||||
)
|
||||
)
|
||||
}
|
||||
launch {
|
||||
delay(500)
|
||||
|
||||
val d100 = RollUseCase.rollD100()
|
||||
|
||||
_result.value = RollResultUio(
|
||||
label = when (d100) {
|
||||
// TODO wording
|
||||
in rollStep.criticalSuccessRange -> "Réussite critique"
|
||||
in rollStep.specialSuccessRange -> "Réussite spéciale"
|
||||
in rollStep.successRange -> "Réussite"
|
||||
in rollStep.failureRange -> "Échec"
|
||||
in rollStep.criticalFailureRange -> "Échec critique"
|
||||
else -> ""
|
||||
},
|
||||
value = d100,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue