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 46d68bb..173f0c7 100644 --- a/app/src/main/java/com/pixelized/rplexicon/business/DiceThrowUseCase.kt +++ b/app/src/main/java/com/pixelized/rplexicon/business/DiceThrowUseCase.kt @@ -843,7 +843,7 @@ class DiceThrowUseCase @Inject constructor( ) // fetch and build a list of additional level effect. - val levelBonus = if (spell?.level != null) { + val levelBonus = if (spell?.level?.dice != null) { ((spell.spell.level + 1)..level).map { val localRoll = roll( amount = spell.level.dice.count, diff --git a/app/src/main/java/com/pixelized/rplexicon/data/model/roll/Throw.kt b/app/src/main/java/com/pixelized/rplexicon/data/model/roll/Throw.kt index 5063917..1252af1 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/model/roll/Throw.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/model/roll/Throw.kt @@ -3,7 +3,7 @@ package com.pixelized.rplexicon.data.model.roll import com.pixelized.rplexicon.data.model.Property class Throw( - val dice: Dice, + val dice: Dice?, val flat: Flat?, val modifier: List, ) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/parser/roll/ThrowParser.kt b/app/src/main/java/com/pixelized/rplexicon/data/parser/roll/ThrowParser.kt index 8f20f59..881dc70 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/parser/roll/ThrowParser.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/parser/roll/ThrowParser.kt @@ -10,14 +10,11 @@ class ThrowParser @Inject constructor( ) { fun parse(value: String?): Throw? { if (value != null) { - val dice = diceParser.parse(value = value).firstOrNull() - if (dice != null) { - return Throw( - dice = dice, - flat = flatValueParser.parse(value = value), - modifier = modifierParser.parse(value = value), - ) - } + return Throw( + dice = diceParser.parse(value = value).firstOrNull(), + flat = flatValueParser.parse(value = value), + modifier = modifierParser.parse(value = value), + ) } return null } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/AttackItem.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/AttackItem.kt index b9f7a62..ad7df27 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/AttackItem.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/AttackItem.kt @@ -32,6 +32,7 @@ import com.pixelized.rplexicon.R import com.pixelized.rplexicon.data.model.Attack import com.pixelized.rplexicon.ui.composable.AsyncImage import com.pixelized.rplexicon.ui.screens.character.composable.common.DiceButton +import com.pixelized.rplexicon.ui.screens.character.composable.common.FlatValue import com.pixelized.rplexicon.ui.theme.LexiconTheme import com.pixelized.rplexicon.utilitary.extentions.uri @@ -42,14 +43,22 @@ data class AttackUio( val name: String, @StringRes val type: Int, val range: String?, - val hit: Dice?, - val damage: Dice?, + val hit: Action?, + val damage: Action?, ) { @Stable - class Dice( - @DrawableRes val icon: Int, - val label: String, - ) + sealed class Action { + @Stable + class Dice( + @DrawableRes val icon: Int, + val label: String, + ) : Action() + + @Stable + class Flat( + val label: String, + ) : Action() + } } @Composable @@ -106,19 +115,31 @@ fun Attack( Row( horizontalArrangement = Arrangement.spacedBy(space = 4.dp), ) { - weapon.hit?.let { dice -> - DiceButton( - icon = dice.icon, - text = dice.label, - onClick = { weapon.name.let(onHit) } - ) + weapon.hit?.let { action -> + when (action) { + is AttackUio.Action.Dice -> DiceButton( + icon = action.icon, + text = action.label, + onClick = { weapon.name.let(onHit) } + ) + + is AttackUio.Action.Flat -> FlatValue( + text = action.label, + ) + } } - weapon.damage?.let { dice -> - DiceButton( - icon = dice.icon, - text = dice.label, - onClick = { weapon.name.let(onDamage) } - ) + weapon.damage?.let { action -> + when (action) { + is AttackUio.Action.Dice -> DiceButton( + icon = action.icon, + text = action.label, + onClick = { weapon.name.let(onDamage) } + ) + + is AttackUio.Action.Flat -> FlatValue( + text = action.label, + ) + } } } } @@ -147,19 +168,19 @@ private class WeaponPreviewProvider : PreviewParameterProvider { override val values: Sequence = sequenceOf( AttackUio( icon = R.drawable.ic_crossed_swords_24.uri.toUri(), - name = "Dagger", + name = "Sans arme", type = R.string.attack_type_melee, range = "5 ft reach", - hit = AttackUio.Dice(icon = R.drawable.ic_d20_24, label = "1d20"), - damage = AttackUio.Dice(icon = R.drawable.ic_d8_24, label = "1d8"), + hit = AttackUio.Action.Dice(icon = R.drawable.ic_d20_24, label = "1d20"), + damage = AttackUio.Action.Flat(label = "1"), ), AttackUio( icon = R.drawable.ic_pocket_bow_24.uri.toUri(), name = "Long bow", type = R.string.attack_type_range, range = "30 ft reach", - hit = AttackUio.Dice(icon = R.drawable.ic_d20_24, label = "1d20+5"), - damage = AttackUio.Dice(icon = R.drawable.ic_d8_24, label = "1d8+3"), + hit = AttackUio.Action.Dice(icon = R.drawable.ic_d20_24, label = "1d20+5"), + damage = AttackUio.Action.Dice(icon = R.drawable.ic_d8_24, label = "1d8+3"), ), ) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/SkillItem.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/SkillItem.kt index 974a755..6dfb887 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/SkillItem.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/SkillItem.kt @@ -33,6 +33,7 @@ import com.pixelized.rplexicon.R import com.pixelized.rplexicon.ui.composable.AsyncImage import com.pixelized.rplexicon.ui.screens.character.composable.common.CounterButton import com.pixelized.rplexicon.ui.screens.character.composable.common.DiceButton +import com.pixelized.rplexicon.ui.screens.character.composable.common.FlatValue import com.pixelized.rplexicon.ui.theme.LexiconTheme @Stable @@ -42,15 +43,24 @@ data class SkillItemUio( val translate: String?, val rest: String?, val cost: String?, - val effect: Dice?, + val effect: Action?, val value: Int?, val max: Int?, val haveDetail: Boolean, ) { - class Dice( - @DrawableRes val icon: Int, - val label: String, - ) + @Stable + sealed class Action { + @Stable + class Dice( + @DrawableRes val icon: Int, + val label: String, + ) : Action() + + @Stable + class Flat( + val label: String, + ) : Action() + } } @Composable @@ -131,11 +141,17 @@ fun SkillItem( } skill.effect?.let { effect -> - DiceButton( - icon = effect.icon, - text = effect.label, - onClick = { onThrow(skill) }, - ) + when (effect) { + is SkillItemUio.Action.Dice -> DiceButton( + icon = effect.icon, + text = effect.label, + onClick = { onThrow(skill) }, + ) + + is SkillItemUio.Action.Flat -> FlatValue( + text = effect.label, + ) + } } skill.max?.let { @@ -193,7 +209,7 @@ private class CounterItemPreviewProvider : PreviewParameterProvider - DiceButton( - icon = dice.icon, - text = dice.label, - onClick = { spell.name.let(onHit) } - ) + spell.hit?.let { action -> + when (action) { + is SpellUio.Action.Dice -> DiceButton( + icon = action.icon, + text = action.label, + onClick = { spell.name.let(onHit) } + ) + + is SpellUio.Action.Flat -> FlatValue( + text = action.label, + ) + } } - spell.effect?.let { dice -> + spell.effect?.let { action -> when { spell.changeWithLevel && spell.isWarlock.not() -> { OutlinedButton( @@ -196,19 +212,33 @@ fun Spell( ) } } + spell.changeWithLevel && spell.isWarlock -> { - DiceButton( - icon = dice.icon, - text = dice.label, - onClick = { onCast(spell.name) }, - ) + when (action) { + is SpellUio.Action.Dice -> DiceButton( + icon = action.icon, + text = action.label, + onClick = { onCast(spell.name) }, + ) + + is SpellUio.Action.Flat -> FlatValue( + text = action.label, + ) + } } + else -> { - DiceButton( - icon = dice.icon, - text = dice.label, - onClick = { spell.name.let(onEffect) } - ) + when (action) { + is SpellUio.Action.Dice -> DiceButton( + icon = action.icon, + text = action.label, + onClick = { spell.name.let(onEffect) } + ) + + is SpellUio.Action.Flat -> FlatValue( + text = action.label, + ) + } } } } @@ -246,11 +276,11 @@ private class SpellPreviewProvider : PreviewParameterProvider { castingTime = "1 action", range = "36 mêtres", duration = "instantanée", - hit = SpellUio.Dice( + hit = SpellUio.Action.Dice( icon = R.drawable.ic_d20_24, label = "1d20+6", ), - effect = SpellUio.Dice( + effect = SpellUio.Action.Dice( icon = R.drawable.ic_d10_24, label = "1d10", ), @@ -267,7 +297,7 @@ private class SpellPreviewProvider : PreviewParameterProvider { range = "18 mêtres", duration = "instantanée", hit = null, - effect = SpellUio.Dice( + effect = SpellUio.Action.Dice( icon = R.drawable.ic_d10_24, label = "1d4", ), @@ -284,7 +314,7 @@ private class SpellPreviewProvider : PreviewParameterProvider { range = "contact", duration = "instantanée", hit = null, - effect = SpellUio.Dice( + effect = SpellUio.Action.Dice( icon = R.drawable.ic_d8_24, label = "1d8+3", ), @@ -301,7 +331,7 @@ private class SpellPreviewProvider : PreviewParameterProvider { range = "contact", duration = "instantanée", hit = null, - effect = SpellUio.Dice( + effect = SpellUio.Action.Dice( icon = R.drawable.ic_d8_24, label = "1d8+3", ), diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/common/FlatValue.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/common/FlatValue.kt new file mode 100644 index 0000000..d10741b --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/common/FlatValue.kt @@ -0,0 +1,42 @@ +package com.pixelized.rplexicon.ui.screens.character.composable.common + +import android.content.res.Configuration +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.minimumInteractiveComponentSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.pixelized.rplexicon.ui.theme.LexiconTheme + +@Composable +fun FlatValue( + modifier: Modifier = Modifier, + text: String, +) { + Column( + modifier = modifier.minimumInteractiveComponentSize(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + style = MaterialTheme.typography.titleLarge, + text = text, + ) + } +} + +@Composable +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun DiceButtonPreview() { + LexiconTheme { + Surface { + FlatValue( + text = "5", + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberAttackListStatePreview.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberAttackListStatePreview.kt index bb47ce9..4f7f582 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberAttackListStatePreview.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberAttackListStatePreview.kt @@ -17,16 +17,15 @@ fun rememberAttackListStatePreview(): State> = remember { listOf( AttackUio( icon = R.drawable.ic_crossed_swords_24.uri.toUri(), - name = "Dagger", + name = "Unarmed attack", type = R.string.attack_type_melee, range = "5 ft reach", - hit = AttackUio.Dice( + hit = AttackUio.Action.Dice( icon = R.drawable.ic_d20_24, label = "1d20", ), - damage = AttackUio.Dice( - icon = R.drawable.ic_d8_24, - label = "1d8", + damage = AttackUio.Action.Flat( + label = "5", ), ), AttackUio( @@ -34,11 +33,11 @@ fun rememberAttackListStatePreview(): State> = remember { name = "Long bow", type = R.string.attack_type_range, range = "30 ft reach", - hit = AttackUio.Dice( + hit = AttackUio.Action.Dice( icon = R.drawable.ic_d20_24, label = "1d20+5", ), - damage = AttackUio.Dice( + damage = AttackUio.Action.Dice( icon = R.drawable.ic_d8_24, label = "1d8+3" ), diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberSpellListStatePreview.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberSpellListStatePreview.kt index 6dc596a..a3910f5 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberSpellListStatePreview.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/preview/rememberSpellListStatePreview.kt @@ -30,11 +30,11 @@ fun rememberSpellListStatePreview(): State characterSheet.dexterity.modifier else -> 0 } - } + hitAlteration + } + hitAlteration + (diceThrow.flat?.value ?: 0) // Build the UIO. - AttackUio.Dice( - icon = diceThrow.dice.icon, - label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}", - ) + when (diceThrow.dice) { + null -> AttackUio.Action.Flat( + label = "$modifier", + ) + + else -> AttackUio.Action.Dice( + icon = diceThrow.dice.icon, + label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}", + ) + } } val damage = attack.damage?.let { diceThrow -> @@ -52,12 +58,18 @@ class AttackUioFactory @Inject constructor() { Property.DEXTERITY -> characterSheet.dexterity.modifier else -> 0 } - } + damageAlteration + } + damageAlteration + (diceThrow.flat?.value ?: 0) // Build the UIO. - AttackUio.Dice( - icon = diceThrow.dice.icon, - label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}", - ) + when (diceThrow.dice) { + null -> AttackUio.Action.Flat( + label = "$modifier", + ) + + else -> AttackUio.Action.Dice( + icon = diceThrow.dice.icon, + label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}" + ) + } } return AttackUio( diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SkillFactoryUioFactory.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SkillFactoryUioFactory.kt index d436cf7..23f8def 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SkillFactoryUioFactory.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SkillFactoryUioFactory.kt @@ -7,11 +7,11 @@ import com.pixelized.rplexicon.data.network.CharacterSheetFire import com.pixelized.rplexicon.data.repository.character.DescriptionRepository import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillItemUio import com.pixelized.rplexicon.utilitary.extentions.icon -import com.pixelized.rplexicon.utilitary.extentions.toLabel import com.pixelized.rplexicon.utilitary.extentions.local.base import com.pixelized.rplexicon.utilitary.extentions.local.primary import com.pixelized.rplexicon.utilitary.extentions.local.secondary import com.pixelized.rplexicon.utilitary.extentions.modifier +import com.pixelized.rplexicon.utilitary.extentions.toLabel import javax.inject.Inject class SkillFactoryUioFactory @Inject constructor( @@ -49,11 +49,17 @@ class SkillFactoryUioFactory @Inject constructor( translate = description?.original, rest = skill.rest, cost = skill.cost, - effect = skill.effect?.let { - SkillItemUio.Dice( - icon = it.dice.icon, + effect = when { + skill.effect?.dice != null -> SkillItemUio.Action.Dice( + icon = skill.effect.dice.icon, label = "${skill.effect.dice.toLabel()}${modifier.toLabel(true)}", ) + + skill.effect != null -> SkillItemUio.Action.Flat( + label = "$modifier", + ) + + else -> null }, value = fire?.skills?.get(skill.name) ?: 0, max = skill.amount, diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SpellUioFactory.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SpellUioFactory.kt index 17bb4a4..95aadfd 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SpellUioFactory.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SpellUioFactory.kt @@ -1,13 +1,14 @@ package com.pixelized.rplexicon.ui.screens.character.factory +import com.pixelized.rplexicon.R import com.pixelized.rplexicon.data.model.AssignedSpell import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.Property import com.pixelized.rplexicon.data.repository.character.DescriptionRepository import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellUio import com.pixelized.rplexicon.utilitary.extentions.icon -import com.pixelized.rplexicon.utilitary.extentions.toLabel import com.pixelized.rplexicon.utilitary.extentions.modifier +import com.pixelized.rplexicon.utilitary.extentions.toLabel import javax.inject.Inject class SpellUioFactory @Inject constructor( @@ -28,11 +29,17 @@ class SpellUioFactory @Inject constructor( Property.CHARISMA -> characterSheet.charisma.modifier else -> 0 } + } + (diceThrow.flat?.value ?: 0) + when (diceThrow.dice) { + null -> SpellUio.Action.Flat( + label = "$modifier", + ) + + else -> SpellUio.Action.Dice( + icon = diceThrow.dice.icon, + label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}", + ) } - SpellUio.Dice( - icon = diceThrow.dice.icon, - label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}", - ) } val effect = assignedSpell.effect?.let { diceThrow -> val modifier = diceThrow.modifier.sumOf { @@ -52,25 +59,38 @@ class SpellUioFactory @Inject constructor( val delta = warlockSpellLevel?.minus(assignedSpell.spell.level) ?: 0 if (warlockSpellLevel == null || level == null || delta <= 0) { // default case of non warlock character of the spell don't scale - SpellUio.Dice( - icon = diceThrow.dice.icon, - label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}", - ) - } else if (diceThrow.dice.faces == level.dice.faces) { + when (diceThrow.dice) { + null -> SpellUio.Action.Flat( + label = "$modifier", + ) + + else -> SpellUio.Action.Dice( + icon = diceThrow.dice.icon, + label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}", + ) + } + } else if (diceThrow.dice?.faces == level.dice?.faces) { // warlock character, upscale the spell to warlock spell level val upscaleModifier = modifier + (level.flat?.value ?: 0) * delta - val diceCount = diceThrow.dice.count + level.dice.count * delta - SpellUio.Dice( - icon = diceThrow.dice.icon, - label = "${diceCount}d${diceThrow.dice.faces}${upscaleModifier.toLabel(true)}", - ) + val diceCount = (diceThrow.dice?.count ?: 0) + (level.dice?.count ?: 0) * delta + + when (diceThrow.dice) { + null -> SpellUio.Action.Flat( + label = "$modifier", + ) + + else -> SpellUio.Action.Dice( + icon = diceThrow.dice.icon, + label = "${diceCount}d${diceThrow.dice.faces}${upscaleModifier.toLabel(true)}", + ) + } } else { // warlock character, case where the dice use to upscale is not the one from the spell. val deltaModifier = level.flat?.value?.times(delta) ?: 0 - SpellUio.Dice( - icon = diceThrow.dice.icon, - label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}" + - "+ ${level.dice.count * delta}d${level.dice.faces}${deltaModifier.toLabel(true)}", + SpellUio.Action.Dice( + icon = diceThrow.dice?.icon ?: R.drawable.ic_d4_24, + label = "${diceThrow.dice?.toLabel()}${modifier.toLabel(true)}" + + "+ ${level.dice?.count?.times(delta)}d${level.dice?.faces}${deltaModifier.toLabel(true)}", ) } } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SpellsViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SpellsViewModel.kt index d32849f..5d16539 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SpellsViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SpellsViewModel.kt @@ -180,7 +180,7 @@ class SpellsViewModel @Inject constructor( val modifierLabel = modifier .sumOf { modifier -> modifier.toValue(character = character) } .let { if (it != 0) "${it.signLabel}${abs(it) * level}" else "" } - return when (level > 0) { + return when (level > 0 && dice != null) { true -> "${dice.count * level}d${dice.faces}${modifierLabel}" else -> null }