Add description to the character sheet skills.

This commit is contained in:
Thomas Andres Gomez 2024-11-29 00:15:19 +01:00
parent 4606122264
commit 95122d2c99
14 changed files with 265 additions and 151 deletions

View file

@ -47,6 +47,7 @@
<string name="character_sheet_edit__skills__special_action">Ajouter une compétence spéciale</string>
<string name="character_sheet_edit__skills__magic_title">Compétences magiques</string>
<string name="character_sheet_edit__skills__magic_action">Ajouter une compétence magique</string>
<string name="character_sheet_edit__skills__description_label">Description</string>
<string name="character_sheet_edit__skills__base_label">Base</string>
<string name="character_sheet_edit__skills__bonus_label">Bonus</string>
<string name="character_sheet_edit__skills__level_label">Niveau</string>

View file

@ -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

View file

@ -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,

View file

@ -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<List<CharacterSheet>>(value = emptyList())
init {
val scope = CoroutineScope(Dispatchers.IO + Job())
scope.launch {
flow.value = load()
}
}
fun characterSheetFlow(): StateFlow<List<CharacterSheet>> = 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<CharacterSheet> {
suspend fun load(): List<CharacterSheet> {
return characterDirectory
.listFiles()
?.mapNotNull { file ->
@ -86,7 +98,7 @@ class CharacterSheetStore(
return@mapNotNull null
}
try {
val sheet = Json.decodeFromString<CharacterSheetJson>(json)
val sheet = jsonFormatter.decodeFromString<CharacterSheetJson>(json)
factory.convertFromJson(sheet)
} catch (exception: Exception) {
throw JsonConversionException(root = exception)

View file

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

View file

@ -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"
}
}

View file

@ -31,10 +31,12 @@ data class CharacterSheetJsonV1(
// attack
val rolls: List<Roll>,
) : CharacterSheetJson {
@Serializable
data class Skill(
val id: String,
val label: String,
val description: String?,
val base: String,
val bonus: Int?,
val level: Int?,

View file

@ -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 {

View file

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

View file

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

View file

@ -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),

View file

@ -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

View file

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

View file

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