Rework a bit the dice throw animation.
This commit is contained in:
parent
248a51f396
commit
aeb8eaf9ff
7 changed files with 192 additions and 163 deletions
|
|
@ -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) } }
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
@ -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,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue