Add values/strings file

This commit is contained in:
Thomas Andres Gomez 2024-11-07 18:42:57 +01:00
parent 5bc8706972
commit 7af0c15a62
14 changed files with 615 additions and 362 deletions

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="main_page__create_action">Créer une feuille de personnage</string>
<string name="roll_page__critical_success">Réussite critique</string>
<string name="roll_page__special_success">Réussite spéciale</string>
<string name="roll_page__success">Réussite</string>
<string name="roll_page__failure">Échec</string>
<string name="roll_page__critical_failure">Échec critique</string>
<string name="character_sheet_edit__title">Création de personnage</string>
<string name="character_sheet_edit__name_placeholder">Nom</string>
<string name="character_sheet_edit__add_roll_action">Ajouter un lancé</string>
<string name="character_sheet_edit__save_action">Sauvegarder</string>
<string name="character_sheet_edit__characteristics__title">Charactéristiques</string>
<string name="character_sheet_edit__characteristics__str">Force</string>
<string name="character_sheet_edit__characteristics__dex">Dextérité</string>
<string name="character_sheet_edit__characteristics__con">Constitution</string>
<string name="character_sheet_edit__characteristics__hei">Taille</string>
<string name="character_sheet_edit__characteristics__int">Intelligence</string>
<string name="character_sheet_edit__characteristics__pow">Pouvoir</string>
<string name="character_sheet_edit__characteristics__cha">Charisme</string>
<string name="character_sheet_edit__sub_characteristics__title">Charactéristiques dérivées</string>
<string name="character_sheet_edit__sub_characteristics__movement">Déplacement</string>
<string name="character_sheet_edit__sub_characteristics__hit_point">Points de vie</string>
<string name="character_sheet_edit__sub_characteristics__power_point">Points de pouvoir</string>
<string name="character_sheet_edit__sub_characteristics__damage_bonus">Bonus aux dégats</string>
<string name="character_sheet_edit__sub_characteristics__armor">Armure</string>
<string name="character_sheet_edit__skills__title">Compétances</string>
<string name="character_sheet_edit__skills__add_action">Ajouter une compétance</string>
<string name="character_sheet_edit__skills__combat">Bagarre</string>
<string name="character_sheet_edit__skills__dodge">Esquive</string>
<string name="character_sheet_edit__skills__grab">Saisie</string>
<string name="character_sheet_edit__skills__throw">Lancer</string>
<string name="character_sheet_edit__skills__athletics">Athlétisme</string>
<string name="character_sheet_edit__skills__acrobatics">Acrobatie</string>
<string name="character_sheet_edit__skills__perception">Perception</string>
<string name="character_sheet_edit__skills__search">Recherche</string>
<string name="character_sheet_edit__skills__empathy">Empathie</string>
<string name="character_sheet_edit__skills__persuasion">Persuasion</string>
<string name="character_sheet_edit__skills__intimidation">Intimidation</string>
<string name="character_sheet_edit__skills__spiel">Baratin</string>
<string name="character_sheet_edit__skills__bargain">Marchandage</string>
<string name="character_sheet_edit__skills__discretion">Discrétion</string>
<string name="character_sheet_edit__skills__sleight_of_hand">Escamotage</string>
<string name="character_sheet_edit__skills__aid">Premiers soins</string>
<string name="character_sheet_edit__occupation__title">Occupations</string>
<string name="character_sheet_edit__occupation__add_action">Ajouter une occupation</string>
<string name="character_sheet_edit__magic__title">Compétences magiques</string>
<string name="character_sheet_edit__magic__add_action">Ajouter une compétence magique</string>
<string name="character_sheet__characteristics__str">Force</string>
<string name="character_sheet__characteristics__dex">Dextérité</string>
<string name="character_sheet__characteristics__con">Constitution</string>
<string name="character_sheet__characteristics__hei">Taille</string>
<string name="character_sheet__characteristics__int">Intelligence</string>
<string name="character_sheet__characteristics__pow">Pouvoir</string>
<string name="character_sheet__characteristics__cha">Charisme</string>
<string name="character_sheet__sub_characteristics__title">Charactéristiques dérivées</string>
<string name="character_sheet__sub_characteristics__movement">Déplacement</string>
<string name="character_sheet__sub_characteristics__hit_point">Points de vie</string>
<string name="character_sheet__sub_characteristics__power_point">Points de pouvoir</string>
<string name="character_sheet__sub_characteristics__damage_bonus">Bonus aux dégats</string>
<string name="character_sheet__sub_characteristics__armor">Armure</string>
<string name="character_sheet__skills__title">Compétences</string>
<string name="character_sheet__occupations_title">Occupations</string>
<string name="character_sheet__magics__title">Compétences magiques</string>
</resources>

View file

@ -0,0 +1,19 @@
package com.pixelized.desktop.lwa.business
object DamageBonusUseCase {
fun bonusDamage(strength: Int, height: Int): String {
return bonusDamage(stat = strength + height)
}
fun bonusDamage(stat: Int): String {
return when {
stat < 12 -> "-1d6"
stat in 12..17 -> "-1d4"
stat in 18..22 -> "+0"
stat in 23..29 -> "+1d4"
stat in 30..39 -> "+1d6"
else -> "+2d6"
}
}
}

View file

@ -3,27 +3,33 @@ package com.pixelized.desktop.lwa.business
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet
object RollUseCase {
private val d100 = (1..100)
private val diceParser = Regex(
"""(?<sign>[+-])?\h*(?<modifier>[ade])?(?<quantity>\d+)d(?<face>\d+)"""
"""(?<sign>[+-])?\s*(?<modifier>[ade])?(?<quantity>\d+)[dD](?<face>\d+)"""
)
private val flatParser = Regex(
"""(?<sign>[+-])?\h(?<value>\d+)\b"""
"""(?<sign>[+-])?\s*[^a-zA-Z](?<value>\d+)\b"""
)
private val paramParser = Regex(
"""(?<sign>[+-])?\h(?<param>BDGT)\b"""
"""(?<sign>[+-])?\s*(?<param>BDGT)\b"""
)
fun rollD100(): Int {
return d100.random()
return roll(quantity = 1, faces = 100)
}
fun roll(quantity: Int, faces: Int): Int {
return sum(count = quantity) { (Math.random() * faces.toDouble() + 1).toInt() }
}
fun roll(characterSheet: CharacterSheet, roll: String): Int {
println(roll)
return diceParser.findAll(roll).sumOf {
val (sign, modifier, quantity, faces) = it.destructured
((if (sign == "-") -1 else 1) * quantity.toInt() * (Math.random() * faces.toDouble() + 1).toInt()).also {
((if (sign == "-") -1 else 1) * roll(
quantity = quantity.toInt(),
faces = faces.toInt()
)).also {
println("roll ${sign}${quantity}d${faces} -> $it")
}
} + flatParser.findAll(roll).sumOf {
@ -36,7 +42,10 @@ object RollUseCase {
(if (sign == "-") -1 else 1) * when (param) {
"BDGT" -> diceParser.findAll(characterSheet.damageBonus).sumOf {
val (sign, modifier, quantity, faces) = it.destructured
((if (sign == "-") -1 else 1) * quantity.toInt() * (Math.random() * faces.toDouble() + 1).toInt()).also {
((if (sign == "-") -1 else 1) * roll(
quantity = quantity.toInt(),
faces = faces.toInt()
)).also {
println("param: ${sign}${param} -> $it")
}
}
@ -45,4 +54,12 @@ object RollUseCase {
}
}
}
private fun sum(count: Int, block: () -> Int): Int {
return if (count > 1) {
block() + sum(count - 1, block)
} else {
block()
}
}
}

View file

@ -3,34 +3,68 @@ package com.pixelized.desktop.lwa.screen.characterSheet.detail
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio.Node
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.*
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__str
import org.jetbrains.compose.resources.getString
class CharacterSheetFactory {
fun convertToUio(model: CharacterSheet): CharacterSheetPageUio {
suspend fun convertToUio(model: CharacterSheet): CharacterSheetPageUio {
return CharacterSheetPageUio(
id = model.id,
name = model.name,
characteristics = listOf(
Characteristic(label = "Force", value = "${model.strength}"),
Characteristic(label = "Dextérité", value = "${model.dexterity}"),
Characteristic(label = "Constitution", value = "${model.constitution}"),
Characteristic(label = "Taille", value = "${model.height}"),
Characteristic(label = "Intelligence", value = "${model.intelligence}"),
Characteristic(label = "Pouvoir", value = "${model.power}"),
Characteristic(label = "Charisme", value = "${model.charisma}"),
Characteristic(
label = getString(Res.string.character_sheet__characteristics__str),
value = "${model.strength}",
),
Characteristic(
label = getString(Res.string.character_sheet__characteristics__dex),
value = "${model.dexterity}",
),
Characteristic(
label = getString(Res.string.character_sheet__characteristics__con),
value = "${model.constitution}",
),
Characteristic(
label = getString(Res.string.character_sheet__characteristics__hei),
value = "${model.height}",
),
Characteristic(
label = getString(Res.string.character_sheet__characteristics__int),
value = "${model.intelligence}",
),
Characteristic(
label = getString(Res.string.character_sheet__characteristics__pow),
value = "${model.power}",
),
Characteristic(
label = getString(Res.string.character_sheet__characteristics__cha),
value = "${model.charisma}",
),
),
subCharacteristics = listOf(
Characteristic(label = "Déplacement ", value = "${model.movement}"),
Characteristic(
label = "Points de vie",
value = "${model.currentHp}/${model.maxHp}"
label = getString(Res.string.character_sheet__sub_characteristics__movement),
value = "${model.movement}",
),
Characteristic(
label = "Points de pouvoir",
value = "${model.currentPP}/${model.maxPP}"
label = getString(Res.string.character_sheet__sub_characteristics__hit_point),
value = "${model.currentHp}/${model.maxHp}",
),
Characteristic(
label = getString(Res.string.character_sheet__sub_characteristics__power_point),
value = "${model.currentPP}/${model.maxPP}",
),
Characteristic(
label = getString(Res.string.character_sheet__sub_characteristics__damage_bonus),
value = model.damageBonus,
),
Characteristic(
label = getString(Res.string.character_sheet__sub_characteristics__armor),
value = "${model.armor}",
),
Characteristic(label = "Bonus aux dégâts", value = model.damageBonus),
Characteristic(label = "Armure", value = "${model.armor}"),
),
skills = model.skills.mapNotNull {
if (it.value > 0) {

View file

@ -49,8 +49,13 @@ import com.pixelized.desktop.lwa.screen.roll.RollPage
import com.pixelized.desktop.lwa.screen.roll.RollViewModel
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet__magics__title
import lwacharactersheet.composeapp.generated.resources.character_sheet__occupations_title
import lwacharactersheet.composeapp.generated.resources.character_sheet__skills__title
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__title
import lwacharactersheet.composeapp.generated.resources.ic_d20_32dp
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
@Stable
data class CharacterSheetPageUio(
@ -122,7 +127,10 @@ fun CharacterSheetPage(
}
},
onCharacteristic = { characteristic ->
rollViewModel.prepareRoll(sheet = sheet, characteristic = characteristic)
rollViewModel.prepareRoll(
sheet = sheet,
characteristic = characteristic
)
overlayViewModel.show()
},
onSkill = { node ->
@ -228,7 +236,7 @@ fun CharacterSheetPageContent(
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
style = MaterialTheme.typography.caption,
textAlign = TextAlign.Center,
text = "Charactéristiques dérivées"
text = stringResource(Res.string.character_sheet__sub_characteristics__title),
)
characterSheet.subCharacteristics.forEach {
Characteristics(
@ -246,7 +254,7 @@ fun CharacterSheetPageContent(
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
style = MaterialTheme.typography.caption,
textAlign = TextAlign.Center,
text = "Compétences",
text = stringResource(Res.string.character_sheet__skills__title),
)
characterSheet.skills.forEach {
Skill(
@ -266,7 +274,7 @@ fun CharacterSheetPageContent(
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
style = MaterialTheme.typography.caption,
textAlign = TextAlign.Center,
text = "Occupations"
text = stringResource(Res.string.character_sheet__occupations_title),
)
characterSheet.occupations.forEach {
Skill(
@ -286,7 +294,7 @@ fun CharacterSheetPageContent(
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
style = MaterialTheme.typography.caption,
textAlign = TextAlign.Center,
text = "Compétences magiques"
text = stringResource(Res.string.character_sheet__magics__title),
)
characterSheet.magics.forEach {
Skill(

View file

@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModel
import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetDestination
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.utils.extention.collectAsState
import kotlinx.coroutines.runBlocking
class CharacterSheetViewModel(
savedStateHandle: SavedStateHandle,
@ -23,7 +24,9 @@ class CharacterSheetViewModel(
get() = repository
.characterSheetFlow(id = argument.id)
.collectAsState { sheet ->
sheet?.let { model -> factory.convertToUio(model = model) }
sheet?.let { model ->
runBlocking { factory.convertToUio(model = model) }
}
}
suspend fun deleteCharacter(id: String) {

View file

@ -0,0 +1,309 @@
package com.pixelized.desktop.lwa.screen.characterSheet.edit
import com.pixelized.desktop.lwa.business.DamageBonusUseCase.bonusDamage
import com.pixelized.desktop.lwa.business.SkillNormalizerUseCase.normalize
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet
import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditPageUio.SkillGroup
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__cha
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__con
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__dex
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__hei
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__int
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__pow
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__str
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__title
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__magic__add_action
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__magic__title
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__name_placeholder
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__occupation__add_action
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__occupation__title
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__acrobatics
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__add_action
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__aid
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__athletics
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__bargain
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__combat
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__discretion
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__dodge
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__empathy
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__grab
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__intimidation
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__perception
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__persuasion
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__search
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__sleight_of_hand
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__spiel
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__throw
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__title
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__armor
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__damage_bonus
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__hit_point
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__movement
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__power_point
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__title
import org.jetbrains.compose.resources.getString
import java.util.UUID
import kotlin.math.ceil
import kotlin.math.max
class CharacterSheetEditFactory {
fun convertToModel(sheet: CharacterSheetEditPageUio): CharacterSheet {
return CharacterSheet(
id = sheet.id,
name = sheet.name.value.value,
strength = sheet.skills[0].fields[0].unpack(),
dexterity = sheet.skills[0].fields[1].unpack(),
constitution = sheet.skills[0].fields[2].unpack(),
height = sheet.skills[0].fields[3].unpack(),
intelligence = sheet.skills[0].fields[4].unpack(),
power = sheet.skills[0].fields[5].unpack(),
charisma = sheet.skills[0].fields[6].unpack(),
movement = sheet.skills[1].fields[0].unpack(),
currentHp = sheet.skills[1].fields[1].unpack(),
maxHp = sheet.skills[1].fields[1].unpack(),
currentPP = sheet.skills[1].fields[2].unpack(),
maxPP = sheet.skills[1].fields[2].unpack(),
damageBonus = sheet.skills[1].fields[3].unpack(),
armor = sheet.skills[1].fields[4].unpack(),
skills = sheet.skills[2].fields.map {
CharacterSheet.Skill(
label = it.label.value,
value = it.unpack(),
used = false,
)
},
occupations = sheet.skills[3].fields.map {
CharacterSheet.Skill(
label = it.label.value,
value = it.unpack(),
used = false,
)
},
magics = sheet.skills[4].fields.map {
CharacterSheet.Skill(
label = it.label.value,
value = it.unpack(),
used = false,
)
},
rolls = sheet.rolls.map {
CharacterSheet.Roll(
label = it.label.value,
roll = it.unpack(),
)
},
)
}
suspend fun convertToUio(
sheet: CharacterSheet?,
): CharacterSheetEditPageUio {
val str = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__str),
initialValue = sheet?.strength?.toString() ?: "",
valuePlaceHolder = { "0" },
)
val dex = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__dex),
initialValue = sheet?.dexterity?.toString() ?: "",
valuePlaceHolder = { "0" }
)
val con = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__con),
initialValue = sheet?.constitution?.toString() ?: "",
valuePlaceHolder = { "0" }
)
val hei = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__hei),
initialValue = sheet?.height?.toString() ?: "",
valuePlaceHolder = { "0" }
)
val int = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__int),
initialValue = sheet?.intelligence?.toString() ?: "",
valuePlaceHolder = { "0" }
)
val pow = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__pow),
initialValue = sheet?.power?.toString() ?: "",
valuePlaceHolder = { "0" }
)
val cha = FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__characteristics__cha),
initialValue = sheet?.charisma?.toString() ?: "",
valuePlaceHolder = { "0" }
)
fun str(): Int = str.unpack() ?: 0
fun dex(): Int = dex.unpack() ?: 0
fun con(): Int = con.unpack() ?: 0
fun hei(): Int = hei.unpack() ?: 0
fun int(): Int = int.unpack() ?: 0
fun pow(): Int = pow.unpack() ?: 0
fun cha(): Int = cha.unpack() ?: 0
return CharacterSheetEditPageUio(
id = sheet?.id ?: UUID.randomUUID().toString(),
name = FieldUio.create(
isLabelDisplayed = false,
initialLabel = getString(Res.string.character_sheet_edit__name_placeholder),
initialValue = sheet?.name ?: ""
),
skills = listOf(
SkillGroup(
type = SkillGroup.Type.CHARACTERISTICS,
title = getString(Res.string.character_sheet_edit__characteristics__title),
fields = listOf(str, dex, con, hei, int, pow, cha),
),
SkillGroup(
type = SkillGroup.Type.SUB_CHARACTERISTICS,
title = getString(Res.string.character_sheet_edit__sub_characteristics__title),
fields = listOf(
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__sub_characteristics__movement),
initialValue = sheet?.movement?.toString() ?: "",
valuePlaceHolder = { "10" }
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__sub_characteristics__hit_point),
initialValue = sheet?.maxHp?.toString() ?: "",
valuePlaceHolder = { "${ceil((con() + hei()) / 2f).toInt()}" }
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__sub_characteristics__power_point),
initialValue = sheet?.maxPP?.toString() ?: "",
valuePlaceHolder = { "${pow()}" }
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__sub_characteristics__damage_bonus),
initialValue = sheet?.damageBonus ?: "",
valuePlaceHolder = { bonusDamage(strength = str(), height = hei()) }
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__sub_characteristics__armor),
initialValue = sheet?.armor?.toString() ?: "",
valuePlaceHolder = { "0" }
),
),
),
SkillGroup(
type = SkillGroup.Type.SKILLS,
title = getString(Res.string.character_sheet_edit__skills__title),
action = getString(Res.string.character_sheet_edit__skills__add_action),
fields = sheet?.skills?.map {
FieldUio.create(
initialLabel = it.label,
initialValue = it.value.toString(),
)
} ?: listOf(
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__combat),
valuePlaceHolder = { "${normalize(dex() * 2)}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__dodge),
valuePlaceHolder = { "${normalize(dex() * 2)}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__grab),
valuePlaceHolder = { "${normalize(str() + hei())}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__throw),
valuePlaceHolder = { "${normalize(str() + dex())}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__athletics),
valuePlaceHolder = { "${normalize(str() + con() * 2)}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__acrobatics),
valuePlaceHolder = { "${normalize(dex() + con() * 2)}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__perception),
valuePlaceHolder = { "${normalize(10 + int() * 2)}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__search),
valuePlaceHolder = { "${normalize(10 + int() * 2)}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__empathy),
valuePlaceHolder = { "${normalize(cha() + int())}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__persuasion),
valuePlaceHolder = { "${normalize(cha() * 3)}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__intimidation),
valuePlaceHolder = { "${normalize(cha() + max(pow(), hei()) * 2)}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__spiel),
valuePlaceHolder = { "${normalize(cha() * 2 + int())}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__bargain),
valuePlaceHolder = { "${normalize(cha() * 2)}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__discretion),
valuePlaceHolder = { "${normalize(cha() + dex() * 2 - hei())}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__sleight_of_hand),
valuePlaceHolder = { "${normalize(dex() * 2)}" },
),
FieldUio.create(
initialLabel = getString(Res.string.character_sheet_edit__skills__aid),
valuePlaceHolder = { "${normalize(int() + dex())}" },
),
),
),
SkillGroup(
type = SkillGroup.Type.OCCUPATIONS,
title = getString(Res.string.character_sheet_edit__occupation__title),
action = getString(Res.string.character_sheet_edit__occupation__add_action),
fields = sheet?.occupations?.map {
FieldUio.create(
initialLabel = it.label,
initialValue = it.value.toString(),
valuePlaceHolder = { "40" }
)
} ?: emptyList(),
),
SkillGroup(
type = SkillGroup.Type.MAGICS,
title = getString(Res.string.character_sheet_edit__magic__title),
action = getString(Res.string.character_sheet_edit__magic__add_action),
fields = sheet?.magics?.map {
FieldUio.create(
initialLabel = it.label,
initialValue = it.value.toString()
)
} ?: emptyList(),
),
),
rolls = sheet?.rolls?.map {
FieldUio.create(
isLabelEditable = true,
initialLabel = it.label,
initialValue = it.roll,
)
} ?: emptyList()
)
}
private inline fun <reified T> FieldUio.unpack(): T {
val tmp = value.value.ifBlank { valuePlaceHolder.value }
return when (T::class) {
Int::class -> (tmp.toIntOrNull() ?: 0) as T
else -> tmp as T
}
}
}

View file

@ -34,6 +34,11 @@ import com.pixelized.desktop.lwa.navigation.LocalScreen
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.Form
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__add_roll_action
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__save_action
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__title
import org.jetbrains.compose.resources.stringResource
@Stable
data class CharacterSheetEditPageUio(
@ -46,7 +51,7 @@ data class CharacterSheetEditPageUio(
data class SkillGroup(
val title: String,
val type: Type,
val editable: Boolean = false,
val action: String? = null,
val fields: List<FieldUio>,
) {
@Stable
@ -106,7 +111,7 @@ fun CharacterSheetEdit(
Text(
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = "Création de personnage",
text = stringResource(Res.string.character_sheet_edit__title),
)
},
navigationIcon = {
@ -152,7 +157,7 @@ fun CharacterSheetEdit(
field = it,
)
}
if (it.editable) {
it.action?.let { label ->
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
@ -169,7 +174,7 @@ fun CharacterSheetEdit(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
text = "Ajouter une ligne",
text = label,
)
Icon(
imageVector = Icons.Default.Add,
@ -186,6 +191,7 @@ fun CharacterSheetEdit(
form.rolls.forEach {
Form(
modifier = Modifier.fillMaxWidth(),
valueWidth = 120.dp,
field = it,
)
}
@ -197,7 +203,7 @@ fun CharacterSheetEdit(
TextButton(
onClick = onNewCategory,
) {
Text(text = "Ajouter un lancé")
Text(text = stringResource(Res.string.character_sheet_edit__add_roll_action))
}
}
@ -208,7 +214,7 @@ fun CharacterSheetEdit(
TextButton(
onClick = onSave,
) {
Text(text = "Sauvegarder")
Text(text = stringResource(Res.string.character_sheet_edit__save_action))
}
}
}

View file

@ -8,6 +8,7 @@ import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetEditDestin
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditPageUio.SkillGroup
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio
import kotlinx.coroutines.runBlocking
class CharacterSheetEditViewModel(
savedStateHandle: SavedStateHandle,
@ -15,11 +16,13 @@ class CharacterSheetEditViewModel(
private val argument = CharacterSheetEditDestination.Argument(savedStateHandle)
private val repository = CharacterSheetRepository
private val factory = CharacterSheetFactory()
private val factory = CharacterSheetEditFactory()
private val _characterSheet = repository
.characterSheetFlow(id = argument.id).value
.let { sheet -> mutableStateOf(factory.convertToUio(sheet = sheet)) }
private val _characterSheet = mutableStateOf(
repository.characterSheetFlow(id = argument.id).value.let {
runBlocking { factory.convertToUio(it) }
}
)
val characterSheet: State<CharacterSheetEditPageUio> get() = _characterSheet
fun onSkill(skill: SkillGroup) {
@ -32,8 +35,8 @@ class CharacterSheetEditViewModel(
addAll(group.fields)
add(
FieldUio.create(
label = "",
isLabelEditable = true,
initialLabel = "",
valuePlaceHolder = {
when (group.type) {
SkillGroup.Type.CHARACTERISTICS -> ""
@ -61,7 +64,7 @@ class CharacterSheetEditViewModel(
rolls = sheet.rolls.toMutableList().apply {
add(
FieldUio.create(
label = "",
initialLabel = "",
isLabelEditable = true,
valuePlaceHolder = { "" },
)

View file

@ -1,279 +0,0 @@
package com.pixelized.desktop.lwa.screen.characterSheet.edit
import com.pixelized.desktop.lwa.business.SkillNormalizerUseCase.normalize
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet
import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditPageUio.SkillGroup
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio
import java.util.UUID
import kotlin.math.ceil
import kotlin.math.max
class CharacterSheetFactory {
fun convertToModel(sheet: CharacterSheetEditPageUio): CharacterSheet {
return CharacterSheet(
id = sheet.id,
name = sheet.name.value.value,
strength = sheet.skills[0].fields[0].unpack(),
dexterity = sheet.skills[0].fields[1].unpack(),
constitution = sheet.skills[0].fields[2].unpack(),
height = sheet.skills[0].fields[3].unpack(),
intelligence = sheet.skills[0].fields[4].unpack(),
power = sheet.skills[0].fields[5].unpack(),
charisma = sheet.skills[0].fields[6].unpack(),
movement = sheet.skills[1].fields[0].unpack(),
currentHp = sheet.skills[1].fields[1].unpack(),
maxHp = sheet.skills[1].fields[1].unpack(),
currentPP = sheet.skills[1].fields[2].unpack(),
maxPP = sheet.skills[1].fields[2].unpack(),
damageBonus = sheet.skills[1].fields[3].unpack(),
armor = sheet.skills[1].fields[4].unpack(),
skills = sheet.skills[2].fields.map {
CharacterSheet.Skill(
label = it.label.value,
value = it.unpack(),
used = false,
)
},
occupations = sheet.skills[3].fields.map {
CharacterSheet.Skill(
label = it.label.value,
value = it.unpack(),
used = false,
)
},
magics = sheet.skills[4].fields.map {
CharacterSheet.Skill(
label = it.label.value,
value = it.unpack(),
used = false,
)
},
rolls = sheet.rolls.map {
CharacterSheet.Roll(
label = it.label.value,
roll = it.unpack(),
)
},
)
}
fun convertToUio(
sheet: CharacterSheet?,
): CharacterSheetEditPageUio {
val str = FieldUio.create(
label = "Force",
valuePlaceHolder = { "0" },
initialValue = sheet?.strength?.toString() ?: ""
)
val dex = FieldUio.create(
label = "Dextérité",
valuePlaceHolder = { "0" },
initialValue = sheet?.dexterity?.toString() ?: ""
)
val con = FieldUio.create(
label = "Constitution",
valuePlaceHolder = { "0" },
initialValue = sheet?.constitution?.toString() ?: ""
)
val hei = FieldUio.create(
label = "Taille",
valuePlaceHolder = { "0" },
initialValue = sheet?.height?.toString() ?: ""
)
val int = FieldUio.create(
label = "Intelligence",
valuePlaceHolder = { "0" },
initialValue = sheet?.intelligence?.toString() ?: ""
)
val pow = FieldUio.create(
label = "Pouvoir",
valuePlaceHolder = { "0" },
initialValue = sheet?.power?.toString() ?: ""
)
val cha = FieldUio.create(
label = "Charisme",
valuePlaceHolder = { "0" },
initialValue = sheet?.charisma?.toString() ?: ""
)
fun str(): Int = str.unpack() ?: 0
fun dex(): Int = dex.unpack() ?: 0
fun con(): Int = con.unpack() ?: 0
fun hei(): Int = hei.unpack() ?: 0
fun int(): Int = int.unpack() ?: 0
fun pow(): Int = pow.unpack() ?: 0
fun cha(): Int = cha.unpack() ?: 0
return CharacterSheetEditPageUio(
id = sheet?.id ?: UUID.randomUUID().toString(),
name = FieldUio.create(
useLabelAsPlaceholder = true,
label = "Name",
initialValue = sheet?.name ?: ""
),
skills = listOf(
SkillGroup(
title = "Charactéristiques",
type = SkillGroup.Type.CHARACTERISTICS,
fields = listOf(str, dex, con, hei, int, pow, cha),
),
SkillGroup(
title = "Charactéristiques dérivées",
type = SkillGroup.Type.SUB_CHARACTERISTICS,
fields = listOf(
FieldUio.create(
label = "Déplacement",
valuePlaceHolder = { "10" },
initialValue = sheet?.movement?.toString() ?: ""
),
FieldUio.create(
label = "Points de vie",
valuePlaceHolder = { "${ceil((con() + hei()) / 2f).toInt()}" },
initialValue = sheet?.maxHp?.toString() ?: ""
),
FieldUio.create(
label = "Points de pouvoir",
valuePlaceHolder = { "${pow()}" },
initialValue = sheet?.maxPP?.toString() ?: ""
),
FieldUio.create(
label = "Bonus aux dégats",
valuePlaceHolder = {
val bonus = str() + hei()
when {
bonus < 12 -> "-1d6"
bonus in 12..17 -> "-1d4"
bonus in 18..22 -> "-0"
bonus in 23..29 -> "1d4"
bonus in 30..39 -> "1d6"
else -> "2d6"
}
},
initialValue = sheet?.damageBonus ?: ""
),
FieldUio.create(
label = "Armure",
valuePlaceHolder = { "0" },
initialValue = sheet?.armor?.toString() ?: ""
),
),
),
SkillGroup(
title = "Compétances",
type = SkillGroup.Type.SKILLS,
editable = true,
fields = sheet?.skills?.map {
FieldUio.create(
label = it.label,
initialValue = it.value.toString(),
)
} ?: listOf(
FieldUio.create(
label = "Bagarre",
valuePlaceHolder = { "${normalize(dex() * 2)}" },
),
FieldUio.create(
label = "Esquive",
valuePlaceHolder = { "${normalize(dex() * 2)}" },
),
FieldUio.create(
label = "Saisie",
valuePlaceHolder = { "${normalize(str() + hei())}" },
),
FieldUio.create(
label = "Lancer",
valuePlaceHolder = { "${normalize(str() + dex())}" },
),
FieldUio.create(
label = "Athlétisme",
valuePlaceHolder = { "${normalize(str() + con() * 2)}" },
),
FieldUio.create(
label = "Acrobatie",
valuePlaceHolder = { "${normalize(dex() + con() * 2)}" },
),
FieldUio.create(
label = "Perception",
valuePlaceHolder = { "${normalize(10 + int() * 2)}" },
),
FieldUio.create(
label = "Recherche",
valuePlaceHolder = { "${normalize(10 + int() * 2)}" },
),
FieldUio.create(
label = "Empathie",
valuePlaceHolder = { "${normalize(cha() + int())}" },
),
FieldUio.create(
label = "Persuasion",
valuePlaceHolder = { "${normalize(cha() * 3)}" },
),
FieldUio.create(
label = "Intimidation",
valuePlaceHolder = { "${normalize(cha() + max(pow(), hei()) * 2)}" },
),
FieldUio.create(
label = "Baratin",
valuePlaceHolder = { "${normalize(cha() * 2 + int())}" },
),
FieldUio.create(
label = "Marchandage",
valuePlaceHolder = { "${normalize(cha() * 2)}" },
),
FieldUio.create(
label = "Discrétion",
valuePlaceHolder = { "${normalize(cha() + dex() * 2 - hei())}" },
),
FieldUio.create(
label = "Escamotage",
valuePlaceHolder = { "${normalize(dex() * 2)}" },
),
FieldUio.create(
label = "Premiers soins",
valuePlaceHolder = { "${normalize(int() + dex())}" },
),
),
),
SkillGroup(
title = "Occupations",
type = SkillGroup.Type.OCCUPATIONS,
editable = true,
fields = sheet?.occupations?.map {
FieldUio.create(
label = it.label,
valuePlaceHolder = { "40" },
initialValue = it.value.toString()
)
} ?: emptyList(),
),
SkillGroup(
title = "Compétences magiques",
type = SkillGroup.Type.MAGICS,
editable = true,
fields = sheet?.magics?.map {
FieldUio.create(
label = it.label,
initialValue = it.value.toString()
)
} ?: emptyList(),
),
),
rolls = sheet?.rolls?.map {
FieldUio.create(
label = it.label,
isLabelEditable = true,
initialValue = it.roll,
)
} ?: emptyList()
)
}
private inline fun <reified T> FieldUio.unpack(): T {
val tmp = value.value.ifBlank { valuePlaceHolder.value }
return when (T::class) {
Int::class -> (tmp.toIntOrNull() ?: 0) as T
else -> tmp as T
}
}
}

View file

@ -22,37 +22,41 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Stable
open class FieldUio(
val useLabelAsPlaceholder: Boolean,
val isLabelDisplayed: Boolean,
val isLabelEditable: Boolean,
val label: State<String>,
val labelPlaceHolder: State<String>,
val onLabelChange: (String) -> Unit,
val valuePlaceHolder: State<String>,
val value: State<String>,
val valuePlaceHolder: State<String>,
val onValueChange: (String) -> Unit,
) {
companion object {
@Stable
fun create(
useLabelAsPlaceholder: Boolean = false,
isLabelDisplayed: Boolean = true,
isLabelEditable: Boolean = false,
label: String = "",
initialLabel: String = "",
labelPlaceHolder: () -> String = { "" },
initialValue: String = "",
valuePlaceHolder: () -> String = { "" },
): FieldUio {
val labelState = mutableStateOf(label)
val valueState = mutableStateOf(initialValue)
val label = mutableStateOf(initialLabel)
val value = mutableStateOf(initialValue)
return FieldUio(
useLabelAsPlaceholder = useLabelAsPlaceholder,
isLabelEditable = useLabelAsPlaceholder.not() && isLabelEditable,
label = labelState,
onLabelChange = { labelState.value = it },
isLabelDisplayed = isLabelDisplayed,
isLabelEditable = isLabelEditable,
label = label,
labelPlaceHolder = derivedStateOf(labelPlaceHolder),
onLabelChange = { label.value = it },
value = value,
valuePlaceHolder = derivedStateOf(valuePlaceHolder),
value = valueState,
onValueChange = { valueState.value = it },
onValueChange = { value.value = it },
)
}
}
@ -61,27 +65,17 @@ open class FieldUio(
@Composable
fun Form(
modifier: Modifier = Modifier,
valueWidth: Dp = 80.dp,
field: FieldUio,
) {
val focus = LocalFocusManager.current
AnimatedContent(
targetState = field.useLabelAsPlaceholder,
targetState = field.isLabelDisplayed,
transitionSpec = { fadeIn() togetherWith fadeOut() }
) {
when (it) {
true -> {
TextField(
modifier = modifier,
value = field.value.value,
label = { Text(text = field.label.value) },
singleLine = true,
keyboardActions = KeyboardActions { focus.moveFocus(FocusDirection.Next) },
onValueChange = field.onValueChange,
)
}
else -> {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
@ -94,32 +88,43 @@ fun Form(
) { editable ->
when (editable) {
true -> TextField(
value = field.label.value,
placeholder = { Text(text = "Nom") },
singleLine = true,
keyboardActions = KeyboardActions { focus.moveFocus(FocusDirection.Next) },
singleLine = true,
placeholder = { Text(text = field.labelPlaceHolder.value) },
onValueChange = field.onLabelChange,
value = field.label.value,
)
else -> Text(
modifier = Modifier.padding(horizontal = 16.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.body1,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = field.label.value
)
}
}
TextField(
modifier = Modifier.width(width = 80.dp),
value = field.value.value,
placeholder = { Text(text = field.valuePlaceHolder.value) },
singleLine = true,
modifier = Modifier.width(width = valueWidth),
keyboardActions = KeyboardActions { focus.moveFocus(FocusDirection.Next) },
singleLine = true,
placeholder = { Text(text = field.valuePlaceHolder.value) },
onValueChange = field.onValueChange,
value = field.value.value,
)
}
}
else -> {
TextField(
modifier = modifier,
keyboardActions = KeyboardActions { focus.moveFocus(FocusDirection.Next) },
singleLine = true,
label = { Text(text = field.label.value) },
onValueChange = field.onValueChange,
value = field.value.value,
)
}
}
}
}

View file

@ -12,7 +12,6 @@ import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
@ -22,6 +21,9 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.pixelized.desktop.lwa.navigation.LocalScreen
import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheet
import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheetEdit
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.main_page__create_action
import org.jetbrains.compose.resources.stringResource
@Stable
data class CharacterUio(
@ -67,13 +69,13 @@ fun MainPageContent(
Column {
characters.value.forEach { sheet ->
TextButton(
onClick = { onCharacter(sheet) }
onClick = { onCharacter(sheet) },
) {
Text(
modifier = Modifier.fillMaxWidth(),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Start,
maxLines = 1,
text = sheet.name,
)
}
@ -88,7 +90,7 @@ fun MainPageContent(
maxLines = 1,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Start,
text = "Créer une feuille de personnage",
text = stringResource(Res.string.main_page__create_action),
)
}
}

View file

@ -16,6 +16,13 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.roll_page__critical_failure
import lwacharactersheet.composeapp.generated.resources.roll_page__critical_success
import lwacharactersheet.composeapp.generated.resources.roll_page__failure
import lwacharactersheet.composeapp.generated.resources.roll_page__special_success
import lwacharactersheet.composeapp.generated.resources.roll_page__success
import org.jetbrains.compose.resources.getString
class RollViewModel : ViewModel() {
private val _roll = mutableStateOf(RollUio(label = "", value = 0))
@ -117,12 +124,11 @@ class RollViewModel : ViewModel() {
_result.value = RollResultUio(
label = rollStep?.let { rollStep ->
when (roll) {
// TODO wording
in rollStep.criticalSuccessRange -> "Réussite critique"
in rollStep.specialSuccessRange -> "Réussite spéciale"
in rollStep.successRange -> "Réussite"
in rollStep.failureRange -> "Échec"
in rollStep.criticalFailureRange -> "Échec critique"
in rollStep.criticalSuccessRange -> getString(resource = Res.string.roll_page__critical_success)
in rollStep.specialSuccessRange -> getString(resource = Res.string.roll_page__special_success)
in rollStep.successRange -> getString(resource = Res.string.roll_page__success)
in rollStep.failureRange -> getString(resource = Res.string.roll_page__failure)
in rollStep.criticalFailureRange -> getString(resource = Res.string.roll_page__critical_failure)
else -> ""
}
} ?: "",

View file

@ -0,0 +1,52 @@
package com.pixelized.desktop.lwa.business
import org.junit.Test
class DamageBonusUseCaseTest {
@Test
fun testBonusDamage() {
(0 until 12).forEach {
val result = DamageBonusUseCase.bonusDamage(stat = it)
val expected = "-1d6"
assert(result == expected) {
"Expected:'$expected' bonus damage for stat:'$it' but was:'$result'"
}
}
(12 until 18).forEach {
val result = DamageBonusUseCase.bonusDamage(stat = it)
val expected = "-1d4"
assert(result == expected) {
"Expected:'$expected' bonus damage for stat:'$it' but was:'$result'"
}
}
(18 until 23).forEach {
val result = DamageBonusUseCase.bonusDamage(stat = it)
val expected = "+0"
assert(result == expected) {
"Expected:'$expected' bonus damage for stat:'$it' but was:'$result'"
}
}
(23 until 30).forEach {
val result = DamageBonusUseCase.bonusDamage(stat = it)
val expected = "+1d4"
assert(result == expected) {
"Expected:'$expected' bonus damage for stat:'$it' but was:'$result'"
}
}
(30 until 40).forEach {
val result = DamageBonusUseCase.bonusDamage(stat = it)
val expected = "+1d6"
assert(result == expected) {
"Expected:'$expected' bonus damage for stat:'$it' but was:'$result'"
}
}
(40 until 100).forEach {
val result = DamageBonusUseCase.bonusDamage(stat = it)
val expected = "+2d6"
assert(result == expected) {
"Expected:'$expected' bonus damage for stat:'$it' but was:'$result'"
}
}
}
}