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 cost: String?,
val effect: Throw?,
val passive: Boolean,
)

View file

@ -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)
}
}

View file

@ -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<List<SkillItemUio>> = remember {
fun rememberSkillsListStatePreview(): State<List<SkillItemUio>> = remember {
val provider = CounterItemPreviewProvider()
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.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<List<AttackUio>>,
objects: State<List<ObjectItemUio>>,
tokens: State<List<SkillItemUio>>,
skills: State<List<SkillItemUio>>,
spells: State<List<Pair<SpellHeaderUio, List<SpellUio>>>>,
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 = { },

View file

@ -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<Skill>,
var fire: CharacterSheetFire? = null
private data class SkillStruct(
var sheet: CharacterSheet? = 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.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<List<SkillItemUio>>,
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<Measurable>.passives get() = first { it.layoutId == "PassivesId" }
val List<Measurable>.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 = { },
)
}
}

View file

@ -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<CharacterSheetUio?>(null)
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 {
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<Skill> = emptyList(),
)
}