From 1b97560dce48b445bcfe0c62f0aecbc64f43fc6f Mon Sep 17 00:00:00 2001 From: "Andres Gomez, Thomas (ITDV RL)" Date: Tue, 4 Jun 2024 14:16:16 +0200 Subject: [PATCH] Support wild shape transformation (hp wise) --- .../rplexicon/business/WildShapeAlteration.kt | 13 ++ .../data/network/CharacterSheetFire.kt | 4 + .../authentication/FirebaseRepository.kt | 8 +- .../character/ActiveAlterationRepository.kt | 7 + .../factory/CharacterSheetHeaderUioFactory.kt | 26 +++- .../pages/actions/HeaderViewModel.kt | 123 +++++++++++------- .../pages/alteration/AlterationViewModel.kt | 12 +- 7 files changed, 138 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/com/pixelized/rplexicon/business/WildShapeAlteration.kt diff --git a/app/src/main/java/com/pixelized/rplexicon/business/WildShapeAlteration.kt b/app/src/main/java/com/pixelized/rplexicon/business/WildShapeAlteration.kt new file mode 100644 index 0000000..c078d1c --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/business/WildShapeAlteration.kt @@ -0,0 +1,13 @@ +package com.pixelized.rplexicon.business + +import com.pixelized.rplexicon.data.model.alteration.Alteration + +private const val WILD_SHAPE = "Forme sauvage" + +fun isWildShape(alteration: Alteration): Boolean { + return isWildShape(alterationId = alteration.name) +} + +fun isWildShape(alterationId: String): Boolean { + return alterationId.contains(WILD_SHAPE) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/network/CharacterSheetFire.kt b/app/src/main/java/com/pixelized/rplexicon/data/network/CharacterSheetFire.kt index b6ba088..bcb5462 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/network/CharacterSheetFire.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/network/CharacterSheetFire.kt @@ -37,6 +37,10 @@ data class CharacterSheetFire( @get:PropertyName("value") @set:PropertyName("value") var value: Int? = null, + + @get:PropertyName("wild_shape") + @set:PropertyName("wild_shape") + var wildShape: Int? = null, ) @Keep 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 83a0eed..3712547 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 @@ -142,7 +142,12 @@ class FirebaseRepository @Inject constructor( fun getAlterationStatusSnapshot(key: AlterationStatus.Key): AlterationStatus.Value = alterationStatus.value[key] ?: AlterationStatus.Value(false) - fun setCharacterHitPoint(character: String, value: Int, extra: Int) { + fun setCharacterHitPoint( + character: String, + value: Int? = characterFireSheet.value[character]?.hitPoint?.value, + extra: Int? = characterFireSheet.value[character]?.hitPoint?.additional, + wildShape: Int? = characterFireSheet.value[character]?.hitPoint?.wildShape, + ) { val reference = database.getReference( "$PATH_CHARACTERS/$character/${CharacterSheetFire.HIT_POINT}" ) @@ -150,6 +155,7 @@ class FirebaseRepository @Inject constructor( CharacterSheetFire.HitPoint( additional = extra, value = value, + wildShape = wildShape, ) ) } diff --git a/app/src/main/java/com/pixelized/rplexicon/data/repository/character/ActiveAlterationRepository.kt b/app/src/main/java/com/pixelized/rplexicon/data/repository/character/ActiveAlterationRepository.kt index 886b195..e4780df 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/repository/character/ActiveAlterationRepository.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/repository/character/ActiveAlterationRepository.kt @@ -50,6 +50,13 @@ class ActiveAlterationRepository @Inject constructor( fun getActiveAssignedAlterations(character: String?): Flow> = activeAlterations.map { it[character] ?: emptyList() } + /** + * Returns the first element matching the given predicate, or null if element was not found. + */ + fun firstOrNull(character: String?, predicate: (Alteration) -> Boolean): Alteration? { + return activeAlterations.value[character]?.firstOrNull { predicate(it) } + } + companion object { val ACTIVE = AlterationStatus.Value(value = true) } 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 dc0bb61..4fa7f95 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 @@ -35,7 +35,10 @@ class CharacterSheetHeaderUioFactory @Inject constructor( ), hitPoint = LabelPointUio( label = R.string.character_sheet_title_hp, - value = convertToHitPointLabel(hitPoint = fireHeaderData), + value = convertToHitPointLabel( + fireHeaderData = fireHeaderData, + sheetHeaderData = sheetHeaderData, + ), max = sheetHeaderData?.hpMax?.let { "/ $it" } ?: " ", ), dC = sheetHeaderData?.dc?.let { @@ -65,11 +68,22 @@ class CharacterSheetHeaderUioFactory @Inject constructor( ) } - private fun convertToHitPointLabel(hitPoint: HeaderViewModel.FireHeaderData?): String { - return when { - hitPoint?.hp == null -> "?" - hitPoint.extra == 0 -> "${hitPoint.hp}" - else -> "${hitPoint.hp}+${hitPoint.extra}" + private fun convertToHitPointLabel( + fireHeaderData: HeaderViewModel.FireHeaderData?, + sheetHeaderData: HeaderViewModel.SheetHeaderData?, + ): String { + return when (fireHeaderData?.wildShapeAlteration) { + null -> when { + fireHeaderData?.hp == null -> "?" + fireHeaderData.extraHp == 0 -> "${fireHeaderData.hp}" + else -> "${fireHeaderData.hp}+${fireHeaderData.extraHp}" + } + + else -> when { + fireHeaderData?.wildShapeHp == null -> sheetHeaderData?.hpMax?.let { "$it" } ?: "?" + fireHeaderData.extraHp == 0 -> "${fireHeaderData.wildShapeHp}" + else -> "${fireHeaderData.wildShapeHp}+${fireHeaderData.extraHp}" + } } } } \ No newline at end of file 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 0a98ad1..2113363 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,14 +7,15 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.pixelized.rplexicon.business.isWildShape import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.DiceThrow import com.pixelized.rplexicon.data.model.Property import com.pixelized.rplexicon.data.model.alteration.Alteration +import com.pixelized.rplexicon.data.model.alteration.AlterationStatus import com.pixelized.rplexicon.data.network.CharacterSheetFire import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository import com.pixelized.rplexicon.data.repository.character.ActiveAlterationRepository -import com.pixelized.rplexicon.data.repository.character.AlterationRepository import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository import com.pixelized.rplexicon.ui.composable.edit.HpPointDialogUio import com.pixelized.rplexicon.ui.composable.edit.SkillEditDialogUio @@ -36,8 +37,8 @@ import javax.inject.Inject class HeaderViewModel @Inject constructor( private val headerFactory: CharacterSheetHeaderUioFactory, private val firebaseRepository: FirebaseRepository, + private val activeAlterationRepository: ActiveAlterationRepository, characterRepository: CharacterSheetRepository, - activeAlterationRepository: ActiveAlterationRepository, savedStateHandle: SavedStateHandle, ) : ViewModel() { val character = savedStateHandle.characterSheetArgument.name @@ -67,10 +68,13 @@ class HeaderViewModel @Inject constructor( struct.sheet = sheets[character] struct.alterationsStatus = alterations.toStatus() } + .combine(firebaseRepository.getCharacter(character = character)) { _, fire -> + struct.fire = fire + } .collect { - val (sheet, status) = struct + val (sheet, status, fire) = struct if (sheet != null) { - val data = SheetHeaderData( + val headerCategories = SheetHeaderData( hpMax = sheet.hitPoint + status[Property.HIT_POINT].sum, characterClass = sheet.characterClass, initiative = (sheet.dexterity + status[Property.DEXTERITY].sum).modifier + status[Property.INITIATIVE].sum, @@ -78,35 +82,27 @@ class HeaderViewModel @Inject constructor( dc = sheet.dC, ) withContext(Dispatchers.Main) { - sheetData.value = data + sheetData.value = headerCategories + } + val headerValues = FireHeaderData( + hp = fire.hitPoint?.value ?: 1, + extraHp = fire.hitPoint?.additional ?: 0, + wildShapeHp = fire.hitPoint?.wildShape ?: headerCategories.hpMax, + resource = fire.skills[sheet.resource], + deathSuccess = fire.death?.success ?: 0, + deathFailure = fire.death?.failure ?: 0, + wildShapeAlteration = activeAlterationRepository.firstOrNull( + character = character + ) { isWildShape(it) }, + ) + withContext(Dispatchers.Main) { + fireData.value = headerValues } } else { launch { characterRepository.fetchCharacterSheet() } } } } - - launch(context = Dispatchers.Default) { - val struct = HeaderValueStruct() - characterRepository.data - .combine(firebaseRepository.getCharacter(character = character)) { sheets, fire -> - struct.sheet = sheets[character] - struct.fire = fire - } - .collect { - val (sheet, fire) = struct - val data = FireHeaderData( - hp = fire.hitPoint?.value ?: 1, - resource = fire.skills[sheet?.resource], - extra = fire.hitPoint?.additional ?: 0, - deathSuccess = fire.death?.success ?: 0, - deathFailure = fire.death?.failure ?: 0, - ) - withContext(Dispatchers.Main) { - fireData.value = data - } - } - } } } @@ -133,8 +129,11 @@ class HeaderViewModel @Inject constructor( fun toggleHitPointDialog() { _hitPointDialog.value = if (_hitPointDialog.value == null) { HpPointDialogUio( - value = fireData.value?.hp ?: 10, - extra = fireData.value?.extra ?: 0, + value = when (fireData.value?.wildShapeAlteration) { + null -> fireData.value?.hp ?: 10 + else -> fireData.value?.wildShapeHp ?: 1 + }, + extra = fireData.value?.extraHp ?: 0, max = sheetData.value?.hpMax ?: 10, ) } else { @@ -143,20 +142,53 @@ 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( + val wildShapeAlteration = fireData.value?.wildShapeAlteration + // check if we have a wild shape alteration activated. + if (wildShapeAlteration != null) { + // character is in wild shape form. + // check if we are down to 0 hp. + val hpDownToZero = (fireData.value?.wildShapeHp ?: 0) > 0 && hp == 0 + if (hpDownToZero) { + // deactivate the wild shape alteration + firebaseRepository.setAlterationStatus( + key = AlterationStatus.Key(character, wildShapeAlteration.name), + value = AlterationStatus.Value(false), + ) + // remove the wild shape hit point + firebaseRepository.setCharacterHitPoint( + character = character, + wildShape = null, + extra = extra, + ) + } else { + // change the current wild shape hit point. + firebaseRepository.setCharacterHitPoint( + character = character, + wildShape = hp, + extra = extra, + ) + } + } else { + // character is NOT in wild shape + // check if we are down to 0 hp or up from 0. + val hpDownToZero = (fireData.value?.hp ?: 0) > 0 && hp == 0 + val hpUpFromZero = (fireData.value?.hp ?: 0) == 0 && hp > 0 + if (hpDownToZero || hpUpFromZero) { + // set the death counter to 0 + firebaseRepository.setCharacterDeathCounter( + character = character, + success = 0, + failure = 0, + ) + } + // change the current hp of the character + firebaseRepository.setCharacterHitPoint( character = character, - success = 0, - failure = 0, + value = hp, + extra = extra, + wildShape = null, ) } - firebaseRepository.setCharacterHitPoint( - character = character, - value = hp, - extra = extra, - ) _hitPointDialog.value = null } @@ -182,15 +214,10 @@ class HeaderViewModel @Inject constructor( data class HeaderStruct( var sheet: CharacterSheet? = null, var alterationsStatus: Map> = emptyMap(), - ) - - class HeaderValueStruct( - var sheet: CharacterSheet? = null ) { lateinit var fire: CharacterSheetFire - operator fun component1() = sheet - operator fun component2() = fire + operator fun component3() = fire } @Stable @@ -205,9 +232,11 @@ class HeaderViewModel @Inject constructor( @Stable data class FireHeaderData( val hp: Int, + val extraHp: Int, + val wildShapeHp: Int?, val resource: Int?, - val extra: Int, val deathSuccess: Int, val deathFailure: Int, + val wildShapeAlteration: Alteration?, ) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationViewModel.kt index 61113bf..d7638bd 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationViewModel.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.pixelized.rplexicon.R import com.pixelized.rplexicon.business.AlterationSortUseCase +import com.pixelized.rplexicon.business.isWildShape import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.Description import com.pixelized.rplexicon.data.model.alteration.Alteration @@ -88,9 +89,18 @@ class AlterationViewModel @Inject constructor( fun toggleAlteration(alteration: String) { val key = AlterationStatus.Key(character = character, alteration = alteration) + val value = firebaseRepository.getAlterationStatusSnapshot(key = key) + // check if this is a wild shape alteration, if it is clear the hit point before activating it. + if (isWildShape(alteration) && !value.value) { + firebaseRepository.setCharacterHitPoint( + character = character, + wildShape = null, + ) + } + // switch the alteration status firebaseRepository.setAlterationStatus( key = key, - value = firebaseRepository.getAlterationStatusSnapshot(key = key).switch(), + value = value.switch(), ) }