Minor UI adjustment.
This commit is contained in:
parent
ca456b55d9
commit
f6d026d50f
11 changed files with 3907 additions and 329 deletions
|
|
@ -75,6 +75,11 @@
|
||||||
<string name="character_sheet__occupations_title">Occupations</string>
|
<string name="character_sheet__occupations_title">Occupations</string>
|
||||||
<string name="character_sheet__magics__title">Compétences magiques</string>
|
<string name="character_sheet__magics__title">Compétences magiques</string>
|
||||||
|
|
||||||
|
<string name="character_sheet__delete_dialog__title">Supprimer la feuille de personnage</string>
|
||||||
|
<string name="character_sheet__delete_dialog__description">Êtes-vous sûr de vouloir supprimer "%1$s" ?</string>
|
||||||
|
<string name="character_sheet__delete_dialog__confirm_action">Confirmer</string>
|
||||||
|
<string name="character_sheet__delete_dialog__cancel_action">Annuler</string>
|
||||||
|
|
||||||
<string name="network__title">Configuration de la table</string>
|
<string name="network__title">Configuration de la table</string>
|
||||||
<string name="network__player_name__label">Nom du joueur</string>
|
<string name="network__player_name__label">Nom du joueur</string>
|
||||||
<string name="network__host__label">host</string>
|
<string name="network__host__label">host</string>
|
||||||
|
|
|
||||||
|
|
@ -1,95 +1,67 @@
|
||||||
package com.pixelized.desktop.lwa.business
|
package com.pixelized.desktop.lwa.business
|
||||||
|
|
||||||
|
import androidx.compose.ui.util.fastRoundToInt
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
import kotlin.math.round
|
||||||
|
|
||||||
object SkillStepUseCase {
|
object SkillStepUseCase {
|
||||||
|
val NONE: IntRange = -1..-1
|
||||||
|
|
||||||
data class SkillStep(
|
data class SkillStep(
|
||||||
val criticalSuccessRange: IntRange,
|
val criticalSuccess: IntRange,
|
||||||
val specialSuccessRange: IntRange,
|
val specialSuccess: IntRange,
|
||||||
val successRange: IntRange,
|
val success: IntRange,
|
||||||
val failureRange: IntRange,
|
val failure: IntRange,
|
||||||
val criticalFailureRange: IntRange,
|
val criticalFailure: 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.
|
* 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 {
|
fun computeSkillStep(skill: Int): SkillStep {
|
||||||
val criticalSuccess: Pair<Int, Int>? = when (skill) {
|
val criticalSuccess = 1..min(roundToInt { skill * 0.05f }, 99)
|
||||||
in (0..5) -> null
|
val specialSuccess = (roundToInt { skill * 0.05f } + 1)..min(roundToInt { skill * 0.2f }, 99)
|
||||||
in (10..25) -> 1 to 1
|
val success = (roundToInt { skill * 0.2f } + 1)..min(skill, 99)
|
||||||
in (30..45) -> 1 to 2
|
val criticalFailure = 100 - max(4 - criticalSuccess.last, 0)..100
|
||||||
in (50..65) -> 1 to 3
|
val failure = (success.last + 1) until criticalFailure.first
|
||||||
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 14
|
|
||||||
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(
|
return SkillStep(
|
||||||
criticalSuccess = criticalSuccess,
|
criticalSuccess = criticalSuccess.takeIf { it.first <= it.last } ?: NONE,
|
||||||
specialSuccess = specialSuccess,
|
specialSuccess = specialSuccess.takeIf { it.first <= it.last } ?: NONE,
|
||||||
success = success,
|
success = success.takeIf { it.first <= it.last } ?: NONE,
|
||||||
failure = failure,
|
failure = failure.takeIf { it.first <= it.last } ?: NONE,
|
||||||
criticalFailure = criticalFailure,
|
criticalFailure = criticalFailure.takeIf { it.first <= it.last } ?: NONE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun roundToInt(block: () -> Float): Int = round(block()).fastRoundToInt()
|
||||||
|
|
||||||
|
fun exportWiki() {
|
||||||
|
fun print(range: IntRange): String = when {
|
||||||
|
range == NONE -> "-"
|
||||||
|
range.first == range.last -> "${range.first}"
|
||||||
|
else -> "${range.first} - ${range.last}"
|
||||||
|
}
|
||||||
|
repeat(100) { skill ->
|
||||||
|
val step = computeSkillStep(skill + 1)
|
||||||
|
println(
|
||||||
|
"|!${skill + 1} " +
|
||||||
|
"|${print(step.criticalSuccess)} " +
|
||||||
|
"|${print(step.specialSuccess)} " +
|
||||||
|
"|${print(step.success)} " +
|
||||||
|
"|${print(step.failure)} " +
|
||||||
|
"|${print(step.criticalFailure)} " +
|
||||||
|
"|"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun exportTest() {
|
||||||
|
println("val expected = hashMapOf(")
|
||||||
|
(1..500).forEach {
|
||||||
|
println(" $it to ${computeSkillStep(it)},")
|
||||||
|
}
|
||||||
|
println(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
package com.pixelized.desktop.lwa.screen.characterSheet.detail
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.SizeTransform
|
||||||
|
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.background
|
||||||
|
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.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Surface
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__cancel_action
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__confirm_action
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__description
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__title
|
||||||
|
import org.jetbrains.compose.resources.stringResource
|
||||||
|
|
||||||
|
private val DefaultScrimColor = Color.Black.copy(alpha = 0.6f)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class CharacterSheetDeleteConfirmationDialogUio(
|
||||||
|
val id: String,
|
||||||
|
val name: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CharacterSheetDeleteConfirmationDialog(
|
||||||
|
dialog: State<CharacterSheetDeleteConfirmationDialogUio?>,
|
||||||
|
onConfirm: (CharacterSheetDeleteConfirmationDialogUio) -> Unit,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = dialog.value != null,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(color = DefaultScrimColor),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AnimatedContent(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
targetState = dialog.value,
|
||||||
|
transitionSpec = {
|
||||||
|
val enter = fadeIn() + slideInVertically { 32 }
|
||||||
|
val exit = fadeOut() + slideOutVertically { 32 }
|
||||||
|
enter togetherWith exit using SizeTransform(clip = false)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
when (it) {
|
||||||
|
null -> Box(
|
||||||
|
modifier = Modifier,
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> Dialog(
|
||||||
|
dialog = it,
|
||||||
|
onConfirm = onConfirm,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Dialog(
|
||||||
|
dialog: CharacterSheetDeleteConfirmationDialogUio,
|
||||||
|
onConfirm: (CharacterSheetDeleteConfirmationDialogUio) -> Unit,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = null,
|
||||||
|
onClick = onDismissRequest,
|
||||||
|
)
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(all = 32.dp),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
shape = remember { RoundedCornerShape(size = 16.dp) },
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(space = 24.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
.padding(top = 24.dp),
|
||||||
|
style = MaterialTheme.typography.h6,
|
||||||
|
text = stringResource(Res.string.character_sheet__delete_dialog__title),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 24.dp),
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
text = stringResource(
|
||||||
|
Res.string.character_sheet__delete_dialog__description,
|
||||||
|
dialog.name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(bottom = 4.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(
|
||||||
|
space = 4.dp,
|
||||||
|
alignment = Alignment.End
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = onDismissRequest,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(Res.string.character_sheet__delete_dialog__cancel_action)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TextButton(
|
||||||
|
onClick = { onConfirm(dialog) },
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(Res.string.character_sheet__delete_dialog__confirm_action)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -71,6 +71,7 @@ class CharacterSheetFactory {
|
||||||
Node(
|
Node(
|
||||||
label = it.label,
|
label = it.label,
|
||||||
value = it.value,
|
value = it.value,
|
||||||
|
used = it.used,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
@ -81,6 +82,7 @@ class CharacterSheetFactory {
|
||||||
Node(
|
Node(
|
||||||
label = it.label,
|
label = it.label,
|
||||||
value = it.value,
|
value = it.value,
|
||||||
|
used = it.used,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
@ -91,6 +93,7 @@ class CharacterSheetFactory {
|
||||||
Node(
|
Node(
|
||||||
label = it.label,
|
label = it.label,
|
||||||
value = it.value,
|
value = it.value,
|
||||||
|
used = it.used,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
|
@ -16,6 +17,7 @@ import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.Checkbox
|
import androidx.compose.material.Checkbox
|
||||||
|
import androidx.compose.material.CheckboxDefaults
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.IconButton
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
|
|
@ -76,6 +78,7 @@ data class CharacterSheetPageUio(
|
||||||
data class Node(
|
data class Node(
|
||||||
val label: String,
|
val label: String,
|
||||||
val value: Int,
|
val value: Int,
|
||||||
|
val used: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
|
|
@ -120,12 +123,7 @@ fun CharacterSheetPage(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onDelete = {
|
onDelete = {
|
||||||
scope.launch {
|
viewModel.showConfirmCharacterDeletionDialog()
|
||||||
viewModel.deleteCharacter(id = sheet.id)
|
|
||||||
if (screen.popBackStack().not()) {
|
|
||||||
window.closeWindows()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onCharacteristic = { characteristic ->
|
onCharacteristic = { characteristic ->
|
||||||
rollViewModel.prepareRoll(
|
rollViewModel.prepareRoll(
|
||||||
|
|
@ -138,6 +136,7 @@ fun CharacterSheetPage(
|
||||||
rollViewModel.prepareRoll(sheet = sheet, node = node)
|
rollViewModel.prepareRoll(sheet = sheet, node = node)
|
||||||
overlayViewModel.show()
|
overlayViewModel.show()
|
||||||
},
|
},
|
||||||
|
onUseSkill = viewModel::onUseSkill,
|
||||||
onRoll = { roll ->
|
onRoll = { roll ->
|
||||||
rollViewModel.prepareRoll(sheet = sheet, roll = roll)
|
rollViewModel.prepareRoll(sheet = sheet, roll = roll)
|
||||||
overlayViewModel.show()
|
overlayViewModel.show()
|
||||||
|
|
@ -147,6 +146,21 @@ fun CharacterSheetPage(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CharacterSheetDeleteConfirmationDialog(
|
||||||
|
dialog = viewModel.displayDeleteConfirmationDialog,
|
||||||
|
onConfirm = {
|
||||||
|
scope.launch {
|
||||||
|
viewModel.deleteCharacter(id = it.id)
|
||||||
|
if (screen.popBackStack().not()) {
|
||||||
|
window.closeWindows()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = {
|
||||||
|
viewModel.hideConfirmCharacterDeletionDialog()
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -158,6 +172,7 @@ fun CharacterSheetPageContent(
|
||||||
onDelete: () -> Unit,
|
onDelete: () -> Unit,
|
||||||
onCharacteristic: (characteristic: CharacterSheetPageUio.Characteristic) -> Unit,
|
onCharacteristic: (characteristic: CharacterSheetPageUio.Characteristic) -> Unit,
|
||||||
onSkill: (skill: CharacterSheetPageUio.Node) -> Unit,
|
onSkill: (skill: CharacterSheetPageUio.Node) -> Unit,
|
||||||
|
onUseSkill: (skill: CharacterSheetPageUio.Node) -> Unit,
|
||||||
onRoll: (roll: CharacterSheetPageUio.Roll) -> Unit,
|
onRoll: (roll: CharacterSheetPageUio.Roll) -> Unit,
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
|
@ -238,7 +253,7 @@ fun CharacterSheetPageContent(
|
||||||
)
|
)
|
||||||
characterSheet.subCharacteristics.forEach {
|
characterSheet.subCharacteristics.forEach {
|
||||||
Characteristics(
|
Characteristics(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.cell(),
|
||||||
characteristic = it,
|
characteristic = it,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -258,12 +273,12 @@ fun CharacterSheetPageContent(
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
text = stringResource(Res.string.character_sheet__skills__title),
|
text = stringResource(Res.string.character_sheet__skills__title),
|
||||||
)
|
)
|
||||||
characterSheet.skills.forEach {
|
characterSheet.skills.forEach { skill ->
|
||||||
Skill(
|
Skill(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.cell(),
|
||||||
label = it.label,
|
node = skill,
|
||||||
value = it.value,
|
onClick = { onSkill(skill) },
|
||||||
onClick = { onSkill(it) },
|
onUse = { onUseSkill(skill) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -280,12 +295,12 @@ fun CharacterSheetPageContent(
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
text = stringResource(Res.string.character_sheet__occupations_title),
|
text = stringResource(Res.string.character_sheet__occupations_title),
|
||||||
)
|
)
|
||||||
characterSheet.occupations.forEach {
|
characterSheet.occupations.forEach { occupation ->
|
||||||
Skill(
|
Skill(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.cell(),
|
||||||
label = it.label,
|
node = occupation,
|
||||||
value = it.value,
|
onClick = { onSkill(occupation) },
|
||||||
onClick = { onSkill(it) },
|
onUse = { onUseSkill(occupation) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -302,21 +317,21 @@ fun CharacterSheetPageContent(
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
text = stringResource(Res.string.character_sheet__magics__title),
|
text = stringResource(Res.string.character_sheet__magics__title),
|
||||||
)
|
)
|
||||||
characterSheet.magics.forEach {
|
characterSheet.magics.forEach { magic ->
|
||||||
Skill(
|
Skill(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.cell(),
|
||||||
label = it.label,
|
node = magic,
|
||||||
value = it.value,
|
onClick = { onSkill(magic) },
|
||||||
onClick = { onSkill(it) },
|
onUse = { onUseSkill(magic) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
characterSheet.rolls.forEach {
|
characterSheet.rolls.forEach { roll ->
|
||||||
Roll(
|
Roll(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.cell(),
|
||||||
label = it.label,
|
label = roll.label,
|
||||||
onClick = { onRoll(it) },
|
onClick = { onRoll(roll) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -381,9 +396,49 @@ private fun Characteristics(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Roll(
|
private fun Skill(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp),
|
paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp),
|
||||||
|
node: CharacterSheetPageUio.Node,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onUse: () -> 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 = node.label
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colors.primary,
|
||||||
|
text = "${node.value}",
|
||||||
|
)
|
||||||
|
Checkbox(
|
||||||
|
modifier = Modifier.size(size = 32.dp),
|
||||||
|
checked = node.used,
|
||||||
|
colors = CheckboxDefaults.colors(
|
||||||
|
checkedColor = MaterialTheme.colors.primary,
|
||||||
|
),
|
||||||
|
onCheckedChange = { onUse() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Roll(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
paddingValues: PaddingValues = PaddingValues(start = 10.dp, end = 14.dp),
|
||||||
label: String,
|
label: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
@ -403,6 +458,7 @@ private fun Roll(
|
||||||
text = label
|
text = label
|
||||||
)
|
)
|
||||||
Icon(
|
Icon(
|
||||||
|
modifier = Modifier.size(size = 24.dp),
|
||||||
painter = painterResource(Res.drawable.ic_d20_32dp),
|
painter = painterResource(Res.drawable.ic_d20_32dp),
|
||||||
tint = MaterialTheme.colors.primary,
|
tint = MaterialTheme.colors.primary,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
|
@ -410,35 +466,4 @@ private fun Roll(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
private fun Modifier.cell(): Modifier = this.fillMaxWidth().height(height = 32.dp)
|
||||||
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,
|
|
||||||
color = MaterialTheme.colors.primary,
|
|
||||||
text = "$value",
|
|
||||||
)
|
|
||||||
Checkbox(modifier = Modifier.size(size = 32.dp), checked = false, onCheckedChange = { })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.pixelized.desktop.lwa.screen.characterSheet.detail
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetDestination
|
import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetDestination
|
||||||
|
|
@ -18,6 +19,11 @@ class CharacterSheetViewModel(
|
||||||
private val repository = CharacterSheetRepository
|
private val repository = CharacterSheetRepository
|
||||||
private val factory = CharacterSheetFactory()
|
private val factory = CharacterSheetFactory()
|
||||||
|
|
||||||
|
private val _displayDeleteConfirmationDialog =
|
||||||
|
mutableStateOf<CharacterSheetDeleteConfirmationDialogUio?>(null)
|
||||||
|
val displayDeleteConfirmationDialog: State<CharacterSheetDeleteConfirmationDialogUio?>
|
||||||
|
get() = _displayDeleteConfirmationDialog
|
||||||
|
|
||||||
val sheet: State<CharacterSheetPageUio?>
|
val sheet: State<CharacterSheetPageUio?>
|
||||||
@Composable
|
@Composable
|
||||||
@Stable
|
@Stable
|
||||||
|
|
@ -32,4 +38,40 @@ class CharacterSheetViewModel(
|
||||||
suspend fun deleteCharacter(id: String) {
|
suspend fun deleteCharacter(id: String) {
|
||||||
repository.delete(id = id)
|
repository.delete(id = id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onUseSkill(skill: CharacterSheetPageUio.Node) {
|
||||||
|
repository.characterSheetFlow(id = argument.id).value?.let { sheet ->
|
||||||
|
|
||||||
|
val skills = sheet.skills.map {
|
||||||
|
if (it.label == skill.label) it.copy(used = it.used.not()) else it
|
||||||
|
}
|
||||||
|
val occupations = sheet.occupations.map {
|
||||||
|
if (it.label == skill.label) it.copy(used = it.used.not()) else it
|
||||||
|
}
|
||||||
|
val magics = sheet.magics.map {
|
||||||
|
if (it.label == skill.label) it.copy(used = it.used.not()) else it
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.save(
|
||||||
|
characterSheet = sheet.copy(
|
||||||
|
skills = skills,
|
||||||
|
occupations = occupations,
|
||||||
|
magics = magics,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showConfirmCharacterDeletionDialog() {
|
||||||
|
repository.characterSheetFlow(id = argument.id).value?.let { sheet ->
|
||||||
|
_displayDeleteConfirmationDialog.value = CharacterSheetDeleteConfirmationDialogUio(
|
||||||
|
id = sheet.id,
|
||||||
|
name = sheet.name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideConfirmCharacterDeletionDialog() {
|
||||||
|
_displayDeleteConfirmationDialog.value = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package com.pixelized.desktop.lwa.screen.roll
|
package com.pixelized.desktop.lwa.screen.roll
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.SizeTransform
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
|
@ -16,6 +18,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
|
@ -146,8 +149,8 @@ fun RollPage(
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
val enter = fadeIn() + slideInVertically { 32 }
|
val enter = fadeIn() + slideInVertically { 32 }
|
||||||
val exit = fadeOut() + slideOutVertically { -32 }
|
val exit = fadeOut() + slideOutVertically { -32 }
|
||||||
enter togetherWith exit
|
enter togetherWith exit using SizeTransform(clip = false)
|
||||||
}
|
},
|
||||||
) { label ->
|
) { label ->
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.width(width = 128.dp),
|
modifier = Modifier.width(width = 128.dp),
|
||||||
|
|
@ -222,11 +225,19 @@ fun Difficulty(
|
||||||
style = MaterialTheme.typography.body1,
|
style = MaterialTheme.typography.body1,
|
||||||
text = stringResource(Res.string.roll_page__dc__label)
|
text = stringResource(Res.string.roll_page__dc__label)
|
||||||
)
|
)
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = difficulty.difficulty,
|
||||||
|
transitionSpec = {
|
||||||
|
val enter = fadeIn() + slideInVertically { -16 }
|
||||||
|
val exit = fadeOut() + slideOutVertically { 16 }
|
||||||
|
enter togetherWith exit using SizeTransform(clip = false)
|
||||||
|
},
|
||||||
|
) { difficulty ->
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.alignByBaseline(),
|
modifier = Modifier.alignByBaseline().animateContentSize(),
|
||||||
style = MaterialTheme.typography.body1,
|
style = MaterialTheme.typography.body1,
|
||||||
color = MaterialTheme.colors.primary,
|
color = MaterialTheme.colors.primary,
|
||||||
text = when (difficulty.difficulty) {
|
text = when (difficulty) {
|
||||||
Difficulty.EASY -> stringResource(Res.string.roll_page__dc_easy__label)
|
Difficulty.EASY -> stringResource(Res.string.roll_page__dc_easy__label)
|
||||||
Difficulty.NORMAL -> stringResource(Res.string.roll_page__dc_normal__label)
|
Difficulty.NORMAL -> stringResource(Res.string.roll_page__dc_normal__label)
|
||||||
Difficulty.HARD -> stringResource(Res.string.roll_page__dc_hard__label)
|
Difficulty.HARD -> stringResource(Res.string.roll_page__dc_hard__label)
|
||||||
|
|
@ -234,13 +245,14 @@ fun Difficulty(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val rotation = animateFloatAsState(
|
val rotation = animateFloatAsState(
|
||||||
targetValue = if (difficulty.open) -180f else 0f,
|
targetValue = if (difficulty.open) -180f else 0f,
|
||||||
)
|
)
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier.graphicsLayer {
|
modifier = Modifier
|
||||||
rotationZ = rotation.value
|
.offset(x = (-12).dp)
|
||||||
},
|
.graphicsLayer { rotationZ = rotation.value },
|
||||||
imageVector = Icons.Default.ArrowDropDown,
|
imageVector = Icons.Default.ArrowDropDown,
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class RollViewModel : ViewModel() {
|
||||||
_rollResult.value = null
|
_rollResult.value = null
|
||||||
_rollTitle.value = RollTitleUio(
|
_rollTitle.value = RollTitleUio(
|
||||||
label = label,
|
label = label,
|
||||||
value = rollStep?.successRange?.last
|
value = rollStep?.success?.last
|
||||||
)
|
)
|
||||||
_rollDifficulty.value = rollSuccessValue?.let {
|
_rollDifficulty.value = rollSuccessValue?.let {
|
||||||
DifficultyUio(
|
DifficultyUio(
|
||||||
|
|
@ -151,11 +151,11 @@ class RollViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
val success = rollStep?.let {
|
val success = rollStep?.let {
|
||||||
when (roll) {
|
when (roll) {
|
||||||
in it.criticalSuccessRange -> getString(resource = Res.string.roll_page__critical_success)
|
in it.criticalSuccess -> getString(resource = Res.string.roll_page__critical_success)
|
||||||
in it.specialSuccessRange -> getString(resource = Res.string.roll_page__special_success)
|
in it.specialSuccess -> getString(resource = Res.string.roll_page__special_success)
|
||||||
in it.successRange -> getString(resource = Res.string.roll_page__success)
|
in it.success -> getString(resource = Res.string.roll_page__success)
|
||||||
in it.failureRange -> getString(resource = Res.string.roll_page__failure)
|
in it.failure -> getString(resource = Res.string.roll_page__failure)
|
||||||
in it.criticalFailureRange -> getString(resource = Res.string.roll_page__critical_failure)
|
in it.criticalFailure -> getString(resource = Res.string.roll_page__critical_failure)
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +175,7 @@ class RollViewModel : ViewModel() {
|
||||||
else -> null
|
else -> null
|
||||||
},
|
},
|
||||||
rollValue = roll,
|
rollValue = roll,
|
||||||
rollSuccessLimit = rollStep?.successRange?.last,
|
rollSuccessLimit = rollStep?.success?.last,
|
||||||
resultLabel = success,
|
resultLabel = success,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +207,7 @@ class RollViewModel : ViewModel() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_rollTitle.value = _rollTitle.value.copy(
|
_rollTitle.value = _rollTitle.value.copy(
|
||||||
value = rollStep?.successRange?.last
|
value = rollStep?.success?.last
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,7 +94,7 @@ fun RollHistoryItem(
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.alignByBaseline(),
|
modifier = Modifier.alignByBaseline(),
|
||||||
style = MaterialTheme.typography.h6,
|
style = MaterialTheme.typography.h5,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
text = "${roll.rollValue}",
|
text = "${roll.rollValue}",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +1,12 @@
|
||||||
package com.pixelized.desktop.lwa
|
package com.pixelized.desktop.lwa
|
||||||
|
|
||||||
import androidx.compose.ui.window.application
|
import androidx.compose.ui.window.application
|
||||||
|
import com.pixelized.desktop.lwa.business.SkillStepUseCase
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
|
|
||||||
|
SkillStepUseCase.exportTest()
|
||||||
|
|
||||||
application {
|
application {
|
||||||
App()
|
App()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue