From ff105e2f8160faec58a0a1c1d79a9b7e3dc53e21 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Tue, 19 Dec 2023 15:27:58 +0100 Subject: [PATCH] GameMaster summary screen --- .../rplexicon/data/model/CharacterSheet.kt | 29 +- .../data/model/CharacterSheetFireMap.kt | 17 + .../authentication/FirebaseRepository.kt | 31 + .../rplexicon/ui/navigation/HomeNavHost.kt | 13 +- .../rplexicon/ui/navigation/ScreenNavHost.kt | 3 + .../screens/ComposableSummaryScreen.kt | 27 + .../composable/character/MasteryCircle.kt | 36 +- .../factory/CharacterSheetUioFactory.kt | 99 ++- .../pages/proficiency/ProficiencyPage.kt | 2 +- .../pages/proficiency/ProficiencyViewModel.kt | 2 +- .../ui/screens/lexicon/list/LexiconItem.kt | 6 +- .../ui/screens/summary/SummaryFactory.kt | 598 ++++++++++++++++++ .../ui/screens/summary/SummaryScreen.kt | 288 +++++++++ .../ui/screens/summary/SummaryViewModel.kt | 13 + .../summary/composable/AttributesSummary.kt | 81 +++ .../composable/CharacteristicsSummary.kt | 90 +++ .../summary/composable/ClassHeaderSummary.kt | 118 ++++ .../summary/composable/PassivesSummary.kt | 76 +++ .../summary/composable/ProficiencySummary.kt | 136 ++++ .../summary/composable/SavingThrowsSummary.kt | 88 +++ .../summary/composable/SpellSummary.kt | 136 ++++ .../summary/composable/common/SummaryCell.kt | 273 ++++++++ .../summary/composable/common/SummaryRow.kt | 125 ++++ .../preview/rememberCharacteristicsSummary.kt | 67 ++ .../preview/rememberClassHeaderSummary.kt | 47 ++ .../preview/rememberPassivesSummary.kt | 43 ++ .../preview/rememberProficienciesSummary.kt | 164 +++++ .../preview/rememberSavingThrowsSummary.kt | 67 ++ .../preview/rememberSpellsSummary.kt | 61 ++ .../preview/rememberStatsSummary.kt | 51 ++ .../rplexicon/ui/theme/dimen/LexiconDimens.kt | 18 + .../utilitary/extentions/ModifierEx.kt | 8 +- .../extentions/local/AlterationEx.kt | 3 + app/src/main/res/values-fr/strings.xml | 14 + app/src/main/res/values/strings.xml | 14 + 35 files changed, 2770 insertions(+), 74 deletions(-) create mode 100644 app/src/main/java/com/pixelized/rplexicon/data/model/CharacterSheetFireMap.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/ComposableSummaryScreen.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryFactory.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryScreen.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryViewModel.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/AttributesSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/CharacteristicsSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/ClassHeaderSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/PassivesSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/ProficiencySummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/SavingThrowsSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/SpellSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/common/SummaryCell.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/common/SummaryRow.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberCharacteristicsSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberClassHeaderSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberPassivesSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberProficienciesSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberSavingThrowsSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberSpellsSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberStatsSummary.kt diff --git a/app/src/main/java/com/pixelized/rplexicon/data/model/CharacterSheet.kt b/app/src/main/java/com/pixelized/rplexicon/data/model/CharacterSheet.kt index 70e582e..2c82a72 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/model/CharacterSheet.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/model/CharacterSheet.kt @@ -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), } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/model/CharacterSheetFireMap.kt b/app/src/main/java/com/pixelized/rplexicon/data/model/CharacterSheetFireMap.kt new file mode 100644 index 0000000..aa0e911 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/data/model/CharacterSheetFireMap.kt @@ -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 = emptyMap(), +) { + companion object { + private const val CHARACTERS = "Characters" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/repository/authentication/FirebaseRepository.kt b/app/src/main/java/com/pixelized/rplexicon/data/repository/authentication/FirebaseRepository.kt index c5fdafc..0ba8fc3 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/repository/authentication/FirebaseRepository.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/repository/authentication/FirebaseRepository.kt @@ -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 { + 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 { return callbackFlow { // reference to the node diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/navigation/HomeNavHost.kt b/app/src/main/java/com/pixelized/rplexicon/ui/navigation/HomeNavHost.kt index 160cb79..81495f0 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/navigation/HomeNavHost.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/navigation/HomeNavHost.kt @@ -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 { @@ -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 = { diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/navigation/ScreenNavHost.kt b/app/src/main/java/com/pixelized/rplexicon/ui/navigation/ScreenNavHost.kt index 1aa0a41..96e903f 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/navigation/ScreenNavHost.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/navigation/ScreenNavHost.kt @@ -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 { @@ -58,6 +59,8 @@ fun ScreenNavHost( composableLocationDetail() composableCharacterSheet() composableSpellDetail() + + composableSummary() } } } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/ComposableSummaryScreen.kt b/app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/ComposableSummaryScreen.kt new file mode 100644 index 0000000..aeee416 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/ComposableSummaryScreen.kt @@ -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) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/MasteryCircle.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/MasteryCircle.kt index 7c3c933..08e9db5 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/MasteryCircle.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/MasteryCircle.kt @@ -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 { + override val values: Sequence = sequenceOf(0, 1, 2, 3) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/CharacterSheetUioFactory.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/CharacterSheetUioFactory.kt index 7e23ad2..b0760de 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/CharacterSheetUioFactory.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/CharacterSheetUioFactory.kt @@ -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>, + status: Map>, ): 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( diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyPage.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyPage.kt index c911208..1524a10 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyPage.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyPage.kt @@ -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 = { diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyViewModel.kt index 49957cf..735a1a1 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyViewModel.kt @@ -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 diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/lexicon/list/LexiconItem.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/lexicon/list/LexiconItem.kt index 6f2a83d..fd21124 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/lexicon/list/LexiconItem.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/lexicon/list/LexiconItem.kt @@ -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 diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryFactory.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryFactory.kt new file mode 100644 index 0000000..cd5ddf6 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryFactory.kt @@ -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 + lateinit var fires: Map + + operator fun component1() = sheets + operator fun component2() = fires + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryScreen.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryScreen.kt new file mode 100644 index 0000000..bc67eb1 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryScreen.kt @@ -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, + val header: ClassHeaderSummaryUio, + val statsVisibility: State, + val stats: AttributesSummaryUio, + val characteristicsVisibility: State, + val characteristics: CharacteristicsSummaryUio, + val savingThrowsVisibility: State, + val savingThrows: SavingThrowsSummaryUio, + val proficienciesVisibility: State, + val proficiencies: ProficiencySummaryUio, + val passivesVisibility: State, + val passives: PassivesSummaryUio, + val spellsVisibility: State, + 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 = { }, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryViewModel.kt new file mode 100644 index 0000000..375b1b1 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/SummaryViewModel.kt @@ -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) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/AttributesSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/AttributesSummary.kt new file mode 100644 index 0000000..2f9e85e --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/AttributesSummary.kt @@ -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() + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/CharacteristicsSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/CharacteristicsSummary.kt new file mode 100644 index 0000000..61c2068 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/CharacteristicsSummary.kt @@ -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(), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/ClassHeaderSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/ClassHeaderSummary.kt new file mode 100644 index 0000000..cf3a59e --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/ClassHeaderSummary.kt @@ -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
, + val c2: MutableState
, + val c3: MutableState
, + val c4: MutableState
, + val c5: MutableState
, +) { + @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 = { } + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/PassivesSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/PassivesSummary.kt new file mode 100644 index 0000000..c92afc7 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/PassivesSummary.kt @@ -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(), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/ProficiencySummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/ProficiencySummary.kt new file mode 100644 index 0000000..a065cd3 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/ProficiencySummary.kt @@ -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(), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/SavingThrowsSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/SavingThrowsSummary.kt new file mode 100644 index 0000000..3b38538 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/SavingThrowsSummary.kt @@ -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(), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/SpellSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/SpellSummary.kt new file mode 100644 index 0000000..e4bd03e --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/SpellSummary.kt @@ -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, +) + +@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(), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/common/SummaryCell.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/common/SummaryCell.kt new file mode 100644 index 0000000..c17689b --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/common/SummaryCell.kt @@ -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, +) { + 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, +) { + LexiconTheme { + Surface { + SummaryValue(cell = preview) + } + } +} + +private class SummaryValueProvider : PreviewParameterProvider> { + override val values: Sequence> = 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))), + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/common/SummaryRow.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/common/SummaryRow.kt new file mode 100644 index 0000000..95661c6 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/common/SummaryRow.kt @@ -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 = mutableStateOf(none()), + val c2: MutableState = mutableStateOf(none()), + val c3: MutableState = mutableStateOf(none()), + val c4: MutableState = mutableStateOf(none()), + val c5: MutableState = 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)), + ) + } + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberCharacteristicsSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberCharacteristicsSummary.kt new file mode 100644 index 0000000..74e3319 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberCharacteristicsSummary.kt @@ -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")), + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberClassHeaderSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberClassHeaderSummary.kt new file mode 100644 index 0000000..26df5b7 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberClassHeaderSummary.kt @@ -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, + ) + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberPassivesSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberPassivesSummary.kt new file mode 100644 index 0000000..0740314 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberPassivesSummary.kt @@ -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")), + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberProficienciesSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberProficienciesSummary.kt new file mode 100644 index 0000000..2abd604 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberProficienciesSummary.kt @@ -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)), + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberSavingThrowsSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberSavingThrowsSummary.kt new file mode 100644 index 0000000..118fb92 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberSavingThrowsSummary.kt @@ -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)), + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberSpellsSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberSpellsSummary.kt new file mode 100644 index 0000000..e532309 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberSpellsSummary.kt @@ -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), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberStatsSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberStatsSummary.kt new file mode 100644 index 0000000..d1e392e --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/rememberStatsSummary.kt @@ -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")), + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/theme/dimen/LexiconDimens.kt b/app/src/main/java/com/pixelized/rplexicon/ui/theme/dimen/LexiconDimens.kt index 6ea9967..579b721 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/theme/dimen/LexiconDimens.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/theme/dimen/LexiconDimens.kt @@ -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, ) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/ModifierEx.kt b/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/ModifierEx.kt index 83fb244..429af35 100644 --- a/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/ModifierEx.kt +++ b/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/ModifierEx.kt @@ -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 @@ -171,4 +173,8 @@ fun Modifier.lexiconShadow() = composed { } else { shadow(elevation = 4.dp) } -} \ No newline at end of file +} + +fun Modifier.verticalDivider() = this + .fillMaxHeight() + .width(1.dp) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/local/AlterationEx.kt b/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/local/AlterationEx.kt index 784249b..701ff5d 100644 --- a/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/local/AlterationEx.kt +++ b/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/local/AlterationEx.kt @@ -25,6 +25,9 @@ val List?.disadvantage: Boolean val List?.fail: Boolean get() = this?.any { it.fail } ?: false +val List?.passivesBonus: Int + get() = (if (advantage) 5 else 0) - (if (disadvantage) 5 else 0) + // force a roll to hit a 20 val List?.critical: Boolean get() = this?.any { it.critical } ?: false diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 74e63f2..bf4d666 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -86,6 +86,7 @@ Destinations : Illustrations : + Détails des personnages Rafraichir Talents Inventaire @@ -100,6 +101,10 @@ Inspiration Conduit Divin Initiative + Caractéristiques + Attributs + Talents passifs + Sortilèges Jet de sauvegarde Talents Maîtrises martiales : @@ -110,6 +115,15 @@ Objets Inventaire Equipement + Sort de niveau 1 + Sort de niveau 2 + Sort de niveau 3 + Sort de niveau 4 + Sort de niveau 5 + Sort de niveau 6 + Sort de niveau 7 + Sort de niveau 8 + Sort de niveau 9 Niveau Force FOR diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 498230e..f10810c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -86,6 +86,7 @@ Destinations: Illustrations: + Characters sheets details Refresh Proficiencies Inventory @@ -102,6 +103,10 @@ Initiative Saving Throws Proficiencies + Characteristics + Attributes + Passives proficiencies + Spells Martial masteries: Languages masteries: Others masteries: @@ -110,6 +115,15 @@ Skills Inventory Equipment + Spell slot 1 + Spell slot 2 + Spell slot 3 + Spell slot 4 + Spell slot 5 + Spell slot 6 + Spell slot 7 + Spell slot 8 + Spell slot 9 Level Strength STR