From c8e7fee2c27fc4b3a559b240fefcd7a064d05fd9 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Fri, 5 Jan 2024 16:26:41 +0100 Subject: [PATCH] Add alteration to the summary page. --- .../character/AlterationRepository.kt | 10 + .../alteration/AlterationDetailHandler.kt | 27 +++ .../pages/alteration/AlterationPage.kt | 20 +- .../ui/screens/summary/SummaryScreen.kt | 15 ++ .../summary/composable/StatusSummary.kt | 217 ++++++++++++++++++ .../statistic/rememberStatisticSummary.kt | 3 + .../statistic/rememberStatusSummary.kt | 45 ++++ .../pages/statistic/StatisticSummary.kt | 31 ++- .../pages/statistic/StatisticViewModel.kt | 37 ++- .../summary/pages/statistic/SummaryFactory.kt | 87 +++++++ .../res/drawable/ic_status_blinded_24dp.xml | 9 + .../res/drawable/ic_status_charmed_24dp.xml | 9 + .../res/drawable/ic_status_deafened_24dp.xml | 9 + .../drawable/ic_status_frightened_24dp.xml | 9 + .../res/drawable/ic_status_grappled_24dp.xml | 9 + .../drawable/ic_status_incapacitated_24dp.xml | 9 + .../res/drawable/ic_status_invisible_24dp.xml | 9 + .../res/drawable/ic_status_paralyzed_24dp.xml | 9 + .../res/drawable/ic_status_petrified_24dp.xml | 9 + .../res/drawable/ic_status_poisoned_24dp.xml | 9 + .../res/drawable/ic_status_prone_24dp.xml | 9 + .../drawable/ic_status_restrained_24dp.xml | 9 + .../res/drawable/ic_status_stunned_24dp.xml | 9 + .../drawable/ic_status_unconscious_24dp.xml | 9 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 26 files changed, 596 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/alteration/AlterationDetailHandler.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/StatusSummary.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/statistic/rememberStatusSummary.kt create mode 100644 app/src/main/res/drawable/ic_status_blinded_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_charmed_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_deafened_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_frightened_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_grappled_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_incapacitated_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_invisible_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_paralyzed_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_petrified_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_poisoned_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_prone_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_restrained_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_stunned_24dp.xml create mode 100644 app/src/main/res/drawable/ic_status_unconscious_24dp.xml diff --git a/app/src/main/java/com/pixelized/rplexicon/data/repository/character/AlterationRepository.kt b/app/src/main/java/com/pixelized/rplexicon/data/repository/character/AlterationRepository.kt index 06b15d4..a80c055 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/repository/character/AlterationRepository.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/repository/character/AlterationRepository.kt @@ -58,6 +58,16 @@ class AlterationRepository @Inject constructor( } } + /** + * get a list of Active [Alteration] for a character + * @return a list of alterations. + */ + fun getActiveAlterations(character: String): List { + return _assignedAlterations.value[character]?.filter { alteration -> + fireRepository.status.value[character + alteration.name] == true + } ?: emptyList() + } + /** * get a map of [Property] and [Alteration.Status] for a given player if the alteration is active. * @param character the character name diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/alteration/AlterationDetailHandler.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/alteration/AlterationDetailHandler.kt new file mode 100644 index 0000000..f2e9793 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/alteration/AlterationDetailHandler.kt @@ -0,0 +1,27 @@ +package com.pixelized.rplexicon.ui.screens.character.composable.alteration + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.remember +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationDetail +import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationDetailUio + +@Composable +fun AlterationDetailHandler( + detail: State, + onDismissRequest: () -> Unit, +) { + detail.value?.let { + Dialog( + properties = remember { DialogProperties(usePlatformDefaultWidth = false) }, + onDismissRequest = onDismissRequest, + ) { + AlterationDetail( + detail = it, + onClose = onDismissRequest, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationPage.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationPage.kt index 9da9667..a963bc5 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationPage.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationPage.kt @@ -1,3 +1,4 @@ + package com.pixelized.rplexicon.ui.screens.character.pages.alteration import android.content.res.Configuration.UI_MODE_NIGHT_NO @@ -23,6 +24,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.pixelized.rplexicon.ui.composable.CategoryHeader import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItem import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItemUio +import com.pixelized.rplexicon.ui.screens.character.composable.alteration.AlterationDetailHandler import com.pixelized.rplexicon.ui.screens.rolls.preview.rememberRollAlterations import com.pixelized.rplexicon.ui.theme.LexiconTheme import kotlinx.coroutines.launch @@ -91,24 +93,6 @@ fun AlterationPageContent( } } -@Composable -private fun AlterationDetailHandler( - detail: State, - onDismissRequest: () -> Unit, -) { - detail.value?.let { - Dialog( - properties = remember { DialogProperties(usePlatformDefaultWidth = false) }, - onDismissRequest = onDismissRequest, - ) { - AlterationDetail( - detail = it, - onClose = onDismissRequest, - ) - } - } -} - @Composable @Preview(uiMode = UI_MODE_NIGHT_NO) @Preview(uiMode = UI_MODE_NIGHT_YES) 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 index 1a1d84c..188847b 100644 --- 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 @@ -43,6 +43,7 @@ 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.character.composable.alteration.AlterationDetailHandler import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCard import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio import com.pixelized.rplexicon.ui.screens.summary.pages.statistic.StatisticSummary @@ -66,6 +67,7 @@ fun SummaryScreen( val extendProficiencies = rememberSaveable { mutableStateOf(true) } val extendPassives = rememberSaveable { mutableStateOf(true) } val extendSpells = rememberSaveable { mutableStateOf(true) } + val extendStatus = rememberSaveable { mutableStateOf(true) } Surface( modifier = Modifier.fillMaxSize(), @@ -88,6 +90,7 @@ fun SummaryScreen( extendProficiencies = extendProficiencies, extendPassives = extendPassives, extendSpells = extendSpells, + extendStatus = extendStatus, onClass = { screen.navigateToCharacterSheet(name = it.label) }, onDice = { statisticsViewModel.showDetail(dice = it) }, onAttribute = { extendAttribute.value = it }, @@ -96,6 +99,13 @@ fun SummaryScreen( onProficiencies = { extendProficiencies.value = it }, onPassives = { extendPassives.value = it }, onSpells = { extendSpells.value = it }, + onStatus = { extendStatus.value = it }, + onAlteration = { + statisticsViewModel.showAlterationDetail( + name = it.id, + character = it.character, + ) + } ) }, ) @@ -108,6 +118,11 @@ fun SummaryScreen( detail = statisticsViewModel.detail, onDetail = { statisticsViewModel.hideDetail() }, ) + + AlterationDetailHandler( + detail = statisticsViewModel.alterationDetail, + onDismissRequest = { statisticsViewModel.hideAlterationDetail() }, + ) } KeepOnScreen() diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/StatusSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/StatusSummary.kt new file mode 100644 index 0000000..33e75f0 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/StatusSummary.kt @@ -0,0 +1,217 @@ +package com.pixelized.rplexicon.ui.screens.summary.composable + +import android.content.res.Configuration +import androidx.annotation.DrawableRes +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +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.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CutCornerShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.minimumInteractiveComponentSize +import androidx.compose.material3.Divider +import androidx.compose.material3.Icon +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.State +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +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.preview.statistic.rememberStatusSummary +import com.pixelized.rplexicon.ui.theme.LexiconTheme +import com.pixelized.rplexicon.utilitary.extentions.ddBorder +import com.pixelized.rplexicon.utilitary.extentions.lexicon +import com.pixelized.rplexicon.utilitary.extentions.verticalDivider + +@Stable +data class StatusSummaryUio( + val max: MutableState, + val c1: MutableState>, + val c2: MutableState>, + val c3: MutableState>, + val c4: MutableState>, + val c5: MutableState>, +) { + operator fun get(index: Int): MutableState>? { + return when (index) { + 0 -> c1 + 1 -> c2 + 2 -> c3 + 3 -> c4 + 4 -> c5 + else -> null + } + } + + @Stable + data class Status( + val character: String, + val id: String, + @DrawableRes val icon: Int, + ) +} + +@Composable +fun StatusSummary( + modifier: Modifier = Modifier, + status: StatusSummaryUio, + extended: State, + onStatus: (StatusSummaryUio.Status) -> Unit, + onClick: () -> Unit, +) { + Column( + modifier = Modifier + .ddBorder( + inner = remember { RoundedCornerShape(size = 8.dp) }, + outline = remember { CutCornerShape(size = 16.dp) }, + ) + .clickable(onClick = onClick) + .padding(start = 8.dp, top = 4.dp, bottom = 4.dp) + .then(other = modifier), + horizontalAlignment = Alignment.End, + ) { + AnimatedVisibility( + visible = extended.value, + ) { + Column(modifier = Modifier.animateContentSize()) { + for (index in 0 until status.max.value) { + Row( + modifier = Modifier.height(intrinsicSize = IntrinsicSize.Min), + ) { + val c1 by remember { + derivedStateOf { + status.c1.value.getOrNull(index) + } + } + Divider( + modifier = Modifier.verticalDivider(), + ) + SummaryIcon( + icon = c1?.icon, + onClick = { c1?.let(onStatus) } + ) + + val c2 by remember { + derivedStateOf { + status.c2.value.getOrNull(index) + } + } + Divider( + modifier = Modifier.verticalDivider(), + ) + SummaryIcon( + icon = c2?.icon, + onClick = { c2?.let(onStatus) } + ) + + val c3 by remember { + derivedStateOf { + status.c3.value.getOrNull(index) + } + } + Divider( + modifier = Modifier.verticalDivider(), + ) + SummaryIcon( + icon = c3?.icon, + onClick = { c3?.let(onStatus) } + ) + + val c4 by remember { + derivedStateOf { + status.c4.value.getOrNull(index) + } + } + Divider( + modifier = Modifier.verticalDivider(), + ) + SummaryIcon( + icon = c4?.icon, + onClick = { c4?.let(onStatus) } + ) + + val c5 by remember { + derivedStateOf { + status.c5.value.getOrNull(index) + } + } + Divider( + modifier = Modifier.verticalDivider(), + ) + SummaryIcon( + icon = c5?.icon, + onClick = { c5?.let(onStatus) } + ) + } + } + } + } + Text( + modifier = Modifier + .padding(vertical = 4.dp) + .align(alignment = Alignment.CenterHorizontally), + style = MaterialTheme.typography.labelSmall, + text = stringResource(id = R.string.character_sheet_title_status) + ) + } +} + + +@Composable +fun SummaryIcon( + modifier: Modifier = Modifier, + @DrawableRes icon: Int?, + onClick: () -> Unit, +) { + Box( + modifier = Modifier + .clickable(enabled = icon != null, onClick = onClick) + .size(size = MaterialTheme.lexicon.dimens.summary.cell) + .then(other = modifier), + contentAlignment = Alignment.Center, + ) { + icon?.let { + Icon( + painter = painterResource(id = it), + contentDescription = null, + ) + } + } +} + + +@Composable +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun StatusSummaryPreview() { + LexiconTheme { + Surface { + StatusSummary( + modifier = Modifier.fillMaxWidth(), + status = rememberStatusSummary(), + extended = remember { mutableStateOf(true) }, + onStatus = { }, + onClick = { }, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/statistic/rememberStatisticSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/statistic/rememberStatisticSummary.kt index 5e040a5..20d7435 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/statistic/rememberStatisticSummary.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/statistic/rememberStatisticSummary.kt @@ -16,6 +16,7 @@ fun rememberStatisticSummary(): StatisticSummaryUio { val proficiencies = rememberProficienciesSummary() val passives = rememberPassivesSummary() val spells = rememberSpellsSummary() + val status = rememberStatusSummary() return remember { StatisticSummaryUio( @@ -33,6 +34,8 @@ fun rememberStatisticSummary(): StatisticSummaryUio { passives = passives, spellsVisibility = mutableStateOf(true), spells = spells, + statusVisibility = mutableStateOf(true), + status = status, ) } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/statistic/rememberStatusSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/statistic/rememberStatusSummary.kt new file mode 100644 index 0000000..cd3c641 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/composable/preview/statistic/rememberStatusSummary.kt @@ -0,0 +1,45 @@ +package com.pixelized.rplexicon.ui.screens.summary.composable.preview.statistic + +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.StatusSummaryUio + +@Composable +@Stable +fun rememberStatusSummary(): StatusSummaryUio { + return remember { + StatusSummaryUio( + max = mutableIntStateOf(2), + c1 = mutableStateOf( + listOf( + StatusSummaryUio.Status( + character = "Bulkhai", + id = "frightened", + icon = R.drawable.ic_status_frightened_24dp + ), + ) + ), + c2 = mutableStateOf(emptyList()), + c3 = mutableStateOf(emptyList()), + c4 = mutableStateOf(emptyList()), + c5 = mutableStateOf( + listOf( + StatusSummaryUio.Status( + character = "Unathana", + id = "paralyzed", + icon = R.drawable.ic_status_paralyzed_24dp + ), + StatusSummaryUio.Status( + character = "Unathana", + id = "poisoned", + icon = R.drawable.ic_status_poisoned_24dp + ), + ) + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/StatisticSummary.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/StatisticSummary.kt index 38c0b4e..a20a182 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/StatisticSummary.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/StatisticSummary.kt @@ -47,6 +47,8 @@ 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.StatusSummary +import com.pixelized.rplexicon.ui.screens.summary.composable.StatusSummaryUio import com.pixelized.rplexicon.ui.screens.summary.composable.preview.statistic.rememberStatisticSummary import com.pixelized.rplexicon.ui.theme.LexiconTheme @@ -66,6 +68,8 @@ data class StatisticSummaryUio( val passives: PassivesSummaryUio, val spellsVisibility: State, val spells: SpellSummaryUio, + val statusVisibility: State, + val status: StatusSummaryUio, ) @Composable @@ -81,15 +85,18 @@ fun StatisticSummary( extendProficiencies: State, extendPassives: State, extendSpells: State, + extendStatus: State, onAttribute: (Boolean) -> Unit, onCharacteristic: (Boolean) -> Unit, onSavingThrows: (Boolean) -> Unit, onProficiencies: (Boolean) -> Unit, onPassives: (Boolean) -> Unit, onSpells: (Boolean) -> Unit, + onStatus: (Boolean) -> Unit, + onAlteration: (StatusSummaryUio.Status) -> Unit, ) { val density = LocalDensity.current - + val headerSize = remember { mutableStateOf(IntSize.Zero) } @@ -104,12 +111,11 @@ fun StatisticSummary( ) { Column( modifier = Modifier.verticalScroll(state = scrollState), - verticalArrangement = Arrangement.spacedBy(space = 16.dp), + verticalArrangement = Arrangement.spacedBy(space = 8.dp), ) { Spacer( modifier = Modifier.height(height = headerHeight.value), ) - AnimatedVisibility( visible = summary.statsVisibility.value, enter = enterTransition(), @@ -176,6 +182,18 @@ fun StatisticSummary( onClick = { onSpells(extendSpells.value.not()) }, ) } + AnimatedVisibility( + visible = summary.statusVisibility.value && summary.status.max.value > 0, + enter = enterTransition(), + ) { + StatusSummary( + modifier = Modifier.fillMaxWidth(), + status = summary.status, + extended = extendStatus, + onClick = { onStatus(extendStatus.value.not()) }, + onStatus = onAlteration, + ) + } } AnimatedVisibility( @@ -219,8 +237,8 @@ private fun rememberHeaderBackgroundGradient(): Brush { } @Composable -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, heightDp = 1680) -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, heightDp = 1680) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, heightDp = 2000) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, heightDp = 2000) fun StatisticSummaryPreview() { LexiconTheme { Surface { @@ -235,12 +253,15 @@ fun StatisticSummaryPreview() { extendProficiencies = remember { mutableStateOf(true) }, extendPassives = remember { mutableStateOf(true) }, extendSpells = remember { mutableStateOf(true) }, + extendStatus = remember { mutableStateOf(true) }, onAttribute = { }, onCharacteristic = { }, onSavingThrows = { }, onProficiencies = { }, onPassives = { }, onSpells = { }, + onStatus = { }, + onAlteration = { }, ) } } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/StatisticViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/StatisticViewModel.kt index 5315590..48421a8 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/StatisticViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/StatisticViewModel.kt @@ -1,32 +1,46 @@ package com.pixelized.rplexicon.ui.screens.summary.pages.statistic +import android.app.Application import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.pixelized.rplexicon.R import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository +import com.pixelized.rplexicon.data.repository.character.AlterationRepository +import com.pixelized.rplexicon.data.repository.character.DescriptionRepository +import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationDetailUio import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowCardFactory import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio import com.pixelized.rplexicon.ui.screens.summary.composable.ClassHeaderSummaryUio +import com.pixelized.rplexicon.utilitary.extentions.context import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.lang.Appendable import javax.inject.Inject @HiltViewModel class StatisticViewModel @Inject constructor( private val firebaseRepository: FirebaseRepository, private val throwCardFactory: ThrowCardFactory, + private val alterationRepository: AlterationRepository, + private val descriptionRepository: DescriptionRepository, summaryFactory: SummaryFactory, -) : ViewModel() { + application: Application, +) : AndroidViewModel(application) { val summary = summaryFactory.fetchSummary(viewModelScope) private var detailJob: Job? = null private val _detail = mutableStateOf(null) val detail: State get() = _detail + private val _alterationDetail = mutableStateOf(null) + val alterationDetail: State get() = _alterationDetail + fun showDetail(dice: ClassHeaderSummaryUio.Dice) { detailJob?.cancel() detailJob = viewModelScope.launch(Dispatchers.IO) { @@ -49,4 +63,25 @@ class StatisticViewModel @Inject constructor( detailJob = null _detail.value = null } + + fun showAlterationDetail(name: String, character: String,) { + val alteration = alterationRepository.getAlterations(character = character) + .firstOrNull { it.name == name } + val description = descriptionRepository.find(name = alteration?.name) + + if (alteration != null) { + _alterationDetail.value = AlterationDetailUio( + name = name, + original = description?.original, + source = alteration.source, + target = alteration.target, + description = description?.description + ?: context.getString(R.string.no_available_description) + ) + } + } + + fun hideAlterationDetail() { + _alterationDetail.value = null + } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/SummaryFactory.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/SummaryFactory.kt index 76a2ff4..12abb08 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/SummaryFactory.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/summary/pages/statistic/SummaryFactory.kt @@ -3,6 +3,7 @@ package com.pixelized.rplexicon.ui.screens.summary.pages.statistic import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import com.pixelized.rplexicon.R +import com.pixelized.rplexicon.data.model.Alteration import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.Property import com.pixelized.rplexicon.data.network.CharacterSheetFire @@ -17,6 +18,7 @@ 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.StatusSummaryUio 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 @@ -51,6 +53,7 @@ class SummaryFactory @Inject constructor( val proficienciesVisibility = mutableStateOf(false) val passivesVisibility = mutableStateOf(false) val spellsVisibility = mutableStateOf(false) + val statusVisibility = mutableStateOf(false) val header = ClassHeaderSummaryUio( clazz1 = mutableStateOf(ClassHeaderSummaryUio.Header()), @@ -126,6 +129,14 @@ class SummaryFactory @Inject constructor( slot9 = SummaryRowUio(label = R.string.character_sheet_title_spell_slot_9), max = maxSpellSlot ) + val statuses = StatusSummaryUio( + c1 = mutableStateOf(emptyList()), + c2 = mutableStateOf(emptyList()), + c3 = mutableStateOf(emptyList()), + c4 = mutableStateOf(emptyList()), + c5 = mutableStateOf(emptyList()), + max = mutableIntStateOf(0), + ) scope.launch(Dispatchers.IO) { val data = DataStruct() @@ -150,6 +161,9 @@ class SummaryFactory @Inject constructor( fun SummaryRowUio.get(sheet: CharacterSheet?) = get(index = characters.indexOf(sheet?.name)) + fun StatusSummaryUio.get(sheet: CharacterSheet?) = + get(index = characters.indexOf(sheet?.name)) + fun ClassHeaderSummaryUio.getClass(sheet: CharacterSheet?) = classes.getOrNull(index = characters.indexOf(sheet?.name)) @@ -597,6 +611,29 @@ class SummaryFactory @Inject constructor( if (spellsVisibility.value.not()) delay(ENTER_ANIMATION_DELAY) spellsVisibility.value = true } + + // Update the Status + sheets.values.forEach { sheet -> + val status = alterationRepository + .getActiveAlterations(character = sheet.name) + .filterDndStatus(character = sheet.name) + withContext(Dispatchers.Main) { + statuses.get(sheet = sheet)?.value = status + } + } + withContext(Dispatchers.Main) { + statuses.max.value = listOf( + statuses.c1.value.count(), + statuses.c2.value.count(), + statuses.c3.value.count(), + statuses.c4.value.count(), + statuses.c5.value.count(), + ).max() + } + withContext(Dispatchers.Main) { + if (statusVisibility.value.not()) delay(ENTER_ANIMATION_DELAY) + statusVisibility.value = true + } } } @@ -641,9 +678,44 @@ class SummaryFactory @Inject constructor( passives = passives, spellsVisibility = spellsVisibility, spells = spells, + statusVisibility = statusVisibility, + status = statuses, ) } + private fun List.filterDndStatus( + character: String, + ): List { + return this + .mapNotNull { alteration -> + val icon = when (alteration.name) { + PRONE -> R.drawable.ic_status_prone_24dp + GRAPPLED -> R.drawable.ic_status_grappled_24dp + DEAFENED -> R.drawable.ic_status_deafened_24dp + BLINDED -> R.drawable.ic_status_blinded_24dp + CHARMED -> R.drawable.ic_status_charmed_24dp + FRIGHTENED -> R.drawable.ic_status_frightened_24dp + RESTRAINED -> R.drawable.ic_status_restrained_24dp + STUNNED -> R.drawable.ic_status_stunned_24dp + INCAPACITATED -> R.drawable.ic_status_incapacitated_24dp + UNCONSCIOUS -> R.drawable.ic_status_unconscious_24dp + INVISIBLE -> R.drawable.ic_status_invisible_24dp + PETRIFIED -> R.drawable.ic_status_petrified_24dp + POISONED -> R.drawable.ic_status_poisoned_24dp + PARALYZED -> R.drawable.ic_status_paralyzed_24dp + else -> null + } + icon?.let { + StatusSummaryUio.Status( + character = character, + id = alteration.name, + icon = icon + ) + } + } + .sortedBy { it.id } + } + private class DataStruct { lateinit var sheets: Map lateinit var fires: Map @@ -663,5 +735,20 @@ class SummaryFactory @Inject constructor( companion object { private const val ENTER_ANIMATION_INITIAL_DELAY = 250L private const val ENTER_ANIMATION_DELAY = 100L + + private const val PRONE = "À terre" + private const val GRAPPLED = "Agrippé" + private const val DEAFENED = "Assourdi" + private const val BLINDED = "Aveuglé" + private const val CHARMED = "Charmé" + private const val FRIGHTENED = "Effrayé" + private const val RESTRAINED = "Entravé" + private const val STUNNED = "Étourdi" + private const val INCAPACITATED = "Neutralisé" + private const val UNCONSCIOUS = "Inconscient" + private const val INVISIBLE = "Invisible" + private const val PETRIFIED = "Pétrifié" + private const val POISONED = "Empoisonné" + private const val PARALYZED = "Paralysé" } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_status_blinded_24dp.xml b/app/src/main/res/drawable/ic_status_blinded_24dp.xml new file mode 100644 index 0000000..4a3d962 --- /dev/null +++ b/app/src/main/res/drawable/ic_status_blinded_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_charmed_24dp.xml b/app/src/main/res/drawable/ic_status_charmed_24dp.xml new file mode 100644 index 0000000..c03d1bf --- /dev/null +++ b/app/src/main/res/drawable/ic_status_charmed_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_deafened_24dp.xml b/app/src/main/res/drawable/ic_status_deafened_24dp.xml new file mode 100644 index 0000000..a4ae902 --- /dev/null +++ b/app/src/main/res/drawable/ic_status_deafened_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_frightened_24dp.xml b/app/src/main/res/drawable/ic_status_frightened_24dp.xml new file mode 100644 index 0000000..e5233ec --- /dev/null +++ b/app/src/main/res/drawable/ic_status_frightened_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_grappled_24dp.xml b/app/src/main/res/drawable/ic_status_grappled_24dp.xml new file mode 100644 index 0000000..9a43e9f --- /dev/null +++ b/app/src/main/res/drawable/ic_status_grappled_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_incapacitated_24dp.xml b/app/src/main/res/drawable/ic_status_incapacitated_24dp.xml new file mode 100644 index 0000000..eeda3fe --- /dev/null +++ b/app/src/main/res/drawable/ic_status_incapacitated_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_invisible_24dp.xml b/app/src/main/res/drawable/ic_status_invisible_24dp.xml new file mode 100644 index 0000000..fff4f00 --- /dev/null +++ b/app/src/main/res/drawable/ic_status_invisible_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_paralyzed_24dp.xml b/app/src/main/res/drawable/ic_status_paralyzed_24dp.xml new file mode 100644 index 0000000..d4c4d70 --- /dev/null +++ b/app/src/main/res/drawable/ic_status_paralyzed_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_petrified_24dp.xml b/app/src/main/res/drawable/ic_status_petrified_24dp.xml new file mode 100644 index 0000000..b5c0c31 --- /dev/null +++ b/app/src/main/res/drawable/ic_status_petrified_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_poisoned_24dp.xml b/app/src/main/res/drawable/ic_status_poisoned_24dp.xml new file mode 100644 index 0000000..9127c1f --- /dev/null +++ b/app/src/main/res/drawable/ic_status_poisoned_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_prone_24dp.xml b/app/src/main/res/drawable/ic_status_prone_24dp.xml new file mode 100644 index 0000000..b4efebd --- /dev/null +++ b/app/src/main/res/drawable/ic_status_prone_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_restrained_24dp.xml b/app/src/main/res/drawable/ic_status_restrained_24dp.xml new file mode 100644 index 0000000..6d217d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_status_restrained_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_stunned_24dp.xml b/app/src/main/res/drawable/ic_status_stunned_24dp.xml new file mode 100644 index 0000000..d589821 --- /dev/null +++ b/app/src/main/res/drawable/ic_status_stunned_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_status_unconscious_24dp.xml b/app/src/main/res/drawable/ic_status_unconscious_24dp.xml new file mode 100644 index 0000000..a4e68fd --- /dev/null +++ b/app/src/main/res/drawable/ic_status_unconscious_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index bfe361b..9039a3f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -105,6 +105,7 @@ Attributs Talents passifs Sortilèges + Statut Jet de sauvegarde Talents Maîtrises martiales : diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b23756d..102e40c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -107,6 +107,7 @@ Attributes Passives proficiencies Spells + Status Martial masteries: Languages masteries: Others masteries: