Add the old ui to the new one.

This commit is contained in:
Thomas Andres Gomez 2025-02-27 19:15:06 +01:00
parent d8ce46fe43
commit f51a83cf6e
38 changed files with 635 additions and 250 deletions

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="app_name">La table de Lwa</string>
<string name="dialog__confirm_action">Confirmer</string>
<string name="dialog__cancel_action">Annuler</string>

View file

@ -42,17 +42,21 @@ import com.pixelized.desktop.lwa.ui.navigation.window.destination.CharacterSheet
import com.pixelized.desktop.lwa.ui.navigation.window.destination.CharacterSheetWindow
import com.pixelized.desktop.lwa.ui.navigation.window.destination.RollHistoryWindow
import com.pixelized.desktop.lwa.ui.navigation.window.rememberMaxWindowHeight
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignScreen
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
import com.pixelized.desktop.lwa.ui.screen.characterSheet.CharacterSheetMainNavHost
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryPage
import com.pixelized.desktop.lwa.ui.theme.LwaTheme
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.app_name
import lwacharactersheet.composeapp.generated.resources.network__connect__message
import lwacharactersheet.composeapp.generated.resources.network__disconnect__message
import org.jetbrains.compose.resources.getString
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.koin.compose.koinInject
import org.koin.compose.viewmodel.koinViewModel
val LocalWindowController = compositionLocalOf<WindowController> {
error("Local Window Controller is not yet ready")
@ -84,11 +88,7 @@ fun ApplicationScope.App() {
) {
Window(
onCloseRequest = ::exitApplication,
// state = rememberWindowState(
// width = 320.dp + 64.dp,
// height = 900.dp,
// ),
title = "LwaCharacterSheet",
title = runBlocking { getString(Res.string.app_name) },
onKeyEvent = { event ->
keyEventHandlers.reversed().any { it(event) }
},
@ -125,8 +125,7 @@ fun ApplicationScope.App() {
}
},
content = {
// MainNavHost()
CampaignScreen()
MainScreen()
}
)
NetworkSnackHandler(
@ -141,6 +140,19 @@ fun ApplicationScope.App() {
}
}
@Composable
private fun MainScreen(
campaignViewModel: CampaignViewModel = koinViewModel(),
networkViewModel: NetworkViewModel = koinViewModel(),
) {
LaunchedEffect(Unit) {
networkViewModel.connect()
campaignViewModel.init()
}
MainNavHost()
}
@Composable
private fun WindowsHandler(
windowController: WindowController,

View file

@ -32,7 +32,7 @@ import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
import com.pixelized.desktop.lwa.usecase.SettingsUseCase
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import io.ktor.client.HttpClient
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.engine.okhttp.OkHttp

View file

@ -5,7 +5,7 @@ import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.shared.lwa.model.alteration.Alteration
import com.pixelized.shared.lwa.model.alteration.AlterationJsonFactory
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import com.pixelized.shared.lwa.protocol.websocket.Message
import com.pixelized.shared.lwa.protocol.websocket.payload.RestSynchronisation
import kotlinx.coroutines.CoroutineScope
@ -90,7 +90,7 @@ class AlterationStore(
when (val payload = message.value) {
is RestSynchronisation.ToggleActiveAlteration -> {
setActiveAlteration(
characterInstanceId = campaignJsonFactory.convertFromV1(
characterInstanceId = campaignJsonFactory.characterInstanceIdFromJson(
characterInstanceIdJson = payload.characterId,
),
alterationId = payload.alterationId,

View file

@ -3,7 +3,7 @@ package com.pixelized.desktop.lwa.repository.campaign
import com.pixelized.desktop.lwa.network.LwaClient
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.character
import com.pixelized.shared.lwa.model.campaign.npc
import com.pixelized.shared.lwa.protocol.websocket.Message
@ -63,7 +63,7 @@ class CampaignStore(
when (payload) {
is CampaignMessage.UpdateCharacteristic -> updateCharacteristic(
characterInstanceId = instanceId,
characteristic = factory.convertFromV1(characteristicJson = payload.characteristic),
characteristic = factory.convertFromJson(json = payload.characteristic),
value = payload.value,
)

View file

@ -11,7 +11,7 @@ import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance.Characteristic
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import com.pixelized.shared.lwa.protocol.websocket.payload.CampaignMessage

View file

@ -61,7 +61,7 @@ fun MasteryShape(
shape = shape,
color = color,
borderWidth = borderWidth,
size = size - borderWidth * 2,
size = size - borderWidth * 3,
multiplier = multiplier - 1,
)
}

View file

@ -2,7 +2,6 @@ package com.pixelized.desktop.lwa.ui.navigation.screen
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
@ -10,8 +9,7 @@ import androidx.navigation.compose.rememberNavController
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.MainDestination
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableMainPage
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableNetworkPage
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
import org.koin.compose.viewmodel.koinViewModel
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableOldMainPage
val LocalScreenController = compositionLocalOf<NavHostController> {
error("MainNavHost controller is not yet ready")
@ -21,12 +19,7 @@ val LocalScreenController = compositionLocalOf<NavHostController> {
fun MainNavHost(
controller: NavHostController = rememberNavController(),
startDestination: String = MainDestination.navigationRoute(),
networkViewModel: NetworkViewModel = koinViewModel(),
) {
LaunchedEffect(Unit) {
networkViewModel.connect()
}
CompositionLocalProvider(
LocalScreenController provides controller,
) {
@ -35,6 +28,7 @@ fun MainNavHost(
startDestination = startDestination,
) {
composableMainPage()
composableOldMainPage()
composableNetworkPage()
}
}

View file

@ -3,7 +3,7 @@ package com.pixelized.desktop.lwa.ui.navigation.screen.destination
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import com.pixelized.desktop.lwa.ui.screen.main.MainPage
import com.pixelized.desktop.lwa.ui.screen.campaign.MainPage
object MainDestination {
private const val ROUTE = "main"

View file

@ -0,0 +1,26 @@
package com.pixelized.desktop.lwa.ui.navigation.screen.destination
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import com.pixelized.desktop.lwa.ui.screen.main.OldMainPage
object OldMainDestination {
private const val ROUTE = "old_main"
fun baseRoute() = ROUTE
fun navigationRoute() = ROUTE
}
fun NavGraphBuilder.composableOldMainPage() {
composable(
route = OldMainDestination.baseRoute(),
) {
OldMainPage()
}
}
fun NavHostController.navigateToOldMainPage() {
val route = OldMainDestination.navigationRoute()
navigate(route = route)
}

View file

@ -13,17 +13,17 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
@ -39,31 +39,30 @@ import com.pixelized.desktop.lwa.ui.composable.blur.rememberBlurContentControlle
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.navigation.screen.LocalScreenController
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToOldMainPage
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.CharacterDiminishedViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbon
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.Toolbar
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.roll.RollPage
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.ic_table_24dp
import org.jetbrains.compose.resources.painterResource
import org.koin.compose.viewmodel.koinViewModel
@Composable
fun CampaignScreen(
fun MainPage(
campaignViewModel: CampaignViewModel = koinViewModel(),
characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel = koinViewModel(),
dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(),
networkViewModel: NetworkViewModel = koinViewModel(),
rollViewModel: RollViewModel = koinViewModel(),
) {
LaunchedEffect(Unit) {
networkViewModel.connect()
campaignViewModel.init()
}
KeyHandler {
when {
it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
@ -75,6 +74,7 @@ fun CampaignScreen(
}
}
val screen = LocalScreenController.current
val scope = rememberCoroutineScope()
val blurController = rememberBlurContentController()
@ -88,15 +88,22 @@ fun CampaignScreen(
CampaignScreenLayout(
modifier = Modifier.fillMaxSize(),
top = {
Surface(
modifier = Modifier
// .height(32.dp)
.fillMaxWidth(),
elevation = 1.dp,
Toolbar(
title = campaignViewModel.title.collectAsState(initial = "").value,
actions = {
IconButton(
onClick = {
screen.navigateToOldMainPage()
},
) {
Icon(
painter = painterResource(Res.drawable.ic_table_24dp),
contentDescription = null,
)
}
},
)
},
bottom = {
Surface(
modifier = Modifier
@ -123,8 +130,7 @@ fun CampaignScreen(
modifier = Modifier
.width(width = 128.dp * 4)
.fillMaxHeight()
.padding(all = 8.dp)
.clip(shape = remember { RoundedCornerShape(16.dp) }),
.padding(all = 8.dp),
blurController = blurController,
detailViewModel = characterDetailViewModel,
rollViewModel = rollViewModel,

View file

@ -5,7 +5,9 @@ import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
class CampaignViewModel(
@ -14,8 +16,10 @@ class CampaignViewModel(
private val campaignRepository: CampaignRepository,
) : ViewModel() {
suspend fun init() {
val title: Flow<String> = campaignRepository.campaignFlow
.map { it.scene.name }
fun init() {
viewModelScope.launch {
campaignRepository.campaignFlow.collectLatest {
it.characters.keys.forEach { id ->

View file

@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
@ -22,8 +23,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
@ -178,6 +181,7 @@ fun CharacterDetailAnimatedPanel(
@Composable
fun CharacterDetailContent(
modifier: Modifier = Modifier,
shape: Shape = remember { RoundedCornerShape(16.dp) },
header: State<CharacterDetailHeaderUio?>,
sheet: State<CharacterDetailSheetUio?>,
onDismissRequest: () -> Unit,
@ -191,6 +195,7 @@ fun CharacterDetailContent(
) {
Surface(
modifier = modifier.fillMaxSize(),
shape = shape,
color = MaterialTheme.lwa.colorScheme.elevated.base1dp,
) {
Column {

View file

@ -59,7 +59,7 @@ fun CharacterDetailSheet(
}
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
) {
DecoratedBox(
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),

View file

@ -4,6 +4,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
@ -15,6 +16,7 @@ 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.circle.MasteryShape
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.screen.roll.RollActionUio
import lwacharactersheet.composeapp.generated.resources.Res
@ -32,7 +34,12 @@ data class CharacterDetailSheetActionUio(
@Composable
fun CharacterDetailSheetAction(
modifier: Modifier = Modifier,
paddingValues: PaddingValues = PaddingValues(start = 28.dp, end = 9.dp, top = 6.dp, bottom = 6.dp),
paddingValues: PaddingValues = PaddingValues(
start = 10.dp,
end = 9.dp,
top = 6.dp,
bottom = 6.dp
),
action: CharacterDetailSheetActionUio,
onClick: (CharacterDetailSheetActionUio) -> Unit,
) {
@ -41,9 +48,13 @@ fun CharacterDetailSheetAction(
.clickable(onClick = { onClick(action) })
.padding(paddingValues = paddingValues)
.then(other = modifier),
horizontalArrangement = Arrangement.SpaceBetween,
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
MasteryShape(
modifier = Modifier.padding(top = 4.dp, end = 2.dp),
multiplier = 2,
)
Text(
modifier = Modifier.weight(1f),
style = MaterialTheme.typography.body1,

View file

@ -6,13 +6,15 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
@ -21,13 +23,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.ui.theme.lwa
import com.pixelized.shared.lwa.model.campaign.Campaign
import lwacharactersheet.composeapp.generated.resources.Res
@ -59,7 +61,7 @@ fun PlayerPortrait(
modifier = modifier
.size(size = size)
.clip(shape = remember { RoundedCornerShape(8.dp) })
.background(color = MaterialTheme.lwa.colorScheme.elevated.base1dp)
.background(color = colorScheme.elevated.base1dp)
.clickable { onCharacter(character.id) },
) {
AsyncImage(
@ -74,50 +76,54 @@ fun PlayerPortrait(
modifier = Modifier
.fillMaxSize()
.drawWithContent {
drawRect(
brush = Brush.verticalGradient(
listOf(
colorScheme.elevated.base1dp.copy(alpha = 0.0f),
colorScheme.elevated.base1dp.copy(alpha = 0.0f),
colorScheme.elevated.base1dp.copy(alpha = 0.0f),
colorScheme.elevated.base1dp.copy(alpha = 0.5f),
colorScheme.elevated.base1dp.copy(alpha = 0.8f),
)
)
)
drawRect(brush = colorScheme.portraitBackgroundBrush)
drawContent()
}
.padding(vertical = 2.dp, horizontal = 4.dp),
verticalArrangement = Arrangement.aligned(alignment = Alignment.Bottom),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(space = 2.dp),
) {
Row {
Icon(
modifier = Modifier.size(12.dp),
modifier = Modifier.size(12.dp).offset(y = 3.dp),
painter = painterResource(Res.drawable.ic_heart_24dp),
contentDescription = null
)
Spacer(
modifier = Modifier.width(width = 2.dp),
)
Text(
modifier = Modifier.padding(bottom = 2.dp),
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.caption,
text = "${character.hp}/${character.maxHp}",
fontWeight = FontWeight.Bold,
text = "${character.hp}",
)
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.caption,
fontWeight = FontWeight.Light,
text = "/${character.maxHp}",
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(space = 2.dp),
) {
Row {
Icon(
modifier = Modifier.size(12.dp),
modifier = Modifier.size(12.dp).offset(y = 2.dp),
painter = painterResource(Res.drawable.ic_water_drop_24dp),
contentDescription = null
)
Spacer(
modifier = Modifier.width(width = 2.dp),
)
Text(
modifier = Modifier.padding(bottom = 2.dp),
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.caption,
text = "${character.pp}/${character.maxPp}",
fontWeight = FontWeight.Bold,
text = "${character.pp}",
)
Text(
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Light,
style = MaterialTheme.typography.caption,
text = "/${character.maxPp}",
)
}
}

View file

@ -0,0 +1,52 @@
package com.pixelized.desktop.lwa.ui.screen.campaign.toolbar
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.theme.lwa
@Composable
fun Toolbar(
modifier: Modifier = Modifier,
title: String,
actions: @Composable RowScope.() -> Unit = { },
) {
Surface(
modifier = modifier,
elevation = 1.dp,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.minimumInteractiveComponentSize(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier.padding(start = 16.dp),
style = MaterialTheme.typography.h6,
text = title,
)
CompositionLocalProvider(
LocalContentColor provides MaterialTheme.lwa.colorScheme.base.primary,
) {
Row(
modifier = Modifier.padding(end = 8.dp),
content = actions,
)
}
}
}
}

View file

@ -19,7 +19,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
@ -27,11 +26,11 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.LocalWindowController
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToMainPage
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToNetwork
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToCharacterSheet
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToCharacterSheetEdit
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToRollHistory
import com.pixelized.shared.lwa.model.campaign.Campaign
import kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res
@ -39,6 +38,7 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__cr
import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp
import lwacharactersheet.composeapp.generated.resources.ic_file_24dp
import lwacharactersheet.composeapp.generated.resources.ic_folder_24dp
import lwacharactersheet.composeapp.generated.resources.ic_swords_24dp
import lwacharactersheet.composeapp.generated.resources.ic_table_24dp
import lwacharactersheet.composeapp.generated.resources.main_page__create_action
import lwacharactersheet.composeapp.generated.resources.main_page__network_action
@ -56,12 +56,13 @@ data class CharacterUio(
)
@Composable
fun MainPage(
fun OldMainPage(
viewModel: MainPageViewModel = koinViewModel(),
) {
val window = LocalWindowController.current
val screen = LocalScreenController.current
val characters = viewModel.characters.collectAsState()
val npcs = viewModel.npcs.collectAsState()
Surface(
modifier = Modifier.fillMaxSize(),
@ -75,6 +76,7 @@ fun MainPage(
) {
MainPageContent(
characters = characters,
npcs = npcs,
enableRollHistory = viewModel.enableRollHistory,
onCharacter = {
window.navigateToCharacterSheet(
@ -97,6 +99,9 @@ fun MainPage(
onNetwork = {
screen.navigateToNetwork()
},
onMainPage = {
screen.navigateToMainPage()
}
)
}
}
@ -106,17 +111,20 @@ fun MainPage(
fun MainPageContent(
modifier: Modifier = Modifier,
characters: State<List<CharacterUio>>,
npcs: State<List<CharacterUio>>,
enableRollHistory: State<Boolean>,
onCharacter: (CharacterUio) -> Unit,
onCreateCharacter: () -> Unit,
onRollHistory: () -> Unit,
onOpenSaveDirectory: () -> Unit,
onNetwork: () -> Unit,
onMainPage: () -> Unit,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
if (characters.value.isNotEmpty()) {
Column {
characters.value.forEach { sheet ->
TextButton(
onClick = { onCharacter(sheet) },
@ -130,6 +138,30 @@ fun MainPageContent(
)
}
}
}
}
if (characters.value.isNotEmpty() && npcs.value.isNotEmpty()) {
Spacer(modifier = Modifier.height(height = 24.dp))
}
if (npcs.value.isNotEmpty()) {
Column {
npcs.value.forEach { sheet ->
TextButton(
onClick = { onCharacter(sheet) },
) {
Text(
modifier = Modifier.fillMaxWidth(),
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Start,
maxLines = 1,
text = sheet.name,
)
}
}
}
}
Spacer(modifier = Modifier.height(height = 24.dp))
@ -217,5 +249,25 @@ fun MainPageContent(
)
}
}
TextButton(
onClick = onMainPage,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painter = painterResource(Res.drawable.ic_swords_24dp),
contentDescription = null,
)
Text(
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Start,
maxLines = 1,
text = "Nouvelle interface utilisateur",
)
}
}
}
}

View file

@ -15,8 +15,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
class MainPageViewModel(
@ -27,21 +28,48 @@ class MainPageViewModel(
@OptIn(ExperimentalCoroutinesApi::class)
val characters: StateFlow<List<CharacterUio>> = campaignRepository.campaignFlow
.flatMapLatest { campaign ->
.flatMapMerge { campaign ->
combine(
campaign.characters.map { entry ->
characterSheetRepository.characterDetailFlow(characterId = entry.key.characterSheetId)
.mapNotNull { sheet ->
sheet?.let {
CharacterUio(
id = entry.key,
name = it.name,
campaign.characters
.map { entry ->
characterSheetRepository
.characterDetailFlow(characterId = entry.key.characterSheetId)
.map transform@{ sheet ->
if (sheet == null) return@transform null
CharacterUio(id = entry.key, name = sheet.name)
}
}
.ifEmpty {
listOf(flowOf(null))
}
) { data ->
data.mapNotNull { it }
}
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = emptyList(),
)
@OptIn(ExperimentalCoroutinesApi::class)
val npcs: StateFlow<List<CharacterUio>> = campaignRepository.campaignFlow
.flatMapMerge { campaign ->
combine(
campaign.npcs
.map { entry ->
characterSheetRepository
.characterDetailFlow(characterId = entry.key.characterSheetId)
.map transform@{ sheet ->
if (sheet == null) return@transform null
CharacterUio(id = entry.key, name = sheet.name)
}
}
.ifEmpty {
listOf(flowOf(null))
}
) {
it.asList()
) { data ->
data.mapNotNull { it }
}
}
.stateIn(

View file

@ -59,12 +59,14 @@ class RollViewModel(
private val _displayOverlay = mutableStateOf(false)
val displayOverlay: State<Boolean> get() = _displayOverlay
@Deprecated(message = "@See prepareRoll(RollActionUio)")
fun prepareRoll(
sheet: CharacterSheetPageUio,
characteristic: CharacterSheetPageUio.Characteristic,
) {
val diminished =
0 // TODO characterSheetRepository.characterDiminishedFlow(id = sheet.id).value
// TODO characterSheetRepository.characterDiminishedFlow(id = sheet.id).value
val diminished = 0
prepareRoll(
characterSheetId = sheet.id,
label = characteristic.label,
@ -73,6 +75,7 @@ class RollViewModel(
)
}
@Deprecated(message = "@See prepareRoll(RollActionUio)")
fun prepareRoll(
sheet: CharacterSheetPageUio,
node: CharacterSheetPageUio.Node,
@ -85,6 +88,7 @@ class RollViewModel(
)
}
@Deprecated(message = "@See prepareRoll(RollActionUio)")
fun prepareRoll(
sheet: CharacterSheetPageUio,
roll: CharacterSheetPageUio.Roll,
@ -115,8 +119,8 @@ class RollViewModel(
this.sheet = runBlocking {
rollRotation.snapTo(0f)
rollScale.snapTo(1f)
characterSheetRepository.characterDetail(characterSheetId = characterSheetId)!!
}
characterSheetRepository.characterDetail(characterSheetId = characterSheetId)
} ?: return
this.rollAction = rollAction
this.rollSuccessValue = rollSuccessValue

View file

@ -5,6 +5,7 @@ import androidx.compose.material.darkColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.unit.Dp
@ -15,6 +16,7 @@ import kotlin.math.ln
data class LwaColorTheme(
val base: Colors,
val elevated: Elevated,
val portraitBackgroundBrush: Brush,
) {
@Stable
data class Elevated(
@ -39,9 +41,19 @@ fun darkLwaColorTheme(
elevation = 2.dp,
),
),
portraitBackgroundBrush: Brush = Brush.verticalGradient(
listOf(
elevated.base1dp.copy(alpha = 0.0f),
elevated.base1dp.copy(alpha = 0.0f),
elevated.base1dp.copy(alpha = 0.0f),
elevated.base1dp.copy(alpha = 0.5f),
elevated.base1dp.copy(alpha = 0.8f),
)
)
): LwaColorTheme = LwaColorTheme(
base = base,
elevated = elevated,
portraitBackgroundBrush = portraitBackgroundBrush,
)
@ReadOnlyComposable

View file

@ -2,7 +2,7 @@ package com.pixelized.server.lwa.model.alteration
import com.pixelized.shared.lwa.model.alteration.AlterationJson
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -19,7 +19,7 @@ class AlterationService(
private val actives = store.activeFlow()
.map { data ->
data.mapKeys { it: Map.Entry<String, List<String>> ->
campaignJsonFactory.convertFromV1(characterInstanceIdJson = it.key)
campaignJsonFactory.characterInstanceIdFromJson(characterInstanceIdJson = it.key)
}
}.stateIn(
scope = scope,

View file

@ -3,14 +3,13 @@ package com.pixelized.server.lwa.model.alteration
import com.pixelized.shared.lwa.alterationsPath
import com.pixelized.shared.lwa.model.alteration.AlterationJson
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File

View file

@ -2,8 +2,8 @@ package com.pixelized.server.lwa.model.campaign
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJson
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.character
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.npc
import com.pixelized.shared.lwa.usecase.CampaignUseCase
import kotlinx.coroutines.CoroutineScope
@ -47,10 +47,14 @@ class CampaignService(
// update the corresponding character
characters[characterInstanceId] = campaign.character(id = characterInstanceId)
// save the campaign to the disk + update the flow.
return try {
store.save(
campaign = campaign.copy(characters = characters)
)
return true
true
} catch (exception: Exception) {
false
}
}
suspend fun removeCharacter(
@ -63,10 +67,14 @@ class CampaignService(
// update the corresponding character
characters.remove(characterInstanceId)
// save the campaign to the disk + update the flow.
return try {
store.save(
campaign = campaign.copy(characters = characters)
)
return true
true
} catch (exception: Exception) {
false
}
}
suspend fun addNpc(
@ -79,10 +87,14 @@ class CampaignService(
// update the corresponding character
npcs[npcInstanceId] = campaign.npc(id = npcInstanceId)
// save the campaign to the disk + update the flow.
return try {
store.save(
campaign = campaign.copy(npcs = npcs)
)
return true
true
} catch (exception: Exception) {
false
}
}
suspend fun removeNpc(
@ -95,10 +107,28 @@ class CampaignService(
// update the corresponding character
npcs.remove(npcInstanceId)
// save the campaign to the disk + update the flow.
return try {
store.save(
campaign = campaign.copy(npcs = npcs)
)
return true
true
} catch (exception: Exception) {
false
}
}
suspend fun setScene(
scene: Campaign.Scene,
): Boolean {
// save the campaign to the disk + update the flow.
return try {
store.save(
campaign.copy(scene = scene)
)
true
} catch (exception: Exception) {
false
}
}
// Data manipulation through WebSocket.

View file

@ -3,7 +3,7 @@ package com.pixelized.server.lwa.model.campaign
import com.pixelized.shared.lwa.campaignPath
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJson
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job

View file

@ -4,7 +4,7 @@ import com.pixelized.server.lwa.model.alteration.AlterationService
import com.pixelized.server.lwa.model.campaign.CampaignService
import com.pixelized.server.lwa.model.character.CharacterSheetService
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import com.pixelized.shared.lwa.protocol.websocket.Message
import com.pixelized.shared.lwa.protocol.websocket.payload.CampaignMessage
import com.pixelized.shared.lwa.protocol.websocket.payload.RestSynchronisation
@ -33,7 +33,7 @@ class Engine(
when (data) {
is CampaignMessage.UpdateCharacteristic -> campaignService.updateCharacteristic(
characterInstanceId = instanceId,
characteristic = campaignJsonFactory.convertFromV1(characteristicJson = data.characteristic),
characteristic = campaignJsonFactory.convertFromJson(json = data.characteristic),
value = data.value,
)

View file

@ -11,6 +11,7 @@ import com.pixelized.server.lwa.server.rest.campaign.deleteCampaignNpc
import com.pixelized.server.lwa.server.rest.campaign.getCampaign
import com.pixelized.server.lwa.server.rest.campaign.putCampaignCharacter
import com.pixelized.server.lwa.server.rest.campaign.putCampaignNpc
import com.pixelized.server.lwa.server.rest.campaign.putCampaignScene
import com.pixelized.server.lwa.server.rest.character.deleteCharacter
import com.pixelized.server.lwa.server.rest.character.getCharacter
import com.pixelized.server.lwa.server.rest.character.getCharacters
@ -146,6 +147,10 @@ class LocalServer {
body = engine.deleteCampaignNpc(),
)
}
put(
path = "/scene",
body = engine.putCampaignScene(),
)
}
route(path = "/alterations") {
get(

View file

@ -0,0 +1,38 @@
package com.pixelized.server.lwa.server.rest.campaign
import com.pixelized.server.lwa.server.Engine
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJsonV1
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJson
import com.pixelized.shared.lwa.protocol.websocket.Message
import com.pixelized.shared.lwa.protocol.websocket.payload.RestSynchronisation
import io.ktor.http.HttpStatusCode
import io.ktor.server.request.receive
import io.ktor.server.response.respondText
fun Engine.putCampaignScene(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
return {
val form = call.receive<CampaignJsonV1.SceneJsonV1>()
val scene = campaignJsonFactory.convertFromJson(json = form)
val updated = campaignService.setScene(scene = scene)
val code = when (updated) {
true -> HttpStatusCode.Accepted
else -> HttpStatusCode.UnprocessableEntity
}
call.respondText(
text = "$code",
status = code,
)
webSocket.emit(
Message(
from = "Server",
value = RestSynchronisation.Campaign,
)
)
}
}

View file

@ -2,7 +2,8 @@ package com.pixelized.shared.lwa
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
import com.pixelized.shared.lwa.model.alteration.AlterationJsonFactory
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonV1Factory
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJsonFactory
import com.pixelized.shared.lwa.parser.dice.DiceParser
import com.pixelized.shared.lwa.parser.expression.ExpressionParser
@ -38,6 +39,7 @@ val factoryDependencies
get() = module {
factoryOf(::CharacterSheetJsonFactory)
factoryOf(::CampaignJsonFactory)
factoryOf(::CampaignJsonV1Factory)
factoryOf(::AlteredCharacterSheetFactory)
factoryOf(::AlterationJsonFactory)
}

View file

@ -3,6 +3,7 @@ package com.pixelized.shared.lwa.model.campaign
data class Campaign(
val characters: Map<CharacterInstance.Id, CharacterInstance>,
val npcs: Map<CharacterInstance.Id, CharacterInstance>,
val scene: Scene,
) {
data class CharacterInstance(
val characteristic: Map<Characteristic, Int>,
@ -26,10 +27,21 @@ data class Campaign(
}
}
data class Scene(
val name: String,
) {
companion object {
fun empty() = Scene(
name = "",
)
}
}
companion object {
val EMPTY = Campaign(
characters = emptyMap(),
npcs = emptyMap(),
scene = Scene.empty(),
)
}
}

View file

@ -3,4 +3,15 @@ package com.pixelized.shared.lwa.model.campaign
import kotlinx.serialization.Serializable
@Serializable
sealed interface CampaignJson
sealed interface CampaignJson {
@Serializable
sealed interface CharacterInstanceJson {
@Serializable
sealed interface CharacteristicJson
}
@Serializable
sealed interface SceneJson
}

View file

@ -1,101 +0,0 @@
package com.pixelized.shared.lwa.model.campaign
class CampaignJsonFactory {
fun convertFromJson(
json: CampaignJson,
): Campaign {
return when (json) {
is CampaignJsonV1 -> convertFromV1(campaignJson = json)
}
}
private fun convertFromV1(
campaignJson: CampaignJsonV1,
): Campaign {
return Campaign(
characters = campaignJson.characters
.map {
convertFromV1(characterInstanceIdJson = it.key) to convertFromV1(
characterInstanceJson = it.value
)
}
.toMap(),
npcs = campaignJson.npcs
.map {
convertFromV1(characterInstanceIdJson = it.key) to convertFromV1(
characterInstanceJson = it.value
)
}
.toMap(),
)
}
fun convertFromV1(
characterInstanceIdJson: String,
): Campaign.CharacterInstance.Id {
return Campaign.CharacterInstance.Id(
characterSheetId = characterInstanceIdJson.drop(4), // drop first 3 number then the -
instanceId = characterInstanceIdJson.take(3).toIntOrNull() ?: 0,
)
}
fun convertFromV1(
characterInstanceJson: CampaignJsonV1.CharacterInstanceJson,
): Campaign.CharacterInstance {
return Campaign.CharacterInstance(
characteristic = characterInstanceJson.characteristic
.map { char -> convertFromV1(characteristicJson = char.key) to char.value }
.toMap(),
diminished = characterInstanceJson.diminished ?: 0,
)
}
fun convertFromV1(
characteristicJson: CampaignJsonV1.CharacterInstanceJson.Characteristic,
): Campaign.CharacterInstance.Characteristic {
return when (characteristicJson) {
CampaignJsonV1.CharacterInstanceJson.Characteristic.Damage -> Campaign.CharacterInstance.Characteristic.Damage
CampaignJsonV1.CharacterInstanceJson.Characteristic.Power -> Campaign.CharacterInstance.Characteristic.Power
}
}
fun convertToJson(
data: Campaign,
): CampaignJson {
return CampaignJsonV1(
characters = data.characters
.map { convertToJson(id = it.key) to convertToJson(data = it.value) }
.toMap(),
npcs = data.npcs
.map { convertToJson(id = it.key) to convertToJson(data = it.value) }
.toMap(),
)
}
fun convertToJson(
id: Campaign.CharacterInstance.Id,
): String {
return "${String.format("%03d", id.instanceId)}-${id.characterSheetId}"
}
fun convertToJson(
data: Campaign.CharacterInstance,
): CampaignJsonV1.CharacterInstanceJson {
return CampaignJsonV1.CharacterInstanceJson(
characteristic = data.characteristic
.map { char -> convertToJson(characteristic = char.key) to char.value }
.toMap(),
diminished = data.diminished,
)
}
fun convertToJson(
characteristic: Campaign.CharacterInstance.Characteristic,
): CampaignJsonV1.CharacterInstanceJson.Characteristic {
return when (characteristic) {
Campaign.CharacterInstance.Characteristic.Damage -> CampaignJsonV1.CharacterInstanceJson.Characteristic.Damage
Campaign.CharacterInstance.Characteristic.Power -> CampaignJsonV1.CharacterInstanceJson.Characteristic.Power
}
}
}

View file

@ -4,18 +4,24 @@ import kotlinx.serialization.Serializable
@Serializable
data class CampaignJsonV1(
val characters: Map<String, CharacterInstanceJson>,
val npcs: Map<String, CharacterInstanceJson>,
val characters: Map<String, CharacterInstanceJsonV1>,
val npcs: Map<String, CharacterInstanceJsonV1>,
val scene: SceneJsonV1?,
) : CampaignJson {
@Serializable
data class CharacterInstanceJson(
val characteristic: Map<Characteristic, Int>,
data class CharacterInstanceJsonV1(
val characteristic: Map<CharacteristicV1, Int>,
val diminished: Int?,
) {
enum class Characteristic {
) : CampaignJson.CharacterInstanceJson {
enum class CharacteristicV1 : CampaignJson.CharacterInstanceJson.CharacteristicJson {
Damage,
Power,
}
}
@Serializable
data class SceneJsonV1(
val name: String,
) : CampaignJson.SceneJson
}

View file

@ -0,0 +1,98 @@
package com.pixelized.shared.lwa.model.campaign.factory
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJson
import com.pixelized.shared.lwa.model.campaign.CampaignJsonV1
class CampaignJsonFactory(
private val v1: CampaignJsonV1Factory,
) {
fun characterInstanceIdFromJson(
characterInstanceIdJson: String,
): Campaign.CharacterInstance.Id {
return v1.convertFromV1(characterInstanceIdJson)
}
fun convertFromJson(
json: CampaignJson,
): Campaign {
return when (json) {
is CampaignJsonV1 -> v1.convertFromV1(campaignJson = json)
}
}
fun convertFromJson(
json: CampaignJson.CharacterInstanceJson,
): Campaign.CharacterInstance {
return when (json) {
is CampaignJsonV1.CharacterInstanceJsonV1 -> v1.convertFromV1(characterInstanceJson = json)
}
}
fun convertFromJson(
json: CampaignJson.CharacterInstanceJson.CharacteristicJson,
): Campaign.CharacterInstance.Characteristic {
return when (json) {
CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1.Damage -> Campaign.CharacterInstance.Characteristic.Damage
CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1.Power -> Campaign.CharacterInstance.Characteristic.Power
}
}
fun convertFromJson(
json: CampaignJson.SceneJson,
): Campaign.Scene {
return when (json) {
is CampaignJsonV1.SceneJsonV1 -> v1.convertFromV1(sceneJson = json)
}
}
fun convertToJson(
data: Campaign,
): CampaignJson {
return CampaignJsonV1(
characters = data.characters
.map { convertToJson(id = it.key) to convertToJson(data = it.value) }
.toMap(),
npcs = data.npcs
.map { convertToJson(id = it.key) to convertToJson(data = it.value) }
.toMap(),
scene = CampaignJsonV1.SceneJsonV1(
name = data.scene.name
)
)
}
fun convertToJson(
id: Campaign.CharacterInstance.Id,
): String {
return "${String.format("%03d", id.instanceId)}-${id.characterSheetId}"
}
fun convertToJson(
data: Campaign.CharacterInstance,
): CampaignJsonV1.CharacterInstanceJsonV1 {
return CampaignJsonV1.CharacterInstanceJsonV1(
characteristic = data.characteristic
.map { char -> convertToJson(characteristic = char.key) to char.value }
.toMap(),
diminished = data.diminished,
)
}
fun convertToJson(
characteristic: Campaign.CharacterInstance.Characteristic,
): CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1 {
return when (characteristic) {
Campaign.CharacterInstance.Characteristic.Damage -> CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1.Damage
Campaign.CharacterInstance.Characteristic.Power -> CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1.Power
}
}
fun convertToJson(
scene: Campaign.Scene,
): CampaignJsonV1.SceneJsonV1 {
return CampaignJsonV1.SceneJsonV1(
name = scene.name,
)
}
}

View file

@ -0,0 +1,68 @@
package com.pixelized.shared.lwa.model.campaign.factory
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJsonV1
class CampaignJsonV1Factory {
fun convertFromV1(
campaignJson: CampaignJsonV1,
): Campaign {
return Campaign(
characters = campaignJson.characters
.map {
val key = convertFromV1(characterInstanceIdJson = it.key)
val instance = convertFromV1(characterInstanceJson = it.value)
key to instance
}
.toMap(),
npcs = campaignJson.npcs
.map {
val key = convertFromV1(characterInstanceIdJson = it.key)
val instance = convertFromV1(characterInstanceJson = it.value)
key to instance
}
.toMap(),
scene = campaignJson.scene
?.let { convertFromV1(it) }
?: Campaign.Scene.empty(),
)
}
fun convertFromV1(
characterInstanceIdJson: String,
): Campaign.CharacterInstance.Id {
return Campaign.CharacterInstance.Id(
characterSheetId = characterInstanceIdJson.drop(4), // drop first 3 number then the -
instanceId = characterInstanceIdJson.take(3).toIntOrNull() ?: 0,
)
}
fun convertFromV1(
characterInstanceJson: CampaignJsonV1.CharacterInstanceJsonV1,
): Campaign.CharacterInstance {
return Campaign.CharacterInstance(
characteristic = characterInstanceJson.characteristic
.mapKeys { convertFromV1(characteristicJson = it.key) }
.toMap(),
diminished = characterInstanceJson.diminished ?: 0,
)
}
fun convertFromV1(
characteristicJson: CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1,
): Campaign.CharacterInstance.Characteristic {
return when (characteristicJson) {
CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1.Damage -> Campaign.CharacterInstance.Characteristic.Damage
CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1.Power -> Campaign.CharacterInstance.Characteristic.Power
}
}
fun convertFromV1(
sceneJson: CampaignJsonV1.SceneJsonV1,
): Campaign.Scene {
return Campaign.Scene(
name = sceneJson.name
)
}
}

View file

@ -12,7 +12,7 @@ sealed interface CampaignMessage : MessagePayload {
data class UpdateCharacteristic(
override val characterSheetId: String,
override val instanceId: Int,
val characteristic: CampaignJsonV1.CharacterInstanceJson.Characteristic,
val characteristic: CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1,
val value: Int,
) : CampaignMessage

View file

@ -60,13 +60,16 @@ class ExpressionUseCase(
alterations: Map<String, List<FieldAlteration>>,
expression: String,
): Int {
return expressionParser.parse(input = expression)?.let {
print("Roll::$expression::")
val roll = expressionParser.parse(input = expression)?.let {
computeExpression(
sheet = sheet,
alterations = alterations,
expression = it,
)
} ?: 0
println("::$roll")
return roll
}
fun computeExpression(

View file

@ -59,7 +59,7 @@ class RollUseCase {
if (quantity > 1 && left != 1) print(",")
}
}.also {
println("}")
print("}")
}
}