Add spacial resource into CharacterSheet header.

This commit is contained in:
Thomas Andres Gomez 2023-12-12 12:25:13 +01:00
parent 8c1d83402c
commit 75644aad91
14 changed files with 246 additions and 33 deletions

View file

@ -1,11 +1,13 @@
package com.pixelized.rplexicon.data.model package com.pixelized.rplexicon.data.model
import com.pixelized.rplexicon.R
data class CharacterSheet( data class CharacterSheet(
val name: String, val name: String,
val race: String?, val race: String?,
val proficiency: Int, // Bonus de maîtrise val proficiency: Int, // Bonus de maîtrise
val level: Int, // Niveau val level: Int, // Niveau
val characterClass: String, // Classe val characterClass: List<Class>, // Classe
val hitPoint: Int, // Point de vie MAX val hitPoint: Int, // Point de vie MAX
val spell1: Int?, // level 1 spell slot val spell1: Int?, // level 1 spell slot
val spell2: Int?, // level 2 spell slot val spell2: Int?, // level 2 spell slot
@ -53,9 +55,36 @@ data class CharacterSheet(
val languages: List<String>, // languages masteries val languages: List<String>, // languages masteries
val others: List<String>, // others masteries val others: List<String>, // others masteries
) { ) {
val isWarlock: Boolean get() = characterClass.contains(CLASS_WARLOCK) val isWarlock: Boolean get() = characterClass.contains(Class.WARLOCK)
companion object { enum class Class(
const val CLASS_WARLOCK = "Occultiste" val value: String,
val resource: String? = null,
val label: Int? = null,
) {
BARBARIAN(
value = "Barbare",
resource = "Rage",
label = R.string.character_sheet_title_rage
),
BARD(
value = "Barde",
resource = "Inspiration bardique",
label = R.string.character_sheet_title_inspiration
),
CLERIC(
value = "Clerc",
resource = "Conduit divin",
label = R.string.character_sheet_title_conduit
),
DRUID(value = "Druide"),
FIGHTER(value = "Guerrier"),
MONK(value = "Moine"),
PALADIN(value = "Paladin"),
RANGER(value = "Rodeur"),
ROGUE(value = "Roublard"),
SORCERER(value = "Ensorceleur"),
WARLOCK(value = "Occultiste"),
WIZARD(value = "Magicien"),
} }
} }

View file

@ -25,7 +25,9 @@ class AlterationParser @Inject constructor(
sheet.forEachRowIndexed { index, row -> sheet.forEachRowIndexed { index, row ->
when (index) { when (index) {
0 -> updateStructure(row = row, columns = COLUMNS + characterSheets.map { column(it.name) }) 0 -> updateStructure(
row = row,
columns = COLUMNS + characterSheets.map { column(it.name) })
else -> { else -> {
// Assume that the name is the first column. // Assume that the name is the first column.
@ -56,7 +58,7 @@ class AlterationParser @Inject constructor(
characterSheets characterSheets
.filter { // check if the alteration is applicable to the character .filter { // check if the alteration is applicable to the character
alteration.target.let { target -> alteration.target.let { target ->
target == ALL || target == it.characterClass || target == it.race || target == it.name target == ALL || it.characterClass.any { it.value == target } || target == it.race || target == it.name
} }
} }
.forEach { // check the default alteration state for that character .forEach { // check the default alteration state for that character

View file

@ -0,0 +1,32 @@
package com.pixelized.rplexicon.data.parser.characterSheet
import com.pixelized.rplexicon.data.model.CharacterSheet
import javax.inject.Inject
class CharacterClassParser @Inject constructor() {
fun parse(value: String?): List<CharacterSheet.Class> {
return value
?.split(",")
?.map { it.trim() }
?.mapNotNull {
when (it) {
CharacterSheet.Class.BARBARIAN.value -> CharacterSheet.Class.BARBARIAN
CharacterSheet.Class.BARD.value -> CharacterSheet.Class.BARD
CharacterSheet.Class.CLERIC.value -> CharacterSheet.Class.CLERIC
CharacterSheet.Class.DRUID.value -> CharacterSheet.Class.DRUID
CharacterSheet.Class.FIGHTER.value -> CharacterSheet.Class.FIGHTER
CharacterSheet.Class.MONK.value -> CharacterSheet.Class.MONK
CharacterSheet.Class.PALADIN.value -> CharacterSheet.Class.PALADIN
CharacterSheet.Class.RANGER.value -> CharacterSheet.Class.RANGER
CharacterSheet.Class.ROGUE.value -> CharacterSheet.Class.ROGUE
CharacterSheet.Class.SORCERER.value -> CharacterSheet.Class.SORCERER
CharacterSheet.Class.WARLOCK.value -> CharacterSheet.Class.WARLOCK
CharacterSheet.Class.WIZARD.value -> CharacterSheet.Class.WIZARD
else -> null
}
}
?: emptyList()
}
}

View file

@ -1,14 +1,17 @@
package com.pixelized.rplexicon.data.parser package com.pixelized.rplexicon.data.parser.characterSheet
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.parser.column
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class CharacterSheetParser @Inject constructor() { class CharacterSheetParser @Inject constructor(
private val classParser: CharacterClassParser,
) {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(value: ValueRange): Map<String, CharacterSheet> = parserScope { fun parse(value: ValueRange): Map<String, CharacterSheet> = parserScope {
// fetch the character sheet // fetch the character sheet
@ -34,7 +37,7 @@ class CharacterSheetParser @Inject constructor() {
race = item.parse(column = RACE), race = item.parse(column = RACE),
proficiency = item.parseInt(column = MASTERY) ?: 2, proficiency = item.parseInt(column = MASTERY) ?: 2,
level = item.parseInt(column = LEVEL) ?: 2, level = item.parseInt(column = LEVEL) ?: 2,
characterClass = item.parse(column = CLASS) ?: "", characterClass = classParser.parse(value = item.parse(column = CLASS)),
hitPoint = item.parseInt(column = MAX_HIT_POINT) ?: 1, hitPoint = item.parseInt(column = MAX_HIT_POINT) ?: 1,
spell1 = item.parseInt(column = SPELL_LEVEL_1), spell1 = item.parseInt(column = SPELL_LEVEL_1),
spell2 = item.parseInt(column = SPELL_LEVEL_2), spell2 = item.parseInt(column = SPELL_LEVEL_2),

View file

@ -1,7 +1,7 @@
package com.pixelized.rplexicon.data.repository.character package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.parser.CharacterSheetParser import com.pixelized.rplexicon.data.parser.characterSheet.CharacterSheetParser
import com.pixelized.rplexicon.data.repository.CharacterBinder import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.utilitary.Update import com.pixelized.rplexicon.utilitary.Update

View file

@ -22,7 +22,7 @@ class SkillRepository @Inject constructor(
var lastSuccessFullUpdate: Update = Update.INITIAL var lastSuccessFullUpdate: Update = Update.INITIAL
private set private set
fun find(character: String, skill: String): Skill? { fun find(character: String, skill: String?): Skill? {
return skills.value[character]?.firstOrNull { it.name == skill } return skills.value[character]?.firstOrNull { it.name == skill }
} }

View file

@ -74,8 +74,6 @@ import com.pixelized.rplexicon.ui.screens.character.CharacterTabUio.Inventory
import com.pixelized.rplexicon.ui.screens.character.CharacterTabUio.Proficiency import com.pixelized.rplexicon.ui.screens.character.CharacterTabUio.Proficiency
import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeader import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeader
import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio
import com.pixelized.rplexicon.ui.screens.character.composable.character.ProficiencyUio.ID.*
import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio.ID.*
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberCharacterHeaderStatePreview import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberCharacterHeaderStatePreview
import com.pixelized.rplexicon.ui.screens.character.pages.actions.ActionPage import com.pixelized.rplexicon.ui.screens.character.pages.actions.ActionPage
import com.pixelized.rplexicon.ui.screens.character.pages.actions.ActionPagePreview import com.pixelized.rplexicon.ui.screens.character.pages.actions.ActionPagePreview
@ -157,6 +155,9 @@ fun CharacterSheetScreen(
scope.launch { pagerState.animateScrollToPage(it) } scope.launch { pagerState.animateScrollToPage(it) }
}, },
onHitPoint = headerViewModel::toggleHitPointDialog, onHitPoint = headerViewModel::toggleHitPointDialog,
onResource = {
// TODO()
},
onDeathRoll = { onDeathRoll = {
overlay.prepareRoll(diceThrow = headerViewModel.onDeathThrow()) overlay.prepareRoll(diceThrow = headerViewModel.onDeathThrow())
overlay.showOverlay() overlay.showOverlay()
@ -260,6 +261,7 @@ private fun CharacterSheetContent(
tabs: State<List<CharacterTabUio>>, tabs: State<List<CharacterTabUio>>,
header: State<CharacterSheetHeaderUio?>, header: State<CharacterSheetHeaderUio?>,
onHitPoint: () -> Unit, onHitPoint: () -> Unit,
onResource: () -> Unit,
onDeathRoll: () -> Unit, onDeathRoll: () -> Unit,
onDeathSuccess: () -> Unit, onDeathSuccess: () -> Unit,
onDeathFailure: () -> Unit, onDeathFailure: () -> Unit,
@ -360,6 +362,7 @@ private fun CharacterSheetContent(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
header = header, header = header,
onHitPoint = onHitPoint, onHitPoint = onHitPoint,
onResource = onResource,
onDeathRoll = onDeathRoll, onDeathRoll = onDeathRoll,
onDeathSuccess = onDeathSuccess, onDeathSuccess = onDeathSuccess,
onDeathFailure = onDeathFailure, onDeathFailure = onDeathFailure,
@ -501,6 +504,7 @@ private fun CharacterScreenPreview(
onFullRefresh = { }, onFullRefresh = { },
loader = { }, loader = { },
onHitPoint = { }, onHitPoint = { },
onResource = { },
onDeathRoll = { }, onDeathRoll = { },
onDeathSuccess = { }, onDeathSuccess = { },
onDeathFailure = { }, onDeathFailure = { },

View file

@ -27,6 +27,7 @@ data class CharacterSheetHeaderUio(
val hitPoint: LabelPointUio, val hitPoint: LabelPointUio,
val speed: LabelPointUio, val speed: LabelPointUio,
val dC: LabelPointUio?, val dC: LabelPointUio?,
val resource: ResourcePointUio?,
val death: DeathThrowUio? = null val death: DeathThrowUio? = null
) )
@ -36,6 +37,7 @@ fun CharacterSheetHeader(
padding: PaddingValues = PaddingValues(start = 16.dp, end = 16.dp, bottom = 4.dp), padding: PaddingValues = PaddingValues(start = 16.dp, end = 16.dp, bottom = 4.dp),
header: State<CharacterSheetHeaderUio?>, header: State<CharacterSheetHeaderUio?>,
onHitPoint: () -> Unit, onHitPoint: () -> Unit,
onResource: () -> Unit,
onDeathRoll: () -> Unit, onDeathRoll: () -> Unit,
onDeathSuccess: () -> Unit, onDeathSuccess: () -> Unit,
onDeathFailure: () -> Unit, onDeathFailure: () -> Unit,
@ -55,6 +57,9 @@ fun CharacterSheetHeader(
alignment = Alignment.CenterHorizontally alignment = Alignment.CenterHorizontally
), ),
) { ) {
header.value?.resource?.let {
ResourcePoint(label = it, onClick = onResource)
}
header.value?.armorClass?.let { header.value?.armorClass?.let {
LabelPoint(label = it) LabelPoint(label = it)
} }
@ -95,6 +100,7 @@ private fun CharacterSheetHeaderPreview() {
death = rememberDeathThrowUio(), death = rememberDeathThrowUio(),
), ),
onHitPoint = { }, onHitPoint = { },
onResource = { },
onDeathRoll = { }, onDeathRoll = { },
onDeathSuccess = { }, onDeathSuccess = { },
onDeathFailure = { }, onDeathFailure = { },

View file

@ -0,0 +1,100 @@
package com.pixelized.rplexicon.ui.screens.character.composable.character
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.theme.LexiconTheme
@Stable
data class ResourcePointUio(
val label: Int?,
val value: String?,
val max: String? = null,
)
@Composable
fun ResourcePoint(
modifier: Modifier = Modifier,
labelStyle: TextStyle = MaterialTheme.typography.labelSmall,
valueStyle: TextStyle = MaterialTheme.typography.headlineSmall,
maxStyle: TextStyle = MaterialTheme.typography.titleSmall,
label: ResourcePointUio,
onClick: (() -> Unit)? = null,
) {
Column(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false),
enabled = onClick != null, onClick = { onClick?.invoke() }
)
.then(other = modifier),
horizontalAlignment = Alignment.CenterHorizontally
) {
label.label?.let {
Text(
style = labelStyle,
fontWeight = FontWeight.Light,
text = stringResource(id = it),
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
style = valueStyle,
color = when (onClick) {
null -> MaterialTheme.colorScheme.onSurface
else -> MaterialTheme.colorScheme.primary
},
fontWeight = FontWeight.Bold,
text = label.value ?: "0"
)
label.max?.let {
Text(
modifier = Modifier.alignByBaseline(),
style = maxStyle,
text = it,
)
}
}
}
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun LabelPointPreview() {
LexiconTheme {
Surface {
ResourcePoint(
label = ResourcePointUio(
label = R.string.character_sheet_title_inspiration,
value = "2",
max = "/ 2",
),
onClick = { },
)
}
}
}

View file

@ -8,6 +8,7 @@ import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio
import com.pixelized.rplexicon.ui.screens.character.composable.character.DeathThrowUio import com.pixelized.rplexicon.ui.screens.character.composable.character.DeathThrowUio
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.ResourcePointUio
@Composable @Composable
@Stable @Stable
@ -34,6 +35,11 @@ fun rememberCharacterHeaderStatePreview(
label = R.string.character_sheet_title_dc, label = R.string.character_sheet_title_dc,
value = "13", value = "13",
), ),
resource = ResourcePointUio(
label = R.string.character_sheet_title_rage,
value = "2",
max = "/ 2",
),
death = death, death = death,
) )
) )

View file

@ -4,6 +4,7 @@ import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio
import com.pixelized.rplexicon.ui.screens.character.composable.character.DeathThrowUio import com.pixelized.rplexicon.ui.screens.character.composable.character.DeathThrowUio
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.ResourcePointUio
import com.pixelized.rplexicon.ui.screens.character.pages.actions.HeaderViewModel import com.pixelized.rplexicon.ui.screens.character.pages.actions.HeaderViewModel
import javax.inject.Inject import javax.inject.Inject
@ -35,6 +36,15 @@ class CharacterSheetHeaderUioFactory @Inject constructor() {
value = "$it", value = "$it",
) )
}, },
resource = if (sheetHeaderData?.resourceMax != null) {
ResourcePointUio(
label = sheetHeaderData.resourceLabel,
value = "${fireHeaderData?.resource ?: 0}",
max = "/ ${sheetHeaderData.resourceMax}"
)
} else {
null
},
death = if (fireHeaderData?.hp == 0) { death = if (fireHeaderData?.hp == 0) {
DeathThrowUio( DeathThrowUio(
success = fireHeaderData.deathSuccess, success = fireHeaderData.deathSuccess,

View file

@ -12,6 +12,7 @@ import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository 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.SkillRepository
import com.pixelized.rplexicon.ui.composable.edit.HpPointDialogUio import com.pixelized.rplexicon.ui.composable.edit.HpPointDialogUio
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio
@ -30,6 +31,7 @@ class HeaderViewModel @Inject constructor(
private val firebaseRepository: FirebaseRepository, private val firebaseRepository: FirebaseRepository,
characterRepository: CharacterSheetRepository, characterRepository: CharacterSheetRepository,
alterationRepository: AlterationRepository, alterationRepository: AlterationRepository,
skillRepository: SkillRepository,
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
) : ViewModel() { ) : ViewModel() {
val character = savedStateHandle.characterSheetArgument.name val character = savedStateHandle.characterSheetArgument.name
@ -51,18 +53,24 @@ class HeaderViewModel @Inject constructor(
viewModelScope.launch { viewModelScope.launch {
launch(context = Dispatchers.IO) { launch(context = Dispatchers.IO) {
characterRepository.data characterRepository.data
.combine(alterationRepository.assignedAlterations) { sheets, _ -> sheets } .combine(skillRepository.skills) { sheets, _ -> sheets[character] }
.collect { sheets -> .combine(alterationRepository.assignedAlterations) { sheet, _ -> sheet }
val character = sheets[character] .collect { sheet ->
if (character != null) { if (sheet != null) {
val alterations = alterationRepository.getActiveAlterationsStatus( val alterations = alterationRepository.getActiveAlterationsStatus(
character = character.name, character = sheet.name,
) )
val resource = sheet.characterClass.firstOrNull()
val data = SheetHeaderData( val data = SheetHeaderData(
hpMax = character.hitPoint + alterations[Property.HIT_POINT].sum, hpMax = sheet.hitPoint + alterations[Property.HIT_POINT].sum,
speed = character.speed, resourceLabel = resource?.label,
ca = character.armorClass + alterations[Property.ARMOR_CLASS].sum, resourceMax = skillRepository.find(
dc = character.dC, character = character,
skill = resource?.resource,
)?.amount,
speed = sheet.speed,
ca = sheet.armorClass + alterations[Property.ARMOR_CLASS].sum,
dc = sheet.dC,
) )
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
sheetData.value = data sheetData.value = data
@ -73,17 +81,21 @@ class HeaderViewModel @Inject constructor(
} }
} }
launch(context = Dispatchers.IO) { launch(context = Dispatchers.IO) {
firebaseRepository.getCharacter(character = character).collect { sheets -> characterRepository.data
val data = FireHeaderData( .combine(firebaseRepository.getCharacter(character = character)) { sheets, fire -> sheets[character] to fire }
hp = sheets.hitPoint?.value ?: 1, .collect {
extra = sheets.hitPoint?.additional ?: 0, val (sheet, fire) = it
deathSuccess = sheets.death?.success ?: 0, val data = FireHeaderData(
deathFailure = sheets.death?.failure ?: 0, hp = fire.hitPoint?.value ?: 1,
) resource = fire.skills[sheet?.characterClass?.firstOrNull()?.resource],
withContext(Dispatchers.Main) { extra = fire.hitPoint?.additional ?: 0,
fireData.value = data deathSuccess = fire.death?.success ?: 0,
deathFailure = fire.death?.failure ?: 0,
)
withContext(Dispatchers.Main) {
fireData.value = data
}
} }
}
} }
} }
} }
@ -141,6 +153,8 @@ class HeaderViewModel @Inject constructor(
@Stable @Stable
data class SheetHeaderData( data class SheetHeaderData(
val hpMax: Int, val hpMax: Int,
val resourceLabel: Int?,
val resourceMax: Int?,
val speed: Int, val speed: Int,
val ca: Int, val ca: Int,
val dc: Int?, val dc: Int?,
@ -149,6 +163,7 @@ class HeaderViewModel @Inject constructor(
@Stable @Stable
data class FireHeaderData( data class FireHeaderData(
val hp: Int, val hp: Int,
val resource: Int?,
val extra: Int, val extra: Int,
val deathSuccess: Int, val deathSuccess: Int,
val deathFailure: Int, val deathFailure: Int,

View file

@ -88,6 +88,9 @@
<string name="character_sheet_title_ca">CA</string> <string name="character_sheet_title_ca">CA</string>
<string name="character_sheet_title_dc">DD</string> <string name="character_sheet_title_dc">DD</string>
<string name="character_sheet_title_speed">Vitesse</string> <string name="character_sheet_title_speed">Vitesse</string>
<string name="character_sheet_title_rage">Rage</string>
<string name="character_sheet_title_inspiration">Inspiration</string>
<string name="character_sheet_title_conduit">Conduit Divin</string>
<string name="character_sheet_title_initiative">Initiative</string> <string name="character_sheet_title_initiative">Initiative</string>
<string name="character_sheet_title_saving_throws">Jet de sauvegarde</string> <string name="character_sheet_title_saving_throws">Jet de sauvegarde</string>
<string name="character_sheet_title_proficiencies">Talents</string> <string name="character_sheet_title_proficiencies">Talents</string>

View file

@ -96,6 +96,9 @@
<string name="character_sheet_title_ca">CA</string> <string name="character_sheet_title_ca">CA</string>
<string name="character_sheet_title_dc">DC</string> <string name="character_sheet_title_dc">DC</string>
<string name="character_sheet_title_speed">Speed</string> <string name="character_sheet_title_speed">Speed</string>
<string name="character_sheet_title_rage">Rage</string>
<string name="character_sheet_title_inspiration">Inspiration</string>
<string name="character_sheet_title_conduit">Divine conduit</string>
<string name="character_sheet_title_initiative">Initiative</string> <string name="character_sheet_title_initiative">Initiative</string>
<string name="character_sheet_title_saving_throws">Saving Throws</string> <string name="character_sheet_title_saving_throws">Saving Throws</string>
<string name="character_sheet_title_proficiencies">Proficiencies</string> <string name="character_sheet_title_proficiencies">Proficiencies</string>