Add characteristic + common / special / magic skill to detail panel.
This commit is contained in:
		
							parent
							
								
									b6b135cd40
								
							
						
					
					
						commit
						b6d02c21be
					
				
					 28 changed files with 633 additions and 135 deletions
				
			
		| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<Map<Campaign.CharacterInstance.Id, Map<String, List<FieldAlteration>>>> =
 | 
			
		||||
    private val activeAlterationMapFlow: StateFlow<Map<CharacterInstance.Id, Map<String, List<FieldAlteration>>>> =
 | 
			
		||||
        combine(
 | 
			
		||||
            store.alterations,
 | 
			
		||||
            store.active,
 | 
			
		||||
| 
						 | 
				
			
			@ -38,17 +38,36 @@ class AlterationRepository(
 | 
			
		|||
        )
 | 
			
		||||
 | 
			
		||||
    fun alterationsFlow(
 | 
			
		||||
        characterId: Campaign.CharacterInstance.Id,
 | 
			
		||||
        characterId: CharacterInstance.Id,
 | 
			
		||||
    ): Flow<Map<String, List<FieldAlteration>>> {
 | 
			
		||||
        return activeAlterationMapFlow.map { it[characterId] ?: emptyMap() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun alterations(
 | 
			
		||||
        characterInstanceId: Campaign.CharacterInstance.Id,
 | 
			
		||||
        characterInstanceId: CharacterInstance.Id,
 | 
			
		||||
    ): Map<String, List<FieldAlteration>> {
 | 
			
		||||
        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<String, Alteration>,
 | 
			
		||||
        actives: List<String>,
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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<String, Alteration> {
 | 
			
		||||
        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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<String, List<FieldAlteration>>,
 | 
			
		||||
    ): 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 }),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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<Campaign.CharacterInstance.Id?>(null)
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +28,6 @@ class CharacterDetailViewModel(
 | 
			
		|||
    val detail: StateFlow<CharacterDetailPanelUio> = 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),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<CharacterDetailHeaderUio?>,
 | 
			
		||||
    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 ?: "",
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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<CharacterDetailSheetCharacteristicUio>,
 | 
			
		||||
    val commonSkills: List<CharacterDetailSheetSkillUio>,
 | 
			
		||||
    val specialSkill: List<CharacterDetailSheetSkillUio>,
 | 
			
		||||
    val magicSkill: List<CharacterDetailSheetSkillUio>,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun CharacterDetailSheet(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    sheet: State<CharacterDetailSheetUio?>,
 | 
			
		||||
    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,
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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<CharacterDetailSheetCharacteristicUio>,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun CharacterDetailSheet(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    sheet: State<CharacterDetailSheetUio?>,
 | 
			
		||||
    onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    Row(
 | 
			
		||||
        modifier = modifier
 | 
			
		||||
    ) {
 | 
			
		||||
        Column(
 | 
			
		||||
            verticalArrangement = Arrangement.spacedBy(space = 8.dp)
 | 
			
		||||
        ) {
 | 
			
		||||
            sheet.value?.characteristics?.forEach {
 | 
			
		||||
                CharacterDetailSheetCharacteristic(
 | 
			
		||||
                    modifier = Modifier.size(width = 80.dp, height = 120.dp),
 | 
			
		||||
                    characteristic = it,
 | 
			
		||||
                    onClick = { onCharacteristic(it) },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Column(
 | 
			
		||||
            verticalArrangement = Arrangement.spacedBy(space = 8.dp)
 | 
			
		||||
        ) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,19 +62,6 @@ class PlayerRibbonViewModel(
 | 
			
		|||
 | 
			
		||||
    private val rolls = hashMapOf<String, MutableState<PlayerPortraitRollUio?>>()
 | 
			
		||||
 | 
			
		||||
    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<PlayerPortraitRollUio?> {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(),
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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?,
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue