Clean up old roll code + refactor some ugly stuff.

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2024-06-01 22:39:20 +02:00
parent 8808040e14
commit 8841529b31
26 changed files with 251 additions and 244 deletions

View file

@ -9,7 +9,8 @@ import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.DiceThrow import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.model.Property import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.Skill import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.model.Throw import com.pixelized.rplexicon.data.model.roll.Flat
import com.pixelized.rplexicon.data.model.roll.Throw
import com.pixelized.rplexicon.data.network.NetworkThrow import com.pixelized.rplexicon.data.network.NetworkThrow
import com.pixelized.rplexicon.data.repository.character.ActionRepository import com.pixelized.rplexicon.data.repository.character.ActionRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository import com.pixelized.rplexicon.data.repository.character.AlterationRepository
@ -19,6 +20,7 @@ import com.pixelized.rplexicon.data.repository.character.SkillRepository
import com.pixelized.rplexicon.data.repository.character.SpellRepository import com.pixelized.rplexicon.data.repository.character.SpellRepository
import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio
import com.pixelized.rplexicon.utilitary.extentions.icon import com.pixelized.rplexicon.utilitary.extentions.icon
import com.pixelized.rplexicon.utilitary.extentions.toLabel
import com.pixelized.rplexicon.utilitary.extentions.local.advantage import com.pixelized.rplexicon.utilitary.extentions.local.advantage
import com.pixelized.rplexicon.utilitary.extentions.local.base import com.pixelized.rplexicon.utilitary.extentions.local.base
import com.pixelized.rplexicon.utilitary.extentions.local.critical import com.pixelized.rplexicon.utilitary.extentions.local.critical
@ -34,7 +36,6 @@ import com.pixelized.rplexicon.utilitary.extentions.local.sum
import com.pixelized.rplexicon.utilitary.extentions.local.toStatus import com.pixelized.rplexicon.utilitary.extentions.local.toStatus
import com.pixelized.rplexicon.utilitary.extentions.masteryMultiplier import com.pixelized.rplexicon.utilitary.extentions.masteryMultiplier
import com.pixelized.rplexicon.utilitary.extentions.modifier import com.pixelized.rplexicon.utilitary.extentions.modifier
import com.pixelized.rplexicon.utilitary.extentions.toLabel
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
@ -748,7 +749,7 @@ class DiceThrowUseCase @Inject constructor(
// compute the amount of main dice to throw. // compute the amount of main dice to throw.
val amount = if (status.isCritical) { val amount = if (status.isCritical) {
diceThrow?.amount?.times(2)?.let { diceThrow?.dice?.count?.times(2)?.let {
if (ability == Property.PHYSICAL_MELEE_DAMAGE && status.isSavageAttacks) if (ability == Property.PHYSICAL_MELEE_DAMAGE && status.isSavageAttacks)
it.plus(1) else it it.plus(1) else it
}?.let { }?.let {
@ -756,12 +757,12 @@ class DiceThrowUseCase @Inject constructor(
it.plus(1) else it it.plus(1) else it
} }
} else { } else {
diceThrow?.amount diceThrow?.dice?.count
} }
// main roll // main roll
val result = roll( val result = roll(
amount = amount ?: 1, amount = amount ?: 1,
faces = diceThrow?.faces ?: 20, faces = diceThrow?.dice?.faces ?: 20,
advantage = advantage, advantage = advantage,
disadvantage = disadvantage, disadvantage = disadvantage,
emphasis = emphasis, emphasis = emphasis,
@ -791,7 +792,7 @@ class DiceThrowUseCase @Inject constructor(
// build the result. // build the result.
return DiceThrowResult( return DiceThrowResult(
dice = RollDiceUio( dice = RollDiceUio(
icon = diceThrow?.faces?.icon ?: R.drawable.ic_d20_24, icon = diceThrow?.dice?.icon ?: R.drawable.ic_d20_24,
isCriticalSuccess = canMakeCriticalRoll && result.value == 20, isCriticalSuccess = canMakeCriticalRoll && result.value == 20,
isCriticalFailure = canMakeCriticalRoll && result.value == 1, isCriticalFailure = canMakeCriticalRoll && result.value == 1,
result = rollResult, result = rollResult,
@ -800,7 +801,7 @@ class DiceThrowUseCase @Inject constructor(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
title = titleString.uppercase(), title = titleString.uppercase(),
highlight = action.uppercase(), highlight = action.uppercase(),
face = diceThrow?.faces ?: 20, face = diceThrow?.dice?.faces ?: 20,
isCriticalSuccess = canMakeCriticalRoll && result.value == 20, isCriticalSuccess = canMakeCriticalRoll && result.value == 20,
isCriticalFailure = canMakeCriticalRoll && result.value == 1, isCriticalFailure = canMakeCriticalRoll && result.value == 1,
roll = allValue.toLabel(), roll = allValue.toLabel(),
@ -809,11 +810,11 @@ class DiceThrowUseCase @Inject constructor(
NetworkThrow.Detail( NetworkThrow.Detail(
title = action, title = action,
throws = NetworkThrow.Throw( throws = NetworkThrow.Throw(
dice = diceThrow?.faces?.icon ?: R.drawable.ic_d20_24, dice = diceThrow?.dice?.icon ?: R.drawable.ic_d20_24,
advantage = advantage, advantage = advantage,
disadvantage = disadvantage, disadvantage = disadvantage,
emphasis = emphasis, emphasis = emphasis,
roll = "${diceThrow?.amount}d${diceThrow?.faces}", roll = "${diceThrow?.dice?.count}d${diceThrow?.dice?.faces}",
result = result.label, result = result.label,
), ),
result = "${result.value}", result = "${result.value}",
@ -840,8 +841,8 @@ class DiceThrowUseCase @Inject constructor(
val emphasis = status[Property.SPELL_EFFECT].emphasis val emphasis = status[Property.SPELL_EFFECT].emphasis
// main roll // main roll
val result = roll( val result = roll(
amount = spell?.effect?.amount ?: 1, amount = spell?.effect?.dice?.count ?: 1,
faces = spell?.effect?.faces ?: 4, faces = spell?.effect?.dice?.faces ?: 4,
advantage = advantage, advantage = advantage,
disadvantage = disadvantage, disadvantage = disadvantage,
emphasis = emphasis, emphasis = emphasis,
@ -849,12 +850,12 @@ class DiceThrowUseCase @Inject constructor(
critical = status[Property.SPELL_EFFECT].critical, critical = status[Property.SPELL_EFFECT].critical,
) )
// fetch and build a list of additionnal level effect. // fetch and build a list of additional level effect.
val levelBonus = if (spell?.level != null) { val levelBonus = if (spell?.level != null) {
((spell.spell.level + 1)..level).map { ((spell.spell.level + 1)..level).map {
val localRoll = roll( val localRoll = roll(
amount = spell.level.amount, amount = spell.level.dice.count,
faces = spell.level.faces, faces = spell.level.dice.faces,
advantage = advantage, advantage = advantage,
disadvantage = disadvantage, disadvantage = disadvantage,
emphasis = emphasis, emphasis = emphasis,
@ -864,8 +865,8 @@ class DiceThrowUseCase @Inject constructor(
NetworkThrow.Detail( NetworkThrow.Detail(
title = application.getString(R.string.spell_level_chooser_label, "$it"), title = application.getString(R.string.spell_level_chooser_label, "$it"),
throws = NetworkThrow.Throw( throws = NetworkThrow.Throw(
dice = spell.level.faces.icon, dice = spell.level.dice.icon,
roll = "${spell.level.amount}d${spell.level.faces}", roll = spell.level.dice.toLabel(),
advantage = advantage, advantage = advantage,
disadvantage = disadvantage, disadvantage = disadvantage,
emphasis = emphasis, emphasis = emphasis,
@ -890,22 +891,22 @@ class DiceThrowUseCase @Inject constructor(
// build the result. // build the result.
return DiceThrowResult( return DiceThrowResult(
dice = RollDiceUio( dice = RollDiceUio(
icon = (spell?.effect?.faces ?: 4).icon, icon = spell?.effect?.dice?.icon ?: R.drawable.ic_d4_24,
result = rollResult, result = rollResult,
), ),
throws = NetworkThrow( throws = NetworkThrow(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
title = titleString.uppercase(), title = titleString.uppercase(),
highlight = spellName.uppercase(), highlight = spellName.uppercase(),
face = spell?.effect?.faces ?: 4, face = spell?.effect?.dice?.faces ?: 4,
roll = allValue.toLabel(), roll = allValue.toLabel(),
result = rollResult, result = rollResult,
details = listOf( details = listOf(
NetworkThrow.Detail( NetworkThrow.Detail(
title = spellName, title = spellName,
throws = NetworkThrow.Throw( throws = NetworkThrow.Throw(
dice = (spell?.effect?.faces ?: 4).icon, dice = spell?.effect?.dice?.icon ?: R.drawable.ic_d4_24,
roll = "${spell?.effect?.amount ?: 1}d${spell?.effect?.faces ?: 4}", roll = spell?.effect?.dice?.toLabel() ?: "1d4",
advantage = advantage, advantage = advantage,
disadvantage = disadvantage, disadvantage = disadvantage,
emphasis = emphasis, emphasis = emphasis,
@ -934,8 +935,8 @@ class DiceThrowUseCase @Inject constructor(
val emphasis = status[Property.SKILL].emphasis val emphasis = status[Property.SKILL].emphasis
// main roll // main roll
val result = roll( val result = roll(
amount = skill?.effect?.amount ?: 1, amount = skill?.effect?.dice?.count ?: 1,
faces = skill?.effect?.faces ?: 4, faces = skill?.effect?.dice?.faces ?: 4,
advantage = advantage, advantage = advantage,
disadvantage = disadvantage, disadvantage = disadvantage,
emphasis = emphasis, emphasis = emphasis,
@ -961,22 +962,22 @@ class DiceThrowUseCase @Inject constructor(
// build the result. // build the result.
return DiceThrowResult( return DiceThrowResult(
dice = RollDiceUio( dice = RollDiceUio(
icon = (skill?.effect?.faces ?: 4).icon, icon = skill?.effect?.dice?.icon ?: R.drawable.ic_d4_24,
result = rollResult, result = rollResult,
), ),
throws = NetworkThrow( throws = NetworkThrow(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
title = titleString.uppercase(), title = titleString.uppercase(),
highlight = spellName?.uppercase() ?: "", highlight = spellName?.uppercase() ?: "",
face = skill?.effect?.faces ?: 4, face = skill?.effect?.dice?.faces ?: 4,
roll = allValue.toLabel(), roll = allValue.toLabel(),
result = rollResult, result = rollResult,
details = listOf( details = listOf(
NetworkThrow.Detail( NetworkThrow.Detail(
title = spellName ?: "", title = spellName ?: "",
throws = NetworkThrow.Throw( throws = NetworkThrow.Throw(
dice = (skill?.effect?.faces ?: 4).icon, dice = skill?.effect?.dice?.icon ?: R.drawable.ic_d4_24,
roll = "${skill?.effect?.amount ?: 1}d${skill?.effect?.faces ?: 4}", roll = skill?.effect?.dice?.toLabel() ?: "1d4",
advantage = advantage, advantage = advantage,
disadvantage = disadvantage, disadvantage = disadvantage,
emphasis = emphasis, emphasis = emphasis,
@ -1054,15 +1055,17 @@ class DiceThrowUseCase @Inject constructor(
} }
} }
private fun Int.flatBonus(name: String?): NetworkThrow.Detail? { private fun Flat.flatBonus(name: String?): NetworkThrow.Detail? {
allValue.add(this) return when {
return if (this != 0) { value != 0 -> {
allValue.add(value)
NetworkThrow.Detail( NetworkThrow.Detail(
title = context.getString(R.string.dice_roll_bonus_detail, name), title = context.getString(R.string.dice_roll_bonus_detail, name),
result = "$this", result = "$value",
) )
} else { }
null
else -> null
} }
} }
@ -1082,9 +1085,9 @@ class DiceThrowUseCase @Inject constructor(
critical = status.critical, critical = status.critical,
) )
NetworkThrow.Detail( NetworkThrow.Detail(
title = dice.title ?: "", title = status.name,
throws = NetworkThrow.Throw( throws = NetworkThrow.Throw(
dice = dice.faces.icon, dice = dice.icon,
advantage = dice.advantage, advantage = dice.advantage,
disadvantage = dice.disadvantage, disadvantage = dice.disadvantage,
emphasis = dice.emphasis, emphasis = dice.emphasis,
@ -1101,11 +1104,11 @@ class DiceThrowUseCase @Inject constructor(
* Fetch any flat number related bonus and build a ThrowsCardUio.Detail for each. * Fetch any flat number related bonus and build a ThrowsCardUio.Detail for each.
*/ */
fun List<Alteration.Status>?.flatAlterationBonus(): List<NetworkThrow.Detail> { fun List<Alteration.Status>?.flatAlterationBonus(): List<NetworkThrow.Detail> {
return this?.flatMap { status -> return this?.mapNotNull { status ->
status.bonus.map { bonus -> status.bonus?.let { bonus ->
allValue.add(bonus.value) allValue.add(bonus.value)
NetworkThrow.Detail( NetworkThrow.Detail(
title = bonus.title, title = status.name,
result = "${bonus.value}", result = "${bonus.value}",
) )
} }

View file

@ -1,6 +1,8 @@
package com.pixelized.rplexicon.data.model package com.pixelized.rplexicon.data.model
import android.net.Uri import android.net.Uri
import com.pixelized.rplexicon.data.model.roll.Dice
import com.pixelized.rplexicon.data.model.roll.Flat
data class Alteration( data class Alteration(
val icon: Uri?, val icon: Uri?,
@ -19,7 +21,7 @@ data class Alteration(
val amateurism: Boolean = false, val amateurism: Boolean = false,
val fail: Boolean = false, val fail: Boolean = false,
val critical: Boolean = false, val critical: Boolean = false,
val dices: List<Roll.Dice> = emptyList(), val dices: List<Dice> = emptyList(),
val bonus: List<Roll.Bonus> = emptyList(), val bonus: Flat? = null,
) )
} }

View file

@ -1,5 +1,7 @@
package com.pixelized.rplexicon.data.model package com.pixelized.rplexicon.data.model
import com.pixelized.rplexicon.data.model.roll.Throw
data class AssignedSpell( data class AssignedSpell(
val hit: Throw?, val hit: Throw?,
val effect: Throw?, val effect: Throw?,

View file

@ -1,6 +1,7 @@
package com.pixelized.rplexicon.data.model package com.pixelized.rplexicon.data.model
import android.net.Uri import android.net.Uri
import com.pixelized.rplexicon.data.model.roll.Throw
data class Attack( data class Attack(
val icon: Uri?, val icon: Uri?,
@ -13,6 +14,11 @@ data class Attack(
) { ) {
enum class Type(val key: String) { enum class Type(val key: String) {
PHYSICAL_MELEE_ATTACK("Mêlée"), PHYSICAL_MELEE_ATTACK("Mêlée"),
PHYSICAL_RANGE_ATTACK("Distance"), PHYSICAL_RANGE_ATTACK("Distance");
fun toProperty(): Property = when (this) {
PHYSICAL_MELEE_ATTACK -> Property.PHYSICAL_MELEE_ATTACK
PHYSICAL_RANGE_ATTACK -> Property.PHYSICAL_RANGE_ATTACK
}
} }
} }

View file

@ -1,6 +1,7 @@
package com.pixelized.rplexicon.data.model package com.pixelized.rplexicon.data.model
import android.net.Uri import android.net.Uri
import com.pixelized.rplexicon.data.model.roll.Throw
data class ObjectAction( data class ObjectAction(
val prefix: String?, val prefix: String?,

View file

@ -1,26 +0,0 @@
package com.pixelized.rplexicon.data.model
data class Roll(
val character: String,
val canUseCriticalDice: Boolean,
val title: String,
val highlight: String? = null,
val dices: List<Dice>,
val bonus: List<Bonus>,
) {
data class Dice(
val title: String? = null,
val advantage: Boolean = false,
val disadvantage: Boolean = false,
val emphasis: Boolean,
val count: Int,
val faces: Int,
) {
val label: String = "${count}d${faces}"
}
data class Bonus(
val title: String,
val value: Int,
)
}

View file

@ -1,6 +1,7 @@
package com.pixelized.rplexicon.data.model package com.pixelized.rplexicon.data.model
import android.net.Uri import android.net.Uri
import com.pixelized.rplexicon.data.model.roll.Throw
data class Skill( data class Skill(
val icon: Uri?, val icon: Uri?,

View file

@ -1,8 +0,0 @@
package com.pixelized.rplexicon.data.model
class Throw(
val amount: Int,
val faces: Int,
val flat: Int,
val modifier: List<Property>,
)

View file

@ -0,0 +1,14 @@
package com.pixelized.rplexicon.data.model.roll
import com.pixelized.rplexicon.utilitary.extentions.toLabel
data class Dice(
val sign: Int = 1,
val advantage: Boolean = false,
val disadvantage: Boolean = false,
val emphasis: Boolean = false,
val count: Int,
val faces: Int,
) {
override fun toString(): String = toLabel(ignoreSign = false)
}

View file

@ -0,0 +1,11 @@
package com.pixelized.rplexicon.data.model.roll
import com.pixelized.rplexicon.utilitary.extentions.toLabel
data class Flat(
val value: Int,
) {
override fun toString(): String {
return value.toLabel()
}
}

View file

@ -0,0 +1,9 @@
package com.pixelized.rplexicon.data.model.roll
import com.pixelized.rplexicon.data.model.Property
class Throw(
val dice: Dice,
val flat: Flat?,
val modifier: List<Property>,
)

View file

@ -25,44 +25,26 @@ class AlterationParser @Inject constructor(
sheet.forEachRowIndexed { index, row -> sheet.forEachRowIndexed { index, row ->
when (index) { when (index) {
0 -> updateStructure( 0 -> updateStructure(row = row, columns = COLUMNS)
row = row,
columns = COLUMNS,
)
else -> { else -> {
val name = row.parse(column = NAME) val name = row.parse(column = NAME)
val source = row.parse(column = SOURCE) val source = row.parse(column = SOURCE)
val target = row.parse(column = TARGET) val target = row.parse(column = TARGET)
val alteration = if (name != null && source != null && target != null) { if (name != null && source != null && target != null) {
Alteration( val alteration = Alteration(
icon = row.parseUri(column = ICON), icon = row.parseUri(column = ICON),
name = name, name = name,
source = source, source = source,
target = target, target = target,
status = properties status = properties.mapNotNull { property ->
.mapNotNull { property -> val column = column(property.key)
val value = row.parse(column = column(property.key)) row.parse(column = column)
if (value?.isNotEmpty() == true) { ?.takeIf { it.isNotEmpty() }
property to parseAlterationStatus( ?.let { parseAlterationStatus(name = name, value = it) }
name = name, ?.let { property to it }
value = value }.toMap(),
) )
} else {
null
}
}
.toMap(),
)
} else {
null
}
if (alteration != null) {
if (alteration.target == EFFECT && alteration.source == EFFECT) {
alterations
.getOrPut(alteration.name) { mutableListOf() }
.add(alteration)
} else {
characterSheets characterSheets
.filter { // check if the alteration is applicable to the character .filter { // check if the alteration is applicable to the character
alteration.target.let { target -> alteration.target.let { target ->
@ -78,13 +60,11 @@ class AlterationParser @Inject constructor(
} }
} }
} }
}
return@parserScope alterations return@parserScope alterations
} }
private fun parseAlterationStatus(name: String, value: String): Alteration.Status = private fun parseAlterationStatus(name: String, value: String) = when (value) {
when (value) {
ADVANTAGE -> Alteration.Status(name = name, advantage = true) ADVANTAGE -> Alteration.Status(name = name, advantage = true)
DISADVANTAGE -> Alteration.Status(name = name, disadvantage = true) DISADVANTAGE -> Alteration.Status(name = name, disadvantage = true)
EMPHASIS -> Alteration.Status(name = name, emphasis = true) EMPHASIS -> Alteration.Status(name = name, emphasis = true)
@ -93,15 +73,12 @@ class AlterationParser @Inject constructor(
AMATEURISM -> Alteration.Status(name = name, amateurism = true) AMATEURISM -> Alteration.Status(name = name, amateurism = true)
FAIL -> Alteration.Status(name = name, fail = true) FAIL -> Alteration.Status(name = name, fail = true)
CRITICAL -> Alteration.Status(name = name, critical = true) CRITICAL -> Alteration.Status(name = name, critical = true)
else -> Alteration.Status(
else -> {
Alteration.Status(
name = name, name = name,
dices = diceParser.findAll(title = name, value = value), dices = diceParser.parse(value = value),
bonus = flatParser.findAll(title = name, value = value), bonus = flatParser.parse(value = value),
) )
} }
}
companion object { companion object {
private const val ALL = "Tous" private const val ALL = "Tous"
@ -113,12 +90,11 @@ class AlterationParser @Inject constructor(
private const val AMATEURISM = "ama" private const val AMATEURISM = "ama"
private const val FAIL = "fail" private const val FAIL = "fail"
private const val CRITICAL = "crit" private const val CRITICAL = "crit"
private const val EFFECT = "Effet"
private val ICON = column("Icone")
private val NAME = column("Altération") private val NAME = column("Altération")
private val TARGET = column("Cible") private val TARGET = column("Cible")
private val SOURCE = column("Source") private val SOURCE = column("Source")
private val ICON = column("Icone")
private val COLUMNS private val COLUMNS
get() = listOf(NAME, SOURCE, TARGET, ICON) + Property.entries.map { column(it.key) } get() = listOf(NAME, SOURCE, TARGET, ICON) + Property.entries.map { column(it.key) }
} }

View file

@ -1,28 +1,27 @@
package com.pixelized.rplexicon.data.parser.roll package com.pixelized.rplexicon.data.parser.roll
import com.pixelized.rplexicon.data.model.Roll import com.pixelized.rplexicon.data.model.roll.Dice
import javax.inject.Inject import javax.inject.Inject
class DiceParser @Inject constructor() { class DiceParser @Inject constructor() {
companion object { companion object {
private val DICE_REGEX = Regex("(a|adv|d|dis|e|emp)*(\\d+)d(\\d+)") private val DICE_REGEX = Regex("([-+])?\\s*([ade])?(\\d+)*d(\\d+)")
} }
fun findAll(title: String? = null, value: String): List<Roll.Dice> = fun parse(value: String): List<Dice> {
DICE_REGEX.findAll(value).map { it.parse(title = title) }.toList() return DICE_REGEX.findAll(value)
.map {
private fun MatchResult.parse( val (sign, status, count, faces) = it.destructured
title: String? = null, Dice(
): Roll.Dice { sign = if (sign == "-") -1 else 1,
val (status, count, faces) = destructured advantage = status == "a",
return Roll.Dice( disadvantage = status == "d",
title = title, emphasis = status == "e",
advantage = status == "a" || status == "adv", count = count.toIntOrNull() ?: 1,
disadvantage = status == "d" || status == "dis",
emphasis = status == "e" || status == "emp",
count = count.toIntOrNull() ?: 0,
faces = faces.toIntOrNull() ?: 0, faces = faces.toIntOrNull() ?: 0,
) )
} }
.toList()
}
} }

View file

@ -1,6 +1,6 @@
package com.pixelized.rplexicon.data.parser.roll package com.pixelized.rplexicon.data.parser.roll
import com.pixelized.rplexicon.data.model.Roll import com.pixelized.rplexicon.data.model.roll.Flat
import javax.inject.Inject import javax.inject.Inject
class FlatValueParser @Inject constructor() { class FlatValueParser @Inject constructor() {
@ -9,21 +9,11 @@ class FlatValueParser @Inject constructor() {
private val FLAT_REGEX = Regex("(?<!d)([+-]\\s?\\d+)(?!d)|^\\d+\$") private val FLAT_REGEX = Regex("(?<!d)([+-]\\s?\\d+)(?!d)|^\\d+\$")
} }
fun parse(value: String): Int { fun parse(value: String): Flat? {
return FLAT_REGEX.findAll(value) return FLAT_REGEX.findAll(value)
.sumOf { it.value.replace(" ", "").toIntOrNull() ?: 0 } .mapNotNull { it.value.replace(" ", "").toIntOrNull() }
} .toList()
.takeIf { it.isNotEmpty() }
fun findAll(title: String, value: String): List<Roll.Bonus> { ?.let { Flat(value = it.sum()) }
return FLAT_REGEX.findAll(value).map { it.parse(title = title) }.toList()
}
private fun MatchResult.parse(
title: String,
): Roll.Bonus {
return Roll.Bonus(
title = title,
value = value.toIntOrNull() ?: 0,
)
} }
} }

View file

@ -31,7 +31,7 @@ class ModifierParser @Inject constructor(
) )
} }
fun findAll(value: String): List<Property> { fun parse(value: String): List<Property> {
return MODIFIER_REGEX.findAll(value) return MODIFIER_REGEX.findAll(value)
.mapNotNull { propertyParser.parseProperty(it.value) } .mapNotNull { propertyParser.parseProperty(it.value) }
.toList() .toList()

View file

@ -1,6 +1,6 @@
package com.pixelized.rplexicon.data.parser.roll package com.pixelized.rplexicon.data.parser.roll
import com.pixelized.rplexicon.data.model.Throw import com.pixelized.rplexicon.data.model.roll.Throw
import javax.inject.Inject import javax.inject.Inject
class ThrowParser @Inject constructor( class ThrowParser @Inject constructor(
@ -10,15 +10,12 @@ class ThrowParser @Inject constructor(
) { ) {
fun parse(value: String?): Throw? { fun parse(value: String?): Throw? {
if (value != null) { if (value != null) {
val dice = diceParser.findAll(value = value).firstOrNull() val dice = diceParser.parse(value = value).firstOrNull()
if (dice != null) { if (dice != null) {
val modifier = modifierParser.findAll(value = value)
val flat = flatValueParser.parse(value = value)
return Throw( return Throw(
amount = dice.count, dice = dice,
faces = dice.faces, flat = flatValueParser.parse(value = value),
flat = flat, modifier = modifierParser.parse(value = value),
modifier = modifier,
) )
} }
} }

View file

@ -28,7 +28,9 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
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.data.model.Throw import com.pixelized.rplexicon.data.model.roll.Dice
import com.pixelized.rplexicon.data.model.roll.Flat
import com.pixelized.rplexicon.data.model.roll.Throw
import com.pixelized.rplexicon.ui.composable.AsyncImage import com.pixelized.rplexicon.ui.composable.AsyncImage
import com.pixelized.rplexicon.ui.theme.LexiconTheme import com.pixelized.rplexicon.ui.theme.LexiconTheme
@ -116,7 +118,11 @@ private fun ObjectItemPreview() {
prefix = "Parchemin de ", prefix = "Parchemin de ",
name = "Bénédiction", name = "Bénédiction",
original = "Blessing", original = "Blessing",
effect = Throw(1, 1, 0, emptyList()), effect = Throw(
dice = Dice(count = 1, faces = 1),
flat = Flat(value = 0),
modifier = emptyList(),
),
), ),
onObject = { }, onObject = { },
onUse = { }, onUse = { },

View file

@ -4,7 +4,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import com.pixelized.rplexicon.data.model.Throw import com.pixelized.rplexicon.data.model.roll.Dice
import com.pixelized.rplexicon.data.model.roll.Flat
import com.pixelized.rplexicon.data.model.roll.Throw
import com.pixelized.rplexicon.ui.screens.character.composable.actions.ObjectItemUio import com.pixelized.rplexicon.ui.screens.character.composable.actions.ObjectItemUio
@Composable @Composable
@ -24,7 +26,11 @@ fun rememberObjectListStatePreview() = remember {
prefix = "Parchemin de", prefix = "Parchemin de",
name = "Bénédiction", name = "Bénédiction",
original = "Blessing", original = "Blessing",
effect = Throw(1, 1, 0, emptyList()), effect = Throw(
dice = Dice(count = 1, faces = 1),
flat = Flat(value = 0),
modifier = emptyList(),
),
), ),
) )
) )

View file

@ -7,6 +7,7 @@ import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Property import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AttackUio import com.pixelized.rplexicon.ui.screens.character.composable.actions.AttackUio
import com.pixelized.rplexicon.utilitary.extentions.icon 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.modifier
import javax.inject.Inject import javax.inject.Inject
@ -17,16 +18,13 @@ class AttackUioFactory @Inject constructor() {
alterations: Map<Property, List<Alteration.Status>>, alterations: Map<Property, List<Alteration.Status>>,
attack: Attack, attack: Attack,
): AttackUio { ): AttackUio {
val hit = attack.hit?.let { dice -> val hit = attack.hit?.let { diceThrow ->
// compute alteration for attack type. // compute alteration for attack type.
val hitAlteration: Int = alterations[when (attack.type) { val hitAlteration: Int = alterations[attack.type.toProperty()]
Attack.Type.PHYSICAL_MELEE_ATTACK -> Property.PHYSICAL_MELEE_ATTACK ?.sumOf { it.bonus?.value ?: 0 }
Attack.Type.PHYSICAL_RANGE_ATTACK -> Property.PHYSICAL_RANGE_ATTACK ?: 0
}]?.sumOf {
it.bonus.sumOf { bonus -> bonus.value }
} ?: 0
// compute stats modifier // compute stats modifier
val modifier = dice.modifier.sumOf { val modifier = diceThrow.modifier.sumOf {
when (it) { when (it) {
Property.PROFICIENCY -> characterSheet.proficiency Property.PROFICIENCY -> characterSheet.proficiency
Property.STRENGTH -> characterSheet.strength.modifier Property.STRENGTH -> characterSheet.strength.modifier
@ -36,21 +34,18 @@ class AttackUioFactory @Inject constructor() {
} + hitAlteration } + hitAlteration
// Build the UIO. // Build the UIO.
AttackUio.Dice( AttackUio.Dice(
icon = dice.faces.icon, icon = diceThrow.dice.icon,
label = "${dice.amount}d${dice.faces}${if (modifier > 0) "+$modifier" else ""}", label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}",
) )
} }
val damage = attack.damage?.let { dice -> val damage = attack.damage?.let { diceThrow ->
// compute alteration for attack type. // compute alteration for attack type.
val damageAlteration: Int = alterations[when (attack.type) { val damageAlteration: Int = alterations[attack.type.toProperty()]
Attack.Type.PHYSICAL_MELEE_ATTACK -> Property.PHYSICAL_MELEE_DAMAGE ?.sumOf { it.bonus?.value ?: 0 }
Attack.Type.PHYSICAL_RANGE_ATTACK -> Property.PHYSICAL_RANGE_DAMAGE ?: 0
}]?.sumOf {
it.bonus.sumOf { bonus -> bonus.value }
} ?: 0
// compute stats modifier // compute stats modifier
val modifier = dice.modifier.sumOf { val modifier = diceThrow.modifier.sumOf {
when (it) { when (it) {
Property.PROFICIENCY -> characterSheet.proficiency Property.PROFICIENCY -> characterSheet.proficiency
Property.STRENGTH -> characterSheet.strength.modifier Property.STRENGTH -> characterSheet.strength.modifier
@ -60,8 +55,8 @@ class AttackUioFactory @Inject constructor() {
} + damageAlteration } + damageAlteration
// Build the UIO. // Build the UIO.
AttackUio.Dice( AttackUio.Dice(
icon = dice.faces.icon, icon = diceThrow.dice.icon,
label = "${dice.amount}d${dice.faces}${if (modifier > 0) "+$modifier" else ""}", label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}",
) )
} }

View file

@ -7,11 +7,11 @@ import com.pixelized.rplexicon.data.network.CharacterSheetFire
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillItemUio import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillItemUio
import com.pixelized.rplexicon.utilitary.extentions.icon 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.base
import com.pixelized.rplexicon.utilitary.extentions.local.primary import com.pixelized.rplexicon.utilitary.extentions.local.primary
import com.pixelized.rplexicon.utilitary.extentions.local.secondary import com.pixelized.rplexicon.utilitary.extentions.local.secondary
import com.pixelized.rplexicon.utilitary.extentions.modifier import com.pixelized.rplexicon.utilitary.extentions.modifier
import com.pixelized.rplexicon.utilitary.extentions.toLabel
import javax.inject.Inject import javax.inject.Inject
class SkillFactoryUioFactory @Inject constructor( class SkillFactoryUioFactory @Inject constructor(
@ -40,7 +40,7 @@ class SkillFactoryUioFactory @Inject constructor(
else -> 0 else -> 0
} }
} ?: 0 } ?: 0
val effectFlat = skill.effect?.flat ?: 0 val effectFlat = skill.effect?.flat?.value ?: 0
val modifier = effectModifier + effectFlat val modifier = effectModifier + effectFlat
SkillItemUio( SkillItemUio(
@ -51,8 +51,8 @@ class SkillFactoryUioFactory @Inject constructor(
cost = skill.cost, cost = skill.cost,
effect = skill.effect?.let { effect = skill.effect?.let {
SkillItemUio.Dice( SkillItemUio.Dice(
icon = it.faces.icon, icon = it.dice.icon,
label = "${skill.effect.amount}d${skill.effect.faces}${if (modifier > 0) modifier.toLabel() else ""}", label = "${skill.effect.dice.toLabel()}${modifier.toLabel(true)}",
) )
}, },
value = fire?.skills?.get(skill.name) ?: 0, value = fire?.skills?.get(skill.name) ?: 0,

View file

@ -1,12 +1,12 @@
package com.pixelized.rplexicon.ui.screens.character.factory package com.pixelized.rplexicon.ui.screens.character.factory
import androidx.compose.animation.core.spring
import com.pixelized.rplexicon.data.model.AssignedSpell import com.pixelized.rplexicon.data.model.AssignedSpell
import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Property import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellUio import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellUio
import com.pixelized.rplexicon.utilitary.extentions.icon 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.modifier
import javax.inject.Inject import javax.inject.Inject
@ -19,8 +19,8 @@ class SpellUioFactory @Inject constructor(
characterSheet: CharacterSheet, characterSheet: CharacterSheet,
warlockSpellLevel: Int?, warlockSpellLevel: Int?,
): SpellUio { ): SpellUio {
val hit = assignedSpell.hit?.let { dice -> val hit = assignedSpell.hit?.let { diceThrow ->
val modifier = dice.modifier.sumOf { val modifier = diceThrow.modifier.sumOf {
when (it) { when (it) {
Property.PROFICIENCY -> characterSheet.proficiency Property.PROFICIENCY -> characterSheet.proficiency
Property.INTELLIGENCE -> characterSheet.intelligence.modifier Property.INTELLIGENCE -> characterSheet.intelligence.modifier
@ -30,12 +30,12 @@ class SpellUioFactory @Inject constructor(
} }
} }
SpellUio.Dice( SpellUio.Dice(
icon = dice.faces.icon, icon = diceThrow.dice.icon,
label = "${dice.amount}d${dice.faces}${if (modifier > 0) "+$modifier" else ""}", label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}",
) )
} }
val effect = assignedSpell.effect?.let { dice -> val effect = assignedSpell.effect?.let { diceThrow ->
val modifier = dice.modifier.sumOf { val modifier = diceThrow.modifier.sumOf {
when (it) { when (it) {
Property.PROFICIENCY -> characterSheet.proficiency Property.PROFICIENCY -> characterSheet.proficiency
Property.STRENGTH -> characterSheet.strength.modifier Property.STRENGTH -> characterSheet.strength.modifier
@ -46,33 +46,34 @@ class SpellUioFactory @Inject constructor(
Property.CHARISMA -> characterSheet.charisma.modifier Property.CHARISMA -> characterSheet.charisma.modifier
else -> 0 else -> 0
} }
} + dice.flat } + (diceThrow.flat?.value ?: 0)
val level = assignedSpell.level val level = assignedSpell.level
val delta = warlockSpellLevel?.minus(assignedSpell.spell.level) ?: 0 val delta = warlockSpellLevel?.minus(assignedSpell.spell.level) ?: 0
if (warlockSpellLevel == null || level == null || delta <= 0) { if (warlockSpellLevel == null || level == null || delta <= 0) {
// default case of non warlock character of the spell don't scale // default case of non warlock character of the spell don't scale
SpellUio.Dice( SpellUio.Dice(
icon = dice.faces.icon, icon = diceThrow.dice.icon,
label = "${dice.amount}d${dice.faces}${if (modifier > 0) "+$modifier" else ""}", label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}",
) )
} else { } else if (diceThrow.dice.faces == level.dice.faces) {
// warlock character, upscale the spell to warlock spell level // warlock character, upscale the spell to warlock spell level
if (dice.faces == level.faces) { val upscaleModifier = modifier + (level.flat?.value ?: 0) * delta
val upscaleModifier = modifier + level.flat * delta val diceCount = diceThrow.dice.count + level.dice.count * delta
SpellUio.Dice( SpellUio.Dice(
icon = dice.faces.icon, icon = diceThrow.dice.icon,
label = "${dice.amount + level.amount * delta}d${dice.faces}${if (upscaleModifier > 0) "+$upscaleModifier" else ""}", label = "${diceCount}d${diceThrow.dice.faces}${upscaleModifier.toLabel(true)}",
) )
} else { } 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( SpellUio.Dice(
icon = dice.faces.icon, icon = diceThrow.dice.icon,
label = "${dice.amount}d${dice.faces}${modifier.takeIf { it > 0 }?.let { "+$it" } ?: ""} + " + label = "${diceThrow.dice.toLabel()}${modifier.toLabel(true)}" +
"${level.amount}d${level.faces * delta}${(level.flat * delta).takeIf { it > 0 }?.let { "+$it" } ?: ""}", "+ ${level.dice.count * delta}d${level.dice.faces}${deltaModifier.toLabel(true)}",
) )
} }
} }
}
return SpellUio( return SpellUio(
icon = assignedSpell.spell.icon, icon = assignedSpell.spell.icon,
school = assignedSpell.spell.school, school = assignedSpell.spell.school,

View file

@ -12,7 +12,7 @@ import com.pixelized.rplexicon.data.model.AssignedSpell
import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.DiceThrow import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.model.Property import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.Throw import com.pixelized.rplexicon.data.model.roll.Throw
import com.pixelized.rplexicon.data.network.CharacterSheetFire import com.pixelized.rplexicon.data.network.CharacterSheetFire
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
@ -28,12 +28,14 @@ import com.pixelized.rplexicon.utilitary.extentions.local.firstSpellSlot
import com.pixelized.rplexicon.utilitary.extentions.local.highestSpellLevel import com.pixelized.rplexicon.utilitary.extentions.local.highestSpellLevel
import com.pixelized.rplexicon.utilitary.extentions.local.spell import com.pixelized.rplexicon.utilitary.extentions.local.spell
import com.pixelized.rplexicon.utilitary.extentions.modifier import com.pixelized.rplexicon.utilitary.extentions.modifier
import com.pixelized.rplexicon.utilitary.extentions.signLabel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
@HiltViewModel @HiltViewModel
@ -101,7 +103,7 @@ class SpellsViewModel @Inject constructor(
spell = name, spell = name,
) )
if (assignedSpell != null && character != null && characterFire != null) { if (assignedSpell != null && character != null && characterFire != null) {
val icon = assignedSpell.effect?.faces?.icon ?: R.drawable.ic_d20_24 val icon = assignedSpell.effect?.dice?.icon ?: R.drawable.ic_d20_24
val base = assignedSpell.effect?.toString(character = character, level = 1) ?: "" val base = assignedSpell.effect?.toString(character = character, level = 1) ?: ""
_preparedSpellLevel.value = SpellChooserUio( _preparedSpellLevel.value = SpellChooserUio(
name = name, name = name,
@ -177,9 +179,9 @@ class SpellsViewModel @Inject constructor(
private fun Throw.toString(character: CharacterSheet, level: Int = 0): String? { private fun Throw.toString(character: CharacterSheet, level: Int = 0): String? {
val modifierLabel = modifier val modifierLabel = modifier
.sumOf { modifier -> modifier.toValue(character = character) } .sumOf { modifier -> modifier.toValue(character = character) }
.let { if (it > 0) "+${it * level}" else "" } .let { if (it != 0) "${it.signLabel}${abs(it) * level}" else "" }
return when (level > 0) { return when (level > 0) {
true -> "${amount * level}d${faces}${modifierLabel}" true -> "${dice.count * level}d${dice.faces}${modifierLabel}"
else -> null else -> null
} }
} }

View file

@ -20,42 +20,42 @@ class DiceFactory @Inject constructor(
is DiceThrow.PhysicalMeleeDamage -> actionRepository.find( is DiceThrow.PhysicalMeleeDamage -> actionRepository.find(
character = diceThrow.character, character = diceThrow.character,
action = diceThrow.weapon action = diceThrow.weapon
)?.damage?.faces?.icon?.let { )?.damage?.dice?.icon?.let {
RollDiceUio(icon = it) RollDiceUio(icon = it)
} }
is DiceThrow.PhysicalRangeDamage -> actionRepository.find( is DiceThrow.PhysicalRangeDamage -> actionRepository.find(
character = diceThrow.character, character = diceThrow.character,
action = diceThrow.weapon action = diceThrow.weapon
)?.damage?.faces?.icon?.let { )?.damage?.dice?.icon?.let {
RollDiceUio(icon = it) RollDiceUio(icon = it)
} }
is DiceThrow.Skill -> skillRepository.find( is DiceThrow.Skill -> skillRepository.find(
character = diceThrow.character, character = diceThrow.character,
skill = diceThrow.skill skill = diceThrow.skill
)?.effect?.faces?.icon?.let { )?.effect?.dice?.icon?.let {
RollDiceUio(icon = it) RollDiceUio(icon = it)
} }
is DiceThrow.SpellDamage -> spellRepository.findAssignedSpell( is DiceThrow.SpellDamage -> spellRepository.findAssignedSpell(
character = diceThrow.character, character = diceThrow.character,
spell = diceThrow.spell spell = diceThrow.spell
)?.effect?.faces?.icon?.let { )?.effect?.dice?.icon?.let {
RollDiceUio(icon = it) RollDiceUio(icon = it)
} }
is DiceThrow.SpellEffect -> spellRepository.findAssignedSpell( is DiceThrow.SpellEffect -> spellRepository.findAssignedSpell(
character = diceThrow.character, character = diceThrow.character,
spell = diceThrow.spell spell = diceThrow.spell
)?.effect?.faces?.icon?.let { )?.effect?.dice?.icon?.let {
RollDiceUio(icon = it) RollDiceUio(icon = it)
} }
is DiceThrow.Object -> objectActionRepository.find( is DiceThrow.Object -> objectActionRepository.find(
character = diceThrow.character, character = diceThrow.character,
item = diceThrow.item, item = diceThrow.item,
)?.effect?.faces?.icon?.let { )?.effect?.dice?.icon?.let {
RollDiceUio(icon = it) RollDiceUio(icon = it)
} }

View file

@ -0,0 +1,18 @@
package com.pixelized.rplexicon.utilitary.extentions
import androidx.annotation.DrawableRes
import com.pixelized.rplexicon.data.model.roll.Dice
val Dice.icon: Int
@DrawableRes
get() = faces.icon
fun Dice.toLabel(ignoreSign: Boolean = true): String = buildString {
if (!ignoreSign) append(sign.signLabel)
if (advantage) append("a")
if (disadvantage) append("d")
if (emphasis) append("e")
append(count)
append("d")
append(faces)
}

View file

@ -1,6 +1,7 @@
package com.pixelized.rplexicon.utilitary.extentions package com.pixelized.rplexicon.utilitary.extentions
import android.net.Uri import android.net.Uri
import androidx.annotation.DrawableRes
import com.pixelized.rplexicon.BuildConfig import com.pixelized.rplexicon.BuildConfig
import com.pixelized.rplexicon.R import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.data.model.Alteration import com.pixelized.rplexicon.data.model.Alteration
@ -11,6 +12,7 @@ import kotlin.math.floor
import kotlin.math.max import kotlin.math.max
val Int.icon val Int.icon
@DrawableRes
get() = when (this) { get() = when (this) {
4 -> R.drawable.ic_d4_24 4 -> R.drawable.ic_d4_24
6 -> R.drawable.ic_d6_24 6 -> R.drawable.ic_d6_24
@ -30,8 +32,8 @@ fun Int?.masteryMultiplier(status: List<Alteration.Status>?): Int {
} }
} }
fun Int.toLabel(): String = fun Int.toLabel(excludeZero: Boolean = false): String =
"${this.signLabel}${abs(this)}" if (excludeZero && this == 0) "" else "${this.signLabel}${abs(this)}"
val Int.signLabel: String val Int.signLabel: String
get() = if (this < 0) "-" else "+" get() = if (this < 0) "-" else "+"

View file

@ -14,7 +14,7 @@ fun List<Alteration>.toStatus(): Map<Property, List<Alteration.Status>> {
} }
val List<Alteration.Status>?.sum: Int val List<Alteration.Status>?.sum: Int
get() = this?.sumOf { alt -> alt.bonus.sumOf { it.value } } ?: 0 get() = this?.sumOf { alt -> alt.bonus?.value ?: 0 } ?: 0
val List<Alteration.Status>?.advantage: Boolean val List<Alteration.Status>?.advantage: Boolean
get() = this?.any { it.advantage } ?: false get() = this?.any { it.advantage } ?: false