diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 164a914..4f9d158 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -84,6 +84,7 @@
Points de vie maximum
Points de vie
Points de pouvoir maximum
+ Utiliser l'armure
Points de pouvoir
Bonus aux dégats
Armure
@@ -253,6 +254,7 @@
%1$s passe à %2$d d'état diminuée
%1$s subit %2$d point(s) de dommage
+ %1$s subit %2$d point(s) de dommage (armure : %3$d)
%1$s récupère %2$d point(s) de vie
%1$s utilise %2$d point(s) de pouvoir
%1$s récupère %2$d point(s) de pouvoir
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/button/SignButton.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/button/SignButton.kt
new file mode 100644
index 0000000..5fab8d5
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/button/SignButton.kt
@@ -0,0 +1,58 @@
+package com.pixelized.desktop.lwa.ui.composable.button
+
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
+import com.pixelized.desktop.lwa.ui.theme.lwa
+import kotlinx.coroutines.flow.StateFlow
+import lwacharactersheet.composeapp.generated.resources.Res
+import lwacharactersheet.composeapp.generated.resources.ic_remove_24dp
+import org.jetbrains.compose.resources.painterResource
+
+@Composable
+fun SignButton(
+ modifier: Modifier = Modifier,
+ interactionSource: MutableInteractionSource? = null,
+ enabled: Boolean = true,
+ add: StateFlow,
+ onClick: () -> Unit,
+) {
+ IconButton(
+ onClick = onClick,
+ modifier = modifier,
+ enabled = enabled,
+ interactionSource = interactionSource,
+ ) {
+ val rotation = animateFloatAsState(
+ animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
+ targetValue = when (add.collectAsState().value) {
+ true -> 90f
+ else -> 0f
+ }
+ )
+ Icon(
+ modifier = Modifier.graphicsLayer {
+ this.rotationZ = rotation.value * 2f
+ },
+ painter = painterResource(Res.drawable.ic_remove_24dp),
+ tint = MaterialTheme.lwa.colorScheme.base.primary,
+ contentDescription = null
+ )
+ Icon(
+ modifier = Modifier.graphicsLayer {
+ this.rotationZ = rotation.value * 3f
+ },
+ painter = painterResource(Res.drawable.ic_remove_24dp),
+ tint = MaterialTheme.lwa.colorScheme.base.primary,
+ contentDescription = null
+ )
+ }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialog.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialog.kt
index e4c0dbe..c19946f 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialog.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialog.kt
@@ -1,40 +1,43 @@
package com.pixelized.desktop.lwa.ui.composable.character.characteristic
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.text.BasicTextField
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.TextFieldValue
-import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.type
+import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
-import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
-import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
+import com.pixelized.desktop.lwa.ui.composable.button.SignButton
+import com.pixelized.desktop.lwa.ui.composable.character.LwaDialog
+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.key.KeyHandler
+import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextField
+import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
+import com.pixelized.desktop.lwa.ui.theme.lwa
+import kotlinx.coroutines.flow.StateFlow
import lwacharactersheet.composeapp.generated.resources.Res
+import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__use_armor
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
import org.jetbrains.compose.resources.stringResource
@@ -44,9 +47,10 @@ data class CharacterSheetCharacteristicDialogUio(
val characterSheetId: String,
val characteristic: Characteristic,
val label: String,
- val value: () -> TextFieldValue,
- val onValueChange: (TextFieldValue) -> Unit,
- val maxValue: String,
+ val add: StateFlow,
+ val value: LwaTextFieldUio,
+ val enableArmor: LwaCheckBoxUio?,
+ val enableConfirm: StateFlow,
) {
@Stable
enum class Characteristic {
@@ -55,125 +59,131 @@ data class CharacterSheetCharacteristicDialogUio(
}
}
-@Composable
-fun CharacterSheetCharacteristicDialog(
- dialog: State,
- onConfirm: (CharacterSheetCharacteristicDialogUio) -> Unit,
- onDismissRequest: () -> Unit,
-) {
- dialog.value?.let {
- Dialog(
- onDismissRequest = onDismissRequest,
- content = {
- CharacterSheetCharacteristicContent(
- dialog = it,
- onConfirm = onConfirm,
- onDismissRequest = onDismissRequest,
- )
- }
- )
- }
+@Stable
+object CharacterSheetCharacteristicDialogDefault {
+ @Stable
+ val paddings: PaddingValues =
+ PaddingValues(top = 16.dp, start = 8.dp, end = 8.dp, bottom = 8.dp)
+
+ @Stable
+ val spacings: DpSize = DpSize(width = 4.dp, height = 8.dp)
}
@Composable
-private fun CharacterSheetCharacteristicContent(
- dialog: CharacterSheetCharacteristicDialogUio,
+fun CharacterSheetCharacteristicDialog(
+ dialog: State,
+ paddings: PaddingValues = CharacterSheetCharacteristicDialogDefault.paddings,
+ spacings: DpSize = CharacterSheetCharacteristicDialogDefault.spacings,
onConfirm: (CharacterSheetCharacteristicDialogUio) -> Unit,
+ onSwapSign: (CharacterSheetCharacteristicDialogUio) -> Unit,
onDismissRequest: () -> Unit,
) {
- val typography = MaterialTheme.typography
- val colors = MaterialTheme.colors
+ LwaDialog(
+ state = dialog,
+ onDismissRequest = onDismissRequest,
+ onConfirm = { dialog.value?.let(onConfirm) }
+ ) { state ->
+ val focusRequester = remember { FocusRequester() }
+ LaunchedEffect(Unit) { focusRequester.requestFocus() }
- val focusRequester = remember { FocusRequester() }
- LaunchedEffect(Unit) {
- focusRequester.requestFocus()
- }
+ CharacterSheetCharacteristicDialogKeyHandler(
+ onSwap = { onSwapSign(state) },
+ )
- Box(
- modifier = Modifier
- .clickable(
- interactionSource = remember { MutableInteractionSource() },
- indication = null,
- onClick = onDismissRequest,
+ Column(
+ modifier = Modifier.padding(paddingValues = paddings),
+ verticalArrangement = Arrangement.spacedBy(space = spacings.height),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Text(
+ style = MaterialTheme.typography.caption,
+ text = state.label,
)
- .onPreviewEscape(
- escape = onDismissRequest,
- enter = { onConfirm(dialog) },
- )
- .fillMaxSize()
- .padding(all = 32.dp),
- contentAlignment = Alignment.Center,
- ) {
- DecoratedBox {
- Surface {
- Column(
- verticalArrangement = Arrangement.spacedBy(space = 8.dp),
- horizontalAlignment = Alignment.CenterHorizontally,
+
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(space = spacings.width),
+ verticalAlignment = Alignment.Bottom,
+ ) {
+ SignButton(
+ modifier = Modifier
+ .size(size = 56.dp)
+ .background(
+ color = MaterialTheme.lwa.colorScheme.elevated.base1dp,
+ shape = MaterialTheme.shapes.small,
+ ),
+ add = state.add,
+ onClick = { onSwapSign(state) },
+ )
+ LwaTextField(
+ modifier = Modifier
+ .focusRequester(focusRequester = focusRequester)
+ .width(192.dp),
+ field = state.value,
+ )
+ }
+ state.enableArmor?.let {
+ Row (
+ modifier = Modifier.align(alignment = Alignment.End),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(
+ space = spacings.width,
+ alignment = Alignment.End
+ ),
) {
Text(
- modifier = Modifier.padding(top = 16.dp, start = 24.dp, end = 24.dp),
style = MaterialTheme.typography.caption,
- text = dialog.label,
+ text = stringResource(Res.string.character_sheet_edit__use_armor),
+ )
+ LwaCheckBox(
+ field = it,
+ )
+ }
+ }
+ Row(
+ modifier = Modifier.align(alignment = Alignment.End),
+ horizontalArrangement = Arrangement.spacedBy(
+ space = spacings.width,
+ alignment = Alignment.End
+ )
+ ) {
+ TextButton(
+ onClick = onDismissRequest,
+ ) {
+ Text(
+ color = MaterialTheme.colors.primary.copy(alpha = .7f),
+ text = stringResource(Res.string.dialog__cancel_action)
+ )
+ }
+ TextButton(
+ enabled = state.enableConfirm.collectAsState().value,
+ onClick = { onConfirm(state) },
+ ) {
+ Text(
+ text = stringResource(Res.string.dialog__confirm_action)
)
- Row(
- horizontalArrangement = Arrangement.spacedBy(4.dp),
- ) {
- BasicTextField(
- modifier = Modifier
- .focusRequester(focusRequester = focusRequester)
- .alignByBaseline()
- .width(width = 120.dp),
- textStyle = remember {
- typography.h5.copy(
- color = colors.primary,
- textAlign = TextAlign.End,
- fontWeight = FontWeight.Bold
- )
- },
- cursorBrush = SolidColor(MaterialTheme.colors.primary),
- singleLine = true,
- keyboardActions = KeyboardActions { onConfirm(dialog) },
- keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
- value = dialog.value(),
- onValueChange = dialog.onValueChange,
- )
- Text(
- modifier = Modifier.alignByBaseline(),
- text = "/",
- )
- Text(
- modifier = Modifier.alignByBaseline(),
- text = dialog.maxValue,
- )
- }
- Row(
- modifier = Modifier
- .padding(bottom = 4.dp)
- .padding(horizontal = 16.dp)
- .align(alignment = Alignment.End),
- horizontalArrangement = Arrangement.spacedBy(
- space = 4.dp,
- alignment = Alignment.End
- )
- ) {
- TextButton(
- onClick = onDismissRequest,
- ) {
- Text(
- color = MaterialTheme.colors.primary.copy(alpha = .7f),
- text = stringResource(Res.string.dialog__cancel_action)
- )
- }
- TextButton(
- onClick = { onConfirm(dialog) },
- ) {
- Text(
- text = stringResource(Res.string.dialog__confirm_action)
- )
- }
- }
}
}
}
}
+}
+
+
+@Composable
+private fun CharacterSheetCharacteristicDialogKeyHandler(
+ onSwap: () -> Unit,
+) {
+ KeyHandler {
+ if (it.type == KeyEventType.KeyDown) {
+ when (it.key) {
+ Key.AltLeft, Key.AltRight -> {
+ onSwap()
+ true
+ }
+
+ else -> false
+ }
+ } else {
+ false
+ }
+ }
}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialogFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialogFactory.kt
index 34746a4..c995855 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialogFactory.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialogFactory.kt
@@ -1,15 +1,22 @@
package com.pixelized.desktop.lwa.ui.composable.character.characteristic
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.text.TextRange
-import androidx.compose.ui.text.input.TextFieldValue
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogUio.Characteristic
+import com.pixelized.desktop.lwa.ui.composable.checkbox.createLwaCheckBox
+import com.pixelized.desktop.lwa.ui.composable.checkbox.createLwaCheckBoxFlow
+import com.pixelized.desktop.lwa.ui.composable.textfield.createLwaTextField
+import com.pixelized.desktop.lwa.ui.composable.textfield.createLwaTextFieldFlow
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
import lwacharactersheet.composeapp.generated.resources.Res
+import lwacharactersheet.composeapp.generated.resources.character__inventory__add_to_purse__title
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__hit_point
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__power_point
import org.jetbrains.compose.resources.getString
@@ -19,9 +26,13 @@ class CharacterSheetCharacteristicDialogFactory(
private val alterationRepository: AlterationRepository,
private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory,
) {
+ private val decimalChecker = Regex("""^\s*\d*\s*${'$'}""")
+
suspend fun convertToDialogUio(
+ scope: CoroutineScope,
characterSheetId: String?,
characteristic: Characteristic,
+ signFlow: StateFlow,
): CharacterSheetCharacteristicDialogUio? {
if (characterSheetId == null) return null
@@ -32,10 +43,10 @@ class CharacterSheetCharacteristicDialogFactory(
if (characterSheet == null) return null
- val alterations: Map> = alterationRepository.activeFieldAlterations(
- characterSheetId = characterSheetId,
- )
-
+ val alterations: Map> =
+ alterationRepository.activeFieldAlterations(
+ characterSheetId = characterSheetId,
+ )
val alteredCharacterSheet = alteredCharacterSheetFactory.sheet(
characterSheet = characterSheet,
alterations = alterations,
@@ -43,36 +54,61 @@ class CharacterSheetCharacteristicDialogFactory(
return when (characteristic) {
Characteristic.Damage -> {
- val value = mutableStateOf(
- "${alteredCharacterSheet.maxHp - alteredCharacterSheet.damage}".let {
- TextFieldValue(text = it, selection = TextRange(it.length))
- }
+ val valueFlow = createLwaTextFieldFlow(
+ label = null,
)
+ val armor = if (alteredCharacterSheet.armor > 0) {
+ createLwaCheckBoxFlow(
+ checked = true,
+ )
+ } else {
+ null
+ }
CharacterSheetCharacteristicDialogUio(
characterSheetId = characterSheetId,
characteristic = characteristic,
- label = getString(resource = Res.string.character_sheet_edit__sub_characteristics__hit_point),
- value = { value.value },
- onValueChange = { value.value = it },
- maxValue = "${alteredCharacterSheet.maxHp}",
+ label = getString(Res.string.character_sheet_edit__sub_characteristics__hit_point).let {
+ "$it : ${alteredCharacterSheet.maxHp - alteredCharacterSheet.damage}/${alteredCharacterSheet.maxHp}"
+ },
+ add = signFlow,
+ value = valueFlow.createLwaTextField {
+ valueFlow.errorFlow.value = check(it)
+ valueFlow.valueFlow.value = it
+ },
+ enableArmor = armor?.createLwaCheckBox(),
+ enableConfirm = valueFlow.errorFlow.map { it.not() }.stateIn(
+ scope = scope,
+ started = SharingStarted.Lazily,
+ initialValue = valueFlow.errorFlow.value.not(),
+ ),
)
}
Characteristic.Fatigue -> {
- val value = mutableStateOf(
- "${alteredCharacterSheet.maxPp - alteredCharacterSheet.fatigue}".let {
- TextFieldValue(text = it, selection = TextRange(it.length))
- }
+ val valueFlow = createLwaTextFieldFlow(
+ label = null,
)
CharacterSheetCharacteristicDialogUio(
characterSheetId = characterSheetId,
characteristic = characteristic,
- label = getString(resource = Res.string.character_sheet_edit__sub_characteristics__power_point),
- value = { value.value },
- onValueChange = { value.value = it },
- maxValue = "${alteredCharacterSheet.maxPp}",
+ label = getString(Res.string.character_sheet_edit__sub_characteristics__power_point).let {
+ "$it : ${alteredCharacterSheet.maxPp - alteredCharacterSheet.fatigue}/${alteredCharacterSheet.maxPp}"
+ },
+ add = signFlow,
+ value = valueFlow.createLwaTextField {
+ valueFlow.errorFlow.value = check(it)
+ valueFlow.valueFlow.value = it
+ },
+ enableArmor = null,
+ enableConfirm = valueFlow.errorFlow.map { it.not() }.stateIn(
+ scope = scope,
+ started = SharingStarted.Lazily,
+ initialValue = valueFlow.errorFlow.value.not(),
+ ),
)
}
}
}
+
+ private fun check(value: String): Boolean = !decimalChecker.matches(value)
}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialogViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialogViewModel.kt
index 9ea4c93..7e104d4 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialogViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/characteristic/CharacterSheetCharacteristicDialogViewModel.kt
@@ -3,12 +3,15 @@ package com.pixelized.desktop.lwa.ui.composable.character.characteristic
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogUio.Characteristic
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.update
class CharacterSheetCharacteristicDialogViewModel(
private val characterSheetRepository: CharacterSheetRepository,
@@ -18,6 +21,8 @@ class CharacterSheetCharacteristicDialogViewModel(
private val network: NetworkRepository,
) : ViewModel() {
+ private val signFlow = MutableStateFlow(false)
+
private val _statChangeDialog = mutableStateOf(null)
val statChangeDialog: State get() = _statChangeDialog
@@ -25,19 +30,27 @@ class CharacterSheetCharacteristicDialogViewModel(
_statChangeDialog.value = null
}
+ fun swapCharacteristicSign() {
+ signFlow.update { it.not() }
+ }
+
suspend fun showSubCharacteristicDialog(
characterSheetId: String?,
characteristic: Characteristic,
) {
+ signFlow.update { false }
_statChangeDialog.value = factory.convertToDialogUio(
+ scope = viewModelScope,
characterSheetId = characterSheetId,
characteristic = characteristic,
+ signFlow = signFlow,
)
}
suspend fun changeSubCharacteristic(
characterSheetId: String?,
characteristic: Characteristic,
+ useArmor: Boolean,
value: Int,
) {
if (characterSheetId == null) return
@@ -49,29 +62,37 @@ class CharacterSheetCharacteristicDialogViewModel(
if (characterSheet == null) return
- val alterations = alterationRepository.activeFieldAlterations(
- characterSheetId = characterSheetId,
- )
-
- // we need the maximum HP / Power that the character sheet have.
- val alteredSheet = alteredCharacterSheetFactory.sheet(
- characterSheet = characterSheet,
- alterations = alterations,
- )
+ val sign = if (signFlow.value) 1 else -1
val message = when (characteristic) {
- Characteristic.Damage -> CharacterSheetEvent.UpdateDamage(
- timestamp = System.currentTimeMillis(),
- characterSheetId = characterSheetId,
- oldValue = alteredSheet.damage,
- damage = alteredSheet.maxHp - value,
- )
+ Characteristic.Damage -> {
+ val armor = if (useArmor && signFlow.value.not()) {
+ val alterations = alterationRepository.activeFieldAlterations(
+ characterSheetId = characterSheetId,
+ )
+ val alteredSheet = alteredCharacterSheetFactory.sheet(
+ characterSheet = characterSheet,
+ alterations = alterations,
+ )
+ alteredSheet.armor
+ } else {
+ null
+ }
+
+ CharacterSheetEvent.UpdateDamage(
+ timestamp = System.currentTimeMillis(),
+ characterSheetId = characterSheetId,
+ oldValue = characterSheet.damage,
+ damage = characterSheet.damage - value * sign - (armor ?: 0),
+ armor = armor,
+ )
+ }
Characteristic.Fatigue -> CharacterSheetEvent.UpdateFatigue(
timestamp = System.currentTimeMillis(),
characterSheetId = characterSheetId,
- oldValue = alteredSheet.fatigue,
- fatigue = alteredSheet.maxPp - value,
+ oldValue = characterSheet.fatigue,
+ fatigue = characterSheet.fatigue - value * sign,
)
}
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/purse/PurseDialog.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/purse/PurseDialog.kt
index 8a6e708..9c4c360 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/purse/PurseDialog.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/character/purse/PurseDialog.kt
@@ -1,9 +1,6 @@
package com.pixelized.desktop.lwa.ui.composable.character.purse
import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.core.Spring
-import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.spring
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
@@ -19,8 +16,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
@@ -35,13 +30,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
+import com.pixelized.desktop.lwa.ui.composable.button.SignButton
import com.pixelized.desktop.lwa.ui.composable.character.LwaDialog
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
@@ -53,8 +48,6 @@ import kotlinx.coroutines.flow.StateFlow
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
-import lwacharactersheet.composeapp.generated.resources.ic_remove_24dp
-import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
@Stable
@@ -94,6 +87,10 @@ fun PurseDialog(
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) { focusRequester.requestFocus() }
+ PurseDialogKeyHandler(
+ onSwap = { onSwapSign(state) },
+ )
+
Column(
modifier = Modifier.padding(paddingValues = paddings),
verticalArrangement = Arrangement.spacedBy(space = spacings.height),
@@ -162,10 +159,6 @@ fun PurseDialog(
}
}
}
-
- PurseDialogKeyHandler(
- onSwap = { onSwapSign(state) },
- )
}
}
@@ -202,46 +195,6 @@ private fun PurseContent(
}
}
-@Composable
-private fun SignButton(
- modifier: Modifier = Modifier,
- interactionSource: MutableInteractionSource? = null,
- enabled: Boolean = true,
- add: StateFlow,
- onClick: () -> Unit,
-) {
- IconButton(
- onClick = onClick,
- modifier = modifier,
- enabled = enabled,
- interactionSource = interactionSource,
- ) {
- val rotation = animateFloatAsState(
- animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
- targetValue = when (add.collectAsState().value) {
- true -> 90f
- else -> 0f
- }
- )
- Icon(
- modifier = Modifier.graphicsLayer {
- this.rotationZ = rotation.value * 2f
- },
- painter = painterResource(Res.drawable.ic_remove_24dp),
- tint = MaterialTheme.lwa.colorScheme.base.primary,
- contentDescription = null
- )
- Icon(
- modifier = Modifier.graphicsLayer {
- this.rotationZ = rotation.value * 3f
- },
- painter = painterResource(Res.drawable.ic_remove_24dp),
- tint = MaterialTheme.lwa.colorScheme.base.primary,
- contentDescription = null
- )
- }
-}
-
@Composable
private fun PurseDialogKeyHandler(
onSwap: () -> Unit,
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/checkbox/LwaCheckBoxHelper.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/checkbox/LwaCheckBoxHelper.kt
new file mode 100644
index 0000000..7caea00
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/checkbox/LwaCheckBoxHelper.kt
@@ -0,0 +1,30 @@
+package com.pixelized.desktop.lwa.ui.composable.checkbox
+
+import androidx.compose.runtime.Stable
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@Stable
+data class LwaCheckBoxFlow(
+ val checkedFlow: MutableStateFlow,
+)
+
+@Stable
+fun createLwaCheckBoxFlow(
+ checked: Boolean,
+): LwaCheckBoxFlow {
+ return LwaCheckBoxFlow(
+ checkedFlow = MutableStateFlow(checked),
+ )
+}
+
+@Stable
+fun LwaCheckBoxFlow.createLwaCheckBox(
+ enable: Boolean = true,
+ onCheckedChange: (Boolean) -> Unit = { checkedFlow.value = it },
+): LwaCheckBoxUio {
+ return LwaCheckBoxUio(
+ enable = enable,
+ checked = checkedFlow,
+ onCheckedChange = onCheckedChange,
+ )
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/textfield/LwaTextFieldHelper.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/textfield/LwaTextFieldHelper.kt
index 9bc4fe0..b1e88a8 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/textfield/LwaTextFieldHelper.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/textfield/LwaTextFieldHelper.kt
@@ -6,13 +6,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
@Stable
data class LwaTextFieldFlow(
val errorFlow: MutableStateFlow,
- val valueFlow: MutableStateFlow,
val labelFlow: MutableStateFlow,
+ val valueFlow: MutableStateFlow,
)
fun createLwaTextFieldFlow(
error: Boolean = false,
- label: String,
+ label: String?,
value: String = "",
): LwaTextFieldFlow {
return createLwaTextFieldFlow(
@@ -24,8 +24,8 @@ fun createLwaTextFieldFlow(
fun createLwaTextFieldFlow(
errorFlow: MutableStateFlow = MutableStateFlow(false),
- valueFlow: MutableStateFlow,
labelFlow: MutableStateFlow,
+ valueFlow: MutableStateFlow,
): LwaTextFieldFlow {
return LwaTextFieldFlow(
errorFlow = errorFlow,
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt
index 0fafa77..c18e99b 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt
@@ -206,12 +206,16 @@ fun CampaignScreen(
characteristicDialogViewModel.changeSubCharacteristic(
characterSheetId = dialog.characterSheetId,
characteristic = dialog.characteristic,
- value = dialog.value().text.toIntOrNull() ?: 0,
+ useArmor= dialog.enableArmor?.checked?.value == true,
+ value = dialog.value.valueFlow.value.toIntOrNull() ?: 0,
)
characteristicDialogViewModel.hideSubCharacteristicDialog()
blurController.hide()
}
},
+ onSwapSign = {
+ characteristicDialogViewModel.swapCharacteristicSign()
+ },
onDismissRequest = {
characteristicDialogViewModel.hideSubCharacteristicDialog()
blurController.hide()
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt
index cbb6ece..373c3a4 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt
@@ -17,6 +17,7 @@ import com.pixelized.shared.lwa.protocol.websocket.RollEvent
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_down
+import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_down_armor
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_up
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__pp_down
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__pp_up
@@ -94,16 +95,19 @@ class TextMessageFactory(
?: return null
// We are talking about damage / consumption there so old value have to be put first.
val value = message.oldValue - message.damage
+ val armor = message.armor
CharacteristicTextMessageUio(
id = "${message.timestamp}-${message.characterSheetId}-Damage",
timestamp = formatTime.format(message.timestamp),
label = when {
- value < 0 -> Res.string.chat__characteristic_change__hp_down
- else -> Res.string.chat__characteristic_change__hp_up
+ value >= 0 -> Res.string.chat__characteristic_change__hp_up
+ armor != null -> Res.string.chat__characteristic_change__hp_down_armor
+ else -> Res.string.chat__characteristic_change__hp_down
},
character = sheet.name,
value = abs(value),
+ armor = message.armor,
)
}
@@ -127,6 +131,7 @@ class TextMessageFactory(
},
character = sheet.name,
value = abs(value),
+ armor = 0,
)
}
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/CharacteristicTextMessage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/CharacteristicTextMessage.kt
index 9e31927..40568f8 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/CharacteristicTextMessage.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/CharacteristicTextMessage.kt
@@ -21,6 +21,7 @@ data class CharacteristicTextMessageUio(
val label: StringResource,
val character: String,
val value: Int,
+ val armor: Int?,
) : TextMessage
@Composable
@@ -52,7 +53,7 @@ fun CharacteristicTextMessage(
style = MaterialTheme.lwa.typography.chat.text,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
- text = stringResource(message.label, message.character, message.value),
+ text = stringResource(message.label, message.character, message.value, message.armor ?: 0),
)
}
}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/list/GMCharacterPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/list/GMCharacterPage.kt
index cb1bdb9..79899c8 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/list/GMCharacterPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/list/GMCharacterPage.kt
@@ -126,12 +126,16 @@ fun GMCharacterPage(
characteristicDialogViewModel.changeSubCharacteristic(
characterSheetId = dialog.characterSheetId,
characteristic = dialog.characteristic,
- value = dialog.value().text.toIntOrNull() ?: 0,
+ useArmor= dialog.enableArmor?.checked?.value == true,
+ value = dialog.value.valueFlow.value.toIntOrNull() ?: 0,
)
characteristicDialogViewModel.hideSubCharacteristicDialog()
blurController.hide()
}
},
+ onSwapSign = {
+ characteristicDialogViewModel.swapCharacteristicSign()
+ },
onDismissRequest = {
characteristicDialogViewModel.hideSubCharacteristicDialog()
blurController.hide()
diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Damage.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Damage.kt
index 5c8094d..aa60fd6 100644
--- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Damage.kt
+++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Damage.kt
@@ -18,6 +18,10 @@ fun Engine.putCharacterDamage(): suspend RoutingContext.() -> Unit {
name = "damage",
code = APIResponse.ErrorCode.Damage,
)
+ val armor: Int = call.queryParameters.param(
+ name = "armor",
+ code = APIResponse.ErrorCode.Armor,
+ )
// fetch the character sheet
val characterSheet = characterService.character(characterSheetId)
?: error("CharacterSheet with id:$characterSheetId not found.")
@@ -36,6 +40,7 @@ fun Engine.putCharacterDamage(): suspend RoutingContext.() -> Unit {
characterSheetId = characterSheetId,
oldValue = characterSheet.damage,
damage = damage,
+ armor = armor,
)
)
} catch (exception: Exception) {
diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/rest/APIResponse.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/rest/APIResponse.kt
index b058d0d..b58c0e9 100644
--- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/rest/APIResponse.kt
+++ b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/rest/APIResponse.kt
@@ -22,6 +22,7 @@ data class APIResponse(
Active,
Equip,
Damage,
+ Armor,
Fatigue,
Diminished,
Count,
diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/CharacterSheetEvent.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/CharacterSheetEvent.kt
index 408d131..37c24d2 100644
--- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/CharacterSheetEvent.kt
+++ b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/CharacterSheetEvent.kt
@@ -9,6 +9,7 @@ sealed interface CharacterSheetEvent : SocketMessage, CharacterSheetIdMessage {
data class UpdateDamage(
override val characterSheetId: String,
override val timestamp: Long,
+ val armor: Int?,
val oldValue: Int,
val damage: Int,
) : CharacterSheetEvent