Add string roll support
This commit is contained in:
parent
b71645a7a2
commit
1b9a2f48ca
10 changed files with 241 additions and 66 deletions
Binary file not shown.
|
|
@ -1,12 +1,48 @@
|
|||
package com.pixelized.desktop.lwa.business
|
||||
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet
|
||||
|
||||
object RollUseCase {
|
||||
private val d100 = (1..100)
|
||||
|
||||
/**
|
||||
* (Math.random() * 100 + 1).toInt()
|
||||
*/
|
||||
private val diceParser = Regex(
|
||||
"""(?<sign>[+-])?\h*(?<modifier>[ade])?(?<quantity>\d+)d(?<face>\d+)"""
|
||||
)
|
||||
private val flatParser = Regex(
|
||||
"""(?<sign>[+-])?\h(?<value>\d+)\b"""
|
||||
)
|
||||
private val paramParser = Regex(
|
||||
"""(?<sign>[+-])?\h(?<param>BDGT)\b"""
|
||||
)
|
||||
|
||||
fun rollD100(): Int {
|
||||
return d100.random()
|
||||
}
|
||||
|
||||
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 {
|
||||
println("roll ${sign}${quantity}d${faces} -> $it")
|
||||
}
|
||||
} + flatParser.findAll(roll).sumOf {
|
||||
val (sign, value) = it.destructured
|
||||
((if (sign == "-") -1 else 1) * value.toInt()).also {
|
||||
println("flat: ${sign}${value} -> $it")
|
||||
}
|
||||
} + paramParser.findAll(roll).sumOf {
|
||||
val (sign, param) = it.destructured
|
||||
(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 {
|
||||
println("param: ${sign}${param} -> $it")
|
||||
}
|
||||
}
|
||||
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ data class CharacterSheet(
|
|||
// magic skill
|
||||
val magics: List<Skill>,
|
||||
// attack
|
||||
val attacks: List<Roll>,
|
||||
val rolls: List<Roll>,
|
||||
) : Serializable {
|
||||
|
||||
data class Skill(
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ class CharacterSheetFactory {
|
|||
skills = model.skills.mapNotNull {
|
||||
if (it.value > 0) {
|
||||
Node(
|
||||
type = Node.Type.SKILLS,
|
||||
label = it.label,
|
||||
value = it.value,
|
||||
)
|
||||
|
|
@ -46,7 +45,6 @@ class CharacterSheetFactory {
|
|||
occupations = model.occupations.mapNotNull {
|
||||
if (it.value > 0) {
|
||||
Node(
|
||||
type = Node.Type.OCCUPATIONS,
|
||||
label = it.label,
|
||||
value = it.value,
|
||||
)
|
||||
|
|
@ -57,7 +55,6 @@ class CharacterSheetFactory {
|
|||
magics = model.magics.mapNotNull {
|
||||
if (it.value > 0) {
|
||||
Node(
|
||||
type = Node.Type.MAGICS,
|
||||
label = it.label,
|
||||
value = it.value,
|
||||
)
|
||||
|
|
@ -65,6 +62,16 @@ class CharacterSheetFactory {
|
|||
null
|
||||
}
|
||||
},
|
||||
rolls = model.rolls.mapNotNull {
|
||||
if (it.roll.isNotEmpty()) {
|
||||
CharacterSheetPageUio.Roll(
|
||||
label = it.label,
|
||||
value = it.roll,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheet
|
|||
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.ic_d20_32dp
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
|
||||
@Stable
|
||||
data class CharacterSheetPageUio(
|
||||
|
|
@ -58,6 +61,7 @@ data class CharacterSheetPageUio(
|
|||
val skills: List<Node>,
|
||||
val occupations: List<Node>,
|
||||
val magics: List<Node>,
|
||||
val rolls: List<Roll>,
|
||||
) {
|
||||
@Stable
|
||||
data class Characteristic(
|
||||
|
|
@ -67,15 +71,15 @@ data class CharacterSheetPageUio(
|
|||
|
||||
@Stable
|
||||
data class Node(
|
||||
val type: Type,
|
||||
val label: String,
|
||||
val value: Int,
|
||||
) {
|
||||
)
|
||||
|
||||
@Stable
|
||||
enum class Type {
|
||||
SKILLS, OCCUPATIONS, MAGICS,
|
||||
}
|
||||
}
|
||||
data class Roll(
|
||||
val label: String,
|
||||
val value: String,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -101,28 +105,32 @@ fun CharacterSheetPage(
|
|||
)
|
||||
},
|
||||
content = {
|
||||
viewModel.sheet.value?.let {
|
||||
viewModel.sheet.value?.let { sheet ->
|
||||
CharacterSheetPageContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
characterSheet = it,
|
||||
characterSheet = sheet,
|
||||
onBack = {
|
||||
screen.popBackStack()
|
||||
},
|
||||
onEdit = {
|
||||
screen.navigateToCharacterSheetEdit(id = it.id)
|
||||
screen.navigateToCharacterSheetEdit(id = sheet.id)
|
||||
},
|
||||
onDelete = {
|
||||
scope.launch {
|
||||
viewModel.deleteCharacter(id = it.id)
|
||||
viewModel.deleteCharacter(id = sheet.id)
|
||||
screen.popBackStack()
|
||||
}
|
||||
},
|
||||
onCharacteristic = { characteristic ->
|
||||
rollViewModel.prepareRoll(characteristic = characteristic)
|
||||
rollViewModel.prepareRoll(sheet = sheet, characteristic = characteristic)
|
||||
overlayViewModel.show()
|
||||
},
|
||||
onSkill = { node ->
|
||||
rollViewModel.prepareRoll(node = node)
|
||||
rollViewModel.prepareRoll(sheet = sheet, node = node)
|
||||
overlayViewModel.show()
|
||||
},
|
||||
onRoll = { roll ->
|
||||
rollViewModel.prepareRoll(sheet = sheet, roll = roll)
|
||||
overlayViewModel.show()
|
||||
},
|
||||
)
|
||||
|
|
@ -144,6 +152,7 @@ fun CharacterSheetPageContent(
|
|||
onDelete: () -> Unit,
|
||||
onCharacteristic: (characteristic: CharacterSheetPageUio.Characteristic) -> Unit,
|
||||
onSkill: (skill: CharacterSheetPageUio.Node) -> Unit,
|
||||
onRoll: (roll: CharacterSheetPageUio.Roll) -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
|
|
@ -289,6 +298,13 @@ fun CharacterSheetPageContent(
|
|||
}
|
||||
}
|
||||
}
|
||||
characterSheet.rolls.forEach {
|
||||
Roll(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = it.label,
|
||||
onClick = { onRoll(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -346,6 +362,35 @@ private fun Characteristics(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Roll(
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp),
|
||||
label: String,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onClick)
|
||||
.padding(paddingValues = paddingValues)
|
||||
.then(other = modifier),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.body1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = label
|
||||
)
|
||||
Icon(
|
||||
painter = painterResource(Res.drawable.ic_d20_32dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Skill(
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -355,7 +400,9 @@ private fun Skill(
|
|||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.clickable(onClick = onClick).padding(paddingValues = paddingValues)
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onClick)
|
||||
.padding(paddingValues = paddingValues)
|
||||
.then(other = modifier),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ data class CharacterSheetEditPageUio(
|
|||
val id: String,
|
||||
val name: FieldUio,
|
||||
val skills: List<SkillGroup>,
|
||||
val rolls: List<FieldUio>,
|
||||
) {
|
||||
@Stable
|
||||
data class SkillGroup(
|
||||
|
|
@ -55,7 +56,7 @@ data class CharacterSheetEditPageUio(
|
|||
SKILLS,
|
||||
OCCUPATIONS,
|
||||
MAGICS,
|
||||
OTHER,
|
||||
OTHERS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -77,8 +78,9 @@ fun CharacterSheetEditPage(
|
|||
) {
|
||||
CharacterSheetEdit(
|
||||
form = viewModel.characterSheet.value,
|
||||
onSkill = viewModel::onSkill,
|
||||
onBack = { screen.popBackStack() },
|
||||
onNewSkill = viewModel::onSkill,
|
||||
onNewCategory = viewModel::onNewRoll,
|
||||
onSave = {
|
||||
scope.launch {
|
||||
viewModel.save()
|
||||
|
|
@ -92,8 +94,9 @@ fun CharacterSheetEditPage(
|
|||
@Composable
|
||||
fun CharacterSheetEdit(
|
||||
form: CharacterSheetEditPageUio,
|
||||
onSkill: (CharacterSheetEditPageUio.SkillGroup) -> Unit,
|
||||
onBack: () -> Unit,
|
||||
onNewSkill: (CharacterSheetEditPageUio.SkillGroup) -> Unit,
|
||||
onNewCategory: () -> Unit,
|
||||
onSave: () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
|
|
@ -159,7 +162,7 @@ fun CharacterSheetEdit(
|
|||
)
|
||||
) {
|
||||
TextButton(
|
||||
onClick = { onSkill(it) },
|
||||
onClick = { onNewSkill(it) },
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
@ -180,6 +183,24 @@ fun CharacterSheetEdit(
|
|||
}
|
||||
}
|
||||
|
||||
form.rolls.forEach {
|
||||
Form(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
field = it,
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
TextButton(
|
||||
onClick = onNewCategory,
|
||||
) {
|
||||
Text(text = "Ajouter un lancé")
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ class CharacterSheetEditViewModel(
|
|||
|
||||
fun onSkill(skill: SkillGroup) {
|
||||
val sheet = _characterSheet.value
|
||||
|
||||
_characterSheet.value = sheet.copy(
|
||||
skills = sheet.skills.map { group ->
|
||||
if (skill.title == group.title) {
|
||||
|
|
@ -42,7 +41,7 @@ class CharacterSheetEditViewModel(
|
|||
SkillGroup.Type.SKILLS -> "0"
|
||||
SkillGroup.Type.OCCUPATIONS -> "40"
|
||||
SkillGroup.Type.MAGICS -> "0"
|
||||
SkillGroup.Type.OTHER -> ""
|
||||
SkillGroup.Type.OTHERS -> ""
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -56,6 +55,21 @@ class CharacterSheetEditViewModel(
|
|||
)
|
||||
}
|
||||
|
||||
fun onNewRoll() {
|
||||
val sheet = _characterSheet.value
|
||||
_characterSheet.value = sheet.copy(
|
||||
rolls = sheet.rolls.toMutableList().apply {
|
||||
add(
|
||||
FieldUio.create(
|
||||
label = "",
|
||||
isLabelEditable = true,
|
||||
valuePlaceHolder = { "" },
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun save() {
|
||||
val sheet = _characterSheet.value
|
||||
val model = factory.convertToModel(sheet = sheet)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ 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 {
|
||||
|
|
@ -48,7 +49,12 @@ class CharacterSheetFactory {
|
|||
used = false,
|
||||
)
|
||||
},
|
||||
attacks = emptyList(),
|
||||
rolls = sheet.rolls.map {
|
||||
CharacterSheet.Roll(
|
||||
label = it.label.value,
|
||||
roll = it.unpack(),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +129,7 @@ class CharacterSheetFactory {
|
|||
),
|
||||
FieldUio.create(
|
||||
label = "Points de vie",
|
||||
valuePlaceHolder = { "${(con() + hei()) / 2}" },
|
||||
valuePlaceHolder = { "${ceil((con() + hei()) / 2f).toInt()}" },
|
||||
initialValue = sheet?.maxHp?.toString() ?: ""
|
||||
),
|
||||
FieldUio.create(
|
||||
|
|
@ -160,7 +166,6 @@ class CharacterSheetFactory {
|
|||
fields = sheet?.skills?.map {
|
||||
FieldUio.create(
|
||||
label = it.label,
|
||||
valuePlaceHolder = { "" },
|
||||
initialValue = it.value.toString(),
|
||||
)
|
||||
} ?: listOf(
|
||||
|
|
@ -249,12 +254,18 @@ class CharacterSheetFactory {
|
|||
fields = sheet?.magics?.map {
|
||||
FieldUio.create(
|
||||
label = it.label,
|
||||
valuePlaceHolder = { "" },
|
||||
initialValue = it.value.toString()
|
||||
)
|
||||
} ?: emptyList(),
|
||||
),
|
||||
),
|
||||
rolls = sheet?.rolls?.map {
|
||||
FieldUio.create(
|
||||
label = it.label,
|
||||
isLabelEditable = true,
|
||||
initialValue = it.roll,
|
||||
)
|
||||
} ?: emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ import org.jetbrains.compose.resources.painterResource
|
|||
@Stable
|
||||
data class RollUio(
|
||||
val label: String,
|
||||
val value: Int,
|
||||
val value: Int?,
|
||||
)
|
||||
|
||||
@Stable
|
||||
|
|
@ -80,12 +80,14 @@ fun RollPage(
|
|||
overflow = TextOverflow.Ellipsis,
|
||||
text = viewModel.roll.value.label,
|
||||
)
|
||||
viewModel.roll.value.value?.let {
|
||||
Text(
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
text = "Réussite en dessous de : ${viewModel.roll.value.value}",
|
||||
text = "Réussite en dessous de : ${it}",
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.lifecycle.ViewModel
|
||||
import com.pixelized.desktop.lwa.business.RollUseCase
|
||||
import com.pixelized.desktop.lwa.business.SkillStepUseCase
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
|
@ -20,42 +22,71 @@ class RollViewModel : ViewModel() {
|
|||
val roll: State<RollUio> get() = _roll
|
||||
|
||||
private var rollJob: Job? = null
|
||||
private lateinit var rollStep: SkillStepUseCase.SkillStep
|
||||
private var rollStep: SkillStepUseCase.SkillStep? = null
|
||||
private lateinit var rollAction: String
|
||||
private lateinit var sheet: CharacterSheet
|
||||
val rollRotation = Animatable(0f)
|
||||
|
||||
private val _result = mutableStateOf<RollResultUio?>(null)
|
||||
val result: State<RollResultUio?> get() = _result
|
||||
|
||||
fun prepareRoll(node: CharacterSheetPageUio.Node) {
|
||||
val step = SkillStepUseCase.computeSkillStep(
|
||||
skill = node.value,
|
||||
)
|
||||
prepareRoll(
|
||||
label = node.label,
|
||||
step = step,
|
||||
)
|
||||
}
|
||||
|
||||
fun prepareRoll(characteristic: CharacterSheetPageUio.Characteristic) {
|
||||
fun prepareRoll(
|
||||
sheet: CharacterSheetPageUio,
|
||||
characteristic: CharacterSheetPageUio.Characteristic,
|
||||
) {
|
||||
val step = SkillStepUseCase.computeSkillStep(
|
||||
skill = (characteristic.value.toIntOrNull() ?: 0) * 5
|
||||
)
|
||||
prepareRoll(
|
||||
label = characteristic.label,
|
||||
step = step,
|
||||
rollAction = "1d100",
|
||||
sheet = sheet,
|
||||
rollStep = step,
|
||||
)
|
||||
}
|
||||
|
||||
fun prepareRoll(
|
||||
sheet: CharacterSheetPageUio,
|
||||
node: CharacterSheetPageUio.Node,
|
||||
) {
|
||||
val step = SkillStepUseCase.computeSkillStep(
|
||||
skill = node.value,
|
||||
)
|
||||
prepareRoll(
|
||||
label = node.label,
|
||||
rollAction = "1d100",
|
||||
sheet = sheet,
|
||||
rollStep = step,
|
||||
)
|
||||
}
|
||||
|
||||
fun prepareRoll(
|
||||
sheet: CharacterSheetPageUio,
|
||||
roll: CharacterSheetPageUio.Roll,
|
||||
) {
|
||||
prepareRoll(
|
||||
label = roll.label,
|
||||
rollAction = roll.value,
|
||||
sheet = sheet,
|
||||
rollStep = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun prepareRoll(
|
||||
label: String,
|
||||
step: SkillStepUseCase.SkillStep,
|
||||
rollAction: String,
|
||||
sheet: CharacterSheetPageUio,
|
||||
rollStep: SkillStepUseCase.SkillStep?,
|
||||
) {
|
||||
runBlocking { rollRotation.snapTo(0f) }
|
||||
rollStep = step
|
||||
this.rollStep = rollStep
|
||||
this.rollAction = rollAction
|
||||
this.sheet = CharacterSheetRepository.characterSheetFlow(id = sheet.id).value!!
|
||||
|
||||
_result.value = null
|
||||
_roll.value = RollUio(
|
||||
label = label,
|
||||
value = step.successRange.last
|
||||
value = rollStep?.successRange?.last
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -77,10 +108,15 @@ class RollViewModel : ViewModel() {
|
|||
launch {
|
||||
delay(500)
|
||||
|
||||
val d100 = RollUseCase.rollD100()
|
||||
val roll = if (rollAction == "1d100") {
|
||||
RollUseCase.rollD100()
|
||||
} else {
|
||||
RollUseCase.roll(characterSheet = sheet, roll = rollAction)
|
||||
}
|
||||
|
||||
_result.value = RollResultUio(
|
||||
label = when (d100) {
|
||||
label = rollStep?.let { rollStep ->
|
||||
when (roll) {
|
||||
// TODO wording
|
||||
in rollStep.criticalSuccessRange -> "Réussite critique"
|
||||
in rollStep.specialSuccessRange -> "Réussite spéciale"
|
||||
|
|
@ -88,8 +124,9 @@ class RollViewModel : ViewModel() {
|
|||
in rollStep.failureRange -> "Échec"
|
||||
in rollStep.criticalFailureRange -> "Échec critique"
|
||||
else -> ""
|
||||
},
|
||||
value = d100,
|
||||
}
|
||||
} ?: "",
|
||||
value = roll,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue