Add item specific alteration

This commit is contained in:
Thomas Andres Gomez 2023-12-17 17:59:37 +01:00
parent 2c6fc19a2c
commit 3bda885411
10 changed files with 137 additions and 291 deletions

View file

@ -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.ui.screens.rolls.composable.ThrowsCardUio
import com.pixelized.rplexicon.utilitary.extentions.icon import com.pixelized.rplexicon.utilitary.extentions.icon
import com.pixelized.rplexicon.utilitary.extentions.local.advantage 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.disadvantage
import com.pixelized.rplexicon.utilitary.extentions.local.fail import com.pixelized.rplexicon.utilitary.extentions.local.fail
import com.pixelized.rplexicon.utilitary.extentions.local.isBrutalCritical import com.pixelized.rplexicon.utilitary.extentions.local.isBrutalCritical
@ -52,7 +53,6 @@ class DiceThrowUseCase @Inject constructor(
val sheet = characterSheetRepository.find(name = diceThrow.character) val sheet = characterSheetRepository.find(name = diceThrow.character)
val alterations = alterationRepository.getAlterations(character = diceThrow.character) val alterations = alterationRepository.getAlterations(character = diceThrow.character)
.filter { alterationId.contains(it.name) } .filter { alterationId.contains(it.name) }
.toStatus()
return if (sheet != null) { return if (sheet != null) {
when (diceThrow) { when (diceThrow) {
@ -349,12 +349,15 @@ class DiceThrowUseCase @Inject constructor(
character = diceThrow.character, character = diceThrow.character,
action = diceThrow.weapon, action = diceThrow.weapon,
) )
val objectAlterations = alterationRepository.getAlterations(
objects = action?.alterations,
)
actionThrow( actionThrow(
character = sheet, character = sheet,
action = diceThrow.weapon, action = diceThrow.weapon,
diceThrow = action?.hit, diceThrow = action?.hit,
title = { getString(R.string.dice_roll_attack_hit_title, it) }, title = { getString(R.string.dice_roll_attack_hit_title, it) },
alterations = alterations, alterations = alterations + objectAlterations,
ability = Property.PHYSICAL_MELEE_ATTACK, ability = Property.PHYSICAL_MELEE_ATTACK,
) )
} }
@ -364,12 +367,15 @@ class DiceThrowUseCase @Inject constructor(
character = diceThrow.character, character = diceThrow.character,
action = diceThrow.weapon, action = diceThrow.weapon,
) )
val objectAlterations = alterationRepository.getAlterations(
objects = action?.alterations,
)
actionThrow( actionThrow(
character = sheet, character = sheet,
action = diceThrow.weapon, action = diceThrow.weapon,
diceThrow = action?.damage, diceThrow = action?.damage,
title = { getString(R.string.dice_roll_attack_damage_title, it) }, title = { getString(R.string.dice_roll_attack_damage_title, it) },
alterations = alterations, alterations = alterations + objectAlterations,
ability = Property.PHYSICAL_MELEE_DAMAGE, ability = Property.PHYSICAL_MELEE_DAMAGE,
) )
} }
@ -379,12 +385,15 @@ class DiceThrowUseCase @Inject constructor(
character = diceThrow.character, character = diceThrow.character,
action = diceThrow.weapon, action = diceThrow.weapon,
) )
val objectAlterations = alterationRepository.getAlterations(
objects = action?.alterations,
)
actionThrow( actionThrow(
character = sheet, character = sheet,
action = diceThrow.weapon, action = diceThrow.weapon,
diceThrow = action?.hit, diceThrow = action?.hit,
title = { getString(R.string.dice_roll_attack_hit_title, it) }, title = { getString(R.string.dice_roll_attack_hit_title, it) },
alterations = alterations, alterations = alterations + objectAlterations,
ability = Property.PHYSICAL_RANGE_ATTACK, ability = Property.PHYSICAL_RANGE_ATTACK,
) )
} }
@ -394,12 +403,15 @@ class DiceThrowUseCase @Inject constructor(
character = diceThrow.character, character = diceThrow.character,
action = diceThrow.weapon, action = diceThrow.weapon,
) )
val objectAlterations = alterationRepository.getAlterations(
objects = action?.alterations,
)
actionThrow( actionThrow(
character = sheet, character = sheet,
action = diceThrow.weapon, action = diceThrow.weapon,
diceThrow = action?.damage, diceThrow = action?.damage,
title = { getString(R.string.dice_roll_attack_damage_title, it) }, title = { getString(R.string.dice_roll_attack_damage_title, it) },
alterations = alterations, alterations = alterations + objectAlterations,
ability = Property.PHYSICAL_RANGE_DAMAGE, ability = Property.PHYSICAL_RANGE_DAMAGE,
) )
} }
@ -414,7 +426,7 @@ class DiceThrowUseCase @Inject constructor(
action = diceThrow.item, action = diceThrow.item,
diceThrow = action?.effect, diceThrow = action?.effect,
title = { getString(R.string.dice_roll_use_object, it) }, title = { getString(R.string.dice_roll_use_object, it) },
alterations = emptyMap(), alterations = emptyList(),
ability = null, ability = null,
) )
} }
@ -481,7 +493,7 @@ class DiceThrowUseCase @Inject constructor(
private fun abilityThrow( private fun abilityThrow(
character: CharacterSheet, character: CharacterSheet,
alterations: Map<Property, List<Alteration.Status>>, alterations: List<Alteration>,
abilityLabel: Context.() -> String, abilityLabel: Context.() -> String,
relatedLabel: Context.() -> String?, relatedLabel: Context.() -> String?,
ability: Property, ability: Property,
@ -499,7 +511,7 @@ class DiceThrowUseCase @Inject constructor(
private fun savingThrow( private fun savingThrow(
character: CharacterSheet, character: CharacterSheet,
alterations: Map<Property, List<Alteration.Status>>, alterations: List<Alteration>,
abilityLabel: Context.() -> String, abilityLabel: Context.() -> String,
relatedLabel: Context.() -> String?, relatedLabel: Context.() -> String?,
ability: Property, ability: Property,
@ -517,7 +529,7 @@ class DiceThrowUseCase @Inject constructor(
private fun rollAbility( private fun rollAbility(
character: CharacterSheet, character: CharacterSheet,
alterations: Map<Property, List<Alteration.Status>>, alterations: List<Alteration>,
abilityTitle: Context.(label: String) -> String, abilityTitle: Context.(label: String) -> String,
abilityLabel: Context.() -> String, abilityLabel: Context.() -> String,
relatedTitle: Context.(label: String) -> String?, relatedTitle: Context.(label: String) -> String?,
@ -531,27 +543,30 @@ class DiceThrowUseCase @Inject constructor(
val abilityTitleString = abilityTitle(application, abilityLabelString) val abilityTitleString = abilityTitle(application, abilityLabelString)
// check if the roll is affected by some status. // check if the roll is affected by some status.
val advantage = alterations[ability].advantage val advantage = status[ability].advantage
val disadvantage = alterations[ability].disadvantage val disadvantage = status[ability].disadvantage
val fail = alterations[ability].fail
// main roll // 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. // 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 // fetch and build a list of flat bonus
val flatAlterationBonus = alterations[ability].flatAlterationBonus() val flatAlterationBonus = status[ability].flatAlterationBonus()
// fetch and build the associated characteristic // fetch and build the associated characteristic
val relatedStatBonus = when (relatedStat) { val relatedStatBonus = when (relatedStat) {
Property.STRENGTH -> (character.strength + alterations[relatedStat].sum).modifier Property.STRENGTH -> (character.strength + status[relatedStat].sum).modifier
Property.DEXTERITY -> (character.dexterity + alterations[relatedStat].sum).modifier Property.DEXTERITY -> (character.dexterity + status[relatedStat].sum).modifier
Property.CONSTITUTION -> (character.constitution + alterations[relatedStat].sum).modifier Property.CONSTITUTION -> (character.constitution + status[relatedStat].sum).modifier
Property.INTELLIGENCE -> (character.intelligence + alterations[relatedStat].sum).modifier Property.INTELLIGENCE -> (character.intelligence + status[relatedStat].sum).modifier
Property.WISDOM -> (character.wisdom + alterations[relatedStat].sum).modifier Property.WISDOM -> (character.wisdom + status[relatedStat].sum).modifier
Property.CHARISMA -> (character.charisma + alterations[relatedStat].sum).modifier Property.CHARISMA -> (character.charisma + status[relatedStat].sum).modifier
Property.PROFICIENCY -> character.proficiency Property.PROFICIENCY -> character.proficiency
else -> null else -> null
}?.let { value -> }?.let { value ->
@ -657,7 +672,7 @@ class DiceThrowUseCase @Inject constructor(
action: String, action: String,
diceThrow: Throw?, diceThrow: Throw?,
title: Context.(action: String) -> String, title: Context.(action: String) -> String,
alterations: Map<Property, List<Alteration.Status>>, alterations: List<Alteration>,
ability: Property?, ability: Property?,
): DiceThrowResult { ): DiceThrowResult {
with(ThrowScope(context = application, character = character, alterations = alterations)) { 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. // check if the roll is affected by some status.
val advantage = alterations[ability].advantage val advantage = status[ability].advantage
val disadvantage = alterations[ability].disadvantage val disadvantage = status[ability].disadvantage
val fail = alterations[ability].fail
// compute the amount of main dice to throw. // compute the amount of main dice to throw.
val amount = if (alterations.isCritical) { val amount = if (status.isCritical) {
diceThrow?.amount?.times(2)?.let { 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 it.plus(1) else it
}?.let { }?.let {
if (ability == Property.PHYSICAL_MELEE_DAMAGE && alterations.isBrutalCritical) if (ability == Property.PHYSICAL_MELEE_DAMAGE && status.isBrutalCritical)
it.plus(1) else it it.plus(1) else it
} }
} else { } else {
@ -695,14 +709,15 @@ class DiceThrowUseCase @Inject constructor(
faces = diceThrow?.faces ?: 20, faces = diceThrow?.faces ?: 20,
advantage = advantage, advantage = advantage,
disadvantage = disadvantage, disadvantage = disadvantage,
fail = fail, fail = status[ability].fail,
critical = status[ability].critical,
) )
// fetch and build a list of dice roll base on alterations. // 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 // fetch and build a list of flat bonus
val flatAlterationBonus = alterations[ability].flatAlterationBonus() val flatAlterationBonus = status[ability].flatAlterationBonus()
// fetch and build the associated characteristic // fetch and build the associated characteristic
val relatedStatBonus = diceThrow.statBonus(name = action) val relatedStatBonus = diceThrow.statBonus(name = action)
@ -763,7 +778,7 @@ class DiceThrowUseCase @Inject constructor(
character: CharacterSheet, character: CharacterSheet,
spell: AssignedSpell?, spell: AssignedSpell?,
level: Int, level: Int,
alterations: Map<Property, List<Alteration.Status>>, alterations: List<Alteration>,
): DiceThrowResult { ): DiceThrowResult {
with(ThrowScope(context = application, character = character, alterations = alterations)) { with(ThrowScope(context = application, character = character, alterations = alterations)) {
// retrieve some wording. // retrieve some wording.
@ -833,7 +848,7 @@ class DiceThrowUseCase @Inject constructor(
private fun skillThrow( private fun skillThrow(
character: CharacterSheet, character: CharacterSheet,
alterations: Map<Property, List<Alteration.Status>>, alterations: List<Alteration>,
skill: Skill?, skill: Skill?,
): DiceThrowResult { ): DiceThrowResult {
with(ThrowScope(context = application, character = character, alterations = alterations)) { 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. // 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 // 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 // fetch and build the associated characteristic, proficiency or level
val relatedStatBonus = skill?.effect.statBonus(name = skill?.name) val relatedStatBonus = skill?.effect.statBonus(name = skill?.name)
@ -894,8 +909,10 @@ class DiceThrowUseCase @Inject constructor(
val context: Context, val context: Context,
val allValue: MutableList<Int> = mutableListOf(), val allValue: MutableList<Int> = mutableListOf(),
val character: CharacterSheet, val character: CharacterSheet,
val alterations: Map<Property, List<Alteration.Status>>, val alterations: List<Alteration>,
) { ) {
val status = this.alterations.toStatus()
/** /**
* Fetch any stats / proficiency / level related bonus and build a ThrowsCardUio.Detail for each. * 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? { fun Property.statBonus(name: String?): ThrowsCardUio.Detail? {
return when (this) { return when (this) {
Property.STRENGTH -> (character.strength + alterations[this].sum).modifier Property.STRENGTH -> (character.strength + this@ThrowScope.status[this].sum).modifier
Property.DEXTERITY -> (character.dexterity + alterations[this].sum).modifier Property.DEXTERITY -> (character.dexterity + this@ThrowScope.status[this].sum).modifier
Property.CONSTITUTION -> (character.constitution + alterations[this].sum).modifier Property.CONSTITUTION -> (character.constitution + this@ThrowScope.status[this].sum).modifier
Property.INTELLIGENCE -> (character.intelligence + alterations[this].sum).modifier Property.INTELLIGENCE -> (character.intelligence + this@ThrowScope.status[this].sum).modifier
Property.WISDOM -> (character.wisdom + alterations[this].sum).modifier Property.WISDOM -> (character.wisdom + this@ThrowScope.status[this].sum).modifier
Property.CHARISMA -> (character.charisma + alterations[this].sum).modifier Property.CHARISMA -> (character.charisma + this@ThrowScope.status[this].sum).modifier
Property.PROFICIENCY -> character.proficiency Property.PROFICIENCY -> character.proficiency
Property.LEVEL -> character.level Property.LEVEL -> character.level
else -> null else -> null
@ -945,11 +962,12 @@ class DiceThrowUseCase @Inject constructor(
return this?.flatMap { status -> return this?.flatMap { status ->
status.dices.map { dice -> status.dices.map { dice ->
val localRoll = roll( 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, faces = dice.faces,
advantage = dice.advantage, advantage = dice.advantage,
disadvantage = dice.disadvantage, disadvantage = dice.disadvantage,
fail = dice.fail fail = status.fail,
critical = status.critical,
) )
ThrowsCardUio.Detail( ThrowsCardUio.Detail(
title = dice.title, title = dice.title,
@ -990,10 +1008,15 @@ class DiceThrowUseCase @Inject constructor(
advantage: Boolean = false, advantage: Boolean = false,
disadvantage: Boolean = false, disadvantage: Boolean = false,
fail: Boolean = false, fail: Boolean = false,
critical: Boolean = false,
): DiceRollResult { ): DiceRollResult {
val result = when { val result = when {
advantage && !disadvantage -> { 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( DiceRollResult(
label = roll.joinToString(" + ") { "${it.first}~${it.second}" }, label = roll.joinToString(" + ") { "${it.first}~${it.second}" },
value = roll.sumOf { max(it.first, it.second) }, value = roll.sumOf { max(it.first, it.second) },
@ -1001,7 +1024,11 @@ class DiceThrowUseCase @Inject constructor(
} }
disadvantage && !advantage -> { 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( DiceRollResult(
label = roll.joinToString(" + ") { "${it.first}~${it.second}" }, label = roll.joinToString(" + ") { "${it.first}~${it.second}" },
value = roll.sumOf { min(it.first, it.second) }, value = roll.sumOf { min(it.first, it.second) },
@ -1009,7 +1036,9 @@ class DiceThrowUseCase @Inject constructor(
} }
else -> { else -> {
val roll = List(amount) { random(faces, fail) } val roll = List(amount) {
random(faces = faces, fail = fail, critical = critical)
}
DiceRollResult( DiceRollResult(
label = roll.toLabel(), label = roll.toLabel(),
value = roll.sum(), value = roll.sum(),
@ -1020,8 +1049,11 @@ class DiceThrowUseCase @Inject constructor(
return result return result
} }
private fun random(faces: Int, fail: Boolean): Int = private fun random(faces: Int, fail: Boolean, critical: Boolean): Int = when {
if (fail) 1 else (Math.random() * faces + 1).toInt() fail && !critical -> 1
critical && !fail -> faces
else -> (Math.random() * faces + 1).toInt()
}
} }
private data class DiceRollResult( private data class DiceRollResult(

View file

@ -9,9 +9,10 @@ data class Alteration(
) { ) {
data class Status( data class Status(
val name: String, val name: String,
val advantage: Boolean, val advantage: Boolean = false,
val disadvantage: Boolean, val disadvantage: Boolean = false,
val fail: Boolean, val fail: Boolean = false,
val critical: Boolean = false,
val dices: List<Roll.Dice> = emptyList(), val dices: List<Roll.Dice> = emptyList(),
val bonus: List<Roll.Bonus> = emptyList(), val bonus: List<Roll.Bonus> = emptyList(),
) )

View file

@ -6,6 +6,7 @@ data class Attack(
val range: String?, val range: String?,
val hit: Throw?, val hit: Throw?,
val damage: Throw?, val damage: Throw?,
val alterations: List<String>,
) { ) {
enum class Type(val key: String) { enum class Type(val key: String) {
PHYSICAL_MELEE_ATTACK("Mêlée"), PHYSICAL_MELEE_ATTACK("Mêlée"),

View file

@ -12,7 +12,6 @@ data class Roll(
val title: String? = null, val title: String? = null,
val advantage: Boolean = false, val advantage: Boolean = false,
val disadvantage: Boolean = false, val disadvantage: Boolean = false,
val fail: Boolean = false,
val count: Int, val count: Int,
val faces: Int, val faces: Int,
) { ) {
@ -23,84 +22,4 @@ data class Roll(
val title: String, val title: String,
val value: Int, 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,
)

View file

@ -33,6 +33,7 @@ class AttackParser @Inject constructor(
range = row.parse(column = RANGE), range = row.parse(column = RANGE),
hit = throwParser.parse(value = row.parse(column = HIT)), hit = throwParser.parse(value = row.parse(column = HIT)),
damage = throwParser.parse(value = row.parse(column = DAMAGE)), damage = throwParser.parse(value = row.parse(column = DAMAGE)),
alterations = row.parseList(column = ALTERATION),
) )
actions actions
.getOrPut(characterSheet.name) { mutableListOf() } .getOrPut(characterSheet.name) { mutableListOf() }
@ -59,6 +60,7 @@ class AttackParser @Inject constructor(
private val RANGE = column("Portée") private val RANGE = column("Portée")
private val HIT = column("Touché") private val HIT = column("Touché")
private val DAMAGE = column("Dommage") 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)
} }
} }

View file

@ -80,4 +80,11 @@ class SheetParserScope<T> {
fun List<*>.parseUri(column: Column): Uri? = fun List<*>.parseUri(column: Column): Uri? =
parse(column)?.takeIf { it.isNotBlank() }?.toUri() parse(column)?.takeIf { it.isNotBlank() }?.toUri()
fun List<*>.parseList(column: Column, separator: String = ","): List<String> =
parse(column)
?.takeIf { it.isNotBlank() }
?.split(separator)
?.mapNotNull { it.toItem()?.trim() }
?: emptyList()
} }

View file

@ -55,20 +55,24 @@ class AlterationParser @Inject constructor(
null null
} }
if (alteration != null) { if (alteration != null) {
characterSheets if (alteration.target == OBJECT && alteration.source == OBJECT) {
.filter { // check if the alteration is applicable to the character alterations
alteration.target.let { target -> .getOrPut(alteration.name) { mutableListOf() }
target == ALL || it.characterClass.any { it.value == target } || target == it.race || target == it.name .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
.forEach { // check the default alteration state for that character val isActive = row.parseBool(column = column(it.name))
val default = row.parseBool(column = column(it.name)) alterations
val assignedAlteration = alteration.copy( .getOrPut(it.name) { mutableListOf() }
active = default ?: false .add(alteration.copy(active = isActive ?: false))
) }
alterations.getOrPut(it.name) { mutableListOf() } }
.add(assignedAlteration)
}
} }
} }
} }
@ -82,30 +86,26 @@ class AlterationParser @Inject constructor(
ADVANTAGE -> Alteration.Status( ADVANTAGE -> Alteration.Status(
name = name, name = name,
advantage = true, advantage = true,
disadvantage = false,
fail = false,
) )
DISADVANTAGE -> Alteration.Status( DISADVANTAGE -> Alteration.Status(
name = name, name = name,
advantage = false,
disadvantage = true, disadvantage = true,
fail = false,
) )
FAIL -> Alteration.Status( FAIL -> Alteration.Status(
name = name, name = name,
advantage = false,
disadvantage = false,
fail = true, fail = true,
) )
CRITICAL -> Alteration.Status(
name = name,
critical = true,
)
else -> { else -> {
Alteration.Status( Alteration.Status(
name = name, name = name,
advantage = false,
disadvantage = false,
fail = false,
dices = diceParser.findAll(title = name, value = value), dices = diceParser.findAll(title = name, value = value),
bonus = flatParser.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 ADVANTAGE = "adv"
private const val DISADVANTAGE = "dis" private const val DISADVANTAGE = "dis"
private const val FAIL = "fail" private const val FAIL = "fail"
private const val CRITICAL = "crit"
private const val OBJECT = "Objet"
private val TARGET = column("Cible") private val TARGET = column("Cible")
private val SOURCE = column("Source") private val SOURCE = column("Source")

View file

@ -35,6 +35,16 @@ class AlterationRepository @Inject constructor(
return _assignedAlterations.value[character] ?: emptyList() return _assignedAlterations.value[character] ?: emptyList()
} }
/**
* get all [Alteration] for a list of objects
* @returna list of alterations.
*/
fun getAlterations(objects: List<String>?): List<Alteration> {
return objects?.flatMap { objectAlteration ->
_assignedAlterations.value[objectAlteration] ?: emptyList()
} ?: emptyList()
}
/** /**
* get [Alteration] for a character that affect at least one [Property] * get [Alteration] for a character that affect at least one [Property]
* @param character the character name * @param character the character name

View file

@ -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<Int>()
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()
}

View file

@ -25,11 +25,16 @@ val List<Alteration.Status>?.disadvantage: Boolean
val List<Alteration.Status>?.fail: Boolean val List<Alteration.Status>?.fail: Boolean
get() = this?.any { it.fail } ?: false get() = this?.any { it.fail } ?: false
// force a roll to hit a 20
val List<Alteration.Status>?.critical: Boolean
get() = this?.any { it.critical } ?: false
val Map<Property, List<Alteration.Status>>.isSavageAttacks: Boolean val Map<Property, List<Alteration.Status>>.isSavageAttacks: Boolean
get() = this[Property.PHYSICAL_MELEE_DAMAGE]?.any { it.name == "Attaques sauvages" } == true get() = this[Property.PHYSICAL_MELEE_DAMAGE]?.any { it.name == "Attaques sauvages" } == true
val Map<Property, List<Alteration.Status>>.isBrutalCritical: Boolean val Map<Property, List<Alteration.Status>>.isBrutalCritical: Boolean
get() = this[Property.PHYSICAL_MELEE_DAMAGE]?.any { it.name == "Critique brutal" } == true get() = this[Property.PHYSICAL_MELEE_DAMAGE]?.any { it.name == "Critique brutal" } == true
// for damage roll, previous attack roll was critical
val Map<Property, List<Alteration.Status>>.isCritical: Boolean val Map<Property, List<Alteration.Status>>.isCritical: Boolean
get() = this[Property.PHYSICAL_MELEE_DAMAGE]?.any { it.name == "Critique" } == true get() = this[Property.PHYSICAL_MELEE_DAMAGE]?.any { it.name == "Critique" } == true