Add characteristic to the campagin detail panel.
This commit is contained in:
parent
29747dcb5c
commit
b6b135cd40
12 changed files with 690 additions and 331 deletions
|
|
@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
|
||||||
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 androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.BlurredEdgeTreatment
|
import androidx.compose.ui.draw.BlurredEdgeTreatment
|
||||||
import androidx.compose.ui.draw.blur
|
import androidx.compose.ui.draw.blur
|
||||||
|
|
@ -36,6 +37,20 @@ class BlurContentController(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
fun rememberBlurContentController(
|
||||||
|
blurred: Boolean = false,
|
||||||
|
blurredRadius: Dp = 8.dp,
|
||||||
|
scrimColor: Color = LwaColorPalette.DefaultScrimColor,
|
||||||
|
) = remember {
|
||||||
|
BlurContentController(
|
||||||
|
blurred = blurred,
|
||||||
|
blurredRadius = blurredRadius,
|
||||||
|
scrimColor = scrimColor,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BlurContent(
|
fun BlurContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,11 @@ class CharacterDetailCharacteristicDialogViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun showSubCharacteristicDialog(
|
suspend fun showSubCharacteristicDialog(
|
||||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
characterInstanceId: Campaign.CharacterInstance.Id?,
|
||||||
characteristic: Characteristic,
|
characteristic: Characteristic,
|
||||||
) {
|
) {
|
||||||
|
if (characterInstanceId == null) return
|
||||||
|
|
||||||
val sheet: CharacterSheet? = characterSheetRepository.characterDetail(
|
val sheet: CharacterSheet? = characterSheetRepository.characterDetail(
|
||||||
characterSheetId = characterInstanceId.characterSheetId,
|
characterSheetId = characterInstanceId.characterSheetId,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
package com.pixelized.desktop.lwa.ui.screen.campaign
|
package com.pixelized.desktop.lwa.ui.screen.campaign
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
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.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
|
@ -7,12 +13,14 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
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.input.key.Key
|
import androidx.compose.ui.input.key.Key
|
||||||
|
|
@ -25,20 +33,29 @@ import androidx.compose.ui.unit.Density
|
||||||
import androidx.compose.ui.unit.DpSize
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.blur.rememberBlurContentController
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialog
|
||||||
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
|
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetail
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailPanel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbon
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbon
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialog
|
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialog
|
||||||
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
|
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.roll.RollPage
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CampaignScreen(
|
fun CampaignScreen(
|
||||||
characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
|
characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
|
||||||
|
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel = koinViewModel(),
|
||||||
dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(),
|
dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(),
|
||||||
networkViewModel: NetworkViewModel = koinViewModel(),
|
networkViewModel: NetworkViewModel = koinViewModel(),
|
||||||
|
rollViewModel: RollViewModel = koinViewModel(),
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
networkViewModel.connect()
|
networkViewModel.connect()
|
||||||
|
|
@ -55,48 +72,105 @@ fun CampaignScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Surface {
|
val scope = rememberCoroutineScope()
|
||||||
CampaignScreenLayout(
|
val blurController = rememberBlurContentController()
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
BlurContent(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
top = {
|
controller = blurController
|
||||||
Surface(
|
) {
|
||||||
modifier = Modifier
|
CampaignScreenLayout(
|
||||||
.height(32.dp)
|
modifier = Modifier.fillMaxSize(),
|
||||||
.fillMaxWidth(),
|
top = {
|
||||||
elevation = 1.dp,
|
Surface(
|
||||||
) {
|
modifier = Modifier
|
||||||
|
.height(32.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
elevation = 1.dp,
|
||||||
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
bottom = {
|
bottom = {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(48.dp)
|
.height(48.dp)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
elevation = 1.dp,
|
elevation = 1.dp,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
main = {
|
main = {
|
||||||
|
|
||||||
|
},
|
||||||
|
leftOverlay = {
|
||||||
|
PlayerRibbon(
|
||||||
|
modifier = Modifier.fillMaxHeight(),
|
||||||
|
onCharacter = {
|
||||||
|
characterDetailViewModel.showCharacter(id = it)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
rightOverlay = {
|
||||||
|
CharacterDetailPanel(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(width = 128.dp * 4)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.padding(all = 8.dp),
|
||||||
|
blurController = blurController,
|
||||||
|
detailViewModel = characterDetailViewModel,
|
||||||
|
rollViewModel = rollViewModel,
|
||||||
|
characterDiminishedViewModel = dismissedViewModel,
|
||||||
|
characteristicDialogViewModel = characteristicDialogViewModel,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedContent(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
targetState = rollViewModel.displayOverlay.value,
|
||||||
|
transitionSpec = {
|
||||||
|
val enter = fadeIn() + slideInVertically { 64 }
|
||||||
|
val exit = fadeOut() + slideOutVertically { 64 }
|
||||||
|
enter togetherWith exit
|
||||||
},
|
},
|
||||||
leftOverlay = {
|
) { roll ->
|
||||||
PlayerRibbon(
|
when (roll) {
|
||||||
modifier = Modifier.fillMaxHeight(),
|
true -> RollPage(
|
||||||
onCharacter = {
|
viewModel = rollViewModel,
|
||||||
characterDetailViewModel.showCharacter(id = it)
|
onDismissRequest = {
|
||||||
|
blurController.hide()
|
||||||
|
rollViewModel.hideOverlay()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
},
|
|
||||||
rightOverlay = {
|
else -> Box(
|
||||||
CharacterDetail(
|
modifier = Modifier.fillMaxSize()
|
||||||
modifier = Modifier
|
|
||||||
.padding(all = 8.dp)
|
|
||||||
.fillMaxHeight(),
|
|
||||||
detailViewModel = characterDetailViewModel,
|
|
||||||
dismissedViewModel = dismissedViewModel,
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterSheetCharacteristicDialog(
|
||||||
|
dialog = characteristicDialogViewModel.statChangeDialog,
|
||||||
|
onConfirm = { dialog ->
|
||||||
|
scope.launch {
|
||||||
|
characteristicDialogViewModel.changeSubCharacteristic(
|
||||||
|
characterInstanceId = dialog.characterInstanceId,
|
||||||
|
characteristic = dialog.characteristic,
|
||||||
|
value = dialog.value().text.toIntOrNull() ?: 0,
|
||||||
|
)
|
||||||
|
characteristicDialogViewModel.hideSubCharacteristicDialog()
|
||||||
|
blurController.hide()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = {
|
||||||
|
characteristicDialogViewModel.hideSubCharacteristicDialog()
|
||||||
|
blurController.hide()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,141 +6,150 @@ import androidx.compose.animation.ExitTransition
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.slideInHorizontally
|
import androidx.compose.animation.slideInHorizontally
|
||||||
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
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.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.material.Icon
|
|
||||||
import androidx.compose.material.IconButton
|
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
import androidx.compose.material.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
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.text.font.FontWeight
|
|
||||||
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.blur.BlurContentController
|
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
|
||||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
|
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialog
|
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialog
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeader
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeaderUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheet
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetCharacteristicUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_near_me
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_skull_24dp
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
|
|
||||||
import org.jetbrains.compose.resources.painterResource
|
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class CharacterDetailHeaderUio(
|
data class CharacterDetailPanelUio(
|
||||||
val id: Campaign.CharacterInstance.Id,
|
val characterInstanceId: Campaign.CharacterInstance.Id?,
|
||||||
val portrait: String?,
|
val header: StateFlow<CharacterDetailHeaderUio?>,
|
||||||
val name: String,
|
val sheet: StateFlow<CharacterDetailSheetUio?>,
|
||||||
val hp: String,
|
|
||||||
val maxHp: String,
|
|
||||||
val pp: String,
|
|
||||||
val maxPp: String,
|
|
||||||
val mov: String,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterDetail(
|
fun CharacterDetailPanel(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
dismissedViewModel: CharacterDiminishedViewModel,
|
blurController: BlurContentController,
|
||||||
detailViewModel: CharacterDetailViewModel = koinViewModel(),
|
detailViewModel: CharacterDetailViewModel,
|
||||||
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel = koinViewModel(),
|
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel,
|
||||||
|
characterDiminishedViewModel: CharacterDiminishedViewModel,
|
||||||
|
rollViewModel: RollViewModel,
|
||||||
) {
|
) {
|
||||||
val blurController = remember { BlurContentController() }
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val detail = detailViewModel.detail.collectAsState()
|
val detail: State<CharacterDetailPanelUio> = detailViewModel.detail.collectAsState()
|
||||||
|
|
||||||
AnimatedContent(
|
CharacterDetailAnimatedPanel(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
targetState = detail.value,
|
detail = detail,
|
||||||
transitionSpec = {
|
onDismissRequest = {
|
||||||
if (initialState?.id != targetState?.id) {
|
detailViewModel.hideCharacter()
|
||||||
(fadeIn() + slideInHorizontally { it / 2 }).togetherWith(fadeOut())
|
},
|
||||||
} else {
|
onDiminished = {
|
||||||
EnterTransition.None togetherWith ExitTransition.None
|
scope.launch {
|
||||||
|
characterDiminishedViewModel.showDiminishedDialog(
|
||||||
|
characterInstanceId = it
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
) {
|
onHp = {
|
||||||
when (it) {
|
scope.launch {
|
||||||
null -> Box(
|
blurController.show()
|
||||||
modifier = Modifier.fillMaxHeight(),
|
characteristicDialogViewModel.showSubCharacteristicDialog(
|
||||||
|
characterInstanceId = it,
|
||||||
|
characteristic = Campaign.CharacterInstance.Characteristic.Damage,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onPp = {
|
||||||
|
scope.launch {
|
||||||
|
blurController.show()
|
||||||
|
characteristicDialogViewModel.showSubCharacteristicDialog(
|
||||||
|
characterInstanceId = it,
|
||||||
|
characteristic = Campaign.CharacterInstance.Characteristic.Power,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCharacteristic = {
|
||||||
|
rollViewModel.prepareRoll(
|
||||||
|
characterSheetId = detail.value.characterInstanceId?.characterSheetId!!,
|
||||||
|
label = it.label,
|
||||||
|
rollAction = "1d100",
|
||||||
|
rollSuccessValue = (it.value.toIntOrNull() ?: 0) * 5,
|
||||||
)
|
)
|
||||||
|
blurController.show()
|
||||||
|
rollViewModel.showOverlay()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
@Composable
|
||||||
Box(
|
fun CharacterDetailAnimatedPanel(
|
||||||
modifier = Modifier
|
modifier: Modifier = Modifier,
|
||||||
.fillMaxHeight()
|
detail: State<CharacterDetailPanelUio>,
|
||||||
.width(width = 128.dp * 4),
|
onDismissRequest: (id: Campaign.CharacterInstance.Id) -> Unit,
|
||||||
) {
|
onDiminished: (id: Campaign.CharacterInstance.Id) -> Unit,
|
||||||
CharacterDetailContent(
|
onHp: (id: Campaign.CharacterInstance.Id) -> Unit,
|
||||||
|
onPp: (id: Campaign.CharacterInstance.Id) -> Unit,
|
||||||
|
onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
AnimatedContent(
|
||||||
|
modifier = Modifier.matchParentSize(),
|
||||||
|
targetState = detail.value,
|
||||||
|
transitionSpec = {
|
||||||
|
if (initialState.characterInstanceId != targetState.characterInstanceId) {
|
||||||
|
val enter = fadeIn() + slideInHorizontally { it / 2 }
|
||||||
|
val exit = fadeOut() + slideOutHorizontally { it / 2 }
|
||||||
|
enter togetherWith exit
|
||||||
|
} else {
|
||||||
|
EnterTransition.None togetherWith ExitTransition.None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
when {
|
||||||
|
it.characterInstanceId == null -> Box(
|
||||||
|
modifier = Modifier.matchParentSize(),
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
Box(
|
||||||
modifier = Modifier.matchParentSize(),
|
modifier = Modifier.matchParentSize(),
|
||||||
character = it,
|
) {
|
||||||
onDismissRequest = {
|
CharacterDetailContent(
|
||||||
detailViewModel.hideCharacter()
|
modifier = Modifier.matchParentSize(),
|
||||||
},
|
header = it.header.collectAsState(),
|
||||||
onDiminished = {
|
sheet = it.sheet.collectAsState(),
|
||||||
scope.launch {
|
onDismissRequest = { onDismissRequest(it.characterInstanceId) },
|
||||||
dismissedViewModel.showDiminishedDialog(id = it.id)
|
onDiminished = { onDiminished(it.characterInstanceId) },
|
||||||
}
|
onHp = { onHp(it.characterInstanceId) },
|
||||||
},
|
onPp = { onPp(it.characterInstanceId) },
|
||||||
onHp = {
|
onCharacteristic = onCharacteristic,
|
||||||
scope.launch {
|
)
|
||||||
characteristicDialogViewModel.showSubCharacteristicDialog(
|
}
|
||||||
characterInstanceId = it.id,
|
|
||||||
characteristic = Campaign.CharacterInstance.Characteristic.Damage,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onPp = {
|
|
||||||
scope.launch {
|
|
||||||
characteristicDialogViewModel.showSubCharacteristicDialog(
|
|
||||||
characterInstanceId = it.id,
|
|
||||||
characteristic = Campaign.CharacterInstance.Characteristic.Power,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
CharacterSheetCharacteristicDialog(
|
|
||||||
dialog = characteristicDialogViewModel.statChangeDialog,
|
|
||||||
onConfirm = { dialog ->
|
|
||||||
scope.launch {
|
|
||||||
characteristicDialogViewModel.changeSubCharacteristic(
|
|
||||||
characterInstanceId = dialog.characterInstanceId,
|
|
||||||
characteristic = dialog.characteristic,
|
|
||||||
value = dialog.value().text.toIntOrNull() ?: 0,
|
|
||||||
)
|
|
||||||
characteristicDialogViewModel.hideSubCharacteristicDialog()
|
|
||||||
blurController.hide()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDismissRequest = {
|
|
||||||
characteristicDialogViewModel.hideSubCharacteristicDialog()
|
|
||||||
blurController.hide()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -150,169 +159,35 @@ fun CharacterDetail(
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterDetailContent(
|
fun CharacterDetailContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
character: CharacterDetailHeaderUio,
|
header: State<CharacterDetailHeaderUio?>,
|
||||||
|
sheet: State<CharacterDetailSheetUio?>,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onDiminished: () -> Unit,
|
onDiminished: () -> Unit,
|
||||||
onHp: () -> Unit,
|
onHp: () -> Unit,
|
||||||
onPp: () -> Unit,
|
onPp: () -> Unit,
|
||||||
) {
|
onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit,
|
||||||
Box(
|
|
||||||
modifier = modifier,
|
|
||||||
) {
|
|
||||||
Background(
|
|
||||||
character = character,
|
|
||||||
)
|
|
||||||
Column {
|
|
||||||
CharacterHeader(
|
|
||||||
modifier = Modifier.padding(start = 16.dp).fillMaxWidth(),
|
|
||||||
character = character,
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
onDiminished = onDiminished,
|
|
||||||
onHp = onHp,
|
|
||||||
onPp = onPp,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun Background(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
character: CharacterDetailHeaderUio,
|
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
color = MaterialTheme.lwa.colorScheme.elevatedSurface,
|
color = MaterialTheme.lwa.colorScheme.elevatedSurface,
|
||||||
) {
|
) {
|
||||||
// Image(
|
Column {
|
||||||
// modifier = Modifier.fillMaxSize().drawWithContent {
|
CharacterDetailHeader(
|
||||||
// drawContent()
|
modifier = Modifier.padding(start = 16.dp).fillMaxWidth(),
|
||||||
// drawRect(
|
header = header,
|
||||||
// brush = Brush.verticalGradient(
|
onDismissRequest = onDismissRequest,
|
||||||
// listOfNotNull(
|
onDiminished = onDiminished,
|
||||||
// color?.copy(alpha = 0.7f),
|
onHp = onHp,
|
||||||
// color,
|
onPp = onPp,
|
||||||
// )
|
)
|
||||||
// )
|
CharacterDetailSheet(
|
||||||
// )
|
modifier = Modifier
|
||||||
// },
|
.weight(1f)
|
||||||
// painter = rememberAsyncImagePainter(model = character.portrait),
|
.verticalScroll(state = rememberScrollState())
|
||||||
// contentDescription = null,
|
.padding(all = 16.dp),
|
||||||
// contentScale = ContentScale.Crop,
|
sheet = sheet,
|
||||||
// alignment = Alignment.TopCenter,
|
onCharacteristic = onCharacteristic,
|
||||||
// )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun CharacterHeader(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
character: CharacterDetailHeaderUio,
|
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
onDiminished: () -> Unit,
|
|
||||||
onHp: () -> Unit,
|
|
||||||
onPp: () -> Unit,
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = modifier,
|
|
||||||
) {
|
|
||||||
Row {
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
.align(alignment = Alignment.CenterVertically),
|
|
||||||
style = MaterialTheme.typography.h5,
|
|
||||||
text = character.name,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
maxLines = 1,
|
|
||||||
)
|
)
|
||||||
IconButton(
|
|
||||||
onClick = onDiminished,
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
modifier = Modifier.size(size = 24.dp),
|
|
||||||
painter = painterResource(Res.drawable.ic_skull_24dp),
|
|
||||||
tint = MaterialTheme.colors.primary,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
IconButton(
|
|
||||||
onClick = onDismissRequest,
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(Res.drawable.ic_close_24dp),
|
|
||||||
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(space = 12.dp),
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.clip(shape = CircleShape).clickable { onHp() },
|
|
||||||
verticalAlignment = Alignment.Bottom,
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
|
||||||
painter = painterResource(Res.drawable.ic_heart_24dp),
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.typography.h6,
|
|
||||||
color = MaterialTheme.lwa.colorScheme.base.primary,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
text = character.hp,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.typography.caption,
|
|
||||||
fontWeight = FontWeight.Thin,
|
|
||||||
text = "/${character.maxHp}",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.clip(shape = CircleShape).clickable { onPp() },
|
|
||||||
verticalAlignment = Alignment.Bottom,
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
|
||||||
painter = painterResource(Res.drawable.ic_water_drop_24dp),
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.typography.h6,
|
|
||||||
color = MaterialTheme.lwa.colorScheme.base.primary,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
text = character.pp,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.typography.caption,
|
|
||||||
fontWeight = FontWeight.Thin,
|
|
||||||
text = "/${character.maxPp}",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.Bottom,
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
|
||||||
painter = painterResource(Res.drawable.ic_near_me),
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.typography.h6,
|
|
||||||
text = character.mov,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.typography.caption,
|
|
||||||
text = "m",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,16 +1,35 @@
|
||||||
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.screen.campaign.player.detail.header.CharacterDetailHeaderUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetCharacteristicUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetUio
|
||||||
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
|
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
|
||||||
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
|
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
|
||||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||||
import com.pixelized.shared.lwa.model.campaign.damage
|
import com.pixelized.shared.lwa.model.campaign.damage
|
||||||
import com.pixelized.shared.lwa.model.campaign.power
|
import com.pixelized.shared.lwa.model.campaign.power
|
||||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
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.tooltip__characteristics__charisma
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__constitution
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__dexterity
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__height
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__intelligence
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__power
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__strength
|
||||||
|
import org.jetbrains.compose.resources.getString
|
||||||
|
|
||||||
class CharacterDetailFactory(
|
class CharacterDetailFactory(
|
||||||
private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory,
|
private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun convertToCharacterDetailHeaderUio(
|
fun convertToCharacterDetailHeaderUio(
|
||||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
characterInstanceId: Campaign.CharacterInstance.Id,
|
||||||
characterSheet: CharacterSheet?,
|
characterSheet: CharacterSheet?,
|
||||||
|
|
@ -38,4 +57,80 @@ class CharacterDetailFactory(
|
||||||
mov = "${alteredCharacterSheet.movement}"
|
mov = "${alteredCharacterSheet.movement}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun convertToCharacterDetailSheetUio(
|
||||||
|
characterInstanceId: Campaign.CharacterInstance.Id,
|
||||||
|
characterSheet: CharacterSheet?,
|
||||||
|
characterInstance: Campaign.CharacterInstance,
|
||||||
|
alterations: Map<String, List<FieldAlteration>>,
|
||||||
|
): CharacterDetailSheetUio? {
|
||||||
|
if (characterSheet == null) return null
|
||||||
|
|
||||||
|
val alteredCharacterSheet = alteredCharacterSheetFactory.sheet(
|
||||||
|
characterSheet = characterSheet,
|
||||||
|
alterations = alterations,
|
||||||
|
)
|
||||||
|
|
||||||
|
return CharacterDetailSheetUio(
|
||||||
|
characterInstanceId = characterInstanceId,
|
||||||
|
characteristics = listOf(
|
||||||
|
CharacterDetailSheetCharacteristicUio(
|
||||||
|
label = getString(Res.string.character_sheet__characteristics__str),
|
||||||
|
value = "${alteredCharacterSheet.strength}",
|
||||||
|
tooltips = TooltipUio(
|
||||||
|
title = getString(Res.string.character_sheet__characteristics__str),
|
||||||
|
description = getString(Res.string.tooltip__characteristics__strength),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CharacterDetailSheetCharacteristicUio(
|
||||||
|
label = getString(Res.string.character_sheet__characteristics__dex),
|
||||||
|
value = "${alteredCharacterSheet.dexterity}",
|
||||||
|
tooltips = TooltipUio(
|
||||||
|
title = getString(Res.string.character_sheet__characteristics__dex),
|
||||||
|
description = getString(Res.string.tooltip__characteristics__dexterity),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CharacterDetailSheetCharacteristicUio(
|
||||||
|
label = getString(Res.string.character_sheet__characteristics__con),
|
||||||
|
value = "${alteredCharacterSheet.constitution}",
|
||||||
|
tooltips = TooltipUio(
|
||||||
|
title = getString(Res.string.character_sheet__characteristics__con),
|
||||||
|
description = getString(Res.string.tooltip__characteristics__constitution),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CharacterDetailSheetCharacteristicUio(
|
||||||
|
label = getString(Res.string.character_sheet__characteristics__hei),
|
||||||
|
value = "${alteredCharacterSheet.height}",
|
||||||
|
tooltips = TooltipUio(
|
||||||
|
title = getString(Res.string.character_sheet__characteristics__hei),
|
||||||
|
description = getString(Res.string.tooltip__characteristics__height),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CharacterDetailSheetCharacteristicUio(
|
||||||
|
label = getString(Res.string.character_sheet__characteristics__int),
|
||||||
|
value = "${alteredCharacterSheet.intelligence}",
|
||||||
|
tooltips = TooltipUio(
|
||||||
|
title = getString(Res.string.character_sheet__characteristics__int),
|
||||||
|
description = getString(Res.string.tooltip__characteristics__intelligence),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CharacterDetailSheetCharacteristicUio(
|
||||||
|
label = getString(Res.string.character_sheet__characteristics__pow),
|
||||||
|
value = "${alteredCharacterSheet.power}",
|
||||||
|
tooltips = TooltipUio(
|
||||||
|
title = getString(Res.string.character_sheet__characteristics__pow),
|
||||||
|
description = getString(Res.string.tooltip__characteristics__power),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CharacterDetailSheetCharacteristicUio(
|
||||||
|
label = getString(Res.string.character_sheet__characteristics__cha),
|
||||||
|
value = "${alteredCharacterSheet.charisma}",
|
||||||
|
tooltips = TooltipUio(
|
||||||
|
title = getString(Res.string.character_sheet__characteristics__cha),
|
||||||
|
description = getString(Res.string.tooltip__characteristics__charisma),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,13 +6,11 @@ import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.flowOf
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
class CharacterDetailViewModel(
|
class CharacterDetailViewModel(
|
||||||
|
|
@ -24,32 +22,50 @@ class CharacterDetailViewModel(
|
||||||
|
|
||||||
private val displayedCharacterId = MutableStateFlow<Campaign.CharacterInstance.Id?>(null)
|
private val displayedCharacterId = MutableStateFlow<Campaign.CharacterInstance.Id?>(null)
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
val detail: StateFlow<CharacterDetailPanelUio> = displayedCharacterId
|
||||||
val detail: StateFlow<CharacterDetailHeaderUio?> = displayedCharacterId
|
.map { characterInstanceId ->
|
||||||
.flatMapLatest { characterInstanceId ->
|
if (characterInstanceId == null) return@map empty()
|
||||||
if (characterInstanceId != null) {
|
|
||||||
campaignRepository
|
CharacterDetailPanelUio(
|
||||||
.characterInstanceFlow(id = characterInstanceId)
|
characterInstanceId = characterInstanceId,
|
||||||
.flatMapLatest { characterInstance ->
|
header = combine(
|
||||||
combine(
|
campaignRepository.characterInstanceFlow(id = characterInstanceId),
|
||||||
characterSheetRepository.characterDetailFlow(characterId = characterInstanceId.characterSheetId),
|
characterSheetRepository.characterDetailFlow(characterId = characterInstanceId.characterSheetId),
|
||||||
alterationRepository.alterationsFlow(characterId = characterInstanceId),
|
alterationRepository.alterationsFlow(characterId = characterInstanceId),
|
||||||
) { characterSheet, alterations ->
|
) { characterInstance, characterSheet, alterations ->
|
||||||
characterDetailFactory.convertToCharacterDetailHeaderUio(
|
characterDetailFactory.convertToCharacterDetailHeaderUio(
|
||||||
characterInstanceId = characterInstanceId,
|
characterInstanceId = characterInstanceId,
|
||||||
characterSheet = characterSheet,
|
characterSheet = characterSheet,
|
||||||
characterInstance = characterInstance,
|
characterInstance = characterInstance,
|
||||||
alterations = alterations,
|
alterations = alterations,
|
||||||
)
|
)
|
||||||
}
|
}.stateIn(
|
||||||
}
|
scope = viewModelScope,
|
||||||
} else {
|
started = SharingStarted.Eagerly,
|
||||||
flowOf(null)
|
initialValue = null,
|
||||||
}
|
),
|
||||||
}.stateIn(
|
sheet = combine(
|
||||||
|
campaignRepository.characterInstanceFlow(id = characterInstanceId),
|
||||||
|
characterSheetRepository.characterDetailFlow(characterId = characterInstanceId.characterSheetId),
|
||||||
|
alterationRepository.alterationsFlow(characterId = characterInstanceId),
|
||||||
|
) { characterInstance, characterSheet, alterations ->
|
||||||
|
characterDetailFactory.convertToCharacterDetailSheetUio(
|
||||||
|
characterInstanceId = characterInstanceId,
|
||||||
|
characterSheet = characterSheet,
|
||||||
|
characterInstance = characterInstance,
|
||||||
|
alterations = alterations,
|
||||||
|
)
|
||||||
|
}.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.Eagerly,
|
||||||
|
initialValue = null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
started = SharingStarted.Eagerly,
|
started = SharingStarted.Eagerly,
|
||||||
initialValue = null,
|
initialValue = empty(),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun showCharacter(id: Campaign.CharacterInstance.Id) {
|
fun showCharacter(id: Campaign.CharacterInstance.Id) {
|
||||||
|
|
@ -59,4 +75,10 @@ class CharacterDetailViewModel(
|
||||||
fun hideCharacter() {
|
fun hideCharacter() {
|
||||||
displayedCharacterId.value = null
|
displayedCharacterId.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun empty() = CharacterDetailPanelUio(
|
||||||
|
characterInstanceId = null,
|
||||||
|
header = MutableStateFlow(null),
|
||||||
|
sheet = MutableStateFlow(null),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -20,14 +20,16 @@ class CharacterDiminishedViewModel(
|
||||||
val diminishedDialog: State<DiminishedStatDialogUio?> get() = _diminishedDialog
|
val diminishedDialog: State<DiminishedStatDialogUio?> get() = _diminishedDialog
|
||||||
|
|
||||||
suspend fun showDiminishedDialog(
|
suspend fun showDiminishedDialog(
|
||||||
id: Campaign.CharacterInstance.Id,
|
characterInstanceId: Campaign.CharacterInstance.Id?,
|
||||||
) {
|
) {
|
||||||
|
if (characterInstanceId == null) return
|
||||||
|
|
||||||
val diminished = 0 // TODO repository.characterDiminishedFlow(id = id).value
|
val diminished = 0 // TODO repository.characterDiminishedFlow(id = id).value
|
||||||
val textFieldValue = mutableStateOf(
|
val textFieldValue = mutableStateOf(
|
||||||
TextFieldValue("$diminished", selection = TextRange(index = 0))
|
TextFieldValue("$diminished", selection = TextRange(index = 0))
|
||||||
)
|
)
|
||||||
_diminishedDialog.value = DiminishedStatDialogUio(
|
_diminishedDialog.value = DiminishedStatDialogUio(
|
||||||
id = id,
|
id = characterInstanceId,
|
||||||
label = getString(resource = Res.string.character_sheet__diminished__label),
|
label = getString(resource = Res.string.character_sheet__diminished__label),
|
||||||
value = { textFieldValue.value },
|
value = { textFieldValue.value },
|
||||||
onValueChange = { value ->
|
onValueChange = { value ->
|
||||||
|
|
@ -51,6 +53,4 @@ class CharacterDiminishedViewModel(
|
||||||
// diminished = value,
|
// diminished = value,
|
||||||
// )
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_near_me
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_skull_24dp
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
|
||||||
|
import org.jetbrains.compose.resources.painterResource
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class CharacterDetailHeaderUio(
|
||||||
|
val id: Campaign.CharacterInstance.Id,
|
||||||
|
val portrait: String?,
|
||||||
|
val name: String,
|
||||||
|
val hp: String,
|
||||||
|
val maxHp: String,
|
||||||
|
val pp: String,
|
||||||
|
val maxPp: String,
|
||||||
|
val mov: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CharacterDetailHeader(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
header: State<CharacterDetailHeaderUio?>,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
onDiminished: () -> Unit,
|
||||||
|
onHp: () -> Unit,
|
||||||
|
onPp: () -> Unit,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
.align(alignment = Alignment.CenterVertically),
|
||||||
|
style = MaterialTheme.typography.h5,
|
||||||
|
text = header.value?.name ?: "",
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
IconButton(
|
||||||
|
onClick = onDiminished,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.size(size = 24.dp),
|
||||||
|
painter = painterResource(Res.drawable.ic_skull_24dp),
|
||||||
|
tint = MaterialTheme.colors.primary,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = onDismissRequest,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(Res.drawable.ic_close_24dp),
|
||||||
|
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(space = 12.dp),
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.clip(shape = CircleShape).clickable { onHp() },
|
||||||
|
verticalAlignment = Alignment.Bottom,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
||||||
|
painter = painterResource(Res.drawable.ic_heart_24dp),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.typography.h6,
|
||||||
|
color = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
text = header.value?.hp ?: "",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
fontWeight = FontWeight.Thin,
|
||||||
|
text = "/${header.value?.maxHp ?: ""}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.clip(shape = CircleShape).clickable { onPp() },
|
||||||
|
verticalAlignment = Alignment.Bottom,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
||||||
|
painter = painterResource(Res.drawable.ic_water_drop_24dp),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.typography.h6,
|
||||||
|
color = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
text = header.value?.pp ?: "",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
fontWeight = FontWeight.Thin,
|
||||||
|
text = "/${header.value?.maxPp ?: ""}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.Bottom,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
||||||
|
painter = painterResource(Res.drawable.ic_near_me),
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.typography.h6,
|
||||||
|
text = header.value?.mov ?: "",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
text = "m",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
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.TooltipUio
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class CharacterDetailSheetCharacteristicUio(
|
||||||
|
val value: String,
|
||||||
|
val label: String,
|
||||||
|
val tooltips: TooltipUio?,
|
||||||
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun CharacterDetailSheetCharacteristic(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
paddingValues: PaddingValues = PaddingValues(all = 8.dp),
|
||||||
|
characteristic: CharacterDetailSheetCharacteristicUio,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
TooltipLayout(
|
||||||
|
tooltip = characteristic.tooltips,
|
||||||
|
content = {
|
||||||
|
DecoratedBox(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable(onClick = onClick)
|
||||||
|
.padding(paddingValues = paddingValues)
|
||||||
|
.then(other = modifier),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.align(alignment = Alignment.TopCenter),
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = characteristic.label,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.align(alignment = Alignment.Center),
|
||||||
|
style = MaterialTheme.typography.h3,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
color = MaterialTheme.colors.primary,
|
||||||
|
text = characteristic.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class CharacterDetailSheetUio(
|
||||||
|
val characterInstanceId: Campaign.CharacterInstance.Id,
|
||||||
|
val characteristics: List<CharacterDetailSheetCharacteristicUio>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CharacterDetailSheet(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
sheet: State<CharacterDetailSheetUio?>,
|
||||||
|
onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
|
||||||
|
) {
|
||||||
|
sheet.value?.characteristics?.forEach {
|
||||||
|
CharacterDetailSheetCharacteristic(
|
||||||
|
modifier = Modifier.size(width = 80.dp, height = 120.dp),
|
||||||
|
characteristic = it,
|
||||||
|
onClick = { onCharacteristic(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -6,13 +6,13 @@ import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.pixelized.shared.lwa.usecase.ExpressionUseCase
|
|
||||||
import com.pixelized.shared.lwa.usecase.SkillStepUseCase
|
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio
|
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.roll.DifficultyUio.Difficulty
|
import com.pixelized.desktop.lwa.ui.screen.roll.DifficultyUio.Difficulty
|
||||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||||
|
import com.pixelized.shared.lwa.usecase.ExpressionUseCase
|
||||||
|
import com.pixelized.shared.lwa.usecase.SkillStepUseCase
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
@ -55,13 +55,17 @@ 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 _displayOverlay = mutableStateOf(false)
|
||||||
|
val displayOverlay: State<Boolean> get() = _displayOverlay
|
||||||
|
|
||||||
fun prepareRoll(
|
fun prepareRoll(
|
||||||
sheet: CharacterSheetPageUio,
|
sheet: CharacterSheetPageUio,
|
||||||
characteristic: CharacterSheetPageUio.Characteristic,
|
characteristic: CharacterSheetPageUio.Characteristic,
|
||||||
) {
|
) {
|
||||||
val diminished = 0 // TODO characterSheetRepository.characterDiminishedFlow(id = sheet.id).value
|
val diminished =
|
||||||
|
0 // TODO characterSheetRepository.characterDiminishedFlow(id = sheet.id).value
|
||||||
prepareRoll(
|
prepareRoll(
|
||||||
sheet = sheet,
|
characterSheetId = sheet.id,
|
||||||
label = characteristic.label,
|
label = characteristic.label,
|
||||||
rollAction = "1d100",
|
rollAction = "1d100",
|
||||||
rollSuccessValue = (characteristic.value.toIntOrNull() ?: 0) * 5 - diminished,
|
rollSuccessValue = (characteristic.value.toIntOrNull() ?: 0) * 5 - diminished,
|
||||||
|
|
@ -73,7 +77,7 @@ class RollViewModel(
|
||||||
node: CharacterSheetPageUio.Node,
|
node: CharacterSheetPageUio.Node,
|
||||||
) {
|
) {
|
||||||
prepareRoll(
|
prepareRoll(
|
||||||
sheet = sheet,
|
characterSheetId = sheet.id,
|
||||||
label = node.label,
|
label = node.label,
|
||||||
rollAction = "1d100",
|
rollAction = "1d100",
|
||||||
rollSuccessValue = node.value,
|
rollSuccessValue = node.value,
|
||||||
|
|
@ -85,15 +89,15 @@ class RollViewModel(
|
||||||
roll: CharacterSheetPageUio.Roll,
|
roll: CharacterSheetPageUio.Roll,
|
||||||
) {
|
) {
|
||||||
prepareRoll(
|
prepareRoll(
|
||||||
sheet = sheet,
|
characterSheetId = sheet.id,
|
||||||
label = roll.label,
|
label = roll.label,
|
||||||
rollAction = roll.value,
|
rollAction = roll.value,
|
||||||
rollSuccessValue = null,
|
rollSuccessValue = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareRoll(
|
fun prepareRoll(
|
||||||
sheet: CharacterSheetPageUio,
|
characterSheetId: String,
|
||||||
label: String,
|
label: String,
|
||||||
rollAction: String,
|
rollAction: String,
|
||||||
rollSuccessValue: Int?,
|
rollSuccessValue: Int?,
|
||||||
|
|
@ -101,7 +105,7 @@ class RollViewModel(
|
||||||
this.sheet = runBlocking {
|
this.sheet = runBlocking {
|
||||||
rollRotation.snapTo(0f)
|
rollRotation.snapTo(0f)
|
||||||
rollScale.snapTo(1f)
|
rollScale.snapTo(1f)
|
||||||
characterSheetRepository.characterDetail(characterSheetId = sheet.id)!!
|
characterSheetRepository.characterDetail(characterSheetId = characterSheetId)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rollAction = rollAction
|
this.rollAction = rollAction
|
||||||
|
|
@ -238,4 +242,12 @@ class RollViewModel(
|
||||||
value = rollStep?.success?.last
|
value = rollStep?.success?.last
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showOverlay() {
|
||||||
|
_displayOverlay.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideOverlay() {
|
||||||
|
_displayOverlay.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +59,7 @@ class RollUseCase {
|
||||||
if (quantity > 1 && left != 1) print(",")
|
if (quantity > 1 && left != 1) print(",")
|
||||||
}
|
}
|
||||||
}.also {
|
}.also {
|
||||||
print("}")
|
println("}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue