From cbb0460bbfcbc10a8484319f5a6da71caeeffd9a Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Tue, 28 Nov 2023 16:30:11 +0100 Subject: [PATCH] Add level modifier to throws and refactor usecase. --- .../rplexicon/business/DiceThrowUseCase.kt | 937 ++++++++---------- .../rplexicon/data/model/Property.kt | 1 + .../data/parser/roll/ModifierParser.kt | 1 + .../pages/actions/SkillsViewModel.kt | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 6 files changed, 427 insertions(+), 515 deletions(-) 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 436b421..694a396 100644 --- a/app/src/main/java/com/pixelized/rplexicon/business/DiceThrowUseCase.kt +++ b/app/src/main/java/com/pixelized/rplexicon/business/DiceThrowUseCase.kt @@ -525,163 +525,131 @@ class DiceThrowUseCase @Inject constructor( ability: Property, relatedStat: Property?, ): DiceThrowResult { - // retrieve some wording. - val abilityLabelString = abilityLabel(application) - val abilityTitleString = abilityTitle(application, abilityLabelString) + 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() + // check if the roll is affected by some status. + val advantage = alterations[ability].advantage + val disadvantage = alterations[ability].disadvantage + val fail = alterations[ability].fail - // check if the roll is affected by some status. - val advantage = alterations[ability].advantage - val disadvantage = alterations[ability].disadvantage - val fail = alterations[ability].fail + // main roll + val result = roll(advantage = advantage, disadvantage = disadvantage, fail = fail) - // 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].alterationsBonus() - // 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() + // fetch and build a list of flat bonus + val flatAlterationBonus = alterations[ability].flatAlterationBonus() - // 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() + // 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.PROFICIENCY -> character.proficiency + else -> null + }?.let { value -> + val relatedLabelString = relatedLabel(application) + val relatedTitleString = relatedLabelString?.let { relatedTitle(application, it) } + if (relatedTitleString != null) { + allValue.add(value) + listOf( + ThrowsCardUio.Detail( + title = relatedTitleString, + result = "$value", + ) + ) + } else { + null + } + } ?: emptyList() - // 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.PROFICIENCY -> character.proficiency - else -> null - }?.let { value -> - val relatedLabelString = relatedLabel(application) - val relatedTitleString = relatedLabelString?.let { relatedTitle(application, it) } - if (relatedTitleString != null) { - allValue.add(value) + // fetch and build the mastery bonus + val mastery = when (ability) { + Property.STRENGTH_SAVING_THROW -> character.strengthSavingThrows + Property.DEXTERITY_SAVING_THROW -> character.dexteritySavingThrows + Property.CONSTITUTION_SAVING_THROW -> character.constitutionSavingThrows + Property.INTELLIGENCE_SAVING_THROW -> character.intelligenceSavingThrows + Property.WISDOM_SAVING_THROW -> character.wisdomSavingThrows + Property.CHARISMA_SAVING_THROW -> character.charismaSavingThrows + Property.ACROBATICS -> character.acrobatics + Property.ANIMAL_HANDLING -> character.animalHandling + Property.ARCANA -> character.arcana + Property.ATHLETICS -> character.athletics + Property.DECEPTION -> character.deception + Property.HISTORY -> character.history + Property.INSIGHT -> character.insight + Property.INTIMIDATION -> character.intimidation + Property.INVESTIGATION -> character.investigation + Property.MEDICINE -> character.medicine + Property.NATURE -> character.nature + Property.PERCEPTION -> character.perception + Property.PERFORMANCE -> character.performance + Property.PERSUASION -> character.persuasion + Property.RELIGION -> character.religion + Property.SLEIGHT_OF_HAND -> character.sleightOfHand + Property.STEALTH -> character.stealth + Property.SURVIVAL -> character.survival + else -> null + }?.let { multiplier -> + val mastery = character.proficiency * multiplier + allValue.add(mastery) listOf( ThrowsCardUio.Detail( - title = relatedTitleString, - result = "$value", - ) - ) - } else { - null - } - } ?: emptyList() - - // fetch and build the mastery bonus - val mastery = when (ability) { - Property.STRENGTH_SAVING_THROW -> character.strengthSavingThrows - Property.DEXTERITY_SAVING_THROW -> character.dexteritySavingThrows - Property.CONSTITUTION_SAVING_THROW -> character.constitutionSavingThrows - Property.INTELLIGENCE_SAVING_THROW -> character.intelligenceSavingThrows - Property.WISDOM_SAVING_THROW -> character.wisdomSavingThrows - Property.CHARISMA_SAVING_THROW -> character.charismaSavingThrows - Property.ACROBATICS -> character.acrobatics - Property.ANIMAL_HANDLING -> character.animalHandling - Property.ARCANA -> character.arcana - Property.ATHLETICS -> character.athletics - Property.DECEPTION -> character.deception - Property.HISTORY -> character.history - Property.INSIGHT -> character.insight - Property.INTIMIDATION -> character.intimidation - Property.INVESTIGATION -> character.investigation - Property.MEDICINE -> character.medicine - Property.NATURE -> character.nature - Property.PERCEPTION -> character.perception - Property.PERFORMANCE -> character.performance - Property.PERSUASION -> character.persuasion - Property.RELIGION -> character.religion - Property.SLEIGHT_OF_HAND -> character.sleightOfHand - Property.STEALTH -> character.stealth - Property.SURVIVAL -> character.survival - else -> null - }?.let { multiplier -> - val mastery = character.proficiency * multiplier - allValue.add(mastery) - listOf( - ThrowsCardUio.Detail( - title = application.getString( - when (multiplier) { - 2 -> R.string.dice_roll_expertise_bonus - else -> R.string.dice_roll_proficiency_bonus - } - ), - result = "$mastery", - ), - ) - } ?: emptyList() - - // build the result. - return DiceThrowResult( - dice = RollDiceUio( - isCriticalSuccess = result.value == 20, - isCriticalFailure = result.value == 1, - result = "${result.value}", - ), - card = ThrowsCardUio( - title = abilityTitleString.uppercase(), - highlight = abilityLabelString.uppercase(), - dice = R.drawable.ic_d20_24, - roll = allValue.toLabel(), - result = "${ - when (result.value) { - 20 -> 20 - 1 -> 1 - else -> allValue.sum() - } - }", - isCriticalSuccess = result.value == 20, - isCriticalFailure = result.value == 1, - details = listOf( - ThrowsCardUio.Detail( - title = abilityTitleString, - throws = ThrowsCardUio.Throw( - dice = R.drawable.ic_d20_24, - advantage = advantage, - disadvantage = disadvantage, - roll = "1d20", - result = result.label, + title = application.getString( + when (multiplier) { + 2 -> R.string.dice_roll_expertise_bonus + else -> R.string.dice_roll_proficiency_bonus + } ), - result = "${result.value}", + result = "$mastery", ), - ) + diceAlterationBonus + flatAlterationBonus + relatedStatBonus + mastery, + ) + } ?: emptyList() + + // build the result. + return DiceThrowResult( + dice = RollDiceUio( + isCriticalSuccess = result.value == 20, + isCriticalFailure = result.value == 1, + result = "${result.value}", + ), + card = ThrowsCardUio( + title = abilityTitleString.uppercase(), + highlight = abilityLabelString.uppercase(), + dice = R.drawable.ic_d20_24, + roll = allValue.toLabel(), + result = "${ + when (result.value) { + 20 -> 20 + 1 -> 1 + else -> allValue.sum() + } + }", + isCriticalSuccess = result.value == 20, + isCriticalFailure = result.value == 1, + details = listOf( + ThrowsCardUio.Detail( + title = abilityTitleString, + throws = ThrowsCardUio.Throw( + dice = R.drawable.ic_d20_24, + advantage = advantage, + disadvantage = disadvantage, + roll = "1d20", + result = result.label, + ), + result = "${result.value}", + ), + ) + diceAlterationBonus + flatAlterationBonus + relatedStatBonus + mastery, + ) ) - ) + } } private fun actionThrow( @@ -692,166 +660,102 @@ class DiceThrowUseCase @Inject constructor( alterations: Map>, ability: Property?, ): DiceThrowResult { - // retrieve some wording. - val titleString = title(application, action) + 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.SPELL_ATTACK -> true - - else -> false - } - - // create a list destined to contain all the values (rolled + bonus) - val allValue = mutableListOf() - - // check if the roll is affected by some status. - val advantage = alterations[ability].advantage - val disadvantage = alterations[ability].disadvantage - val fail = alterations[ability].fail - - // compute the amount of main dice to throw. - val amount = if (alterations.isCritical) { - diceThrow?.amount?.times(2)?.let { - if (ability == Property.PHYSICAL_MELEE_DAMAGE && alterations.isSavageAttacks) - it.plus(1) else it - }?.let { - if (ability == Property.PHYSICAL_MELEE_DAMAGE && alterations.isBrutalCritical) - it.plus(1) else it + // check if this throw can be a critical success of failure. + val canMakeCriticalRoll = when (ability) { + Property.PHYSICAL_MELEE_ATTACK -> true + Property.PHYSICAL_RANGE_ATTACK -> true + Property.SPELL_ATTACK -> true + else -> false } - } else { - diceThrow?.amount - } ?: 1 - // main roll - val result = roll( - amount = amount, - faces = diceThrow?.faces ?: 20, - 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 = 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() + // check if the roll is affected by some status. + val advantage = alterations[ability].advantage + val disadvantage = alterations[ability].disadvantage + val fail = alterations[ability].fail - // 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() - - // 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) + // compute the amount of main dice to throw. + val amount = if (alterations.isCritical) { + diceThrow?.amount?.times(2)?.let { + if (ability == Property.PHYSICAL_MELEE_DAMAGE && alterations.isSavageAttacks) + it.plus(1) else it + }?.let { + if (ability == Property.PHYSICAL_MELEE_DAMAGE && alterations.isBrutalCritical) + it.plus(1) else it } - allValue.add(value) - ThrowsCardUio.Detail( - title = titleLabel, - result = "$value", - ) - } - } ?: emptyList() - - // check for flat dice bonus (ex: healing potion 2d4 + 2) - val flatBonus = diceThrow?.flat?.takeIf { it > 0 }?.let { - allValue.add(it) - listOf( - ThrowsCardUio.Detail( - title = application.getString(R.string.dice_roll_bonus_detail, action), - result = "$it", - ) + } else { + diceThrow?.amount + } ?: 1 + // main roll + val result = roll( + amount = amount, + faces = diceThrow?.faces ?: 20, + advantage = advantage, + disadvantage = disadvantage, + fail = fail, ) - }?: emptyList() - // build the result. - return DiceThrowResult( - dice = RollDiceUio( - icon = diceThrow?.faces?.icon ?: R.drawable.ic_d20_24, - isCriticalSuccess = canMakeCriticalRoll && result.value == 20, - isCriticalFailure = canMakeCriticalRoll && result.value == 1, - result = "${result.value}", - ), - card = ThrowsCardUio( - title = titleString.uppercase(), - highlight = action, - dice = diceThrow?.faces?.icon ?: R.drawable.ic_d20_24, - isCriticalSuccess = canMakeCriticalRoll && result.value == 20, - isCriticalFailure = canMakeCriticalRoll && result.value == 1, - roll = allValue.toLabel(), - result = "${ - when { - canMakeCriticalRoll && result.value == 20 -> 20 - canMakeCriticalRoll && result.value == 1 -> 1 - else -> allValue.sum() - } - }", - details = listOf( + // fetch and build a list of dice roll base on alterations. + val diceAlterationBonus = alterations[ability].alterationsBonus() + + // fetch and build a list of flat bonus + val flatAlterationBonus = alterations[ability].flatAlterationBonus() + + // fetch and build the associated characteristic + val relatedStatBonus = diceThrow.statBonus(name = action) + + // check for flat dice bonus (ex: healing potion 2d4 + 2) + val flatBonus = diceThrow?.flat?.takeIf { it > 0 }?.let { + allValue.add(it) + listOf( ThrowsCardUio.Detail( - title = action, - throws = ThrowsCardUio.Throw( - dice = diceThrow?.faces?.icon ?: R.drawable.ic_d20_24, - advantage = advantage, - disadvantage = disadvantage, - roll = "${diceThrow?.amount}d${diceThrow?.faces}", - result = result.label, + title = application.getString(R.string.dice_roll_bonus_detail, action), + result = "$it", + ) + ) + } ?: emptyList() + + // build the result. + return DiceThrowResult( + dice = RollDiceUio( + icon = diceThrow?.faces?.icon ?: R.drawable.ic_d20_24, + isCriticalSuccess = canMakeCriticalRoll && result.value == 20, + isCriticalFailure = canMakeCriticalRoll && result.value == 1, + result = "${result.value}", + ), + card = ThrowsCardUio( + title = titleString.uppercase(), + highlight = action, + dice = diceThrow?.faces?.icon ?: R.drawable.ic_d20_24, + isCriticalSuccess = canMakeCriticalRoll && result.value == 20, + isCriticalFailure = canMakeCriticalRoll && result.value == 1, + roll = allValue.toLabel(), + result = "${ + when { + canMakeCriticalRoll && result.value == 20 -> 20 + canMakeCriticalRoll && result.value == 1 -> 1 + else -> allValue.sum() + } + }", + details = listOf( + ThrowsCardUio.Detail( + title = action, + throws = ThrowsCardUio.Throw( + dice = diceThrow?.faces?.icon ?: R.drawable.ic_d20_24, + advantage = advantage, + disadvantage = disadvantage, + roll = "${diceThrow?.amount}d${diceThrow?.faces}", + result = result.label, + ), + result = "${result.value}", ), - result = "${result.value}", - ), - ) + diceAlterationBonus + flatAlterationBonus + relatedStatBonus + flatBonus, + ) + diceAlterationBonus + flatAlterationBonus + relatedStatBonus + flatBonus, + ) ) - ) + } } private fun spellThrow( @@ -860,101 +764,67 @@ class DiceThrowUseCase @Inject constructor( level: Int, alterations: Map>, ): DiceThrowResult { - // retrieve some wording. - val spellName = spell?.spell?.name ?: "" - val titleString = application.getString(R.string.dice_roll_spell_cast, spellName) + 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() - - // 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) { - ((spell.spell.level + 1)..level).map { - val localRoll = roll( - 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( - dice = spell.level.faces.icon, - roll = "${spell.level.amount}d${spell.level.faces}", - result = localRoll.label, - ), - result = "${localRoll.value}", - ) - } - } else { - emptyList() - } - - // 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() - - // build the result. - return DiceThrowResult( - dice = RollDiceUio( - icon = (spell?.effect?.faces ?: 4).icon, - result = "${result.value}", - ), - card = ThrowsCardUio( - title = titleString.uppercase(), - highlight = spellName, - dice = (spell?.effect?.faces ?: 4).icon, - roll = allValue.toLabel(), - result = "${allValue.sum()}", - details = listOf( - ThrowsCardUio.Detail( - title = spellName, - throws = ThrowsCardUio.Throw( - dice = (spell?.effect?.faces ?: 4).icon, - roll = "${spell?.effect?.amount ?: 1}d${spell?.effect?.faces ?: 4}", - result = result.label, - ), - result = "${result.value}", - ), - ) + levelBonus + relatedStatBonus, + // main roll + val result = roll( + amount = spell?.effect?.amount ?: 1, + faces = spell?.effect?.faces ?: 4, ) - ) + + // fetch and build a list of additionnal level effect. + val levelBonus = if (spell?.level != null) { + ((spell.spell.level + 1)..level).map { + val localRoll = roll( + amount = spell.level.amount, + faces = spell.level.faces, + ) + ThrowsCardUio.Detail( + title = application.getString(R.string.spell_level_chooser_label, "$it"), + throws = ThrowsCardUio.Throw( + dice = spell.level.faces.icon, + roll = "${spell.level.amount}d${spell.level.faces}", + result = localRoll.label, + ), + result = "${localRoll.value}", + ) + } + } else { + emptyList() + } + + // fetch and build the associated characteristic + val relatedStatBonus = spell?.effect.statBonus(name = spellName) + + // build the result. + return DiceThrowResult( + dice = RollDiceUio( + icon = (spell?.effect?.faces ?: 4).icon, + result = "${result.value}", + ), + card = ThrowsCardUio( + title = titleString.uppercase(), + highlight = spellName, + dice = (spell?.effect?.faces ?: 4).icon, + roll = allValue.toLabel(), + result = "${allValue.sum()}", + details = listOf( + ThrowsCardUio.Detail( + title = spellName, + throws = ThrowsCardUio.Throw( + dice = (spell?.effect?.faces ?: 4).icon, + roll = "${spell?.effect?.amount ?: 1}d${spell?.effect?.faces ?: 4}", + result = result.label, + ), + result = "${result.value}", + ), + ) + levelBonus + relatedStatBonus, + ) + ) + } } private fun skillThrow( @@ -962,81 +832,96 @@ class DiceThrowUseCase @Inject constructor( alterations: Map>, skill: Skill?, ): DiceThrowResult { - // retrieve some wording. - val spellName = skill?.name - val titleString = application.getString(R.string.dice_roll_spell_cast, spellName) + 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() + // main roll + val result = roll( + amount = skill?.effect?.amount ?: 1, + faces = skill?.effect?.faces ?: 4, + ) - // 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].alterationsBonus() - // 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 + // fetch and build a list of flat bonus + val flatAlterationBonus = alterations[Property.SKILL].flatAlterationBonus() + + // fetch and build the associated characteristic, proficiency or level + val relatedStatBonus = skill?.effect.statBonus(name = skill?.name) + + // build the result. + return DiceThrowResult( + dice = RollDiceUio( + icon = (skill?.effect?.faces ?: 4).icon, + result = "${result.value}", + ), + card = ThrowsCardUio( + title = titleString.uppercase(), + highlight = spellName, + dice = (skill?.effect?.faces ?: 4).icon, + roll = allValue.toLabel(), + result = "${allValue.sum()}", + details = listOf( + ThrowsCardUio.Detail( + title = spellName, + throws = ThrowsCardUio.Throw( + dice = (skill?.effect?.faces ?: 4).icon, + roll = "${skill?.effect?.amount ?: 1}d${skill?.effect?.faces ?: 4}", + result = result.label, + ), + result = "${result.value}", + ), + ) + diceAlterationBonus + flatAlterationBonus + relatedStatBonus, ) - 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() + ) + } + } - // 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() + /** + * Helper class to track rolls and declare helper method. + */ + private class ThrowScope( + val context: Context, + val allValue: MutableList = mutableListOf(), + val character: CharacterSheet, + val alterations: Map>, + ) { + /** + * Fetch any stats / proficiency / level related bonus and build a ThrowsCardUio.Detail for each. + */ + fun Throw?.statBonus(name: String?): List { + return this?.modifier?.mapNotNull { it.statBonus(name = name) } ?: emptyList() + } - // 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 + 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 (property == Property.PROFICIENCY) { - application.getString(R.string.dice_roll_mastery_proficiency, skill) + val titleLabel = if (this == Property.PROFICIENCY) { + context.getString(R.string.dice_roll_mastery_proficiency, name) } 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) + 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 -> "" } - application.getString(R.string.dice_roll_bonus_detail, label) + context.getString(R.string.dice_roll_bonus_detail, label) } allValue.add(value) ThrowsCardUio.Detail( @@ -1044,71 +929,93 @@ class DiceThrowUseCase @Inject constructor( result = "$value", ) } - } ?: emptyList() - - // build the result. - return DiceThrowResult( - dice = RollDiceUio( - icon = (skill?.effect?.faces ?: 4).icon, - result = "${result.value}", - ), - card = ThrowsCardUio( - title = titleString.uppercase(), - highlight = spellName, - dice = (skill?.effect?.faces ?: 4).icon, - roll = allValue.toLabel(), - result = "${allValue.sum()}", - details = listOf( - ThrowsCardUio.Detail( - title = spellName, - throws = ThrowsCardUio.Throw( - dice = (skill?.effect?.faces ?: 4).icon, - roll = "${skill?.effect?.amount ?: 1}d${skill?.effect?.faces ?: 4}", - result = result.label, - ), - result = "${result.value}", - ), - ) + diceAlterationBonus + flatAlterationBonus + relatedStatBonus, - ) - ) - } - - private fun roll( - amount: Int = 1, - faces: Int = 20, - advantage: Boolean = false, - disadvantage: Boolean = false, - fail: Boolean = false, - ): DiceRollResult { - return when { - advantage && !disadvantage -> { - val roll = List(amount) { random(faces, fail) to random(faces, fail) } - DiceRollResult( - label = roll.joinToString(" + ") { "${it.first}~${it.second}" }, - value = roll.sumOf { max(it.first, it.second) }, - ) - } - - disadvantage && !advantage -> { - val roll = List(amount) { random(faces, fail) to random(faces, fail) } - DiceRollResult( - label = roll.joinToString(" + ") { "${it.first}~${it.second}" }, - value = roll.sumOf { min(it.first, it.second) }, - ) - } - - else -> { - val roll = List(amount) { random(faces, fail) } - DiceRollResult( - label = roll.toLabel(), - value = roll.sum(), - ) - } } - } - private fun random(faces: Int, fail: Boolean): Int = - if (fail) 1 else (Math.random() * faces + 1).toInt() + /** + * Fetch any alteration related bonus and build a ThrowsCardUio.Detail for each. + */ + fun List?.alterationsBonus(): List { + 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?.flatAlterationBonus(): List { + 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 { + val result = when { + advantage && !disadvantage -> { + val roll = List(amount) { random(faces, fail) to random(faces, fail) } + DiceRollResult( + label = roll.joinToString(" + ") { "${it.first}~${it.second}" }, + value = roll.sumOf { max(it.first, it.second) }, + ) + } + + disadvantage && !advantage -> { + val roll = List(amount) { random(faces, fail) to random(faces, fail) } + DiceRollResult( + label = roll.joinToString(" + ") { "${it.first}~${it.second}" }, + value = roll.sumOf { min(it.first, it.second) }, + ) + } + + else -> { + val roll = List(amount) { random(faces, fail) } + DiceRollResult( + label = roll.toLabel(), + value = roll.sum(), + ) + } + } + 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, diff --git a/app/src/main/java/com/pixelized/rplexicon/data/model/Property.kt b/app/src/main/java/com/pixelized/rplexicon/data/model/Property.kt index 76e9303..b926cc9 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/model/Property.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/model/Property.kt @@ -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"), diff --git a/app/src/main/java/com/pixelized/rplexicon/data/parser/roll/ModifierParser.kt b/app/src/main/java/com/pixelized/rplexicon/data/parser/roll/ModifierParser.kt index d1d3c82..297f279 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/parser/roll/ModifierParser.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/parser/roll/ModifierParser.kt @@ -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}" + diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SkillsViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SkillsViewModel.kt index 35e2459..568f380 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SkillsViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SkillsViewModel.kt @@ -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 diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index e096099..ef0609a 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -96,6 +96,7 @@ Objets Inventaire Equipement + Niveau Force FOR Dextérité diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6fc5d9c..045173d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,6 +104,7 @@ Skills Inventory Equipment + Level Strength STR Dexterity