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 package com.pixelized.rplexicon.data.model
import com.pixelized.rplexicon.data.model.roll.Throw import com.pixelized.rplexicon.data.model.roll.Throw
import com.pixelized.rplexicon.script.Script
data class AssignedSpell( data class AssignedSpell(
val hit: Throw?, val hit: Throw?,
val effect: Throw?, val effect: Throw?,
val level: Throw?, val level: Throw?,
val spell: Spell, val spell: Spell,
val script: Script?,
) )

View file

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

View file

@ -2,13 +2,15 @@ package com.pixelized.rplexicon.data.model
import android.net.Uri import android.net.Uri
import com.pixelized.rplexicon.data.model.roll.Throw import com.pixelized.rplexicon.data.model.roll.Throw
import com.pixelized.rplexicon.script.Script
data class Skill( data class Skill(
val icon: Uri?,
val name: String, val name: String,
val amount: Int?, val amount: Int?,
val rest: String?, val rest: String?,
val cost: String?, val cost: String?,
val effect: Throw?, val effect: Throw?,
val passive: Boolean, 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.Property
import com.pixelized.rplexicon.data.model.roll.Dice import com.pixelized.rplexicon.data.model.roll.Dice
import com.pixelized.rplexicon.data.model.roll.Flat import com.pixelized.rplexicon.data.model.roll.Flat
import com.pixelized.rplexicon.script.Script
data class Alteration( data class Alteration(
val icon: Uri?,
val name: String, val name: String,
val source: String, val source: String,
val target: String, val target: String,
val script: Script?,
val icon: Uri?,
val status: Map<Property, Status>, val status: Map<Property, Status>,
) { ) {
data class 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.Attack
import com.pixelized.rplexicon.data.model.CharacterSheet import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.parser.roll.ThrowParser import com.pixelized.rplexicon.data.parser.roll.ThrowParser
import com.pixelized.rplexicon.script.ScriptParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject import javax.inject.Inject
class AttackParser @Inject constructor( class AttackParser @Inject constructor(
private val throwParser: ThrowParser, private val throwParser: ThrowParser,
private val scriptParser: ScriptParser,
) { ) {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse( fun parse(
@ -33,6 +35,7 @@ class AttackParser @Inject constructor(
title = title, title = title,
type = parseType(value = row.parse(column = TYPE)), type = parseType(value = row.parse(column = TYPE)),
range = row.parse(column = RANGE), range = row.parse(column = RANGE),
script = row.parse(column = SCRIPT)?.let { scriptParser.parse(it) },
hit = throwParser.parse(value = row.parse(column = HIT)), hit = throwParser.parse(value = row.parse(column = HIT)),
damage = throwParser.parse(value = row.parse(column = DAMAGE)), damage = throwParser.parse(value = row.parse(column = DAMAGE)),
alterations = row.parseList(column = ALTERATION), alterations = row.parseList(column = ALTERATION),
@ -64,6 +67,18 @@ class AttackParser @Inject constructor(
private val DAMAGE = column("Dommage") private val DAMAGE = column("Dommage")
private val ALTERATION = column("Altérations") private val ALTERATION = column("Altérations")
private val ICON = column("Icone") 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.CharacterSheet
import com.pixelized.rplexicon.data.model.Skill import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.parser.roll.ThrowParser import com.pixelized.rplexicon.data.parser.roll.ThrowParser
import com.pixelized.rplexicon.script.ScriptParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject import javax.inject.Inject
class SkillParser @Inject constructor( class SkillParser @Inject constructor(
private val throwParser: ThrowParser, private val throwParser: ThrowParser,
private val scriptParser: ScriptParser,
) { ) {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse( fun parse(
@ -32,7 +34,8 @@ class SkillParser @Inject constructor(
rest = row.parse(column = REST), rest = row.parse(column = REST),
cost = row.parse(column = COST), cost = row.parse(column = COST),
effect = throwParser.parse(row.parse(column = EFFECT)), 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) skills.getOrPut(characterSheet.name) { mutableListOf() }.add(skill)
} }
@ -52,7 +55,19 @@ class SkillParser @Inject constructor(
private val COST = column("Coût") private val COST = column("Coût")
private val EFFECT = column("Effect") private val EFFECT = column("Effect")
private val PASSIVE = column("Passif") 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 package com.pixelized.rplexicon.data.parser.alteration
import com.google.api.services.sheets.v4.model.ValueRange 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.CharacterSheet
import com.pixelized.rplexicon.data.model.Property 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.column
import com.pixelized.rplexicon.data.parser.parserScope import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.data.parser.roll.DiceParser import com.pixelized.rplexicon.data.parser.roll.DiceParser
import com.pixelized.rplexicon.data.parser.roll.FlatValueParser import com.pixelized.rplexicon.data.parser.roll.FlatValueParser
import com.pixelized.rplexicon.script.ScriptParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject import javax.inject.Inject
class AlterationParser @Inject constructor( class AlterationParser @Inject constructor(
private val diceParser: DiceParser, private val diceParser: DiceParser,
private val flatParser: FlatValueParser, private val flatParser: FlatValueParser,
private val scriptParser: ScriptParser,
) { ) {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse( fun parse(
@ -37,6 +39,7 @@ class AlterationParser @Inject constructor(
name = name, name = name,
source = source, source = source,
target = target, target = target,
script = row.parse(column = SCRIPT)?.let { scriptParser.parse(it) },
status = properties.mapNotNull { property -> status = properties.mapNotNull { property ->
val column = column(property.key) val column = column(property.key)
row.parse(column = column) row.parse(column = column)
@ -95,7 +98,14 @@ class AlterationParser @Inject constructor(
private val TARGET = column("Cible") private val TARGET = column("Cible")
private val SOURCE = column("Source") private val SOURCE = column("Source")
private val ICON = column("Icone") private val ICON = column("Icone")
private val SCRIPT = column("Script")
private val COLUMNS 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.column
import com.pixelized.rplexicon.data.parser.parserScope import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.data.parser.roll.ThrowParser import com.pixelized.rplexicon.data.parser.roll.ThrowParser
import com.pixelized.rplexicon.script.ScriptParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject import javax.inject.Inject
class AssignedSpellParser @Inject constructor( class AssignedSpellParser @Inject constructor(
private val throwParser: ThrowParser, private val throwParser: ThrowParser,
private val scriptParser: ScriptParser,
) { ) {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse( fun parse(
@ -31,6 +33,7 @@ class AssignedSpellParser @Inject constructor(
effect = throwParser.parse(value = row.parse(EFFECT)), effect = throwParser.parse(value = row.parse(EFFECT)),
level = throwParser.parse(value = row.parse(LEVEL)), level = throwParser.parse(value = row.parse(LEVEL)),
spell = spell, spell = spell,
script = row.parse(SCRIPT)?.let { scriptParser.parse(it) }
) )
assignedSpells assignedSpells
.getOrPut(key = character) { mutableListOf() } .getOrPut(key = character) { mutableListOf() }
@ -47,7 +50,8 @@ class AssignedSpellParser @Inject constructor(
private val HIT = column("Touché") private val HIT = column("Touché")
private val EFFECT = column("Effet") private val EFFECT = column("Effet")
private val LEVEL = column("Par niveau") 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.parser.AttackParser
import com.pixelized.rplexicon.data.repository.CharacterBinder import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure 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.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -16,15 +24,29 @@ import javax.inject.Singleton
class ActionRepository @Inject constructor( class ActionRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository, private val googleRepository: GoogleSheetServiceRepository,
private val actionParser: AttackParser, private val actionParser: AttackParser,
firebaseRepository: FirebaseRepository,
) { ) {
private val _data = MutableStateFlow<Map<String, List<Attack>>>(emptyMap()) private val _actions = MutableStateFlow<Map<String, List<Attack>>>(emptyMap())
val data: StateFlow<Map<String, List<Attack>>> get() = _data 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 var lastSuccessFullUpdate: Update = Update.INITIAL
private set private set
fun find(character: String?): List<Attack>? { 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? { fun find(character: String?, action: String): Attack? {
@ -39,7 +61,7 @@ class ActionRepository @Inject constructor(
value = request.execute(), value = request.execute(),
charactersSheets = characters charactersSheets = characters
) )
_data.emit(data) _actions.emit(data)
lastSuccessFullUpdate = Update.currentTime() 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.parser.alteration.AlterationParser
import com.pixelized.rplexicon.data.repository.CharacterBinder import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure 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.Flow
import kotlinx.coroutines.flow.MutableStateFlow 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.map
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -19,8 +27,23 @@ import javax.inject.Singleton
class AlterationRepository @Inject constructor( class AlterationRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository, private val googleRepository: GoogleSheetServiceRepository,
private val alterationParser: AlterationParser, 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 var lastSuccessFullUpdate: Update = Update.INITIAL
private set private set
@ -37,9 +60,8 @@ class AlterationRepository @Inject constructor(
* get an alteration for a character that have [alteration] as namer. * get an alteration for a character that have [alteration] as namer.
* @return a nullable [Alteration] * @return a nullable [Alteration]
*/ */
suspend fun getAssignedAlterationSnapshot(character: String?, alteration: String?): Alteration? { fun getAssignedAlterationSnapshot(character: String?, alteration: String?): Alteration? {
return assignedAlterations.firstOrNull()?.get(character) return assignedAlterations.value[character]?.firstOrNull { it.name == alteration }
?.firstOrNull { it.name == alteration }
} }
/** /**
@ -75,7 +97,7 @@ class AlterationRepository @Inject constructor(
googleRepository.fetch { sheet -> googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.ALTERATION) val request = sheet.get(CharacterBinder.ID, CharacterBinder.ALTERATION)
val data = alterationParser.parse(sheet = request.execute(), characterSheets = sheets) val data = alterationParser.parse(sheet = request.execute(), characterSheets = sheets)
assignedAlterations.tryEmit(data) alterations.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime() 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.parser.SkillParser
import com.pixelized.rplexicon.data.repository.CharacterBinder import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure 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.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -16,9 +24,23 @@ import javax.inject.Singleton
class SkillRepository @Inject constructor( class SkillRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository, private val googleRepository: GoogleSheetServiceRepository,
private val skillParser: SkillParser, private val skillParser: SkillParser,
firebaseRepository: FirebaseRepository,
) { ) {
private val _skills = MutableStateFlow<Map<String, List<Skill>>>(emptyMap()) 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 var lastSuccessFullUpdate: Update = Update.INITIAL
private set private set
@ -35,7 +57,6 @@ class SkillRepository @Inject constructor(
sheet = request.execute(), sheet = request.execute(),
charactersSheets = characters, charactersSheets = characters,
) )
_skills.emit(skills) _skills.emit(skills)
lastSuccessFullUpdate = Update.currentTime() 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.parser.spell.SpellBookParser
import com.pixelized.rplexicon.data.repository.CharacterBinder import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure 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.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -20,11 +28,25 @@ class SpellRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository, private val googleRepository: GoogleSheetServiceRepository,
private val spellBookParser: SpellBookParser, private val spellBookParser: SpellBookParser,
private val assignedSpellParser: AssignedSpellParser, private val assignedSpellParser: AssignedSpellParser,
firebaseRepository: FirebaseRepository,
) { ) {
private var _spellsBook = MutableStateFlow<List<Spell>>(emptyList()) private var _spellsBook = MutableStateFlow<List<Spell>>(emptyList())
val spellsBook: StateFlow<List<Spell>> get() = _spellsBook val spellsBook: StateFlow<List<Spell>> get() = _spellsBook
private val _spells = MutableStateFlow<Map<String, List<AssignedSpell>>>(emptyMap()) 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 var lastSuccessFullUpdate: Update = Update.INITIAL
private set private set
@ -34,7 +56,7 @@ class SpellRepository @Inject constructor(
} }
fun findAssignedSpell(character: String?): List<AssignedSpell>? { 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? { fun findAssignedSpell(character: String?, spell: String): AssignedSpell? {

View file

@ -1,6 +1,6 @@
package com.pixelized.rplexicon.script package com.pixelized.rplexicon.script
import com.pixelized.rplexicon.data.model.alteration.Alteration import com.pixelized.rplexicon.data.model.alteration.AlterationStatus
sealed class Script( sealed class Script(
val predicate: ScriptExecutor.() -> Boolean val predicate: ScriptExecutor.() -> Boolean
@ -36,7 +36,7 @@ sealed class Script(
class ACTIVE( class ACTIVE(
val alteration: String, val alteration: String,
) : Script(predicate = { ) : Script(predicate = {
alterations.any { it.name == alteration } alterations[AlterationStatus.Key(character, alteration)]?.value == true
}) { }) {
override fun toString(): String { override fun toString(): String {
return "ACTIVE('$alteration')" return "ACTIVE('$alteration')"
@ -46,7 +46,9 @@ sealed class Script(
class ANY( class ANY(
val alteration: String, val alteration: String,
) : Script(predicate = { ) : 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 { override fun toString(): String {
return "ANY('$alteration')" return "ANY('$alteration')"
@ -56,7 +58,7 @@ sealed class Script(
class NOT( class NOT(
val alteration: String, val alteration: String,
) : Script(predicate = { ) : Script(predicate = {
alterations.none { it.name == alteration } alterations[AlterationStatus.Key(character, alteration)]?.value != true
}) { }) {
override fun toString(): String { override fun toString(): String {
return "NOT('$alteration')" return "NOT('$alteration')"
@ -66,7 +68,9 @@ sealed class Script(
class NONE( class NONE(
val alteration: String, val alteration: String,
) : Script(predicate = { ) : 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 { override fun toString(): String {
return "NONE('$alteration')" return "NONE('$alteration')"
@ -77,12 +81,17 @@ sealed class Script(
} }
class ScriptExecutor( class ScriptExecutor(
val alterations: List<Alteration>, val character: String,
val alterations: Map<AlterationStatus.Key, AlterationStatus.Value>,
) )
fun scriptExecutor( fun scriptExecutor(
alterations: List<Alteration>, character: String,
alterations: Map<AlterationStatus.Key, AlterationStatus.Value>,
block: ScriptExecutor.() -> Boolean block: ScriptExecutor.() -> Boolean
): 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 package com.pixelized.rplexicon.script
class ScriptParser { import javax.inject.Inject
class ScriptParser @Inject constructor() {
companion object { companion object {
private val FUNCTION_REGEX = Regex("""^(\w+)\((?:(.*);(.*)|(.*))\)$""") private val FUNCTION_REGEX = Regex("""^(\w+)\((?:(.*);(.*)|(.*))\)$""")

View file

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

View file

@ -1,4 +1,3 @@
package com.pixelized.rplexicon.ui.screens.character.pages.alteration package com.pixelized.rplexicon.ui.screens.character.pages.alteration
import android.content.res.Configuration.UI_MODE_NIGHT_NO 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( AlterationItem(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
alteration = alteration, 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( val alteration = alterationRepository.getAssignedAlterationSnapshot(
character = character, character = character,
alteration = id, alteration = id,