Add PP and HP managmenet
This commit is contained in:
parent
f6d026d50f
commit
3705fbd947
5 changed files with 305 additions and 8 deletions
|
|
@ -4,66 +4,105 @@ import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet
|
|||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio.Node
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.*
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__cha
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__con
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__dex
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__hei
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__int
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__pow
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__str
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__armor
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__damage_bonus
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__hit_point
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__movement
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__power_point
|
||||
import org.jetbrains.compose.resources.getString
|
||||
|
||||
class CharacterSheetFactory {
|
||||
|
||||
companion object {
|
||||
const val HP = "HP"
|
||||
const val PP = "PP"
|
||||
}
|
||||
|
||||
suspend fun convertToUio(model: CharacterSheet): CharacterSheetPageUio {
|
||||
return CharacterSheetPageUio(
|
||||
id = model.id,
|
||||
name = model.name,
|
||||
characteristics = listOf(
|
||||
Characteristic(
|
||||
id = "STR",
|
||||
label = getString(Res.string.character_sheet__characteristics__str),
|
||||
value = "${model.strength}",
|
||||
editable = false,
|
||||
),
|
||||
Characteristic(
|
||||
id = "DEX",
|
||||
label = getString(Res.string.character_sheet__characteristics__dex),
|
||||
value = "${model.dexterity}",
|
||||
editable = false,
|
||||
),
|
||||
Characteristic(
|
||||
id = "CON",
|
||||
label = getString(Res.string.character_sheet__characteristics__con),
|
||||
value = "${model.constitution}",
|
||||
editable = false,
|
||||
),
|
||||
Characteristic(
|
||||
id = "HEI",
|
||||
label = getString(Res.string.character_sheet__characteristics__hei),
|
||||
value = "${model.height}",
|
||||
editable = false,
|
||||
),
|
||||
Characteristic(
|
||||
id = "INT",
|
||||
label = getString(Res.string.character_sheet__characteristics__int),
|
||||
value = "${model.intelligence}",
|
||||
editable = false,
|
||||
),
|
||||
Characteristic(
|
||||
id = "POW",
|
||||
label = getString(Res.string.character_sheet__characteristics__pow),
|
||||
value = "${model.power}",
|
||||
editable = false,
|
||||
),
|
||||
Characteristic(
|
||||
id = "CHA",
|
||||
label = getString(Res.string.character_sheet__characteristics__cha),
|
||||
value = "${model.charisma}",
|
||||
editable = false,
|
||||
),
|
||||
),
|
||||
subCharacteristics = listOf(
|
||||
Characteristic(
|
||||
id = "MOV",
|
||||
label = getString(Res.string.character_sheet__sub_characteristics__movement),
|
||||
value = "${model.movement}",
|
||||
editable = false,
|
||||
),
|
||||
Characteristic(
|
||||
id = HP,
|
||||
label = getString(Res.string.character_sheet__sub_characteristics__hit_point),
|
||||
value = "${model.currentHp}/${model.maxHp}",
|
||||
editable = true,
|
||||
),
|
||||
Characteristic(
|
||||
id = PP,
|
||||
label = getString(Res.string.character_sheet__sub_characteristics__power_point),
|
||||
value = "${model.currentPP}/${model.maxPP}",
|
||||
editable = true,
|
||||
),
|
||||
Characteristic(
|
||||
id = "DMG",
|
||||
label = getString(Res.string.character_sheet__sub_characteristics__damage_bonus),
|
||||
value = model.damageBonus,
|
||||
editable = false,
|
||||
),
|
||||
Characteristic(
|
||||
id = "ARMOR",
|
||||
label = getString(Res.string.character_sheet__sub_characteristics__armor),
|
||||
value = "${model.armor}",
|
||||
editable = false,
|
||||
),
|
||||
),
|
||||
skills = model.skills.mapNotNull {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ import com.pixelized.desktop.lwa.composable.overlay.BlurOverlay
|
|||
import com.pixelized.desktop.lwa.composable.overlay.BlurOverlayViewModel
|
||||
import com.pixelized.desktop.lwa.navigation.LocalScreenController
|
||||
import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheetEdit
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog.CharacterSheetDeleteConfirmationDialog
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog.CharacterSheetStatDialog
|
||||
import com.pixelized.desktop.lwa.screen.roll.RollPage
|
||||
import com.pixelized.desktop.lwa.screen.roll.RollViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -70,8 +72,10 @@ data class CharacterSheetPageUio(
|
|||
) {
|
||||
@Stable
|
||||
data class Characteristic(
|
||||
val id: String,
|
||||
val label: String,
|
||||
val value: String,
|
||||
val editable: Boolean,
|
||||
)
|
||||
|
||||
@Stable
|
||||
|
|
@ -132,6 +136,9 @@ fun CharacterSheetPage(
|
|||
)
|
||||
overlayViewModel.show()
|
||||
},
|
||||
onSubCharacteristic = {
|
||||
viewModel.showSubCharacteristicDialog(id = it.id)
|
||||
},
|
||||
onSkill = { node ->
|
||||
rollViewModel.prepareRoll(sheet = sheet, node = node)
|
||||
overlayViewModel.show()
|
||||
|
|
@ -161,6 +168,20 @@ fun CharacterSheetPage(
|
|||
viewModel.hideConfirmCharacterDeletionDialog()
|
||||
},
|
||||
)
|
||||
|
||||
CharacterSheetStatDialog(
|
||||
dialog = viewModel.statChangeDialog,
|
||||
onConfirm = {
|
||||
viewModel.changeSubCharacteristic(
|
||||
characteristicId = it.id,
|
||||
value = it.value().text.toIntOrNull() ?: 0,
|
||||
)
|
||||
viewModel.hideSubCharacteristicDialog()
|
||||
},
|
||||
onDismissRequest = {
|
||||
viewModel.hideSubCharacteristicDialog()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -171,6 +192,7 @@ fun CharacterSheetPageContent(
|
|||
onEdit: () -> Unit,
|
||||
onDelete: () -> Unit,
|
||||
onCharacteristic: (characteristic: CharacterSheetPageUio.Characteristic) -> Unit,
|
||||
onSubCharacteristic: (characteristic: CharacterSheetPageUio.Characteristic) -> Unit,
|
||||
onSkill: (skill: CharacterSheetPageUio.Node) -> Unit,
|
||||
onUseSkill: (skill: CharacterSheetPageUio.Node) -> Unit,
|
||||
onRoll: (roll: CharacterSheetPageUio.Roll) -> Unit,
|
||||
|
|
@ -252,9 +274,13 @@ fun CharacterSheetPageContent(
|
|||
text = stringResource(Res.string.character_sheet__sub_characteristics__title),
|
||||
)
|
||||
characterSheet.subCharacteristics.forEach {
|
||||
Characteristics(
|
||||
SubCharacteristics(
|
||||
modifier = Modifier.cell(),
|
||||
characteristic = it,
|
||||
onCharacteristic = when {
|
||||
it.editable -> onSubCharacteristic
|
||||
else -> null
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -372,24 +398,32 @@ private fun Stat(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun Characteristics(
|
||||
private fun SubCharacteristics(
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp),
|
||||
characteristic: CharacterSheetPageUio.Characteristic,
|
||||
onCharacteristic: ((characteristic: CharacterSheetPageUio.Characteristic) -> Unit)?,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(paddingValues = paddingValues).then(other = modifier),
|
||||
modifier = Modifier
|
||||
.clickable(enabled = characteristic.editable && onCharacteristic != null) {
|
||||
onCharacteristic?.invoke(characteristic)
|
||||
}
|
||||
.padding(paddingValues = paddingValues).then(other = modifier),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.body1,
|
||||
text = characteristic.label
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.body1,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = when (characteristic.editable && onCharacteristic != null) {
|
||||
true -> MaterialTheme.colors.primary
|
||||
else -> MaterialTheme.colors.onSurface
|
||||
},
|
||||
text = characteristic.value,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,18 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetDestination
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog.CharacterSheetDeleteConfirmationDialogUio
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog.StatChangeDialogUio
|
||||
import com.pixelized.desktop.lwa.utils.extention.collectAsState
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class CharacterSheetViewModel(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
|
|
@ -24,6 +30,9 @@ class CharacterSheetViewModel(
|
|||
val displayDeleteConfirmationDialog: State<CharacterSheetDeleteConfirmationDialogUio?>
|
||||
get() = _displayDeleteConfirmationDialog
|
||||
|
||||
private val _statChangeDialog = mutableStateOf<StatChangeDialogUio?>(null)
|
||||
val statChangeDialog: State<StatChangeDialogUio?> get() = _statChangeDialog
|
||||
|
||||
val sheet: State<CharacterSheetPageUio?>
|
||||
@Composable
|
||||
@Stable
|
||||
|
|
@ -35,7 +44,7 @@ class CharacterSheetViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun deleteCharacter(id: String) {
|
||||
fun deleteCharacter(id: String) {
|
||||
repository.delete(id = id)
|
||||
}
|
||||
|
||||
|
|
@ -74,4 +83,61 @@ class CharacterSheetViewModel(
|
|||
fun hideConfirmCharacterDeletionDialog() {
|
||||
_displayDeleteConfirmationDialog.value = null
|
||||
}
|
||||
|
||||
fun showSubCharacteristicDialog(id: String) {
|
||||
repository.characterSheetFlow(id = argument.id).value?.let { sheet ->
|
||||
_statChangeDialog.value = when (id) {
|
||||
CharacterSheetFactory.HP -> {
|
||||
val value = mutableStateOf(
|
||||
"${sheet.currentHp}".let {
|
||||
TextFieldValue(text = it, selection = TextRange(it.length))
|
||||
}
|
||||
)
|
||||
StatChangeDialogUio(
|
||||
id = CharacterSheetFactory.HP,
|
||||
value = { value.value },
|
||||
onValueChange = { value.value = it },
|
||||
placeholder = "${sheet.currentHp}",
|
||||
maxValue = "${sheet.maxHp}",
|
||||
)
|
||||
}
|
||||
|
||||
CharacterSheetFactory.PP -> {
|
||||
val value = mutableStateOf(
|
||||
"${sheet.currentPP}".let {
|
||||
TextFieldValue(text = it, selection = TextRange(it.length))
|
||||
}
|
||||
)
|
||||
StatChangeDialogUio(
|
||||
id = CharacterSheetFactory.PP,
|
||||
value = { value.value },
|
||||
onValueChange = { value.value = it },
|
||||
placeholder = "${sheet.currentPP}",
|
||||
maxValue = "${sheet.maxPP}",
|
||||
)
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun hideSubCharacteristicDialog() {
|
||||
_statChangeDialog.value = null
|
||||
}
|
||||
|
||||
fun changeSubCharacteristic(
|
||||
characteristicId: String,
|
||||
value: Int,
|
||||
) {
|
||||
val sheet = repository.characterSheetFlow(id = argument.id).value
|
||||
val updated = when (characteristicId) {
|
||||
CharacterSheetFactory.HP -> sheet?.copy(currentHp = max(0, min(sheet.maxHp, value)))
|
||||
CharacterSheetFactory.PP -> sheet?.copy(currentPP = max(0, min(sheet.maxPP, value)))
|
||||
else -> null
|
||||
}
|
||||
updated?.let {
|
||||
repository.save(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.desktop.lwa.screen.characterSheet.detail
|
||||
package com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
package com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.SizeTransform
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
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.Color
|
||||
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.unit.dp
|
||||
|
||||
private val DefaultScrimColor = Color.Black.copy(alpha = 0.6f)
|
||||
|
||||
@Stable
|
||||
data class StatChangeDialogUio(
|
||||
val id: String,
|
||||
val value: () -> TextFieldValue,
|
||||
val placeholder: String,
|
||||
val onValueChange: (TextFieldValue) -> Unit,
|
||||
val maxValue: String,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun CharacterSheetStatDialog(
|
||||
dialog: State<StatChangeDialogUio?>,
|
||||
onConfirm: (StatChangeDialogUio) -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = dialog.value != null,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = DefaultScrimColor),
|
||||
)
|
||||
}
|
||||
AnimatedContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
targetState = dialog.value,
|
||||
transitionSpec = {
|
||||
val enter = fadeIn() + slideInVertically { 32 }
|
||||
val exit = fadeOut() + slideOutVertically { 32 }
|
||||
enter togetherWith exit using SizeTransform(clip = false)
|
||||
},
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
when (it) {
|
||||
null -> Box(
|
||||
modifier = Modifier,
|
||||
)
|
||||
|
||||
else -> Dialog(
|
||||
dialog = it,
|
||||
onConfirm = onConfirm,
|
||||
onDismissRequest = onDismissRequest,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Dialog(
|
||||
dialog: StatChangeDialogUio,
|
||||
onConfirm: (StatChangeDialogUio) -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
val typography = MaterialTheme.typography
|
||||
val colors = MaterialTheme.colors
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = onDismissRequest,
|
||||
)
|
||||
.fillMaxSize()
|
||||
.padding(all = 32.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Surface(
|
||||
shape = remember { RoundedCornerShape(size = 16.dp) },
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(all = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester = focusRequester)
|
||||
.alignByBaseline()
|
||||
.width(width = 120.dp),
|
||||
value = dialog.value(),
|
||||
textStyle = remember {
|
||||
typography.body1.copy(
|
||||
color = colors.primary,
|
||||
textAlign = TextAlign.End,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
keyboardActions = KeyboardActions { onConfirm(dialog) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
placeholder = { dialog.placeholder },
|
||||
onValueChange = dialog.onValueChange,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
text = "/",
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
text = dialog.maxValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue