Add level modifier to throws and refactor usecase.

This commit is contained in:
Thomas Andres Gomez 2023-11-28 16:30:11 +01:00
parent 601e1b086b
commit cbb0460bbf
6 changed files with 427 additions and 515 deletions

View file

@ -525,13 +525,11 @@ class DiceThrowUseCase @Inject constructor(
ability: Property,
relatedStat: Property?,
): DiceThrowResult {
with(ThrowScope(context = application, character = character, alterations = alterations)) {
// retrieve some wording.
val abilityLabelString = abilityLabel(application)
val abilityTitleString = abilityTitle(application, abilityLabelString)
// create a list destined to contain all the values (rolled + bonus)
val allValue = mutableListOf<Int>()
// check if the roll is affected by some status.
val advantage = alterations[ability].advantage
val disadvantage = alterations[ability].disadvantage
@ -539,43 +537,12 @@ class DiceThrowUseCase @Inject constructor(
// main roll
val result = roll(advantage = advantage, disadvantage = disadvantage, fail = fail)
allValue.add(result.value)
// fetch and build a list of dice roll base on alterations.
val diceAlterationBonus = alterations[ability]?.flatMap { status ->
status.dices.map { dice ->
val localRoll = roll(
amount = dice.count,
faces = dice.faces,
advantage = dice.advantage,
disadvantage = dice.disadvantage,
fail = dice.fail
)
allValue.add(localRoll.value)
ThrowsCardUio.Detail(
title = dice.title,
throws = ThrowsCardUio.Throw(
dice = dice.faces.icon,
advantage = dice.advantage,
disadvantage = dice.disadvantage,
roll = "${dice.count}d${dice.faces}",
result = localRoll.label,
),
result = "${localRoll.value}",
)
}
} ?: emptyList()
val diceAlterationBonus = alterations[ability].alterationsBonus()
// fetch and build a list of flat bonus
val flatAlterationBonus = alterations[ability]?.flatMap { status ->
status.bonus.map { bonus ->
allValue.add(bonus.value)
ThrowsCardUio.Detail(
title = bonus.title,
result = "${bonus.value}",
)
}
} ?: emptyList()
val flatAlterationBonus = alterations[ability].flatAlterationBonus()
// fetch and build the associated characteristic
val relatedStatBonus = when (relatedStat) {
@ -683,6 +650,7 @@ class DiceThrowUseCase @Inject constructor(
)
)
}
}
private fun actionThrow(
character: CharacterSheet,
@ -692,21 +660,18 @@ class DiceThrowUseCase @Inject constructor(
alterations: Map<Property, List<Alteration.Status>>,
ability: Property?,
): DiceThrowResult {
with(ThrowScope(context = application, character = character, alterations = alterations)) {
// retrieve some wording.
val titleString = title(application, action)
// check if this throw can be a critical success of failure.
val canMakeCriticalRoll = when (ability) {
Property.PHYSICAL_MELEE_ATTACK,
Property.PHYSICAL_RANGE_ATTACK,
Property.PHYSICAL_MELEE_ATTACK -> true
Property.PHYSICAL_RANGE_ATTACK -> true
Property.SPELL_ATTACK -> true
else -> false
}
// create a list destined to contain all the values (rolled + bonus)
val allValue = mutableListOf<Int>()
// check if the roll is affected by some status.
val advantage = alterations[ability].advantage
val disadvantage = alterations[ability].disadvantage
@ -732,77 +697,15 @@ class DiceThrowUseCase @Inject constructor(
disadvantage = disadvantage,
fail = fail,
)
allValue.add(result.value)
// fetch and build a list of dice roll base on alterations.
val diceAlterationBonus = alterations[ability]?.flatMap { status ->
status.dices.map { dice ->
val localRoll = roll(
amount = if (alterations.isCritical) dice.count * 2 else dice.count,
faces = dice.faces,
advantage = dice.advantage,
disadvantage = dice.disadvantage,
fail = dice.fail
)
allValue.add(localRoll.value)
ThrowsCardUio.Detail(
title = dice.title,
throws = ThrowsCardUio.Throw(
dice = dice.faces.icon,
advantage = dice.advantage,
disadvantage = dice.disadvantage,
roll = "${dice.count}d${dice.faces}",
result = localRoll.label,
),
result = "${localRoll.value}",
)
}
} ?: emptyList()
val diceAlterationBonus = alterations[ability].alterationsBonus()
// fetch and build a list of flat bonus
val flatAlterationBonus = alterations[ability]?.flatMap { status ->
status.bonus.map { bonus ->
allValue.add(bonus.value)
ThrowsCardUio.Detail(
title = bonus.title,
result = "${bonus.value}",
)
}
} ?: emptyList()
val flatAlterationBonus = alterations[ability].flatAlterationBonus()
// fetch and build the associated characteristic
val relatedStatBonus = diceThrow?.modifier?.mapNotNull { property ->
when (property) {
Property.STRENGTH -> (character.strength + alterations[property].sum).modifier
Property.DEXTERITY -> (character.dexterity + alterations[property].sum).modifier
Property.CONSTITUTION -> (character.constitution + alterations[property].sum).modifier
Property.INTELLIGENCE -> (character.intelligence + alterations[property].sum).modifier
Property.WISDOM -> (character.wisdom + alterations[property].sum).modifier
Property.CHARISMA -> (character.charisma + alterations[property].sum).modifier
Property.PROFICIENCY -> character.proficiency
else -> null
}?.let { value ->
val titleLabel = if (property == Property.PROFICIENCY) {
application.getString(R.string.dice_roll_mastery_proficiency, action)
} else {
val label = when (property) {
Property.STRENGTH -> application.getString(R.string.character_sheet_stat_strength)
Property.DEXTERITY -> application.getString(R.string.character_sheet_stat_dexterity)
Property.CONSTITUTION -> application.getString(R.string.character_sheet_stat_constitution)
Property.INTELLIGENCE -> application.getString(R.string.character_sheet_stat_intelligence)
Property.WISDOM -> application.getString(R.string.character_sheet_stat_wisdom)
Property.CHARISMA -> application.getString(R.string.character_sheet_stat_charisma)
else -> ""
}
application.getString(R.string.dice_roll_bonus_detail, label)
}
allValue.add(value)
ThrowsCardUio.Detail(
title = titleLabel,
result = "$value",
)
}
} ?: emptyList()
val relatedStatBonus = diceThrow.statBonus(name = action)
// check for flat dice bonus (ex: healing potion 2d4 + 2)
val flatBonus = diceThrow?.flat?.takeIf { it > 0 }?.let {
@ -813,7 +716,7 @@ class DiceThrowUseCase @Inject constructor(
result = "$it",
)
)
}?: emptyList()
} ?: emptyList()
// build the result.
return DiceThrowResult(
@ -853,6 +756,7 @@ class DiceThrowUseCase @Inject constructor(
)
)
}
}
private fun spellThrow(
character: CharacterSheet,
@ -860,19 +764,16 @@ class DiceThrowUseCase @Inject constructor(
level: Int,
alterations: Map<Property, List<Alteration.Status>>,
): DiceThrowResult {
with(ThrowScope(context = application, character = character, alterations = alterations)) {
// retrieve some wording.
val spellName = spell?.spell?.name ?: ""
val titleString = application.getString(R.string.dice_roll_spell_cast, spellName)
// create a list destined to contain all the values (rolled + bonus)
val allValue = mutableListOf<Int>()
// main roll
val result = roll(
amount = spell?.effect?.amount ?: 1,
faces = spell?.effect?.faces ?: 4,
)
allValue.add(result.value)
// fetch and build a list of additionnal level effect.
val levelBonus = if (spell?.level != null) {
@ -881,7 +782,6 @@ class DiceThrowUseCase @Inject constructor(
amount = spell.level.amount,
faces = spell.level.faces,
)
allValue.add(localRoll.value)
ThrowsCardUio.Detail(
title = application.getString(R.string.spell_level_chooser_label, "$it"),
throws = ThrowsCardUio.Throw(
@ -897,38 +797,7 @@ class DiceThrowUseCase @Inject constructor(
}
// fetch and build the associated characteristic
val relatedStatBonus = spell?.effect?.modifier?.mapNotNull { property ->
when (property) {
Property.STRENGTH -> (character.strength + alterations[property].sum).modifier
Property.DEXTERITY -> (character.dexterity + alterations[property].sum).modifier
Property.CONSTITUTION -> (character.constitution + alterations[property].sum).modifier
Property.INTELLIGENCE -> (character.intelligence + alterations[property].sum).modifier
Property.WISDOM -> (character.wisdom + alterations[property].sum).modifier
Property.CHARISMA -> (character.charisma + alterations[property].sum).modifier
Property.PROFICIENCY -> character.proficiency
else -> null
}?.let { value ->
val titleLabel = if (property == Property.PROFICIENCY) {
application.getString(R.string.dice_roll_mastery_proficiency, spell)
} else {
val label = when (property) {
Property.STRENGTH -> application.getString(R.string.character_sheet_stat_strength)
Property.DEXTERITY -> application.getString(R.string.character_sheet_stat_dexterity)
Property.CONSTITUTION -> application.getString(R.string.character_sheet_stat_constitution)
Property.INTELLIGENCE -> application.getString(R.string.character_sheet_stat_intelligence)
Property.WISDOM -> application.getString(R.string.character_sheet_stat_wisdom)
Property.CHARISMA -> application.getString(R.string.character_sheet_stat_charisma)
else -> ""
}
application.getString(R.string.dice_roll_bonus_detail, label)
}
allValue.add(value)
ThrowsCardUio.Detail(
title = titleLabel,
result = "$value",
)
}
} ?: emptyList()
val relatedStatBonus = spell?.effect.statBonus(name = spellName)
// build the result.
return DiceThrowResult(
@ -956,95 +825,32 @@ class DiceThrowUseCase @Inject constructor(
)
)
}
}
private fun skillThrow(
character: CharacterSheet,
alterations: Map<Property, List<Alteration.Status>>,
skill: Skill?,
): DiceThrowResult {
with(ThrowScope(context = application, character = character, alterations = alterations)) {
// retrieve some wording.
val spellName = skill?.name
val titleString = application.getString(R.string.dice_roll_spell_cast, spellName)
// create a list destined to contain all the values (rolled + bonus)
val allValue = mutableListOf<Int>()
// main roll
val result = roll(
amount = skill?.effect?.amount ?: 1,
faces = skill?.effect?.faces ?: 4,
)
allValue.add(result.value)
// fetch and build a list of dice roll base on alterations.
val diceAlterationBonus = alterations[Property.SKILL]?.flatMap { status ->
status.dices.map { dice ->
val localRoll = roll(
amount = if (alterations.isCritical) dice.count * 2 else dice.count,
faces = dice.faces,
advantage = dice.advantage,
disadvantage = dice.disadvantage,
fail = dice.fail
)
allValue.add(localRoll.value)
ThrowsCardUio.Detail(
title = dice.title,
throws = ThrowsCardUio.Throw(
dice = dice.faces.icon,
advantage = dice.advantage,
disadvantage = dice.disadvantage,
roll = "${dice.count}d${dice.faces}",
result = localRoll.label,
),
result = "${localRoll.value}",
)
}
} ?: emptyList()
val diceAlterationBonus = alterations[Property.SKILL].alterationsBonus()
// fetch and build a list of flat bonus
val flatAlterationBonus = alterations[Property.SKILL]?.flatMap { status ->
status.bonus.map { bonus ->
allValue.add(bonus.value)
ThrowsCardUio.Detail(
title = bonus.title,
result = "${bonus.value}",
)
}
} ?: emptyList()
val flatAlterationBonus = alterations[Property.SKILL].flatAlterationBonus()
// fetch and build the associated characteristic
val relatedStatBonus = skill?.effect?.modifier?.mapNotNull { property ->
when (property) {
Property.STRENGTH -> (character.strength + alterations[property].sum).modifier
Property.DEXTERITY -> (character.dexterity + alterations[property].sum).modifier
Property.CONSTITUTION -> (character.constitution + alterations[property].sum).modifier
Property.INTELLIGENCE -> (character.intelligence + alterations[property].sum).modifier
Property.WISDOM -> (character.wisdom + alterations[property].sum).modifier
Property.CHARISMA -> (character.charisma + alterations[property].sum).modifier
Property.PROFICIENCY -> character.proficiency
else -> null
}?.let { value ->
val titleLabel = if (property == Property.PROFICIENCY) {
application.getString(R.string.dice_roll_mastery_proficiency, skill)
} else {
val label = when (property) {
Property.STRENGTH -> application.getString(R.string.character_sheet_stat_strength)
Property.DEXTERITY -> application.getString(R.string.character_sheet_stat_dexterity)
Property.CONSTITUTION -> application.getString(R.string.character_sheet_stat_constitution)
Property.INTELLIGENCE -> application.getString(R.string.character_sheet_stat_intelligence)
Property.WISDOM -> application.getString(R.string.character_sheet_stat_wisdom)
Property.CHARISMA -> application.getString(R.string.character_sheet_stat_charisma)
else -> ""
}
application.getString(R.string.dice_roll_bonus_detail, label)
}
allValue.add(value)
ThrowsCardUio.Detail(
title = titleLabel,
result = "$value",
)
}
} ?: emptyList()
// fetch and build the associated characteristic, proficiency or level
val relatedStatBonus = skill?.effect.statBonus(name = skill?.name)
// build the result.
return DiceThrowResult(
@ -1072,15 +878,113 @@ class DiceThrowUseCase @Inject constructor(
)
)
}
}
private fun roll(
/**
* Helper class to track rolls and declare helper method.
*/
private class ThrowScope(
val context: Context,
val allValue: MutableList<Int> = mutableListOf(),
val character: CharacterSheet,
val alterations: Map<Property, List<Alteration.Status>>,
) {
/**
* Fetch any stats / proficiency / level related bonus and build a ThrowsCardUio.Detail for each.
*/
fun Throw?.statBonus(name: String?): List<ThrowsCardUio.Detail> {
return this?.modifier?.mapNotNull { it.statBonus(name = name) } ?: emptyList()
}
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.PROFICIENCY -> character.proficiency
Property.LEVEL -> character.level
else -> null
}?.let { value ->
val titleLabel = if (this == Property.PROFICIENCY) {
context.getString(R.string.dice_roll_mastery_proficiency, name)
} else {
val label = when (this) {
Property.STRENGTH -> context.getString(R.string.character_sheet_stat_strength)
Property.DEXTERITY -> context.getString(R.string.character_sheet_stat_dexterity)
Property.CONSTITUTION -> context.getString(R.string.character_sheet_stat_constitution)
Property.INTELLIGENCE -> context.getString(R.string.character_sheet_stat_intelligence)
Property.WISDOM -> context.getString(R.string.character_sheet_stat_wisdom)
Property.CHARISMA -> context.getString(R.string.character_sheet_stat_charisma)
Property.LEVEL -> context.getString(R.string.character_sheet_stat_level)
else -> ""
}
context.getString(R.string.dice_roll_bonus_detail, label)
}
allValue.add(value)
ThrowsCardUio.Detail(
title = titleLabel,
result = "$value",
)
}
}
/**
* Fetch any alteration related bonus and build a ThrowsCardUio.Detail for each.
*/
fun List<Alteration.Status>?.alterationsBonus(): List<ThrowsCardUio.Detail> {
return this?.flatMap { status ->
status.dices.map { dice ->
val localRoll = roll(
amount = if (alterations.isCritical) dice.count * 2 else dice.count,
faces = dice.faces,
advantage = dice.advantage,
disadvantage = dice.disadvantage,
fail = dice.fail
)
ThrowsCardUio.Detail(
title = dice.title,
throws = ThrowsCardUio.Throw(
dice = dice.faces.icon,
advantage = dice.advantage,
disadvantage = dice.disadvantage,
roll = "${dice.count}d${dice.faces}",
result = localRoll.label,
),
result = "${localRoll.value}",
)
}
} ?: emptyList()
}
/**
* Fetch any flat number related bonus and build a ThrowsCardUio.Detail for each.
*/
fun List<Alteration.Status>?.flatAlterationBonus(): List<ThrowsCardUio.Detail> {
return this?.flatMap { status ->
status.bonus.map { bonus ->
allValue.add(bonus.value)
ThrowsCardUio.Detail(
title = bonus.title,
result = "${bonus.value}",
)
}
} ?: emptyList()
}
/**
* Make a roll of dices.
*/
fun roll(
amount: Int = 1,
faces: Int = 20,
advantage: Boolean = false,
disadvantage: Boolean = false,
fail: Boolean = false,
): DiceRollResult {
return when {
val result = when {
advantage && !disadvantage -> {
val roll = List(amount) { random(faces, fail) to random(faces, fail) }
DiceRollResult(
@ -1105,10 +1009,13 @@ class DiceThrowUseCase @Inject constructor(
)
}
}
allValue.add(result.value)
return result
}
private fun random(faces: Int, fail: Boolean): Int =
if (fail) 1 else (Math.random() * faces + 1).toInt()
}
private data class DiceRollResult(
val label: String,

View file

@ -2,6 +2,7 @@ package com.pixelized.rplexicon.data.model
enum class Property(val key: String) {
PROFICIENCY("Maîtrise"),
LEVEL("Niveau"),
HIT_POINT("Point de vie"),
ARMOR_CLASS("Classe d'armure"),
INITIATIVE("Initiative"),

View file

@ -10,6 +10,7 @@ class ModifierParser @Inject constructor(
companion object {
private val MODIFIER_REGEX = Regex(
pattern = Property.PROFICIENCY.key +
"|${Property.LEVEL.key}" +
"|${Property.STRENGTH.key}" +
"|${Property.DEXTERITY.key}" +
"|${Property.CONSTITUTION.key}" +

View file

@ -64,6 +64,7 @@ class SkillsViewModel @Inject constructor(
val description = descriptionRepository.find(name = skill.name)
val modifier = skill.effect?.modifier?.sumOf {
when (it) {
Property.LEVEL -> character?.level ?: 0
Property.PROFICIENCY -> character?.proficiency ?: 0
Property.STRENGTH -> character?.strength?.modifier ?: 0
Property.DEXTERITY -> character?.dexterity?.modifier ?: 0

View file

@ -96,6 +96,7 @@
<string name="character_sheet_title_objects">Objets</string>
<string name="character_sheet_title_inventory">Inventaire</string>
<string name="character_sheet_title_equipment">Equipement</string>
<string name="character_sheet_stat_level">Niveau</string>
<string name="character_sheet_stat_strength">Force</string>
<string name="character_sheet_stat_strength_short">FOR</string>
<string name="character_sheet_stat_dexterity">Dextérité</string>

View file

@ -104,6 +104,7 @@
<string name="character_sheet_title_skills">Skills</string>
<string name="character_sheet_title_inventory">Inventory</string>
<string name="character_sheet_title_equipment">Equipment</string>
<string name="character_sheet_stat_level">Level</string>
<string name="character_sheet_stat_strength">Strength</string>
<string name="character_sheet_stat_strength_short">STR</string>
<string name="character_sheet_stat_dexterity">Dexterity</string>