Add inventory and equipment feature.

This commit is contained in:
Thomas Andres Gomez 2023-10-20 17:29:15 +02:00
parent 2be527abf9
commit daea5fd6dc
74 changed files with 1609 additions and 574 deletions

View file

@ -6,15 +6,17 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.repository.data.ActionRepository
import com.pixelized.rplexicon.repository.data.AlterationRepository
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.DescriptionRepository
import com.pixelized.rplexicon.repository.data.LexiconRepository
import com.pixelized.rplexicon.repository.data.LocationRepository
import com.pixelized.rplexicon.repository.data.QuestRepository
import com.pixelized.rplexicon.repository.data.SkillRepository
import com.pixelized.rplexicon.repository.data.SpellRepository
import com.pixelized.rplexicon.repository.data.character.ActionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.EquipmentRepository
import com.pixelized.rplexicon.repository.data.character.InventoryRepository
import com.pixelized.rplexicon.repository.data.lexicon.LexiconRepository
import com.pixelized.rplexicon.repository.data.lexicon.LocationRepository
import com.pixelized.rplexicon.repository.data.lexicon.QuestRepository
import com.pixelized.rplexicon.repository.data.character.SkillRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -34,6 +36,8 @@ class LauncherViewModel @Inject constructor(
spellRepository: SpellRepository,
skillRepository: SkillRepository,
descriptionRepository: DescriptionRepository,
inventoryRepository: InventoryRepository,
equipmentRepository: EquipmentRepository,
) : ViewModel() {
private val _error = MutableSharedFlow<String>()
@ -76,7 +80,7 @@ class LauncherViewModel @Inject constructor(
_error.tryEmit("CharacterSheet fail to update")
}
}
val descriptionSheet = async {
val description = async {
try {
descriptionRepository.fetchDescription()
} catch (exception: Exception) {
@ -84,7 +88,23 @@ class LauncherViewModel @Inject constructor(
_error.tryEmit("Skill/Spell description fail to update")
}
}
awaitAll(lexicon, location, quest, characterSheet, descriptionSheet)
val inventory = async {
try {
inventoryRepository.fetchInventory()
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
_error.tryEmit("Inventories fail to update")
}
}
val equipment = async {
try {
equipmentRepository.fetchEquipment()
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
_error.tryEmit("Equipments fail to update")
}
}
awaitAll(characterSheet)
val alteration = async {
try {
@ -96,7 +116,7 @@ class LauncherViewModel @Inject constructor(
}
val action = async {
try {
actionRepository.fetchActions()
actionRepository.fetchActions(characters = characterSheetRepository.data.value)
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
_error.tryEmit("Action fail to update")
@ -118,7 +138,10 @@ class LauncherViewModel @Inject constructor(
_error.tryEmit("Skill fail to update")
}
}
awaitAll(alteration, action, spell, skill)
awaitAll(
lexicon, location, quest, description, inventory,
equipment, alteration, action, spell, skill,
)
isLoading = false
}

View file

@ -6,25 +6,31 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import com.pixelized.rplexicon.ui.composable.BlurredOverlayHost
import com.pixelized.rplexicon.ui.navigation.ScreenNavHost
import com.pixelized.rplexicon.ui.screens.rolls.BlurredRollOverlayHostState
@ -32,6 +38,7 @@ import com.pixelized.rplexicon.ui.screens.rolls.RollOverlay
import com.pixelized.rplexicon.ui.screens.rolls.RollOverlayViewModel
import com.pixelized.rplexicon.ui.screens.rolls.rememberBlurredRollOverlayHostState
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
import dagger.hilt.android.AndroidEntryPoint
val LocalActivity = staticCompositionLocalOf<Activity> {
@ -97,10 +104,32 @@ class MainActivity : ComponentActivity() {
}
},
snackbarHost = {
val isDarkTheme = isSystemInDarkTheme()
val elevation = remember {
derivedStateOf { if (isDarkTheme) 2.dp else 0.dp }
}
SnackbarHost(
modifier = Modifier.navigationBarsPadding(),
hostState = LocalSnack.current,
)
) {
Snackbar(
modifier = Modifier
.padding(horizontal = 16.dp)
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
),
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
elevation = elevation.value
),
content = {
Text(
color = MaterialTheme.colorScheme.onSurface,
text = it.visuals.message,
)
}
)
}
}
)
BackHandler(enabled = overlay.isOverlayVisible) {

View file

@ -10,11 +10,11 @@ import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.model.Skill
import com.pixelized.rplexicon.model.Throw
import com.pixelized.rplexicon.repository.data.ActionRepository
import com.pixelized.rplexicon.repository.data.AlterationRepository
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.SkillRepository
import com.pixelized.rplexicon.repository.data.SpellRepository
import com.pixelized.rplexicon.repository.data.character.ActionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.SkillRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio
import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio
import com.pixelized.rplexicon.utilitary.extentions.icon

View file

@ -0,0 +1,52 @@
package com.pixelized.rplexicon.model
data class Equipment(
val head: String? = null,
val face: String? = null,
val neck: String? = null,
val shoulder: String? = null,
val body: String? = null,
val chest: String? = null,
val arm: String? = null,
val hand: String? = null,
val ring1: String? = null,
val ring2: String? = null,
val waist: String? = null,
val foot: String? = null,
val mainHand: String? = null,
val offHand: String? = null,
) {
class Builder(
var head: String? = null,
var face: String? = null,
var neck: String? = null,
var shoulder: String? = null,
var body: String? = null,
var chest: String? = null,
var arm: String? = null,
var hand: String? = null,
var ring1: String? = null,
var ring2: String? = null,
var waist: String? = null,
var foot: String? = null,
var mainHand: String? = null,
var offHand: String? = null,
) {
fun build(): Equipment = Equipment(
head = head,
face = face,
neck = neck,
shoulder = shoulder,
body = body,
chest = chest,
arm = arm,
hand = hand,
ring1 = ring1,
ring2 = ring2,
waist = waist,
foot = foot,
mainHand = mainHand,
offHand = offHand,
)
}
}

View file

@ -0,0 +1,42 @@
package com.pixelized.rplexicon.model
data class Inventory(
val items: List<Item>,
) {
class Builder(
val items: MutableList<Item.Builder> = mutableListOf(),
) {
fun build(): Inventory = Inventory(
items = items.map { it.build() }
)
fun find(container: String): Item.Builder? {
return items.firstOrNull { it.find(name = container) != null }
}
}
data class Item(
val name: String,
val amount: String?,
val items: List<Item>,
) {
class Builder(
var name: String,
var amount: String? = null,
val items: MutableList<Builder> = mutableListOf(),
) {
fun build(): Item = Item(
name = name,
amount = amount,
items = items.map { it.build() }
)
fun find(name: String): Builder? {
return when (this.name) {
name -> this
else -> items.firstOrNull { it.find(name = name) != null }
}
}
}
}
}

View file

@ -1,25 +0,0 @@
package com.pixelized.rplexicon.repository.data
object Sheet {
object Lexicon {
const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs"
const val LEXICON = "Lexique"
const val QUEST_JOURNAL = "Journal de quêtes"
const val MAP = "Lieux"
const val MARQUEE = "Points d'intérêt"
}
object Character {
const val ID = "1fHfzeb8y5u9lEQB1iI-jBEhqu7YSip5sAajXcXK7VJ8"
const val CHARACTER = "Feuille de personnage"
const val ATTACK = "Attaques"
const val MAGIC = "Magies"
const val SKILL = "Capacités"
const val MAGIC_LEXICON = "Lexique magique"
const val ALTERATION = "Altérations"
const val DESCRIPTION = "Descriptions"
}
}

View file

@ -0,0 +1,24 @@
package com.pixelized.rplexicon.repository.data
object LexiconBinder {
const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs"
const val LEXICON = "Lexique"
const val QUEST_JOURNAL = "Journal de quêtes"
const val MAP = "Lieux"
const val MARQUEE = "Points d'intérêt"
}
object CharacterBinder {
const val ID = "1fHfzeb8y5u9lEQB1iI-jBEhqu7YSip5sAajXcXK7VJ8"
const val CHARACTER = "Feuille de personnage"
const val ATTACK = "Attaques"
const val MAGIC = "Magies"
const val SKILL = "Capacités"
const val MAGIC_LEXICON = "Lexique magique"
const val ALTERATION = "Altérations"
const val DESCRIPTION = "Descriptions"
const val INVENTORY = "Inventaires"
const val EQUIPMENT = "Équipements"
}

View file

@ -1,7 +1,9 @@
package com.pixelized.rplexicon.repository.data
package com.pixelized.rplexicon.repository.data.character
import com.pixelized.rplexicon.model.Attack
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.AttackParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
@ -13,7 +15,6 @@ import javax.inject.Singleton
@Singleton
class ActionRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val characterSheetRepository: CharacterSheetRepository,
private val actionParser: AttackParser,
) {
private val _data = MutableStateFlow<Map<String, List<Attack>>>(emptyMap())
@ -31,12 +32,12 @@ class ActionRepository @Inject constructor(
}
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchActions() {
suspend fun fetchActions(characters: Map<String, CharacterSheet>) {
googleRepository.fetch { sheet ->
val request = sheet.get(Sheet.Character.ID, Sheet.Character.ATTACK)
val request = sheet.get(CharacterBinder.ID, CharacterBinder.ATTACK)
val data = actionParser.parse(
value = request.execute(),
charactersSheets = characterSheetRepository.data.value
charactersSheets = characters
)
_data.emit(data)
lastSuccessFullUpdate = Update.currentTime()

View file

@ -1,9 +1,10 @@
package com.pixelized.rplexicon.repository.data
package com.pixelized.rplexicon.repository.data.character
import com.pixelized.rplexicon.model.Alteration
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.alteration.AlterationParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
@ -86,7 +87,7 @@ class AlterationRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchAlterationSheet(sheets: List<CharacterSheet>) {
googleRepository.fetch { sheet ->
val request = sheet.get(Sheet.Character.ID, Sheet.Character.ALTERATION)
val request = sheet.get(CharacterBinder.ID, CharacterBinder.ALTERATION)
val data = alterationParser.parse(values = request.execute(), sheets = sheets)
_assignedAlterations.emit(data)
lastSuccessFullUpdate = Update.currentTime()

View file

@ -1,13 +1,13 @@
package com.pixelized.rplexicon.repository.data
package com.pixelized.rplexicon.repository.data.character
import com.pixelized.rplexicon.repository.parser.CharacterSheetParser
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flow
import javax.inject.Inject
import javax.inject.Singleton
@ -35,7 +35,7 @@ class CharacterSheetRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchCharacterSheet() {
googleRepository.fetch { sheet ->
val request = sheet.get(Sheet.Character.ID, Sheet.Character.CHARACTER)
val request = sheet.get(CharacterBinder.ID, CharacterBinder.CHARACTER)
val data = characterSheetParser.parse(value = request.execute())
_data.emit(data)
lastSuccessFullUpdate = Update.currentTime()

View file

@ -1,7 +1,8 @@
package com.pixelized.rplexicon.repository.data
package com.pixelized.rplexicon.repository.data.character
import com.pixelized.rplexicon.model.Description
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.DescriptionParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
@ -26,7 +27,7 @@ class DescriptionRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchDescription() {
googleRepository.fetch { sheet ->
val request = sheet.get(Sheet.Character.ID, Sheet.Character.DESCRIPTION)
val request = sheet.get(CharacterBinder.ID, CharacterBinder.DESCRIPTION)
val data = parser.parse(data = request.execute())
_data.tryEmit(data)

View file

@ -0,0 +1,36 @@
package com.pixelized.rplexicon.repository.data.character
import com.pixelized.rplexicon.model.Equipment
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.inventory.EquipmentParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class EquipmentRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val equipmentParser: EquipmentParser,
) {
private val _data = MutableStateFlow<Map<String, Equipment>>(emptyMap())
val data: StateFlow<Map<String, Equipment>> get() = _data
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
fun find(name: String?): Equipment? = _data.value[name]
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchEquipment() {
googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.EQUIPMENT)
val data = equipmentParser.parse(data = request.execute())
_data.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime()
}
}
}

View file

@ -0,0 +1,36 @@
package com.pixelized.rplexicon.repository.data.character
import com.pixelized.rplexicon.model.Inventory
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.inventory.InventoryParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class InventoryRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val inventoryParser: InventoryParser,
) {
private val _data = MutableStateFlow<Map<String, Inventory>>(emptyMap())
val data: StateFlow<Map<String, Inventory>> get() = _data
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
fun find(name: String?): Inventory? = _data.value[name]
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchInventory() {
googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.INVENTORY)
val data = inventoryParser.parse(data = request.execute())
_data.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime()
}
}
}

View file

@ -1,7 +1,8 @@
package com.pixelized.rplexicon.repository.data
package com.pixelized.rplexicon.repository.data.character
import com.pixelized.rplexicon.model.Skill
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.SkillParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
@ -28,7 +29,7 @@ class SkillRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchSkills() {
googleRepository.fetch { sheet ->
val request = sheet.get(Sheet.Character.ID, Sheet.Character.SKILL)
val request = sheet.get(CharacterBinder.ID, CharacterBinder.SKILL)
val skills = skillParser.parse(data = request.execute())
_skills.emit(skills)

View file

@ -1,8 +1,9 @@
package com.pixelized.rplexicon.repository.data
package com.pixelized.rplexicon.repository.data.character
import com.pixelized.rplexicon.model.AssignedSpell
import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.spell.AssignedSpellParser
import com.pixelized.rplexicon.repository.parser.spell.SpellBookParser
import com.pixelized.rplexicon.utilitary.Update
@ -39,8 +40,8 @@ class SpellRepository @Inject constructor(
suspend fun fetchSpells() {
googleRepository.fetch { sheet ->
val (lexicon, magic) = awaitAll(
async { sheet.get(Sheet.Character.ID, Sheet.Character.MAGIC_LEXICON).execute() },
async { sheet.get(Sheet.Character.ID, Sheet.Character.MAGIC).execute() },
async { sheet.get(CharacterBinder.ID, CharacterBinder.MAGIC_LEXICON).execute() },
async { sheet.get(CharacterBinder.ID, CharacterBinder.MAGIC).execute() },
)
val spellsBook = spellBookParser.parse(data = lexicon)
val assignedSpells = assignedSpellParser.parse(data = magic, spells = spellsBook)

View file

@ -1,8 +1,9 @@
package com.pixelized.rplexicon.repository.data
package com.pixelized.rplexicon.repository.data.lexicon
import com.pixelized.rplexicon.repository.parser.LexiconParser
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.LexiconBinder
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow
@ -28,7 +29,7 @@ class LexiconRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchLexicon() {
googleRepository.fetch { sheet ->
val request = sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.LEXICON)
val request = sheet.get(LexiconBinder.ID, LexiconBinder.LEXICON)
val data = lexiconParser.parse(data = request.execute())
_data.tryEmit(data)

View file

@ -1,10 +1,11 @@
package com.pixelized.rplexicon.repository.data
package com.pixelized.rplexicon.repository.data.lexicon
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.repository.parser.LocationParser
import com.pixelized.rplexicon.repository.parser.MarqueeParser
import com.pixelized.rplexicon.model.Location
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.LexiconBinder
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.async
@ -34,8 +35,8 @@ class LocationRepository @Inject constructor(
suspend fun fetchLocation() {
googleRepository.fetch { sheet ->
val (map, marquee) = awaitAll(
async { sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.MAP).execute() },
async { sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.MARQUEE).execute() },
async { sheet.get(LexiconBinder.ID, LexiconBinder.MAP).execute() },
async { sheet.get(LexiconBinder.ID, LexiconBinder.MARQUEE).execute() },
)
updateData(map = map, marquee = marquee)
lastSuccessFullUpdate = Update.currentTime()
@ -64,6 +65,6 @@ class LocationRepository @Inject constructor(
companion object {
const val SHEET_URL =
"https://docs.google.com/spreadsheets/d/${Sheet.Lexicon.ID}/edit#gid=1985553511"
"https://docs.google.com/spreadsheets/d/${LexiconBinder.ID}/edit#gid=1985553511"
}
}

View file

@ -1,9 +1,10 @@
package com.pixelized.rplexicon.repository.data
package com.pixelized.rplexicon.repository.data.lexicon
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.repository.parser.QuestParser
import com.pixelized.rplexicon.model.Quest
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.LexiconBinder
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow
@ -25,7 +26,7 @@ class QuestRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchQuests() {
googleRepository.fetch { sheet ->
val request = sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.QUEST_JOURNAL)
val request = sheet.get(LexiconBinder.ID, LexiconBinder.QUEST_JOURNAL)
val data = request.execute()
updateData(data = data)
lastSuccessFullUpdate = Update.currentTime()

View file

@ -0,0 +1,37 @@
package com.pixelized.rplexicon.repository.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
inline fun <reified T> parserScope(
noinline block: SheetParserScope<T>.() -> T
): T {
return SheetParserScope<T>().parse(block)
}
class SheetParserScope<T> {
private var structure: Map<String, Int> = hashMapOf()
fun updateStructure(row: Any, columns: List<String>) {
structure = row.checkSheetStructure(model = columns)
}
fun parse(block: SheetParserScope<T>.() -> T): T {
return this.block()
}
fun ValueRange.forEachRow(block: (index: Int, row: List<*>) -> Unit) {
val sheet = this.values.sheet()
sheet?.mapNotNull { it as? List<*> }?.forEachIndexed(block)
}
fun Any?.toItem(): String? =
this.toString().takeIf { it.isNotBlank() }
fun List<*>.parse(column: String): String? =
getOrNull(structure.getValue(column))?.toItem()
fun List<*>.parseInt(column: String): Int? =
parse(column)?.toIntOrNull()
}

View file

@ -0,0 +1,69 @@
package com.pixelized.rplexicon.repository.parser.inventory
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Equipment
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class EquipmentParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): Map<String, Equipment> = parserScope {
lateinit var characters: List<String>
val equipments = hashMapOf<String, Equipment.Builder>()
data.forEachRow { index, row ->
when (index) {
// ignore the first column
0 -> characters = row.drop(1).map { it.toString() }
else -> row.drop(1).forEachIndexed { column, item ->
val character = characters[column]
equipments.getEquipment(name = character).let {
when (row[0]) {
HEAD -> it.head = item.toItem()
FACE -> it.face = item.toItem()
NECK -> it.neck = item.toItem()
SHOULDER -> it.shoulder = item.toItem()
BODY -> it.body = item.toItem()
CHEST -> it.chest = item.toItem()
ARM -> it.arm = item.toItem()
HAND -> it.hand = item.toItem()
RING_1 -> it.ring1 = item.toItem()
RING_2 -> it.ring2 = item.toItem()
WAIST -> it.waist = item.toItem()
FOOT -> it.foot = item.toItem()
MAIN_HAND -> it.mainHand = item.toItem()
OFF_HAND -> it.offHand = item.toItem()
}
}
}
}
}
return@parserScope characters.associateWith {
equipments.getEquipment(name = it).build()
}
}
private fun HashMap<String, Equipment.Builder>.getEquipment(name: String) =
this.getOrPut(name) { Equipment.Builder() }
companion object {
private const val HEAD = "Tête"
private const val FACE = "Visage"
private const val NECK = "Gorge"
private const val SHOULDER = "Épaules"
private const val BODY = "Corp"
private const val CHEST = "Torse"
private const val ARM = "Bras"
private const val HAND = "Mains"
private const val RING_1 = "Anneau 1"
private const val RING_2 = "Anneau 2"
private const val WAIST = "Taille"
private const val FOOT = "Pieds"
private const val MAIN_HAND = "Main droite"
private const val OFF_HAND = "Main gauche"
}
}

View file

@ -0,0 +1,55 @@
package com.pixelized.rplexicon.repository.parser.inventory
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Inventory
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class InventoryParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): Map<String, Inventory> = parserScope {
val inventories = hashMapOf<String, Inventory.Builder>()
data.forEachRow { index, row ->
when {
index == 0 -> updateStructure(row = row, columns = COLUMNS)
row.isNotEmpty() -> {
val character = row[0]?.toItem()
val container = row.parse(column = CONTAINER)
val name = row.parse(column = NAME)
val amount = row.parse(column = AMOUNT)
if (character != null && name != null) {
// retrieve the character inventory
val inventory = inventories.getOrPut(character) { Inventory.Builder() }
// make a item builder
val item = Inventory.Item.Builder(
name = name,
amount = amount,
)
// add the item to its container or by default to the inventory.
val isAdded = container?.let {
inventory.find(container = it)?.items?.add(item)
} ?: false
if (isAdded.not()) {
inventory.items.add(item)
}
}
}
}
}
return@parserScope inventories.mapValues { entry -> entry.value.build() }
}
companion object {
private const val CONTAINER = "Contenant"
private const val NAME = "Name"
private const val AMOUNT = "Quantité"
private const val THROW = "Effet"
private val COLUMNS = listOf(CONTAINER, NAME, AMOUNT, THROW)
}
}

View file

@ -15,13 +15,13 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
import com.pixelized.rplexicon.R
@Composable
fun BackgroundImage(
modifier: Modifier = Modifier,
tint: Color = LocalContentColor.current,
background: Brush = rememberBackgroundGradient(),
painter: Painter,
) {
Box(
@ -36,7 +36,7 @@ fun BackgroundImage(
Box(
modifier = Modifier
.matchParentSize()
.background(brush = rememberBackgroundGradient())
.background(brush = background)
)
}
}
@ -52,6 +52,7 @@ fun BackgroundImage(
)
},
model: Any?,
background: Brush = rememberBackgroundGradient(),
) {
Box(
modifier = modifier
@ -66,19 +67,22 @@ fun BackgroundImage(
Box(
modifier = Modifier
.matchParentSize()
.background(brush = rememberBackgroundGradient())
.background(brush = background)
)
}
}
@Composable
private fun rememberBackgroundGradient(): Brush {
fun rememberBackgroundGradient(
from: Float = 0.5f,
to: Float = 1.0f,
): Brush {
val colorScheme = MaterialTheme.colorScheme
return remember {
Brush.verticalGradient(
colors = listOf(
colorScheme.surface.copy(alpha = 0.5f),
colorScheme.surface.copy(alpha = 1.0f),
colorScheme.surface.copy(alpha = from),
colorScheme.surface.copy(alpha = to),
)
)
}

View file

@ -1,129 +0,0 @@
package com.pixelized.rplexicon.ui.composable
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.ui.theme.LexiconTheme
@Composable
fun IndicatorStep(
modifier: Modifier = Modifier,
count: Int,
selectedIndex: Int?,
defaultSize: Dp = 4.dp,
selectedWidth: Dp = 16.dp,
indicatorSpacing: Dp = 4.dp,
) {
val colorScheme = MaterialTheme.colorScheme
val defaultColor = remember { colorScheme.onSurface.copy(alpha = 0.35f) }
val selectedColor = remember { colorScheme.primary }
Row(modifier = modifier) {
repeat(count) { index ->
val transition = rememberIndicatorTransition(
selected = selectedIndex == index,
defaultSize = defaultSize,
selectedWidth = selectedWidth,
defaultColor = defaultColor,
selectedColor = selectedColor,
)
Box(
modifier = Modifier
.size(size = transition.size)
.background(color = transition.color, shape = CircleShape)
)
if (index != count - 1) {
Spacer(modifier = Modifier.width(indicatorSpacing))
}
}
}
}
@Composable
private fun rememberIndicatorTransition(
selected: Boolean,
defaultSize: Dp,
selectedWidth: Dp,
defaultColor: Color,
selectedColor: Color,
): IndicatorStyle {
val transition = updateTransition(
label = "IndicatorStep selection transition",
targetState = selected
)
val color = transition.animateColor(
label = "IndicatorStep background animation",
targetValueByState = {
when (it) {
true -> selectedColor
else -> defaultColor
}
}
)
val width by transition.animateDp(
label = "IndicatorStep size animation",
targetValueByState = {
when (it) {
true -> selectedWidth
else -> defaultSize
}
}
)
return remember {
IndicatorStyle(
color = color,
size = derivedStateOf {
DpSize(width = width, height = defaultSize)
}
)
}
}
@Stable
class IndicatorStyle(
color: State<Color>,
size: State<DpSize>,
) {
val color: Color by color
val size: DpSize by size
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
fun IndicatorStepPreview() {
LexiconTheme {
Surface {
IndicatorStep(
modifier = Modifier.padding(all = 4.dp),
count = 3,
selectedIndex = 0,
)
}
}
}

View file

@ -5,7 +5,6 @@ package com.pixelized.rplexicon.ui.screens.character
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
@ -33,13 +32,17 @@ import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.Surface
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
@ -47,6 +50,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -57,7 +61,6 @@ import com.pixelized.rplexicon.LocalRollOverlay
import com.pixelized.rplexicon.LocalSnack
import com.pixelized.rplexicon.NO_WINDOW_INSETS
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.IndicatorStep
import com.pixelized.rplexicon.ui.composable.Loader
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.screens.character.composable.character.ProficiencyUio.ID.*
@ -71,6 +74,9 @@ import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationP
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationViewModel
import com.pixelized.rplexicon.ui.screens.character.pages.chooser.SpellLevelChooser
import com.pixelized.rplexicon.ui.screens.character.pages.chooser.SpellLevelChooserPreview
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryPage
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryPagePreview
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryViewModel
import com.pixelized.rplexicon.ui.screens.character.pages.proficiency.ProficiencyPage
import com.pixelized.rplexicon.ui.screens.character.pages.proficiency.ProficiencyPreview
import com.pixelized.rplexicon.ui.screens.character.pages.proficiency.ProficiencyViewModel
@ -84,6 +90,7 @@ import kotlinx.coroutines.launch
@Composable
fun CharacterSheetScreen(
viewModel: CharacterSheetViewModel = hiltViewModel(),
inventoryViewModel: InventoryViewModel = hiltViewModel(),
proficiencyViewModel: ProficiencyViewModel = hiltViewModel(),
attacksViewModel: AttacksViewModel = hiltViewModel(),
spellsViewModel: SpellsViewModel = hiltViewModel(),
@ -106,7 +113,8 @@ fun CharacterSheetScreen(
val haveAction = attacksViewModel.attacks.value.isNotEmpty()
val haveSpell = spellsViewModel.spells.value.isNotEmpty()
val haveAlteration = alterationsViewModel.alterations.value.isNotEmpty()
haveSheet.toInt() + (haveAction || haveSpell).toInt() + haveAlteration.toInt()
val haveInventory = true
haveSheet.toInt() + (haveAction || haveSpell).toInt() + haveAlteration.toInt() + haveInventory.toInt()
}
Surface(
modifier = Modifier.fillMaxSize(),
@ -126,14 +134,8 @@ fun CharacterSheetScreen(
onBack = {
screen.popBackStack()
},
indicator = {
IndicatorStep(
modifier = Modifier
.align(alignment = Alignment.BottomCenter)
.padding(all = 4.dp),
count = pagerState.pageCount,
selectedIndex = pagerState.currentPage,
)
onTab = {
scope.launch { pagerState.animateScrollToPage(it) }
},
loader = {
Loader(
@ -159,6 +161,11 @@ fun CharacterSheetScreen(
viewModel = alterationsViewModel,
)
},
inventory = {
InventoryPage(
viewModel = inventoryViewModel,
)
},
sheet = {
SpellLevelChooser(
modifier = Modifier.navigationBarsPadding(),
@ -205,11 +212,12 @@ private fun CharacterSheetContent(
onFullRefresh: () -> Unit,
name: String,
onBack: () -> Unit,
onTab: (Int) -> Unit,
loader: @Composable BoxScope.() -> Unit,
proficiencies: @Composable PagerScope.() -> Unit,
actions: @Composable PagerScope.() -> Unit,
alterations: @Composable PagerScope.() -> Unit,
indicator: @Composable BoxScope.() -> Unit,
inventory: @Composable PagerScope.() -> Unit,
sheet: @Composable () -> Unit,
) {
Scaffold(
@ -263,33 +271,34 @@ private fun CharacterSheetContent(
}
},
content = {
Column(
modifier = Modifier.navigationBarsPadding(),
horizontalAlignment = Alignment.CenterHorizontally,
Box(
modifier = Modifier
.fillMaxSize()
.navigationBarsPadding(),
) {
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
) {
Column {
PagerHeader(
pagerState = pagerState,
onTab = onTab,
)
HorizontalPager(
modifier = Modifier
.fillMaxWidth()
.pullRefresh(refreshState),
state = pagerState,
beyondBoundsPageCount = 2,
beyondBoundsPageCount = 0,
verticalAlignment = Alignment.Top,
pageContent = { page ->
when (page) {
0 -> proficiencies()
1 -> actions()
2 -> alterations()
0 -> inventory()
1 -> proficiencies()
2 -> actions()
3 -> alterations()
}
}
)
loader()
indicator()
}
loader()
}
}
)
@ -298,6 +307,45 @@ private fun CharacterSheetContent(
)
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun PagerHeader(
pagerState: PagerState,
tabs: List<String> = headers(),
onTab: (Int) -> Unit,
) {
ScrollableTabRow(
selectedTabIndex = pagerState.currentPage,
) {
tabs.forEachIndexed { index, tab ->
Tab(
selected = pagerState.currentPage == index,
selectedContentColor = MaterialTheme.colorScheme.onSurface,
unselectedContentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f),
onClick = { onTab(index) },
) {
Text(
modifier = Modifier.padding(all = 8.dp),
style = MaterialTheme.typography.labelSmall,
text = tab,
)
}
}
}
}
@Stable
@Composable
private fun headers(): List<String> {
val inventory = stringResource(id = R.string.character_sheet_tab_inventory)
val proficiency = stringResource(id = R.string.character_sheet_tab_proficiency)
val actions = stringResource(id = R.string.character_sheet_tab_actions)
val alteration = stringResource(id = R.string.character_sheet_tab_alteration)
return remember {
listOf(inventory, proficiency, actions, alteration)
}
}
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@ -320,21 +368,14 @@ private fun CharacterScreenPreview(
refreshState = rememberPullRefreshState(refreshing = false, onRefresh = { }),
name = "Brulkhai",
onBack = { },
onTab = { },
onRefresh = { },
onFullRefresh = { },
loader = { },
proficiencies = { ProficiencyPreview() },
actions = { ActionPagePreview() },
alterations = { AlterationPagePreview() },
indicator = {
IndicatorStep(
modifier = Modifier
.align(alignment = Alignment.BottomCenter)
.padding(all = 4.dp),
count = 3,
selectedIndex = 0,
)
},
inventory = { InventoryPagePreview() },
sheet = { SpellLevelChooserPreview() },
)
}

View file

@ -7,12 +7,14 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.repository.data.ActionRepository
import com.pixelized.rplexicon.repository.data.AlterationRepository
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.DescriptionRepository
import com.pixelized.rplexicon.repository.data.SkillRepository
import com.pixelized.rplexicon.repository.data.SpellRepository
import com.pixelized.rplexicon.repository.data.character.ActionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.EquipmentRepository
import com.pixelized.rplexicon.repository.data.character.InventoryRepository
import com.pixelized.rplexicon.repository.data.character.SkillRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import dagger.hilt.android.lifecycle.HiltViewModel
@ -29,6 +31,8 @@ class CharacterSheetViewModel @Inject constructor(
private val characterRepository: CharacterSheetRepository,
private val descriptionRepository: DescriptionRepository,
private val alterationRepository: AlterationRepository,
private val inventoryRepository: InventoryRepository,
private val equipmentRepository: EquipmentRepository,
private val actionRepository: ActionRepository,
private val spellRepository: SpellRepository,
private val skillRepository: SkillRepository,
@ -80,7 +84,29 @@ class CharacterSheetViewModel @Inject constructor(
Log.e(TAG, exception.message, exception)
}
}
awaitAll(characters, description)
val inventory = async {
try {
if (force || inventoryRepository.lastSuccessFullUpdate.shouldUpdate()) {
inventoryRepository.fetchInventory()
} else {
Unit
}
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
}
val equipment = async {
try {
if (force || equipmentRepository.lastSuccessFullUpdate.shouldUpdate()) {
equipmentRepository.fetchEquipment()
} else {
Unit
}
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
}
awaitAll(characters)
val alterations = async {
try {
if (force || alterationRepository.lastSuccessFullUpdate.shouldUpdate()) {
@ -95,7 +121,7 @@ class CharacterSheetViewModel @Inject constructor(
val actions = async {
try {
if (force || actionRepository.lastSuccessFullUpdate.shouldUpdate()) {
actionRepository.fetchActions()
actionRepository.fetchActions(characters = characterRepository.data.value)
} else {
Unit
}
@ -125,7 +151,7 @@ class CharacterSheetViewModel @Inject constructor(
Log.e(TAG, exception.message, exception)
}
}
awaitAll(alterations, actions, spells, skill)
awaitAll(description, alterations, inventory, equipment, actions, spells, skill)
_isLoading.value = false
}

View file

@ -1,74 +0,0 @@
package com.pixelized.rplexicon.ui.screens.character.composable.actions
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
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
import com.pixelized.rplexicon.utilitary.extentions.lexicon
@Composable
fun AttackHeader(
modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(horizontal = 16.dp),
) {
Column(
modifier = modifier,
) {
Surface(
modifier = Modifier.fillMaxWidth(),
) {
Text(
modifier = Modifier.padding(paddingValues = padding),
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(id = R.string.character_sheet_title_attacks).let {
AnnotatedString(
text = it,
spanStyles = listOf(
AnnotatedString.Range(
item = MaterialTheme.lexicon.typography.bodyDropCapSpan,
start = 0,
end = Integer.min(1, it.length),
)
)
)
},
)
}
Divider(
modifier = Modifier.padding(paddingValues = padding),
color = MaterialTheme.lexicon.colorScheme.placeholder,
)
}
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun AttackHeaderPreview() {
LexiconTheme {
Surface {
SkillHeader()
}
}
}

View file

@ -0,0 +1,286 @@
package com.pixelized.rplexicon.ui.screens.character.composable.actions
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.BackgroundImage
import com.pixelized.rplexicon.ui.composable.rememberBackgroundGradient
import com.pixelized.rplexicon.ui.theme.LexiconTheme
@Stable
data class EquipmentItemUio(
@DrawableRes val silhouette: Int,
val head: String? = null,
val face: String? = null,
val shoulder: String? = null,
val neck: String? = null,
val body: String? = null,
val chest: String? = null,
val arm: String? = null,
val waist: String? = null,
val hands: String? = null,
val ringRight: String? = null,
val foot: String? = null,
val ringLeft: String? = null,
val mainHand: String? = null,
val offHand: String? = null,
)
@Composable
fun EquipmentItem(
modifier: Modifier = Modifier,
equipments: EquipmentItemUio,
onClick: (String) -> Unit,
) {
Box(modifier = modifier) {
BackgroundImage(
modifier = Modifier.matchParentSize(),
background = rememberBackgroundGradient(from = 0.75f, to = 0.95f),
painter = painterResource(id = R.drawable.ic_woman_archer_256),
)
Column {
Row {
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Ltr,
icon = R.drawable.ic_visored_helm_24,
label = stringResource(id = R.string.character_sheet_equipment_head),
item = equipments.head,
onClick = onClick,
)
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Rtl,
icon = R.drawable.ic_double_face_mask_24,
label = stringResource(id = R.string.character_sheet_equipment_face),
item = equipments.face,
onClick = onClick,
)
}
Row {
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Ltr,
icon = R.drawable.ic_cape_24,
label = stringResource(id = R.string.character_sheet_equipment_shoulder),
item = equipments.shoulder,
onClick = onClick,
)
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Rtl,
icon = R.drawable.ic_gem_chain_24,
label = stringResource(id = R.string.character_sheet_equipment_neck),
item = equipments.neck,
onClick = onClick,
)
}
Row {
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Ltr,
icon = R.drawable.ic_lamellar_24,
label = stringResource(id = R.string.character_sheet_equipment_body),
item = equipments.body,
onClick = onClick,
)
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Rtl,
icon = R.drawable.ic_leather_armor_24,
label = stringResource(id = R.string.character_sheet_equipment_chest),
item = equipments.chest,
onClick = onClick,
)
}
Row {
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Ltr,
icon = R.drawable.ic_bracer_24,
label = stringResource(id = R.string.character_sheet_equipment_arm),
item = equipments.arm,
onClick = onClick,
)
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Rtl,
icon = R.drawable.ic_belt_armor_24,
label = stringResource(id = R.string.character_sheet_equipment_waist),
item = equipments.waist,
onClick = onClick,
)
}
Row {
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Ltr,
icon = R.drawable.ic_gauntlet_24,
label = stringResource(id = R.string.character_sheet_equipment_hands),
item = equipments.hands,
onClick = onClick,
)
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Rtl,
icon = R.drawable.ic_ring_1_24,
label = stringResource(id = R.string.character_sheet_equipment_ring_right),
item = equipments.ringRight,
onClick = onClick,
)
}
Row {
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Ltr,
icon = R.drawable.ic_boots_24,
label = stringResource(id = R.string.character_sheet_equipment_foot),
item = equipments.foot,
onClick = onClick,
)
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Rtl,
icon = R.drawable.ic_ring_2_24,
label = stringResource(id = R.string.character_sheet_equipment_ring_left),
item = equipments.ringLeft,
onClick = onClick,
)
}
Row {
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Ltr,
icon = R.drawable.ic_sword_24,
label = stringResource(id = R.string.character_sheet_equipment_main_hand),
item = equipments.mainHand,
onClick = onClick,
)
EquipmentItemContent(
modifier = Modifier.weight(weight = 1f),
direction = LayoutDirection.Rtl,
icon = R.drawable.ic_shield_24,
label = stringResource(id = R.string.character_sheet_equipment_off_hand),
item = equipments.offHand,
onClick = onClick,
)
}
}
}
}
@Composable
fun EquipmentItemContent(
modifier: Modifier = Modifier,
direction: LayoutDirection,
padding: PaddingValues = PaddingValues(start = 16.dp, top = 4.dp, bottom = 4.dp),
@DrawableRes icon: Int,
label: String,
item: String? = null,
onClick: (String) -> Unit,
) {
EquipmentItemLayout(
modifier = Modifier
.clickable(enabled = item != null) { item?.let(onClick) }
.padding(paddingValues = padding)
.then(other = modifier),
direction = direction,
icon = {
Icon(
modifier = Modifier.size(size = 32.dp),
painter = painterResource(id = icon),
contentDescription = null,
)
},
text = {
Column {
Text(
style = MaterialTheme.typography.labelSmall,
fontStyle = FontStyle.Italic,
fontWeight = FontWeight.Light,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = label,
)
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = item ?: "",
)
}
},
)
}
@Composable
private fun EquipmentItemLayout(
modifier: Modifier = Modifier,
direction: LayoutDirection,
icon: @Composable RowScope.() -> Unit,
text: @Composable RowScope.() -> Unit,
) {
CompositionLocalProvider(
LocalLayoutDirection provides direction,
) {
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
icon()
text()
}
}
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun InventoryItemPreview() {
LexiconTheme {
Surface {
EquipmentItem(
modifier = Modifier.fillMaxWidth(),
equipments = EquipmentItemUio(
silhouette = R.drawable.ic_woman_archer_256,
shoulder = "Cloak of Protection",
mainHand = "Battleaxe",
offHand = "Shield",
),
onClick = { },
)
}
}
}

View file

@ -2,6 +2,7 @@ package com.pixelized.rplexicon.ui.screens.character.composable.actions
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
@ -24,9 +25,10 @@ import com.pixelized.rplexicon.utilitary.extentions.lexicon
@Composable
fun AlterationHeader(
fun GenericHeader(
modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(horizontal = 16.dp),
@StringRes label: Int,
) {
Column(
modifier = modifier,
@ -40,7 +42,7 @@ fun AlterationHeader(
fontWeight = FontWeight.Bold,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(id = R.string.character_sheet_title_alteration).let {
text = stringResource(id = label).let {
AnnotatedString(
text = it,
spanStyles = listOf(
@ -65,10 +67,12 @@ fun AlterationHeader(
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun AlterationPreview() {
private fun GenericHeaderPreview() {
LexiconTheme {
Surface {
AlterationHeader()
GenericHeader(
label = R.string.character_sheet_title_inventory
)
}
}
}

View file

@ -0,0 +1,88 @@
package com.pixelized.rplexicon.ui.screens.character.composable.actions
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
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.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.ui.theme.LexiconTheme
@Stable
data class InventoryItemUio(
val name: String,
val amount: String? = null,
)
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun InventoryItem(
modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 2.dp),
item: InventoryItemUio,
) {
FlowRow(
modifier = Modifier
.padding(paddingValues = padding)
.then(other = modifier),
) {
Text(
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.bodyMedium,
text = item.name,
)
item.amount?.let {
Text(
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Medium,
style = MaterialTheme.typography.labelLarge,
text = " : ",
)
Text(
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Light,
style = MaterialTheme.typography.bodyMedium,
text = it
)
}
}
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun InventoryItemPreview(
@PreviewParameter(InventoryItemPreviewProvider::class) preview: InventoryItemUio,
) {
LexiconTheme {
Surface {
InventoryItem(item = preview)
}
}
}
private class InventoryItemPreviewProvider : PreviewParameterProvider<InventoryItemUio> {
override val values: Sequence<InventoryItemUio> = sequenceOf(
InventoryItemUio(
name = "- Cordes",
amount = "15 mètres",
),
InventoryItemUio(
name = "- Piège de chasse",
amount = null,
)
)
}

View file

@ -1,74 +0,0 @@
package com.pixelized.rplexicon.ui.screens.character.composable.actions
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
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
import com.pixelized.rplexicon.utilitary.extentions.lexicon
@Composable
fun SkillHeader(
modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(horizontal = 16.dp),
) {
Column(
modifier = modifier
) {
Surface(
modifier = Modifier.fillMaxWidth(),
) {
Text(
modifier = Modifier.padding(paddingValues = padding),
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(id = R.string.character_sheet_title_skills).let {
AnnotatedString(
text = it,
spanStyles = listOf(
AnnotatedString.Range(
item = MaterialTheme.lexicon.typography.bodyDropCapSpan,
start = 0,
end = Integer.min(1, it.length),
)
)
)
},
)
}
Divider(
modifier = Modifier.padding(paddingValues = padding),
color = MaterialTheme.lexicon.colorScheme.placeholder,
)
}
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun SkillHeaderPreview() {
LexiconTheme {
Surface {
SkillHeader()
}
}
}

View file

@ -0,0 +1,24 @@
package com.pixelized.rplexicon.ui.screens.character.composable.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
@Composable
@Stable
fun rememberEquipmentState(): State<EquipmentItemUio> {
return remember {
mutableStateOf(
EquipmentItemUio(
silhouette = R.drawable.ic_woman_archer_256,
shoulder = "Cloak of Protection",
mainHand = "Battleaxe",
offHand = "Shield",
)
)
}
}

View file

@ -0,0 +1,36 @@
package com.pixelized.rplexicon.ui.screens.character.composable.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
@Composable
@Stable
fun rememberInventoryListState(): State<List<InventoryItemUio>> {
return remember {
mutableStateOf(
listOf(
InventoryItemUio(name = "Bourse"),
InventoryItemUio(name = "- Or", amount = "21"),
InventoryItemUio(name = "Sac à dos"),
InventoryItemUio(name = "- Sac de couchage"),
InventoryItemUio(name = "- Kit de cuisine"),
InventoryItemUio(name = "- Boite d'allume-feu"),
InventoryItemUio(name = "- Torches", amount = "10"),
InventoryItemUio(name = "- Rations journalières", amount = "10"),
InventoryItemUio(name = "- Outre d'eau"),
InventoryItemUio(name = "- Cordes", amount = "15 mètres"),
InventoryItemUio(name = "- Piège de chasse"),
InventoryItemUio(name = "- Bâton de marche"),
InventoryItemUio(name = "Dague"),
InventoryItemUio(name = "Javelot", amount = "4"),
InventoryItemUio(name = "Cape de protection"),
)
)
}
}

View file

@ -3,7 +3,7 @@ package com.pixelized.rplexicon.ui.screens.character.factory
import com.pixelized.rplexicon.model.AssignedSpell
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.data.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellUio
import com.pixelized.rplexicon.utilitary.extentions.icon
import com.pixelized.rplexicon.utilitary.extentions.local.icon

View file

@ -26,15 +26,15 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.LocalRollOverlay
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.edit.HandleHitPointEditDialog
import com.pixelized.rplexicon.ui.composable.edit.HandleSkillEditDialog
import com.pixelized.rplexicon.ui.composable.edit.HandleSpellEditDialog
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.navigation.screens.navigateToSpellDetail
import com.pixelized.rplexicon.ui.screens.character.composable.actions.Attack
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AttackHeader
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AttackUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillHeader
import com.pixelized.rplexicon.ui.screens.character.composable.actions.GenericHeader
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.Spell
@ -179,7 +179,9 @@ fun ActionsPageContent(
) {
if (attacks.value.isNotEmpty()) {
stickyHeader {
AttackHeader()
GenericHeader(
label = R.string.character_sheet_title_attacks,
)
}
items(items = attacks.value) {
Attack(
@ -195,7 +197,9 @@ fun ActionsPageContent(
if (tokens.value.isNotEmpty()) {
stickyHeader {
SkillHeader()
GenericHeader(
label = R.string.character_sheet_title_skills,
)
}
items(items = tokens.value) {
SkillItem(
@ -234,24 +238,6 @@ fun ActionsPageContent(
}
}
@Composable
private fun HandleSkillDetailDialog(
dialog: State<SkillDetailUio?>,
onDismissRequest: () -> Unit,
) {
dialog.value?.let {
Dialog(
properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
onDismissRequest = onDismissRequest,
) {
SkillDetail(
detail = it,
onClose = onDismissRequest,
)
}
}
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)

View file

@ -9,9 +9,9 @@ import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.model.Attack
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.repository.data.ActionRepository
import com.pixelized.rplexicon.repository.data.AlterationRepository
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.ActionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AttackUio
import com.pixelized.rplexicon.ui.screens.character.factory.AttackUioFactory
@ -38,25 +38,22 @@ class AttacksViewModel @Inject constructor(
val attacks: State<List<AttackUio>> get() = _attacks
init {
viewModelScope.launch {
launch {
actionRepository.data
.combine(alterationRepository.assignedAlterations) { actions, _ -> actions }
.collect { sheets ->
_attacks.value = withContext(Dispatchers.Default) {
val alterations = alterationRepository.getActiveAlterationsStatus(
character = character,
)
sheets[character]?.map { action ->
attackFactory.convert(
characterSheet = model,
alterations = alterations,
attack = action,
)
} ?: emptyList()
}
}
}
viewModelScope.launch(Dispatchers.IO) {
actionRepository.data
.combine(alterationRepository.assignedAlterations) { actions, _ -> actions }
.collect { sheets ->
val alterations = alterationRepository.getActiveAlterationsStatus(
character = character,
)
val attacks = sheets[character]?.map { action ->
attackFactory.convert(
characterSheet = model,
alterations = alterations,
attack = action,
)
} ?: emptyList()
withContext(Dispatchers.Main) { _attacks.value = attacks }
}
}
}

View file

@ -9,8 +9,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.repository.data.AlterationRepository
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.ui.composable.edit.HpPointDialogUio
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio

View file

@ -21,6 +21,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.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -30,6 +31,8 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
import com.pixelized.rplexicon.utilitary.extentions.lexicon
@ -123,6 +126,24 @@ fun SkillDetail(
}
}
@Composable
fun HandleSkillDetailDialog(
dialog: State<SkillDetailUio?>,
onDismissRequest: () -> Unit,
) {
dialog.value?.let {
Dialog(
properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
onDismissRequest = onDismissRequest,
) {
SkillDetail(
detail = it,
onClose = onDismissRequest,
)
}
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)

View file

@ -12,9 +12,9 @@ import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.model.Skill
import com.pixelized.rplexicon.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.DescriptionRepository
import com.pixelized.rplexicon.repository.data.SkillRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.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

View file

@ -14,8 +14,8 @@ import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.model.Throw
import com.pixelized.rplexicon.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.SpellRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import com.pixelized.rplexicon.ui.composable.edit.SpellEditDialogUio
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellHeaderUio
@ -60,80 +60,77 @@ class SpellsViewModel @Inject constructor(
init {
// TODO rework that part. use factory
viewModelScope.launch {
launch(Dispatchers.IO) {
characterRepository.data
.combine(spellRepository.spells) { sheets, spells ->
character = sheets.getValue(characterName)
Struct(sheets = sheets, spells = spells)
}
.combine(firebaseRepository.getCharacter(character = characterName)) { struct, fire ->
characterFire = fire
struct.also { it.fire = fire }
}
.collect { data ->
val spells = data.spells[characterName]
val character = data.sheets.getValue(characterName)
_spells.value = withContext(Dispatchers.Default) {
if (character.isWarlock) {
spells
?.sortedBy { it.spell.name }
?.sortedBy { it.spell.level }
?.groupBy { it.spell.level == 0 }
?.map { entry ->
if (entry.key) {
SpellHeaderUio(
level = 0,
count = null,
)
} else {
val firstSpellSlot = character.firstSpellSlot()
SpellHeaderUio(
level = firstSpellSlot ?: 1,
count = character.spell(level = firstSpellSlot ?: 1)
?.let { max ->
SpellHeaderUio.Count(
value = data.fire.spell(
level = firstSpellSlot ?: 1
) ?: 0,
max = max
)
}
)
} to entry.value.map {
spellFactory.toUio(
assignedSpell = it,
characterSheet = character
)
}
}
?: emptyList()
} else {
spells
?.sortedBy { it.spell.name }
?.sortedBy { it.spell.level }
?.groupBy { it.spell.level }
?.map { entry ->
SpellHeaderUio(
level = entry.key,
count = character.spell(level = entry.key)?.let { max ->
viewModelScope.launch(Dispatchers.IO) {
characterRepository.data
.combine(spellRepository.spells) { sheets, spells ->
character = sheets.getValue(characterName)
Struct(sheets = sheets, spells = spells)
}
.combine(firebaseRepository.getCharacter(character = characterName)) { struct, fire ->
characterFire = fire
struct.also { it.fire = fire }
}
.collect { data ->
val spellsBook = data.spells[characterName]
val character = data.sheets.getValue(characterName)
val spells = if (character.isWarlock) {
spellsBook
?.sortedBy { it.spell.name }
?.sortedBy { it.spell.level }
?.groupBy { it.spell.level == 0 }
?.map { entry ->
if (entry.key) {
SpellHeaderUio(
level = 0,
count = null,
)
} else {
val firstSpellSlot = character.firstSpellSlot()
SpellHeaderUio(
level = firstSpellSlot ?: 1,
count = character.spell(level = firstSpellSlot ?: 1)
?.let { max ->
SpellHeaderUio.Count(
value = data.fire.spell(level = entry.key) ?: 0,
value = data.fire.spell(
level = firstSpellSlot ?: 1
) ?: 0,
max = max
)
},
) to entry.value.map {
spellFactory.toUio(
assignedSpell = it,
characterSheet = character
)
}
}
?: emptyList()
}
)
} to entry.value.map {
spellFactory.toUio(
assignedSpell = it,
characterSheet = character
)
}
}
}
?: emptyList()
} else {
spellsBook
?.sortedBy { it.spell.name }
?.sortedBy { it.spell.level }
?.groupBy { it.spell.level }
?.map { entry ->
SpellHeaderUio(
level = entry.key,
count = character.spell(level = entry.key)?.let { max ->
SpellHeaderUio.Count(
value = data.fire.spell(level = entry.key) ?: 0,
max = max
)
},
) to entry.value.map {
spellFactory.toUio(
assignedSpell = it,
characterSheet = character
)
}
}
?: emptyList()
}
}
withContext(Dispatchers.Main) { _spells.value = spells }
}
}
}

View file

@ -19,9 +19,10 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationHeader
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItem
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.GenericHeader
import com.pixelized.rplexicon.ui.screens.rolls.preview.rememberRollAlterations
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import kotlinx.coroutines.launch
@ -64,9 +65,6 @@ fun AlterationPageContent(
modifier = modifier,
contentPadding = PaddingValues(top = 8.dp, bottom = 16.dp),
) {
stickyHeader {
AlterationHeader()
}
items(items = alterations.value) {
AlterationItem(
modifier = Modifier.fillMaxWidth(),

View file

@ -7,8 +7,8 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.repository.data.AlterationRepository
import com.pixelized.rplexicon.repository.data.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItemUio
import com.pixelized.rplexicon.ui.screens.rolls.factory.AlterationFactory

View file

@ -0,0 +1,124 @@
package com.pixelized.rplexicon.ui.screens.character.pages.inventory
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.LocalSnack
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItem
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.GenericHeader
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItem
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberEquipmentState
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberInventoryListState
import com.pixelized.rplexicon.ui.screens.character.pages.actions.HandleSkillDetailDialog
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@Composable
fun InventoryPage(
viewModel: InventoryViewModel,
) {
val snack = LocalSnack.current
val snackJob = remember { mutableStateOf<Job?>(null) }
val scope = rememberCoroutineScope()
InventoryPageContent(
modifier = Modifier.fillMaxSize(),
equipments = viewModel.equipments,
inventory = viewModel.inventory,
onEquipment = {
snackJob.value?.cancel()
viewModel.showSkillDetailDialog(item = it)
},
)
HandleSkillDetailDialog(
dialog = viewModel.dialog,
onDismissRequest = viewModel::hideSkillDetailDialog,
)
LaunchedEffect(key1 = "InventorySnack") {
viewModel.snack.collect { message ->
if (message != null) {
snackJob.value?.cancel()
snackJob.value = scope.launch { snack.showSnackbar(message = message) }
}
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun InventoryPageContent(
modifier: Modifier = Modifier,
equipments: State<EquipmentItemUio?>,
inventory: State<List<InventoryItemUio>>,
onEquipment: (String) -> Unit,
) {
LazyColumn(
modifier = modifier,
contentPadding = PaddingValues(top = 8.dp, bottom = 16.dp),
) {
equipments.value?.let {
item {
EquipmentItem(
modifier = Modifier.padding(bottom = 16.dp),
equipments = it,
onClick = onEquipment,
)
}
}
if (inventory.value.isNotEmpty()) {
stickyHeader {
GenericHeader(
label = R.string.character_sheet_title_inventory
)
}
items(items = inventory.value) {
InventoryItem(
modifier = Modifier.padding(
top = when (it.name.contains("-")) {
true -> 0.dp
else -> 4.dp
}
),
item = it,
)
}
}
}
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
fun InventoryPagePreview() {
LexiconTheme {
Surface {
InventoryPageContent(
modifier = Modifier.fillMaxSize(),
equipments = rememberEquipmentState(),
inventory = rememberInventoryListState(),
onEquipment = { },
)
}
}
}

View file

@ -0,0 +1,113 @@
package com.pixelized.rplexicon.ui.screens.character.pages.inventory
import android.app.Application
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.EquipmentRepository
import com.pixelized.rplexicon.repository.data.character.InventoryRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
import com.pixelized.rplexicon.ui.screens.character.pages.actions.SkillDetailUio
import com.pixelized.rplexicon.utilitary.extentions.context
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
@HiltViewModel
class InventoryViewModel @Inject constructor(
private val inventoryRepository: InventoryRepository,
private val equipmentRepository: EquipmentRepository,
private val descriptionRepository: DescriptionRepository,
savedStateHandle: SavedStateHandle,
application: Application
) : AndroidViewModel(application) {
private val character = savedStateHandle.characterSheetArgument.name
private val _equipments = mutableStateOf<EquipmentItemUio?>(null)
val equipments: State<EquipmentItemUio?> get() = _equipments
private val _inventory = mutableStateOf<List<InventoryItemUio>>(emptyList())
val inventory: State<List<InventoryItemUio>> get() = _inventory
private val _dialog = mutableStateOf<SkillDetailUio?>(null)
val dialog: State<SkillDetailUio?> get() = _dialog
private val _snack = MutableSharedFlow<String?>()
val snack: SharedFlow<String?> get() = _snack
init {
viewModelScope.launch {
launch(Dispatchers.IO) {
inventoryRepository.data.collect {
val items = it[character]?.items?.map { item ->
InventoryItemUio(
name = item.name,
amount = item.amount,
)
} ?: emptyList()
withContext(Dispatchers.Main) {
_inventory.value = items
}
}
}
launch(Dispatchers.IO) {
equipmentRepository.data.collect {
val equipments = it[character]?.let { data ->
EquipmentItemUio(
silhouette = R.drawable.ic_woman_archer_256,
head = data.head,
face = data.face,
shoulder = data.shoulder,
neck = data.neck,
body = data.body,
chest = data.chest,
arm = data.arm,
waist = data.waist,
hands = data.hand,
ringRight = data.ring1,
foot = data.foot,
ringLeft = data.ring2,
mainHand = data.mainHand,
offHand = data.offHand,
)
}
withContext(Dispatchers.Main) {
_equipments.value = equipments
}
}
}
}
}
fun showSkillDetailDialog(item: String) {
val description = descriptionRepository.find(name = item)
if (description != null) {
_dialog.value = SkillDetailUio(
name = item,
original = description.original,
description = description.description
)
} else {
viewModelScope.launch {
_snack.emit(
context.getString(R.string.character_sheet_equipment_empty_description)
)
}
}
}
fun hideSkillDetailDialog() {
_dialog.value = null
}
}

View file

@ -7,8 +7,8 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.repository.data.AlterationRepository
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.character.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio
@ -34,21 +34,19 @@ class ProficiencyViewModel @Inject constructor(
val sheet: State<CharacterSheetUio?> get() = _sheet
init {
viewModelScope.launch {
launch {
characterRepository.data
.combine(alterationRepository.assignedAlterations) { sheets, _ -> sheets }
.collect {
_sheet.value = withContext(Dispatchers.Default) {
val alterations =
alterationRepository.getActiveAlterationsStatus(character)
characterSheetFactory.convert(
sheet = it.getValue(key = character),
alterations = alterations,
)
}
viewModelScope.launch(Dispatchers.IO) {
characterRepository.data
.combine(alterationRepository.assignedAlterations) { sheets, _ -> sheets }
.collect {
val alterations = alterationRepository.getActiveAlterationsStatus(character)
val sheet = characterSheetFactory.convert(
sheet = it.getValue(key = character),
alterations = alterations,
)
withContext(Dispatchers.Main) {
_sheet.value = sheet
}
}
}
}
}

View file

@ -4,8 +4,8 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.LexiconRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.lexicon.LexiconRepository
import com.pixelized.rplexicon.ui.navigation.screens.lexiconDetailArgument
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

View file

@ -7,8 +7,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.LexiconRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.lexicon.LexiconRepository
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import dagger.hilt.android.lifecycle.HiltViewModel

View file

@ -7,7 +7,7 @@ import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.model.Lexicon.Gender
import com.pixelized.rplexicon.model.Lexicon.Race
import com.pixelized.rplexicon.repository.data.LexiconRepository
import com.pixelized.rplexicon.repository.data.lexicon.LexiconRepository
import com.pixelized.rplexicon.ui.composable.form.DropDownFieldUio
import com.pixelized.rplexicon.ui.composable.form.TextFieldUio
import com.pixelized.rplexicon.utilitary.composable.stringResource

View file

@ -9,7 +9,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.geometry.Offset
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import com.pixelized.rplexicon.repository.data.LocationRepository
import com.pixelized.rplexicon.repository.data.lexicon.LocationRepository
import com.pixelized.rplexicon.ui.navigation.screens.locationDetailArgument
import com.pixelized.rplexicon.utilitary.cells
import com.pixelized.rplexicon.utilitary.line

View file

@ -5,7 +5,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.repository.data.LocationRepository
import com.pixelized.rplexicon.repository.data.lexicon.LocationRepository
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import dagger.hilt.android.lifecycle.HiltViewModel

View file

@ -4,9 +4,9 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import com.pixelized.rplexicon.repository.data.LexiconRepository
import com.pixelized.rplexicon.repository.data.LocationRepository
import com.pixelized.rplexicon.repository.data.QuestRepository
import com.pixelized.rplexicon.repository.data.lexicon.LexiconRepository
import com.pixelized.rplexicon.repository.data.lexicon.LocationRepository
import com.pixelized.rplexicon.repository.data.lexicon.QuestRepository
import com.pixelized.rplexicon.ui.navigation.screens.questDetailArgument
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

View file

@ -5,7 +5,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.repository.data.QuestRepository
import com.pixelized.rplexicon.repository.data.lexicon.QuestRepository
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import dagger.hilt.android.lifecycle.HiltViewModel

View file

@ -8,8 +8,8 @@ import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.business.DiceThrowUseCase
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.repository.data.AlterationRepository
import com.pixelized.rplexicon.repository.data.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationDetailUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItemUio
import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio

View file

@ -3,11 +3,11 @@ package com.pixelized.rplexicon.ui.screens.rolls.factory
import com.pixelized.rplexicon.model.Alteration
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.data.ActionRepository
import com.pixelized.rplexicon.repository.data.AlterationRepository
import com.pixelized.rplexicon.repository.data.DescriptionRepository
import com.pixelized.rplexicon.repository.data.SkillRepository
import com.pixelized.rplexicon.repository.data.SpellRepository
import com.pixelized.rplexicon.repository.data.character.ActionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.SkillRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItemUio
import javax.inject.Inject

View file

@ -2,9 +2,9 @@ package com.pixelized.rplexicon.ui.screens.rolls.factory
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.repository.data.ActionRepository
import com.pixelized.rplexicon.repository.data.SkillRepository
import com.pixelized.rplexicon.repository.data.SpellRepository
import com.pixelized.rplexicon.repository.data.character.ActionRepository
import com.pixelized.rplexicon.repository.data.character.SkillRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio
import com.pixelized.rplexicon.utilitary.extentions.icon
import javax.inject.Inject

View file

@ -5,8 +5,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.repository.data.DescriptionRepository
import com.pixelized.rplexicon.repository.data.SpellRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
import com.pixelized.rplexicon.ui.navigation.screens.spellDetailArgument
import com.pixelized.rplexicon.utilitary.extentions.local.icon

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#000000"
android:pathData="M41.64,149.08c-8,8 -12.61,12.19 -15.69,18.34a24.99,24.99 0,0 0,-0.76 1.69c2.52,3.11 8.07,7.31 15.91,11.32 0.12,-2.59 0.39,-3.83 0.95,-4.96 0.93,-1.85 4.31,-5.66 12.31,-13.66zM470.36,149.08l-12.73,12.73c8,8 11.39,11.81 12.31,13.66 0.56,1.13 0.83,2.37 0.95,4.96 7.85,-4.01 13.39,-8.21 15.91,-11.32a24.99,24.99 0,0 0,-0.76 -1.69c-3.07,-6.15 -7.69,-10.34 -15.69,-18.34zM25,192.24v40.46c2.57,3.83 8.59,8.77 17.59,13.35 -0.09,-7.82 -0.05,-16.63 0.42,-25.1 0.37,-6.69 0.97,-13.16 2.11,-19.07 -0.93,-0.36 -1.86,-0.73 -2.77,-1.1 -6.41,-2.61 -12.16,-5.42 -17.36,-8.55zM487,192.24c-5.2,3.13 -10.95,5.94 -17.36,8.55 -0.91,0.37 -1.83,0.74 -2.77,1.1 1.14,5.91 1.74,12.38 2.11,19.07 0.47,8.48 0.51,17.28 0.42,25.1 9.01,-4.58 15.02,-9.52 17.59,-13.35zM199.05,196.45l-28.78,116.92L247,362.92v-53.04l32,-16v-36.88l-32,-16v-44.56zM265,196.45v33.44l32,16v59.13l-32,16v41.91l76.73,-49.56 -28.78,-116.92zM62.41,207.77c-0.65,4.18 -1.14,9.07 -1.42,14.18 -0.6,10.81 -0.44,22.19 -0.24,31.54 10.02,3.45 21.35,6.54 33.21,9.18 3.02,0.67 6.08,1.3 9.14,1.92 0.17,-11.67 0.63,-24.21 1.96,-36.13 0.38,-3.44 0.81,-6.83 1.31,-10.15a577.92,577.92 0,0 1,-10.22 -2.05c-11.62,-2.45 -23.06,-5.26 -33.74,-8.49zM449.59,207.77c-10.68,3.22 -22.12,6.04 -33.74,8.49a577.93,577.93 0,0 1,-10.22 2.05c0.51,3.32 0.93,6.72 1.31,10.15 1.33,11.92 1.79,24.46 1.96,36.13 3.06,-0.61 6.12,-1.24 9.14,-1.92 11.85,-2.63 23.19,-5.73 33.21,-9.18 0.2,-9.35 0.36,-20.73 -0.24,-31.54 -0.28,-5.11 -0.77,-10 -1.42,-14.18zM124.23,221.46a143.35,143.35 0,0 0,-1.29 8.98c-1.33,11.97 -1.74,25.1 -1.88,37.37a552.7,552.7 0,0 0,40.65 5l11.18,-45.42c-13.4,-1.03 -30.39,-2.98 -48.67,-5.94zM387.77,221.46c-18.28,2.96 -35.26,4.91 -48.67,5.94l11.18,45.42a552.7,552.7 0,0 0,40.65 -5c-0.13,-12.27 -0.55,-25.4 -1.88,-37.37 -0.34,-3.1 -0.78,-6.1 -1.29,-8.98zM27.09,258.22l1.45,24.82c34.69,13.85 77.37,23.68 123.4,29.51l5.4,-21.92c-20.85,-1.92 -44.71,-5.38 -67.28,-10.4 -18.38,-4.08 -35.84,-9.18 -50.2,-15.56 -4.55,-2.02 -8.84,-4.14 -12.76,-6.45zM484.91,258.22c-3.92,2.31 -8.21,4.43 -12.76,6.45 -14.37,6.39 -31.82,11.48 -50.2,15.56 -22.58,5.02 -46.43,8.47 -67.28,10.4l5.4,21.92c46.03,-5.83 88.71,-15.67 123.4,-29.51z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#000000"
android:pathData="M334.5,85.22c-31.4,0.52 -69.44,13.37 -100.56,42.81l17.59,215.41 -6.47,77.66h58.13l15.28,-37.72 21.12,37.72h151.53c7.9,-58.59 -44.23,-130.17 -74.53,-130.38l-16.53,1.12c-36.1,7.26 -49.65,27.03 -66.31,49.78l-4.28,5.84 -6.72,-2.69 -13.03,-5.25 -14.28,36.6 -17.41,-6.78 14.38,-36.78 -11.91,-4.78 -8.56,-3.44 3.31,-8.59c7.98,-20.73 20.74,-40.35 38.91,-54.16 14.91,-11.33 33.59,-18.48 55.16,-18.69 1.44,-0.01 2.88,0.01 4.34,0.06 1.92,0.06 3.86,0.17 5.81,0.34l2.5,-18.16c-26.08,-16.1 -67.53,-14.87 -103.72,-0.72l-6.78,-17.41c20.11,-7.86 41.78,-12.5 62.65,-12.56 17.98,-0.05 35.36,3.31 50.56,10.87l5.59,-40.66c-31.29,-27.42 -77.42,-24.61 -119.37,-0.59l-9.28,-16.22c23.57,-13.49 49.49,-21.26 74.31,-21.31 20.46,-0.04 40.18,5.18 57.28,16.78l2.19,-15.84c0.35,-1.53 0.61,-3.01 0.78,-4.47 0,-0.02 0,-0.04 0,-0.06 0.36,-10.55 -4.07,-18.36 -12.94,-25 -9.48,-7.1 -24.38,-11.94 -42.03,-12.66 -2.21,-0.09 -4.44,-0.13 -6.72,-0.09zM140.09,162.53c-0.84,0.01 -1.68,0.03 -2.5,0.06 -13.16,0.54 -24.16,4.18 -30.94,9.25 -6.78,5.07 -9.7,10.64 -8.94,18.84l-0.6,0.06c0.13,1.23 0.32,2.51 0.63,3.81l2.31,16.72c10.81,-7.49 23.1,-11.16 35.53,-11.09 17.79,0.1 35.61,7.28 51.78,19.25l-11.09,15c-13.73,-10.16 -28.06,-15.49 -40.81,-15.56 -11.99,-0.07 -22.78,4.04 -32.41,14.34l4.41,31.84c8.97,-4.18 18.79,-6.08 28.84,-6.06 14.79,0.02 30.09,4.16 44,11.19l-8.44,16.69c-21.8,-11.02 -45.49,-12.51 -61.69,-2l2.03,14.72c0.84,-0.03 1.68,-0.06 2.5,-0.06 14.63,-0.1 27.16,4.06 37.34,11.25 14.48,10.23 24.23,25.51 32.63,41.6l4.28,8.25 -8.22,4.34 -8.47,4.47L186,394.88l-16.44,8.88 -13.81,-25.59 -9.16,4.84 -6.34,3.34 -4.88,-5.25c-16.32,-17.62 -29.14,-32.61 -60.41,-35.19l-9,-0.75c-32.09,15.31 -47.56,38.28 -42.44,76.25h190.44l-5.09,-59.69 -0.03,-0.06 13.59,-162.63c-24.97,-26.09 -56.98,-36.8 -82.34,-36.5zM369.44,261.56c-17.59,0.08 -31.93,5.76 -43.97,14.91 -12.36,9.39 -22.03,22.78 -29,37.59l26.66,10.72c14.06,-18.9 30.82,-38.79 61.81,-48.34l-8.03,-14.47c-1.3,-0.1 -2.64,-0.29 -3.91,-0.34 -1.2,-0.05 -2.39,-0.07 -3.56,-0.06zM116.59,318c-4.29,-0.03 -9.04,0.54 -14.28,1.63l-9,10.66c23.39,6.11 38.25,19.73 50.75,32.94l10.65,-5.63 8.84,-4.69c-6.63,-11.38 -13.85,-20.92 -22.28,-26.88 -6.49,-4.59 -13.69,-7.53 -22.87,-7.97 -0.59,-0.03 -1.2,-0.06 -1.81,-0.06z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#000000"
android:pathData="M412.6,27.25c-62.48,12.86 -108.97,44.93 -148.62,82.2 -8.49,8.78 -16.66,17.99 -24.54,27.56 4.9,1.95 9.43,4.11 13.3,7.09 0.31,-0.4 0.7,-0.9 1,-1.29 6.28,-7.98 10.46,-13.28 13.18,-16.12l12.99,12.46c-0.53,0.55 -5.83,6.92 -12.02,14.8 -6.2,7.88 -13.92,17.86 -21.53,27.89 -7.61,10.02 -15.11,20.09 -20.86,28.09 -2.87,4 -5.31,7.49 -7.06,10.13 -1.75,2.64 -2.99,5.18 -2.58,4.13l-16.79,-6.48c1.2,-3.11 2.41,-4.65 4.38,-7.6 1.08,-1.63 2.37,-3.5 3.77,-5.51 -3.99,-2.19 -8.23,-4.21 -12.56,-5.67 -11.85,18.36 -23.12,37.35 -34.03,56.61 4.74,0.9 9.39,2.15 13.65,4.09 0.55,0.25 1.1,0.51 1.64,0.79 2.28,-3.82 4.35,-7.24 6.07,-9.95 1.92,-3.02 3.05,-4.9 4.93,-6.99l13.37,12.05c0.3,-0.34 -1.36,1.84 -3.11,4.6 -1.76,2.76 -4,6.46 -6.54,10.74 -3.15,5.32 -6.77,11.6 -10.52,18.2 -0.92,1.64 -1.87,3.32 -2.92,5.16 -1.33,2.37 -2.67,4.76 -4.01,7.16 -6.11,10.99 -12.1,22.04 -16.63,30.8 -2.27,4.38 -4.18,8.19 -5.54,11.06 -1.36,2.87 -2.23,5.55 -1.99,4.53l-17.52,-4.13c0.75,-3.17 1.72,-4.89 3.24,-8.09 1.01,-2.13 2.29,-4.69 3.68,-7.43 -0.18,-0.09 -0.34,-0.19 -0.56,-0.29 -1.4,-0.65 -3.91,-1.24 -7.11,-1.46 -2.58,-0.17 -5.55,-0.11 -8.71,0.02 -6.96,13.05 -13.88,26.05 -20.81,38.85 24.22,-3.73 49.8,0.88 72.72,11.27 25.21,11.42 47.41,29.84 61.18,52.78 9.69,-11.39 19.13,-22.82 28.35,-34.29 -6.03,-7.05 -12.93,-13.5 -20.57,-19.51 -3.78,5.39 -12.22,17.41 -12.22,17.41l-14.74,-10.33s15.81,-22.57 31.83,-45.36c8.01,-11.4 16.08,-22.84 22.27,-31.57 3.09,-4.36 5.71,-8.04 7.63,-10.71 0.96,-1.34 1.74,-2.41 2.34,-3.23 0.3,-0.41 0.55,-0.74 0.79,-1.05 0.23,-0.3 0.03,-0.22 1.11,-1.29l12.73,12.73c0.31,-0.31 0.31,-0.32 0.4,-0.41 -0.11,0.14 -0.29,0.38 -0.54,0.71 -0.52,0.7 -1.27,1.74 -2.21,3.04 -1.87,2.6 -4.48,6.26 -7.56,10.61 -0.47,0.66 -1.2,1.71 -1.69,2.4 8.65,6.09 16.44,13.45 22.96,21.5 14.67,-19.69 28.89,-39.48 42.85,-59.32 -6.27,-7.52 -13.51,-14.38 -21.59,-20.72 -3.78,5.39 -12.22,17.41 -12.22,17.41l-14.74,-10.33s15.81,-22.57 31.83,-45.36c8.02,-11.4 16.08,-22.85 22.27,-31.57 3.09,-4.36 5.71,-8.04 7.63,-10.71 0.96,-1.34 1.74,-2.41 2.34,-3.22 0.3,-0.41 0.55,-0.74 0.79,-1.05 0.23,-0.3 0.03,-0.22 1.11,-1.29l12.73,12.73c0.31,-0.31 0.31,-0.32 0.39,-0.41 -0.11,0.15 -0.29,0.38 -0.54,0.71 -0.52,0.7 -1.27,1.74 -2.21,3.05 -1.87,2.6 -4.48,6.26 -7.56,10.61 -0.47,0.66 -1.2,1.71 -1.69,2.4 8.05,5.67 15.35,12.43 21.58,19.83 14.8,-21.63 29.52,-43.29 44.39,-64.95 -8.16,-14.73 -13.61,-35.99 -17.27,-59.9 -3.51,-22.94 -4.82,-47.86 -3.48,-70.62zM387.67,51.23l0.03,12.43c0.07,26.33 0.24,57.67 8.26,81.71l4.05,12.13 -12.78,-0.28c-21.22,-0.47 -41.94,-6.48 -58.82,-14.83 -16.87,-8.35 -30.14,-18.33 -35.92,-30.99l-2.78,-6.09 5.03,-4.41c21.07,-18.48 51.31,-35.89 81.13,-45.77zM369.78,76.59c-20.67,8.33 -41.31,20.34 -56.95,32.72 4.76,5.22 13.01,11.74 23.55,16.95 11.33,5.61 25.01,9.99 38.96,11.9 -4.7,-20.45 -5.43,-42.06 -5.56,-61.58zM214.43,149.06l-19.29,31.21c8.46,2.03 15.83,5.64 22.02,9.22 2.87,-4.03 5.66,-7.92 11.22,-15.91 3.97,-5.7 7.91,-11.44 10.74,-15.73 0.11,-0.17 0.14,-0.23 0.25,-0.39 -2.9,-1.76 -6.68,-3.83 -10.64,-5.29 -5.8,-2.14 -11.27,-2.86 -14.29,-3.11zM358.01,218.45L338.99,246.3c14.88,11.8 27.54,25.66 36.98,42.08l15.57,-23.53c-4,-15.06 -17.05,-34.1 -33.53,-46.39zM143.73,271.91c-2.03,-0.04 -2.87,0.12 -4.33,0.2l-17.69,36.36c3.37,-0.13 6.78,-0.28 10.25,-0.04 4.49,0.3 9.07,1.03 13.52,3.11 0.24,0.11 0.46,0.28 0.7,0.4 2.86,-4.89 4.65,-7.95 9.27,-15.96 3.72,-6.45 7.43,-12.93 10.15,-17.8 0.5,-0.91 0.75,-1.39 1.19,-2.17 -2.53,-1.15 -6.38,-2.25 -10.49,-2.98 -4.13,-0.73 -8.7,-1.05 -12.56,-1.12zM273.66,334.03l-19.02,27.85c14.88,11.8 27.54,25.66 36.98,42.08l15.58,-23.54c-4,-15.06 -17.05,-34.1 -33.54,-46.39zM96.63,384.43c-3.35,0.81 -6.63,1.83 -9.82,3.06l-8.15,3.15c1.37,45.12 28.47,76.88 60.95,88.8 30.69,11.27 66.89,4.88 89.32,-21.72l-3.8,-8.79c-1.42,-3.28 -3.08,-6.5 -4.94,-9.65 -16.81,27.02 -47.22,33.24 -74.36,23.27 -27.3,-10.02 -50.38,-36.35 -49.18,-78.12z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#000000"
android:pathData="M257.1,18.46c-17,19.58 -32.7,35.31 -55.1,42.98 41.5,68.46 139.9,119.76 241.2,62.36 18,-14.1 26.7,-31.45 34.9,-47.34 -98.9,-5.45 -164.8,-19.81 -221,-58zM225,111.1c-18.9,38.3 -41,72.2 -65.1,100.2 -40.8,47.5 -87.03,78.7 -132.67,85.3 6.47,19.8 10.43,59.2 25.84,72.6 45.63,18.5 132.83,-9.1 164.63,-38.7 16.1,-16.4 24,-36.6 34.2,-60.9 -2,35.2 -13.3,56.6 -27.7,72.4 -18.5,18.2 -36.6,30.8 -59,37.8 11.9,22.3 16.8,49.7 27.7,67.8 4.4,6.7 8,9.5 14.6,9.4 42,-10.9 74.4,-45.9 110.9,-60.5 55.3,-29.3 65.3,-74 67,-85.5 -1.1,28.7 -12.7,67.5 -31.7,83.6 33.8,12.4 47.5,67.3 52.3,90.2 15.2,-14 33.2,-35.4 48.1,-60.1C473,393.5 487,357.2 487,324.2c-0.3,-38.8 -17,-76.4 -26.5,-118.2 -5,-21.7 -7.7,-44.7 -4.6,-69.3 -131.7,55.7 -190.9,9.4 -230.9,-25.6z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#000000"
android:pathData="M276.44,20.63v473.23c43.08,-1.26 82.45,-26.44 111.83,-68.04 30.15,-42.69 49.18,-102.42 49.18,-168.57 0,-66.15 -19.03,-125.88 -49.18,-168.57C358.89,47.07 319.52,21.89 276.44,20.63zM156.07,227.19c-10.05,0.02 -20.72,0.97 -31.93,2.67 -0.86,98.74 54.76,57.68 122.39,65.07 -14.89,-50.39 -46.9,-67.83 -90.46,-67.75zM387.53,227.19c10.05,0.02 20.72,0.97 31.93,2.67 0.86,98.74 -54.77,57.68 -122.39,65.07 14.89,-50.39 46.9,-67.83 90.46,-67.75z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M123.15,24.6c-11.35,0.76 -48.79,83 -63.54,132.17 -8.05,26.82 2.98,74.73 41.72,106.45 8.81,-1.5 16.95,-3.05 24.43,-4.63 -22.47,-24.22 -39.05,-50.49 -47.77,-82.06l-1.02,-3.7 1.96,-3.3c26.14,-43.92 37.68,-68.55 50.85,-112.24l3.85,-12.77 10.4,8.35c14.62,11.74 23.72,18.08 32.1,21.81 -14.43,-22.99 -31.84,-41.36 -52.46,-50.06a2.16,2.16 0,0 0,-0.52 -0.03zM142.94,74.81c-11.72,36.18 -24.14,62.49 -46.51,100.38 9,29.98 25.75,54.62 49.73,78.65 18.74,-4.86 32.59,-9.93 43.38,-14.98 16.88,-7.89 26.51,-15.73 36.92,-23.7 -6.53,-34.91 -18.94,-80.14 -38.02,-118.38 -14.75,-2.77 -27.2,-8.37 -45.51,-21.98zM233.04,232.81c-9.16,7.02 -19.8,14.83 -35.86,22.35 -17.36,8.12 -40.95,15.89 -76.29,22.79 35.35,28.76 64.9,62.21 112.64,82.16 2.79,-15.61 10.51,-29.53 20.61,-39.78 9.55,-9.69 21.61,-16.38 34.25,-16.82 2.2,-5.2 5.38,-10.56 10.59,-14.93 6.41,-5.37 15.63,-8.32 26.93,-8.16a35.05,35.05 0,0 1,4.81 -5.42c-7.38,-1.6 -16.19,-3.17 -27.15,-5.59zM353.31,283.11c-0.04,0.01 -0.08,0.01 -0.13,0.02 -11.26,3.73 -12.51,6.78 -16.95,16.08 -8.48,-1 -19.25,-2.08 -25.69,3.16 -6.02,6.31 -7.28,12.7 -9.69,20.72 -14.01,-3.28 -25.77,1.84 -33.9,9.88 -8.03,8.15 -14.35,20.02 -16.08,32.65 5.93,2.63 12.11,4.21 18.14,5.73 12.69,-15.5 32.27,-33.51 50.66,-36.85 12.99,-13.44 28.67,-18.08 41.18,-19.89a60.1,60.1 0,0 1,11.76 -8.4c-6.63,-11.79 -12.4,-18.62 -19.31,-23.09zM121.3,301.99c-1.97,12.93 -8,24.57 -0.64,46.31 32.36,70.65 41.04,23.73 93.74,22.95 -38.94,-18.94 -66.13,-45.74 -93.1,-69.26zM389.07,319.1c-4.87,0.97 -9.34,3.54 -13.06,6.28 9.2,12.25 16.99,27.31 23.28,43.81 7.92,-2.05 14.75,-4.61 21.1,-7.51 -6.47,-26.76 -18.95,-37.98 -31.32,-42.58zM359.13,333c-7.68,1.51 -15.59,4.36 -22.77,10.35 8.37,8.84 20.98,24.77 26.05,43.28 7.63,-1.27 15.91,-3.7 21.01,-8.58 -6.61,-17.86 -15.06,-33.73 -24.28,-45.06zM320.99,352.67c-14.31,5.36 -25.26,14.98 -34.67,25.94l19.38,26.32c13.7,-1.49 26.64,-6.25 39.39,-13.3 -4.69,-15.1 -18.62,-32.91 -24.45,-38.61zM429.84,377.03c-6.68,3.1 -13.97,5.99 -22.24,8.38 6.32,10.32 11.62,21.14 15.6,32.54 7.7,-3.07 14.9,-6.37 21.26,-10.08 -3.96,-11.28 -8.68,-22.06 -14.63,-30.84zM164.69,408.39l49.64,43.38c11.27,-13.52 27.04,-23.94 42.49,-33.94l-22.02,-25.32c-24.33,-8.01 -52.76,2.36 -70.11,15.89zM391.9,394.15c-6.87,5.06 -14.14,7.73 -21.8,9.38 5.37,9.31 10.38,19.5 14.35,29.75 7.95,-2.14 15.45,-4.43 22.34,-7.78 -3.65,-10.9 -8.68,-21.3 -14.9,-31.36zM352.02,408.26c-11.76,6.31 -24.19,11.16 -37.42,13.55l13.32,27.45c14.66,-2.18 28.1,-3.68 39.67,-9.75 -4.2,-10.71 -9.78,-21.71 -15.56,-31.25zM450.04,425.41c-6.63,3.6 -13.65,6.7 -20.85,9.53 2.83,7.17 5.28,14.12 7.21,20.76 10.4,-1.43 20.26,-5.91 18.18,-13.87 -1.42,-5.3 -2.92,-10.83 -4.54,-16.42zM268.82,431.54c-16.06,10.56 -31.09,20.98 -40.73,32.26l1.39,1.22c16.65,14.55 36.26,20.48 44.95,9.13 8.52,-11.12 10.54,-24.12 -5.61,-42.6zM412.83,442.5c-7.17,3.33 -14.4,5.63 -21.47,7.58 3.05,6.99 5.84,13.85 8.24,20.48 9.2,-0.46 17.73,-4.03 19.33,-10.44 -1.62,-5.49 -3.69,-11.41 -6.11,-17.62zM374.39,456.19c-12.72,6.4 -25.87,8.47 -38.18,10.15l6,12.37c10.04,10.7 32.66,11.34 42.21,2.94 -2.65,-7.91 -6.1,-16.52 -10.03,-25.47z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M144.2,19.03c-10.7,0 -20.3,5.36 -26.1,13.52 -14.1,-1.15 -28.27,-0.24 -41.2,3.44 -5.9,-6.63 -14.5,-10.81 -24,-10.81 -17.6,0 -32.1,14.49 -32.1,32.12 0,9.8 4.5,18.62 11.5,24.53 -2,10.17 -1.9,19.47 -0.2,27.97 -8.1,5.8 -13.3,15.3 -13.3,25.9 0,17.6 14.5,32.1 32.1,32.1 8.1,0 15.6,-3 21.2,-8.1 4.7,3.1 9.4,6 14.3,8.9 -0.3,1.5 -0.4,3.1 -0.4,4.7 0,17.6 14.5,32.1 32.1,32.1 5.7,0 11.1,-1.5 15.7,-4.2 3.7,3.5 7,7.2 10,11.1 -5.7,5.8 -9.2,13.7 -9.2,22.4 0,15.2 10.8,28 25,31.3 -0.3,7 -1.2,14.6 -2.9,22.9 -7.7,7.6 -12.5,18.2 -12.5,29.8 0,11.4 4.6,21.8 12,29.4l-13.4,113.3 24.7,-36.3c3.4,-28.2 6.8,-57 9.6,-81.2 11.9,-5.4 21.6,-10 32.4,-14.8 27.1,11.4 51.2,22.2 75.2,32.3l41.2,-2 -97.7,-42.1c-0.4,-12.8 -6.7,-24.2 -16.1,-31.6 6.1,-12.4 12.5,-22.5 19.5,-30.7 3.8,1.7 8.1,2.6 12.6,2.6 17.6,0 32.1,-14.5 32.1,-32.1 0,-0.9 0,-1.8 -0.1,-2.6 7.3,-3 14.9,-5.6 22.8,-8.1 5.7,8.9 15.7,14.8 26.9,14.8 17.7,0 32.1,-14.5 32.1,-32.2v-0.7c7.5,-2.7 15.1,-5.9 22.8,-9.5 5.9,6.9 14.7,11.4 24.4,11.4 17.6,0 32.1,-14.5 32.1,-32.2 0,-4.8 -1.1,-9.5 -3.1,-13.7 6.4,-5.3 12.9,-11.5 17.7,-17.6 2.9,0.9 6,1.3 9.2,1.3 17.6,0 32.1,-14.5 32.1,-32.1 0,-17.1 -13.6,-31.26 -30.5,-32.09 -11,-16.49 -34.3,-24.49 -49.5,-20 -5.9,-7.82 -15.3,-12.91 -25.7,-12.91 -17.7,0 -32.2,14.5 -32.2,32.13 0,2.67 0.4,5.29 1,7.78 -4.6,1.48 -9.3,2.69 -14.2,3.69 -4.7,-11.96 -16.4,-20.5 -29.9,-20.5 -14.3,0 -26.4,9.4 -30.6,22.28 -8.1,-0.99 -16.7,-2.39 -23.7,-3.81 -0.6,-17.08 -14.8,-30.88 -32.1,-30.88 -10.5,0 -20,5.2 -25.8,13.16 -8.2,-3.29 -16.1,-6.82 -23.7,-10.56v-1.44c0,-17.63 -14.5,-32.12 -32.1,-32.12zM144.2,37.7c7.5,0 13.4,5.91 13.4,13.44 0,7.53 -5.9,13.44 -13.4,13.44s-13.5,-5.91 -13.5,-13.44c0,-7.53 6,-13.44 13.5,-13.44zM52.9,43.86c7.6,0 13.5,5.91 13.5,13.44 0,7.53 -5.9,13.43 -13.5,13.43 -7.5,0 -13.4,-5.9 -13.4,-13.43 0,-7.53 5.9,-13.44 13.4,-13.44zM112.1,50.95v0.19c0,17.63 14.5,32.13 32.1,32.13 10.5,0 19.9,-5.12 25.8,-13 7.6,3.72 15.6,7.25 23.7,10.53v1.28c0,17.63 14.5,32.12 32.1,32.12 11.5,0 21.6,-6.1 27.3,-15.25 10.1,1.85 19.1,3.65 28.2,4.55 4,13.3 16.3,23.1 30.9,23.1 15.1,0 27.9,-10.7 31.2,-24.9 7.9,-1.5 15.5,-3.5 22.9,-6.15 5.7,4.95 13.1,8.05 21.2,8.05 17.6,0 32.1,-14.55 32.1,-32.18v-1.75c10.2,-1.39 18.6,2.07 23.9,7.81 -8.7,5.77 -14.6,15.65 -14.6,26.82 0,7.8 2.8,14.8 7.5,20.5 -4.7,5.5 -9.6,9.7 -14.7,14.1 -4.8,-2.9 -10.5,-4.6 -16.5,-4.6 -17.6,0 -32.1,14.5 -32.1,32.1 0,1.3 0.1,2.5 0.2,3.8 -7.1,3.3 -14.2,6.2 -21.2,8.8 -5.8,-8.3 -15.4,-13.7 -26.2,-13.7 -17.4,0 -31.8,14.2 -32.1,31.6 -8.2,2.5 -16.3,5.5 -24.3,8.9 -5.9,-7.5 -15.1,-12.4 -25.3,-12.4 -17.6,0 -32.1,14.4 -32.1,32.1 0,6.6 2,12.8 5.5,17.9 -8.1,9.4 -15.6,20.7 -22.4,34.3 -6.1,-1.2 -12.2,-1.4 -17.8,0 0.5,-4.5 0.8,-8.9 0.8,-13 12,-4.7 20.6,-16.4 20.6,-30 0,-17.6 -14.5,-32.1 -32.1,-32.1 -2.2,0 -4.3,0.2 -6.3,0.6 -4.1,-5.7 -8.7,-10.8 -13.6,-15.5 2.2,-4.4 3.4,-9.3 3.4,-14.4 0,-17.7 -14.5,-32.1 -32.1,-32.1 -9.3,0 -17.7,4 -23.6,10.4 -4.3,-2.7 -8.5,-5.3 -12.5,-7.9 0.7,-2.6 1,-5.2 1,-8 0,-17.6 -14.5,-32.1 -32.1,-32.1 -0.5,0 -1,0 -1.5,0.1 -0.6,-4.35 -0.4,-9.07 0.5,-14.43 1,0.1 2,0.15 3,0.15 17.7,0 32.2,-14.49 32.2,-32.12 0,-1.38 -0.1,-2.76 -0.3,-4.09 9.13,-2.16 18.4,-2.75 27.3,-2.26zM387.5,57.99c7.5,0 13.4,5.9 13.4,13.43 0,7.53 -5.9,13.44 -13.4,13.44 -7.6,0 -13.5,-5.91 -13.5,-13.44 0,-7.53 5.9,-13.43 13.5,-13.43zM225.8,68.67c7.6,0 13.4,5.88 13.4,13.41s-5.8,13.43 -13.4,13.43c-7.5,0 -13.4,-5.9 -13.4,-13.43 0,-7.53 5.9,-13.41 13.4,-13.41zM312.2,81.08c7.5,0 13.4,5.87 13.4,13.4 0,7.62 -5.9,13.52 -13.4,13.52 -7.6,0 -13.5,-5.9 -13.5,-13.52 0,-7.53 5.9,-13.4 13.5,-13.4zM461.1,90.86c7.5,0 13.4,5.91 13.4,13.44 0,7.6 -5.9,13.4 -13.4,13.4 -7.6,0 -13.4,-5.8 -13.4,-13.4 0,-7.53 5.8,-13.44 13.4,-13.44zM50.9,122.2c7.5,0 13.5,6 13.5,13.5s-6,13.5 -13.5,13.5 -13.4,-6 -13.4,-13.5 5.9,-13.5 13.4,-13.5zM405.2,153c7.5,0 13.5,5.9 13.5,13.4 0,7.6 -6,13.5 -13.5,13.5s-13.4,-5.9 -13.4,-13.5c0,-7.5 5.9,-13.4 13.4,-13.4zM118.1,159.8c7.5,0 13.5,5.9 13.5,13.5 0,7.5 -6,13.4 -13.5,13.4s-13.4,-5.9 -13.4,-13.4c0,-7.6 5.9,-13.5 13.4,-13.5zM325.9,184c7.6,0 13.4,5.9 13.4,13.4 0,7.6 -5.8,13.5 -13.4,13.5 -7.5,0 -13.4,-5.9 -13.4,-13.5 0,-7.5 5.9,-13.4 13.4,-13.4zM244.2,212.1c7.5,0 13.4,5.8 13.4,13.4 0,7.5 -5.9,13.4 -13.4,13.4s-13.4,-5.9 -13.4,-13.4c0,-7.6 5.9,-13.4 13.4,-13.4zM166.7,221.2c7.5,0 13.4,6 13.4,13.5s-5.9,13.4 -13.4,13.4 -13.4,-5.9 -13.4,-13.4 5.9,-13.5 13.4,-13.5zM186.2,295.4c8.1,1.1 17.1,5.2 20.9,12.9 -20.1,7.4 -29.6,10.8 -43.5,16.3 -1.8,-10.9 -0.4,-20.2 10,-25.5 3.9,-2 8.3,-4.3 12.6,-3.7zM209.7,349.5l-15.3,7 -7,59.9 52.3,3.2 23.5,-47.1zM338.9,377.5l-58.7,2.9 -24.9,49.9 30.2,54.3zM183.4,434.9l-34.6,50.8 120,7.3 -30.5,-54.8z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M158.09,75.16c-20.64,13.65 -46.02,22.5 -69.44,26.03 6.79,48.94 26.14,84.13 45.97,97.85 10.52,7.28 20.36,9.01 30.41,5.56 8.93,-3.06 18.63,-10.97 27.75,-25.59 -26.93,-30.23 -31.61,-71.05 -34.69,-103.84zM355,75.25c-3.08,32.79 -7.79,73.58 -34.72,103.78 9.12,14.61 18.86,22.5 27.78,25.56 10.05,3.45 19.85,1.72 30.38,-5.56 19.8,-13.69 39.12,-48.82 45.94,-97.65 -24.5,-3.17 -47.8,-11.35 -69.38,-26.13zM256.5,88.22c-31.2,0 -58.75,6.99 -75.47,17.66 0.99,5.46 2.15,10.91 3.56,16.25 16.14,-6.94 42.33,-11.47 71.94,-11.47 29.6,0 55.8,4.53 71.94,11.47 1.41,-5.34 2.58,-10.79 3.56,-16.25C315.31,95.2 287.72,88.22 256.5,88.22zM75.41,132.37l-47.72,45.47c29.75,37.18 63.04,56.83 86.47,58.72 1.14,0.09 2.25,0.14 3.34,0.16 1.79,-7.58 3.96,-15.05 6.5,-22.34 -21.72,-15.02 -39.01,-43.74 -48.59,-82zM437.66,132.37c-9.59,38.26 -26.87,66.98 -48.59,82 -0.03,0.02 -0.06,0.04 -0.09,0.06 2.54,7.28 4.73,14.72 6.53,22.28 1.11,-0.02 2.24,-0.06 3.41,-0.16 23.43,-1.89 56.75,-21.52 86.5,-58.72l-47.75,-45.47zM192.66,144.84c4.54,9.7 10.45,18.6 18.25,26.16l5.13,4.97 -3.44,6.22c-1.8,3.29 -3.67,6.39 -5.59,9.31 32.59,11.97 67.86,11.14 98.94,-0.19 -1.88,-2.87 -3.71,-5.9 -5.47,-9.13l-3.41,-6.22 5.09,-4.97c7.53,-7.29 13.31,-15.85 17.78,-25.16 -43.63,10.53 -85.4,8.5 -127.28,-1zM318.16,206.65c-38.4,15.68 -83.03,16.82 -123.41,0.16 -7.3,7.37 -15.22,12.57 -23.66,15.47 -9.92,3.4 -20.24,3.31 -30.06,0.37 -6.91,20.59 -10.78,42.6 -10.78,64.41 0,7.31 0.56,14.82 1.66,22.47l122.41,-29.91 2.22,-0.56 2.22,0.56 122.75,29.97c0.89,-7.65 1.34,-15.17 1.34,-22.53 0,-21.79 -3.89,-43.79 -10.84,-64.38 -9.81,2.93 -20.12,2.99 -30.03,-0.41 -8.5,-2.91 -16.47,-8.17 -23.81,-15.62zM476.63,215.22c-25.4,24.17 -52.07,38.02 -76.22,39.97 -0.43,0.03 -0.86,0.04 -1.28,0.06 1.21,8.07 1.99,16.23 2.28,24.41 22.81,3.78 54.72,0.1 90,-14.34l-14.78,-50.09zM36.44,215.25l-14.75,50.06c35.27,14.43 67.17,18.12 89.97,14.34 0.29,-8.18 1.08,-16.33 2.28,-24.41 -0.43,-0.03 -0.85,-0.03 -1.28,-0.06 -24.15,-1.95 -50.82,-15.78 -76.22,-39.94zM256.53,298.31l-121.06,29.59c1.97,8.13 4.51,16.35 7.5,24.63 33.54,-7.73 71.23,-11.51 108.5,-11.78 2.78,-0.02 5.56,-0.02 8.34,0 40.26,0.26 79.61,4.58 112.28,12.25 2.62,-8.34 4.8,-16.65 6.47,-24.88L256.53,298.31zM251.63,359.72c-35.09,0.24 -70.5,3.56 -101.63,10.4 3.59,8.16 7.62,16.33 12.03,24.44 28.36,-2.45 59.06,-3.68 89.47,-3.78 2.76,-0.01 5.53,-0.01 8.28,0 33.54,0.11 66.45,1.56 95.22,4.16 4.04,-8.02 7.64,-16.1 10.81,-24.19 -32.65,-7.39 -73.18,-11.31 -114.19,-11.03zM251.56,409.53c-26.84,0.09 -53.86,1.04 -79.13,2.94 5.52,8.88 11.47,17.65 17.81,26.22 4.83,1.1 13.82,2.33 25.34,3.03 13.2,0.8 29.06,1.14 44.81,1.06 15.75,-0.07 31.41,-0.56 44.22,-1.4 10.92,-0.72 19.23,-1.85 23.47,-2.72 6.2,-8.43 11.88,-17.07 17.06,-25.81 -28.49,-2.27 -60.9,-3.42 -93.59,-3.31z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#000000"
android:pathData="M339.8,33.47l14,2.81c-1.2,3.02 -2.5,6.32 -4,9.75 -5.8,13.63 -14.2,29.31 -20,35.12 -4.9,4.85 -19.8,9.57 -33.1,11.48 -11.2,1.8 -22.4,3.9 -33.7,3.9v15.97h-14L249,96.53c-12,0.1 -24.2,-2.6 -33.7,-3.9 -13.3,-1.9 -28.2,-6.62 -33.1,-11.45 -5.8,-5.81 -14.2,-21.49 -20,-35.11 -1.5,-3.44 -2.8,-6.75 -4,-9.79l14,-2.81c8.4,11.37 38,47.06 83.8,47.06s75.4,-35.69 83.8,-47.06zM371.7,39.85l31.9,6.38 -29.7,74.37 32.7,49 -9.5,66.1c-1.8,1.4 -3.8,2.8 -6,4.3 -11.3,7.5 -26.1,14.5 -39.1,14.5 -4.3,0 -12,-3.5 -20.2,-10.4 -8.3,-6.9 -17,-16.5 -24.8,-26.2 -14.2,-17.9 -24.3,-34.2 -26,-37v-18.4h11v-18h-11v-14h11v-18s38.9,-7.7 50.4,-18.57c10.2,-10.11 17.8,-26.43 24,-40.82 2,-4.79 3.8,-9.3 5.3,-13.26zM140.3,39.85c1.5,3.96 3.3,8.48 5.3,13.29 6.2,14.39 13.8,30.71 24,40.79C185,107.1 220,112.5 220,112.5v18h11v14h-11v18h11v18.4c-1.7,2.8 -11.8,19.1 -26,37 -7.8,9.7 -16.5,19.3 -24.8,26.2 -8.2,6.9 -15.9,10.4 -20.2,10.4 -13,0 -27.8,-7 -39.1,-14.5 -2.2,-1.5 -4.2,-2.9 -6,-4.3l-9.5,-66.1 32.7,-49 -29.7,-74.38zM263,130.5v14h-14v-14zM263,162.5L263,186l1.3,2.1s12.2,20.4 28.7,41c8.2,10.3 17.5,20.7 27.2,28.8 9.8,8.1 20.1,14.6 31.8,14.6 15.6,0 30,-6.1 41.7,-13l-2.1,15 -54.2,40.7 -81.4,-97.8 -81.4,97.8 -54.2,-40.7 -2.1,-15c11.7,6.9 26.1,13 41.7,13 11.7,0 22,-6.5 31.8,-14.6 9.7,-8.1 19,-18.5 27.2,-28.8 16.5,-20.6 28.7,-41 28.7,-41l1.3,-2.1v-23.5zM256,245.6l78.6,94.2 33.1,-24.8 -7.4,37c-13.1,3.1 -64.2,14.5 -104.3,14.5 -40.1,0 -91.2,-11.4 -104.3,-14.5l-7.4,-37 33.1,24.8zM346.3,373.6l8.7,34.9c-5.9,2 -12.5,4.2 -19.6,6.4 -4.2,1.3 -8.6,2.6 -13,3.8l-6.6,-39.6c11.3,-1.8 21.8,-3.8 30.5,-5.5zM165.7,373.6c8.7,1.7 19.2,3.7 30.5,5.5l-6.6,39.6c-4.4,-1.2 -8.8,-2.5 -13,-3.8 -7.1,-2.2 -13.7,-4.4 -19.6,-6.4zM297.9,381.6l6.9,41.7c-13.9,3.3 -27.8,5.9 -39.8,6.8v-45.8c10.8,-0.3 22,-1.4 32.9,-2.7zM214.1,381.6c10.9,1.3 22.1,2.4 32.9,2.7v45.8c-12,-0.9 -25.9,-3.5 -39.8,-6.8zM377.7,419.5l11.2,44.7s-56.2,20.2 -85.6,24.3c-31.2,4.3 -63.4,4.3 -94.6,0 -29.4,-4.1 -85.6,-24.3 -85.6,-24.3l11.2,-44.7c8.1,3.1 20.1,7.4 37.1,12.6 26.3,8.1 58.6,16.4 84.6,16.4 26,0 58.3,-8.3 84.6,-16.4 17,-5.2 29,-9.5 37.1,-12.6z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M230.31,18.22c-38.37,0 -65.98,2.77 -85.03,8.5 -19.05,5.73 -29.25,13.7 -35.87,25.47 -12.14,21.56 -10.57,61.47 -8.38,120.59 19.88,-8.89 47.44,-11.33 78.19,-10.9 35.38,0.49 74.69,5.72 109.31,14.38 18.68,-46.79 28.3,-91.06 38,-135.34 0.55,43.8 0.98,87.61 -9.65,131.41 26.36,9.14 45.59,23.72 56.53,39.44 11.88,-31.15 20.09,-58.35 23.63,-81.09 4.29,-27.62 1.97,-48.6 -6.87,-64.41 -8.85,-15.8 -24.57,-27.57 -50.66,-35.75 -26.09,-8.18 -62.19,-12.28 -109.19,-12.28zM171.97,180.53c-9.12,0.09 -17.84,0.61 -25.94,1.47 -9.86,77.77 -11.77,156.56 -4.47,254.56 26.54,23.08 66.74,31.19 105.47,40.91 -32.55,1.31 -65.25,0.19 -98.5,-9.6 8.33,17.87 23.53,25.03 44,25.03H450.5c37.1,0 22.51,-40.21 3.09,-66.94 -22.22,-30.58 -65.65,-34.5 -101.22,-58.53l0.13,0.22c-38.97,-15.46 -77.94,0.41 -116.91,31.56 20.12,-29.93 48.29,-56.02 80.19,-56.47 3.74,-0.05 7.54,0.28 11.38,0.97 -3.08,-8.27 -5.61,-16.39 -7.66,-24.38 -33.09,-22.49 -73.68,-16.99 -118,2.5 22.38,-19.51 49.35,-35.1 75.44,-35.63 13.23,-0.27 26.24,3.35 38.31,12.28 -5.43,-35.45 -1.14,-68.74 9.44,-102.47 -4.55,-2.32 -9.48,-4.46 -14.78,-6.31l-12.38,-2.13 -2.72,9.66c-35.35,-9.93 -78.42,-16.17 -115.88,-16.69 -2.34,-0.03 -4.67,-0.05 -6.97,-0.03z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3,14A9,9 0,1 0,14.822 5.46l1.019,-1.585a1,1 0,0 0,0.031 -1.032l-0.75,-1.333A1,1 0,0 0,14.25 1L9.75,1a1,1 0,0 0,-0.872 0.51l-0.75,1.333a1,1 0,0 0,0.031 1.032L9.178,5.46A9.011,9.011 0,0 0,3 14ZM11.282,5.034L10.167,3.3l0.168,-0.3h3.33l0.168,0.3L12.718,5.034C12.468,5.011 12.23,5 12,5S11.532,5.011 11.282,5.034ZM12,7a7,7 0,1 1,-7 7A7.022,7.022 0,0 1,12 7Z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3.5,13.5A8.5,8.5 0,1 0,14.354 5.339L15.5,3.556 14.625,2H9.375L8.5,3.556 9.646,5.339A8.509,8.509 0,0 0,3.5 13.5ZM12,7a6.5,6.5 0,1 1,-6.5 6.5A6.508,6.508 0,0 1,12 7Z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="32"
android:viewportWidth="32" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000000" android:pathData="M16,1c1.562,1.5 6.25,4.5 9.375,4.5 0,1.5 0,4.5 0,6 0,6 -4.688,15 -9.375,19.5 -4.688,-4.5 -9.375,-13.5 -9.375,-19.5 0,-1.5 0,-4.5 0,-6 3.126,0.001 7.813,-2.999 9.375,-4.5v0z"/>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="290.23"
android:viewportHeight="290.23">
<path
android:fillColor="#000000"
android:pathData="M63.95,243.57c-1.95,-3.58 -4.4,-6.91 -7.36,-9.87c-3.11,-3.1 -6.63,-5.63 -10.4,-7.63c-4.51,-2.39 -0.94,-7.5 -0.94,-7.5c4.62,-7.02 8.82,-14.08 12.31,-20.23l-23.36,-23.34H11.5c-4.36,0 -7.9,-3.54 -7.9,-7.9c0,-4.36 3.54,-7.9 7.9,-7.9h25.95c2.1,0 4.11,0.83 5.59,2.31l85.38,85.29c1.48,1.48 2.32,3.49 2.32,5.59v26.07c0,4.36 -3.54,7.9 -7.9,7.9c-4.37,0 -7.9,-3.53 -7.9,-7.9v-22.8l-23.27,-23.24c-6.28,3.71 -13.58,8.25 -20.82,13.25C70.84,245.68 66.7,248.63 63.95,243.57z" />
<path
android:fillColor="#000000"
android:pathData="M26.61,237.1c-7.11,0 -13.78,2.76 -18.81,7.78c-5.02,5.01 -7.78,11.69 -7.78,18.78c0,7.1 2.76,13.76 7.78,18.78c5.03,5.02 11.71,7.78 18.81,7.78c7.1,0 13.78,-2.77 18.8,-7.78c5.02,-5.01 7.79,-11.68 7.79,-18.78c0,-7.09 -2.77,-13.76 -7.79,-18.78C40.39,239.87 33.71,237.1 26.61,237.1z" />
<path
android:fillColor="#000000"
android:pathData="M100.99,182.32c-3.5,3.5 -9.23,3.5 -12.73,0l-8.81,-8.8c-3.5,-3.5 -3.5,-9.22 0,-12.72L229.83,10.56c3.5,-3.5 10.4,-6.73 15.33,-7.18l36.86,-3.35c4.93,-0.45 8.6,3.22 8.15,8.15l-3.35,36.79c-0.45,4.93 -3.68,11.82 -7.18,15.32l-150.4,150.25c-3.5,3.5 -9.23,3.5 -12.73,0l-8.82,-8.81c-3.5,-3.5 -3.5,-9.22 0,-12.72L233.61,63.21c1.85,-1.85 1.86,-4.85 0,-6.7c-1.85,-1.85 -4.85,-1.85 -6.71,-0L100.99,182.32z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M128.84,16.31c-1.26,0.01 -2.52,0.04 -3.75,0.09 -19.69,0.81 -35.63,6.79 -46.63,17.78 -21.99,21.99 -23.81,63.78 -4.72,115.69s58.57,112.16 113.69,167.28c55.12,55.12 115.38,94.6 167.28,113.69 51.91,19.09 93.73,17.27 115.72,-4.72 21.99,-21.99 23.78,-63.78 4.69,-115.69 -19.09,-51.9 -58.54,-112.19 -113.66,-167.31C306.35,88.01 246.06,48.56 194.15,29.47c-24.33,-8.95 -46.42,-13.32 -65.31,-13.16zM163.81,68.47c45.35,-0.48 113.94,35.97 175.15,97.19 76.95,76.95 114.75,165.57 89.28,205 -16.35,-55.69 -56.74,-120.15 -115.44,-178.84C254.13,133.13 189.69,92.73 134,76.38c8.06,-5.2 18.17,-7.78 29.81,-7.91zM42.72,70.03L31.78,80.97c-10.99,10.99 -16.85,26.35 -17.69,46.06 -0.83,19.71 3.62,43.48 13.22,69.38C46.51,248.2 86.06,308.37 141.16,363.47c55.1,55.1 115.27,94.65 167.06,113.84 25.9,9.6 49.66,14.05 69.37,13.22 19.71,-0.83 35.08,-6.7 46.06,-17.69l10.97,-10.97c-25.29,3.14 -55.02,-1.98 -86.35,-13.5 -55.3,-20.34 -117.29,-61.23 -174.06,-118 -56.77,-56.77 -97.66,-118.73 -118,-174.03 -11.52,-31.3 -16.63,-61.03 -13.5,-86.31zM119.94,91.91c52.77,13.2 119.52,52.99 179.66,113.13 60.13,60.14 99.93,126.89 113.13,179.66 -41.03,21.15 -126.71,-16.64 -201.41,-91.34 -74.71,-74.72 -112.55,-160.42 -91.38,-201.44z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="1200"
android:viewportHeight="1200">
<path
android:fillColor="#FF000000"
android:pathData="M881.2,252.1c-2.7,-9.3 -3.4,-21.3 3,-29.2c5.9,-7.3 15.1,-11.9 23.2,-16.2c-7.8,0 -19.9,3.4 -21.1,3.5c-10.6,6.1 -13.6,13.1 -13.9,20.6L707.8,393.9c-8.4,-19.3 -16.5,-39.1 -19.7,-60c-3.3,-21.2 -3.4,-42.8 -6.3,-64c-1.9,-13.9 -2.8,-31.6 -9.2,-44.3c-7.9,-15.5 -29.8,-18.1 -45.2,-20.9c7.7,-14.2 3,-28.1 2.9,-43.2c-0,-15.1 2.6,-30 2.9,-45.1c0.4,-25.9 -8.9,-52 -34.4,-62.3c-12.4,-5 -27.3,-5.3 -40.2,-1.7c-12.1,3.4 -21.9,13.6 -28.2,24.1c-15.7,25.8 -7.4,58.7 -9.8,87.4L506.1,143c-1.9,-11.2 -6.6,-19.5 -12.5,-29.3c-2.5,-4.1 -4.5,-8.5 -7.3,-12.4c-3.5,-4.8 -4.9,-4.6 -10.4,-2l-8.1,-11.8l-5.1,3.5l8.3,12c-3.3,5.1 -4,7.1 -0.5,12.3c2.9,4.1 6.4,7.8 9.5,11.8c6.4,8.1 11.8,14.7 20.9,19.5l15,21.8c-5.1,1.9 -10.2,4.2 -14.8,6.8l-25.7,-33.6c-2.5,-11.8 -8,-20.3 -14.7,-30.4c-2.5,-3.9 -4.7,-9 -8,-12.3c-3.7,-3.6 -5.5,-1.9 -9.9,0.3l-6.6,-8.6l-4.9,3.8l6.9,9c-2.7,4.4 -3.3,6.6 -0.4,10.9c2.7,4 6.6,7.4 9.7,11.1c6.9,8 12.7,15.1 22.4,19.6l25.7,33.6c-3.8,2.4 -7.5,4.9 -10.7,8l-16.1,-19.4c-2.8,-10.9 -9.2,-18.9 -15.8,-27.9c-4.6,-6.3 -8.4,-17.2 -17.1,-11.9l-5.9,-7.1l-4.8,4l5.8,6.9c-2.5,5.1 -4.3,7.5 -0.5,12.3c3,3.8 7,7 10.3,10.5c7.9,8.3 14.4,14.5 24.9,19l15.2,18.3c-3.6,5.3 -2.5,10.7 1.7,15.3c-23.2,6.5 -34.1,26.3 -41.5,47.8c-3.3,9.5 -5.2,19.8 -10.2,28.6c-2.5,4.4 -5.6,8.7 -9.7,11.7c-4.2,3.1 -9.6,4.8 -13.1,8.7c8.6,6.1 21.8,4.4 31.3,5.5c0,9.9 -2.9,19.1 -5.4,28.5c-1.8,6.8 0.2,8.2 7.6,7.5c-1.7,11 -8.7,20.5 -13.6,30.2c-5.5,10.8 -9.9,22.1 -15.6,32.8c-2.4,-2.1 -9.9,-3.7 -13,-3.9c-4.4,-0.3 -4.7,0.1 -5.5,4.4c-1.4,7.4 1.8,15.4 2.5,22.7c0.7,6.9 -0.2,13.7 -1.8,20.3c-3.6,14.8 -10.5,28.9 -16.8,42.7c-5.6,12.4 -13.3,23.5 -23.7,32.3c-2.6,2.2 -9.6,4.6 -10.8,7.8c-1.6,4.4 3.6,4.2 2.6,7.8c-1,3.7 -10.1,9.1 -12.7,12c-5.1,5.7 -6.4,10.7 -5.6,18.3c1.3,12.4 3,25.9 10.2,36.4c1.6,2.3 5.8,9.2 9.2,6.2c2.7,-2.3 -0.2,-10.7 -1.1,-13.2c3,0.7 10.1,4.5 12.9,2.2c3.7,-3 -2.8,-6.3 -4.3,-8.4c-2.8,-3.9 -5.6,-14.7 -3,-18.8c2,-3.2 7.8,-5.5 11,-7.1c10.8,-5.3 8.7,2 4.3,8.9c-3.5,5.6 -13,18.4 -0.7,20.4c6.9,-19.1 37,-37.3 31,-59.3c2.7,-4 4.1,-8.8 7.2,-12.6c2.9,-3.5 5.4,-6.2 7.4,-10.4c4.2,-8.7 7.6,-17 14.1,-24.4c3.1,-3.5 6.6,-6.4 8.7,-10.6c1.6,-3.3 2.5,-7 4.1,-10.4c5,-10.6 12,-20.1 17.8,-30.3c10.9,-19.1 13.4,-41.9 21.9,-62.2c4.3,-10.3 8.6,-20.6 13,-30.9c2,-4.7 8.4,-14.5 5.5,-19.8c10,19 31.4,38 19.7,61.3c-4.7,9.4 -12.6,16.6 -17.9,25.6c-6.7,11.3 -10.8,24.7 -14.5,37.3c-16.5,55.9 -12.5,115.1 -9.6,172.6L325.9,771.9c-11.5,5 -22.8,11.1 -28.6,22.8c-2.2,4.3 -8.1,21.5 -2.2,25.5c1.6,-8.4 1.2,-15.1 7,-22.1c5.3,-6.4 12.1,-12 19.3,-16.1c16,-9.2 35.6,-13.2 53.4,-17.2c39.9,-9.1 79.4,-18.1 118.6,-30c2.1,12.7 6.3,25 8.9,37.6c2.6,12.4 -0.2,26.5 3.9,38.4c-7.6,-8.6 -6.4,12.5 -5.7,15.1c3,12.7 5.5,25.3 8,38.1c5.3,27.5 16.2,53.5 21.1,81.1c2.3,13 4.1,26.1 7.5,38.8c1.7,6.4 4.6,12.8 5.4,19.4c0.8,6.3 -1.9,10.8 -2.7,16.7c-1.5,11.9 5.2,25.1 5.7,37.2c0.5,12.5 -5.4,22.8 -7,34.8c-1.5,11.2 2.7,24 10,32.5c3.4,4.1 8.2,7.9 13.7,8.1c5.9,0.3 11.7,-4.4 17.6,-3.1c0.1,20.5 25.5,22.6 40.6,18.8c7.9,-2 18.4,-6.5 21.2,-14.8c4.1,-12.1 -5.4,-25.5 -8.3,-36.8c-4,-15.3 -2.5,-32.7 -3.5,-48.4c-1.1,-17 -2.6,-32.9 -0.3,-49.9c4.4,-33.1 10,-65.9 12.3,-99.3c1.1,-16.5 2,-33.1 7.3,-48.9c1.8,-5.2 4.3,-10 5.7,-15.3c0.8,-2.9 2.2,-16.6 -5,-13.3c9.1,-9.1 9.1,-23.9 10.9,-35.8c2.3,-15.2 7.5,-30 11,-45c7.8,-33.5 14.9,-67.1 22.8,-100.5c12.4,-3.3 32.5,-3.1 39.5,-15.8c4.6,-8.4 2.6,-18.1 4.7,-27c14.8,6.1 38.1,-19.9 30.9,-33.4c13.8,-12.7 28,-25.1 42.1,-37.6c8.1,-7.2 19.7,-14.7 25.8,-23.9c3,-4.6 3.4,-5.6 8.9,-7.8c8,-3.2 18.1,-4.6 25.2,-9.8c6.4,-4.7 9.5,-12.4 9,-20.2c-0.4,-5.4 -1.3,-15.4 -5.2,-19.6c2.3,0.6 8.5,-7.8 9.6,-9.2c4.6,-5.5 8.2,-11.6 10.7,-18.4c5.9,-16 5.4,-33.4 5.1,-50.2c-0.3,-19.2 -1.4,-38.6 -4.7,-57.6C892.5,290.4 886.7,271.2 881.2,252.1zM462.1,732.7c-40.3,10 -82.7,17.7 -121.2,33.2l134.2,-132.9c1.2,31 12.7,59.2 16.6,89.7C482,726.5 472.1,730.2 462.1,732.7zM615.8,203c1.8,-17.4 5.9,-35.9 3.8,-53.4c6.1,17.3 9.6,37.9 6.8,54.9C622.9,203.9 619.3,203.4 615.8,203zM669.6,429.1c-3.9,-7.8 -7.6,-16.1 -10.3,-24.4c-2.7,-8.3 -1.8,-17.4 -4.7,-25.7l19.6,48.1l-3.7,3.6C670.2,430.2 669.9,429.6 669.6,429.1zM673.6,436.4l3.1,-3.1l11.6,28.5C683.9,453.1 678.5,444.9 673.6,436.4zM724.9,568.4c-1.5,4.2 -5.6,8.6 -2.3,12.9c-9.4,10.2 -8.5,25 -22.2,31.9c8,-43.3 10.3,-88.7 -3.6,-130.6c5.3,12.9 11.8,24 19.2,35.8c4.3,6.9 8.6,14.4 9.1,22.7c0.2,4.2 -0.4,8.5 -0.1,12.7C725.3,558.8 726.7,563.5 724.9,568.4zM870.1,390.3c-3.9,6.6 -8.5,12.8 -13.7,18.4c-5.3,5.7 -11.9,9.9 -17.2,15.5c-8.2,8.6 -5.5,14.2 3.5,19.1c25.3,13.8 -11.7,41.4 -25,49.2c-0,-0.1 -30.3,27.4 -57,52.2c-3.4,-9.9 -9.8,-19.9 -15.1,-28.9c-6.9,-11.5 -11.4,-23.8 -15.1,-36.6c-7.5,-26.2 -9.6,-53.8 -20.3,-79.2l162.7,-161.1c4,25.8 6.9,52.1 9.5,78.1C885.2,343.3 883.8,366.9 870.1,390.3z" />
</vector>

View file

@ -74,6 +74,10 @@
<string name="map_title">Carte</string>
<string name="map_label">Coordonnées</string>
<string name="character_sheet_tab_proficiency">Talents</string>
<string name="character_sheet_tab_inventory">Inventaire</string>
<string name="character_sheet_tab_alteration">Altérations</string>
<string name="character_sheet_tab_actions">Capacités</string>
<string name="character_sheet_title">Feuille de personnage</string>
<string name="character_sheet_title_hp">Point de vie</string>
<string name="character_sheet_title_ca">CA</string>
@ -84,7 +88,8 @@
<string name="character_sheet_title_proficiencies">Maîtrises</string>
<string name="character_sheet_title_skills">Capacités</string>
<string name="character_sheet_title_attacks">Attaques</string>
<string name="character_sheet_title_alteration">Altérations</string>
<string name="character_sheet_title_inventory">Inventaire</string>
<string name="character_sheet_title_equipment">Equipement</string>
<string name="character_sheet_stat_strength">Force</string>
<string name="character_sheet_stat_strength_short">FOR</string>
<string name="character_sheet_stat_dexterity">Dextérité</string>
@ -157,4 +162,20 @@
<string name="alteration_target">Cible : %1$s</string>
<string name="no_available_description">Aucune description disponnible</string>
<string name="character_sheet_equipment_head">Tête</string>
<string name="character_sheet_equipment_face">Visage</string>
<string name="character_sheet_equipment_shoulder">Épaules</string>
<string name="character_sheet_equipment_neck">Gorge</string>
<string name="character_sheet_equipment_body">Corp</string>
<string name="character_sheet_equipment_chest">Torse</string>
<string name="character_sheet_equipment_arm">Bras</string>
<string name="character_sheet_equipment_waist">Taille</string>
<string name="character_sheet_equipment_hands">Mains</string>
<string name="character_sheet_equipment_ring_right">Anneau 1</string>
<string name="character_sheet_equipment_foot">Pieds</string>
<string name="character_sheet_equipment_ring_left">Anneau 2</string>
<string name="character_sheet_equipment_main_hand">Main droite</string>
<string name="character_sheet_equipment_off_hand">Main gauche</string>
<string name="character_sheet_equipment_empty_description">Aucune description n\'est attachée à cet equipement</string>
</resources>

View file

@ -74,6 +74,10 @@
<string name="map_title">Map</string>
<string name="map_label">Coordinates</string>
<string name="character_sheet_tab_proficiency">Proficiencies</string>
<string name="character_sheet_tab_inventory">Inventory</string>
<string name="character_sheet_tab_alteration">Alterations</string>
<string name="character_sheet_tab_actions">Actions</string>
<string name="character_sheet_title">Character sheet</string>
<string name="character_sheet_title_hp">Hit Point</string>
<string name="character_sheet_title_ca">CA</string>
@ -84,7 +88,8 @@
<string name="character_sheet_title_proficiencies">Proficiencies</string>
<string name="character_sheet_title_attacks">Attacks</string>
<string name="character_sheet_title_skills">Skills</string>
<string name="character_sheet_title_alteration">Alterations</string>
<string name="character_sheet_title_inventory">Inventory</string>
<string name="character_sheet_title_equipment">Equipment</string>
<string name="character_sheet_stat_strength">Strength</string>
<string name="character_sheet_stat_strength_short">STR</string>
<string name="character_sheet_stat_dexterity">Dexterity</string>
@ -157,4 +162,20 @@
<string name="alteration_target">Target: %1$s</string>
<string name="no_available_description">No available description</string>
<string name="character_sheet_equipment_head">Head</string>
<string name="character_sheet_equipment_face">face</string>
<string name="character_sheet_equipment_shoulder">Shoulder</string>
<string name="character_sheet_equipment_neck">Neck</string>
<string name="character_sheet_equipment_body">Body</string>
<string name="character_sheet_equipment_chest">Chest</string>
<string name="character_sheet_equipment_arm">Arm</string>
<string name="character_sheet_equipment_waist">Waist</string>
<string name="character_sheet_equipment_hands">Hands</string>
<string name="character_sheet_equipment_ring_right">Ring 1</string>
<string name="character_sheet_equipment_foot">Foot</string>
<string name="character_sheet_equipment_ring_left">Ring2 </string>
<string name="character_sheet_equipment_main_hand">Main hand</string>
<string name="character_sheet_equipment_off_hand">Off hand</string>
<string name="character_sheet_equipment_empty_description">This equipment does not have any description</string>
</resources>