GameMaster summary screen

This commit is contained in:
Thomas Andres Gomez 2023-12-19 15:27:58 +01:00
parent 805bdc65bc
commit ff105e2f81
35 changed files with 2770 additions and 74 deletions

View file

@ -1,5 +1,6 @@
package com.pixelized.rplexicon.data.model
import androidx.annotation.DrawableRes
import com.pixelized.rplexicon.R
data class CharacterSheet(
@ -61,30 +62,34 @@ data class CharacterSheet(
val value: String,
val resource: String? = null,
val label: Int? = null,
@DrawableRes val icon: Int,
) {
BARBARIAN(
value = "Barbare",
resource = "Rage",
label = R.string.character_sheet_title_rage
label = R.string.character_sheet_title_rage,
icon = R.drawable.ic_class_barbarian_24
),
BARD(
value = "Barde",
resource = "Inspiration bardique",
label = R.string.character_sheet_title_inspiration
label = R.string.character_sheet_title_inspiration,
icon = R.drawable.ic_class_bard_24
),
CLERIC(
value = "Clerc",
resource = "Conduit divin",
label = R.string.character_sheet_title_conduit
label = R.string.character_sheet_title_conduit,
icon = R.drawable.ic_class_cleric_24
),
DRUID(value = "Druide"),
FIGHTER(value = "Guerrier"),
MONK(value = "Moine"),
PALADIN(value = "Paladin"),
RANGER(value = "Rodeur"),
ROGUE(value = "Roublard"),
SORCERER(value = "Ensorceleur"),
WARLOCK(value = "Occultiste"),
WIZARD(value = "Magicien"),
DRUID(value = "Druide", icon = R.drawable.ic_class_druid_24),
FIGHTER(value = "Guerrier", icon = R.drawable.ic_class_fighter_24),
MONK(value = "Moine", icon = R.drawable.ic_class_monk_24),
PALADIN(value = "Paladin", icon = R.drawable.ic_class_paladin_24),
RANGER(value = "Rodeur", icon = R.drawable.ic_class_ranger_24),
ROGUE(value = "Roublard", icon = R.drawable.ic_class_rogue_24),
SORCERER(value = "Ensorceleur", icon = R.drawable.ic_class_sorcerer_24),
WARLOCK(value = "Occultiste", icon = R.drawable.ic_class_warlock_24),
WIZARD(value = "Magicien", icon = R.drawable.ic_class_wizard_24),
}
}

View file

@ -0,0 +1,17 @@
package com.pixelized.rplexicon.data.model
import androidx.annotation.Keep
import com.google.firebase.database.IgnoreExtraProperties
import com.google.firebase.database.PropertyName
@Keep
@IgnoreExtraProperties
data class CharacterSheetFireMap(
@get:PropertyName(CHARACTERS)
@set:PropertyName(CHARACTERS)
var characters: Map<String, CharacterSheetFire> = emptyMap(),
) {
companion object {
private const val CHARACTERS = "Characters"
}
}

View file

@ -9,6 +9,7 @@ import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.data.model.CharacterSheetFire
import com.pixelized.rplexicon.data.model.CharacterSheetFireMap
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@ -33,6 +34,36 @@ class FirebaseRepository @Inject constructor(
Firebase.database.setPersistenceEnabled(true)
}
fun getCharacter(): Flow<CharacterSheetFireMap> {
return callbackFlow {
// reference to the node
val reference = database.getReference("/")
// build a register the callback
val listener = reference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val value = try {
dataSnapshot.getValue(CharacterSheetFireMap::class.java)
} catch (exception: Exception) {
Log.e(TAG, "Failed to parse value.", exception)
_error.tryEmit(exception)
null
}
if (value != null) {
trySend(value)
}
}
override fun onCancelled(error: DatabaseError) {
Log.e(TAG, "Failed to read value.", error.toException())
cancel()
}
})
awaitClose {
reference.removeEventListener(listener)
}
}
}
fun getCharacter(character: String): Flow<CharacterSheetFire> {
return callbackFlow {
// reference to the node

View file

@ -2,9 +2,9 @@ package com.pixelized.rplexicon.ui.navigation
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
@ -43,6 +43,7 @@ import com.pixelized.rplexicon.ui.navigation.pages.composableQuests
import com.pixelized.rplexicon.ui.navigation.pages.navigateToLexicon
import com.pixelized.rplexicon.ui.navigation.pages.navigateToLocation
import com.pixelized.rplexicon.ui.navigation.pages.navigateToQuestList
import com.pixelized.rplexicon.ui.navigation.screens.navigateToSummary
import kotlinx.coroutines.launch
val LocalPageNavHost = staticCompositionLocalOf<NavHostController> {
@ -59,7 +60,9 @@ fun HomeNavHost(
questListState: LazyListState,
locationListState: LazyListState,
) {
val screen = LocalScreenNavHost.current
val scope = rememberCoroutineScope()
CompositionLocalProvider(
LocalSnack provides remember { SnackbarHostState() },
) {
@ -72,6 +75,14 @@ fun HomeNavHost(
title = {
Text(text = stringResource(id = R.string.app_name))
},
actions = {
IconButton(onClick = { screen.navigateToSummary() }) {
Icon(
painter = painterResource(id = R.drawable.ic_d20_24),
contentDescription = null,
)
}
}
)
},
snackbarHost = {

View file

@ -19,6 +19,7 @@ import com.pixelized.rplexicon.ui.navigation.screens.composableSearch
import com.pixelized.rplexicon.ui.navigation.screens.composableLocationDetail
import com.pixelized.rplexicon.ui.navigation.screens.composableQuestDetail
import com.pixelized.rplexicon.ui.navigation.screens.composableSpellDetail
import com.pixelized.rplexicon.ui.navigation.screens.composableSummary
import com.pixelized.rplexicon.ui.navigation.screens.navigateToHome
val LocalScreenNavHost = staticCompositionLocalOf<NavHostController> {
@ -58,6 +59,8 @@ fun ScreenNavHost(
composableLocationDetail()
composableCharacterSheet()
composableSpellDetail()
composableSummary()
}
}
}

View file

@ -0,0 +1,27 @@
package com.pixelized.rplexicon.ui.navigation.screens
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder
import com.pixelized.rplexicon.ui.navigation.NavigationAnimation
import com.pixelized.rplexicon.ui.navigation.animatedComposable
import com.pixelized.rplexicon.ui.screens.summary.SummaryScreen
private const val ROUTE = "summary"
const val SUMMARY_ROUTE = ROUTE
fun NavGraphBuilder.composableSummary() {
animatedComposable(
route = SUMMARY_ROUTE,
animation = NavigationAnimation.Push,
) {
SummaryScreen()
}
}
fun NavHostController.navigateToSummary(
option: NavOptionsBuilder.() -> Unit = {},
) {
val route = ROUTE
navigate(route = route, builder = option)
}

View file

@ -1,5 +1,6 @@
package com.pixelized.rplexicon.ui.screens.character.composable.character
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
@ -7,10 +8,15 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.dashedBorder
@Composable
@ -18,13 +24,14 @@ fun MasteryCircle(
modifier: Modifier = Modifier,
multiplier: Int,
size: Dp = 12.dp,
borderWidth: Dp = 1.dp
) {
when (multiplier) {
0 -> Box(
modifier = modifier
.size(size = size)
.dashedBorder(
width = 1.dp,
width = borderWidth,
color = MaterialTheme.colorScheme.onSurface,
shape = CircleShape,
on = 2.dp,
@ -43,14 +50,35 @@ fun MasteryCircle(
else -> Box(
modifier = modifier
.size(size = size)
.border(
width = 1.dp,
width = borderWidth,
color = MaterialTheme.colorScheme.onSurface,
shape = CircleShape
)
.padding(2.dp)
.padding(all = 2.dp)
) {
MasteryCircle(multiplier = multiplier - 1)
MasteryCircle(
multiplier = multiplier - 1,
size = size - borderWidth * 2,
)
}
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun MasteryCirclePreview(
@PreviewParameter(MasteryCircleProvider::class) preview: Int,
) {
LexiconTheme {
Surface {
MasteryCircle(multiplier = preview)
}
}
}
private class MasteryCircleProvider : PreviewParameterProvider<Int> {
override val values: Sequence<Int> = sequenceOf(0, 1, 2, 3)
}

View file

@ -10,8 +10,7 @@ import com.pixelized.rplexicon.ui.screens.character.composable.character.Passive
import com.pixelized.rplexicon.ui.screens.character.composable.character.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio
import com.pixelized.rplexicon.ui.screens.character.pages.proficiency.CharacterSheetUio
import com.pixelized.rplexicon.utilitary.extentions.local.advantage
import com.pixelized.rplexicon.utilitary.extentions.local.disadvantage
import com.pixelized.rplexicon.utilitary.extentions.local.passivesBonus
import com.pixelized.rplexicon.utilitary.extentions.local.sum
import com.pixelized.rplexicon.utilitary.extentions.modifier
import com.pixelized.rplexicon.utilitary.extentions.toLabel
@ -21,194 +20,188 @@ class CharacterSheetUioFactory @Inject constructor() {
fun convert(
sheet: CharacterSheet,
alterations: Map<Property, List<Alteration.Status>>,
status: Map<Property, List<Alteration.Status>>,
): CharacterSheetUio {
val proficiency = (sheet.proficiency + alterations[Property.PROFICIENCY].sum)
val strength = sheet.strength + alterations[Property.STRENGTH].sum
val dexterity = sheet.dexterity + alterations[Property.DEXTERITY].sum
val constitution = sheet.constitution + alterations[Property.CONSTITUTION].sum
val intelligence = sheet.intelligence + alterations[Property.INTELLIGENCE].sum
val wisdom = sheet.wisdom + alterations[Property.WISDOM].sum
val charisma = sheet.charisma + alterations[Property.CHARISMA].sum
val proficiency = (sheet.proficiency + status[Property.PROFICIENCY].sum)
val strength = sheet.strength + status[Property.STRENGTH].sum
val dexterity = sheet.dexterity + status[Property.DEXTERITY].sum
val constitution = sheet.constitution + status[Property.CONSTITUTION].sum
val intelligence = sheet.intelligence + status[Property.INTELLIGENCE].sum
val wisdom = sheet.wisdom + status[Property.WISDOM].sum
val charisma = sheet.charisma + status[Property.CHARISMA].sum
return CharacterSheetUio(
initiative = LabelPointUio(
label = R.string.character_sheet_title_initiative,
value = (dexterity.modifier + alterations[Property.INITIATIVE].sum).toLabel(),
value = (dexterity.modifier + status[Property.INITIATIVE].sum).toLabel(),
max = null,
),
stats = listOf(
StatUio(
id = StatUio.ID.STRENGTH,
value = strength,
modifier = strength.modifier + alterations[Property.STRENGTH_THROW].sum,
modifier = strength.modifier + status[Property.STRENGTH_THROW].sum,
),
StatUio(
id = StatUio.ID.DEXTERITY,
value = dexterity,
modifier = dexterity.modifier + alterations[Property.DEXTERITY_THROW].sum,
modifier = dexterity.modifier + status[Property.DEXTERITY_THROW].sum,
),
StatUio(
id = StatUio.ID.CONSTITUTION,
value = constitution,
modifier = constitution.modifier + alterations[Property.CONSTITUTION_THROW].sum,
modifier = constitution.modifier + status[Property.CONSTITUTION_THROW].sum,
),
StatUio(
id = StatUio.ID.INTELLIGENCE,
value = intelligence,
modifier = intelligence.modifier + alterations[Property.INTELLIGENCE_THROW].sum,
modifier = intelligence.modifier + status[Property.INTELLIGENCE_THROW].sum,
),
StatUio(
id = StatUio.ID.WISDOM,
value = wisdom,
modifier = wisdom.modifier + alterations[Property.WISDOM_THROW].sum,
modifier = wisdom.modifier + status[Property.WISDOM_THROW].sum,
),
StatUio(
id = StatUio.ID.CHARISMA,
value = charisma,
modifier = charisma.modifier + alterations[Property.CHARISMA_THROW].sum,
modifier = charisma.modifier + status[Property.CHARISMA_THROW].sum,
),
),
savingThrows = listOf(
ProficiencyUio(
id = ProficiencyUio.ID.STRENGTH_SAVING_THROW,
multiplier = sheet.strengthSavingThrows,
modifier = strength.modifier + alterations[Property.STRENGTH_SAVING_THROW].sum + sheet.strengthSavingThrows * proficiency,
modifier = strength.modifier + status[Property.STRENGTH_SAVING_THROW].sum + sheet.strengthSavingThrows * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.DEXTERITY_SAVING_THROW,
multiplier = sheet.dexteritySavingThrows,
modifier = dexterity.modifier + alterations[Property.DEXTERITY_SAVING_THROW].sum + sheet.dexteritySavingThrows * proficiency,
modifier = dexterity.modifier + status[Property.DEXTERITY_SAVING_THROW].sum + sheet.dexteritySavingThrows * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.CONSTITUTION_SAVING_THROW,
multiplier = sheet.constitutionSavingThrows,
modifier = constitution.modifier + alterations[Property.CONSTITUTION_SAVING_THROW].sum + sheet.constitutionSavingThrows * proficiency,
modifier = constitution.modifier + status[Property.CONSTITUTION_SAVING_THROW].sum + sheet.constitutionSavingThrows * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.INTELLIGENCE_SAVING_THROW,
multiplier = sheet.intelligenceSavingThrows,
modifier = intelligence.modifier + alterations[Property.INTELLIGENCE_SAVING_THROW].sum + sheet.intelligenceSavingThrows * proficiency,
modifier = intelligence.modifier + status[Property.INTELLIGENCE_SAVING_THROW].sum + sheet.intelligenceSavingThrows * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.WISDOM_SAVING_THROW,
multiplier = sheet.wisdomSavingThrows,
modifier = wisdom.modifier + alterations[Property.WISDOM_SAVING_THROW].sum + sheet.wisdomSavingThrows * proficiency,
modifier = wisdom.modifier + status[Property.WISDOM_SAVING_THROW].sum + sheet.wisdomSavingThrows * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.CHARISMA_SAVING_THROW,
multiplier = sheet.charismaSavingThrows,
modifier = charisma.modifier + alterations[Property.CHARISMA_SAVING_THROW].sum + sheet.charismaSavingThrows * proficiency,
modifier = charisma.modifier + status[Property.CHARISMA_SAVING_THROW].sum + sheet.charismaSavingThrows * proficiency,
),
),
proficiencies = listOf(
ProficiencyUio(
id = ProficiencyUio.ID.ACROBATICS,
multiplier = sheet.acrobatics,
modifier = dexterity.modifier + alterations[Property.ACROBATICS].sum + sheet.acrobatics * proficiency,
modifier = dexterity.modifier + status[Property.ACROBATICS].sum + sheet.acrobatics * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.ARCANA,
multiplier = sheet.arcana,
modifier = intelligence.modifier + alterations[Property.ARCANA].sum + sheet.arcana * proficiency,
modifier = intelligence.modifier + status[Property.ARCANA].sum + sheet.arcana * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.ATHLETICS,
multiplier = sheet.athletics,
modifier = strength.modifier + alterations[Property.ATHLETICS].sum + sheet.athletics * proficiency,
modifier = strength.modifier + status[Property.ATHLETICS].sum + sheet.athletics * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.STEALTH,
multiplier = sheet.stealth,
modifier = dexterity.modifier + alterations[Property.STEALTH].sum + sheet.stealth * proficiency,
modifier = dexterity.modifier + status[Property.STEALTH].sum + sheet.stealth * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.ANIMAL_HANDLING,
multiplier = sheet.animalHandling,
modifier = wisdom.modifier + alterations[Property.ANIMAL_HANDLING].sum + sheet.animalHandling * proficiency,
modifier = wisdom.modifier + status[Property.ANIMAL_HANDLING].sum + sheet.animalHandling * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.SLEIGHT_OF_HAND,
multiplier = sheet.sleightOfHand,
modifier = dexterity.modifier + alterations[Property.SLEIGHT_OF_HAND].sum + sheet.sleightOfHand * proficiency,
modifier = dexterity.modifier + status[Property.SLEIGHT_OF_HAND].sum + sheet.sleightOfHand * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.HISTORY,
multiplier = sheet.history,
modifier = intelligence.modifier + alterations[Property.HISTORY].sum + sheet.history * proficiency,
modifier = intelligence.modifier + status[Property.HISTORY].sum + sheet.history * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.INTIMIDATION,
multiplier = sheet.intimidation,
modifier = charisma.modifier + alterations[Property.INTIMIDATION].sum + sheet.intimidation * proficiency,
modifier = charisma.modifier + status[Property.INTIMIDATION].sum + sheet.intimidation * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.INSIGHT,
multiplier = sheet.insight,
modifier = wisdom.modifier + alterations[Property.INSIGHT].sum + sheet.insight * proficiency,
modifier = wisdom.modifier + status[Property.INSIGHT].sum + sheet.insight * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.INVESTIGATION,
multiplier = sheet.investigation,
modifier = intelligence.modifier + alterations[Property.INVESTIGATION].sum + sheet.investigation * proficiency,
modifier = intelligence.modifier + status[Property.INVESTIGATION].sum + sheet.investigation * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.MEDICINE,
multiplier = sheet.medicine,
modifier = wisdom.modifier + alterations[Property.MEDICINE].sum + sheet.medicine * proficiency,
modifier = wisdom.modifier + status[Property.MEDICINE].sum + sheet.medicine * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.NATURE,
multiplier = sheet.nature,
modifier = intelligence.modifier + alterations[Property.NATURE].sum + sheet.nature * proficiency,
modifier = intelligence.modifier + status[Property.NATURE].sum + sheet.nature * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.PERCEPTION,
multiplier = sheet.perception,
modifier = wisdom.modifier + alterations[Property.PERCEPTION].sum + sheet.perception * proficiency,
modifier = wisdom.modifier + status[Property.PERCEPTION].sum + sheet.perception * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.PERSUASION,
multiplier = sheet.persuasion,
modifier = charisma.modifier + alterations[Property.PERSUASION].sum + sheet.persuasion * proficiency,
modifier = charisma.modifier + status[Property.PERSUASION].sum + sheet.persuasion * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.RELIGION,
multiplier = sheet.religion,
modifier = intelligence.modifier + alterations[Property.RELIGION].sum + sheet.religion * proficiency,
modifier = intelligence.modifier + status[Property.RELIGION].sum + sheet.religion * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.PERFORMANCE,
multiplier = sheet.performance,
modifier = charisma.modifier + alterations[Property.PERFORMANCE].sum + sheet.performance * proficiency,
modifier = charisma.modifier + status[Property.PERFORMANCE].sum + sheet.performance * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.SURVIVAL,
multiplier = sheet.survival,
modifier = wisdom.modifier + alterations[Property.SURVIVAL].sum + sheet.survival * proficiency,
modifier = wisdom.modifier + status[Property.SURVIVAL].sum + sheet.survival * proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.DECEPTION,
multiplier = sheet.deception,
modifier = charisma.modifier + alterations[Property.DECEPTION].sum + sheet.deception * proficiency,
modifier = charisma.modifier + status[Property.DECEPTION].sum + sheet.deception * proficiency,
),
),
passives = PassivesUio(
speed = sheet.speed,
perception = alterations[Property.PERCEPTION].let {
val advantage = if (it.advantage) 5 else 0
val disadvantage = if (it.disadvantage) 5 else 0
10 + wisdom.modifier + it.sum + sheet.perception * proficiency + advantage - disadvantage
perception = status[Property.PERCEPTION].let {
10 + wisdom.modifier + it.sum + sheet.perception * proficiency + it.passivesBonus
},
investigation = alterations[Property.INVESTIGATION].let {
val advantage = if (it.advantage) 5 else 0
val disadvantage = if (it.disadvantage) 5 else 0
10 + intelligence.modifier + it.sum + sheet.investigation * proficiency + advantage - disadvantage
investigation = status[Property.INVESTIGATION].let {
10 + intelligence.modifier + it.sum + sheet.investigation * proficiency + it.passivesBonus
},
insight = alterations[Property.INSIGHT].let {
val advantage = if (it.advantage) 5 else 0
val disadvantage = if (it.disadvantage) 5 else 0
10 + wisdom.modifier + it.sum + sheet.insight * proficiency + advantage - disadvantage
insight = status[Property.INSIGHT].let {
10 + wisdom.modifier + it.sum + sheet.insight * proficiency + it.passivesBonus
},
),
masteries = MasteriesUio(

View file

@ -166,7 +166,7 @@ fun ProficiencyPageContent(
modifier = Modifier.padding(vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
textAlign = TextAlign.Center,
text = "Passives",
text = stringResource(id = R.string.character_sheet_title_passive),
)
},
masteries = {

View file

@ -43,7 +43,7 @@ class ProficiencyViewModel @Inject constructor(
val alterations = alterationRepository.getActiveAlterationsStatus(character)
val sheet = characterSheetFactory.convert(
sheet = characterSheet,
alterations = alterations,
status = alterations,
)
withContext(Dispatchers.Main) {
_sheet.value = sheet

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@ -158,7 +159,10 @@ fun LexiconItem(
}
if (item.isPlayingCharacter) {
IconButton(onClick = { item.let(onCharacterSheet) }) {
IconButton(
modifier = Modifier.offset(x = 4.dp),
onClick = { item.let(onCharacterSheet) }
) {
Icon(
painter = painterResource(id = R.drawable.ic_d20_24),
contentDescription = null

View file

@ -0,0 +1,598 @@
package com.pixelized.rplexicon.ui.screens.summary
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.CharacterSheetFire
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.ui.screens.summary.composable.AttributesSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.CharacteristicsSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.ClassHeaderSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.PassivesSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.ProficiencySummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.SavingThrowsSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.SpellSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.label
import com.pixelized.rplexicon.ui.screens.summary.composable.common.maxLabel
import com.pixelized.rplexicon.ui.screens.summary.composable.common.none
import com.pixelized.rplexicon.ui.screens.summary.composable.common.proficiency
import com.pixelized.rplexicon.utilitary.extentions.local.highestSpellLevel
import com.pixelized.rplexicon.utilitary.extentions.local.passivesBonus
import com.pixelized.rplexicon.utilitary.extentions.local.spell
import com.pixelized.rplexicon.utilitary.extentions.local.sum
import com.pixelized.rplexicon.utilitary.extentions.modifier
import com.pixelized.rplexicon.utilitary.extentions.toLabel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
private const val ENTER_ANIMATION_INITIAL_DELAY = 250L
private const val ENTER_ANIMATION_DELAY = 100L
class SummaryFactory @Inject constructor(
private val characterSheetRepository: CharacterSheetRepository,
private val alterationRepository: AlterationRepository,
private val firebaseRepository: FirebaseRepository,
) {
fun fetchSummary(scope: CoroutineScope): SummaryUio {
val headerVisibility = mutableStateOf(false)
val statsVisibility = mutableStateOf(false)
val characteristicsVisibility = mutableStateOf(false)
val savingThrowsVisibility = mutableStateOf(false)
val proficienciesVisibility = mutableStateOf(false)
val passivesVisibility = mutableStateOf(false)
val spellsVisibility = mutableStateOf(false)
val header = ClassHeaderSummaryUio(
c1 = mutableStateOf(ClassHeaderSummaryUio.Header()),
c2 = mutableStateOf(ClassHeaderSummaryUio.Header()),
c3 = mutableStateOf(ClassHeaderSummaryUio.Header()),
c4 = mutableStateOf(ClassHeaderSummaryUio.Header()),
c5 = mutableStateOf(ClassHeaderSummaryUio.Header()),
)
val stats = AttributesSummaryUio(
hp = SummaryRowUio(label = R.string.character_sheet_title_hp),
ac = SummaryRowUio(label = R.string.character_sheet_title_ca),
dc = SummaryRowUio(label = R.string.character_sheet_title_dc),
speed = SummaryRowUio(label = R.string.character_sheet_title_speed),
)
val characteristics = CharacteristicsSummaryUio(
strength = SummaryRowUio(label = R.string.character_sheet_stat_strength),
dexterity = SummaryRowUio(label = R.string.character_sheet_stat_dexterity),
constitution = SummaryRowUio(label = R.string.character_sheet_stat_constitution),
intelligence = SummaryRowUio(label = R.string.character_sheet_stat_intelligence),
wisdom = SummaryRowUio(label = R.string.character_sheet_stat_wisdom),
charisma = SummaryRowUio(label = R.string.character_sheet_stat_charisma),
)
val savingThrows = SavingThrowsSummaryUio(
strength = SummaryRowUio(label = R.string.character_sheet_stat_strength),
dexterity = SummaryRowUio(label = R.string.character_sheet_stat_dexterity),
constitution = SummaryRowUio(label = R.string.character_sheet_stat_constitution),
intelligence = SummaryRowUio(label = R.string.character_sheet_stat_intelligence),
wisdom = SummaryRowUio(label = R.string.character_sheet_stat_wisdom),
charisma = SummaryRowUio(label = R.string.character_sheet_stat_charisma),
)
val proficiencies = ProficiencySummaryUio(
acrobatics = SummaryRowUio(label = R.string.character_sheet_proficiency_acrobatics),
animalHandling = SummaryRowUio(label = R.string.character_sheet_proficiency_animal_handling),
arcana = SummaryRowUio(label = R.string.character_sheet_proficiency_arcana),
athletics = SummaryRowUio(label = R.string.character_sheet_proficiency_athletics),
deception = SummaryRowUio(label = R.string.character_sheet_proficiency_deception),
history = SummaryRowUio(label = R.string.character_sheet_proficiency_history),
insight = SummaryRowUio(label = R.string.character_sheet_proficiency_insight),
intimidation = SummaryRowUio(label = R.string.character_sheet_proficiency_intimidation),
investigation = SummaryRowUio(label = R.string.character_sheet_proficiency_investigation),
medicine = SummaryRowUio(label = R.string.character_sheet_proficiency_medicine),
nature = SummaryRowUio(label = R.string.character_sheet_proficiency_nature),
perception = SummaryRowUio(label = R.string.character_sheet_proficiency_perception),
performance = SummaryRowUio(label = R.string.character_sheet_proficiency_performance),
persuasion = SummaryRowUio(label = R.string.character_sheet_proficiency_persuasion),
religion = SummaryRowUio(label = R.string.character_sheet_proficiency_religion),
sleightOfHand = SummaryRowUio(label = R.string.character_sheet_proficiency_sleight_of_hand),
stealth = SummaryRowUio(label = R.string.character_sheet_proficiency_stealth),
survival = SummaryRowUio(label = R.string.character_sheet_proficiency_survival),
)
val passives = PassivesSummaryUio(
perception = SummaryRowUio(label = R.string.character_sheet_proficiency_perception),
investigation = SummaryRowUio(label = R.string.character_sheet_proficiency_investigation),
insight = SummaryRowUio(label = R.string.character_sheet_proficiency_insight),
)
val maxSpellSlot = mutableIntStateOf(0)
val spells = SpellSummaryUio(
extra = SummaryRowUio(label = R.string.character_sheet_title_skills),
slot1 = SummaryRowUio(label = R.string.character_sheet_title_spell_slot_1),
slot2 = SummaryRowUio(label = R.string.character_sheet_title_spell_slot_2),
slot3 = SummaryRowUio(label = R.string.character_sheet_title_spell_slot_3),
slot4 = SummaryRowUio(label = R.string.character_sheet_title_spell_slot_4),
slot5 = SummaryRowUio(label = R.string.character_sheet_title_spell_slot_5),
slot6 = SummaryRowUio(label = R.string.character_sheet_title_spell_slot_6),
slot7 = SummaryRowUio(label = R.string.character_sheet_title_spell_slot_7),
slot8 = SummaryRowUio(label = R.string.character_sheet_title_spell_slot_8),
slot9 = SummaryRowUio(label = R.string.character_sheet_title_spell_slot_9),
max = maxSpellSlot
)
scope.launch(Dispatchers.IO) {
val data = DataStruct()
characterSheetRepository.data
.combine(alterationRepository.assignedAlterations) { sheets, _ ->
data.sheets = sheets
}
.combine(firebaseRepository.getCharacter()) { _, fire ->
data.fires = fire.characters
}
.collect {
val (sheets, fires) = data
val characters = sheets.keys.sorted()
// Update the max spell slot card.
val highestSpellSlot = sheets.values.maxOf { it.highestSpellLevel() }
withContext(Dispatchers.Main) {
maxSpellSlot.intValue = highestSpellSlot
}
// extension function to root a character to a column property.
fun ClassHeaderSummaryUio.get(sheet: CharacterSheet?) = when (sheet) {
sheets[characters.getOrNull(0)] -> c1
sheets[characters.getOrNull(1)] -> c2
sheets[characters.getOrNull(2)] -> c3
sheets[characters.getOrNull(3)] -> c4
sheets[characters.getOrNull(4)] -> c5
else -> null
}
fun SummaryRowUio.get(sheet: CharacterSheet?) = when (sheet) {
sheets[characters.getOrNull(0)] -> c1
sheets[characters.getOrNull(1)] -> c2
sheets[characters.getOrNull(2)] -> c3
sheets[characters.getOrNull(3)] -> c4
sheets[characters.getOrNull(4)] -> c5
else -> null
}
// Update the header
sheets.values.forEach { sheet ->
val clazz = ClassHeaderSummaryUio.Header(
label = sheet.name,
icon = sheet.characterClass.firstOrNull()?.icon ?: R.drawable.ic_d20_24,
)
withContext(Dispatchers.Main) {
header.get(sheet)?.value = clazz
}
}
withContext(Dispatchers.Main) {
if (headerVisibility.value.not()) delay(ENTER_ANIMATION_INITIAL_DELAY)
headerVisibility.value = true
}
// Update the attributes
sheets.values.forEach { sheet ->
val status = alterationRepository.getActiveAlterationsStatus(sheet.name)
val fire = fires[sheet.name]
val hitPoint = label(
label = fire?.hitPoint?.let {
when (it.additional) {
null, 0 -> "${it.value}"
else -> "${it.value}+${it.additional}"
}
} ?: "?",
)
val armorClass = label(
label = "${sheet.armorClass + status[Property.ARMOR_CLASS].sum}",
)
val dC = label(
label = sheet.dC?.let { "$it" } ?: "",
)
val speed = label(
label = "${sheet.speed}",
)
withContext(Dispatchers.Main) {
stats.hp.get(sheet)?.value = hitPoint
stats.ac.get(sheet)?.value = armorClass
stats.dc.get(sheet)?.value = dC
stats.speed.get(sheet)?.value = speed
}
}
withContext(Dispatchers.Main) {
if (statsVisibility.value.not()) delay(ENTER_ANIMATION_DELAY)
statsVisibility.value = true
}
// Update the Characteristics
sheets.values.forEach { sheet ->
val status = alterationRepository.getActiveAlterationsStatus(sheet.name)
// Update the Characteristics
val strengthLabel = label(
label = "${sheet.strength + status[Property.STRENGTH].sum}",
)
val dexterityLabel = label(
label = "${sheet.dexterity + status[Property.DEXTERITY].sum}",
)
val constitutionLabel = label(
label = "${sheet.constitution + status[Property.CONSTITUTION].sum}",
)
val intelligenceLabel = label(
label = "${sheet.intelligence + status[Property.INTELLIGENCE].sum}",
)
val wisdomLabel = label(
label = "${sheet.wisdom + status[Property.WISDOM].sum}",
)
val charismaLabel = label(
label = "${sheet.charisma + status[Property.CHARISMA].sum}",
)
withContext(Dispatchers.Main) {
characteristics.strength.get(sheet)?.value = strengthLabel
characteristics.dexterity.get(sheet)?.value = dexterityLabel
characteristics.constitution.get(sheet)?.value = constitutionLabel
characteristics.intelligence.get(sheet)?.value = intelligenceLabel
characteristics.wisdom.get(sheet)?.value = wisdomLabel
characteristics.charisma.get(sheet)?.value = charismaLabel
}
}
withContext(Dispatchers.Main) {
if (characteristicsVisibility.value.not()) delay(ENTER_ANIMATION_DELAY)
characteristicsVisibility.value = true
}
// Update the SavingThrows
sheets.values.forEach { sheet ->
val status = alterationRepository.getActiveAlterationsStatus(sheet.name)
val proficiency = (sheet.proficiency + status[Property.PROFICIENCY].sum)
val strength = sheet.strength + status[Property.STRENGTH].sum
val dexterity = sheet.dexterity + status[Property.DEXTERITY].sum
val constitution = sheet.constitution + status[Property.CONSTITUTION].sum
val intelligence = sheet.intelligence + status[Property.INTELLIGENCE].sum
val wisdom = sheet.wisdom + status[Property.WISDOM].sum
val charisma = sheet.charisma + status[Property.CHARISMA].sum
val strengthSavingThrows = proficiency(
multiplier = sheet.strengthSavingThrows,
label = status[Property.STRENGTH_SAVING_THROW].let {
strength.modifier + it.sum + sheet.strengthSavingThrows * proficiency
}.toLabel(),
)
val dexteritySavingThrows = proficiency(
multiplier = sheet.dexteritySavingThrows,
label = status[Property.DEXTERITY_SAVING_THROW].let {
dexterity.modifier + it.sum + sheet.dexteritySavingThrows * proficiency
}.toLabel(),
)
val constitutionSavingThrows = proficiency(
multiplier = sheet.constitutionSavingThrows,
label = status[Property.CONSTITUTION_SAVING_THROW].let {
constitution.modifier + it.sum + sheet.constitutionSavingThrows * proficiency
}.toLabel(),
)
val intelligenceSavingThrows = proficiency(
multiplier = sheet.intelligenceSavingThrows,
label = status[Property.INTELLIGENCE_SAVING_THROW].let {
intelligence.modifier + it.sum + sheet.intelligenceSavingThrows * proficiency
}.toLabel(),
)
val wisdomSavingThrows = proficiency(
multiplier = sheet.wisdomSavingThrows,
label = status[Property.WISDOM_SAVING_THROW].let {
wisdom.modifier + it.sum + sheet.wisdomSavingThrows * proficiency
}.toLabel(),
)
val charismaSavingThrows = proficiency(
multiplier = sheet.charismaSavingThrows,
label = status[Property.CHARISMA_SAVING_THROW].let {
charisma.modifier + it.sum + sheet.charismaSavingThrows * proficiency
}.toLabel(),
)
withContext(Dispatchers.Main) {
savingThrows.strength.get(sheet)?.value = strengthSavingThrows
savingThrows.dexterity.get(sheet)?.value = dexteritySavingThrows
savingThrows.constitution.get(sheet)?.value = constitutionSavingThrows
savingThrows.intelligence.get(sheet)?.value = intelligenceSavingThrows
savingThrows.wisdom.get(sheet)?.value = wisdomSavingThrows
savingThrows.charisma.get(sheet)?.value = charismaSavingThrows
}
}
withContext(Dispatchers.Main) {
if (savingThrowsVisibility.value.not()) delay(ENTER_ANIMATION_DELAY)
savingThrowsVisibility.value = true
}
// Update proficiencies
sheets.values.forEach { sheet ->
val status = alterationRepository.getActiveAlterationsStatus(sheet.name)
val proficiency = (sheet.proficiency + status[Property.PROFICIENCY].sum)
val strength = sheet.strength + status[Property.STRENGTH].sum
val dexterity = sheet.dexterity + status[Property.DEXTERITY].sum
val intelligence = sheet.intelligence + status[Property.INTELLIGENCE].sum
val wisdom = sheet.wisdom + status[Property.WISDOM].sum
val charisma = sheet.charisma + status[Property.CHARISMA].sum
val acrobatics = proficiency(
multiplier = sheet.acrobatics,
label = status[Property.ACROBATICS].let {
dexterity.modifier + it.sum + sheet.acrobatics * proficiency
}.toLabel(),
)
val arcana = proficiency(
multiplier = sheet.arcana,
label = status[Property.ARCANA].let {
intelligence.modifier + it.sum + sheet.arcana * proficiency
}.toLabel(),
)
val athletics = proficiency(
multiplier = sheet.athletics,
label = status[Property.ATHLETICS].let {
strength.modifier + it.sum + sheet.athletics * proficiency
}.toLabel(),
)
val stealth = proficiency(
multiplier = sheet.stealth,
label = status[Property.STEALTH].let {
dexterity.modifier + it.sum + sheet.stealth * proficiency
}.toLabel(),
)
val animalHandling = proficiency(
multiplier = sheet.animalHandling,
label = status[Property.ANIMAL_HANDLING].let {
wisdom.modifier + it.sum + sheet.animalHandling * proficiency
}.toLabel(),
)
val sleightOfHand = proficiency(
multiplier = sheet.sleightOfHand,
label = status[Property.SLEIGHT_OF_HAND].let {
dexterity.modifier + it.sum + sheet.sleightOfHand * proficiency
}.toLabel(),
)
val history = proficiency(
multiplier = sheet.history,
label = status[Property.HISTORY].let {
intelligence.modifier + it.sum + sheet.history * proficiency
}.toLabel(),
)
val intimidation = proficiency(
multiplier = sheet.intimidation,
label = status[Property.INTIMIDATION].let {
charisma.modifier + it.sum + sheet.intimidation * proficiency
}.toLabel(),
)
val insight = proficiency(
multiplier = sheet.insight,
label = status[Property.INSIGHT].let {
wisdom.modifier + it.sum + sheet.insight * proficiency
}.toLabel(),
)
val investigation = proficiency(
multiplier = sheet.investigation,
label = status[Property.INVESTIGATION].let {
intelligence.modifier + it.sum + sheet.investigation * proficiency
}.toLabel(),
)
val medicine = proficiency(
multiplier = sheet.medicine,
label = status[Property.MEDICINE].let {
wisdom.modifier + it.sum + sheet.medicine * proficiency
}.toLabel(),
)
val nature = proficiency(
multiplier = sheet.nature,
label = status[Property.NATURE].let {
intelligence.modifier + it.sum + sheet.nature * proficiency
}.toLabel(),
)
val perception = proficiency(
multiplier = sheet.perception,
label = status[Property.PERCEPTION].let {
wisdom.modifier + it.sum + sheet.perception * proficiency
}.toLabel(),
)
val persuasion = proficiency(
multiplier = sheet.persuasion,
label = status[Property.PERSUASION].let {
charisma.modifier + it.sum + sheet.persuasion * proficiency
}.toLabel(),
)
val religion = proficiency(
multiplier = sheet.religion,
label = status[Property.RELIGION].let {
intelligence.modifier + it.sum + sheet.religion * proficiency
}.toLabel(),
)
val performance = proficiency(
multiplier = sheet.performance,
label = status[Property.PERFORMANCE].let {
charisma.modifier + it.sum + sheet.performance * proficiency
}.toLabel(),
)
val survival = proficiency(
multiplier = sheet.survival,
label = status[Property.SURVIVAL].let {
wisdom.modifier + it.sum + sheet.survival * proficiency
}.toLabel(),
)
val deception = proficiency(
multiplier = sheet.deception,
label = status[Property.DECEPTION].let {
charisma.modifier + it.sum + sheet.deception * proficiency
}.toLabel(),
)
withContext(Dispatchers.Main) {
proficiencies.acrobatics.get(sheet)?.value = acrobatics
proficiencies.arcana.get(sheet)?.value = arcana
proficiencies.athletics.get(sheet)?.value = athletics
proficiencies.stealth.get(sheet)?.value = stealth
proficiencies.animalHandling.get(sheet)?.value = animalHandling
proficiencies.sleightOfHand.get(sheet)?.value = sleightOfHand
proficiencies.history.get(sheet)?.value = history
proficiencies.intimidation.get(sheet)?.value = intimidation
proficiencies.insight.get(sheet)?.value = insight
proficiencies.investigation.get(sheet)?.value = investigation
proficiencies.medicine.get(sheet)?.value = medicine
proficiencies.nature.get(sheet)?.value = nature
proficiencies.perception.get(sheet)?.value = perception
proficiencies.persuasion.get(sheet)?.value = persuasion
proficiencies.religion.get(sheet)?.value = religion
proficiencies.performance.get(sheet)?.value = performance
proficiencies.survival.get(sheet)?.value = survival
proficiencies.deception.get(sheet)?.value = deception
}
}
withContext(Dispatchers.Main) {
if (proficienciesVisibility.value.not()) delay(ENTER_ANIMATION_DELAY)
proficienciesVisibility.value = true
}
// Update passives
sheets.values.forEach { sheet ->
val status = alterationRepository.getActiveAlterationsStatus(sheet.name)
val proficiency = (sheet.proficiency + status[Property.PROFICIENCY].sum)
val intelligence = sheet.intelligence + status[Property.INTELLIGENCE].sum
val wisdom = sheet.wisdom + status[Property.WISDOM].sum
val passiveInsight = label(
label = status[Property.INSIGHT].let {
wisdom.modifier + it.sum + sheet.insight * proficiency + it.passivesBonus
}.let { "${10 + it}" },
)
val passiveInvestigation = label(
label = status[Property.INVESTIGATION].let {
intelligence.modifier + it.sum + sheet.investigation * proficiency + it.passivesBonus
}.let { "${10 + it}" },
)
val passivePerception = label(
label = status[Property.PERCEPTION].let {
wisdom.modifier + it.sum + sheet.perception * proficiency + it.passivesBonus
}.let { "${10 + it}" }
)
withContext(Dispatchers.Main) {
passives.insight.get(sheet)?.value = passiveInsight
passives.investigation.get(sheet)?.value = passiveInvestigation
passives.perception.get(sheet)?.value = passivePerception
}
}
withContext(Dispatchers.Main) {
if (passivesVisibility.value.not()) delay(ENTER_ANIMATION_DELAY)
passivesVisibility.value = true
}
sheets.values.forEach { sheet ->
val fire = fires[sheet.name]
// Update the extra skill.
val slot1 = sheet.spell(level = 1)?.let { max ->
maxLabel(
label = fire?.spell(level = 1)?.let { "$it" } ?: "",
max = "$max",
)
} ?: none()
// Update spells
val skill = sheet.characterClass.firstOrNull()?.resource?.let { skill ->
fire?.skills?.get(skill)
}?.let { skill ->
label(label = "$skill")
} ?: none()
val slot2 = sheet.spell(level = 2)?.let { max ->
maxLabel(
label = fire?.spell(level = 2)?.let { "$it" } ?: "",
max = "$max",
)
} ?: none()
val slot3 = sheet.spell(level = 3)?.let { max ->
maxLabel(
label = fire?.spell(level = 3)?.let { "$it" } ?: "",
max = "$max",
)
} ?: none()
val slot4 = sheet.spell(level = 4)?.let { max ->
maxLabel(
label = fire?.spell(level = 4)?.let { "$it" } ?: "",
max = "$max",
)
} ?: none()
val slot5 = sheet.spell(level = 5)?.let { max ->
maxLabel(
label = fire?.spell(level = 5)?.let { "$it" } ?: "",
max = "$max",
)
} ?: none()
val slot6 = sheet.spell(level = 6)?.let { max ->
maxLabel(
label = fire?.spell(level = 6)?.let { "$it" } ?: "",
max = "$max",
)
} ?: none()
val slot7 = sheet.spell(level = 7)?.let { max ->
maxLabel(
label = fire?.spell(level = 7)?.let { "$it" } ?: "",
max = "$max",
)
} ?: none()
val slot8 = sheet.spell(level = 8)?.let { max ->
maxLabel(
label = fire?.spell(level = 8)?.let { "$it" } ?: "",
max = "$max",
)
} ?: none()
val slot9 = sheet.spell(level = 9)?.let { max ->
maxLabel(
label = fire?.spell(level = 9)?.let { "$it" } ?: "",
max = "$max",
)
} ?: none()
withContext(Dispatchers.Main) {
spells.extra.get(sheet = sheet)?.value = skill
spells.slot1.get(sheet = sheet)?.value = slot1
spells.slot2.get(sheet = sheet)?.value = slot2
spells.slot3.get(sheet = sheet)?.value = slot3
spells.slot4.get(sheet = sheet)?.value = slot4
spells.slot5.get(sheet = sheet)?.value = slot5
spells.slot6.get(sheet = sheet)?.value = slot6
spells.slot7.get(sheet = sheet)?.value = slot7
spells.slot8.get(sheet = sheet)?.value = slot8
spells.slot9.get(sheet = sheet)?.value = slot9
}
}
withContext(Dispatchers.Main) {
if (spellsVisibility.value.not()) delay(ENTER_ANIMATION_DELAY)
spellsVisibility.value = true
}
}
}
return SummaryUio(
headerVisibility = headerVisibility,
header = header,
statsVisibility = statsVisibility,
stats = stats,
characteristicsVisibility = characteristicsVisibility,
characteristics = characteristics,
savingThrowsVisibility = savingThrowsVisibility,
savingThrows = savingThrows,
proficienciesVisibility = proficienciesVisibility,
proficiencies = proficiencies,
passivesVisibility = passivesVisibility,
passives = passives,
spellsVisibility = spellsVisibility,
spells = spells,
)
}
private class DataStruct {
lateinit var sheets: Map<String, CharacterSheet>
lateinit var fires: Map<String, CharacterSheetFire>
operator fun component1() = sheets
operator fun component2() = fires
}
}

View file

@ -0,0 +1,288 @@
package com.pixelized.rplexicon.ui.screens.summary
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.fadeIn
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.KeepOnScreen
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.navigation.screens.navigateToCharacterSheet
import com.pixelized.rplexicon.ui.screens.summary.composable.AttributesSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.AttributesSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.CharacteristicsSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.CharacteristicsSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.ClassHeaderSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.ClassHeaderSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.PassivesSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.PassivesSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.ProficiencySummary
import com.pixelized.rplexicon.ui.screens.summary.composable.ProficiencySummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.SavingThrowsSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.SavingThrowsSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.SpellSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.SpellSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberCharacteristicsSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberClassHeaderSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberPassivesSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberProficienciesSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberSavingThrowsSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberSpellsSummary
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberStatsSummary
import com.pixelized.rplexicon.ui.theme.LexiconTheme
@Stable
data class SummaryUio(
val headerVisibility: State<Boolean>,
val header: ClassHeaderSummaryUio,
val statsVisibility: State<Boolean>,
val stats: AttributesSummaryUio,
val characteristicsVisibility: State<Boolean>,
val characteristics: CharacteristicsSummaryUio,
val savingThrowsVisibility: State<Boolean>,
val savingThrows: SavingThrowsSummaryUio,
val proficienciesVisibility: State<Boolean>,
val proficiencies: ProficiencySummaryUio,
val passivesVisibility: State<Boolean>,
val passives: PassivesSummaryUio,
val spellsVisibility: State<Boolean>,
val spells: SpellSummaryUio,
)
@Composable
fun SummaryScreen(
viewModel: SummaryViewModel = hiltViewModel(),
) {
val screen = LocalScreenNavHost.current
Surface(
modifier = Modifier.fillMaxSize(),
) {
SummaryContent(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding(),
summary = viewModel.summary,
onBack = {
screen.popBackStack()
},
onHeader = {
screen.navigateToCharacterSheet(name = it.label)
}
)
KeepOnScreen()
}
}
@Composable
private fun SummaryContent(
modifier: Modifier = Modifier,
scrollState: ScrollState = rememberScrollState(),
summary: SummaryUio,
onBack: () -> Unit,
onHeader: (ClassHeaderSummaryUio.Header) -> Unit,
) {
Scaffold(
modifier = modifier,
containerColor = Color.Transparent,
topBar = {
Surface(
modifier = Modifier.fillMaxWidth(),
) {
Row(
modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
IconButton(onClick = onBack) {
Icon(
painter = painterResource(id = R.drawable.ic_arrow_back_ios_new_24),
contentDescription = null
)
}
Text(
text = stringResource(id = R.string.character_sheet_summary_title),
style = MaterialTheme.typography.titleLarge,
)
}
}
},
) { paddingValues ->
Column(
modifier = Modifier
.verticalScroll(state = scrollState)
.padding(paddingValues = paddingValues)
.padding(horizontal = 16.dp),
) {
AnimatedVisibility(
modifier = Modifier
.zIndex(zIndex = 1f)
.fillMaxWidth()
.offset { IntOffset(x = 0, y = scrollState.value) }
.background(brush = rememberHeaderBackgroundGradient())
.padding(bottom = 16.dp, end = 3.dp),
visible = summary.headerVisibility.value,
enter = fadeIn(),
) {
ClassHeaderSummary(
header = summary.header,
onClick = onHeader,
)
}
Column(
verticalArrangement = Arrangement.spacedBy(space = 16.dp),
) {
AnimatedVisibility(
visible = summary.statsVisibility.value,
enter = enterTransition(),
) {
AttributesSummary(
attributes = summary.stats,
)
}
AnimatedVisibility(
visible = summary.characteristicsVisibility.value,
enter = enterTransition(),
) {
CharacteristicsSummary(
characteristics = summary.characteristics,
)
}
AnimatedVisibility(
visible = summary.savingThrowsVisibility.value,
enter = enterTransition(),
) {
SavingThrowsSummary(
savingThrows = summary.savingThrows,
)
}
AnimatedVisibility(
visible = summary.proficienciesVisibility.value,
enter = enterTransition(),
) {
ProficiencySummary(
proficiencies = summary.proficiencies,
)
}
AnimatedVisibility(
visible = summary.passivesVisibility.value,
enter = enterTransition(),
) {
PassivesSummary(
passives = summary.passives,
)
}
AnimatedVisibility(
visible = summary.spellsVisibility.value,
enter = enterTransition(),
) {
SpellSummary(
spells = summary.spells,
)
}
}
}
}
}
@Composable
private fun enterTransition(
density: Density = LocalDensity.current,
): EnterTransition {
return fadeIn() + slideInVertically { with(density) { 24.dp.roundToPx() } }
}
@Composable
private fun rememberHeaderBackgroundGradient(): Brush {
val colorScheme = MaterialTheme.colorScheme
return remember {
Brush.verticalGradient(
colors = listOf(
colorScheme.surface.copy(alpha = 1.0f),
colorScheme.surface.copy(alpha = 1.0f),
colorScheme.surface.copy(alpha = 0.5f),
colorScheme.surface.copy(alpha = 0.0f),
)
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, heightDp = 1680)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, heightDp = 1680)
private fun SummaryPreview() {
LexiconTheme {
Surface {
val header = rememberClassHeaderSummary()
val stats = rememberStatsSummary()
val characteristics = rememberCharacteristicsSummary()
val savingThrows = rememberSavingThrowsSummary()
val proficiencies = rememberProficienciesSummary()
val passives = rememberPassivesSummary()
val spells = rememberSpellsSummary()
SummaryContent(
modifier = Modifier.fillMaxSize(),
summary = remember {
SummaryUio(
headerVisibility = mutableStateOf(true),
header = header,
statsVisibility = mutableStateOf(true),
stats = stats,
characteristicsVisibility = mutableStateOf(true),
characteristics = characteristics,
savingThrowsVisibility = mutableStateOf(true),
savingThrows = savingThrows,
proficienciesVisibility = mutableStateOf(true),
proficiencies = proficiencies,
passivesVisibility = mutableStateOf(true),
passives = passives,
spellsVisibility = mutableStateOf(true),
spells = spells,
)
},
onBack = { },
onHeader = { },
)
}
}
}

View file

@ -0,0 +1,13 @@
package com.pixelized.rplexicon.ui.screens.summary
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class SummaryViewModel @Inject constructor(
factory: SummaryFactory,
) : ViewModel() {
val summary = factory.fetchSummary(viewModelScope)
}

View file

@ -0,0 +1,81 @@
package com.pixelized.rplexicon.ui.screens.summary.composable
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberStatsSummary
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@Stable
data class AttributesSummaryUio(
val hp: SummaryRowUio,
val ac: SummaryRowUio,
val dc: SummaryRowUio,
val speed: SummaryRowUio,
)
@Composable
fun AttributesSummary(
modifier: Modifier = Modifier,
attributes: AttributesSummaryUio,
) {
Column(
modifier = Modifier
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.padding(start = 8.dp, top = 4.dp, bottom = 4.dp)
.then(other = modifier),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SummaryRow(
row = attributes.hp,
color = MaterialTheme.colorScheme.primary,
)
SummaryRow(
row = attributes.ac,
)
SummaryRow(
row = attributes.dc,
)
SummaryRow(
row = attributes.speed,
)
Text(
modifier = Modifier.padding(vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
text = stringResource(id = R.string.character_sheet_title_attribute)
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun StatsSummaryPreview() {
LexiconTheme {
Surface {
AttributesSummary(
attributes = rememberStatsSummary()
)
}
}
}

View file

@ -0,0 +1,90 @@
package com.pixelized.rplexicon.ui.screens.summary.composable
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberCharacteristicsSummary
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@Stable
data class CharacteristicsSummaryUio(
val strength: SummaryRowUio,
val dexterity: SummaryRowUio,
val constitution: SummaryRowUio,
val intelligence: SummaryRowUio,
val wisdom: SummaryRowUio,
val charisma: SummaryRowUio,
)
@Composable
fun CharacteristicsSummary(
modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(start = 8.dp, top = 4.dp, bottom = 4.dp),
characteristics: CharacteristicsSummaryUio,
) {
Column(
modifier = Modifier
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.padding(paddingValues = padding)
.then(other = modifier),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SummaryRow(
row = characteristics.strength,
)
SummaryRow(
row = characteristics.dexterity,
)
SummaryRow(
row = characteristics.constitution,
)
SummaryRow(
row = characteristics.intelligence,
)
SummaryRow(
row = characteristics.wisdom,
)
SummaryRow(
row = characteristics.charisma,
)
Text(
modifier = Modifier.padding(vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
text = stringResource(id = R.string.character_sheet_title_characteristic)
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun CharacteristicsSummaryPreview() {
LexiconTheme {
Surface {
CharacteristicsSummary(
characteristics = rememberCharacteristicsSummary(),
)
}
}
}

View file

@ -0,0 +1,118 @@
package com.pixelized.rplexicon.ui.screens.summary.composable
import android.content.res.Configuration
import androidx.annotation.DrawableRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberClassHeaderSummary
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.lexicon
@Stable
data class ClassHeaderSummaryUio(
val c1: MutableState<Header>,
val c2: MutableState<Header>,
val c3: MutableState<Header>,
val c4: MutableState<Header>,
val c5: MutableState<Header>,
) {
@Stable
data class Header(
val label: String = "",
@DrawableRes val icon: Int = R.drawable.ic_d20_24,
)
}
@Composable
fun ClassHeaderSummary(
modifier: Modifier = Modifier,
header: ClassHeaderSummaryUio,
onClick: (ClassHeaderSummaryUio.Header) -> Unit
) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(space = 1.dp, alignment = Alignment.End),
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(
modifier = Modifier.width(MaterialTheme.lexicon.dimens.summary.cell.width),
onClick = { onClick(header.c1.value) },
) {
Icon(
modifier = Modifier.width(MaterialTheme.lexicon.dimens.summary.cell.width),
painter = painterResource(id = header.c1.value.icon),
contentDescription = null,
)
}
IconButton(
modifier = Modifier.width(MaterialTheme.lexicon.dimens.summary.cell.width),
onClick = { onClick(header.c2.value) },
) {
Icon(
modifier = Modifier.width(MaterialTheme.lexicon.dimens.summary.cell.width),
painter = painterResource(id = header.c2.value.icon),
contentDescription = null,
)
}
IconButton(
modifier = Modifier.width(MaterialTheme.lexicon.dimens.summary.cell.width),
onClick = { onClick(header.c3.value) },
) {
Icon(
modifier = Modifier.width(MaterialTheme.lexicon.dimens.summary.cell.width),
painter = painterResource(id = header.c3.value.icon),
contentDescription = null,
)
}
IconButton(
modifier = Modifier.width(MaterialTheme.lexicon.dimens.summary.cell.width),
onClick = { onClick(header.c4.value) },
) {
Icon(
modifier = Modifier.width(MaterialTheme.lexicon.dimens.summary.cell.width),
painter = painterResource(id = header.c4.value.icon),
contentDescription = null,
)
}
IconButton(
modifier = Modifier.width(MaterialTheme.lexicon.dimens.summary.cell.width),
onClick = { onClick(header.c5.value) },
) {
Icon(
modifier = Modifier.width(MaterialTheme.lexicon.dimens.summary.cell.width),
painter = painterResource(id = header.c5.value.icon),
contentDescription = null,
)
}
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun ClassHeaderSummaryPreview() {
LexiconTheme {
Surface {
ClassHeaderSummary(
modifier = Modifier.fillMaxWidth(),
header = rememberClassHeaderSummary(),
onClick = { }
)
}
}
}

View file

@ -0,0 +1,76 @@
package com.pixelized.rplexicon.ui.screens.summary.composable
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberPassivesSummary
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@Stable
data class PassivesSummaryUio(
val perception: SummaryRowUio,
val investigation: SummaryRowUio,
val insight: SummaryRowUio,
)
@Composable
fun PassivesSummary(
modifier: Modifier = Modifier,
passives: PassivesSummaryUio,
) {
Column(
modifier = Modifier
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.padding(start = 8.dp, top = 4.dp, bottom = 4.dp)
.then(other = modifier),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SummaryRow(
row = passives.perception,
)
SummaryRow(
row = passives.investigation,
)
SummaryRow(
row = passives.insight,
)
Text(
modifier = Modifier.padding(vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
text = stringResource(id = R.string.character_sheet_title_passive)
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun PassivesSummaryPreview() {
LexiconTheme {
Surface {
PassivesSummary(
passives = rememberPassivesSummary(),
)
}
}
}

View file

@ -0,0 +1,136 @@
package com.pixelized.rplexicon.ui.screens.summary.composable
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberProficienciesSummary
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@Stable
data class ProficiencySummaryUio(
val acrobatics: SummaryRowUio,
val animalHandling: SummaryRowUio,
val arcana: SummaryRowUio,
val athletics: SummaryRowUio,
val deception: SummaryRowUio,
val history: SummaryRowUio,
val insight: SummaryRowUio,
val intimidation: SummaryRowUio,
val investigation: SummaryRowUio,
val medicine: SummaryRowUio,
val nature: SummaryRowUio,
val perception: SummaryRowUio,
val performance: SummaryRowUio,
val persuasion: SummaryRowUio,
val religion: SummaryRowUio,
val sleightOfHand: SummaryRowUio,
val stealth: SummaryRowUio,
val survival: SummaryRowUio,
)
@Composable
fun ProficiencySummary(
modifier: Modifier = Modifier,
proficiencies: ProficiencySummaryUio,
) {
Column(
modifier = Modifier
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.padding(start = 8.dp, top = 4.dp, bottom = 4.dp)
.then(other = modifier),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SummaryRow(
row = proficiencies.acrobatics,
)
SummaryRow(
row = proficiencies.arcana,
)
SummaryRow(
row = proficiencies.athletics,
)
SummaryRow(
row = proficiencies.stealth,
)
SummaryRow(
row = proficiencies.animalHandling,
)
SummaryRow(
row = proficiencies.sleightOfHand,
)
SummaryRow(
row = proficiencies.history,
)
SummaryRow(
row = proficiencies.intimidation,
)
SummaryRow(
row = proficiencies.insight,
)
SummaryRow(
row = proficiencies.investigation,
)
SummaryRow(
row = proficiencies.medicine,
)
SummaryRow(
row = proficiencies.nature,
)
SummaryRow(
row = proficiencies.perception,
)
SummaryRow(
row = proficiencies.persuasion,
)
SummaryRow(
row = proficiencies.religion,
)
SummaryRow(
row = proficiencies.performance,
)
SummaryRow(
row = proficiencies.survival,
)
SummaryRow(
row = proficiencies.deception,
)
Text(
modifier = Modifier.padding(vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
text = stringResource(id = R.string.character_sheet_title_proficiencies)
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun ProficiencySummaryPreview() {
LexiconTheme {
Surface {
ProficiencySummary(
proficiencies = rememberProficienciesSummary(),
)
}
}
}

View file

@ -0,0 +1,88 @@
package com.pixelized.rplexicon.ui.screens.summary.composable
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberSavingThrowsSummary
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@Stable
data class SavingThrowsSummaryUio(
val strength: SummaryRowUio,
val dexterity: SummaryRowUio,
val constitution: SummaryRowUio,
val intelligence: SummaryRowUio,
val wisdom: SummaryRowUio,
val charisma: SummaryRowUio,
)
@Composable
fun SavingThrowsSummary(
modifier: Modifier = Modifier,
savingThrows: SavingThrowsSummaryUio
) {
Column(
modifier = Modifier
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.padding(start = 8.dp, top = 4.dp, bottom = 4.dp)
.then(other = modifier),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SummaryRow(
row = savingThrows.strength,
)
SummaryRow(
row = savingThrows.dexterity,
)
SummaryRow(
row = savingThrows.constitution,
)
SummaryRow(
row = savingThrows.intelligence,
)
SummaryRow(
row = savingThrows.wisdom,
)
SummaryRow(
row = savingThrows.charisma,
)
Text(
modifier = Modifier.padding(vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
text = stringResource(id = R.string.character_sheet_title_saving_throws)
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SavingThrowsSummaryPreview() {
LexiconTheme {
Surface {
SavingThrowsSummary(
savingThrows = rememberSavingThrowsSummary(),
)
}
}
}

View file

@ -0,0 +1,136 @@
package com.pixelized.rplexicon.ui.screens.summary.composable
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
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.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.rememberSpellsSummary
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@Stable
data class SpellSummaryUio(
val extra: SummaryRowUio,
val slot1: SummaryRowUio,
val slot2: SummaryRowUio,
val slot3: SummaryRowUio,
val slot4: SummaryRowUio,
val slot5: SummaryRowUio,
val slot6: SummaryRowUio,
val slot7: SummaryRowUio,
val slot8: SummaryRowUio,
val slot9: SummaryRowUio,
val max: State<Int>,
)
@Composable
fun SpellSummary(
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colorScheme.primary,
spells: SpellSummaryUio,
) {
Column(
modifier = Modifier
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.padding(start = 8.dp, top = 4.dp, bottom = 4.dp)
.then(other = modifier),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SummaryRow(
row = spells.extra,
color = color,
)
if (1 <= spells.max.value) {
SummaryRow(
row = spells.slot1,
color = color,
)
}
if (2 <= spells.max.value) {
SummaryRow(
row = spells.slot2,
color = color,
)
}
if (3 <= spells.max.value) {
SummaryRow(
row = spells.slot3,
color = color,
)
}
if (4 <= spells.max.value) {
SummaryRow(
row = spells.slot4,
color = color,
)
}
if (5 <= spells.max.value) {
SummaryRow(
row = spells.slot5,
color = color,
)
}
if (6 <= spells.max.value) {
SummaryRow(
row = spells.slot6,
color = color,
)
}
if (7 <= spells.max.value) {
SummaryRow(
row = spells.slot7,
color = color,
)
}
if (8 <= spells.max.value) {
SummaryRow(
row = spells.slot8,
color = color,
)
}
if (9 <= spells.max.value) {
SummaryRow(
row = spells.slot9,
color = color,
)
}
Text(
modifier = Modifier.padding(vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
text = stringResource(id = R.string.character_sheet_title_spells)
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SpellSummaryPreview() {
LexiconTheme {
Surface {
SpellSummary(
spells = rememberSpellsSummary(),
)
}
}
}

View file

@ -0,0 +1,273 @@
package com.pixelized.rplexicon.ui.screens.summary.composable.common
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
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.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.ui.screens.character.composable.character.MasteryCircle
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryCellUio.Mode
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.lexicon
@Stable
data class SummaryCellUio(
val mode: Mode = Mode.None,
) {
@Stable
sealed class Mode {
data object None : Mode()
@Stable
data class Label(
val label: String,
) : Mode()
@Stable
data class Proficiency(
val label: String,
val multiplier: Int,
) : Mode()
@Stable
data class LabelMax(
val label: String,
val max: String,
) : Mode()
}
}
@Stable
fun none() = SummaryCellUio(Mode.None)
@Stable
fun label(
label: String,
): SummaryCellUio {
return SummaryCellUio(
Mode.Label(label),
)
}
@Stable
fun maxLabel(
label: String,
max: String,
): SummaryCellUio {
return SummaryCellUio(
Mode.LabelMax(
label = label,
max = max,
),
)
}
@Stable
fun proficiency(
label: String,
multiplier: Int,
): SummaryCellUio {
return SummaryCellUio(
Mode.Proficiency(
label = label,
multiplier = multiplier,
),
)
}
@Composable
fun SummaryValue(
modifier: Modifier = Modifier,
style: TextStyle = MaterialTheme.typography.titleMedium,
color: Color = MaterialTheme.colorScheme.onSurface,
cell: State<SummaryCellUio>,
) {
when (val value = cell.value.mode) {
Mode.None -> Box(
modifier = Modifier
.size(size = MaterialTheme.lexicon.dimens.summary.cell)
.then(other = modifier)
)
is Mode.Label -> SummaryLabel(
modifier = modifier,
style = style,
color = color,
value = value,
)
is Mode.LabelMax -> SummaryMaxLabel(
modifier = modifier,
style = style,
color = color,
value = value,
)
is Mode.Proficiency -> SummaryProficiency(
modifier = modifier,
style = style,
color = color,
value = value,
)
}
}
@Composable
private fun SummaryLabel(
modifier: Modifier = Modifier,
style: TextStyle = MaterialTheme.typography.titleMedium,
color: Color = MaterialTheme.colorScheme.onSurface,
value: Mode.Label,
) {
Box(
modifier = Modifier
.size(size = MaterialTheme.lexicon.dimens.summary.cell)
.then(other = modifier),
contentAlignment = Alignment.Center,
) {
AnimatedContent(
targetState = value,
label = "SummaryLabelAnimation",
transitionSpec = animationSpec(),
) {
Text(
modifier = Modifier.width(width = MaterialTheme.lexicon.dimens.summary.cell.width),
text = it.label,
style = style,
color = color,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
maxLines = 1,
)
}
}
}
@Composable
private fun SummaryMaxLabel(
modifier: Modifier = Modifier,
style: TextStyle = MaterialTheme.typography.titleMedium,
color: Color = MaterialTheme.colorScheme.onSurface,
value: Mode.LabelMax,
) {
Box(
modifier = Modifier
.size(size = MaterialTheme.lexicon.dimens.summary.cell)
.then(other = modifier),
contentAlignment = Alignment.Center,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
AnimatedContent(
targetState = value.label,
label = "SummaryMaxLabelAnimation",
transitionSpec = animationSpec(),
) {
Text(
text = it,
style = style,
color = color,
fontWeight = FontWeight.Bold,
)
}
Text(
modifier = Modifier.padding(top = 3.dp),
text = "/${value.max}",
style = MaterialTheme.typography.labelSmall,
fontWeight = FontWeight.Normal,
overflow = TextOverflow.Ellipsis,
maxLines = 1
)
}
}
}
@Composable
private fun SummaryProficiency(
modifier: Modifier = Modifier,
style: TextStyle = MaterialTheme.typography.titleMedium,
color: Color = MaterialTheme.colorScheme.onSurface,
value: Mode.Proficiency,
) {
Row(
modifier = Modifier
.size(size = MaterialTheme.lexicon.dimens.summary.cell)
.padding(start = 4.dp)
.then(other = modifier),
verticalAlignment = Alignment.CenterVertically,
) {
MasteryCircle(
size = MaterialTheme.lexicon.dimens.summary.mastery,
multiplier = value.multiplier,
)
Text(
modifier = Modifier.weight(weight = 1f),
text = value.label,
style = style,
color = color,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
maxLines = 1
)
}
}
@Stable
private fun animationSpec(): AnimatedContentTransitionScope<*>.() -> ContentTransform = {
slideInVertically { it } + fadeIn() togetherWith
slideOutVertically { -it } + fadeOut() using
SizeTransform(clip = false)
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SummaryValuePreview(
@PreviewParameter(SummaryValueProvider::class) preview: State<SummaryCellUio>,
) {
LexiconTheme {
Surface {
SummaryValue(cell = preview)
}
}
}
private class SummaryValueProvider : PreviewParameterProvider<State<SummaryCellUio>> {
override val values: Sequence<State<SummaryCellUio>> = sequenceOf(
mutableStateOf(SummaryCellUio(Mode.None)),
mutableStateOf(SummaryCellUio(Mode.Label(label = "16"))),
mutableStateOf(SummaryCellUio(Mode.LabelMax(label = "16", max = "25"))),
mutableStateOf(SummaryCellUio(Mode.Proficiency(label = "+5", multiplier = 1))),
)
}

View file

@ -0,0 +1,125 @@
package com.pixelized.rplexicon.ui.screens.summary.composable.common
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
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.MutableState
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.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.verticalDivider
@Stable
data class SummaryRowUio(
@StringRes val label: Int,
val c1: MutableState<SummaryCellUio> = mutableStateOf(none()),
val c2: MutableState<SummaryCellUio> = mutableStateOf(none()),
val c3: MutableState<SummaryCellUio> = mutableStateOf(none()),
val c4: MutableState<SummaryCellUio> = mutableStateOf(none()),
val c5: MutableState<SummaryCellUio> = mutableStateOf(none()),
)
@Composable
fun SummaryRow(
modifier: Modifier = Modifier,
style: TextStyle = MaterialTheme.typography.titleMedium,
color: Color = MaterialTheme.colorScheme.onSurface,
row: SummaryRowUio,
) {
Row(
modifier = modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
modifier = Modifier.weight(weight = 1f, fill = false),
style = MaterialTheme.typography.bodySmall,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(id = row.label),
)
Row(
modifier = Modifier.height(intrinsicSize = IntrinsicSize.Min),
) {
Divider(
modifier = Modifier.verticalDivider(),
)
SummaryValue(
cell = row.c1,
style = style,
color = color,
)
Divider(
modifier = Modifier.verticalDivider(),
)
SummaryValue(
cell = row.c2,
style = style,
color = color,
)
Divider(
modifier = Modifier.verticalDivider(),
)
SummaryValue(
cell = row.c3,
style = style,
color = color,
)
Divider(
modifier = Modifier.verticalDivider(),
)
SummaryValue(
cell = row.c4,
style = style,
color = color,
)
Divider(
modifier = Modifier.verticalDivider(),
)
SummaryValue(
cell = row.c5,
style = style,
color = color,
)
}
}
}
@Composable
@Preview
private fun SummaryRowPreview() {
LexiconTheme {
Surface {
SummaryRow(
row = remember {
SummaryRowUio(
label = R.string.character_sheet_proficiency_perception,
c1 = mutableStateOf(proficiency("+6", 1)),
c2 = mutableStateOf(proficiency("+2", 0)),
c3 = mutableStateOf(proficiency("+4", 1)),
c4 = mutableStateOf(proficiency("-1", 0)),
c5 = mutableStateOf(proficiency("-1", 0)),
)
}
)
}
}
}

View file

@ -0,0 +1,67 @@
package com.pixelized.rplexicon.ui.screens.summary.composable.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.CharacteristicsSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.label
@Composable
@Stable
fun rememberCharacteristicsSummary(): CharacteristicsSummaryUio {
return remember {
CharacteristicsSummaryUio(
strength = SummaryRowUio(
label = R.string.character_sheet_stat_strength,
c1 = mutableStateOf(label(label = "16")),
c2 = mutableStateOf(label(label = "14")),
c3 = mutableStateOf(label(label = "14")),
c4 = mutableStateOf(label(label = "8")),
c5 = mutableStateOf(label(label = "8")),
),
dexterity = SummaryRowUio(
label = R.string.character_sheet_stat_dexterity,
c1 = mutableStateOf(label(label = "11")),
c2 = mutableStateOf(label(label = "8")),
c3 = mutableStateOf(label(label = "16")),
c4 = mutableStateOf(label(label = "14")),
c5 = mutableStateOf(label(label = "14")),
),
constitution = SummaryRowUio(
label = R.string.character_sheet_stat_constitution,
c1 = mutableStateOf(label(label = "16")),
c2 = mutableStateOf(label(label = "15")),
c3 = mutableStateOf(label(label = "12")),
c4 = mutableStateOf(label(label = "10")),
c5 = mutableStateOf(label(label = "14")),
),
intelligence = SummaryRowUio(
label = R.string.character_sheet_stat_intelligence,
c1 = mutableStateOf(label(label = "8")),
c2 = mutableStateOf(label(label = "11")),
c3 = mutableStateOf(label(label = "10")),
c4 = mutableStateOf(label(label = "14")),
c5 = mutableStateOf(label(label = "12")),
),
wisdom = SummaryRowUio(
label = R.string.character_sheet_stat_wisdom,
c1 = mutableStateOf(label(label = "14")),
c2 = mutableStateOf(label(label = "16")),
c3 = mutableStateOf(label(label = "15")),
c4 = mutableStateOf(label(label = "12")),
c5 = mutableStateOf(label(label = "10")),
),
charisma = SummaryRowUio(
label = R.string.character_sheet_stat_charisma,
c1 = mutableStateOf(label(label = "10")),
c2 = mutableStateOf(label(label = "14")),
c3 = mutableStateOf(label(label = "8")),
c4 = mutableStateOf(label(label = "17")),
c5 = mutableStateOf(label(label = "18")),
),
)
}
}

View file

@ -0,0 +1,47 @@
package com.pixelized.rplexicon.ui.screens.summary.composable.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.ClassHeaderSummaryUio
@Composable
@Stable
fun rememberClassHeaderSummary(): ClassHeaderSummaryUio {
return remember {
ClassHeaderSummaryUio(
c1 = mutableStateOf(
ClassHeaderSummaryUio.Header(
label = "Brulkai",
icon = R.drawable.ic_class_barbarian_24,
)
),
c2 = mutableStateOf(
ClassHeaderSummaryUio.Header(
label = "Léandre",
icon = R.drawable.ic_class_cleric_24,
)
),
c3 = mutableStateOf(
ClassHeaderSummaryUio.Header(
label = "Nélia",
icon = R.drawable.ic_class_ranger_24,
)
),
c4 = mutableStateOf(
ClassHeaderSummaryUio.Header(
label = "Tigrane",
icon = R.drawable.ic_class_warlock_24,
)
),
c5 = mutableStateOf(
ClassHeaderSummaryUio.Header(
label = "Unathana",
icon = R.drawable.ic_class_bard_24,
)
),
)
}
}

View file

@ -0,0 +1,43 @@
package com.pixelized.rplexicon.ui.screens.summary.composable.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.PassivesSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.label
@Composable
@Stable
fun rememberPassivesSummary(): PassivesSummaryUio {
return remember {
PassivesSummaryUio(
perception = SummaryRowUio(
label = R.string.character_sheet_proficiency_perception,
c1 = mutableStateOf(label(label = "14")),
c2 = mutableStateOf(label(label = "13")),
c3 = mutableStateOf(label(label = "14")),
c4 = mutableStateOf(label(label = "11")),
c5 = mutableStateOf(label(label = "11")),
),
investigation = SummaryRowUio(
label = R.string.character_sheet_proficiency_investigation,
c1 = mutableStateOf(label(label = "9")),
c2 = mutableStateOf(label(label = "10")),
c3 = mutableStateOf(label(label = "10")),
c4 = mutableStateOf(label(label = "14")),
c5 = mutableStateOf(label(label = "13")),
),
insight = SummaryRowUio(
label = R.string.character_sheet_proficiency_insight,
c1 = mutableStateOf(label(label = "14")),
c2 = mutableStateOf(label(label = "17")),
c3 = mutableStateOf(label(label = "14")),
c4 = mutableStateOf(label(label = "11")),
c5 = mutableStateOf(label(label = "11")),
),
)
}
}

View file

@ -0,0 +1,164 @@
package com.pixelized.rplexicon.ui.screens.summary.composable.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.ProficiencySummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryCellUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.proficiency
@Composable
@Stable
fun rememberProficienciesSummary(): ProficiencySummaryUio {
return remember {
ProficiencySummaryUio(
acrobatics = SummaryRowUio(
label = R.string.character_sheet_proficiency_acrobatics,
c1 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+3", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+6", multiplier = 2)),
),
animalHandling = SummaryRowUio(
label = R.string.character_sheet_proficiency_animal_handling,
c1 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+3", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
),
arcana = SummaryRowUio(
label = R.string.character_sheet_proficiency_arcana,
c1 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c5 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
),
athletics = SummaryRowUio(
label = R.string.character_sheet_proficiency_athletics,
c1 = mutableStateOf(proficiency(label = "+5", multiplier = 1)),
c2 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
),
deception = SummaryRowUio(
label = R.string.character_sheet_proficiency_deception,
c1 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+3", multiplier = 1)),
c4 = mutableStateOf(proficiency(label = "+5", multiplier = 1)),
c5 = mutableStateOf(proficiency(label = "+5", multiplier = 0)),
),
history = SummaryRowUio(
label = R.string.character_sheet_proficiency_history,
c1 = mutableStateOf(proficiency(label = "+1", multiplier = 1)),
c2 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+2", multiplier = 1)),
c4 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+3", multiplier = 1)),
),
insight = SummaryRowUio(
label = R.string.character_sheet_proficiency_insight,
c1 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c2 = mutableStateOf(proficiency(label = "+7", multiplier = 1)),
c3 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c4 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
),
intimidation = SummaryRowUio(
label = R.string.character_sheet_proficiency_intimidation,
c1 = mutableStateOf(proficiency(label = "+2", multiplier = 1)),
c2 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+3", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+5", multiplier = 0)),
),
investigation = SummaryRowUio(
label = R.string.character_sheet_proficiency_investigation,
c1 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c5 = mutableStateOf(proficiency(label = "+3", multiplier = 1)),
),
medicine = SummaryRowUio(
label = R.string.character_sheet_proficiency_medicine,
c1 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+7", multiplier = 1)),
c3 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
),
nature = SummaryRowUio(
label = R.string.character_sheet_proficiency_nature,
c1 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
),
perception = SummaryRowUio(
label = R.string.character_sheet_proficiency_perception,
c1 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c2 = mutableStateOf(proficiency(label = "+3", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c4 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
),
performance = SummaryRowUio(
label = R.string.character_sheet_proficiency_performance,
c1 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+3", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+8", multiplier = 2)),
),
persuasion = SummaryRowUio(
label = R.string.character_sheet_proficiency_persuasion,
c1 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+6", multiplier = 1)),
c3 = mutableStateOf(proficiency(label = "+3", multiplier = 1)),
c4 = mutableStateOf(proficiency(label = "+3", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+6", multiplier = 1)),
),
religion = SummaryRowUio(
label = R.string.character_sheet_proficiency_religion,
c1 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c3 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
),
sleightOfHand = SummaryRowUio(
label = R.string.character_sheet_proficiency_sleight_of_hand,
c1 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+3", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c5 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
),
stealth = SummaryRowUio(
label = R.string.character_sheet_proficiency_stealth,
c1 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+5", multiplier = 1)),
c4 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+3", multiplier = 0)),
),
survival = SummaryRowUio(
label = R.string.character_sheet_proficiency_survival,
c1 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c2 = mutableStateOf(proficiency(label = "+3", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c4 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+2", multiplier = 1)),
),
)
}
}

View file

@ -0,0 +1,67 @@
package com.pixelized.rplexicon.ui.screens.summary.composable.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.SavingThrowsSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.proficiency
@Composable
@Stable
fun rememberSavingThrowsSummary(): SavingThrowsSummaryUio {
return remember {
SavingThrowsSummaryUio(
strength = SummaryRowUio(
label = R.string.character_sheet_stat_strength,
c1 = mutableStateOf(proficiency(label = "+6", multiplier = 1)),
c2 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
c4 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
),
dexterity = SummaryRowUio(
label = R.string.character_sheet_stat_dexterity,
c1 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+5", multiplier = 1)),
c4 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+4", multiplier = 1)),
),
constitution = SummaryRowUio(
label = R.string.character_sheet_stat_constitution,
c1 = mutableStateOf(proficiency(label = "+6", multiplier = 1)),
c2 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
),
intelligence = SummaryRowUio(
label = R.string.character_sheet_stat_intelligence,
c1 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c3 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c5 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
),
wisdom = SummaryRowUio(
label = R.string.character_sheet_stat_wisdom,
c1 = mutableStateOf(proficiency(label = "+3", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+7", multiplier = 1)),
c3 = mutableStateOf(proficiency(label = "+2", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+3", multiplier = 1)),
c5 = mutableStateOf(proficiency(label = "+0", multiplier = 0)),
),
charisma = SummaryRowUio(
label = R.string.character_sheet_stat_charisma,
c1 = mutableStateOf(proficiency(label = "+1", multiplier = 0)),
c2 = mutableStateOf(proficiency(label = "+6", multiplier = 1)),
c3 = mutableStateOf(proficiency(label = "-1", multiplier = 0)),
c4 = mutableStateOf(proficiency(label = "+5", multiplier = 1)),
c5 = mutableStateOf(proficiency(label = "+6", multiplier = 1)),
),
)
}
}

View file

@ -0,0 +1,61 @@
package com.pixelized.rplexicon.ui.screens.summary.composable.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.SpellSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.label
import com.pixelized.rplexicon.ui.screens.summary.composable.common.maxLabel
@Composable
@Stable
fun rememberSpellsSummary(): SpellSummaryUio {
return remember {
SpellSummaryUio(
extra = SummaryRowUio(
label = R.string.character_sheet_title_skills,
c1 = mutableStateOf(label(label = "2")),
c2 = mutableStateOf(label(label = "1")),
c5 = mutableStateOf(label(label = "4")),
),
slot1 = SummaryRowUio(
label = R.string.character_sheet_title_spell_slot_1,
c2 = mutableStateOf(maxLabel(label = "3", max = "4")),
c3 = mutableStateOf(maxLabel(label = "3", max = "3")),
c5 = mutableStateOf(maxLabel(label = "2", max = "4")),
),
slot2 = SummaryRowUio(
label = R.string.character_sheet_title_spell_slot_2,
c2 = mutableStateOf(maxLabel(label = "1", max = "2")),
c4 = mutableStateOf(maxLabel(label = "2", max = "2")),
c5 = mutableStateOf(maxLabel(label = "1", max = "2")),
),
slot3 = SummaryRowUio(
label = R.string.character_sheet_title_spell_slot_3,
),
slot4 = SummaryRowUio(
label = R.string.character_sheet_title_spell_slot_4,
),
slot5 = SummaryRowUio(
label = R.string.character_sheet_title_spell_slot_5,
),
slot6 = SummaryRowUio(
label = R.string.character_sheet_title_spell_slot_6,
),
slot7 = SummaryRowUio(
label = R.string.character_sheet_title_spell_slot_7,
),
slot8 = SummaryRowUio(
label = R.string.character_sheet_title_spell_slot_8,
),
slot9 = SummaryRowUio(
label = R.string.character_sheet_title_spell_slot_9,
),
max = mutableIntStateOf(2),
)
}
}

View file

@ -0,0 +1,51 @@
package com.pixelized.rplexicon.ui.screens.summary.composable.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.summary.composable.AttributesSummaryUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
import com.pixelized.rplexicon.ui.screens.summary.composable.common.label
@Composable
@Stable
fun rememberStatsSummary(): AttributesSummaryUio {
return remember {
AttributesSummaryUio(
hp = SummaryRowUio(
label = R.string.character_sheet_title_hp,
c1 = mutableStateOf(label(label = "25")),
c2 = mutableStateOf(label(label = "21")),
c3 = mutableStateOf(label(label = "25")),
c4 = mutableStateOf(label(label = "20")),
c5 = mutableStateOf(label(label = "23")),
),
ac = SummaryRowUio(
label = R.string.character_sheet_title_ca,
c1 = mutableStateOf(label(label = "16")),
c2 = mutableStateOf(label(label = "15")),
c3 = mutableStateOf(label(label = "15")),
c4 = mutableStateOf(label(label = "13")),
c5 = mutableStateOf(label(label = "13")),
),
dc = SummaryRowUio(
label = R.string.character_sheet_title_dc,
c1 = mutableStateOf(label(label = "")),
c2 = mutableStateOf(label(label = "15")),
c3 = mutableStateOf(label(label = "12")),
c4 = mutableStateOf(label(label = "13")),
c5 = mutableStateOf(label(label = "14")),
),
speed = SummaryRowUio(
label = R.string.character_sheet_title_speed,
c1 = mutableStateOf(label(label = "10m")),
c2 = mutableStateOf(label(label = "10m")),
c3 = mutableStateOf(label(label = "13m")),
c4 = mutableStateOf(label(label = "10m")),
c5 = mutableStateOf(label(label = "10m")),
),
)
}
}

View file

@ -5,6 +5,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import kotlin.math.sqrt
@ -16,6 +17,7 @@ data class LexiconDimens(
val itemListPadding: PaddingValues,
val handle: Handle,
val map: Map,
val summary: Summary,
) {
@Stable
@Immutable
@ -34,6 +36,15 @@ data class LexiconDimens(
val crossStrokePx: Int,
val shadowDropPx: Int,
)
@Stable
@Immutable
data class Summary(
val cell: DpSize,
val mastery: Dp,
val label: Dp,
val space: Dp,
)
}
fun lexiconDimen(
@ -56,10 +67,17 @@ fun lexiconDimen(
crossStrokePx = with(density) { 2.dp.roundToPx() },
shadowDropPx = with(density) { 2.dp.roundToPx() },
),
summary: LexiconDimens.Summary = LexiconDimens.Summary(
cell = DpSize(width = 52.dp, height = 32.dp),
mastery = 12.dp,
label = 32.dp,
space = 4.dp,
),
) = LexiconDimens(
item = itemHeight,
detailPadding = detailPadding,
itemListPadding = itemListPadding,
handle = handle,
map = map,
summary = summary,
)

View file

@ -10,10 +10,12 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.DividerDefaults
import androidx.compose.material3.MaterialTheme
@ -172,3 +174,7 @@ fun Modifier.lexiconShadow() = composed {
shadow(elevation = 4.dp)
}
}
fun Modifier.verticalDivider() = this
.fillMaxHeight()
.width(1.dp)

View file

@ -25,6 +25,9 @@ val List<Alteration.Status>?.disadvantage: Boolean
val List<Alteration.Status>?.fail: Boolean
get() = this?.any { it.fail } ?: false
val List<Alteration.Status>?.passivesBonus: Int
get() = (if (advantage) 5 else 0) - (if (disadvantage) 5 else 0)
// force a roll to hit a 20
val List<Alteration.Status>?.critical: Boolean
get() = this?.any { it.critical } ?: false

View file

@ -86,6 +86,7 @@
<string name="map_destination">Destinations :</string>
<string name="map_illustrations">Illustrations :</string>
<string name="character_sheet_summary_title">Détails des personnages</string>
<string name="character_sheet_refresh_label">Rafraichir</string>
<string name="character_sheet_tab_proficiency">Talents</string>
<string name="character_sheet_tab_inventory">Inventaire</string>
@ -100,6 +101,10 @@
<string name="character_sheet_title_inspiration">Inspiration</string>
<string name="character_sheet_title_conduit">Conduit Divin</string>
<string name="character_sheet_title_initiative">Initiative</string>
<string name="character_sheet_title_characteristic">Caractéristiques</string>
<string name="character_sheet_title_attribute">Attributs</string>
<string name="character_sheet_title_passive">Talents passifs</string>
<string name="character_sheet_title_spells">Sortilèges</string>
<string name="character_sheet_title_saving_throws">Jet de sauvegarde</string>
<string name="character_sheet_title_proficiencies">Talents</string>
<string name="character_sheet_martial_masteries">Maîtrises martiales :</string>
@ -110,6 +115,15 @@
<string name="character_sheet_title_objects">Objets</string>
<string name="character_sheet_title_inventory">Inventaire</string>
<string name="character_sheet_title_equipment">Equipement</string>
<string name="character_sheet_title_spell_slot_1">Sort de niveau 1</string>
<string name="character_sheet_title_spell_slot_2">Sort de niveau 2</string>
<string name="character_sheet_title_spell_slot_3">Sort de niveau 3</string>
<string name="character_sheet_title_spell_slot_4">Sort de niveau 4</string>
<string name="character_sheet_title_spell_slot_5">Sort de niveau 5</string>
<string name="character_sheet_title_spell_slot_6">Sort de niveau 6</string>
<string name="character_sheet_title_spell_slot_7">Sort de niveau 7</string>
<string name="character_sheet_title_spell_slot_8">Sort de niveau 8</string>
<string name="character_sheet_title_spell_slot_9">Sort de niveau 9</string>
<string name="character_sheet_stat_level">Niveau</string>
<string name="character_sheet_stat_strength">Force</string>
<string name="character_sheet_stat_strength_short">FOR</string>

View file

@ -86,6 +86,7 @@
<string name="map_destination">Destinations:</string>
<string name="map_illustrations">Illustrations:</string>
<string name="character_sheet_summary_title">Characters sheets details</string>
<string name="character_sheet_refresh_label">Refresh</string>
<string name="character_sheet_tab_proficiency">Proficiencies</string>
<string name="character_sheet_tab_inventory">Inventory</string>
@ -102,6 +103,10 @@
<string name="character_sheet_title_initiative">Initiative</string>
<string name="character_sheet_title_saving_throws">Saving Throws</string>
<string name="character_sheet_title_proficiencies">Proficiencies</string>
<string name="character_sheet_title_characteristic">Characteristics</string>
<string name="character_sheet_title_attribute">Attributes</string>
<string name="character_sheet_title_passive">Passives proficiencies</string>
<string name="character_sheet_title_spells">Spells</string>
<string name="character_sheet_martial_masteries">Martial masteries:</string>
<string name="character_sheet_languages_masteries">Languages masteries:</string>
<string name="character_sheet_others_masteries">Others masteries:</string>
@ -110,6 +115,15 @@
<string name="character_sheet_title_skills">Skills</string>
<string name="character_sheet_title_inventory">Inventory</string>
<string name="character_sheet_title_equipment">Equipment</string>
<string name="character_sheet_title_spell_slot_1">Spell slot 1</string>
<string name="character_sheet_title_spell_slot_2">Spell slot 2</string>
<string name="character_sheet_title_spell_slot_3">Spell slot 3</string>
<string name="character_sheet_title_spell_slot_4">Spell slot 4</string>
<string name="character_sheet_title_spell_slot_5">Spell slot 5</string>
<string name="character_sheet_title_spell_slot_6">Spell slot 6</string>
<string name="character_sheet_title_spell_slot_7">Spell slot 7</string>
<string name="character_sheet_title_spell_slot_8">Spell slot 8</string>
<string name="character_sheet_title_spell_slot_9">Spell slot 9</string>
<string name="character_sheet_stat_level">Level</string>
<string name="character_sheet_stat_strength">Strength</string>
<string name="character_sheet_stat_strength_short">STR</string>