From 3bda8854119bf946272acba646254708ef13c5a0 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Sun, 17 Dec 2023 17:59:37 +0100 Subject: [PATCH] Add item specific alteration --- .../rplexicon/business/DiceThrowUseCase.kt | 132 ++++++++++------- .../rplexicon/data/model/Alteration.kt | 7 +- .../pixelized/rplexicon/data/model/Attack.kt | 1 + .../pixelized/rplexicon/data/model/Roll.kt | 83 +---------- .../rplexicon/data/parser/AttackParser.kt | 4 +- .../rplexicon/data/parser/SheetParserScope.kt | 7 + .../parser/alteration/AlterationParser.kt | 46 +++--- .../character/AlterationRepository.kt | 10 ++ .../ConvertRollIntoDisplayableFactory.kt | 133 ------------------ .../extentions/local/AlterationEx.kt | 5 + 10 files changed, 137 insertions(+), 291 deletions(-) delete mode 100644 app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/ConvertRollIntoDisplayableFactory.kt 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 1e70071..9dd00c1 100644 --- a/app/src/main/java/com/pixelized/rplexicon/business/DiceThrowUseCase.kt +++ b/app/src/main/java/com/pixelized/rplexicon/business/DiceThrowUseCase.kt @@ -20,6 +20,7 @@ import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio import com.pixelized.rplexicon.utilitary.extentions.icon import com.pixelized.rplexicon.utilitary.extentions.local.advantage +import com.pixelized.rplexicon.utilitary.extentions.local.critical import com.pixelized.rplexicon.utilitary.extentions.local.disadvantage import com.pixelized.rplexicon.utilitary.extentions.local.fail import com.pixelized.rplexicon.utilitary.extentions.local.isBrutalCritical @@ -52,7 +53,6 @@ class DiceThrowUseCase @Inject constructor( val sheet = characterSheetRepository.find(name = diceThrow.character) val alterations = alterationRepository.getAlterations(character = diceThrow.character) .filter { alterationId.contains(it.name) } - .toStatus() return if (sheet != null) { when (diceThrow) { @@ -349,12 +349,15 @@ class DiceThrowUseCase @Inject constructor( character = diceThrow.character, action = diceThrow.weapon, ) + val objectAlterations = alterationRepository.getAlterations( + objects = action?.alterations, + ) actionThrow( character = sheet, action = diceThrow.weapon, diceThrow = action?.hit, title = { getString(R.string.dice_roll_attack_hit_title, it) }, - alterations = alterations, + alterations = alterations + objectAlterations, ability = Property.PHYSICAL_MELEE_ATTACK, ) } @@ -364,12 +367,15 @@ class DiceThrowUseCase @Inject constructor( character = diceThrow.character, action = diceThrow.weapon, ) + val objectAlterations = alterationRepository.getAlterations( + objects = action?.alterations, + ) actionThrow( character = sheet, action = diceThrow.weapon, diceThrow = action?.damage, title = { getString(R.string.dice_roll_attack_damage_title, it) }, - alterations = alterations, + alterations = alterations + objectAlterations, ability = Property.PHYSICAL_MELEE_DAMAGE, ) } @@ -379,12 +385,15 @@ class DiceThrowUseCase @Inject constructor( character = diceThrow.character, action = diceThrow.weapon, ) + val objectAlterations = alterationRepository.getAlterations( + objects = action?.alterations, + ) actionThrow( character = sheet, action = diceThrow.weapon, diceThrow = action?.hit, title = { getString(R.string.dice_roll_attack_hit_title, it) }, - alterations = alterations, + alterations = alterations + objectAlterations, ability = Property.PHYSICAL_RANGE_ATTACK, ) } @@ -394,12 +403,15 @@ class DiceThrowUseCase @Inject constructor( character = diceThrow.character, action = diceThrow.weapon, ) + val objectAlterations = alterationRepository.getAlterations( + objects = action?.alterations, + ) actionThrow( character = sheet, action = diceThrow.weapon, diceThrow = action?.damage, title = { getString(R.string.dice_roll_attack_damage_title, it) }, - alterations = alterations, + alterations = alterations + objectAlterations, ability = Property.PHYSICAL_RANGE_DAMAGE, ) } @@ -414,7 +426,7 @@ class DiceThrowUseCase @Inject constructor( action = diceThrow.item, diceThrow = action?.effect, title = { getString(R.string.dice_roll_use_object, it) }, - alterations = emptyMap(), + alterations = emptyList(), ability = null, ) } @@ -481,7 +493,7 @@ class DiceThrowUseCase @Inject constructor( private fun abilityThrow( character: CharacterSheet, - alterations: Map>, + alterations: List, abilityLabel: Context.() -> String, relatedLabel: Context.() -> String?, ability: Property, @@ -499,7 +511,7 @@ class DiceThrowUseCase @Inject constructor( private fun savingThrow( character: CharacterSheet, - alterations: Map>, + alterations: List, abilityLabel: Context.() -> String, relatedLabel: Context.() -> String?, ability: Property, @@ -517,7 +529,7 @@ class DiceThrowUseCase @Inject constructor( private fun rollAbility( character: CharacterSheet, - alterations: Map>, + alterations: List, abilityTitle: Context.(label: String) -> String, abilityLabel: Context.() -> String, relatedTitle: Context.(label: String) -> String?, @@ -531,27 +543,30 @@ class DiceThrowUseCase @Inject constructor( val abilityTitleString = abilityTitle(application, abilityLabelString) // check if the roll is affected by some status. - val advantage = alterations[ability].advantage - val disadvantage = alterations[ability].disadvantage - val fail = alterations[ability].fail - + val advantage = status[ability].advantage + val disadvantage = status[ability].disadvantage // main roll - val result = roll(advantage = advantage, disadvantage = disadvantage, fail = fail) + val result = roll( + advantage = advantage, + disadvantage = disadvantage, + fail = status[ability].fail, + critical = status[ability].critical, + ) // fetch and build a list of dice roll base on alterations. - val diceAlterationBonus = alterations[ability].alterationsBonus() + val diceAlterationBonus = status[ability].alterationsBonus() // fetch and build a list of flat bonus - val flatAlterationBonus = alterations[ability].flatAlterationBonus() + val flatAlterationBonus = status[ability].flatAlterationBonus() // fetch and build the associated characteristic val relatedStatBonus = when (relatedStat) { - Property.STRENGTH -> (character.strength + alterations[relatedStat].sum).modifier - Property.DEXTERITY -> (character.dexterity + alterations[relatedStat].sum).modifier - Property.CONSTITUTION -> (character.constitution + alterations[relatedStat].sum).modifier - Property.INTELLIGENCE -> (character.intelligence + alterations[relatedStat].sum).modifier - Property.WISDOM -> (character.wisdom + alterations[relatedStat].sum).modifier - Property.CHARISMA -> (character.charisma + alterations[relatedStat].sum).modifier + Property.STRENGTH -> (character.strength + status[relatedStat].sum).modifier + Property.DEXTERITY -> (character.dexterity + status[relatedStat].sum).modifier + Property.CONSTITUTION -> (character.constitution + status[relatedStat].sum).modifier + Property.INTELLIGENCE -> (character.intelligence + status[relatedStat].sum).modifier + Property.WISDOM -> (character.wisdom + status[relatedStat].sum).modifier + Property.CHARISMA -> (character.charisma + status[relatedStat].sum).modifier Property.PROFICIENCY -> character.proficiency else -> null }?.let { value -> @@ -657,7 +672,7 @@ class DiceThrowUseCase @Inject constructor( action: String, diceThrow: Throw?, title: Context.(action: String) -> String, - alterations: Map>, + alterations: List, ability: Property?, ): DiceThrowResult { with(ThrowScope(context = application, character = character, alterations = alterations)) { @@ -673,17 +688,16 @@ class DiceThrowUseCase @Inject constructor( } // check if the roll is affected by some status. - val advantage = alterations[ability].advantage - val disadvantage = alterations[ability].disadvantage - val fail = alterations[ability].fail + val advantage = status[ability].advantage + val disadvantage = status[ability].disadvantage // compute the amount of main dice to throw. - val amount = if (alterations.isCritical) { + val amount = if (status.isCritical) { diceThrow?.amount?.times(2)?.let { - if (ability == Property.PHYSICAL_MELEE_DAMAGE && alterations.isSavageAttacks) + if (ability == Property.PHYSICAL_MELEE_DAMAGE && status.isSavageAttacks) it.plus(1) else it }?.let { - if (ability == Property.PHYSICAL_MELEE_DAMAGE && alterations.isBrutalCritical) + if (ability == Property.PHYSICAL_MELEE_DAMAGE && status.isBrutalCritical) it.plus(1) else it } } else { @@ -695,14 +709,15 @@ class DiceThrowUseCase @Inject constructor( faces = diceThrow?.faces ?: 20, advantage = advantage, disadvantage = disadvantage, - fail = fail, + fail = status[ability].fail, + critical = status[ability].critical, ) // fetch and build a list of dice roll base on alterations. - val diceAlterationBonus = alterations[ability].alterationsBonus() + val diceAlterationBonus = status[ability].alterationsBonus() // fetch and build a list of flat bonus - val flatAlterationBonus = alterations[ability].flatAlterationBonus() + val flatAlterationBonus = status[ability].flatAlterationBonus() // fetch and build the associated characteristic val relatedStatBonus = diceThrow.statBonus(name = action) @@ -763,7 +778,7 @@ class DiceThrowUseCase @Inject constructor( character: CharacterSheet, spell: AssignedSpell?, level: Int, - alterations: Map>, + alterations: List, ): DiceThrowResult { with(ThrowScope(context = application, character = character, alterations = alterations)) { // retrieve some wording. @@ -833,7 +848,7 @@ class DiceThrowUseCase @Inject constructor( private fun skillThrow( character: CharacterSheet, - alterations: Map>, + alterations: List, skill: Skill?, ): DiceThrowResult { with(ThrowScope(context = application, character = character, alterations = alterations)) { @@ -848,10 +863,10 @@ class DiceThrowUseCase @Inject constructor( ) // fetch and build a list of dice roll base on alterations. - val diceAlterationBonus = alterations[Property.SKILL].alterationsBonus() + val diceAlterationBonus = status[Property.SKILL].alterationsBonus() // fetch and build a list of flat bonus - val flatAlterationBonus = alterations[Property.SKILL].flatAlterationBonus() + val flatAlterationBonus = status[Property.SKILL].flatAlterationBonus() // fetch and build the associated characteristic, proficiency or level val relatedStatBonus = skill?.effect.statBonus(name = skill?.name) @@ -894,8 +909,10 @@ class DiceThrowUseCase @Inject constructor( val context: Context, val allValue: MutableList = mutableListOf(), val character: CharacterSheet, - val alterations: Map>, + val alterations: List, ) { + val status = this.alterations.toStatus() + /** * Fetch any stats / proficiency / level related bonus and build a ThrowsCardUio.Detail for each. */ @@ -905,12 +922,12 @@ class DiceThrowUseCase @Inject constructor( fun Property.statBonus(name: String?): ThrowsCardUio.Detail? { return when (this) { - Property.STRENGTH -> (character.strength + alterations[this].sum).modifier - Property.DEXTERITY -> (character.dexterity + alterations[this].sum).modifier - Property.CONSTITUTION -> (character.constitution + alterations[this].sum).modifier - Property.INTELLIGENCE -> (character.intelligence + alterations[this].sum).modifier - Property.WISDOM -> (character.wisdom + alterations[this].sum).modifier - Property.CHARISMA -> (character.charisma + alterations[this].sum).modifier + Property.STRENGTH -> (character.strength + this@ThrowScope.status[this].sum).modifier + Property.DEXTERITY -> (character.dexterity + this@ThrowScope.status[this].sum).modifier + Property.CONSTITUTION -> (character.constitution + this@ThrowScope.status[this].sum).modifier + Property.INTELLIGENCE -> (character.intelligence + this@ThrowScope.status[this].sum).modifier + Property.WISDOM -> (character.wisdom + this@ThrowScope.status[this].sum).modifier + Property.CHARISMA -> (character.charisma + this@ThrowScope.status[this].sum).modifier Property.PROFICIENCY -> character.proficiency Property.LEVEL -> character.level else -> null @@ -945,11 +962,12 @@ class DiceThrowUseCase @Inject constructor( return this?.flatMap { status -> status.dices.map { dice -> val localRoll = roll( - amount = if (alterations.isCritical) dice.count * 2 else dice.count, + amount = if (this@ThrowScope.status.isCritical) dice.count * 2 else dice.count, faces = dice.faces, advantage = dice.advantage, disadvantage = dice.disadvantage, - fail = dice.fail + fail = status.fail, + critical = status.critical, ) ThrowsCardUio.Detail( title = dice.title, @@ -990,10 +1008,15 @@ class DiceThrowUseCase @Inject constructor( advantage: Boolean = false, disadvantage: Boolean = false, fail: Boolean = false, + critical: Boolean = false, ): DiceRollResult { val result = when { advantage && !disadvantage -> { - val roll = List(amount) { random(faces, fail) to random(faces, fail) } + val roll = List(amount) { + val first = random(faces = faces, fail = fail, critical = critical) + val second = random(faces = faces, fail = fail, critical = critical) + first to second + } DiceRollResult( label = roll.joinToString(" + ") { "${it.first}~${it.second}" }, value = roll.sumOf { max(it.first, it.second) }, @@ -1001,7 +1024,11 @@ class DiceThrowUseCase @Inject constructor( } disadvantage && !advantage -> { - val roll = List(amount) { random(faces, fail) to random(faces, fail) } + val roll = List(amount) { + val first = random(faces = faces, fail = fail, critical = critical) + val second = random(faces = faces, fail = fail, critical = critical) + first to second + } DiceRollResult( label = roll.joinToString(" + ") { "${it.first}~${it.second}" }, value = roll.sumOf { min(it.first, it.second) }, @@ -1009,7 +1036,9 @@ class DiceThrowUseCase @Inject constructor( } else -> { - val roll = List(amount) { random(faces, fail) } + val roll = List(amount) { + random(faces = faces, fail = fail, critical = critical) + } DiceRollResult( label = roll.toLabel(), value = roll.sum(), @@ -1020,8 +1049,11 @@ class DiceThrowUseCase @Inject constructor( return result } - private fun random(faces: Int, fail: Boolean): Int = - if (fail) 1 else (Math.random() * faces + 1).toInt() + private fun random(faces: Int, fail: Boolean, critical: Boolean): Int = when { + fail && !critical -> 1 + critical && !fail -> faces + else -> (Math.random() * faces + 1).toInt() + } } private data class DiceRollResult( diff --git a/app/src/main/java/com/pixelized/rplexicon/data/model/Alteration.kt b/app/src/main/java/com/pixelized/rplexicon/data/model/Alteration.kt index 4bb3ba1..5539d3e 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/model/Alteration.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/model/Alteration.kt @@ -9,9 +9,10 @@ data class Alteration( ) { data class Status( val name: String, - val advantage: Boolean, - val disadvantage: Boolean, - val fail: Boolean, + val advantage: Boolean = false, + val disadvantage: Boolean = false, + val fail: Boolean = false, + val critical: Boolean = false, val dices: List = emptyList(), val bonus: List = emptyList(), ) diff --git a/app/src/main/java/com/pixelized/rplexicon/data/model/Attack.kt b/app/src/main/java/com/pixelized/rplexicon/data/model/Attack.kt index e9ac4fd..e49ca47 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/model/Attack.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/model/Attack.kt @@ -6,6 +6,7 @@ data class Attack( val range: String?, val hit: Throw?, val damage: Throw?, + val alterations: List, ) { enum class Type(val key: String) { PHYSICAL_MELEE_ATTACK("Mêlée"), diff --git a/app/src/main/java/com/pixelized/rplexicon/data/model/Roll.kt b/app/src/main/java/com/pixelized/rplexicon/data/model/Roll.kt index e00c50e..54ea7b8 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/model/Roll.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/model/Roll.kt @@ -12,7 +12,6 @@ data class Roll( val title: String? = null, val advantage: Boolean = false, val disadvantage: Boolean = false, - val fail: Boolean = false, val count: Int, val faces: Int, ) { @@ -23,84 +22,4 @@ data class Roll( val title: String, val value: Int, ) -} - -fun d20( - title: String? = null, - advantage: Boolean = false, - disadvantage: Boolean = false, - fail: Boolean = false, - amount: Int = 1, -) = Roll.Dice( - title = title, - advantage = advantage, - disadvantage = disadvantage, - count = amount, - fail = fail, - faces = 20, -) - -fun d12( - title: String? = null, - advantage: Boolean = false, - disadvantage: Boolean = false, - amount: Int = 1, -) = Roll.Dice( - title = title, - advantage = advantage, - disadvantage = disadvantage, - count = amount, - faces = 12, -) - -fun d10( - title: String? = null, - advantage: Boolean = false, - disadvantage: Boolean = false, - amount: Int = 1, -) = Roll.Dice( - title = title, - advantage = advantage, - disadvantage = disadvantage, - count = amount, - faces = 10, -) - -fun d8( - title: String? = null, - advantage: Boolean = false, - disadvantage: Boolean = false, - amount: Int = 1, -) = Roll.Dice( - title = title, - advantage = advantage, - disadvantage = disadvantage, - count = amount, - faces = 8, -) - -fun d6( - title: String? = null, - advantage: Boolean = false, - disadvantage: Boolean = false, - amount: Int = 1, -) = Roll.Dice( - title = title, - advantage = advantage, - disadvantage = disadvantage, - count = amount, - faces = 6, -) - -fun d4( - title: String? = null, - advantage: Boolean = false, - disadvantage: Boolean = false, - amount: Int = 1, -) = Roll.Dice( - title = title, - advantage = advantage, - disadvantage = disadvantage, - count = amount, - faces = 4, -) \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/parser/AttackParser.kt b/app/src/main/java/com/pixelized/rplexicon/data/parser/AttackParser.kt index 6a54fd7..fb1fbba 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/parser/AttackParser.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/parser/AttackParser.kt @@ -33,6 +33,7 @@ class AttackParser @Inject constructor( range = row.parse(column = RANGE), hit = throwParser.parse(value = row.parse(column = HIT)), damage = throwParser.parse(value = row.parse(column = DAMAGE)), + alterations = row.parseList(column = ALTERATION), ) actions .getOrPut(characterSheet.name) { mutableListOf() } @@ -59,6 +60,7 @@ class AttackParser @Inject constructor( private val RANGE = column("Portée") private val HIT = column("Touché") private val DAMAGE = column("Dommage") - private val COLUMNS get() = listOf(CHARACTER, NAME, TYPE, RANGE, HIT, DAMAGE) + private val ALTERATION = column("Altérations") + private val COLUMNS get() = listOf(CHARACTER, NAME, TYPE, RANGE, HIT, DAMAGE, ALTERATION) } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/parser/SheetParserScope.kt b/app/src/main/java/com/pixelized/rplexicon/data/parser/SheetParserScope.kt index 1dac2c7..b0bd6a9 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/parser/SheetParserScope.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/parser/SheetParserScope.kt @@ -80,4 +80,11 @@ class SheetParserScope { fun List<*>.parseUri(column: Column): Uri? = parse(column)?.takeIf { it.isNotBlank() }?.toUri() + + fun List<*>.parseList(column: Column, separator: String = ","): List = + parse(column) + ?.takeIf { it.isNotBlank() } + ?.split(separator) + ?.mapNotNull { it.toItem()?.trim() } + ?: emptyList() } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/parser/alteration/AlterationParser.kt b/app/src/main/java/com/pixelized/rplexicon/data/parser/alteration/AlterationParser.kt index aa27e44..0b73385 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/parser/alteration/AlterationParser.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/parser/alteration/AlterationParser.kt @@ -55,20 +55,24 @@ class AlterationParser @Inject constructor( null } if (alteration != null) { - characterSheets - .filter { // check if the alteration is applicable to the character - alteration.target.let { target -> - target == ALL || it.characterClass.any { it.value == target } || target == it.race || target == it.name + if (alteration.target == OBJECT && alteration.source == OBJECT) { + alterations + .getOrPut(alteration.name) { mutableListOf() } + .add(alteration) + } else { + characterSheets + .filter { // check if the alteration is applicable to the character + alteration.target.let { target -> + target == ALL || it.characterClass.any { it.value == target } || target == it.race || target == it.name + } } - } - .forEach { // check the default alteration state for that character - val default = row.parseBool(column = column(it.name)) - val assignedAlteration = alteration.copy( - active = default ?: false - ) - alterations.getOrPut(it.name) { mutableListOf() } - .add(assignedAlteration) - } + .forEach { // check the default alteration state for that character + val isActive = row.parseBool(column = column(it.name)) + alterations + .getOrPut(it.name) { mutableListOf() } + .add(alteration.copy(active = isActive ?: false)) + } + } } } } @@ -82,30 +86,26 @@ class AlterationParser @Inject constructor( ADVANTAGE -> Alteration.Status( name = name, advantage = true, - disadvantage = false, - fail = false, ) DISADVANTAGE -> Alteration.Status( name = name, - advantage = false, disadvantage = true, - fail = false, ) FAIL -> Alteration.Status( name = name, - advantage = false, - disadvantage = false, fail = true, ) + CRITICAL -> Alteration.Status( + name = name, + critical = true, + ) + else -> { Alteration.Status( name = name, - advantage = false, - disadvantage = false, - fail = false, dices = diceParser.findAll(title = name, value = value), bonus = flatParser.findAll(title = name, value = value), ) @@ -117,6 +117,8 @@ class AlterationParser @Inject constructor( private const val ADVANTAGE = "adv" private const val DISADVANTAGE = "dis" private const val FAIL = "fail" + private const val CRITICAL = "crit" + private const val OBJECT = "Objet" private val TARGET = column("Cible") private val SOURCE = column("Source") diff --git a/app/src/main/java/com/pixelized/rplexicon/data/repository/character/AlterationRepository.kt b/app/src/main/java/com/pixelized/rplexicon/data/repository/character/AlterationRepository.kt index c5b1391..89d37b8 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/repository/character/AlterationRepository.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/repository/character/AlterationRepository.kt @@ -35,6 +35,16 @@ class AlterationRepository @Inject constructor( return _assignedAlterations.value[character] ?: emptyList() } + /** + * get all [Alteration] for a list of objects + * @returna list of alterations. + */ + fun getAlterations(objects: List?): List { + return objects?.flatMap { objectAlteration -> + _assignedAlterations.value[objectAlteration] ?: emptyList() + } ?: emptyList() + } + /** * get [Alteration] for a character that affect at least one [Property] * @param character the character name diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/ConvertRollIntoDisplayableFactory.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/ConvertRollIntoDisplayableFactory.kt deleted file mode 100644 index d5e703a..0000000 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/ConvertRollIntoDisplayableFactory.kt +++ /dev/null @@ -1,133 +0,0 @@ -package com.pixelized.rplexicon.ui.screens.rolls - -import com.pixelized.rplexicon.data.model.Roll -import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio -import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio -import com.pixelized.rplexicon.utilitary.extentions.icon -import com.pixelized.rplexicon.utilitary.extentions.toLabel -import javax.inject.Inject -import kotlin.math.max -import kotlin.math.min - -data class RollResult( - val dice: RollDiceUio?, - val card: ThrowsCardUio?, -) - -class ConvertRollIntoDisplayableFactory @Inject constructor() { - - fun roll( - roll: Roll, - diceMultiplier: Int = 1, - ): RollResult { - val mainDices = roll.dices.firstOrNull() - val bonusDicesList = roll.dices.subList(fromIndex = 1, toIndex = roll.dices.size) - val allRolledValues = mutableListOf() - - val (rollDice, rollDetail) = mainDices?.let { dice -> - val (label, sum) = dice.roll(multiplier = diceMultiplier) - allRolledValues.add(sum) - - val diceIcon = dice.toRollCardUio(result = sum) - val cardDetail = dice.toThrowsCardUio(roll = label, result = sum) - - diceIcon to listOf(cardDetail) - } ?: (null to emptyList()) - - val isCriticalSuccess = rollDice?.isCriticalSuccess ?: false - val isCriticalFailure = rollDice?.isCriticalFailure ?: false - - val diceBonus = bonusDicesList.map { dice -> - val (label, sum) = dice.roll(multiplier = diceMultiplier) - allRolledValues.add(sum) - - dice.toThrowsCardUio( - roll = label, - result = sum, - ) - } - - val flatBonus = roll.bonus.map { bonus -> - allRolledValues.add(bonus.value) - - ThrowsCardUio.Detail( - title = bonus.title, - result = "${bonus.value}", - ) - } - - return RollResult( - dice = rollDice, - card = ThrowsCardUio( - title = roll.title, - highlight = roll.highlight, - dice = mainDices?.faces?.icon, - roll = allRolledValues.toLabel(), - result = when { - isCriticalSuccess -> (mainDices?.faces ?: 20).toString() - isCriticalFailure -> "1" - else -> "${allRolledValues.sum()}" - }, - isCriticalSuccess = isCriticalSuccess, - isCriticalFailure = isCriticalFailure, - details = rollDetail + diceBonus + flatBonus, - ), - ) - } - - private fun Roll.Dice.toRollCardUio(result: Int) = RollDiceUio( - icon = faces.icon, - isCriticalSuccess = count == 1 && faces == 20 && result == faces, - isCriticalFailure = count == 1 && faces == 20 && result == 1, - result = "$result", - ) - - private fun Roll.Dice.toThrowsCardUio(roll: String, result: Int) = ThrowsCardUio.Detail( - title = title, - throws = ThrowsCardUio.Throw( - dice = faces.icon, - advantage = advantage, - disadvantage = disadvantage, - roll = label, - result = roll, - ), - result = "$result", - ) - - private data class DiceRollResult( - val label: String, - val sum: Int, - ) - - private fun Roll.Dice.roll(multiplier: Int): DiceRollResult { - return when { - - advantage && !disadvantage -> { - val roll = List(count * multiplier) { random() to random() } - DiceRollResult( - label = roll.joinToString(" + ") { "${it.first}~${it.second}" }, - sum = roll.sumOf { max(it.first, it.second) }, - ) - } - - disadvantage && !advantage -> { - val roll = List(count * multiplier) { random() to random() } - DiceRollResult( - label = roll.joinToString(" + ") { "${it.first}~${it.second}" }, - sum = roll.sumOf { min(it.first, it.second) }, - ) - } - - else -> { - val roll = List(count * multiplier) { random() } - DiceRollResult( - label = roll.toLabel(), - sum = roll.sum(), - ) - } - } - } - - private fun Roll.Dice.random(): Int = if (fail) 1 else (Math.random() * faces + 1).toInt() -} - diff --git a/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/local/AlterationEx.kt b/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/local/AlterationEx.kt index 5e52c94..784249b 100644 --- a/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/local/AlterationEx.kt +++ b/app/src/main/java/com/pixelized/rplexicon/utilitary/extentions/local/AlterationEx.kt @@ -25,11 +25,16 @@ val List?.disadvantage: Boolean val List?.fail: Boolean get() = this?.any { it.fail } ?: false +// force a roll to hit a 20 +val List?.critical: Boolean + get() = this?.any { it.critical } ?: false + val Map>.isSavageAttacks: Boolean get() = this[Property.PHYSICAL_MELEE_DAMAGE]?.any { it.name == "Attaques sauvages" } == true val Map>.isBrutalCritical: Boolean get() = this[Property.PHYSICAL_MELEE_DAMAGE]?.any { it.name == "Critique brutal" } == true +// for damage roll, previous attack roll was critical val Map>.isCritical: Boolean get() = this[Property.PHYSICAL_MELEE_DAMAGE]?.any { it.name == "Critique" } == true