Add NPC transparency for gamemaster lisibility

This commit is contained in:
Thomas Andres Gomez 2025-03-19 21:20:56 +01:00
parent 49723892fc
commit 5eafe057f1
5 changed files with 53 additions and 9 deletions

View file

@ -37,6 +37,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
@ -65,6 +66,7 @@ data class CharacterPortraitUio(
val portrait: String?, val portrait: String?,
val name: String, val name: String,
val levelUp: Boolean, val levelUp: Boolean,
val hideOverruled: Boolean,
val enableDetail: Boolean, val enableDetail: Boolean,
val stats: StatsDetail?, val stats: StatsDetail?,
) { ) {
@ -90,6 +92,7 @@ fun CharacterPortrait(
Box( Box(
modifier = modifier modifier = modifier
.graphicsLayer { if (character.hideOverruled) this.alpha = 0.3f }
.size(size = size) .size(size = size)
.clip(shape = MaterialTheme.lwa.shapes.portrait) .clip(shape = MaterialTheme.lwa.shapes.portrait)
.background(color = colorScheme.elevated.base1dp) .background(color = colorScheme.elevated.base1dp)

View file

@ -15,6 +15,7 @@ class CharacterRibbonFactory(
alterations: Map<String, List<FieldAlteration>>, alterations: Map<String, List<FieldAlteration>>,
characterInstanceId: Campaign.CharacterInstance.Id, characterInstanceId: Campaign.CharacterInstance.Id,
characterInstance: Campaign.CharacterInstance, characterInstance: Campaign.CharacterInstance,
hideOverruled: Boolean,
enableCharacterId: Boolean, enableCharacterId: Boolean,
enableCharacterSheet: Boolean, enableCharacterSheet: Boolean,
enableCharacterStats: Boolean, enableCharacterStats: Boolean,
@ -32,6 +33,7 @@ class CharacterRibbonFactory(
portrait = alteredCharacterSheet.thumbnail, portrait = alteredCharacterSheet.thumbnail,
name = alteredCharacterSheet.name, name = alteredCharacterSheet.name,
levelUp = alteredCharacterSheet.shouldLevelUp, levelUp = alteredCharacterSheet.shouldLevelUp,
hideOverruled = hideOverruled,
enableDetail = enableCharacterSheet, enableDetail = enableCharacterSheet,
stats = takeIf { enableCharacterStats }?.let { stats = takeIf { enableCharacterStats }?.let {
CharacterPortraitUio.StatsDetail( CharacterPortraitUio.StatsDetail(

View file

@ -13,6 +13,7 @@ import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository 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.CharacterPortraitRollUio
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitUio 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.CharacterRibbonFactory
@ -22,6 +23,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -39,9 +41,18 @@ abstract class CharacterRibbonViewModel(
private val rolls = hashMapOf<CharacterInstance.Id, MutableState<CharacterPortraitRollUio?>>() private val rolls = hashMapOf<CharacterInstance.Id, MutableState<CharacterPortraitRollUio?>>()
abstract val Campaign.data: Map<CharacterInstance.Id, CharacterInstance> abstract fun fetch(
campaign: Campaign,
settings: Settings,
): Map<CharacterInstance.Id, CharacterInstance>
abstract fun hideOverruled(
campaign: Campaign,
settings: Settings,
): Boolean
abstract val enableCharacterSheet: Boolean abstract val enableCharacterSheet: Boolean
abstract val enableCharacterStats: Boolean abstract val enableCharacterStats: Boolean
/** /**
@ -52,22 +63,28 @@ abstract class CharacterRibbonViewModel(
* Then sort the result. * Then sort the result.
*/ */
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val characters: StateFlow<List<CharacterPortraitUio>> = campaignRepository.campaignFlow val characters: StateFlow<List<CharacterPortraitUio>> = combine(
.flatMapLatest { campaign -> settingsRepository.settingsFlow(),
when (campaign.data.isEmpty()) { campaignRepository.campaignFlow,
) { settings, campaign -> campaign to settings }
.distinctUntilChanged()
.flatMapLatest { (campaign, settings) ->
val hideOverruled = hideOverruled(campaign, settings)
val data = fetch(campaign, settings)
when (data.isEmpty()) {
true -> flowOf(emptyList()) true -> flowOf(emptyList())
else -> combine<CharacterPortraitUio?, List<CharacterPortraitUio>>( else -> combine<CharacterPortraitUio?, List<CharacterPortraitUio>>(
flows = campaign.data.map { entry -> flows = data.map { entry ->
combine( combine(
characterRepository.characterDetailFlow(characterSheetId = entry.key.characterSheetId), characterRepository.characterDetailFlow(characterSheetId = entry.key.characterSheetId),
alterationRepository.alterationsFlow(characterInstanceId = entry.key), alterationRepository.alterationsFlow(characterInstanceId = entry.key),
settingsRepository.settingsFlow(), ) { sheet, alterations ->
) { sheet, alterations, settings ->
ribbonFactory.convertToPlayerPortraitUio( ribbonFactory.convertToPlayerPortraitUio(
characterSheet = sheet, characterSheet = sheet,
alterations = alterations, alterations = alterations,
characterInstanceId = entry.key, characterInstanceId = entry.key,
characterInstance = entry.value, characterInstance = entry.value,
hideOverruled = hideOverruled,
enableCharacterId = settings.isGameMaster ?: false, enableCharacterId = settings.isGameMaster ?: false,
enableCharacterSheet = enableCharacterSheet || settings.isGameMaster ?: false, enableCharacterSheet = enableCharacterSheet || settings.isGameMaster ?: false,
enableCharacterStats = enableCharacterStats || settings.isGameMaster ?: false, enableCharacterStats = enableCharacterStats || settings.isGameMaster ?: false,

View file

@ -5,6 +5,7 @@ import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository 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.CharacterRibbonFactory
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonViewModel
import com.pixelized.shared.lwa.model.campaign.Campaign import com.pixelized.shared.lwa.model.campaign.Campaign
@ -24,8 +25,18 @@ class NpcRibbonViewModel(
campaignRepository = campaignRepository, campaignRepository = campaignRepository,
ribbonFactory = ribbonFactory, ribbonFactory = ribbonFactory,
) { ) {
override val Campaign.data get() = if (options.showNpcs) npcs else emptyMap() override fun fetch(
campaign: Campaign,
settings: Settings,
): Map<Campaign.CharacterInstance.Id, Campaign.CharacterInstance> {
return if (campaign.options.showNpcs || settings.isGameMaster == true) campaign.npcs else emptyMap()
}
override fun hideOverruled(campaign: Campaign, settings: Settings): Boolean {
return !campaign.options.showNpcs && settings.isGameMaster == true
}
override val enableCharacterSheet = false override val enableCharacterSheet = false
override val enableCharacterStats = false override val enableCharacterStats = false
} }

View file

@ -5,6 +5,7 @@ import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository 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.CharacterRibbonFactory
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonViewModel
import com.pixelized.shared.lwa.model.campaign.Campaign import com.pixelized.shared.lwa.model.campaign.Campaign
@ -24,8 +25,18 @@ class PlayerRibbonViewModel(
campaignRepository = campaignRepository, campaignRepository = campaignRepository,
ribbonFactory = ribbonFactory, ribbonFactory = ribbonFactory,
) { ) {
override val Campaign.data get() = if (options.showParty) characters else emptyMap() override fun fetch(
campaign: Campaign,
settings: Settings,
): Map<Campaign.CharacterInstance.Id, Campaign.CharacterInstance> {
return if (campaign.options.showParty) campaign.characters else emptyMap()
}
override fun hideOverruled(campaign: Campaign, settings: Settings): Boolean {
return !campaign.options.showParty && settings.isGameMaster == true
}
override val enableCharacterSheet = true override val enableCharacterSheet = true
override val enableCharacterStats = true override val enableCharacterStats = true
} }