Add alteration status to the portrait
This commit is contained in:
parent
2c04559bb7
commit
e81b66e725
23 changed files with 2719 additions and 210 deletions
2296
composeApp/hs_err_pid10276.log
Normal file
2296
composeApp/hs_err_pid10276.log
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -35,7 +35,7 @@ import com.pixelized.desktop.lwa.ui.overlay.portrait.PortraitOverlayViewModel
|
|||
import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.network.NetworkFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.network.NetworkViewModel
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterRibbonFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailPanelViewModel
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeaderFactory
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.pixelized.desktop.lwa.repository.alteration
|
||||
|
||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.desktop.lwa.repository.inventory.InventoryRepository
|
||||
import com.pixelized.desktop.lwa.repository.item.ItemRepository
|
||||
|
|
@ -23,24 +22,23 @@ class AlterationRepository(
|
|||
private val alterationStore: AlterationStore,
|
||||
private val inventoryRepository: InventoryRepository,
|
||||
private val itemRepository: ItemRepository,
|
||||
campaignRepository: CampaignRepository,
|
||||
characterRepository: CharacterSheetRepository,
|
||||
private val characterRepository: CharacterSheetRepository,
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
|
||||
val alterationFlow get() = alterationStore.alterationsFlow
|
||||
|
||||
/**
|
||||
* This flow transform the campaign instance (player + npc) into a
|
||||
* Map<CharacterSheetId, List<AlterationId>> from the CharacterSheet Alteration
|
||||
* This flow build a Map of CharacterSheetId to a list of AlterationId.
|
||||
* Map<CharacterSheetId, List<AlterationId>> from the CharacterSheet Alterations.
|
||||
* It is used by an other flow to build the FieldAlteration cache.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val campaignCharacterAlterationFlow: Flow<Map<String, List<String>>> =
|
||||
campaignRepository.campaignFlow()
|
||||
.flatMapLatest { campaign ->
|
||||
val characters = campaign.instances.map {
|
||||
characterRepository.characterDetailFlow(characterSheetId = it)
|
||||
private val charactersAlterationFlow: Flow<Map<String, List<String>>> =
|
||||
characterRepository.characterSheetPreviewFlow()
|
||||
.flatMapLatest { preview ->
|
||||
val characters = preview.map {
|
||||
characterRepository.characterDetailFlow(characterSheetId = it.characterSheetId)
|
||||
}
|
||||
combine(characters) { sheets: Array<CharacterSheet?> ->
|
||||
sheets
|
||||
|
|
@ -51,20 +49,20 @@ class AlterationRepository(
|
|||
}
|
||||
|
||||
/**
|
||||
* This flow transform the campaign instance (player + npc) into a
|
||||
* Map<CharacterSheetId, List<AlterationId>> from the character inventory items Alteration
|
||||
* This flow build a Map of CharacterSheetId to a list of AlterationId.
|
||||
* Map<CharacterSheetId, List<AlterationId>> from the character inventory items Alterations.
|
||||
* It is used by an other flow to build the FieldAlteration cache.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val campaignCharacterInventoryAlterationFlow: Flow<Map<String, List<String>>> =
|
||||
campaignRepository.campaignFlow()
|
||||
.flatMapLatest { campaign ->
|
||||
val equippedItems = campaign.instances.map { characterSheetId ->
|
||||
private val inventoriesAlterationFlow: Flow<Map<String, List<String>>> =
|
||||
characterRepository.characterSheetPreviewFlow()
|
||||
.flatMapLatest { previews ->
|
||||
val equippedItems = previews.map {
|
||||
combine(
|
||||
inventoryRepository.equippedItemsFlow(characterSheetId = characterSheetId),
|
||||
inventoryRepository.equippedItemsFlow(characterSheetId = it.characterSheetId),
|
||||
itemRepository.itemFlow(),
|
||||
) { equipments, items ->
|
||||
characterSheetId to equipments.flatMap { equipment ->
|
||||
it.characterSheetId to equipments.flatMap { equipment ->
|
||||
items[equipment.itemId]?.alterations ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
|
@ -76,18 +74,60 @@ class AlterationRepository(
|
|||
}
|
||||
}
|
||||
|
||||
private val activeAlterationMapFlow: StateFlow<Map<String, Map<String, List<FieldAlteration>>>> =
|
||||
/**
|
||||
* This flow build a Map of CharacterSheetId to a list of AlterationId.
|
||||
* Map<CharacterSheetId, List<AlterationId>> from the character sheet + inventory items Alterations.
|
||||
* It is used by an other flow to build the FieldAlteration cache.
|
||||
*/
|
||||
private val activeAlterationIdsFlow: StateFlow<Map<String, List<String>>> = combine(
|
||||
charactersAlterationFlow,
|
||||
inventoriesAlterationFlow,
|
||||
) { characters, inventories ->
|
||||
val characterSheetIds = characters.keys + inventories.keys
|
||||
characterSheetIds.associateWith {
|
||||
characters.getOrElse(it) { emptyList() } + inventories.getOrElse(it) { emptyList() }
|
||||
}
|
||||
}.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = emptyMap(),
|
||||
)
|
||||
|
||||
/**
|
||||
* This flow build a Map of CharacterSheetId to a list of Alteration.
|
||||
* Map<CharacterSheetId, List<Alteration>> from the character sheet + inventory items Alterations.
|
||||
*/
|
||||
private val activeAlterationsFlow: StateFlow<Map<String, List<Alteration>>> =
|
||||
combine(
|
||||
characterRepository.characterSheetPreviewFlow(),
|
||||
alterationStore.alterationsFlow,
|
||||
campaignCharacterAlterationFlow,
|
||||
campaignCharacterInventoryAlterationFlow,
|
||||
) { alterations, characters, inventories ->
|
||||
val characterSheetIds = characters.keys + inventories.keys
|
||||
characterSheetIds.associateWith {
|
||||
activeAlterationIdsFlow,
|
||||
) { preview, alterations, activeAlterationIds ->
|
||||
preview.map { it.characterSheetId }.associateWith { characterSheetId ->
|
||||
activeAlterationIds
|
||||
.getOrElse(characterSheetId) { emptyList() }
|
||||
.mapNotNull { alterations[it] }
|
||||
}
|
||||
}.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = emptyMap(),
|
||||
)
|
||||
|
||||
/**
|
||||
* This flow build a Map of CharacterSheetId to a Map of AlterationId and FieldAlteration.
|
||||
* Map<CharacterSheetId, Map<AlterationId, Alteration>> from the character sheet + inventory items Alterations.
|
||||
*/
|
||||
private val activeFieldAlterationsFlow: StateFlow<Map<String, Map<String, List<FieldAlteration>>>> =
|
||||
combine(
|
||||
characterRepository.characterSheetPreviewFlow(),
|
||||
alterationStore.alterationsFlow,
|
||||
activeAlterationIdsFlow,
|
||||
) { previews, alterations, activeAlterationIds ->
|
||||
previews.map { it.characterSheetId }.associateWith {
|
||||
transformToAlterationFieldMap(
|
||||
alterations = alterations,
|
||||
actives = characters.getOrElse(it) { emptyList() } +
|
||||
inventories.getOrElse(it) { emptyList() }
|
||||
actives = activeAlterationIds.getOrElse(it) { emptyList() },
|
||||
)
|
||||
}
|
||||
}.stateIn(
|
||||
|
|
@ -106,16 +146,28 @@ class AlterationRepository(
|
|||
return alterationFlow.value[alterationId]
|
||||
}
|
||||
|
||||
fun fieldAlterations(
|
||||
fun activeFieldAlterations(
|
||||
characterSheetId: String,
|
||||
): Map<String, List<FieldAlteration>> {
|
||||
return activeAlterationMapFlow.value[characterSheetId] ?: emptyMap()
|
||||
return activeFieldAlterationsFlow.value[characterSheetId] ?: emptyMap()
|
||||
}
|
||||
|
||||
fun fieldAlterationsFlow(
|
||||
fun activeFieldAlterationsFlow(
|
||||
characterSheetId: String,
|
||||
): Flow<Map<String, List<FieldAlteration>>> {
|
||||
return activeAlterationMapFlow.map { it[characterSheetId] ?: emptyMap() }
|
||||
return activeFieldAlterationsFlow.map { it[characterSheetId] ?: emptyMap() }
|
||||
}
|
||||
|
||||
fun activeAlterations(
|
||||
characterSheetId: String,
|
||||
): List<Alteration> {
|
||||
return activeAlterationsFlow.value[characterSheetId] ?: emptyList()
|
||||
}
|
||||
|
||||
fun activeAlterationsFlow(
|
||||
characterSheetId: String,
|
||||
): Flow<List<Alteration>> {
|
||||
return activeAlterationsFlow.map { it[characterSheetId] ?: emptyList() }
|
||||
}
|
||||
|
||||
@Throws
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class CharacterSheetCharacteristicDialogFactory(
|
|||
|
||||
if (characterSheet == null) return null
|
||||
|
||||
val alterations: Map<String, List<FieldAlteration>> = alterationRepository.fieldAlterations(
|
||||
val alterations: Map<String, List<FieldAlteration>> = alterationRepository.activeFieldAlterations(
|
||||
characterSheetId = characterSheetId,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class CharacterSheetCharacteristicDialogViewModel(
|
|||
|
||||
if (characterSheet == null) return
|
||||
|
||||
val alterations = alterationRepository.fieldAlterations(
|
||||
val alterations = alterationRepository.activeFieldAlterations(
|
||||
characterSheetId = characterSheetId,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class CharacterSheetDiminishedDialogFactory(
|
|||
|
||||
if (characterSheet == null) return null
|
||||
|
||||
val alterations: Map<String, List<FieldAlteration>> = alterationRepository.fieldAlterations(
|
||||
val alterations: Map<String, List<FieldAlteration>> = alterationRepository.activeFieldAlterations(
|
||||
characterSheetId = characterSheetId,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class RollViewModel(
|
|||
|
||||
if (characterSheet == null) return
|
||||
|
||||
val alterations = alterationRepository.fieldAlterations(
|
||||
val alterations = alterationRepository.activeFieldAlterations(
|
||||
characterSheetId = roll.characterSheetId,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player
|
||||
|
||||
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
|
||||
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
|
||||
class CharacterRibbonFactory(
|
||||
private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory,
|
||||
) {
|
||||
fun convertToPlayerPortraitUio(
|
||||
characterSheet: CharacterSheet?,
|
||||
alterations: Map<String, List<FieldAlteration>>,
|
||||
hideOverruled: Boolean,
|
||||
enableCharacterSheet: Boolean,
|
||||
enableCharacterStats: Boolean,
|
||||
): CharacterPortraitUio? {
|
||||
if (characterSheet == null) return null
|
||||
|
||||
val alteredCharacterSheet = alteredCharacterSheetFactory.sheet(
|
||||
characterSheet = characterSheet,
|
||||
alterations = alterations,
|
||||
)
|
||||
|
||||
return CharacterPortraitUio(
|
||||
characterSheetId = characterSheet.id,
|
||||
portrait = alteredCharacterSheet.thumbnail,
|
||||
name = alteredCharacterSheet.name,
|
||||
levelUp = alteredCharacterSheet.shouldLevelUp,
|
||||
hideOverruled = hideOverruled,
|
||||
enableDetail = enableCharacterSheet,
|
||||
stats = takeIf { enableCharacterStats }?.let {
|
||||
CharacterPortraitUio.StatsDetail(
|
||||
hp = alteredCharacterSheet.maxHp - alteredCharacterSheet.damage,
|
||||
maxHp = alteredCharacterSheet.maxHp,
|
||||
pp = alteredCharacterSheet.maxPp - alteredCharacterSheet.fatigue,
|
||||
maxPp = alteredCharacterSheet.maxPp,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ class CharacterDetailHeaderFactory(
|
|||
): StateFlow<CharacterDetailHeaderUio?> {
|
||||
return combine(
|
||||
characterSheetRepository.characterDetailFlow(characterSheetId = characterSheetId),
|
||||
alterationRepository.fieldAlterationsFlow(characterSheetId = characterSheetId),
|
||||
alterationRepository.activeFieldAlterationsFlow(characterSheetId = characterSheetId),
|
||||
settingRepository.settingsFlow()
|
||||
) { characterSheet, alterations, settings ->
|
||||
convertToCharacterDetailHeaderUio(
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class CharacterDetailSheetFactory(
|
|||
): StateFlow<CharacterDetailSheetUio?> {
|
||||
return combine(
|
||||
characterSheetRepository.characterDetailFlow(characterSheetId = characterSheetId),
|
||||
alterationRepository.fieldAlterationsFlow(characterSheetId = characterSheetId),
|
||||
alterationRepository.activeFieldAlterationsFlow(characterSheetId = characterSheetId),
|
||||
) { characterSheet, alterations ->
|
||||
convertToCharacterDetailSheetUio(
|
||||
characterSheetId = characterSheetId,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
|
||||
|
||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.BasicTooltipUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonAlterationUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonPortraitUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonUio
|
||||
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
|
||||
import com.pixelized.shared.lwa.model.alteration.Alteration
|
||||
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
|
||||
class CharacterRibbonFactory(
|
||||
private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory,
|
||||
) {
|
||||
fun convertToPlayerPortraitUio(
|
||||
characterSheet: CharacterSheet?,
|
||||
alterations: List<Alteration>,
|
||||
fieldAlterations: Map<String, List<FieldAlteration>>,
|
||||
hideOverruled: Boolean,
|
||||
enableCharacterSheet: Boolean,
|
||||
enableCharacterStats: Boolean,
|
||||
): CharacterRibbonUio? {
|
||||
if (characterSheet == null) return null
|
||||
|
||||
val alteredCharacterSheet = alteredCharacterSheetFactory.sheet(
|
||||
characterSheet = characterSheet,
|
||||
alterations = fieldAlterations,
|
||||
)
|
||||
|
||||
val status = alterations.map { alteration ->
|
||||
CharacterRibbonAlterationUio(
|
||||
icon = "https://bg3.wiki/w/images/2/2d/Map_Tutorial_Map_Icon.png",
|
||||
tooltips = BasicTooltipUio(
|
||||
title = alteration.metadata.name,
|
||||
description = alteration.metadata.description,
|
||||
),
|
||||
)
|
||||
}.fold(
|
||||
initial = mutableListOf<MutableList<CharacterRibbonAlterationUio>>(),
|
||||
operation = { acc, item ->
|
||||
if (acc.isEmpty() || acc.last().size == 5) acc.add(mutableListOf())
|
||||
acc.last().add(item)
|
||||
acc
|
||||
}
|
||||
)
|
||||
|
||||
return CharacterRibbonUio(
|
||||
characterSheetId = characterSheet.id,
|
||||
hideOverruled = hideOverruled,
|
||||
portrait = CharacterRibbonPortraitUio(
|
||||
portrait = alteredCharacterSheet.thumbnail,
|
||||
name = alteredCharacterSheet.name,
|
||||
levelUp = alteredCharacterSheet.shouldLevelUp,
|
||||
enableDetail = enableCharacterSheet,
|
||||
stats = takeIf { enableCharacterStats }?.let {
|
||||
CharacterRibbonPortraitUio.StatsDetail(
|
||||
hp = alteredCharacterSheet.maxHp - alteredCharacterSheet.damage,
|
||||
maxHp = alteredCharacterSheet.maxHp,
|
||||
pp = alteredCharacterSheet.maxPp - alteredCharacterSheet.fatigue,
|
||||
maxPp = alteredCharacterSheet.maxPp,
|
||||
)
|
||||
},
|
||||
),
|
||||
status = status,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
|
||||
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||
|
|
@ -14,10 +16,11 @@ import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetReposit
|
|||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
||||
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitRollUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterRibbonFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonRollUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonUio
|
||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import com.pixelized.shared.lwa.protocol.websocket.RollEvent.Critical
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -38,7 +41,7 @@ abstract class CharacterRibbonViewModel(
|
|||
private val ribbonFactory: CharacterRibbonFactory,
|
||||
) : ViewModel() {
|
||||
|
||||
private val rolls = hashMapOf<String, MutableState<CharacterPortraitRollUio?>>()
|
||||
private val rolls = hashMapOf<String, MutableState<CharacterRibbonRollUio?>>()
|
||||
|
||||
abstract fun fetch(
|
||||
campaign: Campaign,
|
||||
|
|
@ -66,7 +69,7 @@ abstract class CharacterRibbonViewModel(
|
|||
* Then sort the result.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val characters: StateFlow<List<CharacterPortraitUio>> = combine(
|
||||
val characters: StateFlow<List<CharacterRibbonUio>> = combine(
|
||||
settingsRepository.settingsFlow(),
|
||||
campaignRepository.campaignFlow(),
|
||||
) { settings, campaign -> campaign to settings }
|
||||
|
|
@ -76,15 +79,17 @@ abstract class CharacterRibbonViewModel(
|
|||
val characterSheetIds = fetch(campaign, settings)
|
||||
when (characterSheetIds.isEmpty()) {
|
||||
true -> flowOf(emptyList())
|
||||
else -> combine<CharacterPortraitUio?, List<CharacterPortraitUio>>(
|
||||
else -> combine<CharacterRibbonUio?, List<CharacterRibbonUio>>(
|
||||
flows = characterSheetIds.map { characterSheetId ->
|
||||
combine(
|
||||
characterRepository.characterDetailFlow(characterSheetId = characterSheetId),
|
||||
alterationRepository.fieldAlterationsFlow(characterSheetId = characterSheetId),
|
||||
) { sheet, alterations ->
|
||||
alterationRepository.activeAlterationsFlow(characterSheetId = characterSheetId),
|
||||
alterationRepository.activeFieldAlterationsFlow(characterSheetId = characterSheetId),
|
||||
) { sheet, alterations, fieldAlterations ->
|
||||
ribbonFactory.convertToPlayerPortraitUio(
|
||||
characterSheet = sheet,
|
||||
alterations = alterations,
|
||||
fieldAlterations = fieldAlterations,
|
||||
hideOverruled = hideOverruled,
|
||||
enableCharacterSheet = enableCharacterSheet(settings = settings),
|
||||
enableCharacterStats = enableCharacterStats(settings = settings),
|
||||
|
|
@ -93,7 +98,7 @@ abstract class CharacterRibbonViewModel(
|
|||
},
|
||||
transform = { headers ->
|
||||
headers.mapNotNull { it }
|
||||
.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
||||
.sortedWith(compareBy(Collator.getInstance()) { it.portrait.name })
|
||||
.toList()
|
||||
}
|
||||
)
|
||||
|
|
@ -113,8 +118,11 @@ abstract class CharacterRibbonViewModel(
|
|||
@Stable
|
||||
fun roll(
|
||||
characterSheetId: String,
|
||||
): State<CharacterPortraitRollUio?> {
|
||||
val state = rolls.getOrPut(characterSheetId) { mutableStateOf(null) }
|
||||
): State<CharacterRibbonRollUio?> {
|
||||
val colorScheme = MaterialTheme.lwa.colorScheme
|
||||
val state = remember(characterSheetId) {
|
||||
rolls.getOrPut(characterSheetId) { mutableStateOf(null) }
|
||||
}
|
||||
|
||||
LaunchedEffect(characterSheetId) {
|
||||
combine(
|
||||
|
|
@ -122,10 +130,17 @@ abstract class CharacterRibbonViewModel(
|
|||
rollHistoryRepository.rolls(),
|
||||
) { settings, roll ->
|
||||
if (settings.portrait.dynamicDice && characterSheetId == roll.characterSheetId) {
|
||||
state.value = CharacterPortraitRollUio(
|
||||
state.value = CharacterRibbonRollUio(
|
||||
characterSheetId = characterSheetId,
|
||||
value = roll.rollValue,
|
||||
label = roll.resultLabel?.split(" ")?.joinToString(separator = "\n") { it }
|
||||
tint = when (roll.critical) {
|
||||
Critical.CRITICAL_SUCCESS -> colorScheme.portrait.criticalSuccess
|
||||
Critical.SPECIAL_SUCCESS -> colorScheme.portrait.spacialSuccess
|
||||
Critical.SUCCESS -> colorScheme.portrait.success
|
||||
Critical.FAILURE -> colorScheme.portrait.failure
|
||||
Critical.CRITICAL_FAILURE -> colorScheme.portrait.criticalFailure
|
||||
null -> colorScheme.portrait.default
|
||||
},
|
||||
)
|
||||
}
|
||||
}.launchIn(this)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.EaseOutCirc
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@Composable
|
||||
fun BloodOverlay(
|
||||
modifier: Modifier = Modifier,
|
||||
bloodColor: Color = MaterialTheme.lwa.colorScheme.portrait.blood,
|
||||
maxHp: Float,
|
||||
hp: Float,
|
||||
) {
|
||||
val animatedRatio = animateFloatAsState(
|
||||
targetValue = min(maxHp, max(0f, (maxHp - hp) / maxHp)),
|
||||
animationSpec = tween(durationMillis = 350, easing = EaseOutCirc)
|
||||
)
|
||||
val animatedColor = animateColorAsState(
|
||||
targetValue = bloodColor.copy(alpha = ((maxHp - hp) / maxHp) / 4f + .25f)
|
||||
)
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(fraction = animatedRatio.value)
|
||||
.background(color = animatedColor.value)
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.TooltipPlacement
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.PlatformContext
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.request.ImageRequest
|
||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.BasicTooltipLayout
|
||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.BasicTooltipUio
|
||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||
|
||||
@Stable
|
||||
data class CharacterRibbonAlterationUio(
|
||||
val icon: String,
|
||||
val tooltips: BasicTooltipUio?,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun CharacterRibbonAlteration(
|
||||
modifier: Modifier = Modifier,
|
||||
size: DpSize = MaterialTheme.lwa.size.portrait.minimized,
|
||||
direction: LayoutDirection,
|
||||
status: List<List<CharacterRibbonAlterationUio>>,
|
||||
) {
|
||||
val currentDirection: LayoutDirection = LocalLayoutDirection.current
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalLayoutDirection provides direction
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.size(size = size)
|
||||
.then(other = modifier),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalLayoutDirection provides currentDirection
|
||||
) {
|
||||
status.forEach { columns ->
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(space = 2.dp),
|
||||
) {
|
||||
columns.forEach {
|
||||
BasicTooltipLayout(
|
||||
delayMillis = 0,
|
||||
tooltip = it.tooltips,
|
||||
tooltipPlacement = remember(currentDirection) {
|
||||
TooltipPlacement.ComponentRect(
|
||||
anchor = when(direction) {
|
||||
LayoutDirection.Ltr -> Alignment.TopStart
|
||||
LayoutDirection.Rtl -> Alignment.TopEnd
|
||||
},
|
||||
alignment = when(direction) {
|
||||
LayoutDirection.Ltr -> Alignment.BottomEnd
|
||||
LayoutDirection.Rtl -> Alignment.BottomStart
|
||||
},
|
||||
)
|
||||
},
|
||||
content = {
|
||||
AsyncImage(
|
||||
modifier = Modifier.size(24.dp),
|
||||
model = ImageRequest.Builder(context = PlatformContext.INSTANCE)
|
||||
.data(data = it.icon)
|
||||
.size(size = 48)
|
||||
.build(),
|
||||
filterQuality = FilterQuality.High,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player
|
||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
|
|
@ -37,7 +37,6 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.PointerButton
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
|
@ -54,12 +53,10 @@ import kotlin.math.max
|
|||
import kotlin.math.min
|
||||
|
||||
@Stable
|
||||
data class CharacterPortraitUio(
|
||||
val characterSheetId: String,
|
||||
data class CharacterRibbonPortraitUio(
|
||||
val portrait: String?,
|
||||
val name: String,
|
||||
val levelUp: Boolean,
|
||||
val hideOverruled: Boolean,
|
||||
val enableDetail: Boolean,
|
||||
val stats: StatsDetail?,
|
||||
) {
|
||||
|
|
@ -74,32 +71,31 @@ data class CharacterPortraitUio(
|
|||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun CharacterPortrait(
|
||||
fun CharacterRibbonPortrait(
|
||||
modifier: Modifier = Modifier,
|
||||
size: DpSize = MaterialTheme.lwa.size.portrait.minimized,
|
||||
levelUpOffset: Dp = 9.dp,
|
||||
character: CharacterPortraitUio,
|
||||
onCharacterLeftClick: (characterSheetId: String) -> Unit,
|
||||
onCharacterRightClick: (characterSheetId: String) -> Unit,
|
||||
onLevelUp: (characterSheetId: String) -> Unit,
|
||||
character: CharacterRibbonPortraitUio,
|
||||
onCharacterLeftClick: () -> Unit,
|
||||
onCharacterRightClick: () -> Unit,
|
||||
onLevelUp: () -> Unit,
|
||||
) {
|
||||
val colorScheme = MaterialTheme.lwa.colorScheme
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.graphicsLayer { if (character.hideOverruled) this.alpha = 0.3f }
|
||||
.size(size = size)
|
||||
.clip(shape = MaterialTheme.lwa.shapes.portrait)
|
||||
.background(color = colorScheme.elevated.base1dp)
|
||||
.onClick(
|
||||
matcher = PointerMatcher.mouse(PointerButton.Primary),
|
||||
enabled = character.enableDetail,
|
||||
onClick = { onCharacterLeftClick(character.characterSheetId) }
|
||||
onClick = onCharacterLeftClick,
|
||||
)
|
||||
.onClick(
|
||||
matcher = PointerMatcher.mouse(PointerButton.Secondary),
|
||||
enabled = character.enableDetail,
|
||||
onClick = { onCharacterRightClick(character.characterSheetId) }
|
||||
onClick = onCharacterRightClick,
|
||||
),
|
||||
) {
|
||||
AnimatedContent(
|
||||
|
|
@ -125,7 +121,7 @@ fun CharacterPortrait(
|
|||
exit = fadeOut(),
|
||||
) {
|
||||
IconButton(
|
||||
onClick = { onLevelUp(character.characterSheetId) },
|
||||
onClick = onLevelUp,
|
||||
) {
|
||||
ArrowShape(
|
||||
color = MaterialTheme.lwa.colorScheme.portrait.levelUp,
|
||||
|
|
@ -193,26 +189,4 @@ fun CharacterPortrait(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BloodOverlay(
|
||||
modifier: Modifier = Modifier,
|
||||
bloodColor: Color = MaterialTheme.lwa.colorScheme.portrait.blood,
|
||||
maxHp: Float,
|
||||
hp: Float,
|
||||
) {
|
||||
val animatedRatio = animateFloatAsState(
|
||||
targetValue = min(maxHp, max(0f, (maxHp - hp) / maxHp)),
|
||||
animationSpec = tween(durationMillis = 350, easing = EaseOutCirc)
|
||||
)
|
||||
val animatedColor = animateColorAsState(
|
||||
targetValue = bloodColor.copy(alpha = ((maxHp - hp) / maxHp) / 4f + .25f)
|
||||
)
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(fraction = animatedRatio.value)
|
||||
.background(color = animatedColor.value)
|
||||
)
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player
|
||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.SizeTransform
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.AnimationVector1D
|
||||
import androidx.compose.animation.core.Spring
|
||||
|
|
@ -9,16 +10,11 @@ import androidx.compose.animation.core.spring
|
|||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.PointerMatcher
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.onClick
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
|
|
@ -28,48 +24,43 @@ import androidx.compose.runtime.Stable
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shadow
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.PointerButton
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import kotlinx.coroutines.launch
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
|
||||
@Stable
|
||||
data class CharacterPortraitRollUio(
|
||||
data class CharacterRibbonRollUio(
|
||||
val characterSheetId: String,
|
||||
val value: Int?,
|
||||
val label: String?,
|
||||
val tint: Color?,
|
||||
)
|
||||
|
||||
@Stable
|
||||
data class CharacterPortraitRollAnimation(
|
||||
val alpha: Animatable<Float, AnimationVector1D> = Animatable(0f),
|
||||
data class CharacterRibbonRollAnimation(
|
||||
val rotation: Animatable<Float, AnimationVector1D> = Animatable(0f),
|
||||
val scale: Animatable<Float, AnimationVector1D> = Animatable(1f),
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun CharacterPortraitRoll(
|
||||
fun CharacterRibbonRoll(
|
||||
modifier: Modifier = Modifier,
|
||||
size: DpSize = MaterialTheme.lwa.size.portrait.minimized,
|
||||
value: CharacterPortraitRollUio?,
|
||||
onLeftClick: (CharacterPortraitRollUio) -> Unit,
|
||||
onRightClick: (CharacterPortraitRollUio) -> Unit,
|
||||
value: CharacterRibbonRollUio?,
|
||||
) {
|
||||
AnimatedContent(
|
||||
modifier = modifier
|
||||
.size(size = size)
|
||||
.width(width = size.width)
|
||||
.aspectRatio(ratio = 1f)
|
||||
.graphicsLayer { clip = false },
|
||||
targetState = value,
|
||||
transitionSpec = {
|
||||
|
|
@ -79,6 +70,7 @@ fun CharacterPortraitRoll(
|
|||
}
|
||||
) {
|
||||
val animation = diceIconAnimation(key = it ?: Unit)
|
||||
val color = animateColorAsState(targetValue = it?.tint ?: Color.Transparent)
|
||||
|
||||
Box(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
|
|
@ -91,23 +83,13 @@ fun CharacterPortraitRoll(
|
|||
Icon(
|
||||
modifier = Modifier
|
||||
.graphicsLayer {
|
||||
this.alpha = 0.8f
|
||||
this.rotationZ = animation.rotation.value
|
||||
}
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f)
|
||||
.padding(all = 8.dp)
|
||||
.clip(shape = CircleShape)
|
||||
.onClick(
|
||||
matcher = PointerMatcher.mouse(PointerButton.Secondary),
|
||||
onClick = { onRightClick(it) },
|
||||
)
|
||||
.clickable {
|
||||
onLeftClick(it)
|
||||
}
|
||||
.padding(all = 8.dp),
|
||||
.padding(all = 16.dp),
|
||||
painter = painterResource(Res.drawable.ic_d20_24dp),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
tint = color.value,
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
|
|
@ -123,21 +105,15 @@ fun CharacterPortraitRoll(
|
|||
color = MaterialTheme.colors.onSurface,
|
||||
text = it.value.toString()
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 84.dp),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = it.label ?: "",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun diceIconAnimation(key: Any = Unit): CharacterPortraitRollAnimation {
|
||||
private fun diceIconAnimation(key: Any = Unit): CharacterRibbonRollAnimation {
|
||||
val animation = remember(key) {
|
||||
CharacterPortraitRollAnimation()
|
||||
CharacterRibbonRollAnimation()
|
||||
}
|
||||
LaunchedEffect(key) {
|
||||
launch {
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
|
||||
@Stable
|
||||
class CharacterRibbonUio(
|
||||
val characterSheetId: String,
|
||||
val hideOverruled: Boolean,
|
||||
val portrait: CharacterRibbonPortraitUio,
|
||||
val status: List<List<CharacterRibbonAlterationUio>>,
|
||||
)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.npc
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -8,9 +9,12 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortrait
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitRoll
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonPortrait
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonRoll
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonAlteration
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@Composable
|
||||
|
|
@ -34,23 +38,26 @@ fun NpcRibbon(
|
|||
key = { it.characterSheetId },
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.animateItem(),
|
||||
modifier = Modifier
|
||||
.animateItem()
|
||||
.graphicsLayer { if (it.hideOverruled) this.alpha = 0.3f },
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
|
||||
) {
|
||||
CharacterPortraitRoll(
|
||||
value = viewModel.roll(characterSheetId = it.characterSheetId).value,
|
||||
onRightClick = {
|
||||
viewModel.onPortraitRollRightClick(characterSheetId = it.characterSheetId)
|
||||
},
|
||||
onLeftClick = {
|
||||
|
||||
},
|
||||
)
|
||||
CharacterPortrait(
|
||||
character = it,
|
||||
onCharacterLeftClick = onCharacterLeftClick,
|
||||
onCharacterRightClick = onCharacterRightClick,
|
||||
onLevelUp = onLevelUp,
|
||||
CharacterRibbonAlteration(
|
||||
status = it.status,
|
||||
direction = LayoutDirection.Rtl,
|
||||
)
|
||||
Box {
|
||||
CharacterRibbonPortrait(
|
||||
character = it.portrait,
|
||||
onCharacterLeftClick = { onCharacterLeftClick(it.characterSheetId) },
|
||||
onCharacterRightClick = { onCharacterRightClick(it.characterSheetId) },
|
||||
onLevelUp = { onLevelUp(it.characterSheetId) },
|
||||
)
|
||||
CharacterRibbonRoll(
|
||||
value = viewModel.roll(characterSheetId = it.characterSheetId).value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetReposit
|
|||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
||||
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterRibbonFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonViewModel
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
|
||||
|
|
@ -36,11 +36,11 @@ class NpcRibbonViewModel(
|
|||
return !campaign.options.showNpcs && settings.isGameMaster == true
|
||||
}
|
||||
|
||||
override fun enableCharacterSheet(settings: Settings) : Boolean {
|
||||
override fun enableCharacterSheet(settings: Settings): Boolean {
|
||||
return settings.isGameMaster ?: false
|
||||
}
|
||||
|
||||
override fun enableCharacterStats(settings: Settings) : Boolean {
|
||||
override fun enableCharacterStats(settings: Settings): Boolean {
|
||||
return settings.isGameMaster ?: false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.player
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -8,9 +9,12 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortrait
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitRoll
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonPortrait
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonRoll
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonAlteration
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@Composable
|
||||
|
|
@ -34,22 +38,25 @@ fun PlayerRibbon(
|
|||
key = { it.characterSheetId },
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.animateItem(),
|
||||
modifier = Modifier
|
||||
.animateItem()
|
||||
.graphicsLayer { if (it.hideOverruled) this.alpha = 0.3f },
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
|
||||
) {
|
||||
CharacterPortrait(
|
||||
character = it,
|
||||
onCharacterLeftClick = onCharacterLeftClick,
|
||||
onCharacterRightClick = onCharacterRightClick,
|
||||
onLevelUp = onLevelUp,
|
||||
)
|
||||
CharacterPortraitRoll(
|
||||
value = viewModel.roll(characterSheetId = it.characterSheetId).value,
|
||||
onRightClick = {
|
||||
viewModel.onPortraitRollRightClick(characterSheetId = it.characterSheetId)
|
||||
},
|
||||
onLeftClick = {
|
||||
|
||||
},
|
||||
Box {
|
||||
CharacterRibbonPortrait(
|
||||
character = it.portrait,
|
||||
onCharacterLeftClick = { onCharacterLeftClick(it.characterSheetId) },
|
||||
onCharacterRightClick = { onCharacterRightClick(it.characterSheetId) },
|
||||
onLevelUp = { onLevelUp(it.characterSheetId) },
|
||||
)
|
||||
CharacterRibbonRoll(
|
||||
value = viewModel.roll(characterSheetId = it.characterSheetId).value,
|
||||
)
|
||||
}
|
||||
CharacterRibbonAlteration(
|
||||
status = it.status,
|
||||
direction = LayoutDirection.Ltr,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetReposit
|
|||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
||||
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterRibbonFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonViewModel
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class CharacterSheetViewModel(
|
|||
|
||||
val sheetFlow = combine(
|
||||
characterRepository.characterDetailFlow(characterSheetId = argument.characterSheetId),
|
||||
alteration.fieldAlterationsFlow(characterSheetId = argument.characterSheetId),
|
||||
alteration.activeFieldAlterationsFlow(characterSheetId = argument.characterSheetId),
|
||||
transform = { characterSheet, alterations ->
|
||||
factory.convertToUio(
|
||||
characterSheetId = argument.characterSheetId,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ data class LwaColors(
|
|||
data class Portrait(
|
||||
val levelUp: Color,
|
||||
val blood: Color,
|
||||
val criticalSuccess: Color,
|
||||
val spacialSuccess: Color,
|
||||
val success: Color,
|
||||
val failure: Color,
|
||||
val criticalFailure: Color,
|
||||
val default: Color,
|
||||
)
|
||||
|
||||
@Stable
|
||||
|
|
@ -84,15 +90,21 @@ fun darkLwaColorTheme(
|
|||
portrait: LwaColors.Portrait = LwaColors.Portrait(
|
||||
levelUp = LwaColorPalette.Gold,
|
||||
blood = LwaColorPalette.Blood,
|
||||
),
|
||||
chat: LwaColors.Chat = LwaColors.Chat(
|
||||
timestamp = base.secondary,
|
||||
text = base.onSurface.copy(alpha = 0.7f),
|
||||
criticalSuccess = LwaColorPalette.Teal400,
|
||||
spacialSuccess = LwaColorPalette.Green400,
|
||||
success = LwaColorPalette.LightGreen400,
|
||||
failure = LwaColorPalette.Orange400,
|
||||
criticalFailure = LwaColorPalette.Red400,
|
||||
default = base.primary,
|
||||
),
|
||||
chat: LwaColors.Chat = LwaColors.Chat(
|
||||
timestamp = base.secondary,
|
||||
text = base.onSurface.copy(alpha = 0.7f),
|
||||
criticalSuccess = portrait.criticalSuccess,
|
||||
spacialSuccess = portrait.spacialSuccess,
|
||||
success = portrait.success,
|
||||
failure = portrait.failure,
|
||||
criticalFailure = portrait.criticalFailure,
|
||||
),
|
||||
): LwaColors = LwaColors(
|
||||
base = base,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue