From 1b9a2f48ca77651cc2209247769131a982c6fd3a Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Tue, 5 Nov 2024 17:17:33 +0100 Subject: [PATCH] Add string roll support --- composeApp/characterssheet.preferences_pb | Bin 2649 -> 88 bytes .../desktop/lwa/business/RollUseCase.kt | 42 +++++++- .../characterSheet/CharacterSheet.kt | 2 +- .../detail/CharacterSheetFactory.kt | 13 ++- .../detail/CharacterSheetPage.kt | 75 ++++++++++++--- .../edit/CharacterSheetEditPage.kt | 29 +++++- .../edit/CharacterSheetEditViewModel.kt | 18 +++- .../edit/CharacterSheetFactory.kt | 21 +++- .../desktop/lwa/screen/roll/RollPage.kt | 16 +-- .../desktop/lwa/screen/roll/RollViewModel.kt | 91 ++++++++++++------ 10 files changed, 241 insertions(+), 66 deletions(-) diff --git a/composeApp/characterssheet.preferences_pb b/composeApp/characterssheet.preferences_pb index a05cb135e44d11a626ad004cd22ebf0a090fa659..428c027aebc5cfe9ee7345210a226bb73c6f3c15 100644 GIT binary patch delta 27 icmca962Zh3HrbI$lG(<|YH~c25eGv#3j+fKLj?d@E(LP{ literal 2649 zcmd#U)zrH3e*z&z{c2!05xko|==Io0?Zr!XV@W(yO17n3t~apOlrFTw+~O zP+Y{|l$@WdSCCnenv+?TnxdDITAW>yU!a#$o~T!pT999yS(0B=sh6CQSd^Gtl3Em; zk(yef=ZwPjFnyj}Y$R&T#31a+z?xW;n_uL~zyZ>dS)7~b$-tAGpI2OxSyBpettSI# za%oXfYF>#)0fHS6;K{(5l3G!cT9jE*>B+#Bk(!yFQR2zKlbKhNnv;{6o|>1O>dC;G zn^*zT2ckiGICAsLz#jBuU@gcmPX$?ATvC*pmtKn8w_G1Mm z29e>FV3;yfKoP)#l607nk`4=I(qZ;tKuS8y;Lrvo9(D!>27U$x20kdw&A`CG%fP_E zi6q7amE$5Y=`b6nn3OQ67#XFR7$g}Q>87Tbndq9BTPErznwc5srllApo2MlkTbd=M zfRh!-J&fQKz*tRwLC`g~Ah9GPGgU#uF)uSWF-H?cnmr)1xFj(h zRS&mIW^r=S;g#?*0BnIq34@GteqLT;W^r+1UUI5}hDT~qQhrfpF{o%uMKxF0v8W`o zI597=L_xzpFTFH1uSBx~Y%|F50$|4rfcapvy-OI_of6X%i;7TP!tPpJSejXeBq##% zW@2VBvNu6uJ|HI*AxtzXVc>Qw$;biuqBs}H0F4p`PRHb;{G`MZWHZc57`Osbi;`0d zP<<^~!oV4nnw*hZl$?QN3V#U$2P|X}mWn_ODK1Sc2Gw4mC6mx2;E3?&TM;|650YjJX7Za!*cij*+$1r(*` zW~LSuD-`Ev<`qLCfr){EfsuiM0hSX%xetUvxsC^#6G40s2IW)`<_39(y@Ual!xB@> zQxnY$b5T;Z!x!XSw~?dPWA zOV^>`=p{E@Gn6o}p{Hv%T[+-])?\h*(?[ade])?(?\d+)d(?\d+)""" + ) + private val flatParser = Regex( + """(?[+-])?\h(?\d+)\b""" + ) + private val paramParser = Regex( + """(?[+-])?\h(?BDGT)\b""" + ) + fun rollD100(): Int { return d100.random() } + + fun roll(characterSheet: CharacterSheet, roll: String): Int { + println(roll) + return diceParser.findAll(roll).sumOf { + val (sign, modifier, quantity, faces) = it.destructured + ((if (sign == "-") -1 else 1) * quantity.toInt() * (Math.random() * faces.toDouble() + 1).toInt()).also { + println("roll ${sign}${quantity}d${faces} -> $it") + } + } + flatParser.findAll(roll).sumOf { + val (sign, value) = it.destructured + ((if (sign == "-") -1 else 1) * value.toInt()).also { + println("flat: ${sign}${value} -> $it") + } + } + paramParser.findAll(roll).sumOf { + val (sign, param) = it.destructured + (if (sign == "-") -1 else 1) * when (param) { + "BDGT" -> diceParser.findAll(characterSheet.damageBonus).sumOf { + val (sign, modifier, quantity, faces) = it.destructured + ((if (sign == "-") -1 else 1) * quantity.toInt() * (Math.random() * faces.toDouble() + 1).toInt()).also { + println("param: ${sign}${param} -> $it") + } + } + + else -> 0 + } + } + } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheet.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheet.kt index b172432..f473606 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheet.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheet.kt @@ -28,7 +28,7 @@ data class CharacterSheet( // magic skill val magics: List, // attack - val attacks: List, + val rolls: List, ) : Serializable { data class Skill( diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetFactory.kt index 931b3d4..2566a44 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetFactory.kt @@ -35,7 +35,6 @@ class CharacterSheetFactory { skills = model.skills.mapNotNull { if (it.value > 0) { Node( - type = Node.Type.SKILLS, label = it.label, value = it.value, ) @@ -46,7 +45,6 @@ class CharacterSheetFactory { occupations = model.occupations.mapNotNull { if (it.value > 0) { Node( - type = Node.Type.OCCUPATIONS, label = it.label, value = it.value, ) @@ -57,7 +55,6 @@ class CharacterSheetFactory { magics = model.magics.mapNotNull { if (it.value > 0) { Node( - type = Node.Type.MAGICS, label = it.label, value = it.value, ) @@ -65,6 +62,16 @@ class CharacterSheetFactory { null } }, + rolls = model.rolls.mapNotNull { + if (it.roll.isNotEmpty()) { + CharacterSheetPageUio.Roll( + label = it.label, + value = it.roll, + ) + } else { + null + } + } ) } } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetPage.kt index 788abc8..227f2b5 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetPage.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetPage.kt @@ -48,6 +48,9 @@ import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheet import com.pixelized.desktop.lwa.screen.roll.RollPage import com.pixelized.desktop.lwa.screen.roll.RollViewModel 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 CharacterSheetPageUio( @@ -58,6 +61,7 @@ data class CharacterSheetPageUio( val skills: List, val occupations: List, val magics: List, + val rolls: List, ) { @Stable data class Characteristic( @@ -67,15 +71,15 @@ data class CharacterSheetPageUio( @Stable data class Node( - val type: Type, val label: String, val value: Int, - ) { - @Stable - enum class Type { - SKILLS, OCCUPATIONS, MAGICS, - } - } + ) + + @Stable + data class Roll( + val label: String, + val value: String, + ) } @Composable @@ -101,28 +105,32 @@ fun CharacterSheetPage( ) }, content = { - viewModel.sheet.value?.let { + viewModel.sheet.value?.let { sheet -> CharacterSheetPageContent( modifier = Modifier.fillMaxSize(), - characterSheet = it, + characterSheet = sheet, onBack = { screen.popBackStack() }, onEdit = { - screen.navigateToCharacterSheetEdit(id = it.id) + screen.navigateToCharacterSheetEdit(id = sheet.id) }, onDelete = { scope.launch { - viewModel.deleteCharacter(id = it.id) + viewModel.deleteCharacter(id = sheet.id) screen.popBackStack() } }, onCharacteristic = { characteristic -> - rollViewModel.prepareRoll(characteristic = characteristic) + rollViewModel.prepareRoll(sheet = sheet, characteristic = characteristic) overlayViewModel.show() }, onSkill = { node -> - rollViewModel.prepareRoll(node = node) + rollViewModel.prepareRoll(sheet = sheet, node = node) + overlayViewModel.show() + }, + onRoll = { roll -> + rollViewModel.prepareRoll(sheet = sheet, roll = roll) overlayViewModel.show() }, ) @@ -144,6 +152,7 @@ fun CharacterSheetPageContent( onDelete: () -> Unit, onCharacteristic: (characteristic: CharacterSheetPageUio.Characteristic) -> Unit, onSkill: (skill: CharacterSheetPageUio.Node) -> Unit, + onRoll: (roll: CharacterSheetPageUio.Roll) -> Unit, ) { Scaffold( topBar = { @@ -289,6 +298,13 @@ fun CharacterSheetPageContent( } } } + characterSheet.rolls.forEach { + Roll( + modifier = Modifier.fillMaxWidth(), + label = it.label, + onClick = { onRoll(it) }, + ) + } } } ) @@ -346,6 +362,35 @@ private fun Characteristics( } } +@Composable +private fun Roll( + modifier: Modifier = Modifier, + paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp), + label: String, + onClick: () -> Unit, +) { + Row( + modifier = Modifier + .clickable(onClick = onClick) + .padding(paddingValues = paddingValues) + .then(other = modifier), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + modifier = Modifier.weight(1f), + style = MaterialTheme.typography.body1, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = label + ) + Icon( + painter = painterResource(Res.drawable.ic_d20_32dp), + contentDescription = null, + ) + } +} + @Composable private fun Skill( modifier: Modifier = Modifier, @@ -355,7 +400,9 @@ private fun Skill( onClick: () -> Unit, ) { Row( - modifier = Modifier.clickable(onClick = onClick).padding(paddingValues = paddingValues) + modifier = Modifier + .clickable(onClick = onClick) + .padding(paddingValues = paddingValues) .then(other = modifier), horizontalArrangement = Arrangement.spacedBy(space = 4.dp), verticalAlignment = Alignment.CenterVertically, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt index 4ed19f8..330cf15 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt @@ -40,6 +40,7 @@ data class CharacterSheetEditPageUio( val id: String, val name: FieldUio, val skills: List, + val rolls: List, ) { @Stable data class SkillGroup( @@ -55,7 +56,7 @@ data class CharacterSheetEditPageUio( SKILLS, OCCUPATIONS, MAGICS, - OTHER, + OTHERS, } } } @@ -77,8 +78,9 @@ fun CharacterSheetEditPage( ) { CharacterSheetEdit( form = viewModel.characterSheet.value, - onSkill = viewModel::onSkill, onBack = { screen.popBackStack() }, + onNewSkill = viewModel::onSkill, + onNewCategory = viewModel::onNewRoll, onSave = { scope.launch { viewModel.save() @@ -92,8 +94,9 @@ fun CharacterSheetEditPage( @Composable fun CharacterSheetEdit( form: CharacterSheetEditPageUio, - onSkill: (CharacterSheetEditPageUio.SkillGroup) -> Unit, onBack: () -> Unit, + onNewSkill: (CharacterSheetEditPageUio.SkillGroup) -> Unit, + onNewCategory: () -> Unit, onSave: () -> Unit, ) { Scaffold( @@ -159,7 +162,7 @@ fun CharacterSheetEdit( ) ) { TextButton( - onClick = { onSkill(it) }, + onClick = { onNewSkill(it) }, ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -180,6 +183,24 @@ fun CharacterSheetEdit( } } + form.rolls.forEach { + Form( + modifier = Modifier.fillMaxWidth(), + field = it, + ) + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End, + ) { + TextButton( + onClick = onNewCategory, + ) { + Text(text = "Ajouter un lancé") + } + } + Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt index 3f9e2f4..55711e5 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt @@ -24,7 +24,6 @@ class CharacterSheetEditViewModel( fun onSkill(skill: SkillGroup) { val sheet = _characterSheet.value - _characterSheet.value = sheet.copy( skills = sheet.skills.map { group -> if (skill.title == group.title) { @@ -42,7 +41,7 @@ class CharacterSheetEditViewModel( SkillGroup.Type.SKILLS -> "0" SkillGroup.Type.OCCUPATIONS -> "40" SkillGroup.Type.MAGICS -> "0" - SkillGroup.Type.OTHER -> "" + SkillGroup.Type.OTHERS -> "" } }, ) @@ -56,6 +55,21 @@ class CharacterSheetEditViewModel( ) } + fun onNewRoll() { + val sheet = _characterSheet.value + _characterSheet.value = sheet.copy( + rolls = sheet.rolls.toMutableList().apply { + add( + FieldUio.create( + label = "", + isLabelEditable = true, + valuePlaceHolder = { "" }, + ) + ) + } + ) + } + suspend fun save() { val sheet = _characterSheet.value val model = factory.convertToModel(sheet = sheet) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt index ab2eb16..471392e 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt @@ -5,6 +5,7 @@ import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditPageUio.SkillGroup import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio import java.util.UUID +import kotlin.math.ceil import kotlin.math.max class CharacterSheetFactory { @@ -48,7 +49,12 @@ class CharacterSheetFactory { used = false, ) }, - attacks = emptyList(), + rolls = sheet.rolls.map { + CharacterSheet.Roll( + label = it.label.value, + roll = it.unpack(), + ) + }, ) } @@ -123,7 +129,7 @@ class CharacterSheetFactory { ), FieldUio.create( label = "Points de vie", - valuePlaceHolder = { "${(con() + hei()) / 2}" }, + valuePlaceHolder = { "${ceil((con() + hei()) / 2f).toInt()}" }, initialValue = sheet?.maxHp?.toString() ?: "" ), FieldUio.create( @@ -160,7 +166,6 @@ class CharacterSheetFactory { fields = sheet?.skills?.map { FieldUio.create( label = it.label, - valuePlaceHolder = { "" }, initialValue = it.value.toString(), ) } ?: listOf( @@ -249,12 +254,18 @@ class CharacterSheetFactory { fields = sheet?.magics?.map { FieldUio.create( label = it.label, - valuePlaceHolder = { "" }, initialValue = it.value.toString() ) } ?: emptyList(), ), - ) + ), + rolls = sheet?.rolls?.map { + FieldUio.create( + label = it.label, + isLabelEditable = true, + initialValue = it.roll, + ) + } ?: emptyList() ) } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt index 8180ca7..7f12bc6 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt @@ -42,7 +42,7 @@ import org.jetbrains.compose.resources.painterResource @Stable data class RollUio( val label: String, - val value: Int, + val value: Int?, ) @Stable @@ -80,12 +80,14 @@ fun RollPage( 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}", - ) + viewModel.roll.value.value?.let { + Text( + style = MaterialTheme.typography.caption, + textAlign = TextAlign.Center, + overflow = TextOverflow.Ellipsis, + text = "Réussite en dessous de : ${it}", + ) + } Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy( diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt index 2190e11..3365441 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt @@ -8,6 +8,8 @@ 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.repository.characterSheet.CharacterSheet +import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope @@ -20,42 +22,71 @@ class RollViewModel : ViewModel() { val roll: State get() = _roll private var rollJob: Job? = null - private lateinit var rollStep: SkillStepUseCase.SkillStep + private var rollStep: SkillStepUseCase.SkillStep? = null + private lateinit var rollAction: String + private lateinit var sheet: CharacterSheet val rollRotation = Animatable(0f) private val _result = mutableStateOf(null) val result: State get() = _result - fun prepareRoll(node: CharacterSheetPageUio.Node) { - val step = SkillStepUseCase.computeSkillStep( - skill = node.value, - ) - prepareRoll( - label = node.label, - step = step, - ) - } - - fun prepareRoll(characteristic: CharacterSheetPageUio.Characteristic) { + fun prepareRoll( + sheet: CharacterSheetPageUio, + characteristic: CharacterSheetPageUio.Characteristic, + ) { val step = SkillStepUseCase.computeSkillStep( skill = (characteristic.value.toIntOrNull() ?: 0) * 5 ) prepareRoll( label = characteristic.label, - step = step, + rollAction = "1d100", + sheet = sheet, + rollStep = step, + ) + } + + fun prepareRoll( + sheet: CharacterSheetPageUio, + node: CharacterSheetPageUio.Node, + ) { + val step = SkillStepUseCase.computeSkillStep( + skill = node.value, + ) + prepareRoll( + label = node.label, + rollAction = "1d100", + sheet = sheet, + rollStep = step, + ) + } + + fun prepareRoll( + sheet: CharacterSheetPageUio, + roll: CharacterSheetPageUio.Roll, + ) { + prepareRoll( + label = roll.label, + rollAction = roll.value, + sheet = sheet, + rollStep = null, ) } private fun prepareRoll( label: String, - step: SkillStepUseCase.SkillStep, + rollAction: String, + sheet: CharacterSheetPageUio, + rollStep: SkillStepUseCase.SkillStep?, ) { runBlocking { rollRotation.snapTo(0f) } - rollStep = step + this.rollStep = rollStep + this.rollAction = rollAction + this.sheet = CharacterSheetRepository.characterSheetFlow(id = sheet.id).value!! + _result.value = null _roll.value = RollUio( label = label, - value = step.successRange.last + value = rollStep?.successRange?.last ) } @@ -77,19 +108,25 @@ class RollViewModel : ViewModel() { launch { delay(500) - val d100 = RollUseCase.rollD100() + val roll = if (rollAction == "1d100") { + RollUseCase.rollD100() + } else { + RollUseCase.roll(characterSheet = sheet, roll = rollAction) + } _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, + label = rollStep?.let { rollStep -> + when (roll) { + // 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 = roll, ) } }