Add support for special / critical roll action.

This commit is contained in:
Thomas Andres Gomez 2025-03-26 17:12:47 +01:00
parent 9cc6414648
commit 51e63202e3
24 changed files with 537 additions and 193 deletions

View file

@ -29,6 +29,10 @@
<string name="roll_page__dc_normal__label">Normal</string> <string name="roll_page__dc_normal__label">Normal</string>
<string name="roll_page__dc_hard__label">Difficile</string> <string name="roll_page__dc_hard__label">Difficile</string>
<string name="roll_page__dc_impossible__label">Impossible</string> <string name="roll_page__dc_impossible__label">Impossible</string>
<string name="roll_page__criticality__label">Type de jet :</string>
<string name="roll_page__criticality_normal__label">Normal</string>
<string name="roll_page__criticality_special__label">Spécial</string>
<string name="roll_page__criticality_critical__label">Critique</string>
<string name="character_sheet_edit__create__title">Création de personnage</string> <string name="character_sheet_edit__create__title">Création de personnage</string>
<string name="character_sheet_edit__edit__title">Édition de personnage</string> <string name="character_sheet_edit__edit__title">Édition de personnage</string>
@ -117,7 +121,10 @@
<string name="character_sheet_edit__occupation__title">Occupations</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__occupation__add_action">Ajouter une occupation</string>
<string name="character_sheet_edit__actions__name_label">Nom</string> <string name="character_sheet_edit__actions__name_label">Nom</string>
<string name="character_sheet_edit__actions__action_label">Action de lancer</string> <string name="character_sheet_edit__actions__description_label">Description</string>
<string name="character_sheet_edit__actions__default_action_label">Action normal</string>
<string name="character_sheet_edit__actions__spacial_action_label">Action spécial</string>
<string name="character_sheet_edit__actions__critical_action_label">Action critique</string>
<string name="character_sheet_edit__magic__title">Compétences magiques</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_edit__magic__add_action">Ajouter une compétence magique</string>
<string name="character_sheet_edit__add_roll_action">Ajouter une action de lancer</string> <string name="character_sheet_edit__add_roll_action">Ajouter une action de lancer</string>

View file

@ -71,7 +71,7 @@ class LwaClientImpl(
override suspend fun updateCharacter( override suspend fun updateCharacter(
sheet: CharacterSheetJson, sheet: CharacterSheetJson,
) = client ) = client
.put("$root/character/update") { .put("$root/character/update/sheet") {
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
setBody(sheet) setBody(sheet)
} }

View file

@ -0,0 +1,33 @@
package com.pixelized.desktop.lwa.ui.composable.checkbox
import androidx.compose.material.Checkbox
import androidx.compose.material.CheckboxColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import com.pixelized.desktop.lwa.ui.theme.color.component.LwaCheckboxColors
import kotlinx.coroutines.flow.StateFlow
@Stable
data class LwaCheckBoxUio(
val enable: Boolean = true,
val checked: StateFlow<Boolean>,
val onCheckedChange: (Boolean) -> Unit,
)
@Composable
fun LwaCheckBox(
modifier: Modifier = Modifier,
colors: CheckboxColors = LwaCheckboxColors(),
field: LwaCheckBoxUio,
) {
val checked = field.checked.collectAsState()
Checkbox(
modifier = modifier,
checked = checked.value,
colors = colors,
onCheckedChange = field.onCheckedChange,
)
}

View file

@ -3,7 +3,6 @@ package com.pixelized.desktop.lwa.ui.overlay.roll
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import com.pixelized.shared.lwa.model.campaign.Campaign
import kotlinx.coroutines.CancellableContinuation import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
@ -24,7 +23,7 @@ class RollHostState {
val rollAction: State<RollAction?> get() = currentRollAction val rollAction: State<RollAction?> get() = currentRollAction
suspend fun showRollOverlay( suspend fun showRollOverlay(
roll: RollAction.RollActionUio, roll: RollAction.Uio,
): RollResult = mutex.withLock { ): RollResult = mutex.withLock {
try { try {
return suspendCancellableCoroutine { continuation -> return suspendCancellableCoroutine { continuation ->
@ -40,7 +39,7 @@ class RollHostState {
@Stable @Stable
private class RollActionImpl( private class RollActionImpl(
override val roll: RollAction.RollActionUio, override val roll: RollAction.Uio,
private val continuation: CancellableContinuation<RollResult>, private val continuation: CancellableContinuation<RollResult>,
) : RollAction { ) : RollAction {
override fun action(result: RollResult) { override fun action(result: RollResult) {
@ -61,15 +60,30 @@ enum class RollResult {
@Stable @Stable
interface RollAction { interface RollAction {
val roll: RollActionUio val roll: Uio
fun action(result: RollResult) fun action(result: RollResult)
sealed interface Uio {
val characterSheetId: String
val label: String
@Stable @Stable
data class RollActionUio( data class BoundedRollActionUio(
val characterSheetId: String, override val characterSheetId: String,
val label: String, override val label: String,
val rollAction: String, val rollAction: String,
val rollSuccessValue: Int?, val rollSuccessValue: Int,
) ): Uio
@Stable
data class BoundlessRollActionUio(
override val characterSheetId: String,
override val label: String,
val canBeCritical: Boolean,
val rollDefaultAction: String,
val rollSpecialAction: String?,
val rollCriticalAction: String?,
): Uio
}
} }

View file

@ -44,6 +44,7 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.overlay.roll.CriticalityUio.Criticality
import com.pixelized.desktop.lwa.ui.overlay.roll.DifficultyUio.Difficulty import com.pixelized.desktop.lwa.ui.overlay.roll.DifficultyUio.Difficulty
import com.pixelized.desktop.lwa.ui.theme.lwa import com.pixelized.desktop.lwa.ui.theme.lwa
import com.pixelized.desktop.lwa.utils.DisableInteractionSource import com.pixelized.desktop.lwa.utils.DisableInteractionSource
@ -58,6 +59,10 @@ import lwacharactersheet.composeapp.generated.resources.roll_page__dc_impossible
import lwacharactersheet.composeapp.generated.resources.roll_page__dc_normal__label import lwacharactersheet.composeapp.generated.resources.roll_page__dc_normal__label
import lwacharactersheet.composeapp.generated.resources.roll_page__roll__label import lwacharactersheet.composeapp.generated.resources.roll_page__roll__label
import lwacharactersheet.composeapp.generated.resources.roll_page__roll__success_label import lwacharactersheet.composeapp.generated.resources.roll_page__roll__success_label
import lwacharactersheet.composeapp.generated.resources.roll_page__criticality__label
import lwacharactersheet.composeapp.generated.resources.roll_page__criticality_normal__label
import lwacharactersheet.composeapp.generated.resources.roll_page__criticality_special__label
import lwacharactersheet.composeapp.generated.resources.roll_page__criticality_critical__label
import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.resources.stringResource
@ -83,6 +88,16 @@ data class DifficultyUio(
} }
} }
@Stable
data class CriticalityUio(
val open: Boolean,
val criticality: Criticality,
) {
enum class Criticality {
NORMAL, SPECIAL, CRITICAL
}
}
@Composable @Composable
fun RollPage( fun RollPage(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -231,6 +246,25 @@ fun RollPage(
) )
} }
} }
viewModel.rollCriticality.value?.let {
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 16.dp)
.clickable(
interactionSource = remember { DisableInteractionSource() },
indication = null,
onClick = { },
)
) {
Criticality(
criticality = it,
onToggle = { viewModel.toggleCriticality() },
onCriticality = { viewModel.onCriticality(it) },
)
}
}
} }
} }
@ -299,19 +333,19 @@ fun Difficulty(
expanded = difficulty.open, expanded = difficulty.open,
onDismissRequest = { onToggle() } onDismissRequest = { onToggle() }
) { ) {
DifficultyDropDownItem( LocalDropDownItem(
label = stringResource(Res.string.roll_page__dc_easy__label), label = stringResource(Res.string.roll_page__dc_easy__label),
onClick = { onDifficulty(Difficulty.EASY) }, onClick = { onDifficulty(Difficulty.EASY) },
) )
DifficultyDropDownItem( LocalDropDownItem(
label = stringResource(Res.string.roll_page__dc_normal__label), label = stringResource(Res.string.roll_page__dc_normal__label),
onClick = { onDifficulty(Difficulty.NORMAL) }, onClick = { onDifficulty(Difficulty.NORMAL) },
) )
DifficultyDropDownItem( LocalDropDownItem(
label = stringResource(Res.string.roll_page__dc_hard__label), label = stringResource(Res.string.roll_page__dc_hard__label),
onClick = { onDifficulty(Difficulty.HARD) }, onClick = { onDifficulty(Difficulty.HARD) },
) )
DifficultyDropDownItem( LocalDropDownItem(
label = stringResource(Res.string.roll_page__dc_impossible__label), label = stringResource(Res.string.roll_page__dc_impossible__label),
onClick = { onDifficulty(Difficulty.IMPOSSIBLE) }, onClick = { onDifficulty(Difficulty.IMPOSSIBLE) },
) )
@ -319,8 +353,88 @@ fun Difficulty(
} }
} }
@OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
private fun DifficultyDropDownItem( fun Criticality(
modifier: Modifier = Modifier,
criticality: CriticalityUio,
onToggle: () -> Unit,
onCriticality: (Criticality) -> Unit,
) {
ExposedDropdownMenuBox(
modifier = modifier,
expanded = criticality.open,
onExpandedChange = { onToggle() },
) {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Row(
modifier = Modifier.padding(
horizontal = 16.dp,
vertical = 8.dp,
),
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.body1,
text = stringResource(Res.string.roll_page__criticality__label)
)
AnimatedContent(
targetState = criticality.criticality,
transitionSpec = {
val enter = fadeIn() + slideInVertically { -16 }
val exit = fadeOut() + slideOutVertically { 16 }
enter togetherWith exit using SizeTransform(clip = false)
},
) {
Text(
modifier = Modifier.alignByBaseline().animateContentSize(),
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.primary,
text = when (it) {
Criticality.NORMAL -> stringResource(Res.string.roll_page__criticality_normal__label)
Criticality.SPECIAL -> stringResource(Res.string.roll_page__criticality_special__label)
Criticality.CRITICAL -> stringResource(Res.string.roll_page__criticality_critical__label)
}
)
}
}
val rotation = animateFloatAsState(
targetValue = if (criticality.open) -180f else 0f,
)
Icon(
modifier = Modifier
.offset(x = (-12).dp)
.graphicsLayer { rotationZ = rotation.value },
imageVector = Icons.Default.ArrowDropDown,
contentDescription = null
)
}
ExposedDropdownMenu(
expanded = criticality.open,
onDismissRequest = { onToggle() }
) {
LocalDropDownItem(
label = stringResource(Res.string.roll_page__criticality_normal__label),
onClick = { onCriticality(Criticality.NORMAL) },
)
LocalDropDownItem(
label = stringResource(Res.string.roll_page__criticality_special__label),
onClick = { onCriticality(Criticality.SPECIAL) },
)
LocalDropDownItem(
label = stringResource(Res.string.roll_page__criticality_critical__label),
onClick = { onCriticality(Criticality.CRITICAL) },
)
}
}
}
@Composable
private fun LocalDropDownItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp), contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp),
label: String, label: String,

View file

@ -9,8 +9,8 @@ import androidx.lifecycle.ViewModel
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.network.NetworkRepository import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.ui.overlay.roll.CriticalityUio.Criticality
import com.pixelized.desktop.lwa.ui.overlay.roll.DifficultyUio.Difficulty import com.pixelized.desktop.lwa.ui.overlay.roll.DifficultyUio.Difficulty
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio
import com.pixelized.shared.lwa.model.AlteredCharacterSheet import com.pixelized.shared.lwa.model.AlteredCharacterSheet
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
import com.pixelized.shared.lwa.protocol.websocket.RollEvent import com.pixelized.shared.lwa.protocol.websocket.RollEvent
@ -42,7 +42,7 @@ class RollViewModel(
private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory, private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory,
) : ViewModel() { ) : ViewModel() {
private var alteredCharacterSheet: AlteredCharacterSheet? = null private var alteredCharacterSheet: AlteredCharacterSheet? = null
private var rollAction: RollActionUio? = null private var rollAction: RollAction.Uio? = null
var lastRollResult: RollResult = RollResult.Dismissed var lastRollResult: RollResult = RollResult.Dismissed
private set private set
@ -64,6 +64,9 @@ class RollViewModel(
private val _rollDifficulty = mutableStateOf<DifficultyUio?>(null) private val _rollDifficulty = mutableStateOf<DifficultyUio?>(null)
val rollDifficulty: State<DifficultyUio?> get() = _rollDifficulty val rollDifficulty: State<DifficultyUio?> get() = _rollDifficulty
private val _rollCriticality = mutableStateOf<CriticalityUio?>(null)
val rollCriticality: State<CriticalityUio?> get() = _rollCriticality
private val _shareResult = mutableStateOf(true) private val _shareResult = mutableStateOf(true)
val shareResult: State<Boolean> = _shareResult val shareResult: State<Boolean> = _shareResult
@ -88,7 +91,7 @@ class RollViewModel(
* return true if the viewModel is ready to roll, otherwise false. * return true if the viewModel is ready to roll, otherwise false.
*/ */
suspend fun prepareRoll( suspend fun prepareRoll(
roll: RollActionUio, roll: RollAction.Uio,
) { ) {
rollRotation.snapTo(0f) rollRotation.snapTo(0f)
rollScale.snapTo(1f) rollScale.snapTo(1f)
@ -111,23 +114,39 @@ class RollViewModel(
this.rollAction = roll this.rollAction = roll
val rollStep = roll.rollSuccessValue?.let { val rollStep = roll
skillStepUseCase.computeSkillStep(skill = it) .let { it as? RollAction.Uio.BoundedRollActionUio }
} ?.rollSuccessValue
?.let { skillStepUseCase.computeSkillStep(skill = it) }
_shareResult.value = true _shareResult.value = true
_cancellable.value = true _cancellable.value = true
_rollResult.value = null _rollResult.value = null
_rollTitle.value = RollTitleUio( _rollTitle.value = RollTitleUio(
label = roll.label, label = roll.label,
value = rollStep?.success?.last value = rollStep?.success?.last
) )
_rollDifficulty.value = roll.rollSuccessValue?.let {
_rollDifficulty.value = roll
.let { it as? RollAction.Uio.BoundedRollActionUio }
?.rollSuccessValue
?.let {
DifficultyUio( DifficultyUio(
open = false, open = false,
difficulty = Difficulty.NORMAL, difficulty = Difficulty.NORMAL,
) )
} }
_rollCriticality.value = roll
.let { it as? RollAction.Uio.BoundlessRollActionUio }
?.takeIf { it.canBeCritical }
?.let {
CriticalityUio(
open = false,
criticality = Criticality.NORMAL
)
}
} }
suspend fun roll() { suspend fun roll() {
@ -151,7 +170,9 @@ class RollViewModel(
delay(500) delay(500)
_cancellable.value = false _cancellable.value = false
// compute the skill critical success to critical failure ranges. // compute the skill critical success to critical failure ranges.
val rollStep = rollAction.rollSuccessValue?.let { val rollStep = rollAction
.let { it as? RollAction.Uio.BoundedRollActionUio }
?.rollSuccessValue?.let {
skillStepUseCase.computeSkillStep( skillStepUseCase.computeSkillStep(
skill = when (_rollDifficulty.value?.difficulty) { skill = when (_rollDifficulty.value?.difficulty) {
Difficulty.EASY -> it * 2 Difficulty.EASY -> it * 2
@ -163,10 +184,20 @@ class RollViewModel(
) )
} }
// compute the expression.
val expression = when (rollAction) {
is RollAction.Uio.BoundedRollActionUio -> rollAction.rollAction
is RollAction.Uio.BoundlessRollActionUio -> when (rollCriticality.value?.criticality) {
Criticality.NORMAL -> rollAction.rollDefaultAction
Criticality.SPECIAL -> rollAction.rollSpecialAction
Criticality.CRITICAL -> rollAction.rollCriticalAction
null -> null
}
} ?: ""
// compute the roll (typically use the expression inside the rollAction) // compute the roll (typically use the expression inside the rollAction)
val roll = skillComputation.computeRoll( val roll = skillComputation.computeRoll(
sheet = alteredCharacterSheet, sheet = alteredCharacterSheet,
expression = rollAction.rollAction, expression = expression,
) )
// check where the roll fall into the rollSteps. // check where the roll fall into the rollSteps.
@ -227,7 +258,10 @@ class RollViewModel(
open = false, open = false,
difficulty = difficulty, difficulty = difficulty,
) )
val rollStep = rollAction?.rollSuccessValue?.let { val rollStep = rollAction
.let { it as? RollAction.Uio.BoundedRollActionUio }
?.rollSuccessValue
?.let {
skillStepUseCase.computeSkillStep( skillStepUseCase.computeSkillStep(
skill = when (_rollDifficulty.value?.difficulty) { skill = when (_rollDifficulty.value?.difficulty) {
Difficulty.EASY -> it * 2 Difficulty.EASY -> it * 2
@ -243,6 +277,19 @@ class RollViewModel(
) )
} }
fun toggleCriticality() {
_rollCriticality.value = _rollCriticality.value?.copy(
open = _rollCriticality.value?.open?.not() ?: false
)
}
fun onCriticality(criticality: Criticality) {
_rollCriticality.value = CriticalityUio(
open = false,
criticality = criticality,
)
}
private suspend fun diceRotationAnimation() { private suspend fun diceRotationAnimation() {
rollRotation.animateTo( rollRotation.animateTo(
targetValue = rollRotation.value.let { it - it % 360 } + 360f * 3, targetValue = rollRotation.value.let { it - it % 360 } + 360f * 3,

View file

@ -1,7 +1,8 @@
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeaderUio import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeaderUio
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetActionUio import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetActionUio
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetCharacteristicUio import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetCharacteristicUio
@ -130,7 +131,7 @@ class CharacterDetailFactory(
title = getString(Res.string.character_sheet__characteristics__str), title = getString(Res.string.character_sheet__characteristics__str),
description = getString(Res.string.tooltip__characteristics__strength), description = getString(Res.string.tooltip__characteristics__strength),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__str), label = getString(Res.string.character_sheet__characteristics__str),
rollAction = "1d100", rollAction = "1d100",
@ -144,7 +145,7 @@ class CharacterDetailFactory(
title = getString(Res.string.character_sheet__characteristics__dex), title = getString(Res.string.character_sheet__characteristics__dex),
description = getString(Res.string.tooltip__characteristics__dexterity), description = getString(Res.string.tooltip__characteristics__dexterity),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__dex), label = getString(Res.string.character_sheet__characteristics__dex),
rollAction = "1d100", rollAction = "1d100",
@ -158,7 +159,7 @@ class CharacterDetailFactory(
title = getString(Res.string.character_sheet__characteristics__con), title = getString(Res.string.character_sheet__characteristics__con),
description = getString(Res.string.tooltip__characteristics__constitution), description = getString(Res.string.tooltip__characteristics__constitution),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__con), label = getString(Res.string.character_sheet__characteristics__con),
rollAction = "1d100", rollAction = "1d100",
@ -172,7 +173,7 @@ class CharacterDetailFactory(
title = getString(Res.string.character_sheet__characteristics__hei), title = getString(Res.string.character_sheet__characteristics__hei),
description = getString(Res.string.tooltip__characteristics__height), description = getString(Res.string.tooltip__characteristics__height),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__hei), label = getString(Res.string.character_sheet__characteristics__hei),
rollAction = "1d100", rollAction = "1d100",
@ -186,7 +187,7 @@ class CharacterDetailFactory(
title = getString(Res.string.character_sheet__characteristics__int), title = getString(Res.string.character_sheet__characteristics__int),
description = getString(Res.string.tooltip__characteristics__intelligence), description = getString(Res.string.tooltip__characteristics__intelligence),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__int), label = getString(Res.string.character_sheet__characteristics__int),
rollAction = "1d100", rollAction = "1d100",
@ -200,7 +201,7 @@ class CharacterDetailFactory(
title = getString(Res.string.character_sheet__characteristics__pow), title = getString(Res.string.character_sheet__characteristics__pow),
description = getString(Res.string.tooltip__characteristics__power), description = getString(Res.string.tooltip__characteristics__power),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__pow), label = getString(Res.string.character_sheet__characteristics__pow),
rollAction = "1d100", rollAction = "1d100",
@ -214,7 +215,7 @@ class CharacterDetailFactory(
title = getString(Res.string.character_sheet__characteristics__cha), title = getString(Res.string.character_sheet__characteristics__cha),
description = getString(Res.string.tooltip__characteristics__charisma), description = getString(Res.string.tooltip__characteristics__charisma),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__cha), label = getString(Res.string.character_sheet__characteristics__cha),
rollAction = "1d100", rollAction = "1d100",
@ -241,7 +242,7 @@ class CharacterDetailFactory(
description = it, description = it,
) )
}, },
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = skill.label, label = skill.label,
rollAction = "1d100", rollAction = "1d100",
@ -268,7 +269,7 @@ class CharacterDetailFactory(
description = it, description = it,
) )
}, },
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = skill.label, label = skill.label,
rollAction = "1d100", rollAction = "1d100",
@ -295,7 +296,7 @@ class CharacterDetailFactory(
description = it, description = it,
) )
}, },
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = skill.label, label = skill.label,
rollAction = "1d100", rollAction = "1d100",
@ -313,14 +314,16 @@ class CharacterDetailFactory(
description = it, description = it,
) )
}, },
roll = RollActionUio( roll = RollAction.Uio.BoundlessRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = action.label, label = action.label,
rollAction = action.roll, canBeCritical = action.canBeCritical,
rollSuccessValue = null, rollDefaultAction = action.default,
rollSpecialAction = action.special,
rollCriticalAction = action.critical,
) )
) )
} }.sortedWith(compareBy(Collator.getInstance()) { it.label }),
) )
} }
} }

View file

@ -17,7 +17,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.shapes.MasteryShape import com.pixelized.desktop.lwa.ui.composable.shapes.MasteryShape
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp
import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.painterResource
@ -27,7 +27,7 @@ data class CharacterDetailSheetActionUio(
val actionId: String, val actionId: String,
val label: String, val label: String,
val tooltips: TooltipUio?, val tooltips: TooltipUio?,
val roll: RollActionUio, val roll: RollAction.Uio,
) )
@Composable @Composable

View file

@ -15,14 +15,14 @@ import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
@Stable @Stable
data class CharacterDetailSheetCharacteristicUio( data class CharacterDetailSheetCharacteristicUio(
val value: String, val value: String,
val label: String, val label: String,
val tooltips: TooltipUio, val tooltips: TooltipUio,
val roll: RollActionUio, val roll: RollAction.Uio,
) )
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)

View file

@ -21,7 +21,7 @@ import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.shapes.MasteryShape import com.pixelized.desktop.lwa.ui.composable.shapes.MasteryShape
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
@Stable @Stable
data class CharacterDetailSheetSkillUio( data class CharacterDetailSheetSkillUio(
@ -31,7 +31,7 @@ data class CharacterDetailSheetSkillUio(
val used: Boolean, val used: Boolean,
val occupation: Boolean, val occupation: Boolean,
val tooltips: TooltipUio?, val tooltips: TooltipUio?,
val roll: RollActionUio, val roll: RollAction.Uio,
) )
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)

View file

@ -1,7 +1,7 @@
package com.pixelized.desktop.lwa.ui.screen.characterSheet.detail package com.pixelized.desktop.lwa.ui.screen.characterSheet.detail
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Node import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Node
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
@ -70,7 +70,7 @@ class CharacterSheetFactory(
title = getString(Res.string.character_sheet__characteristics__str), title = getString(Res.string.character_sheet__characteristics__str),
description = getString(Res.string.tooltip__characteristics__strength), description = getString(Res.string.tooltip__characteristics__strength),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__str), label = getString(Res.string.character_sheet__characteristics__str),
rollAction = "1d100", rollAction = "1d100",
@ -86,7 +86,7 @@ class CharacterSheetFactory(
title = getString(Res.string.character_sheet__characteristics__dex), title = getString(Res.string.character_sheet__characteristics__dex),
description = getString(Res.string.tooltip__characteristics__dexterity), description = getString(Res.string.tooltip__characteristics__dexterity),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__dex), label = getString(Res.string.character_sheet__characteristics__dex),
rollAction = "1d100", rollAction = "1d100",
@ -102,7 +102,7 @@ class CharacterSheetFactory(
title = getString(Res.string.character_sheet__characteristics__con), title = getString(Res.string.character_sheet__characteristics__con),
description = getString(Res.string.tooltip__characteristics__constitution), description = getString(Res.string.tooltip__characteristics__constitution),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__con), label = getString(Res.string.character_sheet__characteristics__con),
rollAction = "1d100", rollAction = "1d100",
@ -118,7 +118,7 @@ class CharacterSheetFactory(
title = getString(Res.string.character_sheet__characteristics__hei), title = getString(Res.string.character_sheet__characteristics__hei),
description = getString(Res.string.tooltip__characteristics__height), description = getString(Res.string.tooltip__characteristics__height),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__hei), label = getString(Res.string.character_sheet__characteristics__hei),
rollAction = "1d100", rollAction = "1d100",
@ -134,7 +134,7 @@ class CharacterSheetFactory(
title = getString(Res.string.character_sheet__characteristics__int), title = getString(Res.string.character_sheet__characteristics__int),
description = getString(Res.string.tooltip__characteristics__intelligence), description = getString(Res.string.tooltip__characteristics__intelligence),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__int), label = getString(Res.string.character_sheet__characteristics__int),
rollAction = "1d100", rollAction = "1d100",
@ -150,7 +150,7 @@ class CharacterSheetFactory(
title = getString(Res.string.character_sheet__characteristics__pow), title = getString(Res.string.character_sheet__characteristics__pow),
description = getString(Res.string.tooltip__characteristics__power), description = getString(Res.string.tooltip__characteristics__power),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__pow), label = getString(Res.string.character_sheet__characteristics__pow),
rollAction = "1d100", rollAction = "1d100",
@ -166,7 +166,7 @@ class CharacterSheetFactory(
title = getString(Res.string.character_sheet__characteristics__cha), title = getString(Res.string.character_sheet__characteristics__cha),
description = getString(Res.string.tooltip__characteristics__charisma), description = getString(Res.string.tooltip__characteristics__charisma),
), ),
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = getString(Res.string.character_sheet__characteristics__cha), label = getString(Res.string.character_sheet__characteristics__cha),
rollAction = "1d100", rollAction = "1d100",
@ -271,7 +271,7 @@ class CharacterSheetFactory(
description = it, description = it,
) )
}, },
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = skill.label, label = skill.label,
rollAction = "1d100", rollAction = "1d100",
@ -297,7 +297,7 @@ class CharacterSheetFactory(
description = it, description = it,
) )
}, },
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = skill.label, label = skill.label,
rollAction = "1d100", rollAction = "1d100",
@ -323,7 +323,7 @@ class CharacterSheetFactory(
description = it, description = it,
) )
}, },
roll = RollActionUio( roll = RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = skill.label, label = skill.label,
rollAction = "1d100", rollAction = "1d100",
@ -331,16 +331,19 @@ class CharacterSheetFactory(
), ),
) )
}, },
actions = characterSheet.actions.mapNotNull { actions = characterSheet.actions.mapNotNull { action ->
if (it.roll.isEmpty()) return@mapNotNull null if (action.default.isEmpty()) return@mapNotNull null
CharacterSheetPageUio.Roll( CharacterSheetPageUio.Roll(
label = it.label, label = action.label,
value = it.roll, value = action.default,
roll = RollActionUio( roll = RollAction.Uio.BoundlessRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = it.label, label = action.label,
rollAction = it.roll, canBeCritical = action.canBeCritical,
rollSuccessValue = null, rollDefaultAction = action.default,
rollSpecialAction = action.special,
rollCriticalAction = action.critical,
) )
) )
} }

View file

@ -64,7 +64,7 @@ import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
import com.pixelized.desktop.lwa.ui.navigation.window.LocalWindow import com.pixelized.desktop.lwa.ui.navigation.window.LocalWindow
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToCharacterSheetEdit import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToCharacterSheetEdit
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
import com.pixelized.desktop.lwa.ui.overlay.roll.RollPage import com.pixelized.desktop.lwa.ui.overlay.roll.RollPage
import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
@ -108,7 +108,7 @@ data class CharacterSheetPageUio(
val value: String, val value: String,
val editable: Boolean, val editable: Boolean,
val tooltips: TooltipUio?, val tooltips: TooltipUio?,
val roll: RollActionUio?, val roll: RollAction.Uio?,
) )
@Stable @Stable
@ -118,14 +118,14 @@ data class CharacterSheetPageUio(
val value: Int, val value: Int,
val used: Boolean, val used: Boolean,
val tooltips: TooltipUio? = null, val tooltips: TooltipUio? = null,
val roll: RollActionUio, val roll: RollAction.Uio,
) )
@Stable @Stable
data class Roll( data class Roll(
val label: String, val label: String,
val value: String, val value: String,
val roll: RollActionUio, val roll: RollAction.Uio,
) )
} }

View file

@ -3,6 +3,8 @@ package com.pixelized.desktop.lwa.ui.screen.characterSheet.edit
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import com.pixelized.desktop.lwa.ui.composable.checkbox.LwaCheckBoxUio
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.SkillFieldFactory import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.SkillFieldFactory
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.occupation import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.occupation
@ -14,9 +16,13 @@ import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.textfi
import com.pixelized.desktop.lwa.utils.extention.unAccent import com.pixelized.desktop.lwa.utils.extention.unAccent
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
import kotlinx.coroutines.flow.MutableStateFlow
import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__action_label import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__critical_action_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__default_action_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__description_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__name_label import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__name_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__spacial_action_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__cha 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__con
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__dex import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__dex
@ -186,10 +192,12 @@ class CharacterSheetEditFactory(
actions = editedSheet.actions.map { actions = editedSheet.actions.map {
CharacterSheet.Roll( CharacterSheet.Roll(
id = it.id, id = it.id,
label = it.label.value.value, label = it.label.valueFlow.value,
description = null, // TODO description = it.description.valueFlow.value,
canBeCritical = false, // TODO canBeCritical = it.canBeCritical.checked.value,
roll = it.action.value.value, default = it.default.valueFlow.value,
special = it.special?.valueFlow?.value,
critical = it.critical?.valueFlow?.value,
) )
}, },
) )
@ -456,13 +464,28 @@ class CharacterSheetEditFactory(
actions = sheet?.actions?.map { action -> actions = sheet?.actions?.map { action ->
ActionFieldUio( ActionFieldUio(
id = action.id, id = action.id,
label = skillFieldFactory.createWrapper( label = createLwaTextField(
label = mutableStateOf(getString(Res.string.character_sheet_edit__actions__name_label)), label = getString(Res.string.character_sheet_edit__actions__name_label),
value = action.label, value = action.label,
), ),
action = skillFieldFactory.createWrapper( description = createLwaTextField(
label = mutableStateOf(getString(Res.string.character_sheet_edit__actions__action_label)), label = getString(Res.string.character_sheet_edit__actions__description_label),
value = action.roll, value = action.description ?: "",
),
canBeCritical = createLwaBox(
checked = action.canBeCritical,
),
default = createLwaTextField(
label = getString(Res.string.character_sheet_edit__actions__default_action_label),
value = action.default,
),
special = createLwaTextField(
label = getString(Res.string.character_sheet_edit__actions__spacial_action_label),
value = action.special,
),
critical = createLwaTextField(
label = getString(Res.string.character_sheet_edit__actions__critical_action_label),
value = action.critical,
), ),
option = skillFieldFactory.deleteOption { onDeleteSkill(action.id) }, option = skillFieldFactory.deleteOption { onDeleteSkill(action.id) },
) )
@ -471,6 +494,41 @@ class CharacterSheetEditFactory(
} }
} }
fun createLwaTextField(
enable: Boolean = true,
isError: Boolean = false,
label: String? = null,
placeholder: String? = null,
value: String? = null,
): LwaTextFieldUio {
val valueFlow = MutableStateFlow(value ?: "")
val labelFlow = MutableStateFlow(label)
val placeholderFlow = MutableStateFlow(placeholder)
val isErrorFlow = MutableStateFlow(isError)
return LwaTextFieldUio(
enable = enable,
isError = isErrorFlow,
labelFlow = labelFlow,
valueFlow = valueFlow,
placeHolderFlow = placeholderFlow,
onValueChange = { valueFlow.value = it },
)
}
fun createLwaBox(
enable: Boolean = true,
checked: Boolean,
): LwaCheckBoxUio {
val checkedFlow = MutableStateFlow(checked)
return LwaCheckBoxUio(
enable = enable,
checked = checkedFlow,
onCheckedChange = { checkedFlow.value = it },
)
}
private suspend fun createLevelUpWrapper( private suspend fun createLevelUpWrapper(
shouldLevelUp: Boolean, shouldLevelUp: Boolean,
): LevelUpWrapperUio { ): LevelUpWrapperUio {

View file

@ -34,7 +34,6 @@ import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
import com.pixelized.desktop.lwa.ui.navigation.window.LocalWindow import com.pixelized.desktop.lwa.ui.navigation.window.LocalWindow
import com.pixelized.desktop.lwa.ui.screen.characterSheet.copy.CharacterSheetCopyDialog import com.pixelized.desktop.lwa.ui.screen.characterSheet.copy.CharacterSheetCopyDialog
import com.pixelized.desktop.lwa.ui.screen.characterSheet.delete.CharacterSheetDeleteDialog import com.pixelized.desktop.lwa.ui.screen.characterSheet.delete.CharacterSheetDeleteDialog
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.CharacterSheetDeleteConfirmationDialog
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionField import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionField
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionFieldUio import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionFieldUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.BaseSkillFieldUio import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.BaseSkillFieldUio
@ -430,8 +429,10 @@ fun CharacterSheetEdit(
) { ) {
form.actions.forEach { form.actions.forEach {
ActionField( ActionField(
modifier = Modifier.fillMaxWidth().padding(end = (4 + 2).dp), modifier = Modifier
action = it .fillMaxWidth()
.padding(end = 6.dp, bottom = 16.dp),
action = it,
) )
} }
TextButton( TextButton(

View file

@ -14,8 +14,11 @@ import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.Action
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__action_label import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__critical_action_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__description_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__default_action_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__name_label import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__name_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__spacial_action_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__copy__label import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__copy__label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__copy__title import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__copy__title
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__magic_title import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__magic_title
@ -125,11 +128,28 @@ class CharacterSheetEditViewModel(
val id = UUID.randomUUID().toString() val id = UUID.randomUUID().toString()
val field = ActionFieldUio( val field = ActionFieldUio(
id = id, id = id,
label = skillFactory.createWrapper( label = sheetFactory.createLwaTextField(
label = mutableStateOf(getString(Res.string.character_sheet_edit__actions__name_label)), label = getString(Res.string.character_sheet_edit__actions__name_label),
value = "",
), ),
action = skillFactory.createWrapper( description = sheetFactory.createLwaTextField(
label = mutableStateOf(getString(Res.string.character_sheet_edit__actions__action_label)), label = getString(Res.string.character_sheet_edit__actions__description_label),
value = "",
),
canBeCritical = sheetFactory.createLwaBox(
checked = false,
),
default = sheetFactory.createLwaTextField(
label = getString(Res.string.character_sheet_edit__actions__default_action_label),
value = "",
),
special = sheetFactory.createLwaTextField(
label = getString(Res.string.character_sheet_edit__actions__spacial_action_label),
value = "",
),
critical = sheetFactory.createLwaTextField(
label = getString(Res.string.character_sheet_edit__actions__critical_action_label),
value = "",
), ),
option = skillFactory.deleteOption { deleteSkill(id) }, option = skillFactory.deleteOption { deleteSkill(id) },
) )
@ -151,7 +171,7 @@ class CharacterSheetEditViewModel(
}, },
actions = _characterSheet.value.actions.toMutableList().also { actions -> actions = _characterSheet.value.actions.toMutableList().also { actions ->
actions.removeIf { it.id == skillId } actions.removeIf { it.id == skillId }
} },
) )
} }

View file

@ -1,9 +1,10 @@
package com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable package com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.DropdownMenu import androidx.compose.material.DropdownMenu
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.IconButton import androidx.compose.material.IconButton
@ -12,45 +13,57 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.checkbox.LwaCheckBox
import com.pixelized.desktop.lwa.ui.composable.checkbox.LwaCheckBoxUio
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextField
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.option.ActionOption import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.option.ActionOption
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.option.DropDownActionMenuItem import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.option.DropDownActionMenuItem
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.textfield.TextFieldWrapper
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.textfield.TextFieldWrapperUio
@Stable @Stable
data class ActionFieldUio( data class ActionFieldUio(
val id: String, val id: String,
val label: TextFieldWrapperUio, val label: LwaTextFieldUio,
val action: TextFieldWrapperUio, val description: LwaTextFieldUio,
val canBeCritical: LwaCheckBoxUio,
val default: LwaTextFieldUio,
val special: LwaTextFieldUio?,
val critical: LwaTextFieldUio?,
val option: ActionOption.DeleteOptionUio, val option: ActionOption.DeleteOptionUio,
) )
@Composable @Composable
fun ActionField( fun ActionField(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
space: Dp = 4.dp,
action: ActionFieldUio, action: ActionFieldUio,
) { ) {
val showMenu = remember { mutableStateOf(false) } val showMenu = remember { mutableStateOf(false) }
val canBeCritical = action.canBeCritical.checked.collectAsState()
Row( Column(
modifier = modifier, modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(space = 4.dp, alignment = Alignment.End), verticalArrangement = Arrangement.spacedBy(space = space),
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(space = space),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
TextFieldWrapper( LwaTextField(
modifier = Modifier.weight(weight = 1f), modifier = Modifier.weight(1f),
wrapper = action.label, field = action.label,
) )
TextFieldWrapper( LwaCheckBox(
modifier = Modifier.width(width = (192+4).dp), field = action.canBeCritical,
wrapper = action.action,
) )
Box {
IconButton( IconButton(
onClick = { showMenu.value = showMenu.value.not() }, onClick = { showMenu.value = showMenu.value.not() },
) { ) {
@ -59,7 +72,6 @@ fun ActionField(
tint = MaterialTheme.colors.primary, tint = MaterialTheme.colors.primary,
contentDescription = null, contentDescription = null,
) )
}
DropdownMenu( DropdownMenu(
expanded = showMenu.value, expanded = showMenu.value,
onDismissRequest = { showMenu.value = false } onDismissRequest = { showMenu.value = false }
@ -74,4 +86,33 @@ fun ActionField(
} }
} }
} }
Row(
modifier = Modifier.fillMaxWidth().animateContentSize(),
horizontalArrangement = Arrangement.spacedBy(space = space),
) {
LwaTextField(
modifier = Modifier.weight(1f),
field = action.default,
)
if (canBeCritical.value) {
action.special?.let { field ->
LwaTextField(
modifier = Modifier.weight(1f),
field = field,
)
}
action.critical?.let { field ->
LwaTextField(
modifier = Modifier.weight(1f),
field = field,
)
}
}
}
LwaTextField(
modifier = Modifier.fillMaxWidth(),
singleLine = false,
field = action.description,
)
}
} }

View file

@ -41,6 +41,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.LocalWindowController import com.pixelized.desktop.lwa.LocalWindowController
@ -153,11 +154,12 @@ private fun GameMasterContent(
.clickable { onGameMaster(gameMaster.value.not()) } .clickable { onGameMaster(gameMaster.value.not()) }
.padding(all = 8.dp), .padding(all = 8.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(space = 4.dp) horizontalArrangement = Arrangement.spacedBy(space = 8.dp)
) { ) {
Text( Text(
color = MaterialTheme.lwa.colorScheme.base.primary, color = MaterialTheme.lwa.colorScheme.base.primary,
style = MaterialTheme.typography.caption, style = MaterialTheme.typography.body2,
fontWeight = FontWeight.SemiBold,
text = stringResource(Res.string.game_master__action), text = stringResource(Res.string.game_master__action),
) )
Switch( Switch(

View file

@ -4,7 +4,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.calculateStartPadding
@ -107,14 +106,13 @@ fun GMCharacter(
.background(color = MaterialTheme.lwa.colorScheme.elevated.base1dp) .background(color = MaterialTheme.lwa.colorScheme.elevated.base1dp)
.then(other = modifier), .then(other = modifier),
) { ) {
Column {
Row( Row(
modifier = Modifier.padding(start = startPadding), modifier = Modifier.padding(start = startPadding),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Row( Row(
modifier = Modifier.weight(weight = 1f), modifier = Modifier.weight(weight = 1f),
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) { ) {
Text( Text(
modifier = Modifier.alignByBaseline(), modifier = Modifier.alignByBaseline(),
@ -130,24 +128,18 @@ fun GMCharacter(
), ),
) )
} }
OverflowActionMenu(
character = character,
onAction = onAction,
)
}
Row(
modifier = Modifier
.padding(paddingValues = padding)
.padding(bottom = 8.dp),
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
character.tags.forEach { tag -> character.tags.forEach { tag ->
GMTag( GMTag(
elevation = 4.dp, elevation = 4.dp,
tag = tag, tag = tag,
) )
} }
}
OverflowActionMenu(
character = character,
onAction = onAction,
)
} }
} }
} }

View file

@ -1,7 +1,8 @@
package com.pixelized.desktop.lwa.ui.screen.levelup package com.pixelized.desktop.lwa.ui.screen.levelup
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristicUio import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristicUio
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkillUio import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkillUio
@ -312,7 +313,7 @@ class LevelUpFactory(
) )
}, },
roll = when (results[skill.id]) { roll = when (results[skill.id]) {
null -> RollActionUio( null -> RollAction.Uio.BoundedRollActionUio(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
label = skill.label, label = skill.label,
rollAction = "1d100", rollAction = "1d100",

View file

@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.shapes.MasteryShape import com.pixelized.desktop.lwa.ui.composable.shapes.MasteryShape
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
import com.pixelized.desktop.lwa.ui.theme.lwa import com.pixelized.desktop.lwa.ui.theme.lwa
import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.level_up__skill_level import lwacharactersheet.composeapp.generated.resources.level_up__skill_level
@ -41,7 +41,7 @@ data class LevelUpSkillUio(
val levelUp: Boolean, val levelUp: Boolean,
val occupation: Boolean, val occupation: Boolean,
val tooltips: TooltipUio?, val tooltips: TooltipUio?,
val roll: RollActionUio?, val roll: RollAction.Uio?,
) )
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)

View file

@ -43,7 +43,9 @@ data class CharacterSheet(
val label: String, val label: String,
val description: String?, val description: String?,
val canBeCritical: Boolean, val canBeCritical: Boolean,
val roll: String, val default: String,
val special: String?,
val critical: String?,
) )
object CharacteristicId { object CharacteristicId {

View file

@ -50,5 +50,7 @@ data class CharacterSheetJsonV1(
val description: String?, val description: String?,
val canBeCritical: Boolean, val canBeCritical: Boolean,
val roll: String, val roll: String,
val special: String?,
val critical: String?,
) )
} }

View file

@ -83,7 +83,9 @@ class CharacterSheetJsonFactory(
label = it.label, label = it.label,
description = it.description, description = it.description,
canBeCritical = it.canBeCritical, canBeCritical = it.canBeCritical,
roll = it.roll, roll = it.default,
special = it.special,
critical = it.critical
) )
}, },
) )

View file

@ -70,7 +70,9 @@ class CharacterSheetJsonV1Factory(
label = it.label, label = it.label,
description = it.description, description = it.description,
canBeCritical = it.canBeCritical, canBeCritical = it.canBeCritical,
roll = it.roll, default = it.roll,
special = it.special,
critical = it.critical,
) )
}, },
) )