Add HP, initiative and CA to the characteur sheet.

This commit is contained in:
Thomas Andres Gomez 2023-09-12 17:14:45 +02:00
parent e99016ee9e
commit e639db219c
11 changed files with 284 additions and 111 deletions

View file

@ -3,16 +3,33 @@ package com.pixelized.rplexicon.facotry.displayable
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.ui.screens.character.CharacterSheetUio
import com.pixelized.rplexicon.ui.screens.character.composable.LabelPointUio
import com.pixelized.rplexicon.ui.screens.character.composable.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.SavingsThrowsUio
import com.pixelized.rplexicon.ui.screens.character.composable.StatUio
import com.pixelized.rplexicon.utilitary.extentions.modifier
import com.pixelized.rplexicon.utilitary.extentions.toLabel
import javax.inject.Inject
class ConvertCharacterSheetIntoDisplayableFactory @Inject constructor() {
fun toUio(sheet: CharacterSheet): CharacterSheetUio {
return CharacterSheetUio(
armorClass = LabelPointUio(
label = R.string.character_sheet_title_ca,
value = "${sheet.armorClass}",
max = null,
),
hitPoint = LabelPointUio(
label = R.string.character_sheet_title_hp,
value = "${sheet.hitPoint}",
max = "${sheet.maxHitPoint}",
),
initiative = LabelPointUio(
label = R.string.character_sheet_title_initiative,
value = sheet.dexterity.modifier.toLabel(),
max = null,
),
strength = StatUio(
label = R.string.character_sheet_stat_strength,
value = sheet.strength,

View file

@ -39,6 +39,7 @@ class CharacterSheetParser @Inject constructor() {
CharacterSheet(
name = name,
hitPoint = item.parse(HIT_POINT),
maxHitPoint = item.parse(MAX_HIT_POINT),
armorClass = item.parse(ARMOR_CLASS),
proficiency = item.parse(MASTERY),
strength = item.parse(STRENGTH),
@ -83,6 +84,7 @@ class CharacterSheetParser @Inject constructor() {
companion object {
private const val NAME = "Nom"
private const val HIT_POINT = "Point de vie"
private const val MAX_HIT_POINT = "Point de vie MAX"
private const val ARMOR_CLASS = "Classe d'armure"
private const val MASTERY = "Bonus de maîtrise"
private const val STRENGTH = "Force"
@ -120,6 +122,7 @@ class CharacterSheetParser @Inject constructor() {
get() = listOf(
NAME,
HIT_POINT,
MAX_HIT_POINT,
ARMOR_CLASS,
MASTERY,
STRENGTH,

View file

@ -8,6 +8,7 @@ class PropertyParser @Inject constructor() {
Property.PROFICIENCY.key -> Property.PROFICIENCY
Property.HIT_POINT.key -> Property.HIT_POINT
Property.ARMOR_CLASS.key -> Property.ARMOR_CLASS
Property.INITIATIVE.key -> Property.INITIATIVE
Property.STRENGTH.key -> Property.STRENGTH
Property.DEXTERITY.key -> Property.DEXTERITY
Property.CONSTITUTION.key -> Property.CONSTITUTION

View file

@ -3,6 +3,7 @@ package com.pixelized.rplexicon.model
data class CharacterSheet(
val name: String,
val hitPoint: Int, // Point de vie
val maxHitPoint: Int,
val armorClass: Int, // Classe d'armure
val proficiency: Int, // Bonus de maîtrise
val strength: Int, // Force

View file

@ -4,6 +4,7 @@ enum class Property(val key: String) {
PROFICIENCY("Maîtrise"),
HIT_POINT("Point de vie"),
ARMOR_CLASS("Classe d'armure"),
INITIATIVE("Initiative"),
STRENGTH("Force"),
DEXTERITY("Dextérité"),
CONSTITUTION("Constitution"),

View file

@ -2,6 +2,7 @@ package com.pixelized.rplexicon.ui.screens.character
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement
@ -58,6 +59,8 @@ import com.pixelized.rplexicon.ui.screens.character.composable.Action
import com.pixelized.rplexicon.ui.screens.character.composable.ActionsUio
import com.pixelized.rplexicon.ui.screens.character.composable.Counter
import com.pixelized.rplexicon.ui.screens.character.composable.CounterUio
import com.pixelized.rplexicon.ui.screens.character.composable.LabelPoint
import com.pixelized.rplexicon.ui.screens.character.composable.LabelPointUio
import com.pixelized.rplexicon.ui.screens.character.composable.Proficiency
import com.pixelized.rplexicon.ui.screens.character.composable.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.SavingsThrows
@ -70,6 +73,9 @@ import kotlinx.coroutines.launch
@Stable
data class CharacterSheetUio(
val armorClass: LabelPointUio,
val hitPoint: LabelPointUio,
val initiative: LabelPointUio,
val strength: StatUio,
val dexterity: StatUio,
val constitution: StatUio,
@ -135,6 +141,11 @@ fun CharacterSheetScreen(
onBack = {
screen.popBackStack()
},
onInitiative = {
val roll = viewModel.initiativeRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onStrength = {
val roll = viewModel.strengthRoll()
overlay.prepareRoll(roll = roll)
@ -317,6 +328,7 @@ private fun CharacterSheetContent(
counter: State<List<CounterUio>>,
alterations: State<List<String>>,
onBack: () -> Unit,
onInitiative: () -> Unit,
onStrength: () -> Unit,
onDexterity: () -> Unit,
onConstitution: () -> Unit,
@ -389,6 +401,30 @@ private fun CharacterSheetContent(
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(
16.dp,
Alignment.CenterHorizontally
),
) {
LabelPoint(
style = MaterialTheme.typography.displaySmall,
hp = sheet.armorClass,
)
LabelPoint(
hp = sheet.hitPoint,
)
LabelPoint(
style = MaterialTheme.typography.displaySmall,
hp = sheet.initiative,
onClick = onInitiative,
)
}
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
@ -434,40 +470,10 @@ private fun CharacterSheetContent(
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
Image(
modifier = Modifier.graphicsLayer { rotationY = 180f },
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
Text(
modifier = Modifier
.weight(weight = 1f, fill = false)
.padding(horizontal = 8.dp),
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(R.string.character_sheet_title_saving_throws),
)
Image(
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
}
SectionTitle(
modifier = Modifier.padding(top = 16.dp),
title = R.string.character_sheet_title_saving_throws,
)
Row(
modifier = Modifier.padding(horizontal = 16.dp),
@ -509,39 +515,10 @@ private fun CharacterSheetContent(
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
Image(
modifier = Modifier.graphicsLayer { rotationY = 180f },
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
Text(
modifier = Modifier
.weight(weight = 1f, fill = false)
.padding(horizontal = 8.dp),
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(R.string.character_sheet_title_proficiencies),
)
Image(
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
}
SectionTitle(
modifier = Modifier.padding(top = 16.dp),
title = R.string.character_sheet_title_proficiencies,
)
Column(
modifier = Modifier
@ -680,39 +657,10 @@ private fun CharacterSheetContent(
}
if (alterations.value.isNotEmpty()) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
Image(
modifier = Modifier.graphicsLayer { rotationY = 180f },
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
Text(
modifier = Modifier
.weight(weight = 1f, fill = false)
.padding(horizontal = 8.dp),
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(R.string.character_sheet_title_alteration),
)
Image(
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
}
SectionTitle(
modifier = Modifier.padding(top = 16.dp),
title = R.string.character_sheet_title_alteration,
)
Column(
modifier = Modifier
@ -749,6 +697,45 @@ private fun CharacterSheetContent(
}
}
@Composable
private fun SectionTitle(
modifier: Modifier = Modifier,
@StringRes title: Int
) {
Row(
modifier = modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
Image(
modifier = Modifier.graphicsLayer { rotationY = 180f },
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
Text(
modifier = Modifier
.weight(weight = 1f, fill = false)
.padding(horizontal = 8.dp),
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(title),
)
Image(
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, heightDp = 2000)
@ -757,6 +744,21 @@ private fun CharacterScreenPreview() {
LexiconTheme {
val bru = remember {
CharacterSheetUio(
armorClass = LabelPointUio(
label = R.string.character_sheet_title_ca,
value = "15",
max = null,
),
hitPoint = LabelPointUio(
label = R.string.character_sheet_title_hp,
value = "13",
max = "15",
),
initiative = LabelPointUio(
label = R.string.character_sheet_title_initiative,
value = "+2",
max = null,
),
strength = StatUio(
label = R.string.character_sheet_stat_strength,
value = 16,
@ -967,6 +969,7 @@ private fun CharacterScreenPreview() {
mutableStateOf(listOf("Rage", "Attaque téméraire"))
},
onBack = { },
onInitiative = { },
onStrength = { },
onDexterity = { },
onConstitution = { },

View file

@ -100,7 +100,9 @@ class CharacterSheetViewModel @Inject constructor(
}
launch {
alterationRepository.status.collect {
alterations.value = it[argument.name]?.map { name -> name } ?: emptyList()
alterations.value = it[argument.name]?.mapNotNull { name ->
name.takeIf { value -> value.isNotEmpty() }
} ?: emptyList()
}
}
}
@ -150,6 +152,14 @@ class CharacterSheetViewModel @Inject constructor(
)
}
fun initiativeRoll(): Roll = abilityRoll(
abilityRes = R.string.character_sheet_title_initiative,
relatedRes = R.string.character_sheet_stat_dexterity,
masteryLevel = null,
abilityValue = model.dexterity,
property = Property.INITIATIVE,
)
fun strengthRoll(): Roll = statRoll(
abilityRes = R.string.character_sheet_stat_strength,
abilityValue = model.strength,
@ -461,7 +471,7 @@ class CharacterSheetViewModel @Inject constructor(
@StringRes abilityRes: Int,
@StringRes relatedRes: Int,
abilityValue: Int,
masteryLevel: Int,
masteryLevel: Int?,
masteryValue: Int = model.proficiency,
property: Property,
): Roll {
@ -489,14 +499,17 @@ class CharacterSheetViewModel @Inject constructor(
fail = fail,
),
) + alterations.map { it.dices }.flatten(),
bonus = listOf(
Roll.Bonus(
title = context.getString(
if (masteryLevel == 2) R.string.dice_roll_mastery_expertise else R.string.dice_roll_mastery_proficiency,
ability
),
value = masteryValue * masteryLevel,
),
bonus = (masteryLevel?.let {
listOf(
Roll.Bonus(
title = context.getString(
if (masteryLevel == 2) R.string.dice_roll_mastery_expertise else R.string.dice_roll_mastery_proficiency,
ability
),
value = masteryValue * masteryLevel,
)
)
} ?: emptyList()) + listOf(
Roll.Bonus(
title = context.getString(R.string.dice_roll_bonus_detail, related),
value = abilityValue.modifier

View file

@ -0,0 +1,128 @@
package com.pixelized.rplexicon.ui.screens.character.composable
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@Stable
class LabelPointUio(
val label: Int?,
val value: String?,
val max: String?,
)
@Composable
fun LabelPoint(
modifier: Modifier = Modifier,
style: TextStyle = MaterialTheme.typography.displayMedium,
hp: LabelPointUio,
onClick: (() -> Unit)? = null
) {
Column(
modifier = modifier
.sizeIn(minWidth = 96.dp, minHeight = 80.dp)
.ddBorder(
horizontalSpacing = 4.dp,
verticalSpacing = 4.dp,
outline = remember {
CutCornerShape(
topStart = 16.dp,
topEnd = 16.dp,
bottomEnd = 38.dp,
bottomStart = 38.dp
)
},
inner = remember {
CutCornerShape(
topStart = 14.dp,
topEnd = 14.dp,
bottomEnd = 36.dp,
bottomStart = 36.dp
)
},
)
.clickable(enabled = onClick != null) { onClick?.let { it() } }
.padding(all = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
style = MaterialTheme.typography.labelSmall,
text = hp.label?.let { stringResource(id = it) } ?: ""
)
Text(
style = style,
text = hp.value ?: ""
)
Divider(
modifier = Modifier.width(width = 32.dp),
)
Text(
style = MaterialTheme.typography.labelSmall,
text = hp.max ?: ""
)
}
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun LabelPointPreview() {
LexiconTheme {
Surface {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally),
) {
LabelPoint(
style = MaterialTheme.typography.displaySmall,
hp = LabelPointUio(
label = R.string.character_sheet_title_hp,
value = "15",
max = null
),
)
LabelPoint(
hp = LabelPointUio(
label = R.string.character_sheet_title_ca,
value = "13",
max = "15"
),
)
LabelPoint(
style = MaterialTheme.typography.displaySmall,
hp = LabelPointUio(
label = R.string.character_sheet_title_initiative,
value = "+0",
max = null
),
)
}
}
}
}

View file

@ -30,7 +30,7 @@ class RollOverlayViewModel @Inject constructor(
private val _card = mutableStateOf<ThrowsCardUio?>(null)
val card: State<ThrowsCardUio?> get() = _card
private val _showDetail = mutableStateOf(false)
private val _showDetail = mutableStateOf(true)
val showDetail: State<Boolean> get() = _showDetail
fun prepareRoll(roll: Roll) {