Add script in alteration attack skill and spells

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2024-06-03 17:24:56 +02:00
parent 9718056260
commit 8e9529d4d0
17 changed files with 188 additions and 36 deletions

View file

@ -1,10 +1,12 @@
package com.pixelized.rplexicon.data.model
import com.pixelized.rplexicon.data.model.roll.Throw
import com.pixelized.rplexicon.script.Script
data class AssignedSpell(
val hit: Throw?,
val effect: Throw?,
val level: Throw?,
val spell: Spell,
val script: Script?,
)

View file

@ -2,14 +2,16 @@ package com.pixelized.rplexicon.data.model
import android.net.Uri
import com.pixelized.rplexicon.data.model.roll.Throw
import com.pixelized.rplexicon.script.Script
data class Attack(
val icon: Uri?,
val title: String,
val type: Type,
val range: String?,
val hit: Throw?,
val damage: Throw?,
val script: Script?,
val icon: Uri?,
val alterations: List<String>,
) {
enum class Type(val key: String) {

View file

@ -2,13 +2,15 @@ package com.pixelized.rplexicon.data.model
import android.net.Uri
import com.pixelized.rplexicon.data.model.roll.Throw
import com.pixelized.rplexicon.script.Script
data class Skill(
val icon: Uri?,
val name: String,
val amount: Int?,
val rest: String?,
val cost: String?,
val effect: Throw?,
val passive: Boolean,
val script: Script?,
val icon: Uri?,
)

View file

@ -4,12 +4,14 @@ import android.net.Uri
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.roll.Dice
import com.pixelized.rplexicon.data.model.roll.Flat
import com.pixelized.rplexicon.script.Script
data class Alteration(
val icon: Uri?,
val name: String,
val source: String,
val target: String,
val script: Script?,
val icon: Uri?,
val status: Map<Property, Status>,
) {
data class Status(

View file

@ -5,11 +5,13 @@ import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.data.model.Attack
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.parser.roll.ThrowParser
import com.pixelized.rplexicon.script.ScriptParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class AttackParser @Inject constructor(
private val throwParser: ThrowParser,
private val scriptParser: ScriptParser,
) {
@Throws(IncompatibleSheetStructure::class)
fun parse(
@ -33,6 +35,7 @@ class AttackParser @Inject constructor(
title = title,
type = parseType(value = row.parse(column = TYPE)),
range = row.parse(column = RANGE),
script = row.parse(column = SCRIPT)?.let { scriptParser.parse(it) },
hit = throwParser.parse(value = row.parse(column = HIT)),
damage = throwParser.parse(value = row.parse(column = DAMAGE)),
alterations = row.parseList(column = ALTERATION),
@ -64,6 +67,18 @@ class AttackParser @Inject constructor(
private val DAMAGE = column("Dommage")
private val ALTERATION = column("Altérations")
private val ICON = column("Icone")
private val COLUMNS get() = listOf(CHARACTER, NAME, TYPE, RANGE, HIT, DAMAGE, ALTERATION, ICON)
private val SCRIPT = column("Script")
private val COLUMNS
get() = listOf(
CHARACTER,
NAME,
TYPE,
RANGE,
HIT,
DAMAGE,
ALTERATION,
SCRIPT,
ICON
)
}
}

View file

@ -4,11 +4,13 @@ import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.parser.roll.ThrowParser
import com.pixelized.rplexicon.script.ScriptParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class SkillParser @Inject constructor(
private val throwParser: ThrowParser,
private val scriptParser: ScriptParser,
) {
@Throws(IncompatibleSheetStructure::class)
fun parse(
@ -32,7 +34,8 @@ class SkillParser @Inject constructor(
rest = row.parse(column = REST),
cost = row.parse(column = COST),
effect = throwParser.parse(row.parse(column = EFFECT)),
passive = row.parseBool(column = PASSIVE) ?: false
passive = row.parseBool(column = PASSIVE) ?: false,
script = row.parse(column = SCRIPT)?.let { scriptParser.parse(it) },
)
skills.getOrPut(characterSheet.name) { mutableListOf() }.add(skill)
}
@ -52,7 +55,19 @@ class SkillParser @Inject constructor(
private val COST = column("Coût")
private val EFFECT = column("Effect")
private val PASSIVE = column("Passif")
private val SCRIPT = column("Script")
private val COLUMNS get() = listOf(CHARACTER, ICON, NAME, AMOUNT, REST, COST, EFFECT, PASSIVE)
private val COLUMNS
get() = listOf(
CHARACTER,
ICON,
NAME,
AMOUNT,
REST,
COST,
EFFECT,
PASSIVE,
SCRIPT
)
}
}

View file

@ -1,19 +1,21 @@
package com.pixelized.rplexicon.data.parser.alteration
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.data.model.alteration.Alteration
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.alteration.Alteration
import com.pixelized.rplexicon.data.parser.column
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.data.parser.roll.DiceParser
import com.pixelized.rplexicon.data.parser.roll.FlatValueParser
import com.pixelized.rplexicon.script.ScriptParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class AlterationParser @Inject constructor(
private val diceParser: DiceParser,
private val flatParser: FlatValueParser,
private val scriptParser: ScriptParser,
) {
@Throws(IncompatibleSheetStructure::class)
fun parse(
@ -37,6 +39,7 @@ class AlterationParser @Inject constructor(
name = name,
source = source,
target = target,
script = row.parse(column = SCRIPT)?.let { scriptParser.parse(it) },
status = properties.mapNotNull { property ->
val column = column(property.key)
row.parse(column = column)
@ -95,7 +98,14 @@ class AlterationParser @Inject constructor(
private val TARGET = column("Cible")
private val SOURCE = column("Source")
private val ICON = column("Icone")
private val SCRIPT = column("Script")
private val COLUMNS
get() = listOf(NAME, SOURCE, TARGET, ICON) + Property.entries.map { column(it.key) }
get() = listOf(
NAME,
SOURCE,
TARGET,
SCRIPT,
ICON
) + Property.entries.map { column(it.key) }
}
}

View file

@ -6,11 +6,13 @@ import com.pixelized.rplexicon.data.model.Spell
import com.pixelized.rplexicon.data.parser.column
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.data.parser.roll.ThrowParser
import com.pixelized.rplexicon.script.ScriptParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class AssignedSpellParser @Inject constructor(
private val throwParser: ThrowParser,
private val scriptParser: ScriptParser,
) {
@Throws(IncompatibleSheetStructure::class)
fun parse(
@ -31,6 +33,7 @@ class AssignedSpellParser @Inject constructor(
effect = throwParser.parse(value = row.parse(EFFECT)),
level = throwParser.parse(value = row.parse(LEVEL)),
spell = spell,
script = row.parse(SCRIPT)?.let { scriptParser.parse(it) }
)
assignedSpells
.getOrPut(key = character) { mutableListOf() }
@ -47,7 +50,8 @@ class AssignedSpellParser @Inject constructor(
private val HIT = column("Touché")
private val EFFECT = column("Effet")
private val LEVEL = column("Par niveau")
private val SCRIPT = column("Script")
private val COLUMNS get() = listOf(NAME, HIT, EFFECT, LEVEL)
private val COLUMNS get() = listOf(NAME, HIT, EFFECT, LEVEL, SCRIPT)
}
}

View file

@ -5,10 +5,18 @@ import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.parser.AttackParser
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
import javax.inject.Singleton
@ -16,15 +24,29 @@ import javax.inject.Singleton
class ActionRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val actionParser: AttackParser,
firebaseRepository: FirebaseRepository,
) {
private val _data = MutableStateFlow<Map<String, List<Attack>>>(emptyMap())
val data: StateFlow<Map<String, List<Attack>>> get() = _data
private val _actions = MutableStateFlow<Map<String, List<Attack>>>(emptyMap())
val actions: StateFlow<Map<String, List<Attack>>> =
combine(_actions, firebaseRepository.getAlterationStatus()) { actions, status ->
actions.mapValues { entry: Map.Entry<String, List<Attack>> ->
val executor = ScriptExecutor(
character = entry.key,
alterations = status,
)
entry.value.filter { it.script == null || it.script.predicate(executor) }
}
}.stateIn(
scope = CoroutineScope(Dispatchers.Default + Job()),
started = SharingStarted.Lazily,
initialValue = emptyMap()
)
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
fun find(character: String?): List<Attack>? {
return character?.let { _data.value[it] }
return character?.let { actions.value[it] }
}
fun find(character: String?, action: String): Attack? {
@ -39,7 +61,7 @@ class ActionRepository @Inject constructor(
value = request.execute(),
charactersSheets = characters
)
_data.emit(data)
_actions.emit(data)
lastSuccessFullUpdate = Update.currentTime()
}
}

View file

@ -6,12 +6,20 @@ import com.pixelized.rplexicon.data.model.alteration.Alteration
import com.pixelized.rplexicon.data.parser.alteration.AlterationParser
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
import javax.inject.Singleton
@ -19,8 +27,23 @@ import javax.inject.Singleton
class AlterationRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val alterationParser: AlterationParser,
firebaseRepository: FirebaseRepository,
) {
private val assignedAlterations = MutableStateFlow<Map<String, List<Alteration>>>(emptyMap())
private val alterations = MutableStateFlow<Map<String, List<Alteration>>>(emptyMap())
private val assignedAlterations: StateFlow<Map<String, List<Alteration>>> =
combine(alterations, firebaseRepository.getAlterationStatus()) { alterations, status ->
alterations.mapValues { entry: Map.Entry<String, List<Alteration>> ->
val executor = ScriptExecutor(
character = entry.key,
alterations = status,
)
entry.value.filter { it.script == null || it.script.predicate(executor) }
}
}.stateIn(
scope = CoroutineScope(Dispatchers.Default + Job()),
started = SharingStarted.Lazily,
initialValue = emptyMap()
)
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
@ -37,9 +60,8 @@ class AlterationRepository @Inject constructor(
* get an alteration for a character that have [alteration] as namer.
* @return a nullable [Alteration]
*/
suspend fun getAssignedAlterationSnapshot(character: String?, alteration: String?): Alteration? {
return assignedAlterations.firstOrNull()?.get(character)
?.firstOrNull { it.name == alteration }
fun getAssignedAlterationSnapshot(character: String?, alteration: String?): Alteration? {
return assignedAlterations.value[character]?.firstOrNull { it.name == alteration }
}
/**
@ -75,7 +97,7 @@ class AlterationRepository @Inject constructor(
googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.ALTERATION)
val data = alterationParser.parse(sheet = request.execute(), characterSheets = sheets)
assignedAlterations.tryEmit(data)
alterations.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime()
}
}

View file

@ -5,10 +5,18 @@ import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.parser.SkillParser
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
import javax.inject.Singleton
@ -16,9 +24,23 @@ import javax.inject.Singleton
class SkillRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val skillParser: SkillParser,
firebaseRepository: FirebaseRepository,
) {
private val _skills = MutableStateFlow<Map<String, List<Skill>>>(emptyMap())
val skills: StateFlow<Map<String, List<Skill>>> get() = _skills
val skills: StateFlow<Map<String, List<Skill>>> =
combine(_skills, firebaseRepository.getAlterationStatus()) { skills, status ->
skills.mapValues { entry: Map.Entry<String, List<Skill>> ->
val executor = ScriptExecutor(
character = entry.key,
alterations = status,
)
entry.value.filter { it.script == null || it.script.predicate(executor) }
}
}.stateIn(
scope = CoroutineScope(Dispatchers.Default + Job()),
started = SharingStarted.Lazily,
initialValue = emptyMap()
)
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
@ -35,7 +57,6 @@ class SkillRepository @Inject constructor(
sheet = request.execute(),
charactersSheets = characters,
)
_skills.emit(skills)
lastSuccessFullUpdate = Update.currentTime()
}

View file

@ -6,12 +6,20 @@ import com.pixelized.rplexicon.data.parser.spell.AssignedSpellParser
import com.pixelized.rplexicon.data.parser.spell.SpellBookParser
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
import javax.inject.Singleton
@ -20,11 +28,25 @@ class SpellRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val spellBookParser: SpellBookParser,
private val assignedSpellParser: AssignedSpellParser,
firebaseRepository: FirebaseRepository,
) {
private var _spellsBook = MutableStateFlow<List<Spell>>(emptyList())
val spellsBook: StateFlow<List<Spell>> get() = _spellsBook
private val _spells = MutableStateFlow<Map<String, List<AssignedSpell>>>(emptyMap())
val spells: StateFlow<Map<String, List<AssignedSpell>>> get() = _spells
val spells: StateFlow<Map<String, List<AssignedSpell>>> =
combine(_spells, firebaseRepository.getAlterationStatus()) { spells, status ->
spells.mapValues { entry: Map.Entry<String, List<AssignedSpell>> ->
val executor = ScriptExecutor(
character = entry.key,
alterations = status,
)
entry.value.filter { it.script == null || it.script.predicate(executor) }
}
}.stateIn(
scope = CoroutineScope(Dispatchers.Default + Job()),
started = SharingStarted.Lazily,
initialValue = emptyMap()
)
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
@ -34,7 +56,7 @@ class SpellRepository @Inject constructor(
}
fun findAssignedSpell(character: String?): List<AssignedSpell>? {
return character?.let { _spells.value[it] }
return character?.let { spells.value[it] }
}
fun findAssignedSpell(character: String?, spell: String): AssignedSpell? {

View file

@ -1,6 +1,6 @@
package com.pixelized.rplexicon.script
import com.pixelized.rplexicon.data.model.alteration.Alteration
import com.pixelized.rplexicon.data.model.alteration.AlterationStatus
sealed class Script(
val predicate: ScriptExecutor.() -> Boolean
@ -36,7 +36,7 @@ sealed class Script(
class ACTIVE(
val alteration: String,
) : Script(predicate = {
alterations.any { it.name == alteration }
alterations[AlterationStatus.Key(character, alteration)]?.value == true
}) {
override fun toString(): String {
return "ACTIVE('$alteration')"
@ -46,7 +46,9 @@ sealed class Script(
class ANY(
val alteration: String,
) : Script(predicate = {
alterations.any { it.name.contains(alteration) }
alterations.any { entry ->
entry.key.character == character && entry.key.alteration.contains(alteration) && entry.value.value
}
}) {
override fun toString(): String {
return "ANY('$alteration')"
@ -56,7 +58,7 @@ sealed class Script(
class NOT(
val alteration: String,
) : Script(predicate = {
alterations.none { it.name == alteration }
alterations[AlterationStatus.Key(character, alteration)]?.value != true
}) {
override fun toString(): String {
return "NOT('$alteration')"
@ -66,7 +68,9 @@ sealed class Script(
class NONE(
val alteration: String,
) : Script(predicate = {
alterations.none { it.name.contains(alteration) }
alterations.none { entry ->
entry.key.character == character && entry.key.alteration.contains(alteration) && entry.value.value
}
}) {
override fun toString(): String {
return "NONE('$alteration')"
@ -77,12 +81,17 @@ sealed class Script(
}
class ScriptExecutor(
val alterations: List<Alteration>,
val character: String,
val alterations: Map<AlterationStatus.Key, AlterationStatus.Value>,
)
fun scriptExecutor(
alterations: List<Alteration>,
character: String,
alterations: Map<AlterationStatus.Key, AlterationStatus.Value>,
block: ScriptExecutor.() -> Boolean
): Boolean {
return ScriptExecutor(alterations = alterations).run(block)
return ScriptExecutor(
character = character,
alterations = alterations,
).run(block)
}

View file

@ -1,6 +1,8 @@
package com.pixelized.rplexicon.script
class ScriptParser {
import javax.inject.Inject
class ScriptParser @Inject constructor() {
companion object {
private val FUNCTION_REGEX = Regex("""^(\w+)\((?:(.*);(.*)|(.*))\)$""")

View file

@ -43,7 +43,7 @@ class AttacksViewModel @Inject constructor(
viewModelScope.launch(Dispatchers.Default) {
val struct = ActionStruct()
characterRepository.data
.combine(actionRepository.data) { characters, actions ->
.combine(actionRepository.actions) { characters, actions ->
struct.character = characters[character]
struct.actions = actions[character] ?: emptyList()
}

View file

@ -1,4 +1,3 @@
package com.pixelized.rplexicon.ui.screens.character.pages.alteration
import android.content.res.Configuration.UI_MODE_NIGHT_NO
@ -80,7 +79,10 @@ fun AlterationPageContent(
)
}
items(items = group.alterations) { alteration ->
items(
items = group.alterations,
key = { "${group.name}-${it.label}" },
) { alteration ->
AlterationItem(
modifier = Modifier.fillMaxWidth(),
alteration = alteration,

View file

@ -94,7 +94,7 @@ class AlterationViewModel @Inject constructor(
)
}
suspend fun showAlterationDetail(id: String) {
fun showAlterationDetail(id: String) {
val alteration = alterationRepository.getAssignedAlterationSnapshot(
character = character,
alteration = id,