Rework the character sheet.
This commit is contained in:
parent
bd4de62841
commit
ad4e053f8e
31 changed files with 531 additions and 426 deletions
|
|
@ -1,16 +0,0 @@
|
|||
package com.pixelized.rplexicon.facotry.displayable
|
||||
|
||||
import com.pixelized.rplexicon.model.Action
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.ActionsUio
|
||||
import com.pixelized.rplexicon.utilitary.extentions.icon
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConvertActionIntoDisplayableFactory @Inject constructor() {
|
||||
fun toUio(action: Action): ActionsUio {
|
||||
return ActionsUio(
|
||||
title = action.title,
|
||||
hit = action.hit?.faces?.icon,
|
||||
damage = action.damage?.faces?.icon,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package com.pixelized.rplexicon.facotry.displayable
|
||||
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.model.Attack
|
||||
import com.pixelized.rplexicon.model.CharacterSheet
|
||||
import com.pixelized.rplexicon.model.Property
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.WeaponUio
|
||||
import com.pixelized.rplexicon.utilitary.extentions.icon
|
||||
import com.pixelized.rplexicon.utilitary.extentions.modifier
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConvertAttackIntoDisplayableFactory @Inject constructor() {
|
||||
|
||||
fun toUio(attack: Attack, characterSheet: CharacterSheet): WeaponUio? {
|
||||
val hit = attack.hit?.let { dice ->
|
||||
val modifier = dice.modifier.sumOf {
|
||||
when (it) {
|
||||
Property.PROFICIENCY -> characterSheet.proficiency
|
||||
Property.STRENGTH -> characterSheet.strength.modifier
|
||||
Property.DEXTERITY -> characterSheet.dexterity.modifier
|
||||
Property.CONSTITUTION -> characterSheet.constitution.modifier
|
||||
Property.INTELLIGENCE -> characterSheet.intelligence.modifier
|
||||
Property.WISDOM -> characterSheet.wisdom.modifier
|
||||
Property.CHARISMA -> characterSheet.charisma.modifier
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
WeaponUio.Dice(
|
||||
icon = dice.faces.icon,
|
||||
label = "${dice.amount}d${dice.faces}${if (modifier > 0) "+$modifier" else ""}",
|
||||
)
|
||||
}
|
||||
val damage = attack.damage?.let { dice ->
|
||||
val modifier = dice.modifier.sumOf {
|
||||
when (it) {
|
||||
Property.PROFICIENCY -> characterSheet.proficiency
|
||||
Property.STRENGTH -> characterSheet.strength.modifier
|
||||
Property.DEXTERITY -> characterSheet.dexterity.modifier
|
||||
Property.CONSTITUTION -> characterSheet.constitution.modifier
|
||||
Property.INTELLIGENCE -> characterSheet.intelligence.modifier
|
||||
Property.WISDOM -> characterSheet.wisdom.modifier
|
||||
Property.CHARISMA -> characterSheet.charisma.modifier
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
WeaponUio.Dice(
|
||||
icon = dice.faces.icon,
|
||||
label = "${dice.amount}d${dice.faces}${if (modifier > 0) "+$modifier" else ""}",
|
||||
)
|
||||
}
|
||||
return when (attack.type) {
|
||||
Attack.Type.PHYSICAL_MELEE_ATTACK -> WeaponUio(
|
||||
id = attack,
|
||||
icon = R.drawable.ic_crossed_swords_24,
|
||||
name = attack.title,
|
||||
type = "Attaque de corps à corps",
|
||||
range = "3 mêtres",
|
||||
hit = hit,
|
||||
damage = damage,
|
||||
)
|
||||
|
||||
Attack.Type.PHYSICAL_RANGE_ATTACK -> WeaponUio(
|
||||
id = attack,
|
||||
icon = R.drawable.ic_pocket_bow_24,
|
||||
name = attack.title,
|
||||
type = "Attaque à distance",
|
||||
range = "30 mêtres",
|
||||
hit = hit,
|
||||
damage = damage,
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import javax.inject.Inject
|
|||
|
||||
class ConvertCounterIntoDisplayableFactory @Inject constructor() {
|
||||
fun toUio(counter: Counter): CounterUio = CounterUio(
|
||||
title = counter.title,
|
||||
title = counter.title ?: "",
|
||||
value = counter.value,
|
||||
max = counter.max,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,15 +4,14 @@ import android.util.Log
|
|||
import com.google.api.services.sheets.v4.model.ValueRange
|
||||
import com.pixelized.rplexicon.facotry.model.roll.DiceParser
|
||||
import com.pixelized.rplexicon.facotry.model.roll.ModifierParser
|
||||
import com.pixelized.rplexicon.model.Action
|
||||
import com.pixelized.rplexicon.model.Attack
|
||||
import com.pixelized.rplexicon.model.CharacterSheet
|
||||
import com.pixelized.rplexicon.model.Property
|
||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||
import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure
|
||||
import com.pixelized.rplexicon.utilitary.extentions.sheet
|
||||
import javax.inject.Inject
|
||||
|
||||
class ActionParser @Inject constructor(
|
||||
class AttackParser @Inject constructor(
|
||||
private val diceParser: DiceParser,
|
||||
private val modifierParser: ModifierParser,
|
||||
) {
|
||||
|
|
@ -20,11 +19,16 @@ class ActionParser @Inject constructor(
|
|||
fun parse(
|
||||
value: ValueRange,
|
||||
charactersSheets: Map<String, CharacterSheet>,
|
||||
): Map<String, List<Action>> {
|
||||
): Map<String, List<Attack>> {
|
||||
val sheet = value.values.sheet()
|
||||
|
||||
lateinit var structure: Map<String, Int>
|
||||
|
||||
val actions = hashMapOf<String, MutableList<Action>>()
|
||||
// declare helper method to parse String
|
||||
fun List<*>.parseString(key: String): String? =
|
||||
(getOrNull(structure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
|
||||
|
||||
val actions = hashMapOf<String, MutableList<Attack>>()
|
||||
|
||||
sheet?.forEachIndexed { index, row ->
|
||||
when {
|
||||
|
|
@ -35,23 +39,18 @@ class ActionParser @Inject constructor(
|
|||
row is List<*> -> {
|
||||
// Assume that the name is the first column.
|
||||
val characterSheet = charactersSheets[row.getOrNull(0) as? String ?: ""]
|
||||
val title = row.getOrNull(structure.getValue(NAME))?.toString()
|
||||
val title = row.parseString(NAME)
|
||||
if (characterSheet != null && title != null) {
|
||||
val action = Action(
|
||||
val attack = Attack(
|
||||
title = title,
|
||||
type = parseType(
|
||||
value = row.getOrNull(structure.getValue(TYPE))?.toString(),
|
||||
),
|
||||
hit = parseThrows(
|
||||
value = row.getOrNull(structure.getValue(HIT))?.toString(),
|
||||
),
|
||||
damage = parseThrows(
|
||||
value = row.getOrNull(structure.getValue(DAMAGE))?.toString(),
|
||||
),
|
||||
type = parseType(value = row.parseString(TYPE)),
|
||||
range = row.parseString(RANGE),
|
||||
hit = parseThrows(value = row.parseString(HIT)),
|
||||
damage = parseThrows(value = row.parseString(DAMAGE)),
|
||||
)
|
||||
actions
|
||||
.getOrPut(characterSheet.name) { mutableListOf() }
|
||||
.add(action)
|
||||
.add(attack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -60,19 +59,19 @@ class ActionParser @Inject constructor(
|
|||
return actions
|
||||
}
|
||||
|
||||
private fun parseType(value: String?): Action.Type? = try {
|
||||
Action.Type.values().firstOrNull { it.key == value }
|
||||
private fun parseType(value: String?): Attack.Type? = try {
|
||||
Attack.Type.values().firstOrNull { it.key == value }
|
||||
} catch (exception: Exception) {
|
||||
Log.e("ActionParser", exception.message, exception)
|
||||
null
|
||||
}
|
||||
|
||||
private fun parseThrows(value: String?): Action.Throw? {
|
||||
private fun parseThrows(value: String?): Attack.Throw? {
|
||||
if (value != null) {
|
||||
val dice = diceParser.findAll(value = value).firstOrNull()
|
||||
if (dice != null) {
|
||||
val modifier = modifierParser.findAll(value = value)
|
||||
return Action.Throw(
|
||||
return Attack.Throw(
|
||||
amount = dice.count,
|
||||
faces = dice.faces,
|
||||
modifier = modifier,
|
||||
|
|
@ -84,10 +83,11 @@ class ActionParser @Inject constructor(
|
|||
|
||||
companion object {
|
||||
const val NAME = "Nom"
|
||||
const val TYPE = "type"
|
||||
const val TYPE = "Type"
|
||||
const val RANGE = "Portée"
|
||||
const val HIT = "Touché"
|
||||
const val DAMAGE = "Dommage"
|
||||
|
||||
val COLUMNS get() = listOf("", NAME, TYPE, HIT, DAMAGE)
|
||||
val COLUMNS get() = listOf("", NAME, TYPE, RANGE, HIT, DAMAGE)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.pixelized.rplexicon.facotry.model
|
||||
|
||||
import com.google.api.services.sheets.v4.model.ValueRange
|
||||
import com.pixelized.rplexicon.facotry.model.alteration.CounterParser
|
||||
import com.pixelized.rplexicon.model.CharacterSheet
|
||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||
import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure
|
||||
|
|
@ -8,7 +9,10 @@ import com.pixelized.rplexicon.utilitary.extentions.sheet
|
|||
import javax.inject.Inject
|
||||
|
||||
|
||||
class CharacterSheetParser @Inject constructor() {
|
||||
class CharacterSheetParser @Inject constructor(
|
||||
private val counterParser: CounterParser,
|
||||
) {
|
||||
|
||||
@Throws(IncompatibleSheetStructure::class)
|
||||
fun parse(value: ValueRange): Map<String, CharacterSheet> {
|
||||
|
||||
|
|
@ -43,11 +47,59 @@ class CharacterSheetParser @Inject constructor() {
|
|||
if (name != null) {
|
||||
CharacterSheet(
|
||||
name = name,
|
||||
proficiency = item.parseInt(MASTERY) ?: 2,
|
||||
level = item.parseInt(LEVEL) ?: 2,
|
||||
characterClass = item.parseString(CLASS) ?: "",
|
||||
hitPoint = item.parseString(HIT_POINT) ?: "1",
|
||||
maxHitPoint = item.parseString(MAX_HIT_POINT) ?: "1",
|
||||
lifeDice = item.parseInt(LIFE_DICE) ?: 0,
|
||||
spell1 = counterParser.parseCounter(
|
||||
value = item.parseString(
|
||||
SPELL_LEVEL_1
|
||||
)
|
||||
),
|
||||
spell2 = counterParser.parseCounter(
|
||||
value = item.parseString(
|
||||
SPELL_LEVEL_2
|
||||
)
|
||||
),
|
||||
spell3 = counterParser.parseCounter(
|
||||
value = item.parseString(
|
||||
SPELL_LEVEL_3
|
||||
)
|
||||
),
|
||||
spell4 = counterParser.parseCounter(
|
||||
value = item.parseString(
|
||||
SPELL_LEVEL_4
|
||||
)
|
||||
),
|
||||
spell5 = counterParser.parseCounter(
|
||||
value = item.parseString(
|
||||
SPELL_LEVEL_5
|
||||
)
|
||||
),
|
||||
spell6 = counterParser.parseCounter(
|
||||
value = item.parseString(
|
||||
SPELL_LEVEL_6
|
||||
)
|
||||
),
|
||||
spell7 = counterParser.parseCounter(
|
||||
value = item.parseString(
|
||||
SPELL_LEVEL_7
|
||||
)
|
||||
),
|
||||
spell8 = counterParser.parseCounter(
|
||||
value = item.parseString(
|
||||
SPELL_LEVEL_8
|
||||
)
|
||||
),
|
||||
spell9 = counterParser.parseCounter(
|
||||
value = item.parseString(
|
||||
SPELL_LEVEL_9
|
||||
)
|
||||
),
|
||||
armorClass = item.parseString(ARMOR_CLASS) ?: "10",
|
||||
speed = item.parseInt(SPEED) ?: 10,
|
||||
proficiency = item.parseInt(MASTERY) ?: 2,
|
||||
strength = item.parseInt(STRENGTH) ?: 10,
|
||||
dexterity = item.parseInt(DEXTERITY) ?: 10,
|
||||
constitution = item.parseInt(CONSTITUTION) ?: 10,
|
||||
|
|
@ -56,8 +108,10 @@ class CharacterSheetParser @Inject constructor() {
|
|||
charisma = item.parseInt(CHARISMA) ?: 10,
|
||||
strengthSavingThrows = item.parseInt(STRENGTH_SAVING_THROW) ?: 0,
|
||||
dexteritySavingThrows = item.parseInt(DEXTERITY_SAVING_THROW) ?: 0,
|
||||
constitutionSavingThrows = item.parseInt(CONSTITUTION_SAVING_THROW) ?: 0,
|
||||
intelligenceSavingThrows = item.parseInt(INTELLIGENCE_SAVING_THROW) ?: 0,
|
||||
constitutionSavingThrows = item.parseInt(CONSTITUTION_SAVING_THROW)
|
||||
?: 0,
|
||||
intelligenceSavingThrows = item.parseInt(INTELLIGENCE_SAVING_THROW)
|
||||
?: 0,
|
||||
wisdomSavingThrows = item.parseInt(WISDOM_SAVING_THROW) ?: 0,
|
||||
charismaSavingThrows = item.parseInt(CHARISMA_SAVING_THROW) ?: 0,
|
||||
acrobatics = item.parseInt(ACROBATICS) ?: 0,
|
||||
|
|
@ -89,8 +143,20 @@ class CharacterSheetParser @Inject constructor() {
|
|||
|
||||
companion object {
|
||||
private const val NAME = "Nom"
|
||||
private const val LEVEL = "Niveau"
|
||||
private const val CLASS = "Classe"
|
||||
private const val HIT_POINT = "Point de vie"
|
||||
private const val MAX_HIT_POINT = "Point de vie MAX"
|
||||
private const val LIFE_DICE = "Dé de vie"
|
||||
private const val SPELL_LEVEL_1 = "Sort de niveau 1"
|
||||
private const val SPELL_LEVEL_2 = "Sort de niveau 2"
|
||||
private const val SPELL_LEVEL_3 = "Sort de niveau 3"
|
||||
private const val SPELL_LEVEL_4 = "Sort de niveau 4"
|
||||
private const val SPELL_LEVEL_5 = "Sort de niveau 5"
|
||||
private const val SPELL_LEVEL_6 = "Sort de niveau 6"
|
||||
private const val SPELL_LEVEL_7 = "Sort de niveau 7"
|
||||
private const val SPELL_LEVEL_8 = "Sort de niveau 8"
|
||||
private const val SPELL_LEVEL_9 = "Sort de niveau 9"
|
||||
private const val ARMOR_CLASS = "Classe d'armure"
|
||||
private const val SPEED = "Vitesse"
|
||||
private const val MASTERY = "Bonus de maîtrise"
|
||||
|
|
@ -128,8 +194,20 @@ class CharacterSheetParser @Inject constructor() {
|
|||
private val ROWS
|
||||
get() = listOf(
|
||||
NAME,
|
||||
LEVEL,
|
||||
CLASS,
|
||||
HIT_POINT,
|
||||
MAX_HIT_POINT,
|
||||
LIFE_DICE,
|
||||
SPELL_LEVEL_1,
|
||||
SPELL_LEVEL_2,
|
||||
SPELL_LEVEL_3,
|
||||
SPELL_LEVEL_4,
|
||||
SPELL_LEVEL_5,
|
||||
SPELL_LEVEL_6,
|
||||
SPELL_LEVEL_7,
|
||||
SPELL_LEVEL_8,
|
||||
SPELL_LEVEL_9,
|
||||
ARMOR_CLASS,
|
||||
SPEED,
|
||||
MASTERY,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class CounterParser @Inject constructor() {
|
|||
return counters
|
||||
}
|
||||
|
||||
private fun parseCounter(title: String, value: String?): Counter? {
|
||||
fun parseCounter(title: String? = null, value: String?): Counter? {
|
||||
return if (value != null) {
|
||||
COUNTER_REGEX.find(value)?.let {
|
||||
val (actual, max) = it.destructured
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
package com.pixelized.rplexicon.model
|
||||
|
||||
data class Action(
|
||||
data class Attack(
|
||||
val title: String,
|
||||
val type: Type?,
|
||||
val range: String?,
|
||||
val hit: Throw?,
|
||||
val damage: Throw?,
|
||||
) {
|
||||
enum class Type(val key: String) {
|
||||
PHYSICAL_MELEE_ATTACK(Property.PHYSICAL_MELEE_ATTACK.key),
|
||||
PHYSICAL_RANGE_ATTACK(Property.PHYSICAL_RANGE_ATTACK.key),
|
||||
SPELL("Sortilège"),
|
||||
PHYSICAL_MELEE_ATTACK("Mêlée"),
|
||||
PHYSICAL_RANGE_ATTACK("Distance"),
|
||||
}
|
||||
|
||||
class Throw(
|
||||
|
|
@ -2,11 +2,23 @@ package com.pixelized.rplexicon.model
|
|||
|
||||
data class CharacterSheet(
|
||||
val name: String,
|
||||
val hitPoint: String, // Point de vie
|
||||
val maxHitPoint: String,
|
||||
val armorClass: String, // Classe d'armure
|
||||
val speed: Int,
|
||||
val proficiency: Int, // Bonus de maîtrise
|
||||
val level: Int, // Niveau
|
||||
val characterClass: String, // Classe
|
||||
val hitPoint: String, // Point de vie
|
||||
val maxHitPoint: String, // Point de vie MAX
|
||||
val lifeDice: Int, // Dé de vie
|
||||
val spell1: Counter?,
|
||||
val spell2: Counter?,
|
||||
val spell3: Counter?,
|
||||
val spell4: Counter?,
|
||||
val spell5: Counter?,
|
||||
val spell6: Counter?,
|
||||
val spell7: Counter?,
|
||||
val spell8: Counter?,
|
||||
val spell9: Counter?,
|
||||
val armorClass: String, // Classe d'armure
|
||||
val speed: Int, // Vitesse
|
||||
val strength: Int, // Force
|
||||
val dexterity: Int, // Dextérité
|
||||
val constitution: Int, // Constitution
|
||||
|
|
@ -52,5 +64,3 @@ data class CharacterSheet(
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.pixelized.rplexicon.model
|
||||
|
||||
data class Counter(
|
||||
val title: String,
|
||||
val title: String? = null,
|
||||
val value: Int,
|
||||
val max: Int?,
|
||||
)
|
||||
|
|
@ -1,39 +1,32 @@
|
|||
package com.pixelized.rplexicon.repository.data
|
||||
|
||||
import com.pixelized.rplexicon.facotry.model.ActionParser
|
||||
import com.pixelized.rplexicon.model.Action
|
||||
import com.pixelized.rplexicon.facotry.model.AttackParser
|
||||
import com.pixelized.rplexicon.model.Attack
|
||||
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
|
||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||
import com.pixelized.rplexicon.utilitary.exceptions.ServiceNotReady
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@Singleton
|
||||
class ActionRepository @Inject constructor(
|
||||
private val googleRepository: GoogleSheetServiceRepository,
|
||||
private val characterSheetRepository: CharacterSheetRepository,
|
||||
private val actionParser: ActionParser,
|
||||
private val actionParser: AttackParser,
|
||||
) {
|
||||
private val _data = MutableStateFlow<Map<String, List<Action>>>(emptyMap())
|
||||
val data: StateFlow<Map<String, List<Action>>> get() = _data
|
||||
private val _data = MutableStateFlow<Map<String, List<Attack>>>(emptyMap())
|
||||
val data: StateFlow<Map<String, List<Attack>>> get() = _data
|
||||
|
||||
fun find(name: String?): List<Action>? {
|
||||
fun find(name: String?): List<Attack>? {
|
||||
return name?.let { _data.value[it] }
|
||||
}
|
||||
|
||||
@Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class)
|
||||
suspend fun fetchActions() {
|
||||
googleRepository.fetch { sheet ->
|
||||
val request = sheet.get(Sheet.ID, Sheet.ACTIONS)
|
||||
val request = sheet.get(Sheet.Character.ID, Sheet.Character.ATTACK)
|
||||
val data = actionParser.parse(
|
||||
value = request.execute(),
|
||||
charactersSheets = characterSheetRepository.data.value
|
||||
|
|
@ -41,9 +34,4 @@ class ActionRepository @Inject constructor(
|
|||
_data.emit(data)
|
||||
}
|
||||
}
|
||||
|
||||
private object Sheet {
|
||||
const val ID = "1fHfzeb8y5u9lEQB1iI-jBEhqu7YSip5sAajXcXK7VJ8"
|
||||
const val ACTIONS = "Actions"
|
||||
}
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ class AlterationRepository @Inject constructor(
|
|||
@Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class)
|
||||
suspend fun fetchAlterationSheet() {
|
||||
googleRepository.fetch { sheet ->
|
||||
val request = sheet.get(Sheet.ID, Sheet.ALTERATION_SHEET)
|
||||
val request = sheet.get(Sheet.Character.ID, Sheet.Character.ALTERATION)
|
||||
val data = alterationParser.parse(value = request.execute())
|
||||
_alterations.emit(data)
|
||||
}
|
||||
|
|
@ -56,20 +56,11 @@ class AlterationRepository @Inject constructor(
|
|||
@Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class)
|
||||
suspend fun fetchStatusSheet() {
|
||||
googleRepository.fetch { sheet ->
|
||||
val request = sheet.get(Sheet.ID, Sheet.STATUS_SHEET)
|
||||
val request = sheet.get(Sheet.Character.ID, Sheet.Character.STATUS)
|
||||
val status = statusParser.parse(value = request.execute())
|
||||
_status.emit(status)
|
||||
val counter = counterParser.parse(values = request.execute())
|
||||
_counter.emit(counter)
|
||||
}
|
||||
}
|
||||
|
||||
private object Sheet {
|
||||
const val ID = "1fHfzeb8y5u9lEQB1iI-jBEhqu7YSip5sAajXcXK7VJ8"
|
||||
const val ALTERATION_SHEET = "Altérations"
|
||||
const val STATUS_SHEET = "État des personnages"
|
||||
const val STATUS_GID = "1246442302"
|
||||
|
||||
const val SHEET_URL = "https://docs.google.com/spreadsheets/d/${ID}/edit#gid=$STATUS_GID"
|
||||
}
|
||||
}
|
||||
|
|
@ -25,19 +25,13 @@ class CharacterSheetRepository @Inject constructor(
|
|||
@Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class)
|
||||
suspend fun fetchCharacterSheet() {
|
||||
googleRepository.fetch { sheet ->
|
||||
val request = sheet.get(Sheet.ID, Sheet.CHARACTER_SHEET)
|
||||
val request = sheet.get(Sheet.Character.ID, Sheet.Character.CHARACTER)
|
||||
val data = characterSheetParser.parse(value = request.execute())
|
||||
_data.emit(data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
const val TAG = "CharacterSheetRepository"
|
||||
}
|
||||
|
||||
private object Sheet {
|
||||
const val ID = "1fHfzeb8y5u9lEQB1iI-jBEhqu7YSip5sAajXcXK7VJ8"
|
||||
const val CHARACTER_SHEET = "Feuille de personnage"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.pixelized.rplexicon.repository.data
|
||||
|
||||
import com.google.api.services.sheets.v4.model.ValueRange
|
||||
import com.pixelized.rplexicon.facotry.model.LexiconParser
|
||||
import com.pixelized.rplexicon.model.Lexicon
|
||||
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
|
||||
|
|
@ -26,7 +25,7 @@ class LexiconRepository @Inject constructor(
|
|||
@Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class)
|
||||
suspend fun fetchLexicon() {
|
||||
googleRepository.fetch { sheet ->
|
||||
val request = sheet.get(Sheet.ID, Sheet.LEXICON)
|
||||
val request = sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.LEXICON)
|
||||
val data = lexiconParser.parse(data = request.execute())
|
||||
_data.tryEmit(data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.pixelized.rplexicon.repository.data
|
||||
|
||||
import com.google.api.services.sheets.v4.Sheets
|
||||
import com.google.api.services.sheets.v4.model.ValueRange
|
||||
import com.pixelized.rplexicon.facotry.model.LocationParser
|
||||
import com.pixelized.rplexicon.facotry.model.MarqueeParser
|
||||
|
|
@ -30,10 +29,10 @@ class LocationRepository @Inject constructor(
|
|||
|
||||
@Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class)
|
||||
suspend fun fetchLocation() {
|
||||
googleRepository.fetch { sheet: Sheets.Spreadsheets.Values ->
|
||||
googleRepository.fetch { sheet ->
|
||||
val (map, marquee) = awaitAll(
|
||||
async { sheet.get(Sheet.ID, Sheet.MAP).execute() },
|
||||
async { sheet.get(Sheet.ID, Sheet.MARQUEE).execute() },
|
||||
async { sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.MAP).execute() },
|
||||
async { sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.MARQUEE).execute() },
|
||||
)
|
||||
updateData(map = map, marquee = marquee)
|
||||
}
|
||||
|
|
@ -61,6 +60,7 @@ class LocationRepository @Inject constructor(
|
|||
|
||||
companion object {
|
||||
private const val TAG = "LocationRepository"
|
||||
const val SHEET_URL = "https://docs.google.com/spreadsheets/d/${Sheet.ID}/edit#gid=1985553511"
|
||||
const val SHEET_URL =
|
||||
"https://docs.google.com/spreadsheets/d/${Sheet.Lexicon.ID}/edit#gid=1985553511"
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ class QuestRepository @Inject constructor(
|
|||
@Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class)
|
||||
suspend fun fetchQuests() {
|
||||
googleRepository.fetch { sheet ->
|
||||
val request = sheet.get(Sheet.ID, Sheet.QUEST_JOURNAL)
|
||||
val request = sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.QUEST_JOURNAL)
|
||||
val data = request.execute()
|
||||
updateData(data = data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,23 @@
|
|||
package com.pixelized.rplexicon.repository.data
|
||||
|
||||
object Sheet {
|
||||
const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs"
|
||||
object Lexicon {
|
||||
const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs"
|
||||
|
||||
const val LEXICON = "Lexique"
|
||||
const val QUEST_JOURNAL = "Journal de quêtes"
|
||||
const val LEXICON = "Lexique"
|
||||
const val QUEST_JOURNAL = "Journal de quêtes"
|
||||
const val MAP = "Lieux"
|
||||
const val MARQUEE = "Points d'intérêt"
|
||||
}
|
||||
|
||||
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 MAGIC_LEXICON = "Lexique magique"
|
||||
const val STATUS = "État des personnages"
|
||||
const val ALTERATION = "Altérations"
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import androidx.compose.animation.AnimatedContent
|
|||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.animation.with
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
|
@ -94,7 +95,7 @@ fun <T> DropDownField(
|
|||
AnimatedContent(
|
||||
modifier = Modifier.size(size = 48.dp),
|
||||
targetState = field.value.value != null,
|
||||
transitionSpec = { fadeIn() with fadeOut() },
|
||||
transitionSpec = { fadeIn() togetherWith fadeOut() },
|
||||
label = "DropDownFieldTrailingIconAnimation",
|
||||
) {
|
||||
when (it) {
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ import androidx.compose.foundation.layout.systemBarsPadding
|
|||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
|
|
@ -50,15 +48,15 @@ import com.pixelized.rplexicon.NO_WINDOW_INSETS
|
|||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.composable.Loader
|
||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.ActionsUio
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.CounterUio
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.LabelPoint
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.LabelPointUio
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.ProficiencyUio
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.StatUio
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.WeaponUio
|
||||
import com.pixelized.rplexicon.ui.screens.character.pages.ActionsPages
|
||||
import com.pixelized.rplexicon.ui.screens.character.pages.ProficiencyPage
|
||||
import com.pixelized.rplexicon.ui.screens.character.preview.rememberCharacterSheetPreview
|
||||
import com.pixelized.rplexicon.ui.screens.character.preview.rememberWeaponListStatePreview
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -96,19 +94,13 @@ fun CharacterSheetScreen(
|
|||
.fillMaxSize()
|
||||
.systemBarsPadding(),
|
||||
pagerState = rememberPagerState {
|
||||
if (viewModel.actions.value.isNotEmpty() || viewModel.counter.value.isNotEmpty()) {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
}
|
||||
2 // TODO
|
||||
},
|
||||
refreshing = viewModel.isLoading,
|
||||
refreshState = refresh,
|
||||
onRefresh = { scope.launch { viewModel.update() } },
|
||||
sheet = it,
|
||||
actions = viewModel.actions,
|
||||
counter = viewModel.counter,
|
||||
alterations = viewModel.alterations,
|
||||
weapons = viewModel.weapons,
|
||||
onBack = {
|
||||
screen.popBackStack()
|
||||
},
|
||||
|
|
@ -191,9 +183,7 @@ private fun CharacterSheetContent(
|
|||
refreshing: State<Boolean>,
|
||||
onRefresh: () -> Unit,
|
||||
sheet: CharacterSheetUio,
|
||||
actions: State<List<ActionsUio>>,
|
||||
counter: State<List<CounterUio>>,
|
||||
alterations: State<List<String>>,
|
||||
weapons: State<List<WeaponUio>>,
|
||||
onBack: () -> Unit,
|
||||
onStats: (StatUio) -> Unit,
|
||||
onProficiencies: (ProficiencyUio) -> Unit,
|
||||
|
|
@ -270,7 +260,6 @@ private fun CharacterSheetContent(
|
|||
0 -> ProficiencyPage(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(state = rememberScrollState())
|
||||
.padding(vertical = 16.dp),
|
||||
sheet = sheet,
|
||||
onInitiative = onInitiative,
|
||||
|
|
@ -281,11 +270,8 @@ private fun CharacterSheetContent(
|
|||
1 -> ActionsPages(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(state = rememberScrollState())
|
||||
.padding(vertical = 16.dp),
|
||||
actions = actions,
|
||||
counter = counter,
|
||||
alterations = alterations,
|
||||
weapons = weapons,
|
||||
onHit = onHit,
|
||||
onDamage = onDamage,
|
||||
)
|
||||
|
|
@ -317,36 +303,7 @@ private fun CharacterScreenPreview() {
|
|||
refreshing = remember { mutableStateOf(false) },
|
||||
onRefresh = { },
|
||||
sheet = rememberCharacterSheetPreview(),
|
||||
actions = remember {
|
||||
mutableStateOf(
|
||||
listOf(
|
||||
ActionsUio(
|
||||
title = "Battle Axe",
|
||||
hit = R.drawable.ic_d20_24,
|
||||
damage = R.drawable.ic_d8_24,
|
||||
),
|
||||
ActionsUio(
|
||||
title = "Greataxe",
|
||||
hit = R.drawable.ic_d20_24,
|
||||
damage = R.drawable.ic_d12_24,
|
||||
),
|
||||
)
|
||||
)
|
||||
},
|
||||
counter = remember {
|
||||
mutableStateOf(
|
||||
listOf(
|
||||
CounterUio(
|
||||
title = "Rage",
|
||||
value = 1,
|
||||
max = 2,
|
||||
),
|
||||
)
|
||||
)
|
||||
},
|
||||
alterations = remember {
|
||||
mutableStateOf(listOf("Rage", "Attaque téméraire"))
|
||||
},
|
||||
weapons = rememberWeaponListStatePreview(),
|
||||
onBack = { },
|
||||
onInitiative = { },
|
||||
onStats = { },
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import androidx.lifecycle.AndroidViewModel
|
|||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.facotry.displayable.ConvertActionIntoDisplayableFactory
|
||||
import com.pixelized.rplexicon.facotry.displayable.ConvertAttackIntoDisplayableFactory
|
||||
import com.pixelized.rplexicon.facotry.displayable.ConvertCharacterSheetIntoDisplayableFactory
|
||||
import com.pixelized.rplexicon.facotry.displayable.ConvertCounterIntoDisplayableFactory
|
||||
import com.pixelized.rplexicon.model.Action
|
||||
import com.pixelized.rplexicon.model.Attack
|
||||
import com.pixelized.rplexicon.model.CharacterSheet
|
||||
import com.pixelized.rplexicon.model.Property
|
||||
import com.pixelized.rplexicon.model.Roll
|
||||
|
|
@ -23,6 +23,7 @@ import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
|
|||
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.ActionsUio
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.CounterUio
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.WeaponUio
|
||||
import com.pixelized.rplexicon.utilitary.extentions.context
|
||||
import com.pixelized.rplexicon.utilitary.extentions.modifier
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -38,7 +39,7 @@ class CharacterSheetViewModel @Inject constructor(
|
|||
savedStateHandle: SavedStateHandle,
|
||||
private val characterSheetFactory: ConvertCharacterSheetIntoDisplayableFactory,
|
||||
private val counterFactory: ConvertCounterIntoDisplayableFactory,
|
||||
private val actionFactory: ConvertActionIntoDisplayableFactory,
|
||||
private val weaponFactory: ConvertAttackIntoDisplayableFactory,
|
||||
private val characterRepository: CharacterSheetRepository,
|
||||
private val alterationRepository: AlterationRepository,
|
||||
private val actionRepository: ActionRepository,
|
||||
|
|
@ -49,6 +50,7 @@ class CharacterSheetViewModel @Inject constructor(
|
|||
val sheet: State<CharacterSheetUio?>
|
||||
val alterations: State<List<String>>
|
||||
val actions: State<List<ActionsUio>>
|
||||
val weapons: State<List<WeaponUio>>
|
||||
val counter: State<List<CounterUio>>
|
||||
|
||||
private val _isLoading = mutableStateOf(false)
|
||||
|
|
@ -72,8 +74,14 @@ class CharacterSheetViewModel @Inject constructor(
|
|||
} ?: emptyList()
|
||||
)
|
||||
actions = mutableStateOf(
|
||||
actionRepository.find(name = argument.name)?.map {
|
||||
actionFactory.toUio(action = it)
|
||||
actionRepository.find(name = argument.name)?.mapNotNull {
|
||||
// actionFactory.toUio(attack = it)
|
||||
null
|
||||
} ?: emptyList()
|
||||
)
|
||||
weapons = mutableStateOf(
|
||||
actionRepository.find(name = argument.name)?.mapNotNull {
|
||||
weaponFactory.toUio(attack = it, characterSheet = model)
|
||||
} ?: emptyList()
|
||||
)
|
||||
|
||||
|
|
@ -86,8 +94,11 @@ class CharacterSheetViewModel @Inject constructor(
|
|||
}
|
||||
launch {
|
||||
actionRepository.data.collect {
|
||||
actions.value = it[argument.name]?.map { action ->
|
||||
actionFactory.toUio(action = action)
|
||||
// actions.value = it[argument.name]?.mapNotNull { action ->
|
||||
// actionFactory.toUio(attack = action)
|
||||
// } ?: emptyList()
|
||||
weapons.value = it[argument.name]?.mapNotNull { action ->
|
||||
weaponFactory.toUio(attack = action, characterSheet = model)
|
||||
} ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
|
@ -139,7 +150,7 @@ class CharacterSheetViewModel @Inject constructor(
|
|||
fun onHitRoll(id: String): Roll {
|
||||
val action = actionRepository.find(argument.name)?.firstOrNull { it.title == id }
|
||||
return actionRoll(
|
||||
action = action,
|
||||
attack = action,
|
||||
throws = action?.hit,
|
||||
)
|
||||
}
|
||||
|
|
@ -147,7 +158,7 @@ class CharacterSheetViewModel @Inject constructor(
|
|||
fun onDamageRoll(id: String): Roll {
|
||||
val action = actionRepository.find(argument.name)?.firstOrNull { it.title == id }
|
||||
return actionRoll(
|
||||
action = action,
|
||||
attack = action,
|
||||
throws = action?.damage,
|
||||
)
|
||||
}
|
||||
|
|
@ -519,44 +530,41 @@ class CharacterSheetViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun actionRoll(
|
||||
action: Action?,
|
||||
throws: Action.Throw?,
|
||||
attack: Attack?,
|
||||
throws: Attack.Throw?,
|
||||
): Roll {
|
||||
// build the title
|
||||
val title = context.getString(
|
||||
when (action?.type) {
|
||||
Action.Type.SPELL -> when (throws === action.hit) {
|
||||
true -> R.string.dice_roll_spell_hit_title
|
||||
else -> R.string.dice_roll_spell_damage_title
|
||||
}
|
||||
|
||||
else -> when (throws === action?.hit) {
|
||||
when (attack?.type) {
|
||||
else -> when (throws === attack?.hit) {
|
||||
true -> R.string.dice_roll_attack_hit_title
|
||||
else -> R.string.dice_roll_attack_damage_title
|
||||
}
|
||||
},
|
||||
action?.title,
|
||||
attack?.title,
|
||||
)
|
||||
// get the alteration for roll and a given player
|
||||
val alterations = when (action?.type) {
|
||||
Action.Type.PHYSICAL_MELEE_ATTACK -> {
|
||||
val alterations = when (attack?.type) {
|
||||
Attack.Type.PHYSICAL_MELEE_ATTACK -> {
|
||||
alterationRepository.getStatus(
|
||||
character = model.name,
|
||||
property = when (throws === action.hit) {
|
||||
property = when (throws === attack.hit) {
|
||||
true -> Property.PHYSICAL_MELEE_ATTACK
|
||||
else -> Property.PHYSICAL_MELEE_DAMAGE
|
||||
},
|
||||
)
|
||||
}
|
||||
Action.Type.PHYSICAL_RANGE_ATTACK -> {
|
||||
|
||||
Attack.Type.PHYSICAL_RANGE_ATTACK -> {
|
||||
alterationRepository.getStatus(
|
||||
character = model.name,
|
||||
property = when (throws === action.hit) {
|
||||
property = when (throws === attack.hit) {
|
||||
true -> Property.PHYSICAL_RANGE_ATTACK
|
||||
else -> Property.PHYSICAL_RANGE_DAMAGE
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
emptyList()
|
||||
}
|
||||
|
|
@ -568,10 +576,10 @@ class CharacterSheetViewModel @Inject constructor(
|
|||
// build the roll
|
||||
return Roll(
|
||||
title = title,
|
||||
highlight = action?.title,
|
||||
highlight = attack?.title,
|
||||
dices = listOf(
|
||||
Roll.Dice(
|
||||
title = action?.title,
|
||||
title = attack?.title,
|
||||
advantage = advantage,
|
||||
disadvantage = disadvantage,
|
||||
fail = fail,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
package com.pixelized.rplexicon.ui.screens.character.composable
|
||||
|
||||
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.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
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.Stable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
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.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.model.Attack
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
|
||||
@Stable
|
||||
data class WeaponUio(
|
||||
val id: Attack? = null,
|
||||
@DrawableRes val icon: Int,
|
||||
val name: String,
|
||||
val type: String,
|
||||
val range: String,
|
||||
val hit: Dice?,
|
||||
val damage: Dice?,
|
||||
) {
|
||||
@Stable
|
||||
class Dice(
|
||||
@DrawableRes val icon: Int,
|
||||
val label: String,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Weapon(
|
||||
modifier: Modifier = Modifier,
|
||||
weapon: WeaponUio,
|
||||
onHit: (String) -> Unit,
|
||||
onDamage: (String) -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = weapon.icon),
|
||||
contentDescription = null,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.weight(weight = 1f),
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = weapon.name,
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
fontStyle = FontStyle.Italic,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = weapon.type,
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
fontStyle = FontStyle.Italic,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = weapon.range,
|
||||
)
|
||||
}
|
||||
weapon.hit?.let { dice ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.sizeIn(minWidth = 32.dp)
|
||||
.clickable { weapon.name.let(onHit) },
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = dice.icon),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
text = dice.label,
|
||||
)
|
||||
}
|
||||
}
|
||||
weapon.damage?.let { dice ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.sizeIn(minWidth = 42.dp)
|
||||
.clickable { weapon.name.let(onDamage) },
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = dice.icon),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
text = dice.label,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = UI_MODE_NIGHT_NO)
|
||||
@Preview(uiMode = UI_MODE_NIGHT_YES)
|
||||
private fun WeaponPreview(
|
||||
@PreviewParameter(WeaponPreviewProvider::class) preview: WeaponUio,
|
||||
) {
|
||||
LexiconTheme {
|
||||
Surface {
|
||||
Weapon(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
weapon = preview,
|
||||
onHit = { },
|
||||
onDamage = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class WeaponPreviewProvider : PreviewParameterProvider<WeaponUio> {
|
||||
override val values: Sequence<WeaponUio> = sequenceOf(
|
||||
WeaponUio(
|
||||
icon = R.drawable.ic_crossed_swords_24,
|
||||
name = "Dagger",
|
||||
type = "Melee weapon",
|
||||
range = "5 ft reach",
|
||||
hit = WeaponUio.Dice(icon = R.drawable.ic_d20_24, label = "1d20"),
|
||||
damage = WeaponUio.Dice(icon = R.drawable.ic_d8_24, label = "1d8"),
|
||||
),
|
||||
WeaponUio(
|
||||
icon = R.drawable.ic_pocket_bow_24,
|
||||
name = "Long bow",
|
||||
type = "Ranged weapon",
|
||||
range = "30 ft reach",
|
||||
hit = WeaponUio.Dice(icon = R.drawable.ic_d20_24, label = "1d20+5"),
|
||||
damage = WeaponUio.Dice(icon = R.drawable.ic_d8_24, label = "1d8+3"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,66 +1,38 @@
|
|||
package com.pixelized.rplexicon.ui.screens.character.pages
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.Action
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.ActionsUio
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.Counter
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.CounterUio
|
||||
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.Weapon
|
||||
import com.pixelized.rplexicon.ui.screens.character.composable.WeaponUio
|
||||
|
||||
@Composable
|
||||
fun ActionsPages(
|
||||
modifier: Modifier = Modifier,
|
||||
actions: State<List<ActionsUio>>,
|
||||
counter: State<List<CounterUio>>,
|
||||
alterations: State<List<String>>,
|
||||
lazyListState: LazyListState = rememberLazyListState(),
|
||||
weapons: State<List<WeaponUio>>,
|
||||
onHit: (id: String) -> Unit,
|
||||
onDamage: (id: String) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
state = lazyListState,
|
||||
verticalArrangement = Arrangement.spacedBy(space = 16.dp),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
counter.value.forEach {
|
||||
Counter(
|
||||
counter = it,
|
||||
)
|
||||
}
|
||||
actions.value.forEach {
|
||||
Action(
|
||||
action = it,
|
||||
onHit = onHit,
|
||||
onDamage = onDamage,
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.ddBorder(
|
||||
inner = remember { RoundedCornerShape(size = 8.dp) },
|
||||
outline = remember { CutCornerShape(size = 16.dp) },
|
||||
)
|
||||
.padding(all = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
alterations.value.forEach {
|
||||
Text(
|
||||
text = it
|
||||
)
|
||||
}
|
||||
items(items = weapons.value) {
|
||||
Weapon(
|
||||
weapon = it,
|
||||
onHit = onHit,
|
||||
onDamage = onDamage,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.pixelized.rplexicon.ui.screens.character.pages
|
|||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -9,8 +10,10 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -40,6 +43,7 @@ import com.pixelized.rplexicon.utilitary.extentions.ddBorder
|
|||
@Composable
|
||||
fun ProficiencyPage(
|
||||
modifier: Modifier = Modifier,
|
||||
state: ScrollState = rememberScrollState(),
|
||||
inner: Shape = remember { RoundedCornerShape(size = 8.dp) },
|
||||
outline: Shape = remember { CutCornerShape(size = 16.dp) },
|
||||
sheet: CharacterSheetUio,
|
||||
|
|
@ -47,7 +51,7 @@ fun ProficiencyPage(
|
|||
onStats: (StatUio) -> Unit,
|
||||
onProficiencies: (ProficiencyUio) -> Unit,
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
Box(modifier = modifier.verticalScroll(state = state)) {
|
||||
ProficiencyLayout(
|
||||
inner = inner,
|
||||
outline = outline,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package com.pixelized.rplexicon.ui.screens.character.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.WeaponUio
|
||||
|
||||
@Stable
|
||||
@Composable
|
||||
fun rememberWeaponListStatePreview(): State<List<WeaponUio>> = remember {
|
||||
mutableStateOf(
|
||||
listOf(
|
||||
WeaponUio(
|
||||
icon = R.drawable.ic_crossed_swords_24,
|
||||
name = "Dagger",
|
||||
type = "Melee weapon",
|
||||
range = "5 ft reach",
|
||||
hit = WeaponUio.Dice(icon = R.drawable.ic_d20_24, label = "1d20"),
|
||||
damage = WeaponUio.Dice(icon = R.drawable.ic_d8_24, label = "1d8"),
|
||||
),
|
||||
WeaponUio(
|
||||
icon = R.drawable.ic_pocket_bow_24,
|
||||
name = "Long bow",
|
||||
type = "Ranged weapon",
|
||||
range = "30 ft reach",
|
||||
hit = WeaponUio.Dice(icon = R.drawable.ic_d20_24, label = "1d20+5"),
|
||||
damage = WeaponUio.Dice(
|
||||
icon = R.drawable.ic_d8_24,
|
||||
label = "1d8+3"
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue