diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 533e975..6361bbf 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -1,6 +1,8 @@
+ ▸
+
La feuille de personnage est introuvable
Ok
@@ -112,36 +114,20 @@
Êtes-vous sûr de vouloir supprimer "%1$s" ?
Les caractéristiques constituent les aptitudes innées d’un personnage comme son intelligence, sa force, son charisme, etc. Elles ne sont pas acquises, mais peuvent être parfois augmentées par un entraînement ou une utilisation réussie. Les caractéristiques des humains normaux varient de 2 (niveau extrêmement bas) à 20 (maximum du potentiel humain), avec une moyenne de 10 ou 11. Plus une caractéristique est élevée plus le personnage est puissant dans cette aptitude.\nÀ la création de votre personnage, répartissez les valeurs suivantes dans les différentes caractéristiques : 15, 15, 13, 11, 10, 9 et 7.
- La Force représente essentiellement la puissance musculaire du personnage. Elle ne décrit pas nécessairement la masse musculaire brute, mais l’efficacité avec laquelle le personnage exerce ses muscles pour accomplir des actions physiques pénibles.
- La Constitution est une mesure de la ténacité et de la résilience du personnage. Elle sert à résister aux maladies. Mais son aspect le plus important réside dans la détermination du nombre de dommages qu’un personnage peut supporter avant de succomber.
- La Taille est une mesure de la masse du personnage. Elle ne représente pas forcément la taille en centimètres, mais une idée générale de sa masse physique. Un personnage à la TAI élevée peut être très grand et mince, ou petit et massif, ou de taille moyenne en surpoids.
- L’Intelligence représente la capacité de discernement du personnage. Elle ne mesure pas nécessairement la somme d’informations mémorisées, mais l’aptitude au raisonnement, l’acuité intellectuelle, la capacité à résoudre des problèmes et l’intuition.
- Le Pouvoir est une mesure presque intangible de la force de volonté, du dynamisme intérieur et de l’énergie spirituelle. Il représente également le potentiel magique du personnage.
- La Dextérité mesure la coordination œil-main, la vitesse physique et l’agilité générale. La DEX détermine à quelle vitesse un personnage peut agir en combat.
- Il représente plusieurs aspects allant de la grâce à la beauté en passant par l’attraction que le personnage exerce sur les autres. Un personnage avec un CHA élevé se remarque dans une foule en raison d’une intangible combinaison de charme et de présence.
+ La Force représente essentiellement la puissance musculaire du personnage. Elle ne décrit pas nécessairement la masse musculaire brute, mais l’efficacité avec laquelle le personnage exerce ses muscles pour accomplir des actions physiques pénibles.\n\n- Bonus aux dégats\n- Athlétisme\n- Lancer\n- Saisie
+ La Constitution est une mesure de la ténacité et de la résilience du personnage. Elle sert à résister aux maladies. Mais son aspect le plus important réside dans la détermination du nombre de dommages qu’un personnage peut supporter avant de succomber.\n\n- Point de vie maximum\n- Athlétisme\n- Acrobatie
+ La Taille est une mesure de la masse du personnage. Elle ne représente pas forcément la taille en centimètres, mais une idée générale de sa masse physique. Un personnage à la TAI élevée peut être très grand et mince, ou petit et massif, ou de taille moyenne en surpoids.\n\n- Bonus aux dégats\n- Point de vie maximum\n- Saisie\n- Intimidation\n- Discrétion (impact négatif)
+ L’Intelligence représente la capacité de discernement du personnage. Elle ne mesure pas nécessairement la somme d’informations mémorisées, mais l’aptitude au raisonnement, l’acuité intellectuelle, la capacité à résoudre des problèmes et l’intuition.\n\n- Bonus d'apprentissage\n- Perception\n- Recherche\n- Empathie\n- Baratin\n- Premiers soins
+ Le Pouvoir est une mesure presque intangible de la force de volonté, du dynamisme intérieur et de l’énergie spirituelle. Il représente également le potentiel magique du personnage.\n\n- Points de pouvoir maximum\n- Intimidation
+ La Dextérité mesure la coordination œil-main, la vitesse physique et l’agilité générale. La DEX détermine à quelle vitesse un personnage peut agir en combat.\n\n- Initiative\n- Bagarre\n- Esquive\n- Lancer\n- Acrobatie\n- Discrétion\n- Escamotage\n- Premiers soins
+ Il représente plusieurs aspects allant de la grâce à la beauté en passant par l’attraction que le personnage exerce sur les autres. Un personnage avec un CHA élevé se remarque dans une foule en raison d’une intangible combinaison de charme et de présence.\n\n- Empathie\n- Persuasion\n- Persuasion\n- Intimidation\n- Baratin\n- Marchandage\n- Discrétion
Le Déplacement (DEP) est une valeur de jeu qui détermine la distance que peut parcourir un personnage en un round de combat. Tous les humains ont un DEP de 10. Le DEP a une valeur réelle flexible, mais généralement, chaque point de DEP équivaut à un déplacement d’un mètre. En course, un point équivaut à trois mètres.
Les points de vie (PV) sont égaux à la somme CON+TAI du personnage, divisée par deux (arrondie au supérieur). Ils sont soustraits lorsque le personnage subit des dommages. Quand les points de vie tombent à 0, le personnage sombre dans l’inconscience. S'il reste inconscient trop longtemps, il meurt. Tous les points de vie régénèrent naturellement après une nuit de repos.
les points de pouvoir sont égaux au POU et sont dépensés pour utiliser la magie ou d’autres pouvoirs. Tous les points de pouvoir régénèrent naturellement après une nuit de repos.
- Les personnages plus massifs ou plus forts infligent plus de dégâts quand ils frappent leurs ennemis en combat au corps à corps. Le modificateur s’applique aux dégâts infligés par toute attaque portée par les personnages avec des armes de mêlée (BDC). La moitié de ce bonus s'applique aux attaques de lancer (BDD).
+ Les personnages plus massifs ou plus forts infligent plus de dégâts quand ils frappent leurs ennemis. Le modificateur s’applique aux dégâts infligés par toute attaque portée par les personnages avec des armes de mêlée (BDC). La moitié de ce bonus s'applique aux attaques de lancer (BDD).\n\nBonus aux dégâts : Force + Taille\n- 2 à 12 : -1d6\n- 12 à 17 : -1d4\n- 18 à 22 : 0\n- 23 à 29 : +1d4\n- 30 à 39 : +1d6\n- Au delà : +2d6
Une armure protège son porteur des blessures. Lorsqu’un personnage est touché en combat par une attaque non magique, soustrayez les points d’armure aux points de dégâts infligés. Les dommages au-delà de la protection de l’armure surpassent celle-ci et sont infligés au personnage, réduisant ses points de vie actuels.
Plus un personnage est intelligent, plus il assimile rapidement les connaissances et plus il digère son expérience efficacement. Ce modificateur s'applique au score des compétences nouvellement acquises et aux jets d'expériences. Il est égal à (INT - 10) x 2 avec une valeur minimale de zéro.
Plus un personnage est de bonne constitution, plus son corps se renforce rapidement. Ce modificateur indique le nombre de "PV" maximum que le personnage gagne à chaque progression. Il est égal à CON / 3, arrondi à l'inférieur.
- Attaque en combat à mains nues. Une attaque réussie inflige 1D3 + BDGT.
- Éviter une attaque, un projectile, etc.
- Maitriser/immobiliser un adversaire.
- Viser et lancer quelque chose à travers les airs et vers une cible.
- Réaliser un mouvement nécessitant une certaine force (Enfoncer une porte, nager à contre-courant, etc.).
- Réaliser un mouvement nécessitant une certaine agilité (Marcher sur une corde, courir sur la glace, etc.).
- Aptitude à percevoir via ses sens ou à percevoir un détail difficile à remarquer.
- Aptitude à trouver des indices et à émettre des suppositions sur la base de ceux-ci.
- Évaluer les pensées et/ou les motivations cachées d’un autre personnage en se basant sur des signaux subliminaux.
- Influencer quelqu'un ou un groupe de personnes avec tact, grâce, ou de bonnes manières.
- Influencer quelqu'un ou un groupe de personnes par des menaces, des actions hostiles, ou de la violence physique.
- Mentir ou cacher la vérité avec efficacité, soit verbalement, soit par vos actions.
- Négocier les prix.
- Passer inaperçu.
- Réaliser une manipulation ou une fourberie, comme prendre quelque chose à quelqu'un ou dissimuler un objet sur vous-même.
- Traiter les blessures légères. Une réussite rétablit 1D3 points de vie. Une réussite spéciale restaure 1D3+3 points de vie.
Configuration de la table
Nom du joueur
@@ -156,6 +142,7 @@
Serveur
Client
Aucun
+ Un nom de joueur est requis
Vous êtes connecté au serveur
Vous êtes déconnecté du serveur
Ok
@@ -186,7 +173,10 @@
Montée de niveau
Level Up !
- Passage du niveau %1$d au niveau supérieur : %2$d
+ niv : %1$d▸%2$d
+ Passage du niveau %1$d ▸ %2$d
niv : %1$d -
+
+
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt
index 1fa5ce5..f3f7ddc 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt
@@ -32,9 +32,6 @@ import androidx.compose.ui.unit.min
import androidx.compose.ui.window.ApplicationScope
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.rememberWindowState
-import coil3.ImageLoader
-import coil3.compose.setSingletonImageLoaderFactory
-import coil3.request.crossfade
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.repository.network.NetworkRepository.Status
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
@@ -53,17 +50,18 @@ import com.pixelized.desktop.lwa.ui.navigation.window.destination.CharacterSheet
import com.pixelized.desktop.lwa.ui.navigation.window.destination.NetworkWindows
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.overlay.roll.RollHostState
+import com.pixelized.desktop.lwa.ui.overlay.roll.RollOverlay
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbon
import com.pixelized.desktop.lwa.ui.screen.characterSheet.CharacterSheetMainNavHost
import com.pixelized.desktop.lwa.ui.screen.network.NetworkPage
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
-import com.pixelized.desktop.lwa.ui.overlay.roll.RollHostState
-import com.pixelized.desktop.lwa.ui.overlay.roll.RollOverlay
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryPage
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
import com.pixelized.desktop.lwa.ui.theme.LwaTheme
+import com.pixelized.desktop.lwa.utils.InstallCoil
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res
@@ -102,7 +100,7 @@ val LocalBlurController = compositionLocalOf {
@Composable
@Preview
-fun ApplicationScope.App() {
+fun ApplicationScope.LwaApplication() {
val maxWindowHeight = rememberMaxWindowHeight()
val snackHostState = remember { SnackbarHostState() }
val errorSnackHostState = remember { SnackbarHostState() }
@@ -120,12 +118,13 @@ fun ApplicationScope.App() {
),
)
- // Coil configuration
- setSingletonImageLoaderFactory { context ->
- ImageLoader.Builder(context)
- .crossfade(enable = false)
- .build()
- }
+ // Coil configuration.
+ InstallCoil(
+ crossfade = false,
+ diskCachePath = { provider ->
+ provider.imagesStorePath()
+ }
+ )
CompositionLocalProvider(
LocalApplicationScope provides this,
@@ -150,16 +149,18 @@ fun ApplicationScope.App() {
}
}
+
@Composable
private fun MainWindowScreen(
- campaignViewModel: CampaignViewModel = koinViewModel(),
+ dataSyncViewModel: DataSyncViewModel = koinViewModel(),
networkViewModel: NetworkViewModel = koinViewModel(),
+ campaignViewModel: CampaignViewModel = koinViewModel(),
campaignChatViewModel: CampaignChatViewModel = koinViewModel(),
rollViewModel: RollHistoryViewModel = koinViewModel(),
) {
LaunchedEffect(Unit) {
- networkViewModel.connect()
- campaignViewModel.init()
+ dataSyncViewModel.autoConnect()
+ dataSyncViewModel.synchronise()
}
val snackHostState = LocalSnackHost.current
@@ -205,15 +206,12 @@ private fun MainWindowScreen(
content = {
BlurContent(
modifier = Modifier.fillMaxSize(),
- controller = blurController
+ controller = blurController,
) {
- MainNavHost(
- campaignViewModel = campaignViewModel,
- networkViewModel = networkViewModel,
- campaignChatViewModel = campaignChatViewModel,
- )
+ MainNavHost()
}
RollOverlay(
+ modifier = Modifier.fillMaxSize(),
hostState = rollHostState,
)
}
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt
new file mode 100644
index 0000000..4eaa5a8
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt
@@ -0,0 +1,58 @@
+package com.pixelized.desktop.lwa
+
+import androidx.lifecycle.ViewModel
+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 com.pixelized.desktop.lwa.repository.network.NetworkRepository
+import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
+import com.pixelized.shared.lwa.model.campaign.Campaign
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+class DataSyncViewModel(
+ private val characterRepository: CharacterSheetRepository,
+ private val alterationRepository: AlterationRepository,
+ private val campaignRepository: CampaignRepository,
+ private val settingsRepository: SettingsRepository,
+ private val networkRepository: NetworkRepository,
+) : ViewModel() {
+
+ fun autoConnect() {
+ val settings = settingsRepository.settings()
+
+ if (settings.playerName.isEmpty()) return
+
+ networkRepository.connect(
+ host = settings.host,
+ port = settings.port,
+ )
+ }
+
+ suspend fun synchronise() = coroutineScope {
+
+ networkRepository.status
+ .filter { status -> status == NetworkRepository.Status.CONNECTED }
+ .onEach { campaignRepository.update() }
+ .launchIn(this)
+
+ networkRepository.status
+ .filter { status -> status == NetworkRepository.Status.CONNECTED }
+ .combine(campaignRepository.campaignFlow) { _, campaign: Campaign -> campaign }
+ .onEach { campaign ->
+ campaign.characters.keys.forEach { id ->
+ characterRepository.characterDetail(
+ characterSheetId = id.characterSheetId,
+ forceUpdate = true,
+ )
+ alterationRepository.updateActiveAlterations(
+ characterInstanceId = id,
+ )
+ }
+ }
+ .launchIn(this)
+ }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt
index c4b528d..6cd6669 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt
@@ -15,8 +15,8 @@ import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.desktop.lwa.repository.settings.SettingsStore
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogFactory
+import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
-import com.pixelized.desktop.lwa.ui.screen.levelup.LevelUpViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.TextMessageFactory
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailFactory
@@ -30,10 +30,10 @@ import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.CharacterSheetEdi
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.CharacterSheetEditViewModel
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.SkillFieldFactory
import com.pixelized.desktop.lwa.ui.screen.levelup.LevelUpFactory
+import com.pixelized.desktop.lwa.ui.screen.levelup.LevelUpViewModel
import com.pixelized.desktop.lwa.ui.screen.main.MainPageViewModel
import com.pixelized.desktop.lwa.ui.screen.network.NetworkFactory
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
-import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
import com.pixelized.desktop.lwa.ui.screen.settings.SettingsViewModel
import com.pixelized.desktop.lwa.usecase.SettingsUseCase
@@ -116,6 +116,7 @@ val factoryDependencies
val viewModelDependencies
get() = module {
+ viewModelOf(::DataSyncViewModel)
viewModelOf(::CampaignViewModel)
viewModelOf(::MainPageViewModel)
viewModelOf(::CharacterSheetViewModel)
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/tooltip/Tooltip.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/tooltip/Tooltip.kt
index bfe6ee5..467cfd6 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/tooltip/Tooltip.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/tooltip/Tooltip.kt
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
@@ -40,7 +41,12 @@ fun TooltipLayout(
else -> TooltipArea(
modifier = modifier,
- tooltip = { Tooltip(tooltip = tooltip) },
+ tooltip = {
+ Tooltip(
+ modifier = Modifier.width(width = 448.dp),
+ tooltip = tooltip,
+ )
+ },
content = content,
delayMillis = delayMillis,
tooltipPlacement = tooltipPlacement,
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/MainNavHost.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/MainNavHost.kt
index 46d8789..2d326ad 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/MainNavHost.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/MainNavHost.kt
@@ -23,9 +23,6 @@ val LocalScreenController = compositionLocalOf {
@Composable
fun MainNavHost(
controller: NavHostController = rememberNavController(),
- campaignViewModel: CampaignViewModel,
- networkViewModel: NetworkViewModel,
- campaignChatViewModel: CampaignChatViewModel,
startDestination: String = MainDestination.navigationRoute(),
) {
CompositionLocalProvider(
@@ -35,11 +32,7 @@ fun MainNavHost(
navController = controller,
startDestination = startDestination,
) {
- composableMainPage(
- campaignViewModel = campaignViewModel,
- networkViewModel = networkViewModel,
- campaignChatViewModel = campaignChatViewModel,
- )
+ composableMainPage()
composableSettingsPage()
composableLevelUp()
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/MainDestination.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/MainDestination.kt
index 2b7f3f8..5e33a2a 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/MainDestination.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/MainDestination.kt
@@ -3,10 +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.campaign.CampaignViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignScreen
-import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
-import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
object MainDestination {
private const val ROUTE = "main"
@@ -15,19 +12,11 @@ object MainDestination {
fun navigationRoute() = ROUTE
}
-fun NavGraphBuilder.composableMainPage(
- campaignViewModel: CampaignViewModel,
- networkViewModel: NetworkViewModel,
- campaignChatViewModel: CampaignChatViewModel,
-) {
+fun NavGraphBuilder.composableMainPage() {
composable(
route = MainDestination.baseRoute(),
) {
- CampaignScreen(
- campaignViewModel = campaignViewModel,
- networkViewModel = networkViewModel,
- campaignChatViewModel = campaignChatViewModel,
- )
+ CampaignScreen()
}
}
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/roll/RollOverlay.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/roll/RollOverlay.kt
index 160a91c..800e2ee 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/roll/RollOverlay.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/roll/RollOverlay.kt
@@ -25,6 +25,7 @@ import org.koin.compose.viewmodel.koinViewModel
@Composable
fun RollOverlay(
viewModel: RollViewModel = koinViewModel(),
+ modifier: Modifier = Modifier,
hostState: RollHostState,
) {
val blur = LocalBlurController.current
@@ -43,7 +44,7 @@ fun RollOverlay(
}
AnimatedContent(
- modifier = Modifier.fillMaxSize(),
+ modifier = modifier,
targetState = hostState.rollAction.value,
transitionSpec = {
val enter = fadeIn() + slideInVertically { 64 }
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt
index 4064fe6..bb3bf52 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt
@@ -61,13 +61,11 @@ fun CampaignScreen(
campaignViewModel: CampaignViewModel = koinViewModel(),
networkViewModel: NetworkViewModel = koinViewModel(),
campaignChatViewModel: CampaignChatViewModel = koinViewModel(),
- rollViewModel: RollViewModel = koinViewModel(),
) {
val screen = LocalScreenController.current
val blurController = rememberBlurContentController()
val scope = rememberCoroutineScope()
-
KeyHandler {
when {
it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/header/CharacterDetailHeader.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/header/CharacterDetailHeader.kt
index 07b157f..34d59c4 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/header/CharacterDetailHeader.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/header/CharacterDetailHeader.kt
@@ -80,7 +80,7 @@ data class CharacterDetailHeaderUio(
@Composable
fun CharacterDetailHeader(
modifier: Modifier = Modifier,
- iconSize: Dp = 14.dp,
+ iconSize: Dp = MaterialTheme.lwa.size.sheet.subCategory,
header: State,
onDismissRequest: () -> Unit,
onDiminished: () -> Unit,
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheet.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheet.kt
index 6a273e1..fec8e53 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheet.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheet.kt
@@ -52,7 +52,7 @@ fun CharacterDetailSheet(
) {
sheet.value?.characteristics?.forEach {
CharacterDetailSheetCharacteristic(
- modifier = Modifier.size(size = MaterialTheme.lwa.size.characteristic),
+ modifier = Modifier.size(size = MaterialTheme.lwa.size.sheet.characteristic),
characteristic = it,
onClick = { onCharacteristic(it) },
)
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelScreen.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelScreen.kt
index 21e8bc9..213f817 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelScreen.kt
@@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -55,6 +57,7 @@ import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import coil3.compose.AsyncImage
@@ -68,6 +71,8 @@ import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristic
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristicUio
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkill
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkillUio
+import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSubCharacteristic
+import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSubCharacteristicUio
import com.pixelized.desktop.lwa.ui.theme.lwa
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
@@ -75,7 +80,7 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet__skills_
import lwacharactersheet.composeapp.generated.resources.character_sheet__skills__magic_title
import lwacharactersheet.composeapp.generated.resources.character_sheet__skills__special_title
import lwacharactersheet.composeapp.generated.resources.level_up__action
-import lwacharactersheet.composeapp.generated.resources.level_up__character_level_description
+import lwacharactersheet.composeapp.generated.resources.level_up__character__label
import lwacharactersheet.composeapp.generated.resources.level_up__title
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
@@ -86,6 +91,11 @@ data class LevelUpHeaderUio(
val name: String,
val level: Int,
val portrait: String?,
+ val hp: LevelUpSubCharacteristicUio,
+ val pp: LevelUpSubCharacteristicUio,
+ val bonus: LevelUpSubCharacteristicUio,
+ val grow: LevelUpSubCharacteristicUio,
+ val learn: LevelUpSubCharacteristicUio,
)
@Stable
@@ -217,18 +227,49 @@ private fun LevelUpContent(
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
- Text(
- style = MaterialTheme.lwa.typography.base.h5,
- text = header.value?.name ?: ""
- )
- Text(
- style = MaterialTheme.lwa.typography.base.body1,
- text = (header.value?.level ?: 0).let {
- stringResource(
- Res.string.level_up__character_level_description, it, it + 1
- )
- },
- )
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
+ ) {
+ Text(
+ modifier = Modifier.alignByBaseline(),
+ style = MaterialTheme.typography.h5,
+ text = header.value?.name ?: "",
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
+ )
+ Text(
+ modifier = Modifier.alignByBaseline(),
+ style = MaterialTheme.lwa.typography.base.caption,
+ text = (header.value?.level ?: 0).let { level ->
+ stringResource(
+ resource = Res.string.level_up__character__label,
+ level, level + 1
+ )
+ },
+ )
+ }
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(space = 16.dp),
+ ) {
+ LevelUpSubCharacteristic(
+ characteristic = header.value?.hp,
+ )
+ LevelUpSubCharacteristic(
+ characteristic = header.value?.pp,
+ )
+ Spacer(
+ modifier = Modifier.weight(weight = 1f)
+ )
+ LevelUpSubCharacteristic(
+ characteristic = header.value?.bonus,
+ )
+ LevelUpSubCharacteristic(
+ characteristic = header.value?.grow,
+ )
+ LevelUpSubCharacteristic(
+ characteristic = header.value?.learn,
+ )
+ }
}
Column(
modifier = Modifier.verticalScroll(state = scrollState),
@@ -251,7 +292,7 @@ private fun LevelUpContent(
key = { it.characteristicId }
) {
LevelUpCharacteristic(
- modifier = Modifier.size(size = MaterialTheme.lwa.size.characteristic),
+ modifier = Modifier.size(size = MaterialTheme.lwa.size.sheet.characteristic),
characteristic = it,
onClick = { onCharacteristic(it) },
)
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelUpFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelUpFactory.kt
index 245a377..55911d4 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelUpFactory.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelUpFactory.kt
@@ -5,10 +5,14 @@ import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristicUio
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkillUio
+import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSubCharacteristicUio
import com.pixelized.shared.lwa.model.AlteredCharacterSheet
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
+import com.pixelized.shared.lwa.model.alteration.Alteration
+import com.pixelized.shared.lwa.model.alteration.FieldAlteration
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
+import com.pixelized.shared.lwa.parser.expression.Expression
import com.pixelized.shared.lwa.usecase.ExpressionUseCase
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__cha
@@ -18,6 +22,20 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet__charact
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__int
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__pow
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__str
+import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__armor
+import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__damage_bonus
+import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__hit_point
+import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__hp_grow
+import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__learning
+import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__movement
+import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__power_point
+import lwacharactersheet.composeapp.generated.resources.ic_cognition_24dp
+import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
+import lwacharactersheet.composeapp.generated.resources.ic_heart_plus_24dp
+import lwacharactersheet.composeapp.generated.resources.ic_near_me
+import lwacharactersheet.composeapp.generated.resources.ic_shield_24dp
+import lwacharactersheet.composeapp.generated.resources.ic_swords_24dp
+import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__charisma
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__constitution
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__dexterity
@@ -25,6 +43,13 @@ import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics
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 lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__armor
+import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__bonus_damage
+import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__hit_point
+import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__hp_grow
+import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__learning
+import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__movement
+import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__power_point
import org.jetbrains.compose.resources.getString
class LevelUpFactory(
@@ -32,14 +57,96 @@ class LevelUpFactory(
private val expressionUseCase: ExpressionUseCase,
) {
- fun convertToLevelUpHeaderUio(
+ suspend fun convertToLevelUpHeaderUio(
characterSheet: CharacterSheet?,
+ selectedCharacteristicId: String?,
): LevelUpHeaderUio? {
if (characterSheet == null) return null
+
+ val levelAlteration = FieldAlteration(
+ alterationId = CharacterSheet.CharacteristicId.LVL,
+ metadata = Alteration.MetaData(
+ name = "LevelUp-${CharacterSheet.CharacteristicId.LVL}",
+ description = "Fake alteration for a levelUp simulation. Alter the ${CharacterSheet.CharacteristicId.LVL} stat."
+ ),
+ expression = Expression.Flat(1),
+ )
+
+ val alteredCharacterSheet = alteredCharacterSheetFactory.sheet(
+ characterSheet = characterSheet,
+ alterations = emptyMap(),
+ )
+ val levelUpCharacterSheet = alteredCharacterSheetFactory.sheet(
+ characterSheet = characterSheet,
+ alterations = selectedCharacteristicId
+ ?.let {
+ val characteristic = FieldAlteration(
+ alterationId = it,
+ metadata = Alteration.MetaData(
+ name = "LevelUp-$it",
+ description = "Fake alteration for a levelUp simulation. Alter the $it stat."
+ ),
+ expression = Expression.Flat(1),
+ )
+ mapOf(
+ CharacterSheet.CharacteristicId.LVL to listOf(levelAlteration),
+ it to listOf(characteristic),
+ )
+ }
+ ?: mapOf(
+ CharacterSheet.CharacteristicId.LVL to listOf(levelAlteration),
+ ),
+ )
+
return LevelUpHeaderUio(
name = characterSheet.name,
portrait = characterSheet.portrait,
level = characterSheet.level,
+ hp = LevelUpSubCharacteristicUio(
+ icon = Res.drawable.ic_heart_24dp,
+ base = "${alteredCharacterSheet.maxHp}",
+ levelUp = "${levelUpCharacterSheet.maxHp}",
+ tooltip = TooltipUio(
+ title = getString(Res.string.character_sheet__sub_characteristics__hit_point),
+ description = getString(Res.string.tooltip__sub_characteristics__hit_point)
+ ),
+ ),
+ pp = LevelUpSubCharacteristicUio(
+ icon = Res.drawable.ic_water_drop_24dp,
+ base = "${alteredCharacterSheet.maxPp}",
+ levelUp = "${levelUpCharacterSheet.maxPp}",
+ tooltip = TooltipUio(
+ title = getString(Res.string.character_sheet__sub_characteristics__power_point),
+ description = getString(Res.string.tooltip__sub_characteristics__power_point)
+ )
+ ),
+ bonus = LevelUpSubCharacteristicUio(
+ icon = Res.drawable.ic_swords_24dp,
+ base = alteredCharacterSheet.damageBonus,
+ levelUp = levelUpCharacterSheet.damageBonus,
+ tooltip = TooltipUio(
+ title = getString(Res.string.character_sheet__sub_characteristics__damage_bonus),
+ description = getString(Res.string.tooltip__sub_characteristics__bonus_damage)
+ ),
+ ),
+ grow = LevelUpSubCharacteristicUio(
+ icon = Res.drawable.ic_heart_plus_24dp,
+ base = "${alteredCharacterSheet.hpGrow}",
+ levelUp = "${levelUpCharacterSheet.hpGrow}",
+ tooltip = TooltipUio(
+ title = getString(Res.string.character_sheet__sub_characteristics__hp_grow),
+ description = getString(Res.string.tooltip__sub_characteristics__hp_grow)
+ ),
+ ),
+ learn = LevelUpSubCharacteristicUio(
+ icon = Res.drawable.ic_cognition_24dp,
+ base = "${alteredCharacterSheet.learning}",
+ levelUp = "${levelUpCharacterSheet.learning}",
+ tooltip = TooltipUio(
+ title = getString(Res.string.character_sheet__sub_characteristics__learning),
+ description = getString(Res.string.tooltip__sub_characteristics__learning)
+ ),
+ )
)
}
@@ -204,6 +311,12 @@ class LevelUpFactory(
level = skill.level,
levelUp = results.isSkillLeveledUp(skillId = skill.id),
occupation = skill.occupation,
+ tooltips = skill.description?.let {
+ TooltipUio(
+ title = skill.label,
+ description = it,
+ )
+ },
roll = when (results[skill.id]) {
null -> RollActionUio(
characterInstanceId = characterInstanceId,
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelUpViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelUpViewModel.kt
index 1677700..16ca8ab 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelUpViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/levelup/LevelUpViewModel.kt
@@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.error__default__action
@@ -35,14 +34,15 @@ class LevelUpViewModel(
private val results = MutableStateFlow