From 96149b951a2ae6384504d6744001b4466c2c043a Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Sun, 5 Nov 2023 22:03:15 +0100 Subject: [PATCH] Add death saving throw management. --- .../rplexicon/business/DiceThrowUseCase.kt | 13 +- .../rplexicon/model/CharacterSheetFire.kt | 17 ++ .../pixelized/rplexicon/model/DiceThrow.kt | 1 + .../com/pixelized/rplexicon/model/Property.kt | 1 + .../authentication/FirebaseRepository.kt | 12 ++ .../screens/character/CharacterSheetScreen.kt | 35 +++- .../character/CharacterSheetHeader.kt | 36 +++- .../composable/character/DeathHeader.kt | 198 ++++++++++++++++++ .../rememberCharacterHeaderStatePreview.kt | 17 +- .../factory/CharacterSheetHeaderUioFactory.kt | 11 +- .../pages/actions/HeaderViewModel.kt | 34 +++ .../ui/screens/rolls/composable/RollDice.kt | 4 +- .../rolls/factory/AlterationFactory.kt | 1 + app/src/main/res/values-fr/strings.xml | 7 +- app/src/main/res/values/strings.xml | 7 +- 15 files changed, 367 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/DeathHeader.kt diff --git a/app/src/main/java/com/pixelized/rplexicon/business/DiceThrowUseCase.kt b/app/src/main/java/com/pixelized/rplexicon/business/DiceThrowUseCase.kt index 510f5e1..05d11b6 100644 --- a/app/src/main/java/com/pixelized/rplexicon/business/DiceThrowUseCase.kt +++ b/app/src/main/java/com/pixelized/rplexicon/business/DiceThrowUseCase.kt @@ -173,6 +173,15 @@ class DiceThrowUseCase @Inject constructor( relatedStat = Property.CHARISMA ) + is DiceThrow.DeathSavingThrow -> savingThrow( + character = sheet, + alterations = alterations, + abilityLabel = { getString(R.string.character_sheet_stat_death) }, + relatedLabel = { getString(R.string.character_sheet_stat_death) }, + ability = Property.DEATH_SAVING_THROW, + relatedStat = null, + ) + is DiceThrow.Acrobatics -> abilityThrow( character = sheet, alterations = alterations, @@ -494,7 +503,7 @@ class DiceThrowUseCase @Inject constructor( abilityLabel: Context.() -> String, relatedLabel: Context.() -> String?, ability: Property, - relatedStat: Property, + relatedStat: Property?, ): DiceThrowResult = rollAbility( character = character, alterations = alterations, @@ -514,7 +523,7 @@ class DiceThrowUseCase @Inject constructor( relatedTitle: Context.(label: String) -> String?, relatedLabel: Context.() -> String?, ability: Property, - relatedStat: Property, + relatedStat: Property?, ): DiceThrowResult { // retrieve some wording. val abilityLabelString = abilityLabel(application) diff --git a/app/src/main/java/com/pixelized/rplexicon/model/CharacterSheetFire.kt b/app/src/main/java/com/pixelized/rplexicon/model/CharacterSheetFire.kt index f37c4f1..d162ebd 100644 --- a/app/src/main/java/com/pixelized/rplexicon/model/CharacterSheetFire.kt +++ b/app/src/main/java/com/pixelized/rplexicon/model/CharacterSheetFire.kt @@ -11,6 +11,10 @@ data class CharacterSheetFire( @set:PropertyName(HIT_POINT) var hitPoint: HitPoint? = null, + @get:PropertyName(DEATH) + @set:PropertyName(DEATH) + var death: Death? = null, + @get:PropertyName(SKILLS) @set:PropertyName(SKILLS) var skills: Map = emptyMap(), @@ -31,8 +35,21 @@ data class CharacterSheetFire( var value: Int? = null, ) + @Keep + @IgnoreExtraProperties + data class Death( + @get:PropertyName("success") + @set:PropertyName("success") + var success: Int? = null, + + @get:PropertyName("failure") + @set:PropertyName("failure") + var failure: Int? = null, + ) + companion object { const val HIT_POINT = "hit_point" + const val DEATH = "death" const val SKILLS = "skills" const val SPELLS = "spells" const val SPELL_PREFIX = "lvl_" diff --git a/app/src/main/java/com/pixelized/rplexicon/model/DiceThrow.kt b/app/src/main/java/com/pixelized/rplexicon/model/DiceThrow.kt index 1d46fa2..70f91aa 100644 --- a/app/src/main/java/com/pixelized/rplexicon/model/DiceThrow.kt +++ b/app/src/main/java/com/pixelized/rplexicon/model/DiceThrow.kt @@ -14,6 +14,7 @@ sealed class DiceThrow(val character: String) { class IntelligenceSavingThrow(character: String) : DiceThrow(character) class WisdomSavingThrow(character: String) : DiceThrow(character) class CharismaSavingThrow(character: String) : DiceThrow(character) + class DeathSavingThrow(character: String): DiceThrow(character) class Acrobatics(character: String) : DiceThrow(character) class AnimalHandling(character: String) : DiceThrow(character) class Arcana(character: String) : DiceThrow(character) diff --git a/app/src/main/java/com/pixelized/rplexicon/model/Property.kt b/app/src/main/java/com/pixelized/rplexicon/model/Property.kt index cfeaa98..4b3437b 100644 --- a/app/src/main/java/com/pixelized/rplexicon/model/Property.kt +++ b/app/src/main/java/com/pixelized/rplexicon/model/Property.kt @@ -23,6 +23,7 @@ enum class Property(val key: String) { INTELLIGENCE_SAVING_THROW("Jet de sauvegarde: Intelligence"), WISDOM_SAVING_THROW("Jet de sauvegarde: Sagesse"), CHARISMA_SAVING_THROW("Jet de sauvegarde: Charisme"), + DEATH_SAVING_THROW("Jet de sauvegarde: Mort"), ACROBATICS("Acrobaties"), ANIMAL_HANDLING("Dressage"), ARCANA("Arcanes"), diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/authentication/FirebaseRepository.kt b/app/src/main/java/com/pixelized/rplexicon/repository/authentication/FirebaseRepository.kt index 8498406..091b2f0 100644 --- a/app/src/main/java/com/pixelized/rplexicon/repository/authentication/FirebaseRepository.kt +++ b/app/src/main/java/com/pixelized/rplexicon/repository/authentication/FirebaseRepository.kt @@ -75,6 +75,18 @@ class FirebaseRepository @Inject constructor( ) } + fun setCharacterDeathCounter(character: String, success: Int, failure: Int) { + val reference = database.getReference( + "$PATH_CHARACTERS/$character/${CharacterSheetFire.DEATH}" + ) + reference.setValue( + CharacterSheetFire.Death( + success = success, + failure = failure, + ) + ) + } + fun setSkill(character: String, name: String, value: Int) { val reference = database.getReference( "$PATH_CHARACTERS/$character/${CharacterSheetFire.SKILLS}/$name" diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetScreen.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetScreen.kt index 093cfab..439ea2f 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetScreen.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetScreen.kt @@ -67,10 +67,10 @@ import com.pixelized.rplexicon.ui.composable.Loader import com.pixelized.rplexicon.ui.composable.edit.HandleHitPointEditDialog import com.pixelized.rplexicon.ui.composable.error.HandleFetchError import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost -import com.pixelized.rplexicon.ui.screens.character.CharacterHeader.Action -import com.pixelized.rplexicon.ui.screens.character.CharacterHeader.Alteration -import com.pixelized.rplexicon.ui.screens.character.CharacterHeader.Inventory -import com.pixelized.rplexicon.ui.screens.character.CharacterHeader.Proficiency +import com.pixelized.rplexicon.ui.screens.character.CharacterTabUio.Action +import com.pixelized.rplexicon.ui.screens.character.CharacterTabUio.Alteration +import com.pixelized.rplexicon.ui.screens.character.CharacterTabUio.Inventory +import com.pixelized.rplexicon.ui.screens.character.CharacterTabUio.Proficiency import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeader import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio import com.pixelized.rplexicon.ui.screens.character.composable.character.ProficiencyUio.ID.* @@ -99,7 +99,7 @@ import com.pixelized.rplexicon.utilitary.extentions.lexiconShadow import kotlinx.coroutines.launch -enum class CharacterHeader(@StringRes val label: Int) { +enum class CharacterTabUio(@StringRes val label: Int) { Action(R.string.character_sheet_tab_actions), Alteration(R.string.character_sheet_tab_alteration), Inventory(R.string.character_sheet_tab_inventory), @@ -154,6 +154,12 @@ fun CharacterSheetScreen( scope.launch { pagerState.animateScrollToPage(it) } }, onHitPoint = headerViewModel::toggleHitPointDialog, + onDeathRoll = { + overlay.prepareRoll(diceThrow = headerViewModel.onDeathThrow()) + overlay.showOverlay() + }, + onDeathSuccess = headerViewModel::onDeathSuccess, + onDeathFailure = headerViewModel::onDeathFailure, onRefresh = { scope.launch { viewModel.update(force = true) } }, @@ -238,9 +244,12 @@ private fun CharacterSheetContent( onRefresh: () -> Unit, onFullRefresh: () -> Unit, name: String, - tabs: State>, + tabs: State>, header: State, onHitPoint: () -> Unit, + onDeathRoll: () -> Unit, + onDeathSuccess: () -> Unit, + onDeathFailure: () -> Unit, onBack: () -> Unit, onTab: (Int) -> Unit, loader: @Composable BoxScope.() -> Unit, @@ -314,6 +323,9 @@ private fun CharacterSheetContent( modifier = Modifier.fillMaxWidth(), header = header, onHitPoint = onHitPoint, + onDeathRoll = onDeathRoll, + onDeathSuccess = onDeathSuccess, + onDeathFailure = onDeathFailure, ) PagerHeader( pagerState = pagerState, @@ -353,7 +365,7 @@ private fun CharacterSheetContent( private fun PagerHeader( modifier: Modifier = Modifier, pagerState: PagerState, - tabs: State>, + tabs: State>, onTab: (Int) -> Unit, ) { if (tabs.value.isNotEmpty()) { @@ -390,10 +402,10 @@ private fun rememberHeaderTabsState( spellsViewModel: SpellsViewModel = hiltViewModel(), skillViewModel: SkillsViewModel = hiltViewModel(), alterationsViewModel: AlterationViewModel = hiltViewModel(), -): State> { +): State> { val headers = remember { derivedStateOf { - mutableListOf().apply { + mutableListOf().apply { addAll( when { proficiencyViewModel.sheet.value != null -> listOf(Proficiency) @@ -450,6 +462,9 @@ private fun CharacterScreenPreview( onFullRefresh = { }, loader = { }, onHitPoint = { }, + onDeathRoll = { }, + onDeathSuccess = { }, + onDeathFailure = { }, proficiencies = { ProficiencyPreview() }, actions = { ActionPagePreview() }, alterations = { AlterationPagePreview() }, @@ -462,7 +477,7 @@ private fun CharacterScreenPreview( @Composable @Stable -private fun rememberHeaderPreview(): State> = remember { +private fun rememberHeaderPreview(): State> = remember { mutableStateOf( listOf( Proficiency, diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/CharacterSheetHeader.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/CharacterSheetHeader.kt index f9ef4f3..ae201de 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/CharacterSheetHeader.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/CharacterSheetHeader.kt @@ -2,13 +2,13 @@ package com.pixelized.rplexicon.ui.screens.character.composable.character import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_YES +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Divider -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable @@ -18,8 +18,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberCharacterHeaderStatePreview +import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberDeathThrowUio import com.pixelized.rplexicon.ui.theme.LexiconTheme -import com.pixelized.rplexicon.utilitary.extentions.lexicon @Stable data class CharacterSheetHeaderUio( @@ -27,23 +27,28 @@ data class CharacterSheetHeaderUio( val hitPoint: LabelPointUio, val speed: LabelPointUio, val dC: LabelPointUio?, + val death: DeathThrowUio? = null ) @Composable fun CharacterSheetHeader( modifier: Modifier = Modifier, - padding : PaddingValues = PaddingValues(start = 16.dp, end = 16.dp, bottom = 4.dp), + padding: PaddingValues = PaddingValues(start = 16.dp, end = 16.dp, bottom = 4.dp), header: State, - onHitPoint : () -> Unit, + onHitPoint: () -> Unit, + onDeathRoll: () -> Unit, + onDeathSuccess: () -> Unit, + onDeathFailure: () -> Unit, ) { Surface( modifier = modifier, ) { Column( + modifier = Modifier.padding(padding), + verticalArrangement = Arrangement.spacedBy(space = 4.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { Row( - modifier = Modifier.padding(padding), verticalAlignment = Alignment.Bottom, horizontalArrangement = Arrangement.spacedBy( space = 16.dp, @@ -63,6 +68,17 @@ fun CharacterSheetHeader( LabelPoint(label = it) } } + + AnimatedVisibility( + visible = header.value?.death != null, + ) { + DeathHeader( + death = header.value?.death, + onDeathRoll = onDeathRoll, + onDeathSuccess = onDeathSuccess, + onDeathFailure = onDeathFailure, + ) + } } } } @@ -74,8 +90,14 @@ private fun CharacterSheetHeaderPreview() { LexiconTheme { Surface { CharacterSheetHeader( - header = rememberCharacterHeaderStatePreview(), + modifier = Modifier.fillMaxWidth(), + header = rememberCharacterHeaderStatePreview( + death = rememberDeathThrowUio(), + ), onHitPoint = { }, + onDeathRoll = { }, + onDeathSuccess = { }, + onDeathFailure = { }, ) } } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/DeathHeader.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/DeathHeader.kt new file mode 100644 index 0000000..7e1666e --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/character/DeathHeader.kt @@ -0,0 +1,198 @@ +package com.pixelized.rplexicon.ui.screens.character.composable.character + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +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.CompositionLocalProvider +import androidx.compose.runtime.Stable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import com.pixelized.rplexicon.R +import com.pixelized.rplexicon.ui.theme.LexiconTheme + +@Stable +data class DeathThrowUio( + val success: Int, + val failure: Int, +) + +@Composable +fun DeathHeader( + death: DeathThrowUio?, + onDeathSuccess: () -> Unit, + onDeathRoll: () -> Unit, + onDeathFailure: () -> Unit, +) { + Row( + modifier = Modifier.height(intrinsicSize = IntrinsicSize.Min), + horizontalArrangement = Arrangement.Center, + ) { + Box( + modifier = Modifier.fillMaxHeight(), + ) { + DeathMark( + modifier = Modifier + .align(alignment = Alignment.Center) + .clip(shape = CircleShape) + .clickable(onClick = onDeathSuccess) + .padding(all = 8.dp), + counter = death?.success ?: 0, + leftToRight = false, + ) + Text( + modifier = Modifier + .align(alignment = Alignment.BottomEnd) + .padding(horizontal = 8.dp), + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.Light, + text = stringResource(id = R.string.generic_success), + ) + } + Icon( + modifier = Modifier + .clip(shape = CircleShape) + .clickable(onClick = onDeathRoll) + .padding(all = 8.dp) + .size(size = 42.dp), + tint = MaterialTheme.colorScheme.primary, + painter = painterResource(id = R.drawable.ic_d20_24), + contentDescription = null + ) + Box( + modifier = Modifier.fillMaxHeight(), + ) { + DeathMark( + modifier = Modifier + .align(alignment = Alignment.Center) + .clip(shape = CircleShape) + .clickable(onClick = onDeathFailure) + .padding(all = 8.dp), + counter = death?.failure ?: 0, + leftToRight = true, + ) + Text( + modifier = Modifier + .align(alignment = Alignment.BottomStart) + .padding(horizontal = 8.dp), + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.Light, + text = stringResource(id = R.string.generic_failure), + ) + } + } +} + +@Composable +private fun DeathMark( + modifier: Modifier = Modifier, + leftToRight: Boolean, + counter: Int, +) { + CompositionLocalProvider( + LocalLayoutDirection provides when (leftToRight) { + true -> LayoutDirection.Ltr + else -> LayoutDirection.Rtl + } + ) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + Circle( + full = counter > 0, + ) + Divider( + modifier = Modifier.width(width = 8.dp), + thickness = 2.dp, + color = MaterialTheme.colorScheme.onSurface, + ) + Circle( + full = counter > 1, + ) + Divider( + modifier = Modifier.width(width = 8.dp), + thickness = 2.dp, + color = MaterialTheme.colorScheme.onSurface, + ) + Circle( + full = counter > 2, + ) + } + } +} + +@Composable +private fun Circle( + modifier: Modifier = Modifier, + full: Boolean, +) { + Box( + modifier = modifier + .size(size = 24.dp) + .border( + width = 2.dp, + shape = CircleShape, + color = MaterialTheme.colorScheme.onSurface, + ), + contentAlignment = Alignment.Center, + ) { + AnimatedVisibility( + visible = full, + enter = fadeIn() + scaleIn(), + exit = fadeOut() + scaleOut(), + ) { + Box( + modifier = Modifier + .size(size = 14.dp) + .background( + color = MaterialTheme.colorScheme.primary, + shape = CircleShape, + ), + ) + } + } +} + +@Composable +@Preview +private fun DeathMarkPreview() { + LexiconTheme { + Surface { + DeathHeader( + death = DeathThrowUio(success = 2, failure = 1), + onDeathRoll = { }, + onDeathSuccess = { }, + onDeathFailure = { }, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberCharacterHeaderStatePreview.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberCharacterHeaderStatePreview.kt index 95b7e71..f6c1f05 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberCharacterHeaderStatePreview.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberCharacterHeaderStatePreview.kt @@ -6,11 +6,14 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import com.pixelized.rplexicon.R import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio +import com.pixelized.rplexicon.ui.screens.character.composable.character.DeathThrowUio import com.pixelized.rplexicon.ui.screens.character.composable.character.LabelPointUio @Composable @Stable -fun rememberCharacterHeaderStatePreview() = remember { +fun rememberCharacterHeaderStatePreview( + death: DeathThrowUio? = null +) = remember { mutableStateOf( CharacterSheetHeaderUio( armorClass = LabelPointUio( @@ -31,6 +34,18 @@ fun rememberCharacterHeaderStatePreview() = remember { label = R.string.character_sheet_title_dc, value = "13", ), + death = death, ) ) +} + +@Composable +@Stable +fun rememberDeathThrowUio(): DeathThrowUio { + return remember { + DeathThrowUio( + success = 2, + failure = 1, + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/CharacterSheetHeaderUioFactory.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/CharacterSheetHeaderUioFactory.kt index 7600d7c..589a86f 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/CharacterSheetHeaderUioFactory.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/CharacterSheetHeaderUioFactory.kt @@ -2,6 +2,7 @@ package com.pixelized.rplexicon.ui.screens.character.factory import com.pixelized.rplexicon.R import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio +import com.pixelized.rplexicon.ui.screens.character.composable.character.DeathThrowUio import com.pixelized.rplexicon.ui.screens.character.composable.character.LabelPointUio import com.pixelized.rplexicon.ui.screens.character.pages.actions.HeaderViewModel import javax.inject.Inject @@ -33,7 +34,15 @@ class CharacterSheetHeaderUioFactory @Inject constructor() { label = R.string.character_sheet_title_dc, value = "$it", ) - } + }, + death = if (fireHeaderData?.hp == 0) { + DeathThrowUio( + success = fireHeaderData.deathSuccess, + failure = fireHeaderData.deathFailure, + ) + } else { + null + }, ) } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/HeaderViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/HeaderViewModel.kt index f4eb308..c59cc37 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/HeaderViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/HeaderViewModel.kt @@ -7,6 +7,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.pixelized.rplexicon.model.DiceThrow import com.pixelized.rplexicon.model.Property import com.pixelized.rplexicon.repository.authentication.FirebaseRepository import com.pixelized.rplexicon.repository.data.character.AlterationRepository @@ -72,6 +73,8 @@ class HeaderViewModel @Inject constructor( val data = FireHeaderData( hp = sheets.hitPoint?.value ?: 1, extra = sheets.hitPoint?.additional ?: 0, + deathSuccess = sheets.death?.success ?: 0, + deathFailure = sheets.death?.failure ?: 0, ) withContext(Dispatchers.Main) { fireData.value = data @@ -81,6 +84,26 @@ class HeaderViewModel @Inject constructor( } } + fun onDeathThrow(): DiceThrow { + return DiceThrow.DeathSavingThrow(character = character) + } + + fun onDeathSuccess() { + firebaseRepository.setCharacterDeathCounter( + character = character, + success = ((fireData.value?.deathSuccess ?: 0) + 1) % 4, + failure = fireData.value?.deathFailure ?: 0, + ) + } + + fun onDeathFailure() { + firebaseRepository.setCharacterDeathCounter( + character = character, + success = fireData.value?.deathSuccess ?: 0, + failure = ((fireData.value?.deathFailure ?: 0) + 1) % 4, + ) + } + fun toggleHitPointDialog() { _hitPointDialog.value = if (_hitPointDialog.value == null) { HpPointDialogUio( @@ -94,6 +117,15 @@ class HeaderViewModel @Inject constructor( } fun applyHitPointChange(hp: Int, extra: Int) { + val hpDownToZero = (fireData.value?.hp ?: 0) > 0 && hp == 0 + val hpUpFromZero = (fireData.value?.hp ?: 0) == 0 && hp > 0 + if (hpDownToZero || hpUpFromZero) { + firebaseRepository.setCharacterDeathCounter( + character = character, + success = 0, + failure = 0, + ) + } firebaseRepository.setCharacterHitPoint( character = character, value = hp, @@ -114,5 +146,7 @@ class HeaderViewModel @Inject constructor( data class FireHeaderData( val hp: Int, val extra: Int, + val deathSuccess: Int, + val deathFailure: Int, ) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/composable/RollDice.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/composable/RollDice.kt index 1e25d60..6c170c8 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/composable/RollDice.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/composable/RollDice.kt @@ -189,8 +189,8 @@ private fun Critical( else -> MaterialTheme.colorScheme.onSurface }, text = when (it) { - 1 -> stringResource(id = R.string.generic_critical_success) - 2 -> stringResource(id = R.string.generic_critical_failure) + 1 -> stringResource(id = R.string.generic_success_critical) + 2 -> stringResource(id = R.string.generic_failure_critical) else -> "" }, ) diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/factory/AlterationFactory.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/factory/AlterationFactory.kt index 7a94816..94ccc4e 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/factory/AlterationFactory.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/factory/AlterationFactory.kt @@ -45,6 +45,7 @@ class AlterationFactory @Inject constructor( is DiceThrow.IntelligenceSavingThrow -> listOf(Property.INTELLIGENCE_SAVING_THROW) is DiceThrow.WisdomSavingThrow -> listOf(Property.WISDOM_SAVING_THROW) is DiceThrow.CharismaSavingThrow -> listOf(Property.CHARISMA_SAVING_THROW) + is DiceThrow.DeathSavingThrow -> listOf(Property.DEATH_SAVING_THROW) is DiceThrow.Acrobatics -> listOf(Property.ACROBATICS, Property.DEXTERITY) is DiceThrow.AnimalHandling -> listOf(Property.ANIMAL_HANDLING, Property.WISDOM) is DiceThrow.Arcana -> listOf(Property.ARCANA, Property.INTELLIGENCE) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2b19304..f857244 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -18,8 +18,10 @@ La structure des cartes semble avoir changé et n\'est plus compatible avec cette application. La structure des quêtes semble avoir changé et n\'est plus compatible avec cette application. - SUCCÈS CRITIQUE - ÉCHEC CRITIQUE + Succès + SUCCÈS CRITIQUE + Échec + ÉCHEC CRITIQUE Mâle Femelle @@ -116,6 +118,7 @@ SAG Charisme CHA + Mort Talent Acrobaties Dressage diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index db5cbeb..6c3c507 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,8 +18,10 @@ The location sheet structure appears to have changed and is no longer compatible with this application The quest sheet structure appears to have changed and is no longer compatible with this application - CRITICAL SUCCESS - CRITICAL FAILURE + Success + CRITICAL SUCCESS + Failure + CRITICAL FAILURE Male Female @@ -116,6 +118,7 @@ WIS Charisma CHA + Death Proficiency Acrobatics Animal Handling