Allow negative value into the character sheet bonuses.

This commit is contained in:
Thomas Andres Gomez 2024-11-27 15:39:22 +01:00
parent 8d93d46cce
commit d93ffd9499
8 changed files with 154 additions and 45 deletions

View file

@ -63,6 +63,8 @@
<string name="character_sheet_edit__skills__aid">Premiers soins</string> <string name="character_sheet_edit__skills__aid">Premiers soins</string>
<string name="character_sheet_edit__occupation__title">Occupations</string> <string name="character_sheet_edit__occupation__title">Occupations</string>
<string name="character_sheet_edit__occupation__add_action">Ajouter une occupation</string> <string name="character_sheet_edit__occupation__add_action">Ajouter une occupation</string>
<string name="character_sheet_edit__actions__name_label">Nom</string>
<string name="character_sheet_edit__actions__action_label">Bonus</string>
<string name="character_sheet_edit__magic__title">Compétences magiques</string> <string name="character_sheet_edit__magic__title">Compétences magiques</string>
<string name="character_sheet_edit__magic__add_action">Ajouter une compétence magique</string> <string name="character_sheet_edit__magic__add_action">Ajouter une compétence magique</string>
<string name="character_sheet_edit__delete__label">Supprimer</string> <string name="character_sheet_edit__delete__label">Supprimer</string>

View file

@ -43,7 +43,7 @@ class SkillValueComputationUseCase(
baseSum baseSum
} }
return max(base + skill.bonus + skill.level - diminished, 0) return max(base + (skill.bonus ?: 0) + (skill.level ?: 0) - diminished, 0)
} }
fun computeRoll( fun computeRoll(
@ -83,8 +83,8 @@ class SkillValueComputationUseCase(
if (damageBonusInstructions is Instruction.Dice) { if (damageBonusInstructions is Instruction.Dice) {
RollUseCase.roll( RollUseCase.roll(
quantity = damageBonusInstructions.quantity, quantity = damageBonusInstructions.quantity,
faces = damageBonusInstructions.faces faces = damageBonusInstructions.faces / 2,
) / 2 )
} else { } else {
0 0
} }

View file

@ -35,8 +35,8 @@ data class CharacterSheet(
val id: String, val id: String,
val label: String, val label: String,
val base: String, val base: String,
val bonus: Int, val bonus: Int?,
val level: Int, val level: Int?,
val occupation: Boolean, val occupation: Boolean,
val used: Boolean, val used: Boolean,
) )

View file

@ -36,8 +36,8 @@ data class CharacterSheetJsonV1(
val id: String, val id: String,
val label: String, val label: String,
val base: String, val base: String,
val bonus: Int, val bonus: Int?,
val level: Int, val level: Int?,
val occupation: Boolean, val occupation: Boolean,
val used: Boolean, val used: Boolean,
) )

View file

@ -4,13 +4,15 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import com.pixelized.desktop.lwa.business.DamageBonusUseCase import com.pixelized.desktop.lwa.business.DamageBonusUseCase
import com.pixelized.desktop.lwa.business.SkillNormalizerUseCase.normalize 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.repository.characterSheet.model.CharacterSheet
import com.pixelized.desktop.lwa.screen.characterSheet.common.SkillFieldFactory 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.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.BaseSkillFieldUio
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio
import lwacharactersheet.composeapp.generated.resources.Res 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__cha
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__con import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__con
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__dex import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__dex
@ -53,7 +55,6 @@ import kotlin.math.min
class CharacterSheetEditFactory( class CharacterSheetEditFactory(
private val bonusDamageUseCase: DamageBonusUseCase, private val bonusDamageUseCase: DamageBonusUseCase,
private val skillFactory: SkillFieldFactory, private val skillFactory: SkillFieldFactory,
private val parser: ArithmeticParser,
) { ) {
fun updateCharacterSheet( fun updateCharacterSheet(
currentSheet: CharacterSheet?, currentSheet: CharacterSheet?,
@ -92,9 +93,9 @@ class CharacterSheetEditFactory(
CharacterSheet.Skill( CharacterSheet.Skill(
id = editedSkill.id, id = editedSkill.id,
label = editedSkill.label, label = editedSkill.label,
base = "${editedSkill.base}", base = "${editedSkill.base.value}",
bonus = editedSkill.bonus.value.value.toIntOrNull() ?: 0, bonus = editedSkill.bonus.value.value.toIntOrNull(),
level = editedSkill.level.value.value.toIntOrNull() ?: 0, level = editedSkill.level.value.value.toIntOrNull(),
occupation = editedSkill.option.checked.value, occupation = editedSkill.option.checked.value,
used = currentSkill?.used ?: false, used = currentSkill?.used ?: false,
) )
@ -107,8 +108,8 @@ class CharacterSheetEditFactory(
id = editedSkill.id, id = editedSkill.id,
label = editedSkill.label.value.value, label = editedSkill.label.value.value,
base = editedSkill.base.value.value, base = editedSkill.base.value.value,
bonus = editedSkill.bonus.value.value.toIntOrNull() ?: 0, bonus = editedSkill.bonus.value.value.toIntOrNull(),
level = editedSkill.level.value.value.toIntOrNull() ?: 0, level = editedSkill.level.value.value.toIntOrNull(),
occupation = editedSkill.options.occupation, occupation = editedSkill.options.occupation,
used = currentSkill?.used ?: false, used = currentSkill?.used ?: false,
) )
@ -121,17 +122,17 @@ class CharacterSheetEditFactory(
id = editedSkill.id, id = editedSkill.id,
label = editedSkill.label.value.value, label = editedSkill.label.value.value,
base = editedSkill.base.value.value, base = editedSkill.base.value.value,
bonus = editedSkill.bonus.value.value.toIntOrNull() ?: 0, bonus = editedSkill.bonus.value.value.toIntOrNull(),
level = editedSkill.level.value.value.toIntOrNull() ?: 0, level = editedSkill.level.value.value.toIntOrNull(),
occupation = editedSkill.options.occupation, occupation = editedSkill.options.occupation,
used = currentSkill?.used ?: false, used = currentSkill?.used ?: false,
) )
}, },
actions = editedSheet.actions.map { actions = editedSheet.actions.map {
CharacterSheet.Roll( CharacterSheet.Roll(
id = "", // TODO id = it.id,
label = it.label.value, label = it.label.value.value,
roll = it.unpack(), roll = it.action.value.value,
) )
}, },
) )
@ -139,7 +140,7 @@ class CharacterSheetEditFactory(
suspend fun convertToUio( suspend fun convertToUio(
sheet: CharacterSheet?, sheet: CharacterSheet?,
onDeleteSkill: (skillId: String) -> Unit, onDeleteSkill: (id: String) -> Unit,
): CharacterSheetEditPageUio { ): CharacterSheetEditPageUio {
val str = FieldUio.create( val str = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__str), initialLabel = getString(Res.string.character_sheet_edit__characteristics__str),
@ -149,27 +150,27 @@ class CharacterSheetEditFactory(
val dex = FieldUio.create( val dex = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__dex), initialLabel = getString(Res.string.character_sheet_edit__characteristics__dex),
initialValue = sheet?.dexterity?.toString() ?: "", initialValue = sheet?.dexterity?.toString() ?: "",
valuePlaceHolder = { "11" } valuePlaceHolder = { "10" }
) )
val con = FieldUio.create( val con = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__con), initialLabel = getString(Res.string.character_sheet_edit__characteristics__con),
initialValue = sheet?.constitution?.toString() ?: "", initialValue = sheet?.constitution?.toString() ?: "",
valuePlaceHolder = { "15" } valuePlaceHolder = { "10" }
) )
val hei = FieldUio.create( val hei = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__hei), initialLabel = getString(Res.string.character_sheet_edit__characteristics__hei),
initialValue = sheet?.height?.toString() ?: "", initialValue = sheet?.height?.toString() ?: "",
valuePlaceHolder = { "13" } valuePlaceHolder = { "10" }
) )
val int = FieldUio.create( val int = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__int), initialLabel = getString(Res.string.character_sheet_edit__characteristics__int),
initialValue = sheet?.intelligence?.toString() ?: "", initialValue = sheet?.intelligence?.toString() ?: "",
valuePlaceHolder = { "9" } valuePlaceHolder = { "10" }
) )
val pow = FieldUio.create( val pow = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__pow), initialLabel = getString(Res.string.character_sheet_edit__characteristics__pow),
initialValue = sheet?.power?.toString() ?: "", initialValue = sheet?.power?.toString() ?: "",
valuePlaceHolder = { "15" } valuePlaceHolder = { "10" }
) )
val cha = FieldUio.create( val cha = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__cha), initialLabel = getString(Res.string.character_sheet_edit__characteristics__cha),
@ -339,8 +340,8 @@ class CharacterSheetEditFactory(
label = specialSkillsLabel, label = specialSkillsLabel,
labelValue = skill.label, labelValue = skill.label,
baseValue = skill.base, baseValue = skill.base,
bonusValue = skill.bonus.takeIf { it > 0 }?.toString() ?: "", bonusValue = skill.bonus?.toString() ?: "",
levelValue = skill.level.takeIf { it > 0 }?.toString() ?: "", levelValue = skill.level?.toString() ?: "",
options = run { options = run {
val current = sheet.specialSkills.firstOrNull { it.id == skill.id } val current = sheet.specialSkills.firstOrNull { it.id == skill.id }
listOf( listOf(
@ -356,8 +357,8 @@ class CharacterSheetEditFactory(
label = magicSkillsLabel, label = magicSkillsLabel,
labelValue = skill.label, labelValue = skill.label,
baseValue = skill.base, baseValue = skill.base,
bonusValue = skill.bonus.takeIf { it > 0 }?.toString() ?: "", bonusValue = skill.bonus?.toString() ?: "",
levelValue = skill.level.takeIf { it > 0 }?.toString() ?: "", levelValue = skill.level?.toString() ?: "",
options = run { options = run {
val current = sheet.magicSkills.firstOrNull { it.id == skill.id } val current = sheet.magicSkills.firstOrNull { it.id == skill.id }
listOf( listOf(
@ -367,7 +368,20 @@ class CharacterSheetEditFactory(
}, },
) )
} ?: emptyList(), } ?: 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, base = base,
bonus = skillFactory.createWrapper( bonus = skillFactory.createWrapper(
label = getString(Res.string.character_sheet_edit__skills__bonus_label), label = getString(Res.string.character_sheet_edit__skills__bonus_label),
value = skill?.bonus?.takeIf { it > 0 }?.toString() ?: "", value = skill?.bonus?.toString() ?: "",
), ),
level = skillFactory.createWrapper( level = skillFactory.createWrapper(
label = getString(Res.string.character_sheet_edit__skills__level_label), 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), option = skillFactory.occupationOption(skill?.occupation ?: false),
) )
} }
private inline fun <reified T> FieldUio.unpack(): T { private inline fun <reified T> FieldUio.unpack(): T {
val tmp = value.value.ifBlank { valuePlaceHolder.value } val tmp = value.value.ifBlank { valuePlaceHolder.value }
return when (T::class) { return when (T::class) {

View file

@ -7,6 +7,7 @@ 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.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Icon 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.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.navigation.screen.LocalScreenController import com.pixelized.desktop.lwa.navigation.screen.LocalScreenController
import com.pixelized.desktop.lwa.navigation.window.LocalWindow 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.BaseSkillFieldUio
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.BaseSkillForm import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.BaseSkillForm
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio
@ -74,7 +77,7 @@ data class CharacterSheetEditPageUio(
val commonSkills: List<BaseSkillFieldUio>, val commonSkills: List<BaseSkillFieldUio>,
val specialSkills: List<SkillFieldUio>, val specialSkills: List<SkillFieldUio>,
val magicSkills: List<SkillFieldUio>, val magicSkills: List<SkillFieldUio>,
val actions: List<FieldUio>, val actions: List<ActionFieldUio>,
) { ) {
val characteristics val characteristics
get() = listOf( get() = listOf(
@ -340,12 +343,15 @@ fun CharacterSheetEdit(
} }
} }
form.actions.forEach { Column(
Form( horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth(), ) {
valueWidth = 120.dp, form.actions.forEach {
field = it, ActionField(
) modifier = Modifier.fillMaxWidth().padding(end = (4 + 2).dp),
action = it
)
}
} }
Row( Row(

View file

@ -7,9 +7,11 @@ import androidx.lifecycle.ViewModel
import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetEditDestination import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetEditDestination
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.screen.characterSheet.common.SkillFieldFactory 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 kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res 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__magic_title
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__special_title import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__special_title
import org.jetbrains.compose.resources.getString import org.jetbrains.compose.resources.getString
@ -73,10 +75,16 @@ class CharacterSheetEditViewModel(
} }
suspend fun onNewAction() { suspend fun onNewAction() {
val field = FieldUio.create( val id = UUID.randomUUID().toString()
initialLabel = "", val field = ActionFieldUio(
isLabelEditable = true, id = id,
valuePlaceHolder = { "" }, 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 { val actions = _characterSheet.value.actions.toMutableList().also {
it.add(field) it.add(field)
@ -94,6 +102,9 @@ class CharacterSheetEditViewModel(
magicSkills = _characterSheet.value.magicSkills.toMutableList().also { skills -> magicSkills = _characterSheet.value.magicSkills.toMutableList().also { skills ->
skills.removeIf { it.id == skillId } skills.removeIf { it.id == skillId }
}, },
actions = _characterSheet.value.actions.toMutableList().also { actions ->
actions.removeIf { it.id == skillId }
}
) )
} }

View file

@ -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()
}
)
}
}
}
}