Add Skill passive in the proficiency page

This commit is contained in:
Thomas Andres Gomez 2024-01-11 07:24:08 +01:00
parent 755caf1194
commit a009e6c831
8 changed files with 265 additions and 85 deletions

View file

@ -6,4 +6,5 @@ data class Skill(
val rest: String?, val rest: String?,
val cost: String?, val cost: String?,
val effect: Throw?, val effect: Throw?,
val passive: Boolean,
) )

View file

@ -31,6 +31,7 @@ class SkillParser @Inject constructor(
rest = row.parse(column = REST), rest = row.parse(column = REST),
cost = row.parse(column = COST), cost = row.parse(column = COST),
effect = throwParser.parse(row.parse(column = EFFECT)), effect = throwParser.parse(row.parse(column = EFFECT)),
passive = row.parseBool(column = PASSIVE) ?: false
) )
skills.getOrPut(characterSheet.name) { mutableListOf() }.add(skill) skills.getOrPut(characterSheet.name) { mutableListOf() }.add(skill)
} }
@ -48,7 +49,8 @@ class SkillParser @Inject constructor(
private val REST = column("Récupération") private val REST = column("Récupération")
private val COST = column("Coût") private val COST = column("Coût")
private val EFFECT = column("Effect") 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)
} }
} }

View file

@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.minimumInteractiveComponentSize import androidx.compose.material.minimumInteractiveComponentSize
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -64,7 +63,6 @@ fun SkillItem(
Box( Box(
modifier = Modifier modifier = Modifier
.clickable { onInfo(skill) } .clickable { onInfo(skill) }
.minimumInteractiveComponentSize()
.padding(paddingValues = padding) .padding(paddingValues = padding)
.then(other = modifier), .then(other = modifier),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
@ -160,7 +158,7 @@ private fun CounterItemPreview(
@Composable @Composable
@Stable @Stable
fun rememberTokenListStatePreview(): State<List<SkillItemUio>> = remember { fun rememberSkillsListStatePreview(): State<List<SkillItemUio>> = remember {
val provider = CounterItemPreviewProvider() val provider = CounterItemPreviewProvider()
mutableStateOf(provider.values.toList()) mutableStateOf(provider.values.toList())
} }

View file

@ -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<Skill>,
): List<SkillItemUio> {
// 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,
)
}
}
}

View file

@ -14,6 +14,7 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetState import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.minimumInteractiveComponentSize
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State 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.SpellHeader
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellHeaderUio 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.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.rememberAttackListStatePreview
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberObjectListStatePreview import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberObjectListStatePreview
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberSpellListStatePreview import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberSpellListStatePreview
@ -63,7 +64,7 @@ fun ActionPage(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
attacks = attacksViewModel.attacks, attacks = attacksViewModel.attacks,
objects = objectsViewModel.objects, objects = objectsViewModel.objects,
tokens = skillViewModel.skills, skills = skillViewModel.skills,
spells = spellsViewModel.spells, spells = spellsViewModel.spells,
onAttackHit = { id -> onAttackHit = { id ->
attacksViewModel.onHitRoll(id)?.let { attacksViewModel.onHitRoll(id)?.let {
@ -146,7 +147,7 @@ fun ActionsPageContent(
lazyListState: LazyListState = rememberLazyListState(), lazyListState: LazyListState = rememberLazyListState(),
attacks: State<List<AttackUio>>, attacks: State<List<AttackUio>>,
objects: State<List<ObjectItemUio>>, objects: State<List<ObjectItemUio>>,
tokens: State<List<SkillItemUio>>, skills: State<List<SkillItemUio>>,
spells: State<List<Pair<SpellHeaderUio, List<SpellUio>>>>, spells: State<List<Pair<SpellHeaderUio, List<SpellUio>>>>,
onAttackHit: (id: String) -> Unit, onAttackHit: (id: String) -> Unit,
onAttackDamage: (id: String) -> Unit, onAttackDamage: (id: String) -> Unit,
@ -204,14 +205,15 @@ fun ActionsPageContent(
} }
} }
if (tokens.value.isNotEmpty()) { if (skills.value.isNotEmpty()) {
stickyHeader { stickyHeader {
GenericHeader( GenericHeader(
label = R.string.character_sheet_title_skills, label = R.string.character_sheet_title_skills,
) )
} }
items(items = tokens.value) { items(items = skills.value) {
SkillItem( SkillItem(
modifier = Modifier.minimumInteractiveComponentSize(),
skill = it, skill = it,
onInfo = onSkillInfo, onInfo = onSkillInfo,
onThrow = onSkillThrow, onThrow = onSkillThrow,
@ -259,7 +261,7 @@ fun ActionPagePreview() {
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
attacks = rememberAttackListStatePreview(), attacks = rememberAttackListStatePreview(),
objects = rememberObjectListStatePreview(), objects = rememberObjectListStatePreview(),
tokens = rememberTokenListStatePreview(), skills = rememberSkillsListStatePreview(),
spells = rememberSpellListStatePreview(), spells = rememberSpellListStatePreview(),
onAttackHit = { }, onAttackHit = { },
onAttackDamage = { }, onAttackDamage = { },

View file

@ -7,10 +7,9 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.data.model.CharacterSheet 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.DiceThrow
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.Skill 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.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository 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.composable.edit.SkillEditDialogUio
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument 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.actions.SkillItemUio
import com.pixelized.rplexicon.utilitary.extentions.icon import com.pixelized.rplexicon.ui.screens.character.factory.SkillFactoryUioFactory
import com.pixelized.rplexicon.utilitary.extentions.modifier
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@ -30,9 +28,10 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class SkillsViewModel @Inject constructor( class SkillsViewModel @Inject constructor(
private val sheetRepository: CharacterSheetRepository, private val sheetRepository: CharacterSheetRepository,
private val skillRepository: SkillRepository,
private val firebaseRepository: FirebaseRepository, private val firebaseRepository: FirebaseRepository,
private val skillRepository: SkillRepository,
private val descriptionRepository: DescriptionRepository, private val descriptionRepository: DescriptionRepository,
private val skillFactory: SkillFactoryUioFactory,
application: Application, application: Application,
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
) : AndroidViewModel(application) { ) : AndroidViewModel(application) {
@ -50,48 +49,23 @@ class SkillsViewModel @Inject constructor(
init { init {
viewModelScope.launch { viewModelScope.launch {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
val data = SkillStruct()
sheetRepository.data sheetRepository.data
.combine(skillRepository.skills) { sheets, skills -> .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 -> .combine(firebaseRepository.getCharacter(character = character)) { _, fire ->
data.apply { this.fire = fire } data.fire = fire
} }
.collect { data -> .collect {
val (character, values, fire) = data val (character, fire, values) = data
// Filter out passive skills, theses are display in the Proficiency Page.
val skills = values.map { skill -> val skills = skillFactory.convert(
val description = descriptionRepository.find(name = skill.name) character = character,
val modifier = skill.effect?.modifier?.sumOf { fire = fire,
when (it) { skills = values.filter { it.passive.not() }
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,
)
}
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
_skills.value = skills _skills.value = skills
} }
@ -140,9 +114,9 @@ class SkillsViewModel @Inject constructor(
hideSkillEditDialog() hideSkillEditDialog()
} }
data class Struct( private data class SkillStruct(
val sheet: CharacterSheet?, var sheet: CharacterSheet? = null,
val skill: List<Skill>, var fire: CharacterSheetFire? = null,
var fire: CharacterSheetFire? = null var skill: List<Skill> = emptyList(),
) )
} }

View file

@ -7,8 +7,10 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope 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.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@ -20,6 +22,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -38,6 +41,10 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.LocalRollOverlay import com.pixelized.rplexicon.LocalRollOverlay
import com.pixelized.rplexicon.R 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.LabelPointUio
import com.pixelized.rplexicon.ui.screens.character.composable.character.Masteries import com.pixelized.rplexicon.ui.screens.character.composable.character.Masteries
import com.pixelized.rplexicon.ui.screens.character.composable.character.MasteriesUio 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.Stat
import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio 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.composable.preview.rememberCharacterSheetStatePreview
import com.pixelized.rplexicon.ui.screens.character.pages.actions.HandleSkillDetailDialog
import com.pixelized.rplexicon.ui.theme.LexiconTheme import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@ -72,6 +80,7 @@ fun ProficiencyPage(
ProficiencyPageContent( ProficiencyPageContent(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
sheet = sheet, sheet = sheet,
passives = viewModel.skills,
onStats = { stat -> onStats = { stat ->
overlay.prepareRoll(diceThrow = viewModel.statRoll(stat.id)) overlay.prepareRoll(diceThrow = viewModel.statRoll(stat.id))
overlay.showOverlay() overlay.showOverlay()
@ -80,6 +89,27 @@ fun ProficiencyPage(
overlay.prepareRoll(diceThrow = viewModel.proficiencyRoll(proficiency.id)) overlay.prepareRoll(diceThrow = viewModel.proficiencyRoll(proficiency.id))
overlay.showOverlay() 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) }, inner: Shape = remember { RoundedCornerShape(size = 8.dp) },
outline: Shape = remember { CutCornerShape(size = 16.dp) }, outline: Shape = remember { CutCornerShape(size = 16.dp) },
sheet: CharacterSheetUio, sheet: CharacterSheetUio,
passives: State<List<SkillItemUio>>,
onStats: (StatUio) -> Unit, onStats: (StatUio) -> Unit,
onProficiencies: (ProficiencyUio) -> Unit, onProficiencies: (ProficiencyUio) -> Unit,
onSkillThrow: (SkillItemUio) -> Unit,
onSkillCount: (SkillItemUio) -> Unit,
onSkillInfo: (SkillItemUio) -> Unit,
) { ) {
Box( Box(
modifier = modifier modifier = modifier
.verticalScroll(state = state) .verticalScroll(state = state)
.padding(all = 16.dp), .padding(vertical = 16.dp),
) { ) {
ProficiencyLayout( ProficiencyLayout(
inner = inner, inner = inner,
@ -181,8 +215,22 @@ fun ProficiencyPageContent(
}, },
masteries = { masteries = {
Masteries( Masteries(
modifier = Modifier.padding(horizontal = 16.dp),
masteries = sheet.masteries, 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 -> measurePolicy = { measurables, constraints ->
val spacingPx = with(density) { spacing.toPx().toInt() } val spacingPx = with(density) { spacing.toPx().toInt() }
val statsWidth = with(density) { 100.dp.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( val passivesMeasure = measurables.passives.measure(
constraints = constraints.copy( constraints = constraints.copy(
minWidth = proficienciesWidth, minWidth = proficienciesWidth,
@ -291,23 +340,23 @@ private fun ProficiencyLayout(
height = statsMeasure.height + masteriesMeasure.height + passivesMeasure.height + spacingPx * 2, height = statsMeasure.height + masteriesMeasure.height + passivesMeasure.height + spacingPx * 2,
) { ) {
statsMeasure.place( statsMeasure.place(
x = 0, x = horizontal,
y = 0, y = 0,
) )
savingThrowsMeasure.place( savingThrowsMeasure.place(
x = statsWidth + spacingPx, x = horizontal + statsWidth + spacingPx,
y = 0, y = 0,
) )
proficienciesMeasure.place( proficienciesMeasure.place(
x = statsWidth + spacingPx, x = horizontal + statsWidth + spacingPx,
y = savingThrowsMeasure.height + spacingPx, y = savingThrowsMeasure.height + spacingPx,
) )
speedMeasure.place( speedMeasure.place(
x = 0, x = horizontal,
y = statsHeight + spacingPx, y = statsHeight + spacingPx,
) )
passivesMeasure.place( passivesMeasure.place(
x = statsWidth + spacingPx, x = horizontal + statsWidth + spacingPx,
y = savingThrowsMeasure.height + proficienciesMeasure.height + spacingPx * 2, y = savingThrowsMeasure.height + proficienciesMeasure.height + spacingPx * 2,
) )
masteriesMeasure.place( masteriesMeasure.place(
@ -327,16 +376,20 @@ val List<Measurable>.passives get() = first { it.layoutId == "PassivesId" }
val List<Measurable>.masteries get() = first { it.layoutId == "MasteriesId" } val List<Measurable>.masteries get() = first { it.layoutId == "MasteriesId" }
@Composable @Composable
@Preview(uiMode = UI_MODE_NIGHT_NO, heightDp = 1300) @Preview(uiMode = UI_MODE_NIGHT_NO, heightDp = 1500)
@Preview(uiMode = UI_MODE_NIGHT_YES, heightDp = 1300) @Preview(uiMode = UI_MODE_NIGHT_YES, heightDp = 1500)
fun ProficiencyPreview() { fun ProficiencyPreview() {
LexiconTheme { LexiconTheme {
Surface { Surface {
val sheet by rememberCharacterSheetStatePreview() val sheet by rememberCharacterSheetStatePreview()
ProficiencyPageContent( ProficiencyPageContent(
sheet = sheet, sheet = sheet,
passives = rememberSkillsListStatePreview(),
onStats = { }, onStats = { },
onProficiencies = { }, onProficiencies = { },
onSkillThrow = { },
onSkillCount = { },
onSkillInfo = { },
) )
} }
} }

View file

@ -6,13 +6,23 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.DiceThrow 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.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository 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.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.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio 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.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 dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@ -23,8 +33,12 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class ProficiencyViewModel @Inject constructor( class ProficiencyViewModel @Inject constructor(
private val characterRepository: CharacterSheetRepository, private val characterRepository: CharacterSheetRepository,
private val firebaseRepository: FirebaseRepository,
private val alterationRepository: AlterationRepository, private val alterationRepository: AlterationRepository,
private val characterSheetFactory: CharacterSheetUioFactory, private val characterSheetFactory: CharacterSheetUioFactory,
private val skillRepository: SkillRepository,
private val descriptionRepository: DescriptionRepository,
private val skillFactory: SkillFactoryUioFactory,
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
application: Application, application: Application,
) : AndroidViewModel(application) { ) : AndroidViewModel(application) {
@ -33,27 +47,63 @@ class ProficiencyViewModel @Inject constructor(
private val _sheet = mutableStateOf<CharacterSheetUio?>(null) private val _sheet = mutableStateOf<CharacterSheetUio?>(null)
val sheet: State<CharacterSheetUio?> get() = _sheet val sheet: State<CharacterSheetUio?> get() = _sheet
private val _skillEditDialog = mutableStateOf<SkillEditDialogUio?>(null)
val skillEditDialog: State<SkillEditDialogUio?> get() = _skillEditDialog
private val _skillDetailDialog = mutableStateOf<SkillDetailUio?>(null)
val skillDetailDialog: State<SkillDetailUio?> get() = _skillDetailDialog
private val _skills = mutableStateOf<List<SkillItemUio>>(emptyList())
val skills: State<List<SkillItemUio>> get() = _skills
init { init {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch {
characterRepository.data launch(Dispatchers.IO) {
.combine(alterationRepository.assignedAlterations) { sheets, _ -> sheets } characterRepository.data
.collect { .combine(alterationRepository.assignedAlterations) { sheets, _ -> sheets }
val characterSheet = it[character] .collect {
if (characterSheet != null) { val characterSheet = it[character]
val alterations = alterationRepository.getActiveAlterationsStatus(character) if (characterSheet != null) {
val sheet = characterSheetFactory.convert( val alterations =
sheet = characterSheet, alterationRepository.getActiveAlterationsStatus(character)
status = alterations, val sheet = characterSheetFactory.convert(
) sheet = characterSheet,
withContext(Dispatchers.Main) { status = alterations,
_sheet.value = sheet )
} withContext(Dispatchers.Main) {
} else { _sheet.value = sheet
launch { }
characterRepository.fetchCharacterSheet() } 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.STEALTH -> DiceThrow.Stealth(character)
ProficiencyUio.ID.SURVIVAL -> DiceThrow.Survival(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<Skill> = emptyList(),
)
} }