From 95122d2c99f11854726161274f8bf3d3a872d4b0 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Fri, 29 Nov 2024 00:15:19 +0100 Subject: [PATCH] Add description to the character sheet skills. --- .../composeResources/values/strings.xml | 1 + .../com/pixelized/desktop/lwa/Module.kt | 2 + .../CharacterSheetJsonFactory.kt | 31 +++++++- .../characterSheet/CharacterSheetStore.kt | 20 ++++- .../characterSheet/SkillDescriptionFactory.kt | 47 +++++++++++ .../characterSheet/model/CharacterSheet.kt | 33 ++++---- .../model/CharacterSheetJsonV1.kt | 2 + .../detail/CharacterSheetFactory.kt | 68 ++++++---------- .../detail/CharacterSheetPage.kt | 77 +++++++++++-------- .../edit/CharacterSheetEditFactory.kt | 65 +++++++++------- .../edit/CharacterSheetEditPage.kt | 2 + .../edit/common/SkillFieldFactory.kt | 6 ++ .../edit/composable/SpecialSkillField.kt | 51 +++++++----- .../edit/composable/textfield/FormWrapper.kt | 11 ++- 14 files changed, 265 insertions(+), 151 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/SkillDescriptionFactory.kt diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 06d6a12..ce157a1 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -47,6 +47,7 @@ Ajouter une compétence spéciale Compétences magiques Ajouter une compétence magique + Description Base Bonus Niveau diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt index 8b333a7..274acf8 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt @@ -9,6 +9,7 @@ import com.pixelized.desktop.lwa.parser.arithmetic.ArithmeticParser import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetJsonFactory import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetStore +import com.pixelized.desktop.lwa.repository.characterSheet.SkillDescriptionFactory import com.pixelized.desktop.lwa.repository.network.NetworkRepository import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetFactory @@ -50,6 +51,7 @@ val factoryDependencies factoryOf(::CharacterSheetJsonFactory) factoryOf(::NetworkFactory) factoryOf(::SkillFieldFactory) + factoryOf(::SkillDescriptionFactory) } val viewModelDependencies diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetJsonFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetJsonFactory.kt index 16edad1..f6bde59 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetJsonFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetJsonFactory.kt @@ -5,11 +5,30 @@ 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.CharacterSheetJson import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheetJsonV1 +import kotlinx.coroutines.runBlocking +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__acrobatics +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__aid +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__athletics +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__bargain +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__combat +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__discretion +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__dodge +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__empathy +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__grab +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__intimidation +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__perception +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__persuasion +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__search +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__sleight_of_hand +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__spiel +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__throw +import org.jetbrains.compose.resources.getString import kotlin.math.ceil class CharacterSheetJsonFactory( private val bonusDamageUseCase: DamageBonusUseCase, - private val arithmeticParser: ArithmeticParser, + private val skillDescriptionFactory: SkillDescriptionFactory, ) { fun convertToJson( @@ -36,6 +55,7 @@ class CharacterSheetJsonFactory( CharacterSheetJsonV1.Skill( id = it.id, label = it.label, + description = null, base = it.base, bonus = it.bonus, level = it.level, @@ -47,6 +67,7 @@ class CharacterSheetJsonFactory( CharacterSheetJsonV1.Skill( id = it.id, label = it.label, + description = it.description, base = it.base, bonus = it.bonus, level = it.level, @@ -58,6 +79,7 @@ class CharacterSheetJsonFactory( CharacterSheetJsonV1.Skill( id = it.id, label = it.label, + description = it.description, base = it.base, bonus = it.bonus, level = it.level, @@ -76,7 +98,7 @@ class CharacterSheetJsonFactory( return json } - fun convertFromJson( + suspend fun convertFromJson( json: CharacterSheetJson, ): CharacterSheet { return when (json) { @@ -84,7 +106,7 @@ class CharacterSheetJsonFactory( } } - private fun convertFromV1( + private suspend fun convertFromV1( json: CharacterSheetJsonV1, ): CharacterSheet { val sheet = CharacterSheet( @@ -117,6 +139,7 @@ class CharacterSheetJsonFactory( CharacterSheet.Skill( id = it.id, label = it.label, + description = skillDescriptionFactory.baseSkillDescription(id = json.id), base = it.base, bonus = it.bonus, level = it.level, @@ -128,6 +151,7 @@ class CharacterSheetJsonFactory( CharacterSheet.Skill( id = it.id, label = it.label, + description = it.description, base = it.base, bonus = it.bonus, level = it.level, @@ -139,6 +163,7 @@ class CharacterSheetJsonFactory( CharacterSheet.Skill( id = it.id, label = it.label, + description = it.description, base = it.base, bonus = it.bonus, level = it.level, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetStore.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetStore.kt index 47d2dd6..a0e3986 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetStore.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetStore.kt @@ -3,8 +3,12 @@ package com.pixelized.desktop.lwa.repository.characterSheet import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheetJson import com.pixelized.desktop.lwa.repository.characterStorePath +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.io.File @@ -13,7 +17,15 @@ class CharacterSheetStore( private val factory: CharacterSheetJsonFactory, ) { private val characterDirectory = File(characterStorePath()).also { it.mkdirs() } - private val flow = MutableStateFlow(value = load()) + private val jsonFormatter: Json = Json { explicitNulls = false } + private val flow = MutableStateFlow>(value = emptyList()) + + init { + val scope = CoroutineScope(Dispatchers.IO + Job()) + scope.launch { + flow.value = load() + } + } fun characterSheetFlow(): StateFlow> = flow @@ -25,7 +37,7 @@ class CharacterSheetStore( fun save(sheet: CharacterSheet) { // convert the character sheet into json format. val json = try { - factory.convertToJson(sheet = sheet).let(Json::encodeToString) + factory.convertToJson(sheet = sheet).let(jsonFormatter::encodeToString) } catch (exception: Exception) { throw JsonConversionException(root = exception) } @@ -72,7 +84,7 @@ class CharacterSheetStore( FileReadException::class, JsonConversionException::class, ) - fun load(): List { + suspend fun load(): List { return characterDirectory .listFiles() ?.mapNotNull { file -> @@ -86,7 +98,7 @@ class CharacterSheetStore( return@mapNotNull null } try { - val sheet = Json.decodeFromString(json) + val sheet = jsonFormatter.decodeFromString(json) factory.convertFromJson(sheet) } catch (exception: Exception) { throw JsonConversionException(root = exception) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/SkillDescriptionFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/SkillDescriptionFactory.kt new file mode 100644 index 0000000..bc3fdf8 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/SkillDescriptionFactory.kt @@ -0,0 +1,47 @@ +package com.pixelized.desktop.lwa.repository.characterSheet + +import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__acrobatics +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__aid +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__athletics +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__bargain +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__combat +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__discretion +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__dodge +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__empathy +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__grab +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__intimidation +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__perception +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__persuasion +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__search +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__sleight_of_hand +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__spiel +import lwacharactersheet.composeapp.generated.resources.tooltip__skills__throw +import org.jetbrains.compose.resources.getString + +class SkillDescriptionFactory { + + suspend fun baseSkillDescription(id: String): String? { + return when (id) { + CharacterSheet.CommonSkillId.COMBAT_ID -> getString(Res.string.tooltip__skills__combat) + CharacterSheet.CommonSkillId.DODGE_ID -> getString(Res.string.tooltip__skills__dodge) + CharacterSheet.CommonSkillId.GRAB_ID -> getString(Res.string.tooltip__skills__grab) + CharacterSheet.CommonSkillId.THROW_ID -> getString(Res.string.tooltip__skills__throw) + CharacterSheet.CommonSkillId.ATHLETICS_ID -> getString(Res.string.tooltip__skills__athletics) + CharacterSheet.CommonSkillId.ACROBATICS_ID -> getString(Res.string.tooltip__skills__acrobatics) + CharacterSheet.CommonSkillId.PERCEPTION_ID -> getString(Res.string.tooltip__skills__perception) + CharacterSheet.CommonSkillId.SEARCH_ID -> getString(Res.string.tooltip__skills__search) + CharacterSheet.CommonSkillId.EMPATHY_ID -> getString(Res.string.tooltip__skills__empathy) + CharacterSheet.CommonSkillId.PERSUASION_ID -> getString(Res.string.tooltip__skills__persuasion) + CharacterSheet.CommonSkillId.INTIMIDATION_ID -> getString(Res.string.tooltip__skills__intimidation) + CharacterSheet.CommonSkillId.SPIEL_ID -> getString(Res.string.tooltip__skills__spiel) + CharacterSheet.CommonSkillId.BARGAIN_ID -> getString(Res.string.tooltip__skills__bargain) + CharacterSheet.CommonSkillId.DISCRETION_ID -> getString(Res.string.tooltip__skills__discretion) + CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID -> getString(Res.string.tooltip__skills__sleight_of_hand) + CharacterSheet.CommonSkillId.AID_ID -> getString(Res.string.tooltip__skills__aid) + else -> null + } + } +} + 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 2080ade..34a282a 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 @@ -34,6 +34,7 @@ data class CharacterSheet( data class Skill( val id: String, val label: String, + val description: String?, val base: String, val bonus: Int?, val level: Int?, @@ -48,21 +49,21 @@ data class CharacterSheet( ) object CommonSkillId { - const val COMBAT_ID = "Bagarre" - const val DODGE_ID = "Esquive" - const val GRAB_ID = "Saisie" - const val THROW_ID = "Lancer" - const val ATHLETICS_ID = "Athlétisme" - const val ACROBATICS_ID = "Acrobatie" - const val PERCEPTION_ID = "Perception" - const val SEARCH_ID = "Recherche" - const val EMPATHY_ID = "Empathie" - const val PERSUASION_ID = "Persuasion" - const val INTIMIDATION_ID = "Intimidation" - const val SPIEL_ID = "Baratin" - const val BARGAIN_ID = "Marchandage" - const val DISCRETION_ID = "Discrétion" - const val SLEIGHT_OF_HAND_ID = "Escamotage" - const val AID_ID = "Premiers soins" + const val COMBAT_ID = "COMBAT" + const val DODGE_ID = "DODGE" + const val GRAB_ID = "GRAB" + const val THROW_ID = "THROW" + const val ATHLETICS_ID = "ATHLETICS" + const val ACROBATICS_ID = "ACROBATICS" + const val PERCEPTION_ID = "PERCEPTION" + const val SEARCH_ID = "SEARCH" + const val EMPATHY_ID = "EMPATHY" + const val PERSUASION_ID = "PERSUASION" + const val INTIMIDATION_ID = "INTIMIDATION" + const val SPIEL_ID = "SPIEL" + const val BARGAIN_ID = "BARGAIN" + const val DISCRETION_ID = "DISCRETION" + const val SLEIGHT_OF_HAND_ID = "SLEIGHT_OF_HAND" + const val AID_ID = "AID" } } \ No newline at end of file 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 559625c..1ae8024 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 @@ -31,10 +31,12 @@ data class CharacterSheetJsonV1( // attack val rolls: List, ) : CharacterSheetJson { + @Serializable data class Skill( val id: String, val label: String, + val description: String?, val base: String, val bonus: Int?, val level: Int?, 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 fb9b02c..5b6b251 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 @@ -2,8 +2,8 @@ package com.pixelized.desktop.lwa.screen.characterSheet.detail import com.pixelized.desktop.lwa.business.SkillValueComputationUseCase import com.pixelized.desktop.lwa.composable.tooltip.TooltipUio +import com.pixelized.desktop.lwa.repository.characterSheet.SkillDescriptionFactory import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet -import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio.Node import lwacharactersheet.composeapp.generated.resources.Res @@ -26,22 +26,6 @@ import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__intelligence import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__power import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__strength -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__acrobatics -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__aid -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__athletics -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__bargain -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__combat -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__discretion -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__dodge -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__empathy -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__grab -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__intimidation -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__perception -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__persuasion -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__search -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__sleight_of_hand -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__spiel -import lwacharactersheet.composeapp.generated.resources.tooltip__skills__throw import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__armor import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__bonus_damage import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__hit_point @@ -51,6 +35,7 @@ import org.jetbrains.compose.resources.getString class CharacterSheetFactory( private val skillUseCase: SkillValueComputationUseCase, + private val skillDescriptionFactory: SkillDescriptionFactory, ) { companion object { const val HP = "HP" @@ -189,25 +174,6 @@ class CharacterSheetFactory( ), ), commonSkills = sheet.commonSkills.map { skill -> - val description = when (skill.id) { - CommonSkillId.COMBAT_ID -> getString(Res.string.tooltip__skills__combat) - CommonSkillId.DODGE_ID -> getString(Res.string.tooltip__skills__dodge) - CommonSkillId.GRAB_ID -> getString(Res.string.tooltip__skills__grab) - CommonSkillId.THROW_ID -> getString(Res.string.tooltip__skills__throw) - CommonSkillId.ATHLETICS_ID -> getString(Res.string.tooltip__skills__athletics) - CommonSkillId.ACROBATICS_ID -> getString(Res.string.tooltip__skills__acrobatics) - CommonSkillId.PERCEPTION_ID -> getString(Res.string.tooltip__skills__perception) - CommonSkillId.SEARCH_ID -> getString(Res.string.tooltip__skills__search) - CommonSkillId.EMPATHY_ID -> getString(Res.string.tooltip__skills__empathy) - CommonSkillId.PERSUASION_ID -> getString(Res.string.tooltip__skills__persuasion) - CommonSkillId.INTIMIDATION_ID -> getString(Res.string.tooltip__skills__intimidation) - CommonSkillId.SPIEL_ID -> getString(Res.string.tooltip__skills__spiel) - CommonSkillId.BARGAIN_ID -> getString(Res.string.tooltip__skills__bargain) - CommonSkillId.DISCRETION_ID -> getString(Res.string.tooltip__skills__discretion) - CommonSkillId.SLEIGHT_OF_HAND_ID -> getString(Res.string.tooltip__skills__sleight_of_hand) - CommonSkillId.AID_ID -> getString(Res.string.tooltip__skills__aid) - else -> null - } Node( label = skill.label, value = skillUseCase.computeSkillValue( @@ -215,7 +181,7 @@ class CharacterSheetFactory( skill = skill, diminished = diminished, ), - tooltips = description?.let { + tooltips = skillDescriptionFactory.baseSkillDescription(id = skill.id)?.let { TooltipUio( title = skill.label, description = it, @@ -224,26 +190,38 @@ class CharacterSheetFactory( used = skill.used, ) }, - specialSKills = sheet.specialSkills.map { + specialSKills = sheet.specialSkills.map { skill -> Node( - label = it.label, + label = skill.label, + tooltips = skill.description?.takeIf { it.isNotBlank() }?.let { description -> + TooltipUio( + title = skill.label, + description = description, + ) + }, value = skillUseCase.computeSkillValue( sheet = sheet, - skill = it, + skill = skill, diminished = diminished, ), - used = it.used, + used = skill.used, ) }, - magicsSkills = sheet.magicSkills.map { + magicsSkills = sheet.magicSkills.map { skill -> Node( - label = it.label, + label = skill.label, + tooltips = skill.description?.takeIf { it.isNotBlank() }?.let { description -> + TooltipUio( + title = skill.label, + description = description, + ) + }, value = skillUseCase.computeSkillValue( sheet = sheet, - skill = it, + skill = skill, diminished = diminished, ), - used = it.used, + used = skill.used, ) }, actions = sheet.actions.mapNotNull { 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 38de253..f201d64 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 @@ -1,6 +1,7 @@ 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 @@ -446,47 +447,55 @@ fun CharacterSheetPageContent( } } } - DecoratedBox( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp), + AnimatedVisibility( + visible = characterSheet.specialSKills.isNotEmpty() ) { - Column { - Text( - modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), - style = MaterialTheme.typography.caption, - textAlign = TextAlign.Center, - text = stringResource(Res.string.character_sheet__skills__special_title), - ) - characterSheet.specialSKills.forEach { occupation -> - Skill( - modifier = Modifier.cell(), - node = occupation, - onClick = { onSkill(occupation) }, - onUse = { onUseSkill(occupation) }, + DecoratedBox( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + ) { + Column { + Text( + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), + style = MaterialTheme.typography.caption, + textAlign = TextAlign.Center, + text = stringResource(Res.string.character_sheet__skills__special_title), ) + characterSheet.specialSKills.forEach { occupation -> + Skill( + modifier = Modifier.cell(), + node = occupation, + onClick = { onSkill(occupation) }, + onUse = { onUseSkill(occupation) }, + ) + } } } } - DecoratedBox( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp), + AnimatedVisibility( + visible = characterSheet.magicsSkills.isNotEmpty() ) { - Column { - Text( - modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), - style = MaterialTheme.typography.caption, - textAlign = TextAlign.Center, - text = stringResource(Res.string.character_sheet__skills__magic_title), - ) - characterSheet.magicsSkills.forEach { magic -> - Skill( - modifier = Modifier.cell(), - node = magic, - onClick = { onSkill(magic) }, - onUse = { onUseSkill(magic) }, + DecoratedBox( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + ) { + Column { + Text( + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), + style = MaterialTheme.typography.caption, + textAlign = TextAlign.Center, + text = stringResource(Res.string.character_sheet__skills__magic_title), ) + characterSheet.magicsSkills.forEach { magic -> + Skill( + modifier = Modifier.cell(), + node = magic, + onClick = { onSkill(magic) }, + onUse = { onUseSkill(magic) }, + ) + } } } } 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 3a584b9..3c5a4cb 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 @@ -5,6 +5,7 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.mutableStateOf import com.pixelized.desktop.lwa.business.DamageBonusUseCase import com.pixelized.desktop.lwa.business.SkillNormalizerUseCase +import com.pixelized.desktop.lwa.repository.characterSheet.SkillDescriptionFactory import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet import com.pixelized.desktop.lwa.screen.characterSheet.edit.common.SkillFieldFactory import com.pixelized.desktop.lwa.screen.characterSheet.edit.common.occupation @@ -57,10 +58,11 @@ import kotlin.math.min class CharacterSheetEditFactory( private val bonusDamageUseCase: DamageBonusUseCase, - private val skillFactory: SkillFieldFactory, + private val skillFieldFactory: SkillFieldFactory, + private val skillDescriptionFactory: SkillDescriptionFactory, private val normalizer: SkillNormalizerUseCase, ) { - fun updateCharacterSheet( + suspend fun updateCharacterSheet( currentSheet: CharacterSheet?, editedSheet: CharacterSheetEditPageUio, ): CharacterSheet { @@ -121,6 +123,7 @@ class CharacterSheetEditFactory( CharacterSheet.Skill( id = editedSkill.id, label = editedSkill.label, + description = skillDescriptionFactory.baseSkillDescription(editedSkill.id), base = "${editedSkill.base.value}", bonus = editedSkill.bonus.value.value.toIntOrNull(), level = editedSkill.level.value.value.toIntOrNull(), @@ -135,6 +138,7 @@ class CharacterSheetEditFactory( CharacterSheet.Skill( id = editedSkill.id, label = editedSkill.label.value.value, + description = editedSkill.description.value.value, base = editedSkill.base.value.value, bonus = editedSkill.bonus.value.value.toIntOrNull(), level = editedSkill.level.value.value.toIntOrNull(), @@ -149,6 +153,7 @@ class CharacterSheetEditFactory( CharacterSheet.Skill( id = editedSkill.id, label = editedSkill.label.value.value, + description = editedSkill.description.value.value, base = editedSkill.base.value.value, bonus = editedSkill.bonus.value.value.toIntOrNull(), level = editedSkill.level.value.value.toIntOrNull(), @@ -172,49 +177,49 @@ class CharacterSheetEditFactory( ): CharacterSheetEditPageUio { val str = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__characteristics__str), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = sheet?.strength?.toString() ?: "", placeholder = mutableStateOf("10") ), ) val dex = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__characteristics__dex), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = sheet?.dexterity?.toString() ?: "", placeholder = mutableStateOf("10"), ), ) val con = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__characteristics__con), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = sheet?.constitution?.toString() ?: "", placeholder = mutableStateOf("10"), ) ) val hei = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__characteristics__hei), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = sheet?.height?.toString() ?: "", placeholder = mutableStateOf("10"), ) ) val int = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__characteristics__int), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = sheet?.intelligence?.toString() ?: "", placeholder = mutableStateOf("10"), ) ) val pow = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__characteristics__pow), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = sheet?.power?.toString() ?: "", placeholder = mutableStateOf("10"), ) ) val cha = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__characteristics__cha), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = sheet?.charisma?.toString() ?: "", placeholder = mutableStateOf("10"), ) @@ -233,14 +238,14 @@ class CharacterSheetEditFactory( val maxHitPoint = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__sub_characteristics__max_hit_point), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = if (sheet?.overrideMaxHp == true) "${sheet.maxHp}" else "", placeholder = derivedStateOf { "${ceil((con() + hei()) / 2f).toInt()}" }, ) ) val maxPowerPoint = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__sub_characteristics__max_power_point), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = if (sheet?.overrideMaxPP == true) "${sheet.maxPp}" else "", placeholder = derivedStateOf { "${pow()}" }, ) @@ -248,7 +253,7 @@ class CharacterSheetEditFactory( return CharacterSheetEditPageUio( id = sheet?.id ?: UUID.randomUUID().toString(), - name = skillFactory.createWrapper( + name = skillFieldFactory.createWrapper( label = mutableStateOf(getString(Res.string.character_sheet_edit__name_label)), value = sheet?.name ?: "" ), @@ -261,7 +266,7 @@ class CharacterSheetEditFactory( charisma = cha, movement = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__sub_characteristics__movement), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = if (sheet?.overrideMovement == true) "${sheet.movement}" else "", placeholder = mutableStateOf("10"), ) @@ -269,7 +274,7 @@ class CharacterSheetEditFactory( maxHp = maxHitPoint, currentHp = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__sub_characteristics__hit_point), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( enable = false, placeholder = derivedStateOf { val min = min( @@ -283,7 +288,7 @@ class CharacterSheetEditFactory( maxPp = maxPowerPoint, currentPp = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__sub_characteristics__power_point), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( enable = false, placeholder = derivedStateOf { val min = min( @@ -296,7 +301,7 @@ class CharacterSheetEditFactory( ), damageBonus = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__sub_characteristics__damage_bonus), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = if (sheet?.overrideDamageBonus == true) sheet.damageBonus else "", placeholder = derivedStateOf { bonusDamageUseCase.bonusDamage( @@ -308,7 +313,7 @@ class CharacterSheetEditFactory( ), armor = SimpleFieldUio( label = getString(Res.string.character_sheet_edit__sub_characteristics__armor), - value = skillFactory.createWrapper( + value = skillFieldFactory.createWrapper( value = if (sheet?.overrideArmor == true) "${sheet.armor}" else "", placeholder = mutableStateOf("0"), ) @@ -412,9 +417,10 @@ class CharacterSheetEditFactory( ), ), specialSkills = sheet?.specialSkills?.map { skill -> - skillFactory.createSkill( + skillFieldFactory.createSkill( id = skill.id, label = specialSkillsLabel, + descriptionValue = skill.description ?: "", labelValue = skill.label, baseValue = skill.base, bonusValue = skill.bonus?.toString() ?: "", @@ -422,16 +428,17 @@ class CharacterSheetEditFactory( options = run { val current = sheet.specialSkills.firstOrNull { it.id == skill.id } listOf( - skillFactory.occupationOption(checked = current?.occupation ?: false), - skillFactory.deleteOption { onDeleteSkill(skill.id) }, + skillFieldFactory.occupationOption(checked = current?.occupation ?: false), + skillFieldFactory.deleteOption { onDeleteSkill(skill.id) }, ) }, ) } ?: emptyList(), magicSkills = sheet?.magicSkills?.map { skill -> - skillFactory.createSkill( + skillFieldFactory.createSkill( id = skill.id, label = magicSkillsLabel, + descriptionValue = skill.description ?: "", labelValue = skill.label, baseValue = skill.base, bonusValue = skill.bonus?.toString() ?: "", @@ -439,8 +446,8 @@ class CharacterSheetEditFactory( options = run { val current = sheet.magicSkills.firstOrNull { it.id == skill.id } listOf( - skillFactory.occupationOption(checked = current?.occupation ?: false), - skillFactory.deleteOption { onDeleteSkill(skill.id) }, + skillFieldFactory.occupationOption(checked = current?.occupation ?: false), + skillFieldFactory.deleteOption { onDeleteSkill(skill.id) }, ) }, ) @@ -448,15 +455,15 @@ class CharacterSheetEditFactory( actions = sheet?.actions?.map { action -> ActionFieldUio( id = action.id, - label = skillFactory.createWrapper( + label = skillFieldFactory.createWrapper( label = mutableStateOf(getString(Res.string.character_sheet_edit__actions__name_label)), value = action.label, ), - action = skillFactory.createWrapper( + action = skillFieldFactory.createWrapper( label = mutableStateOf(getString(Res.string.character_sheet_edit__actions__action_label)), value = action.roll, ), - option = skillFactory.deleteOption { onDeleteSkill(action.id) }, + option = skillFieldFactory.deleteOption { onDeleteSkill(action.id) }, ) } ?: emptyList(), ) @@ -474,15 +481,15 @@ class CharacterSheetEditFactory( id = id, label = label, base = base, - bonus = skillFactory.createWrapper( + bonus = skillFieldFactory.createWrapper( label = mutableStateOf(getString(Res.string.character_sheet_edit__skills__bonus_label)), value = skill?.bonus?.toString() ?: "", ), - level = skillFactory.createWrapper( + level = skillFieldFactory.createWrapper( label = mutableStateOf(getString(Res.string.character_sheet_edit__skills__level_label)), value = skill?.level?.toString() ?: "", ), - option = skillFactory.occupationOption(skill?.occupation ?: false), + option = skillFieldFactory.occupationOption(skill?.occupation ?: false), ) } 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 86a84c0..98c50f6 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 @@ -273,6 +273,7 @@ fun CharacterSheetEdit( Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(space = 16.dp), ) { Text( modifier = Modifier.padding(vertical = 8.dp), @@ -319,6 +320,7 @@ fun CharacterSheetEdit( Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(space = 16.dp), ) { Text( modifier = Modifier.padding(vertical = 8.dp), diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/common/SkillFieldFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/common/SkillFieldFactory.kt index 0d8b8c3..5efc012 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/common/SkillFieldFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/common/SkillFieldFactory.kt @@ -15,6 +15,7 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__de import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__occupation__label import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__base_label import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__bonus_label +import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__description_label import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__level_label import org.jetbrains.compose.resources.getString import java.util.UUID @@ -25,6 +26,7 @@ class SkillFieldFactory { id: String = UUID.randomUUID().toString(), label: String, labelValue: String = "", + descriptionValue: String = "", baseValue: String = "", bonusValue: String = "", levelValue: String = "", @@ -37,6 +39,10 @@ class SkillFieldFactory { label = mutableStateOf(label), value = labelValue, ), + description = createWrapper( + label = mutableStateOf(getString(Res.string.character_sheet_edit__skills__description_label)), + value = descriptionValue, + ), base = createWrapper( label = derivedStateOf { options diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/SpecialSkillField.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/SpecialSkillField.kt index 7fb3abd..a63a756 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/SpecialSkillField.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/SpecialSkillField.kt @@ -2,7 +2,9 @@ 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.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.width import androidx.compose.material.DropdownMenu import androidx.compose.material.Icon @@ -14,7 +16,6 @@ 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.DropDownMenuItemWrapper @@ -26,6 +27,7 @@ import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.textfield class SkillFieldUio( val id: String, val label: TextFieldWrapperUio, + val description: TextFieldWrapperUio, val base: TextFieldWrapperUio, val bonus: TextFieldWrapperUio, val level: TextFieldWrapperUio, @@ -41,25 +43,38 @@ fun SkillForm( Row( modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(space = 4.dp, alignment = Alignment.End), - verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = 4.dp), ) { - TextFieldWrapper( + Column( modifier = Modifier.weight(1f), - wrapper = field.label, - ) - TextFieldWrapper( - modifier = Modifier.width(width = 96.dp), - wrapper = field.base, - ) - TextFieldWrapper( - modifier = Modifier.width(width = 96.dp), - wrapper = field.bonus, - ) - TextFieldWrapper( - modifier = Modifier.width(width = 96.dp), - wrapper = field.level, - ) + verticalArrangement = Arrangement.spacedBy(space = 4.dp) + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(space = 4.dp), + ) { + TextFieldWrapper( + modifier = Modifier.weight(1f), + wrapper = field.label, + ) + TextFieldWrapper( + modifier = Modifier.width(width = 96.dp), + wrapper = field.base, + ) + TextFieldWrapper( + modifier = Modifier.width(width = 96.dp), + wrapper = field.bonus, + ) + TextFieldWrapper( + modifier = Modifier.width(width = 96.dp), + wrapper = field.level, + ) + } + TextFieldWrapper( + modifier = Modifier.fillMaxWidth(), + singleLine = false, + wrapper = field.description, + ) + } if (field.options.isNotEmpty()) { Box { IconButton( diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/textfield/FormWrapper.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/textfield/FormWrapper.kt index b869d44..e55f2f1 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/textfield/FormWrapper.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/textfield/FormWrapper.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.pixelized.desktop.lwa.utils.rememberKeyboardActions +import kotlin.math.sin @Stable data class TextFieldWrapperUio( @@ -28,13 +29,19 @@ data class TextFieldWrapperUio( @Composable fun TextFieldWrapper( modifier: Modifier = Modifier, + singleLine: Boolean = true, wrapper: TextFieldWrapperUio, ) { val colorScheme = MaterialTheme.colors val focus = LocalFocusManager.current + val localModifier = if (singleLine) { + Modifier.height(height = 56.dp) + } else { + Modifier + } TextField( - modifier = Modifier.height(height = 56.dp).then(other = modifier), + modifier = localModifier.then(other = modifier), colors = TextFieldDefaults.textFieldColors( backgroundColor = remember(wrapper.enable) { when (wrapper.enable) { @@ -47,7 +54,7 @@ fun TextFieldWrapper( focus.moveFocus(FocusDirection.Next) }, enabled = wrapper.enable, - singleLine = true, + singleLine = singleLine, placeholder = wrapper.placeholder.value?.let { { Text(