Rework a bit the dice throw animation.

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2024-08-08 17:27:07 +02:00
parent 248a51f396
commit aeb8eaf9ff
7 changed files with 192 additions and 163 deletions

View file

@ -68,7 +68,7 @@ class AlterationRepository @Inject constructor(
* get all [Alteration] for a character * get all [Alteration] for a character
* @return a list of alterations. * @return a list of alterations.
*/ */
fun getAssignedAlterations(character: String): Flow<List<Alteration>> { fun getAssignedAlterations(character: String?): Flow<List<Alteration>> {
return assignedAlterations.map { alterations -> return assignedAlterations.map { alterations ->
alterations[character] ?: emptyList() alterations[character] ?: emptyList()
} }
@ -81,8 +81,8 @@ class AlterationRepository @Inject constructor(
* @return a list of alterations. * @return a list of alterations.
*/ */
fun getAssignedAlterations( fun getAssignedAlterations(
character: String, character: String?,
vararg properties: Property properties: List<Property>,
): Flow<List<Alteration>> { ): Flow<List<Alteration>> {
return getAssignedAlterations(character = character).map { alterations -> return getAssignedAlterations(character = character).map { alterations ->
alterations.filter { it.status.keys.any { key -> properties.contains(key) } } alterations.filter { it.status.keys.any { key -> properties.contains(key) } }

View file

@ -86,6 +86,8 @@ import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDice
import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio
import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCard import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCard
import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio
import com.pixelized.rplexicon.ui.screens.rolls.composable.rememberSlideInOutAnimation
import com.pixelized.rplexicon.ui.screens.rolls.composable.slideInOutAnimation
import com.pixelized.rplexicon.ui.screens.rolls.preview.rememberRollAlterations import com.pixelized.rplexicon.ui.screens.rolls.preview.rememberRollAlterations
import com.pixelized.rplexicon.ui.theme.LexiconTheme import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.lexicon import com.pixelized.rplexicon.utilitary.extentions.lexicon
@ -167,13 +169,9 @@ private fun RollOverlayContent(
onAlteration: (id: String) -> Unit, onAlteration: (id: String) -> Unit,
onThrowVisibilityChange: (Boolean) -> Unit, onThrowVisibilityChange: (Boolean) -> Unit,
) { ) {
val density = LocalDensity.current
val enableDrawer = remember { val enableDrawer = remember {
derivedStateOf { derivedStateOf { groups.value.isNotEmpty() }
groups.value.isNotEmpty()
}
} }
ModalNavigationDrawer( ModalNavigationDrawer(
modifier = modifier, modifier = modifier,
drawerState = drawer, drawerState = drawer,
@ -279,9 +277,9 @@ private fun RollOverlayContent(
Box( Box(
modifier = Modifier modifier = Modifier
.clickable( .clickable(
onClick = onClose,
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = null, indication = null,
onClick = onClose,
) )
.fillMaxSize() .fillMaxSize()
.padding(paddingValues), .padding(paddingValues),
@ -298,6 +296,17 @@ private fun RollOverlayContent(
criticalFailure = stringResource(id = R.string.roll_overlay__critical_failure), criticalFailure = stringResource(id = R.string.roll_overlay__critical_failure),
) )
Card(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.padding(bottom = if (enableDrawer.value) 32.dp else 0.dp),
card = card,
isDarkTheme = isDarkTheme,
showDetail = showDetail,
onCard = onCard
)
AnimatedVisibility( AnimatedVisibility(
modifier = Modifier.align(alignment = Alignment.BottomEnd), modifier = Modifier.align(alignment = Alignment.BottomEnd),
visible = enableDrawer.value, visible = enableDrawer.value,
@ -309,33 +318,6 @@ private fun RollOverlayContent(
) )
} }
} }
AnimatedContent(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter),
targetState = card.value,
transitionSpec = {
animation(density = density) using SizeTransform(clip = false)
},
label = "RollOverlayDisplay",
) {
when (it) {
null -> Box(
modifier = Modifier.fillMaxWidth(),
)
else -> ThrowsCard(
modifier = Modifier
.padding(bottom = if (enableDrawer.value) 32.dp else 0.dp)
.padding(all = 16.dp),
isDarkTheme = isDarkTheme,
throws = it,
showDetail = showDetail,
onClick = onCard,
)
}
}
} }
} }
) )
@ -343,6 +325,33 @@ private fun RollOverlayContent(
) )
} }
@Composable
private fun Card(
modifier: Modifier = Modifier,
card: State<ThrowsCardUio?>,
isDarkTheme: Boolean,
showDetail: State<Boolean>,
onCard: () -> Unit
) {
AnimatedContent(
modifier = modifier,
targetState = card.value,
transitionSpec = rememberSlideInOutAnimation(),
label = "RollOverlayDisplay",
) {
when (it) {
null -> Box(modifier = Modifier.fillMaxWidth())
else -> ThrowsCard(
modifier = Modifier.padding(all = 16.dp),
isDarkTheme = isDarkTheme,
throws = it,
showDetail = showDetail,
onClick = onCard,
)
}
}
}
private fun Modifier.detailPaddingBottom( private fun Modifier.detailPaddingBottom(
showDetail: State<Boolean>, showDetail: State<Boolean>,
bottom: Dp = 128.dp, bottom: Dp = 128.dp,
@ -459,12 +468,6 @@ private class RollOverlayPreviewProvider : PreviewParameterProvider<RollOverlayP
) )
} }
private fun animation(density: Density): ContentTransform {
val enter = fadeIn() + slideInVertically { with(density) { 64.dp.roundToPx() } }
val exit = fadeOut() + slideOutVertically { with(density) { -64.dp.roundToPx() } }
return enter togetherWith exit
}
@Stable @Stable
interface BlurredRollOverlayHostState : BlurredOverlayHostState { interface BlurredRollOverlayHostState : BlurredOverlayHostState {
suspend fun prepareRoll(diceThrow: DiceThrow) suspend fun prepareRoll(diceThrow: DiceThrow)

View file

@ -5,6 +5,7 @@ import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.spring import androidx.compose.animation.core.spring
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -14,7 +15,6 @@ import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.business.AlterationSortUseCase import com.pixelized.rplexicon.business.AlterationSortUseCase
import com.pixelized.rplexicon.business.DiceThrowUseCase import com.pixelized.rplexicon.business.DiceThrowUseCase
import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Description
import com.pixelized.rplexicon.data.model.DiceThrow import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.model.alteration.AlterationStatus import com.pixelized.rplexicon.data.model.alteration.AlterationStatus
import com.pixelized.rplexicon.data.repository.character.ActiveAlterationRepository import com.pixelized.rplexicon.data.repository.character.ActiveAlterationRepository
@ -39,8 +39,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -48,9 +50,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class RollOverlayViewModel @Inject constructor( class RollOverlayViewModel @Inject constructor(
private val characterSheetRepository: CharacterSheetRepository,
private val alterationRepository: AlterationRepository, private val alterationRepository: AlterationRepository,
private val activeAlterationRepository: ActiveAlterationRepository,
private val descriptionRepository: DescriptionRepository, private val descriptionRepository: DescriptionRepository,
private val rollUseCase: DiceThrowUseCase, private val rollUseCase: DiceThrowUseCase,
private val diceFactory: DiceFactory, private val diceFactory: DiceFactory,
@ -58,19 +58,47 @@ class RollOverlayViewModel @Inject constructor(
private val firebaseRepository: CharacterFireSheetRepository, private val firebaseRepository: CharacterFireSheetRepository,
private val throwRepository: NetworkThrowFireRepository, private val throwRepository: NetworkThrowFireRepository,
private val throwCardFactory: ThrowCardFactory, private val throwCardFactory: ThrowCardFactory,
characterSheetRepository: CharacterSheetRepository,
activeAlterationRepository: ActiveAlterationRepository,
application: Application, application: Application,
) : AndroidViewModel(application) { ) : AndroidViewModel(application) {
private var sheet: CharacterSheet? = null private val _diceThrow = MutableStateFlow<DiceThrow?>(null)
private var rollJob: Job? = null
private var alterationJob: Job? = null private val _alterationsOverride = MutableStateFlow<Map<String, Boolean>>(emptyMap())
private lateinit var diceThrow: DiceThrow private val _alterations: StateFlow<List<AlterationItemUio>> = combine(
_diceThrow,
descriptionRepository.data,
activeAlterationRepository.getActiveAlterations(),
_alterationsOverride,
) { diceThrow, descriptions, actives, override ->
alterationFactory.convertThrowAlterationItem(
diceThrow = diceThrow,
description = descriptions,
checked = actives[diceThrow?.character]?.associate { it.name to true } ?: emptyMap(),
override = override,
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.Lazily,
initialValue = emptyList(),
)
private val _characterSheet: StateFlow<CharacterSheet?> = combine(
characterSheetRepository.data,
_diceThrow,
) { sheets, diceThrow ->
sheets[diceThrow?.character]
}.stateIn(
scope = viewModelScope,
started = SharingStarted.Lazily,
initialValue = null,
)
private val _alterations = MutableStateFlow<List<AlterationItemUio>>(emptyList())
private val _alterationStatusOverride = MutableStateFlow<Map<String, Boolean>>(emptyMap())
val alterations: State<List<AlterationGroupUio>> val alterations: State<List<AlterationGroupUio>>
@Composable @Composable
get() = _alterations.map { data -> @Stable
data.groupBy { it.source } get() = combine(_characterSheet, _alterations) { sheet, alterations ->
alterations.groupBy { it.source }
.toSortedMap(AlterationSortUseCase.sort(sheet = sheet)) .toSortedMap(AlterationSortUseCase.sort(sheet = sheet))
.map { entry -> .map { entry ->
AlterationGroupUio( AlterationGroupUio(
@ -80,7 +108,8 @@ class RollOverlayViewModel @Inject constructor(
} }
}.collectAsState(initial = emptyList()) }.collectAsState(initial = emptyList())
private var targetRoll = 0f private var _rollJob: Job? = null
private var _targetRoll = 0f
private val _dice = mutableStateOf<RollDiceUio?>(null) private val _dice = mutableStateOf<RollDiceUio?>(null)
val dice: State<RollDiceUio?> get() = _dice val dice: State<RollDiceUio?> get() = _dice
val diceRotation: Animatable<Float, AnimationVector1D> = Animatable(0f) val diceRotation: Animatable<Float, AnimationVector1D> = Animatable(0f)
@ -98,83 +127,56 @@ class RollOverlayViewModel @Inject constructor(
val alterationDetailDialog: State<AlterationDialogDetailUio?> get() = _alterationDetailDialog val alterationDetailDialog: State<AlterationDialogDetailUio?> get() = _alterationDetailDialog
suspend fun prepareRoll(diceThrow: DiceThrow) { suspend fun prepareRoll(diceThrow: DiceThrow) {
this.targetRoll = 0f // save the dice throw.
this.diceThrow = diceThrow _diceThrow.value = diceThrow
// reinitialise the roll parameter
_targetRoll = 0f
_rollJob?.cancel()
diceRotation.snapTo(_targetRoll)
// hide the throw for other player. // hide the throw for other player.
_isThrowHidden.value = diceThrow is DiceThrow.DeathSavingThrow _isThrowHidden.value = diceThrow is DiceThrow.DeathSavingThrow
// hide the detail throw card. // hide the detail throw card.
_card.value = null _card.value = null
// reset the override status // reset the override status
_alterationStatusOverride.value = emptyMap() _alterationsOverride.value = emptyMap()
// get the character sheet.
sheet = characterSheetRepository.find(
name = diceThrow.character,
)
// build the dice UIO. // build the dice UIO.
_dice.value = diceFactory.convertDiceThrow( _dice.value = withContext(Dispatchers.Default) {
diceThrow = diceThrow, diceFactory.convertDiceThrow(diceThrow = diceThrow)
)
// listen to alteration + active to build the list of applicable alteration.
alterationJob?.cancel()
alterationJob = viewModelScope.launch(Dispatchers.Default) {
val character = diceThrow.character
val struct = AlterationStruct()
descriptionRepository.data
.combine(activeAlterationRepository.getActiveAssignedAlterations(character = character)) { descriptions, actives ->
struct.descriptions = descriptions
struct.actives = actives.associate { it.name to true }
}
.combine(_alterationStatusOverride) { _, override ->
struct.override = override
}
.collect {
val (description, actives, override) = struct
val alterations = alterationFactory.convertThrowAlterationItem(
diceThrow = diceThrow,
description = description,
checked = actives,
override = override,
)
withContext(Dispatchers.Main) {
_alterations.value = alterations
}
}
} }
} }
fun onAlteration(id: String) {
val override = _alterationStatusOverride.value.toMutableMap().also {
it[id] = it[id]?.not() ?: firebaseRepository.getAlterationStatusSnapshot(
key = AlterationStatus.Key(character = diceThrow.character, alteration = id)
).value.not()
}
_alterationStatusOverride.value = override
}
fun roll(uiScope: CoroutineScope) { fun roll(uiScope: CoroutineScope) {
rollJob?.cancel() _rollJob?.cancel()
rollJob = uiScope.launch { _rollJob = uiScope.launch {
// roll the dice ;) // roll the dice ;)
val result = rollUseCase.roll( val result = withContext(Dispatchers.Default) {
diceThrow = diceThrow, _diceThrow.value?.let {
isThrowHidden = _isThrowHidden.value, rollUseCase.roll(
alterationId = _alterations.value.mapNotNull { if (it.checked) it.label else null }, diceThrow = it,
) isThrowHidden = _isThrowHidden.value,
alterationId = _alterations.value.mapNotNull { alteration ->
if (alteration.checked) alteration.label else null
},
)
}
}
if (result != null) { if (result != null) {
// reset the displayed dice value // reset the displayed dice value
_dice.value = result.dice.copy( _dice.value = withContext(Dispatchers.Default) {
result = null, result.dice.copy(
isCriticalSuccess = false, result = null,
isCriticalFailure = false, isCriticalSuccess = false,
) isCriticalFailure = false,
)
}
// play the roll animation // play the roll animation
_targetRoll += 720f
launch { launch {
targetRoll += 360f * 4
diceRotation.animateTo( diceRotation.animateTo(
targetValue = targetRoll, targetValue = _targetRoll,
animationSpec = spring( animationSpec = spring(
dampingRatio = 0.9f, dampingRatio = 0.8f,
stiffness = 105f, stiffness = 15f,
), ),
) )
} }
@ -184,9 +186,17 @@ class RollOverlayViewModel @Inject constructor(
if (isActive) { if (isActive) {
_dice.value = result.dice _dice.value = result.dice
// share the result // share the result
throwRepository.sendThrow(sheet?.name, result.throws) withContext(Dispatchers.IO) {
// Display the roll card. throwRepository.sendThrow(_characterSheet.value?.name, result.throws)
_card.value = throwCardFactory.convert(result.throws) }
}
// Wait a fix amount of time then
delay(RollDiceUio.ROLL_DURATION.toLong() + 250L)
// Display the roll card.
if (isActive) {
_card.value = withContext(Dispatchers.Default) {
throwCardFactory.convert(result.throws)
}
} }
} }
} }
@ -200,9 +210,21 @@ class RollOverlayViewModel @Inject constructor(
_isThrowHidden.value = hidden _isThrowHidden.value = hidden
} }
suspend fun showAlterationDetail(id: String) { fun onAlteration(id: String) {
val override = _alterationsOverride.value.toMutableMap().also {
it[id] = it[id]?.not() ?: firebaseRepository.getAlterationStatusSnapshot(
key = AlterationStatus.Key(
character = _diceThrow.value?.character ?: "",
alteration = id
)
).value.not()
}
_alterationsOverride.value = override
}
fun showAlterationDetail(id: String) {
val alteration = alterationRepository.getAssignedAlterationSnapshot( val alteration = alterationRepository.getAssignedAlterationSnapshot(
character = diceThrow.character, character = _diceThrow.value?.character,
alteration = id, alteration = id,
) )
val description = descriptionRepository.getDescription( val description = descriptionRepository.getDescription(
@ -224,10 +246,4 @@ class RollOverlayViewModel @Inject constructor(
fun hideAlterationDetail() { fun hideAlterationDetail() {
_alterationDetailDialog.value = null _alterationDetailDialog.value = null
} }
private data class AlterationStruct(
var descriptions: Map<String, Description> = emptyMap(),
var actives: Map<String, Boolean> = emptyMap(),
var override: Map<String, Boolean> = emptyMap(),
)
} }

View file

@ -1,18 +1,14 @@
package com.pixelized.rplexicon.ui.screens.rolls.composable package com.pixelized.rplexicon.ui.screens.rolls.composable
import android.content.res.Configuration
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.expandIn
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@ -43,7 +39,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.agsl.dancingColor import com.pixelized.rplexicon.ui.agsl.dancingColor
@ -59,7 +54,7 @@ data class RollDiceUio(
val isCriticalFailure: Boolean = false, val isCriticalFailure: Boolean = false,
val result: String? = null, val result: String? = null,
) { ) {
companion object{ companion object {
const val ROLL_DURATION = 500 const val ROLL_DURATION = 500
} }
} }
@ -146,33 +141,18 @@ private fun Result(
dice: State<RollDiceUio?>, dice: State<RollDiceUio?>,
) { ) {
AnimatedContent( AnimatedContent(
modifier = modifier,
targetState = dice.value?.result, targetState = dice.value?.result,
transitionSpec = { transitionSpec = rememberSlideInOutAnimation(),
val enter = fadeIn() +
expandIn(expandFrom = Alignment.Center) { it } +
slideInVertically { it / 5 }
val exit = fadeOut() +
shrinkOut(shrinkTowards = Alignment.Center) { IntSize.Zero } +
slideOutVertically { -it / 5 }
enter togetherWith exit using SizeTransform(clip = false)
},
label = "ResultAnimation" label = "ResultAnimation"
) { ) {
when (it) { Box(
null -> Box( modifier = modifier,
modifier = Modifier, contentAlignment = Alignment.Center,
) {
Text(
style = MaterialTheme.lexicon.typography.diceRoll,
text = it ?: "",
) )
else -> Box(
modifier = Modifier,
contentAlignment = Alignment.Center,
) {
Text(
style = MaterialTheme.lexicon.typography.diceRoll,
text = it,
)
}
} }
} }
} }
@ -221,7 +201,8 @@ private fun Critical(
} }
@Composable @Composable
@Preview @Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SkillRollPreview( private fun SkillRollPreview(
@PreviewParameter(SkillRollPreviewProvider::class) preview: Int, @PreviewParameter(SkillRollPreviewProvider::class) preview: Int,
) { ) {

View file

@ -0,0 +1,30 @@
package com.pixelized.rplexicon.ui.screens.rolls.composable
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform
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.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
@Composable
@Stable
fun <S> rememberSlideInOutAnimation(): AnimatedContentTransitionScope<S>.() -> ContentTransform {
val density = LocalDensity.current
return remember(density) {
{ slideInOutAnimation(density = density) }
}
}
fun slideInOutAnimation(density: Density): ContentTransform {
val enter = fadeIn() + slideInVertically { with(density) { 64.dp.roundToPx() } }
val exit = fadeOut() + slideOutVertically { with(density) { -64.dp.roundToPx() } }
return enter togetherWith exit
}

View file

@ -36,7 +36,7 @@ class AlterationFactory @Inject constructor(
} }
suspend fun convertThrowAlterationItem( suspend fun convertThrowAlterationItem(
diceThrow: DiceThrow, diceThrow: DiceThrow?,
description: Map<String, Description>, description: Map<String, Description>,
checked: Map<String, Boolean>, checked: Map<String, Boolean>,
override: Map<String, Boolean>, override: Map<String, Boolean>,
@ -139,10 +139,12 @@ class AlterationFactory @Inject constructor(
} }
is DiceThrow.Object -> listOf(Property.OBJECT_EFFECT) is DiceThrow.Object -> listOf(Property.OBJECT_EFFECT)
null -> emptyList()
} }
return alterationRepository return alterationRepository
.getAssignedAlterations(character = diceThrow.character, *properties.toTypedArray()) .getAssignedAlterations(character = diceThrow?.character, properties)
.firstOrNull() .firstOrNull()
?.map { alteration -> ?.map { alteration ->
AlterationItemUio( AlterationItemUio(

View file

@ -187,10 +187,7 @@ fun lexiconTypography(
), ),
diceRoll: TextStyle = base.displayMedium.copy( diceRoll: TextStyle = base.displayMedium.copy(
shadow = when (isDarkTheme) { shadow = when (isDarkTheme) {
true -> Shadow( true -> Shadow(blurRadius = 12f)
offset = Offset(4f, 4f),
blurRadius = 8f,
)
else -> null else -> null
} }
), ),