diff --git a/app/src/main/java/com/pixelized/rplexicon/data/model/Skill.kt b/app/src/main/java/com/pixelized/rplexicon/data/model/Skill.kt index 724c99d..4f9047f 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/model/Skill.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/model/Skill.kt @@ -6,4 +6,5 @@ data class Skill( val rest: String?, val cost: String?, val effect: Throw?, + val passive: Boolean, ) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/parser/SkillParser.kt b/app/src/main/java/com/pixelized/rplexicon/data/parser/SkillParser.kt index beef8be..d14b16b 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/parser/SkillParser.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/parser/SkillParser.kt @@ -31,6 +31,7 @@ class SkillParser @Inject constructor( rest = row.parse(column = REST), cost = row.parse(column = COST), effect = throwParser.parse(row.parse(column = EFFECT)), + passive = row.parseBool(column = PASSIVE) ?: false ) skills.getOrPut(characterSheet.name) { mutableListOf() }.add(skill) } @@ -48,7 +49,8 @@ class SkillParser @Inject constructor( private val REST = column("Récupération") private val COST = column("Coût") private val EFFECT = column("Effect") + private val PASSIVE = column("Passif") - private val COLUMNS get() = listOf(CHARACTER, NAME, AMOUNT, REST, COST, EFFECT) + private val COLUMNS get() = listOf(CHARACTER, NAME, AMOUNT, REST, COST, EFFECT, PASSIVE) } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/SkillItem.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/SkillItem.kt index 8ce4f65..d414275 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/SkillItem.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/composable/actions/SkillItem.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.material.minimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme @@ -64,7 +63,6 @@ fun SkillItem( Box( modifier = Modifier .clickable { onInfo(skill) } - .minimumInteractiveComponentSize() .padding(paddingValues = padding) .then(other = modifier), contentAlignment = Alignment.Center, @@ -160,7 +158,7 @@ private fun CounterItemPreview( @Composable @Stable -fun rememberTokenListStatePreview(): State> = remember { +fun rememberSkillsListStatePreview(): State> = remember { val provider = CounterItemPreviewProvider() mutableStateOf(provider.values.toList()) } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SkillFactoryUioFactory.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SkillFactoryUioFactory.kt new file mode 100644 index 0000000..b155938 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/factory/SkillFactoryUioFactory.kt @@ -0,0 +1,54 @@ +package com.pixelized.rplexicon.ui.screens.character.factory + +import com.pixelized.rplexicon.data.model.CharacterSheet +import com.pixelized.rplexicon.data.model.Property +import com.pixelized.rplexicon.data.model.Skill +import com.pixelized.rplexicon.data.network.CharacterSheetFire +import com.pixelized.rplexicon.data.repository.character.DescriptionRepository +import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillItemUio +import com.pixelized.rplexicon.utilitary.extentions.icon +import com.pixelized.rplexicon.utilitary.extentions.modifier +import javax.inject.Inject + +class SkillFactoryUioFactory @Inject constructor( + private val descriptionRepository: DescriptionRepository, +) { + fun convert( + character: CharacterSheet?, + fire: CharacterSheetFire?, + skills: List, + ): List { + // Filter out passive skills, theses are display in the Proficiency Page. + return skills.map { skill -> + val description = descriptionRepository.find(name = skill.name) + val modifier = skill.effect?.modifier?.sumOf { + when (it) { + Property.LEVEL -> character?.level ?: 0 + Property.PROFICIENCY -> character?.proficiency ?: 0 + Property.STRENGTH -> character?.strength?.modifier ?: 0 + Property.DEXTERITY -> character?.dexterity?.modifier ?: 0 + Property.CONSTITUTION -> character?.constitution?.modifier ?: 0 + Property.INTELLIGENCE -> character?.intelligence?.modifier ?: 0 + Property.WISDOM -> character?.wisdom?.modifier ?: 0 + Property.CHARISMA -> character?.charisma?.modifier ?: 0 + else -> 0 + } + } ?: 0 + SkillItemUio( + label = skill.name, + translate = description?.original, + rest = skill.rest, + cost = skill.cost, + effect = skill.effect?.let { + SkillItemUio.Dice( + icon = it.faces.icon, + label = "${skill.effect.amount}d${skill.effect.faces}${if (modifier > 0) "+$modifier" else ""}", + ) + }, + value = fire?.skills?.get(skill.name) ?: 0, + max = skill.amount, + haveDetail = description?.description != null, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/ActionsPage.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/ActionsPage.kt index f096380..6a1238b 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/ActionsPage.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/ActionsPage.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ModalBottomSheetState +import androidx.compose.material.minimumInteractiveComponentSize import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.State @@ -39,7 +40,7 @@ import com.pixelized.rplexicon.ui.screens.character.composable.actions.Spell import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellHeader import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellHeaderUio import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellUio -import com.pixelized.rplexicon.ui.screens.character.composable.actions.rememberTokenListStatePreview +import com.pixelized.rplexicon.ui.screens.character.composable.actions.rememberSkillsListStatePreview import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberAttackListStatePreview import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberObjectListStatePreview import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberSpellListStatePreview @@ -63,7 +64,7 @@ fun ActionPage( modifier = Modifier.fillMaxSize(), attacks = attacksViewModel.attacks, objects = objectsViewModel.objects, - tokens = skillViewModel.skills, + skills = skillViewModel.skills, spells = spellsViewModel.spells, onAttackHit = { id -> attacksViewModel.onHitRoll(id)?.let { @@ -146,7 +147,7 @@ fun ActionsPageContent( lazyListState: LazyListState = rememberLazyListState(), attacks: State>, objects: State>, - tokens: State>, + skills: State>, spells: State>>>, onAttackHit: (id: String) -> Unit, onAttackDamage: (id: String) -> Unit, @@ -204,14 +205,15 @@ fun ActionsPageContent( } } - if (tokens.value.isNotEmpty()) { + if (skills.value.isNotEmpty()) { stickyHeader { GenericHeader( label = R.string.character_sheet_title_skills, ) } - items(items = tokens.value) { + items(items = skills.value) { SkillItem( + modifier = Modifier.minimumInteractiveComponentSize(), skill = it, onInfo = onSkillInfo, onThrow = onSkillThrow, @@ -259,7 +261,7 @@ fun ActionPagePreview() { modifier = Modifier.fillMaxSize(), attacks = rememberAttackListStatePreview(), objects = rememberObjectListStatePreview(), - tokens = rememberTokenListStatePreview(), + skills = rememberSkillsListStatePreview(), spells = rememberSpellListStatePreview(), onAttackHit = { }, onAttackDamage = { }, diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SkillsViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SkillsViewModel.kt index 1f1ae08..ce3b7cf 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SkillsViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SkillsViewModel.kt @@ -7,10 +7,9 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.pixelized.rplexicon.data.model.CharacterSheet -import com.pixelized.rplexicon.data.network.CharacterSheetFire import com.pixelized.rplexicon.data.model.DiceThrow -import com.pixelized.rplexicon.data.model.Property import com.pixelized.rplexicon.data.model.Skill +import com.pixelized.rplexicon.data.network.CharacterSheetFire import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository import com.pixelized.rplexicon.data.repository.character.DescriptionRepository @@ -18,8 +17,7 @@ import com.pixelized.rplexicon.data.repository.character.SkillRepository import com.pixelized.rplexicon.ui.composable.edit.SkillEditDialogUio import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillItemUio -import com.pixelized.rplexicon.utilitary.extentions.icon -import com.pixelized.rplexicon.utilitary.extentions.modifier +import com.pixelized.rplexicon.ui.screens.character.factory.SkillFactoryUioFactory import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine @@ -30,9 +28,10 @@ import javax.inject.Inject @HiltViewModel class SkillsViewModel @Inject constructor( private val sheetRepository: CharacterSheetRepository, - private val skillRepository: SkillRepository, private val firebaseRepository: FirebaseRepository, + private val skillRepository: SkillRepository, private val descriptionRepository: DescriptionRepository, + private val skillFactory: SkillFactoryUioFactory, application: Application, savedStateHandle: SavedStateHandle, ) : AndroidViewModel(application) { @@ -50,48 +49,23 @@ class SkillsViewModel @Inject constructor( init { viewModelScope.launch { launch(Dispatchers.IO) { + val data = SkillStruct() sheetRepository.data .combine(skillRepository.skills) { sheets, skills -> - Struct(sheet = sheets[character], skill = skills[character] ?: emptyList()) + data.sheet = sheets[character] + data.skill = skills[character] ?: emptyList() } - .combine(firebaseRepository.getCharacter(character = character)) { data, fire -> - data.apply { this.fire = fire } + .combine(firebaseRepository.getCharacter(character = character)) { _, fire -> + data.fire = fire } - .collect { data -> - val (character, values, fire) = data - - val skills = values.map { skill -> - val description = descriptionRepository.find(name = skill.name) - val modifier = skill.effect?.modifier?.sumOf { - when (it) { - Property.LEVEL -> character?.level ?: 0 - Property.PROFICIENCY -> character?.proficiency ?: 0 - Property.STRENGTH -> character?.strength?.modifier ?: 0 - Property.DEXTERITY -> character?.dexterity?.modifier ?: 0 - Property.CONSTITUTION -> character?.constitution?.modifier ?: 0 - Property.INTELLIGENCE -> character?.intelligence?.modifier ?: 0 - Property.WISDOM -> character?.wisdom?.modifier ?: 0 - Property.CHARISMA -> character?.charisma?.modifier ?: 0 - else -> 0 - } - } ?: 0 - SkillItemUio( - label = skill.name, - translate = description?.original, - rest = skill.rest, - cost = skill.cost, - effect = skill.effect?.let { - SkillItemUio.Dice( - icon = it.faces.icon, - label = "${skill.effect.amount}d${skill.effect.faces}${if (modifier > 0) "+$modifier" else ""}", - ) - }, - value = fire?.skills?.get(skill.name) ?: 0, - max = skill.amount, - haveDetail = description?.description != null, - ) - } - + .collect { + val (character, fire, values) = data + // Filter out passive skills, theses are display in the Proficiency Page. + val skills = skillFactory.convert( + character = character, + fire = fire, + skills = values.filter { it.passive.not() } + ) withContext(Dispatchers.Main) { _skills.value = skills } @@ -140,9 +114,9 @@ class SkillsViewModel @Inject constructor( hideSkillEditDialog() } - data class Struct( - val sheet: CharacterSheet?, - val skill: List, - var fire: CharacterSheetFire? = null + private data class SkillStruct( + var sheet: CharacterSheet? = null, + var fire: CharacterSheetFire? = null, + var skill: List = emptyList(), ) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyPage.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyPage.kt index 849df17..897974c 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyPage.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyPage.kt @@ -7,8 +7,10 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.rememberScrollState @@ -20,6 +22,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable +import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -38,6 +41,10 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.pixelized.rplexicon.LocalRollOverlay import com.pixelized.rplexicon.R +import com.pixelized.rplexicon.ui.composable.edit.HandleSkillEditDialog +import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillItem +import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillItemUio +import com.pixelized.rplexicon.ui.screens.character.composable.actions.rememberSkillsListStatePreview import com.pixelized.rplexicon.ui.screens.character.composable.character.LabelPointUio import com.pixelized.rplexicon.ui.screens.character.composable.character.Masteries import com.pixelized.rplexicon.ui.screens.character.composable.character.MasteriesUio @@ -48,6 +55,7 @@ import com.pixelized.rplexicon.ui.screens.character.composable.character.Profici import com.pixelized.rplexicon.ui.screens.character.composable.character.Stat import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberCharacterSheetStatePreview +import com.pixelized.rplexicon.ui.screens.character.pages.actions.HandleSkillDetailDialog import com.pixelized.rplexicon.ui.theme.LexiconTheme import com.pixelized.rplexicon.utilitary.extentions.ddBorder @@ -72,6 +80,7 @@ fun ProficiencyPage( ProficiencyPageContent( modifier = Modifier.fillMaxSize(), sheet = sheet, + passives = viewModel.skills, onStats = { stat -> overlay.prepareRoll(diceThrow = viewModel.statRoll(stat.id)) overlay.showOverlay() @@ -80,6 +89,27 @@ fun ProficiencyPage( overlay.prepareRoll(diceThrow = viewModel.proficiencyRoll(proficiency.id)) overlay.showOverlay() }, + onSkillCount = { + viewModel.showSkillEditDialog(item = it) + }, + onSkillThrow = { + overlay.prepareRoll(diceThrow = viewModel.onSkillRoll(it.label)) + overlay.showOverlay() + }, + onSkillInfo = { + viewModel.showSkillDetailDialog(item = it.label) + }, + ) + + HandleSkillEditDialog( + dialog = viewModel.skillEditDialog, + onDismissRequest = viewModel::hideSkillEditDialog, + onConfirm = viewModel::applySkillChange + ) + + HandleSkillDetailDialog( + dialog = viewModel.skillDetailDialog, + onDismissRequest = viewModel::hideSkillDetailDialog ) } } @@ -91,13 +121,17 @@ fun ProficiencyPageContent( inner: Shape = remember { RoundedCornerShape(size = 8.dp) }, outline: Shape = remember { CutCornerShape(size = 16.dp) }, sheet: CharacterSheetUio, + passives: State>, onStats: (StatUio) -> Unit, onProficiencies: (ProficiencyUio) -> Unit, + onSkillThrow: (SkillItemUio) -> Unit, + onSkillCount: (SkillItemUio) -> Unit, + onSkillInfo: (SkillItemUio) -> Unit, ) { Box( modifier = modifier .verticalScroll(state = state) - .padding(all = 16.dp), + .padding(vertical = 16.dp), ) { ProficiencyLayout( inner = inner, @@ -181,8 +215,22 @@ fun ProficiencyPageContent( }, masteries = { Masteries( + modifier = Modifier.padding(horizontal = 16.dp), masteries = sheet.masteries, ) + Column( + modifier = Modifier.padding(top = 8.dp), + ) { + passives.value.forEach { + SkillItem( + modifier = Modifier.padding(vertical = 4.dp), + skill = it, + onInfo = onSkillInfo, + onThrow = onSkillThrow, + onSkill = onSkillCount, + ) + } + } }, ) } @@ -247,8 +295,9 @@ private fun ProficiencyLayout( measurePolicy = { measurables, constraints -> val spacingPx = with(density) { spacing.toPx().toInt() } val statsWidth = with(density) { 100.dp.toPx().toInt() } + val horizontal = with(density) { 16.dp.toPx().toInt() } - val proficienciesWidth = constraints.maxWidth - statsWidth - spacingPx + val proficienciesWidth = constraints.maxWidth - statsWidth - spacingPx - horizontal * 2 val passivesMeasure = measurables.passives.measure( constraints = constraints.copy( minWidth = proficienciesWidth, @@ -291,23 +340,23 @@ private fun ProficiencyLayout( height = statsMeasure.height + masteriesMeasure.height + passivesMeasure.height + spacingPx * 2, ) { statsMeasure.place( - x = 0, + x = horizontal, y = 0, ) savingThrowsMeasure.place( - x = statsWidth + spacingPx, + x = horizontal + statsWidth + spacingPx, y = 0, ) proficienciesMeasure.place( - x = statsWidth + spacingPx, + x = horizontal + statsWidth + spacingPx, y = savingThrowsMeasure.height + spacingPx, ) speedMeasure.place( - x = 0, + x = horizontal, y = statsHeight + spacingPx, ) passivesMeasure.place( - x = statsWidth + spacingPx, + x = horizontal + statsWidth + spacingPx, y = savingThrowsMeasure.height + proficienciesMeasure.height + spacingPx * 2, ) masteriesMeasure.place( @@ -327,16 +376,20 @@ val List.passives get() = first { it.layoutId == "PassivesId" } val List.masteries get() = first { it.layoutId == "MasteriesId" } @Composable -@Preview(uiMode = UI_MODE_NIGHT_NO, heightDp = 1300) -@Preview(uiMode = UI_MODE_NIGHT_YES, heightDp = 1300) +@Preview(uiMode = UI_MODE_NIGHT_NO, heightDp = 1500) +@Preview(uiMode = UI_MODE_NIGHT_YES, heightDp = 1500) fun ProficiencyPreview() { LexiconTheme { Surface { val sheet by rememberCharacterSheetStatePreview() ProficiencyPageContent( sheet = sheet, + passives = rememberSkillsListStatePreview(), onStats = { }, onProficiencies = { }, + onSkillThrow = { }, + onSkillCount = { }, + onSkillInfo = { }, ) } } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyViewModel.kt index b23ffcf..9f38bb3 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/proficiency/ProficiencyViewModel.kt @@ -6,13 +6,23 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.DiceThrow +import com.pixelized.rplexicon.data.model.Skill +import com.pixelized.rplexicon.data.network.CharacterSheetFire +import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository import com.pixelized.rplexicon.data.repository.character.AlterationRepository import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository +import com.pixelized.rplexicon.data.repository.character.DescriptionRepository +import com.pixelized.rplexicon.data.repository.character.SkillRepository +import com.pixelized.rplexicon.ui.composable.edit.SkillEditDialogUio import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument +import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillItemUio import com.pixelized.rplexicon.ui.screens.character.composable.character.ProficiencyUio import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio import com.pixelized.rplexicon.ui.screens.character.factory.CharacterSheetUioFactory +import com.pixelized.rplexicon.ui.screens.character.factory.SkillFactoryUioFactory +import com.pixelized.rplexicon.ui.screens.character.pages.actions.SkillDetailUio import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine @@ -23,8 +33,12 @@ import javax.inject.Inject @HiltViewModel class ProficiencyViewModel @Inject constructor( private val characterRepository: CharacterSheetRepository, + private val firebaseRepository: FirebaseRepository, private val alterationRepository: AlterationRepository, private val characterSheetFactory: CharacterSheetUioFactory, + private val skillRepository: SkillRepository, + private val descriptionRepository: DescriptionRepository, + private val skillFactory: SkillFactoryUioFactory, savedStateHandle: SavedStateHandle, application: Application, ) : AndroidViewModel(application) { @@ -33,27 +47,63 @@ class ProficiencyViewModel @Inject constructor( private val _sheet = mutableStateOf(null) val sheet: State get() = _sheet + private val _skillEditDialog = mutableStateOf(null) + val skillEditDialog: State get() = _skillEditDialog + + private val _skillDetailDialog = mutableStateOf(null) + val skillDetailDialog: State get() = _skillDetailDialog + + private val _skills = mutableStateOf>(emptyList()) + val skills: State> get() = _skills + init { - viewModelScope.launch(Dispatchers.IO) { - characterRepository.data - .combine(alterationRepository.assignedAlterations) { sheets, _ -> sheets } - .collect { - val characterSheet = it[character] - if (characterSheet != null) { - val alterations = alterationRepository.getActiveAlterationsStatus(character) - val sheet = characterSheetFactory.convert( - sheet = characterSheet, - status = alterations, - ) - withContext(Dispatchers.Main) { - _sheet.value = sheet - } - } else { - launch { - characterRepository.fetchCharacterSheet() + viewModelScope.launch { + launch(Dispatchers.IO) { + characterRepository.data + .combine(alterationRepository.assignedAlterations) { sheets, _ -> sheets } + .collect { + val characterSheet = it[character] + if (characterSheet != null) { + val alterations = + alterationRepository.getActiveAlterationsStatus(character) + val sheet = characterSheetFactory.convert( + sheet = characterSheet, + status = alterations, + ) + withContext(Dispatchers.Main) { + _sheet.value = sheet + } + } else { + launch { + characterRepository.fetchCharacterSheet() + } } } - } + } + + launch(Dispatchers.IO) { + val data = SkillStruct() + characterRepository.data + .combine(skillRepository.skills) { sheets, skills -> + data.sheet = sheets[character] + data.skill = skills[character] ?: emptyList() + } + .combine(firebaseRepository.getCharacter(character = character)) { _, fire -> + data.fire = fire + } + .collect { + val (character, fire, values) = data + // Filter out non passive skills. + val skills = skillFactory.convert( + character = character, + fire = fire, + skills = values.filter { it.passive } + ) + withContext(Dispatchers.Main) { + _skills.value = skills + } + } + } } } @@ -92,4 +142,50 @@ class ProficiencyViewModel @Inject constructor( ProficiencyUio.ID.STEALTH -> DiceThrow.Stealth(character) ProficiencyUio.ID.SURVIVAL -> DiceThrow.Survival(character) } + + fun onSkillRoll(name: String): DiceThrow = DiceThrow.Skill( + character = character, + skill = name, + ) + + fun showSkillDetailDialog(item: String) { + _skillDetailDialog.value = descriptionRepository + .find(name = item) + ?.let { description -> + SkillDetailUio( + name = item, + original = description.original, + description = description.description + ) + } + } + + fun hideSkillDetailDialog() { + _skillDetailDialog.value = null + } + + fun showSkillEditDialog(item: SkillItemUio) { + if (item.max != null) { + _skillEditDialog.value = SkillEditDialogUio( + label = item.label, + value = item.value ?: 0, + max = item.max, + ) + } + } + + fun hideSkillEditDialog() { + _skillEditDialog.value = null + } + + fun applySkillChange(id: String, value: Int) { + firebaseRepository.setSkill(character = character, name = id, value = value) + hideSkillEditDialog() + } + + private data class SkillStruct( + var sheet: CharacterSheet? = null, + var fire: CharacterSheetFire? = null, + var skill: List = emptyList(), + ) } \ No newline at end of file