From d93ffd9499a81fdb60bacbb29e97c350369d997f Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Wed, 27 Nov 2024 15:39:22 +0100 Subject: [PATCH] Allow negative value into the character sheet bonuses. --- .../composeResources/values/strings.xml | 2 + .../business/SkillValueComputationUseCase.kt | 6 +- .../characterSheet/model/CharacterSheet.kt | 4 +- .../model/CharacterSheetJsonV1.kt | 4 +- .../edit/CharacterSheetEditFactory.kt | 65 +++++++++------- .../edit/CharacterSheetEditPage.kt | 20 +++-- .../edit/CharacterSheetEditViewModel.kt | 21 +++-- .../edit/composable/ActionField.kt | 77 +++++++++++++++++++ 8 files changed, 154 insertions(+), 45 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/ActionField.kt diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 550afe3..dce0aee 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -63,6 +63,8 @@ Premiers soins Occupations Ajouter une occupation + Nom + Bonus Compétences magiques Ajouter une compétence magique Supprimer diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/business/SkillValueComputationUseCase.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/business/SkillValueComputationUseCase.kt index 366ed01..3f46a28 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/business/SkillValueComputationUseCase.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/business/SkillValueComputationUseCase.kt @@ -43,7 +43,7 @@ class SkillValueComputationUseCase( baseSum } - return max(base + skill.bonus + skill.level - diminished, 0) + return max(base + (skill.bonus ?: 0) + (skill.level ?: 0) - diminished, 0) } fun computeRoll( @@ -83,8 +83,8 @@ class SkillValueComputationUseCase( if (damageBonusInstructions is Instruction.Dice) { RollUseCase.roll( quantity = damageBonusInstructions.quantity, - faces = damageBonusInstructions.faces - ) / 2 + faces = damageBonusInstructions.faces / 2, + ) } else { 0 } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/model/CharacterSheet.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/model/CharacterSheet.kt index 2ad8a68..3a4b615 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/model/CharacterSheet.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/model/CharacterSheet.kt @@ -35,8 +35,8 @@ data class CharacterSheet( val id: String, val label: String, val base: String, - val bonus: Int, - val level: Int, + val bonus: Int?, + val level: Int?, val occupation: Boolean, val used: Boolean, ) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/model/CharacterSheetJsonV1.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/model/CharacterSheetJsonV1.kt index 3219c2e..559625c 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/model/CharacterSheetJsonV1.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/model/CharacterSheetJsonV1.kt @@ -36,8 +36,8 @@ data class CharacterSheetJsonV1( val id: String, val label: String, val base: String, - val bonus: Int, - val level: Int, + val bonus: Int?, + val level: Int?, val occupation: Boolean, val used: Boolean, ) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditFactory.kt index 9529b19..f1f21f6 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditFactory.kt @@ -4,13 +4,15 @@ import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import com.pixelized.desktop.lwa.business.DamageBonusUseCase import com.pixelized.desktop.lwa.business.SkillNormalizerUseCase.normalize -import com.pixelized.desktop.lwa.parser.arithmetic.ArithmeticParser import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet import com.pixelized.desktop.lwa.screen.characterSheet.common.SkillFieldFactory import com.pixelized.desktop.lwa.screen.characterSheet.common.occupation +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.ActionFieldUio import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.BaseSkillFieldUio import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__action_label +import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__name_label import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__cha import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__con import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__dex @@ -53,7 +55,6 @@ import kotlin.math.min class CharacterSheetEditFactory( private val bonusDamageUseCase: DamageBonusUseCase, private val skillFactory: SkillFieldFactory, - private val parser: ArithmeticParser, ) { fun updateCharacterSheet( currentSheet: CharacterSheet?, @@ -92,9 +93,9 @@ class CharacterSheetEditFactory( CharacterSheet.Skill( id = editedSkill.id, label = editedSkill.label, - base = "${editedSkill.base}", - bonus = editedSkill.bonus.value.value.toIntOrNull() ?: 0, - level = editedSkill.level.value.value.toIntOrNull() ?: 0, + base = "${editedSkill.base.value}", + bonus = editedSkill.bonus.value.value.toIntOrNull(), + level = editedSkill.level.value.value.toIntOrNull(), occupation = editedSkill.option.checked.value, used = currentSkill?.used ?: false, ) @@ -107,8 +108,8 @@ class CharacterSheetEditFactory( id = editedSkill.id, label = editedSkill.label.value.value, base = editedSkill.base.value.value, - bonus = editedSkill.bonus.value.value.toIntOrNull() ?: 0, - level = editedSkill.level.value.value.toIntOrNull() ?: 0, + bonus = editedSkill.bonus.value.value.toIntOrNull(), + level = editedSkill.level.value.value.toIntOrNull(), occupation = editedSkill.options.occupation, used = currentSkill?.used ?: false, ) @@ -121,17 +122,17 @@ class CharacterSheetEditFactory( id = editedSkill.id, label = editedSkill.label.value.value, base = editedSkill.base.value.value, - bonus = editedSkill.bonus.value.value.toIntOrNull() ?: 0, - level = editedSkill.level.value.value.toIntOrNull() ?: 0, + bonus = editedSkill.bonus.value.value.toIntOrNull(), + level = editedSkill.level.value.value.toIntOrNull(), occupation = editedSkill.options.occupation, used = currentSkill?.used ?: false, ) }, actions = editedSheet.actions.map { CharacterSheet.Roll( - id = "", // TODO - label = it.label.value, - roll = it.unpack(), + id = it.id, + label = it.label.value.value, + roll = it.action.value.value, ) }, ) @@ -139,7 +140,7 @@ class CharacterSheetEditFactory( suspend fun convertToUio( sheet: CharacterSheet?, - onDeleteSkill: (skillId: String) -> Unit, + onDeleteSkill: (id: String) -> Unit, ): CharacterSheetEditPageUio { val str = FieldUio.create( initialLabel = getString(Res.string.character_sheet_edit__characteristics__str), @@ -149,27 +150,27 @@ class CharacterSheetEditFactory( val dex = FieldUio.create( initialLabel = getString(Res.string.character_sheet_edit__characteristics__dex), initialValue = sheet?.dexterity?.toString() ?: "", - valuePlaceHolder = { "11" } + valuePlaceHolder = { "10" } ) val con = FieldUio.create( initialLabel = getString(Res.string.character_sheet_edit__characteristics__con), initialValue = sheet?.constitution?.toString() ?: "", - valuePlaceHolder = { "15" } + valuePlaceHolder = { "10" } ) val hei = FieldUio.create( initialLabel = getString(Res.string.character_sheet_edit__characteristics__hei), initialValue = sheet?.height?.toString() ?: "", - valuePlaceHolder = { "13" } + valuePlaceHolder = { "10" } ) val int = FieldUio.create( initialLabel = getString(Res.string.character_sheet_edit__characteristics__int), initialValue = sheet?.intelligence?.toString() ?: "", - valuePlaceHolder = { "9" } + valuePlaceHolder = { "10" } ) val pow = FieldUio.create( initialLabel = getString(Res.string.character_sheet_edit__characteristics__pow), initialValue = sheet?.power?.toString() ?: "", - valuePlaceHolder = { "15" } + valuePlaceHolder = { "10" } ) val cha = FieldUio.create( initialLabel = getString(Res.string.character_sheet_edit__characteristics__cha), @@ -339,8 +340,8 @@ class CharacterSheetEditFactory( label = specialSkillsLabel, labelValue = skill.label, baseValue = skill.base, - bonusValue = skill.bonus.takeIf { it > 0 }?.toString() ?: "", - levelValue = skill.level.takeIf { it > 0 }?.toString() ?: "", + bonusValue = skill.bonus?.toString() ?: "", + levelValue = skill.level?.toString() ?: "", options = run { val current = sheet.specialSkills.firstOrNull { it.id == skill.id } listOf( @@ -356,8 +357,8 @@ class CharacterSheetEditFactory( label = magicSkillsLabel, labelValue = skill.label, baseValue = skill.base, - bonusValue = skill.bonus.takeIf { it > 0 }?.toString() ?: "", - levelValue = skill.level.takeIf { it > 0 }?.toString() ?: "", + bonusValue = skill.bonus?.toString() ?: "", + levelValue = skill.level?.toString() ?: "", options = run { val current = sheet.magicSkills.firstOrNull { it.id == skill.id } listOf( @@ -367,7 +368,20 @@ class CharacterSheetEditFactory( }, ) } ?: emptyList(), - actions = emptyList(), + actions = sheet?.actions?.map { action -> + ActionFieldUio( + id = action.id, + label = skillFactory.createWrapper( + label = getString(Res.string.character_sheet_edit__actions__name_label), + value = action.label, + ), + action = skillFactory.createWrapper( + label = getString(Res.string.character_sheet_edit__actions__action_label), + value = action.roll, + ), + option = skillFactory.deleteOption { onDeleteSkill(action.id) }, + ) + } ?: emptyList(), ) } @@ -385,17 +399,16 @@ class CharacterSheetEditFactory( base = base, bonus = skillFactory.createWrapper( label = getString(Res.string.character_sheet_edit__skills__bonus_label), - value = skill?.bonus?.takeIf { it > 0 }?.toString() ?: "", + value = skill?.bonus?.toString() ?: "", ), level = skillFactory.createWrapper( label = getString(Res.string.character_sheet_edit__skills__level_label), - value = skill?.level?.takeIf { it > 0 }?.toString() ?: "", + value = skill?.level?.toString() ?: "", ), option = skillFactory.occupationOption(skill?.occupation ?: false), ) } - private inline fun FieldUio.unpack(): T { val tmp = value.value.ifBlank { valuePlaceHolder.value } return when (T::class) { 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 50f6c06..a584de3 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 @@ -7,6 +7,7 @@ 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.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Icon @@ -32,6 +33,8 @@ import com.pixelized.desktop.lwa.LocalWindowController import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox import com.pixelized.desktop.lwa.navigation.screen.LocalScreenController import com.pixelized.desktop.lwa.navigation.window.LocalWindow +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.ActionField +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.ActionFieldUio import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.BaseSkillFieldUio import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.BaseSkillForm import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio @@ -74,7 +77,7 @@ data class CharacterSheetEditPageUio( val commonSkills: List, val specialSkills: List, val magicSkills: List, - val actions: List, + val actions: List, ) { val characteristics get() = listOf( @@ -340,12 +343,15 @@ fun CharacterSheetEdit( } } - form.actions.forEach { - Form( - modifier = Modifier.fillMaxWidth(), - valueWidth = 120.dp, - field = it, - ) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + form.actions.forEach { + ActionField( + modifier = Modifier.fillMaxWidth().padding(end = (4 + 2).dp), + action = it + ) + } } Row( 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 88f5470..7f46e1a 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 @@ -7,9 +7,11 @@ import androidx.lifecycle.ViewModel import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetEditDestination import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.screen.characterSheet.common.SkillFieldFactory -import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.ActionFieldUio import kotlinx.coroutines.runBlocking import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__action_label +import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__name_label import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__magic_title import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__special_title import org.jetbrains.compose.resources.getString @@ -73,10 +75,16 @@ class CharacterSheetEditViewModel( } suspend fun onNewAction() { - val field = FieldUio.create( - initialLabel = "", - isLabelEditable = true, - valuePlaceHolder = { "" }, + val id = UUID.randomUUID().toString() + val field = ActionFieldUio( + id = id, + label = skillFactory.createWrapper( + label = getString(Res.string.character_sheet_edit__actions__name_label), + ), + action = skillFactory.createWrapper( + label = getString(Res.string.character_sheet_edit__actions__action_label), + ), + option = skillFactory.deleteOption { deleteSkill(id) }, ) val actions = _characterSheet.value.actions.toMutableList().also { it.add(field) @@ -94,6 +102,9 @@ class CharacterSheetEditViewModel( magicSkills = _characterSheet.value.magicSkills.toMutableList().also { skills -> skills.removeIf { it.id == skillId } }, + actions = _characterSheet.value.actions.toMutableList().also { actions -> + actions.removeIf { it.id == skillId } + } ) } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/ActionField.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/ActionField.kt new file mode 100644 index 0000000..39ea6ab --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/ActionField.kt @@ -0,0 +1,77 @@ +package com.pixelized.desktop.lwa.screen.characterSheet.edit.composable + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.width +import androidx.compose.material.DropdownMenu +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.option.ActionOption +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.option.DropDownActionMenuItem +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.textfield.TextFieldWrapper +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.textfield.TextFieldWrapperUio + +@Stable +data class ActionFieldUio( + val id: String, + val label: TextFieldWrapperUio, + val action: TextFieldWrapperUio, + val option: ActionOption.DeleteOptionUio, +) + +@Composable +fun ActionField( + modifier: Modifier = Modifier, + action: ActionFieldUio, +) { + val showMenu = remember { mutableStateOf(false) } + + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(space = 4.dp, alignment = Alignment.End), + verticalAlignment = Alignment.CenterVertically, + ) { + TextFieldWrapper( + modifier = Modifier.weight(weight = 1f), + wrapper = action.label, + ) + TextFieldWrapper( + modifier = Modifier.width(width = (192+4).dp), + wrapper = action.action, + ) + Box { + IconButton( + onClick = { showMenu.value = showMenu.value.not() }, + ) { + Icon( + imageVector = Icons.Default.MoreVert, + tint = MaterialTheme.colors.primary, + contentDescription = null, + ) + } + DropdownMenu( + expanded = showMenu.value, + onDismissRequest = { showMenu.value = false } + ) { + DropDownActionMenuItem( + wrapper = action.option, + onClick = { + showMenu.value = false + action.option.onOption() + } + ) + } + } + } +} \ No newline at end of file