diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_cognition_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_cognition_24dp.xml new file mode 100644 index 0000000..c42772c --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_cognition_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_heart_plus_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_heart_plus_24dp.xml new file mode 100644 index 0000000..9e66c66 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_heart_plus_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_shield_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_shield_24dp.xml new file mode 100644 index 0000000..41794c9 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_shield_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_swords_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_swords_24dp.xml new file mode 100644 index 0000000..a5cf03c --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_swords_24dp.xml @@ -0,0 +1,9 @@ + + + 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 fae4d5f..147317c 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt @@ -15,6 +15,7 @@ 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.screen.campaign.CampaignViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailFactory import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel @@ -106,6 +107,7 @@ val factoryDependencies val viewModelDependencies get() = module { + viewModelOf(::CampaignViewModel) viewModelOf(::MainPageViewModel) viewModelOf(::CharacterSheetViewModel) viewModelOf(::CharacterSheetEditViewModel) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationRepository.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationRepository.kt index ec0ba3c..84336ed 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationRepository.kt @@ -2,7 +2,7 @@ package com.pixelized.desktop.lwa.repository.alteration 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.campaign.Campaign.CharacterInstance import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -20,7 +20,7 @@ class AlterationRepository( private val store: AlterationStore, ) { private val scope = CoroutineScope(Dispatchers.IO + Job()) - private val activeAlterationMapFlow: StateFlow>>> = + private val activeAlterationMapFlow: StateFlow>>> = combine( store.alterations, store.active, @@ -38,17 +38,36 @@ class AlterationRepository( ) fun alterationsFlow( - characterId: Campaign.CharacterInstance.Id, + characterId: CharacterInstance.Id, ): Flow>> { return activeAlterationMapFlow.map { it[characterId] ?: emptyMap() } } fun alterations( - characterInstanceId: Campaign.CharacterInstance.Id, + characterInstanceId: CharacterInstance.Id, ): Map> { return activeAlterationMapFlow.value[characterInstanceId] ?: emptyMap() } + suspend fun updateActiveAlterations( + characterInstanceId: CharacterInstance.Id, + ) { + store.updateActiveAlterations( + characterInstanceId = characterInstanceId, + ) + } + + suspend fun toggleActiveAlteration( + characterInstanceId: CharacterInstance.Id, + alterationId: String, + ) { + // alteration was active for the character toggle it off. + store.toggleActiveAlteration( + characterInstance = characterInstanceId, + alterationId = alterationId, + ) + } + private fun transformToAlterationFieldMap( alterations: Map, actives: List, @@ -71,15 +90,4 @@ class AlterationRepository( } return fieldAlterations } - - suspend fun toggleActiveAlteration( - characterInstanceId: Campaign.CharacterInstance.Id, - alterationId: String, - ) { - // alteration was active for the character toggle it off. - store.toggleActiveAlteration( - characterInstance = characterInstanceId, - alterationId = alterationId, - ) - } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationStore.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationStore.kt index c367607..dec6ca7 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationStore.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationStore.kt @@ -41,6 +41,16 @@ class AlterationStore( _alterations.value = loadAlteration() } + suspend fun updateActiveAlterations( + characterInstanceId: CharacterInstance.Id, + ) { + _active.value = _active.value.toMutableMap().also { + it[characterInstanceId] = loadActiveAlterations( + characterInstanceId = characterInstanceId, + ) + } + } + private suspend fun loadAlteration(): Map { val request = client.alterations() val data = request.map { alterationFactory.convertFromJson(json = it) } @@ -54,9 +64,6 @@ class AlterationStore( characterSheetId = characterInstanceId.characterSheetId, instanceId = characterInstanceId.instanceId, ) - _active.value = _active.value.toMutableMap().also { - it[characterInstanceId] = request - } return request } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetStore.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetStore.kt index a9c5f49..d0abd95 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetStore.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetStore.kt @@ -109,8 +109,9 @@ class CharacterSheetStore( is UpdateSkillUsageMessage -> { updateCharacterSkillChange( - characterId = payload.characterId, + characterId = payload.characterSheetId, skillId = payload.skillId, + used = payload.used, ) } @@ -121,10 +122,12 @@ class CharacterSheetStore( private suspend fun updateCharacterSkillChange( characterId: String, skillId: String, + used: Boolean, ) { val character = useCase.updateSkillUsage( character = characterDetail(characterId = characterId), skillId = skillId, + used = used, ) _detailFlow.update(character) } 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 6aa93b2..407ea16 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 @@ -11,9 +11,9 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -23,6 +23,7 @@ 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 @@ -51,6 +52,7 @@ import org.koin.compose.viewmodel.koinViewModel @Composable fun CampaignScreen( + campaignViewModel: CampaignViewModel = koinViewModel(), characterDetailViewModel: CharacterDetailViewModel = koinViewModel(), characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel = koinViewModel(), dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(), @@ -59,6 +61,7 @@ fun CampaignScreen( ) { LaunchedEffect(Unit) { networkViewModel.connect() + campaignViewModel.init() } KeyHandler { @@ -87,7 +90,7 @@ fun CampaignScreen( top = { Surface( modifier = Modifier - .height(32.dp) +// .height(32.dp) .fillMaxWidth(), elevation = 1.dp, ) { @@ -97,7 +100,7 @@ fun CampaignScreen( bottom = { Surface( modifier = Modifier - .height(48.dp) +// .height(48.dp) .fillMaxWidth(), elevation = 1.dp, ) { @@ -120,7 +123,8 @@ fun CampaignScreen( modifier = Modifier .width(width = 128.dp * 4) .fillMaxHeight() - .padding(all = 8.dp), + .padding(all = 8.dp) + .clip(shape = remember { RoundedCornerShape(16.dp) }), blurController = blurController, detailViewModel = characterDetailViewModel, rollViewModel = rollViewModel, @@ -180,9 +184,11 @@ fun CampaignScreen( dismissedViewModel.changeDiminished( dialog = diminished ) + blurController.hide() dismissedViewModel.hideDiminishedDialog() }, onDismissRequest = { + blurController.hide() dismissedViewModel.hideDiminishedDialog() }, ) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignViewModel.kt new file mode 100644 index 0000000..2cc12f6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignViewModel.kt @@ -0,0 +1,33 @@ +package com.pixelized.desktop.lwa.ui.screen.campaign + +import androidx.lifecycle.ViewModel +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.collectLatest +import kotlinx.coroutines.launch + +class CampaignViewModel( + private val characterRepository: CharacterSheetRepository, + private val alterationRepository: AlterationRepository, + private val campaignRepository: CampaignRepository, +) : ViewModel() { + + suspend fun init() { + + viewModelScope.launch { + campaignRepository.campaignFlow.collectLatest { + it.characters.keys.forEach { id -> + characterRepository.characterDetail( + characterSheetId = id.characterSheetId, + forceUpdate = true, + ) + alterationRepository.updateActiveAlterations( + characterInstanceId = id, + ) + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetail.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetail.kt index 71eb2e9..b70060f 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetail.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetail.kt @@ -8,6 +8,7 @@ import androidx.compose.animation.fadeOut import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.togetherWith +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -21,19 +22,16 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable 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.Modifier 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 -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.CharacterDetailSheetSkillUio 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 @@ -68,6 +66,7 @@ fun CharacterDetailPanel( }, onDiminished = { scope.launch { + blurController.show() characterDiminishedViewModel.showDiminishedDialog( characterInstanceId = it ) @@ -92,14 +91,22 @@ fun CharacterDetailPanel( } }, onCharacteristic = { - rollViewModel.prepareRoll( - characterSheetId = detail.value.characterInstanceId?.characterSheetId!!, - label = it.label, - rollAction = "1d100", - rollSuccessValue = (it.value.toIntOrNull() ?: 0) * 5, - ) - blurController.show() + rollViewModel.prepareRoll(roll = it.roll) rollViewModel.showOverlay() + blurController.show() + }, + onSkill = { + rollViewModel.prepareRoll(roll = it.roll) + rollViewModel.showOverlay() + blurController.show() + }, + onUseSkill = { + scope.launch { + detailViewModel.onSkillUse( + skillId = it.skillId, + used = it.used, + ) + } }, ) } @@ -113,6 +120,8 @@ fun CharacterDetailAnimatedPanel( onHp: (id: Campaign.CharacterInstance.Id) -> Unit, onPp: (id: Campaign.CharacterInstance.Id) -> Unit, onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit, + onSkill: (CharacterDetailSheetSkillUio) -> Unit, + onUseSkill: (CharacterDetailSheetSkillUio) -> Unit, ) { Box( modifier = modifier, @@ -148,6 +157,8 @@ fun CharacterDetailAnimatedPanel( onHp = { onHp(it.characterInstanceId) }, onPp = { onPp(it.characterInstanceId) }, onCharacteristic = onCharacteristic, + onSkill = onSkill, + onUseSkill = onUseSkill, ) } } @@ -166,14 +177,19 @@ fun CharacterDetailContent( onHp: () -> Unit, onPp: () -> Unit, onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit, + onSkill: (CharacterDetailSheetSkillUio) -> Unit, + onUseSkill: (CharacterDetailSheetSkillUio) -> Unit, ) { Surface( modifier = modifier.fillMaxSize(), - color = MaterialTheme.lwa.colorScheme.elevatedSurface, + color = MaterialTheme.lwa.colorScheme.elevated.base1dp, ) { Column { CharacterDetailHeader( - modifier = Modifier.padding(start = 16.dp).fillMaxWidth(), + modifier = Modifier + .background(color = MaterialTheme.lwa.colorScheme.elevated.base2dp) + .padding(bottom = 8.dp) + .fillMaxWidth(), header = header, onDismissRequest = onDismissRequest, onDiminished = onDiminished, @@ -184,9 +200,12 @@ fun CharacterDetailContent( modifier = Modifier .weight(1f) .verticalScroll(state = rememberScrollState()) - .padding(all = 16.dp), + .padding(horizontal = 16.dp) + .padding(top = 8.dp, bottom = 16.dp), sheet = sheet, onCharacteristic = onCharacteristic, + onSkill = onSkill, + onUseSkill = onUseSkill, ) } } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetailFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetailFactory.kt index 809bb89..a6be22f 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetailFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetailFactory.kt @@ -3,13 +3,16 @@ 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.CharacterDetailSheetSkillUio import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetUio +import com.pixelized.desktop.lwa.ui.screen.roll.RollActionUio 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.damage import com.pixelized.shared.lwa.model.campaign.power import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet +import com.pixelized.shared.lwa.usecase.ExpressionUseCase import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__cha import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__con @@ -26,9 +29,11 @@ import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__power import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__strength import org.jetbrains.compose.resources.getString +import java.text.Collator class CharacterDetailFactory( private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory, + private val expressionUseCase: ExpressionUseCase, ) { fun convertToCharacterDetailHeaderUio( characterInstanceId: Campaign.CharacterInstance.Id, @@ -54,14 +59,17 @@ class CharacterDetailFactory( maxHp = "$maxHp", pp = "${maxPp - characterInstance.power}", maxPp = "$maxPp", - mov = "${alteredCharacterSheet.movement}" + mov = "${alteredCharacterSheet.movement}", + armor = "${alteredCharacterSheet.armor}", + bonus = alteredCharacterSheet.damageBonus, + grow = "${alteredCharacterSheet.hpGrow}", + learn = "${alteredCharacterSheet.learning}", ) } suspend fun convertToCharacterDetailSheetUio( characterInstanceId: Campaign.CharacterInstance.Id, characterSheet: CharacterSheet?, - characterInstance: Campaign.CharacterInstance, alterations: Map>, ): CharacterDetailSheetUio? { if (characterSheet == null) return null @@ -81,6 +89,12 @@ class CharacterDetailFactory( title = getString(Res.string.character_sheet__characteristics__str), description = getString(Res.string.tooltip__characteristics__strength), ), + roll = RollActionUio( + characterSheetId = characterSheet.id, + label = getString(Res.string.character_sheet__characteristics__str), + rollAction = "1d100", + rollSuccessValue = alteredCharacterSheet.strength * 5, + ), ), CharacterDetailSheetCharacteristicUio( label = getString(Res.string.character_sheet__characteristics__dex), @@ -89,6 +103,12 @@ class CharacterDetailFactory( title = getString(Res.string.character_sheet__characteristics__dex), description = getString(Res.string.tooltip__characteristics__dexterity), ), + roll = RollActionUio( + characterSheetId = characterSheet.id, + label = getString(Res.string.character_sheet__characteristics__dex), + rollAction = "1d100", + rollSuccessValue = alteredCharacterSheet.dexterity * 5, + ), ), CharacterDetailSheetCharacteristicUio( label = getString(Res.string.character_sheet__characteristics__con), @@ -97,6 +117,12 @@ class CharacterDetailFactory( title = getString(Res.string.character_sheet__characteristics__con), description = getString(Res.string.tooltip__characteristics__constitution), ), + roll = RollActionUio( + characterSheetId = characterSheet.id, + label = getString(Res.string.character_sheet__characteristics__con), + rollAction = "1d100", + rollSuccessValue = alteredCharacterSheet.constitution * 5, + ), ), CharacterDetailSheetCharacteristicUio( label = getString(Res.string.character_sheet__characteristics__hei), @@ -105,6 +131,12 @@ class CharacterDetailFactory( title = getString(Res.string.character_sheet__characteristics__hei), description = getString(Res.string.tooltip__characteristics__height), ), + roll = RollActionUio( + characterSheetId = characterSheet.id, + label = getString(Res.string.character_sheet__characteristics__hei), + rollAction = "1d100", + rollSuccessValue = alteredCharacterSheet.height * 5, + ), ), CharacterDetailSheetCharacteristicUio( label = getString(Res.string.character_sheet__characteristics__int), @@ -113,6 +145,12 @@ class CharacterDetailFactory( title = getString(Res.string.character_sheet__characteristics__int), description = getString(Res.string.tooltip__characteristics__intelligence), ), + roll = RollActionUio( + characterSheetId = characterSheet.id, + label = getString(Res.string.character_sheet__characteristics__int), + rollAction = "1d100", + rollSuccessValue = alteredCharacterSheet.intelligence * 5, + ), ), CharacterDetailSheetCharacteristicUio( label = getString(Res.string.character_sheet__characteristics__pow), @@ -121,6 +159,12 @@ class CharacterDetailFactory( title = getString(Res.string.character_sheet__characteristics__pow), description = getString(Res.string.tooltip__characteristics__power), ), + roll = RollActionUio( + characterSheetId = characterSheet.id, + label = getString(Res.string.character_sheet__characteristics__pow), + rollAction = "1d100", + rollSuccessValue = alteredCharacterSheet.power * 5, + ), ), CharacterDetailSheetCharacteristicUio( label = getString(Res.string.character_sheet__characteristics__cha), @@ -129,8 +173,89 @@ class CharacterDetailFactory( title = getString(Res.string.character_sheet__characteristics__cha), description = getString(Res.string.tooltip__characteristics__charisma), ), + roll = RollActionUio( + characterSheetId = characterSheet.id, + label = getString(Res.string.character_sheet__characteristics__cha), + rollAction = "1d100", + rollSuccessValue = alteredCharacterSheet.charisma * 5, + ), ), - ) + ), + commonSkills = characterSheet.commonSkills.map { skill -> + val value = expressionUseCase.computeSkillValue( + sheet = characterSheet, + skill = skill, + alterations = alterations, + ) + CharacterDetailSheetSkillUio( + skillId = skill.id, + label = skill.label, + value = "$value", + used = skill.used, + tooltips = skill.description?.let { + TooltipUio( + title = skill.label, + description = it, + ) + }, + roll = RollActionUio( + characterSheetId = characterInstanceId.characterSheetId, + label = skill.label, + rollAction = "1d100", + rollSuccessValue = value, + ), + ) + }.sortedWith(compareBy(Collator.getInstance()) { it.label }), + characterSheet.specialSkills.map { skill -> + val value = expressionUseCase.computeSkillValue( + sheet = characterSheet, + skill = skill, + alterations = alterations, + ) + CharacterDetailSheetSkillUio( + skillId = skill.id, + label = skill.label, + value = "$value", + used = skill.used, + tooltips = skill.description?.let { + TooltipUio( + title = skill.label, + description = it, + ) + }, + roll = RollActionUio( + characterSheetId = characterInstanceId.characterSheetId, + label = skill.label, + rollAction = "1d100", + rollSuccessValue = value, + ), + ) + }.sortedWith(compareBy(Collator.getInstance()) { it.label }), + magicSkill = characterSheet.magicSkills.map { skill -> + val value = expressionUseCase.computeSkillValue( + sheet = characterSheet, + skill = skill, + alterations = alterations, + ) + CharacterDetailSheetSkillUio( + skillId = skill.id, + label = skill.label, + value = "$value", + used = skill.used, + tooltips = skill.description?.let { + TooltipUio( + title = skill.label, + description = it, + ) + }, + roll = RollActionUio( + characterSheetId = characterInstanceId.characterSheetId, + label = skill.label, + rollAction = "1d100", + rollSuccessValue = value, + ), + ) + }.sortedWith(compareBy(Collator.getInstance()) { it.label }), ) } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetailViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetailViewModel.kt index fb7fa88..42e647a 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetailViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/CharacterDetailViewModel.kt @@ -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 com.pixelized.desktop.lwa.repository.network.NetworkRepository import com.pixelized.shared.lwa.model.campaign.Campaign +import com.pixelized.shared.lwa.protocol.websocket.payload.UpdateSkillUsageMessage import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -18,6 +20,7 @@ class CharacterDetailViewModel( private val campaignRepository: CampaignRepository, private val alterationRepository: AlterationRepository, private val characterDetailFactory: CharacterDetailFactory, + private val network: NetworkRepository, ) : ViewModel() { private val displayedCharacterId = MutableStateFlow(null) @@ -25,7 +28,6 @@ class CharacterDetailViewModel( val detail: StateFlow = displayedCharacterId .map { characterInstanceId -> if (characterInstanceId == null) return@map empty() - CharacterDetailPanelUio( characterInstanceId = characterInstanceId, header = combine( @@ -45,14 +47,12 @@ class CharacterDetailViewModel( initialValue = null, ), sheet = combine( - campaignRepository.characterInstanceFlow(id = characterInstanceId), characterSheetRepository.characterDetailFlow(characterId = characterInstanceId.characterSheetId), alterationRepository.alterationsFlow(characterId = characterInstanceId), - ) { characterInstance, characterSheet, alterations -> + ) { characterSheet, alterations -> characterDetailFactory.convertToCharacterDetailSheetUio( characterInstanceId = characterInstanceId, characterSheet = characterSheet, - characterInstance = characterInstance, alterations = alterations, ) }.stateIn( @@ -76,6 +76,20 @@ class CharacterDetailViewModel( displayedCharacterId.value = null } + suspend fun onSkillUse( + skillId: String, + used: Boolean, + ) { + val characterSheetId = displayedCharacterId.value?.characterSheetId ?: return + network.share( + payload = UpdateSkillUsageMessage( + characterSheetId = characterSheetId, + skillId = skillId, + used = used.not(), + ) + ) + } + private fun empty() = CharacterDetailPanelUio( characterInstanceId = null, header = MutableStateFlow(null), 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 75f8866..9d0940f 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 @@ -4,6 +4,7 @@ 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.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape @@ -19,14 +20,19 @@ 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.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_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_skull_24dp +import lwacharactersheet.composeapp.generated.resources.ic_swords_24dp import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp import org.jetbrains.compose.resources.painterResource @@ -40,11 +46,16 @@ data class CharacterDetailHeaderUio( val pp: String, val maxPp: String, val mov: String, + val armor: String, + val bonus: String, + val grow: String, + val learn: String, ) @Composable fun CharacterDetailHeader( modifier: Modifier = Modifier, + iconSize: Dp = 14.dp, header: State, onDismissRequest: () -> Unit, onDiminished: () -> Unit, @@ -54,7 +65,7 @@ fun CharacterDetailHeader( Column( modifier = modifier, ) { - Row { + Row(modifier = Modifier.padding(start = 16.dp)) { Text( modifier = Modifier.weight(1f) .align(alignment = Alignment.CenterVertically), @@ -84,14 +95,17 @@ fun CharacterDetailHeader( } } Row( - horizontalArrangement = Arrangement.spacedBy(space = 12.dp), + modifier = Modifier.padding(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(space = 16.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), + modifier = Modifier + .padding(bottom = 4.dp, end = 2.dp) + .size(size = iconSize), painter = painterResource(Res.drawable.ic_heart_24dp), contentDescription = null ) @@ -114,7 +128,9 @@ fun CharacterDetailHeader( verticalAlignment = Alignment.Bottom, ) { Icon( - modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp), + modifier = Modifier + .padding(bottom = 4.dp, end = 2.dp) + .size(size = iconSize), painter = painterResource(Res.drawable.ic_water_drop_24dp), contentDescription = null ) @@ -132,11 +148,16 @@ fun CharacterDetailHeader( text = "/${header.value?.maxPp ?: ""}", ) } + + Spacer(modifier = Modifier.weight(1f)) + Row( verticalAlignment = Alignment.Bottom, ) { Icon( - modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp), + modifier = Modifier + .padding(bottom = 4.dp, end = 2.dp) + .size(size = iconSize), painter = painterResource(Res.drawable.ic_near_me), contentDescription = null, ) @@ -151,6 +172,70 @@ fun CharacterDetailHeader( text = "m", ) } + Row( + verticalAlignment = Alignment.Bottom, + ) { + Icon( + modifier = Modifier + .padding(bottom = 4.dp, end = 2.dp) + .size(size = iconSize), + painter = painterResource(Res.drawable.ic_shield_24dp), + contentDescription = null, + ) + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.typography.h6, + text = header.value?.armor ?: "", + ) + } + Row( + verticalAlignment = Alignment.Bottom, + ) { + Icon( + modifier = Modifier + .padding(bottom = 4.dp, end = 2.dp) + .size(size = iconSize), + painter = painterResource(Res.drawable.ic_swords_24dp), + contentDescription = null, + ) + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.typography.h6, + text = header.value?.bonus ?: "", + ) + } + Row( + verticalAlignment = Alignment.Bottom, + ) { + Icon( + modifier = Modifier + .padding(bottom = 4.dp, end = 2.dp) + .size(size = iconSize), + painter = painterResource(Res.drawable.ic_heart_plus_24dp), + contentDescription = null, + ) + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.typography.h6, + text = header.value?.grow ?: "", + ) + } + Row( + verticalAlignment = Alignment.Bottom, + ) { + Icon( + modifier = Modifier + .padding(bottom = 4.dp, end = 2.dp) + .size(size = iconSize), + painter = painterResource(Res.drawable.ic_cognition_24dp), + contentDescription = null, + ) + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.typography.h6, + text = header.value?.learn ?: "", + ) + } } } } \ No newline at end of file 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 new file mode 100644 index 0000000..62f932a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheet.kt @@ -0,0 +1,97 @@ +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.fillMaxWidth +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.desktop.lwa.ui.composable.decoratedBox.DecoratedBox +import com.pixelized.shared.lwa.model.campaign.Campaign + +@Stable +data class CharacterDetailSheetUio( + val characterInstanceId: Campaign.CharacterInstance.Id, + val characteristics: List, + val commonSkills: List, + val specialSkill: List, + val magicSkill: List, +) + +@Composable +fun CharacterDetailSheet( + modifier: Modifier = Modifier, + sheet: State, + onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit, + onSkill: (CharacterDetailSheetSkillUio) -> Unit, + onUseSkill: (CharacterDetailSheetSkillUio) -> Unit, +) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(space = 8.dp), + ) { + 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( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(space = 8.dp) + ) { + DecoratedBox( + modifier = Modifier.fillMaxWidth(), + ) { + Column { + sheet.value?.commonSkills?.forEach { skill -> + CharacterDetailSheetSkill( + modifier = Modifier.fillMaxWidth(), + skill = skill, + onSkill = onSkill, + onUse = onUseSkill, + ) + } + } + } + DecoratedBox( + modifier = Modifier.fillMaxWidth(), + ) { + Column { + sheet.value?.specialSkill?.forEach { skill -> + CharacterDetailSheetSkill( + modifier = Modifier.fillMaxWidth(), + skill = skill, + onSkill = onSkill, + onUse = onUseSkill, + ) + } + } + } + DecoratedBox( + modifier = Modifier.fillMaxWidth(), + ) { + Column { + sheet.value?.magicSkill?.forEach { skill -> + CharacterDetailSheetSkill( + modifier = Modifier.fillMaxWidth(), + skill = skill, + onSkill = onSkill, + onUse = onUseSkill, + ) + } + } + } + } + } +} + diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheetCharacteristic.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheetCharacteristic.kt index 6cfaa8a..c07de89 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheetCharacteristic.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheetCharacteristic.kt @@ -15,12 +15,14 @@ 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 +import com.pixelized.desktop.lwa.ui.screen.roll.RollActionUio @Stable data class CharacterDetailSheetCharacteristicUio( val value: String, val label: String, - val tooltips: TooltipUio?, + val tooltips: TooltipUio, + val roll: RollActionUio, ) @OptIn(ExperimentalFoundationApi::class) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheetSkill.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheetSkill.kt new file mode 100644 index 0000000..4da5484 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheetSkill.kt @@ -0,0 +1,79 @@ +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.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.Checkbox +import androidx.compose.material.CheckboxDefaults +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.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout +import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio +import com.pixelized.desktop.lwa.ui.screen.roll.RollActionUio + +@Stable +data class CharacterDetailSheetSkillUio( + val skillId: String, + val label: String, + val value: String, + val used: Boolean, + val tooltips: TooltipUio?, + val roll: RollActionUio, +) + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun CharacterDetailSheetSkill( + modifier: Modifier = Modifier, + paddingValues: PaddingValues = PaddingValues(start = 8.dp), + skill: CharacterDetailSheetSkillUio, + onSkill: (CharacterDetailSheetSkillUio) -> Unit, + onUse: (CharacterDetailSheetSkillUio) -> Unit, +) { + TooltipLayout( + tooltip = skill.tooltips, + content = { + Row( + modifier = Modifier + .clickable(onClick = { onSkill(skill) }) + .padding(paddingValues = paddingValues) + .then(other = modifier), + horizontalArrangement = Arrangement.spacedBy(space = 4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + modifier = Modifier.weight(1f), + style = MaterialTheme.typography.body1, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = skill.label + ) + Text( + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colors.primary, + text = skill.value, + ) + Checkbox( + modifier = Modifier.size(size = 32.dp), + checked = skill.used, + colors = CheckboxDefaults.colors( + checkedColor = MaterialTheme.colors.primary, + ), + onCheckedChange = { onUse(skill) }, + ) + } + }, + ) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheetUio.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheetUio.kt deleted file mode 100644 index 23696d1..0000000 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/sheet/CharacterDetailSheetUio.kt +++ /dev/null @@ -1,47 +0,0 @@ -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, -) - -@Composable -fun CharacterDetailSheet( - modifier: Modifier = Modifier, - sheet: State, - 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) - ) { - - } - } -} - diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/PlayerPortrait.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/PlayerPortrait.kt index fd5cead..c1be104 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/PlayerPortrait.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/PlayerPortrait.kt @@ -1,7 +1,9 @@ package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon +import androidx.compose.foundation.background import androidx.compose.foundation.clickable 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.fillMaxSize @@ -10,6 +12,7 @@ import androidx.compose.foundation.layout.size 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 @@ -52,10 +55,11 @@ fun PlayerPortrait( ) { val colorScheme = MaterialTheme.lwa.colorScheme - DecoratedBox( + Box ( modifier = modifier .size(size = size) .clip(shape = remember { RoundedCornerShape(8.dp) }) + .background(color = MaterialTheme.lwa.colorScheme.elevated.base1dp) .clickable { onCharacter(character.id) }, ) { AsyncImage( @@ -73,17 +77,17 @@ fun PlayerPortrait( drawRect( brush = Brush.verticalGradient( listOf( - colorScheme.elevatedSurface.copy(alpha = 0.0f), - colorScheme.elevatedSurface.copy(alpha = 0.0f), - colorScheme.elevatedSurface.copy(alpha = 0.0f), - colorScheme.elevatedSurface.copy(alpha = 0.5f), - colorScheme.elevatedSurface.copy(alpha = 0.8f), + 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), ) ) ) drawContent() } - .padding(all = 2.dp), + .padding(vertical = 2.dp, horizontal = 4.dp), verticalArrangement = Arrangement.aligned(alignment = Alignment.Bottom), ) { Row( diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/PlayerRibbonViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/PlayerRibbonViewModel.kt index b3c2a91..964c95d 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/PlayerRibbonViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/PlayerRibbonViewModel.kt @@ -62,19 +62,6 @@ class PlayerRibbonViewModel( private val rolls = hashMapOf>() - init { - viewModelScope.launch { - campaignRepository.campaignFlow.collectLatest { - it.characters.keys.forEach { id -> - characterRepository.characterDetail( - characterSheetId = id.characterSheetId, - forceUpdate = true, - ) - } - } - } - } - @Composable @Stable fun roll(characterSheetId: String): State { diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/characterSheet/detail/CharacterSheetViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/characterSheet/detail/CharacterSheetViewModel.kt index cd47b3d..2ae42b2 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/characterSheet/detail/CharacterSheetViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/characterSheet/detail/CharacterSheetViewModel.kt @@ -84,8 +84,9 @@ class CharacterSheetViewModel( viewModelScope.launch { network.share( payload = UpdateSkillUsageMessage( - characterId = argument.characterInstanceId.characterSheetId, + characterSheetId = argument.characterInstanceId.characterSheetId, skillId = skill.id, + used = skill.used.not(), ) ) } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollActionUio.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollActionUio.kt new file mode 100644 index 0000000..0ec6dce --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollActionUio.kt @@ -0,0 +1,11 @@ +package com.pixelized.desktop.lwa.ui.screen.roll + +import androidx.compose.runtime.Stable + +@Stable +data class RollActionUio( + val characterSheetId: String, + val label: String, + val rollAction: String, + val rollSuccessValue: Int?, +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollViewModel.kt index a314dc9..d6557da 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollViewModel.kt @@ -97,6 +97,15 @@ class RollViewModel( } fun prepareRoll( + roll: RollActionUio, + ) = prepareRoll( + characterSheetId = roll.characterSheetId, + label = roll.label, + rollAction = roll.rollAction, + rollSuccessValue = roll.rollSuccessValue, + ) + + private fun prepareRoll( characterSheetId: String, label: String, rollAction: String, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/LwaColorTheme.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/LwaColorTheme.kt index a24332a..ecca34a 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/LwaColorTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/LwaColorTheme.kt @@ -14,21 +14,34 @@ import kotlin.math.ln @Stable data class LwaColorTheme( val base: Colors, - val elevatedSurface: Color, -) + val elevated: Elevated, +) { + @Stable + data class Elevated( + val base1dp: Color, + val base2dp: Color, + ) +} @Composable @Stable fun darkLwaColorTheme( base: Colors = darkColors(), - elevatedSurface: Color = base.calculateElevatedColor( - color = base.surface, - onColor = base.onSurface, - elevation = 1.dp, + elevated: LwaColorTheme.Elevated = LwaColorTheme.Elevated( + base1dp = base.calculateElevatedColor( + color = base.surface, + onColor = base.onSurface, + elevation = 1.dp, + ), + base2dp = base.calculateElevatedColor( + color = base.surface, + onColor = base.onSurface, + elevation = 2.dp, + ), ), ): LwaColorTheme = LwaColorTheme( base = base, - elevatedSurface = elevatedSurface, + elevated = elevated, ) @ReadOnlyComposable diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetService.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetService.kt index 4175faf..5e1ae08 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetService.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetService.kt @@ -1,6 +1,5 @@ package com.pixelized.server.lwa.model.character -import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJson import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJsonFactory import com.pixelized.shared.lwa.protocol.rest.CharacterPreviewJson @@ -39,7 +38,7 @@ class CharacterSheetService( return store.save(sheet = factory.convertFromJson(character)) } - fun deleteCharacter(characterId: String) : Boolean { + fun deleteCharacter(characterId: String): Boolean { return store.delete(id = characterId) } @@ -74,13 +73,15 @@ class CharacterSheetService( } fun updateCharacterSkillUsage( - characterId: String, + characterSheetId: String, skillId: String, + used: Boolean, ) { - sheets[characterId]?.let { character -> + sheets[characterSheetId]?.let { character -> val update = useCase.updateSkillUsage( character = character, skillId = skillId, + used = used, ) store.save(sheet = update) } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/Engine.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/Engine.kt index f3accff..1caeb13 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/Engine.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/Engine.kt @@ -33,8 +33,9 @@ class Engine( } is UpdateSkillUsageMessage -> characterService.updateCharacterSkillUsage( - characterId = data.characterId, - skillId = data.skillId + characterSheetId = data.characterSheetId, + skillId = data.skillId, + used = data.used, ) RestSynchronisation.Campaign -> Unit // Handle in the Rest diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/payload/UpdateSkillUsageMessage.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/payload/UpdateSkillUsageMessage.kt index a3a0e17..a11c140 100644 --- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/payload/UpdateSkillUsageMessage.kt +++ b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/payload/UpdateSkillUsageMessage.kt @@ -4,6 +4,7 @@ import kotlinx.serialization.Serializable @Serializable data class UpdateSkillUsageMessage( - val characterId: String, + val characterSheetId: String, val skillId: String, + val used: Boolean, ) : MessagePayload \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/usecase/CharacterSheetUseCase.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/usecase/CharacterSheetUseCase.kt index aaa09b2..567384b 100644 --- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/usecase/CharacterSheetUseCase.kt +++ b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/usecase/CharacterSheetUseCase.kt @@ -89,16 +89,17 @@ class CharacterSheetUseCase { fun updateSkillUsage( character: CharacterSheet, skillId: String, + used: Boolean, ): CharacterSheet { return character.copy( commonSkills = character.commonSkills.map { skill -> - skill.takeIf { skill.id == skillId }?.copy(used = skill.used.not()) ?: skill + skill.takeIf { skill.id == skillId }?.copy(used = used) ?: skill }, specialSkills = character.specialSkills.map { skill -> - skill.takeIf { skill.id == skillId }?.copy(used = skill.used.not()) ?: skill + skill.takeIf { skill.id == skillId }?.copy(used = used) ?: skill }, magicSkills = character.magicSkills.map { skill -> - skill.takeIf { skill.id == skillId }?.copy(used = skill.used.not()) ?: skill + skill.takeIf { skill.id == skillId }?.copy(used = used) ?: skill }, ) }