Add auto check feature to skill after a successfull roll.

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2025-04-12 11:48:55 +02:00
parent 9588a3bfaf
commit 04b203239d
9 changed files with 116 additions and 35 deletions

View file

@ -31,7 +31,7 @@ class PortraitOverlayViewModel(
.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = null
initialValue = defaultPortrait()
)
val options = settingsRepository.settingsFlow()
@ -45,10 +45,7 @@ class PortraitOverlayViewModel(
.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = PortraitOptionUio(
isGameMaster = false,
isBrowserAvailable = false,
)
initialValue = defaultOptions()
)
suspend fun dismissPortrait() {
@ -68,4 +65,11 @@ class PortraitOverlayViewModel(
}
}
}
private fun defaultPortrait() = null
private fun defaultOptions() = PortraitOptionUio(
isGameMaster = false,
isBrowserAvailable = false,
)
}

View file

@ -49,13 +49,37 @@ class RollHostState {
}
@Stable
enum class RollResult {
Dismissed,
CriticalSuccess,
SpecialSuccess,
Success,
Failure,
CriticalFailure,
interface RollResult {
@Stable
data class BoundedRollResult(
val result: Result,
val difficulty: Difficulty,
) : RollResult {
@Stable
enum class Result {
CriticalSuccess,
SpecialSuccess,
Success,
Failure,
CriticalFailure,
}
@Stable
enum class Difficulty{
Easy,
Normal,
Hard,
Impossible
}
}
@Stable
data class BoundlessRollResult(
val value: Int,
) : RollResult
data object Dismissed: RollResult
}
@Stable

View file

@ -83,6 +83,7 @@ data class DifficultyUio(
val open: Boolean,
val difficulty: Difficulty,
) {
@Stable
enum class Difficulty {
EASY, NORMAL, HARD, IMPOSSIBLE
}

View file

@ -212,16 +212,29 @@ class RollViewModel(
}
}
lastRollResult = rollStep?.let {
when (roll) {
in it.criticalSuccess -> RollResult.CriticalSuccess
in it.specialSuccess -> RollResult.SpecialSuccess
in it.success -> RollResult.Success
in it.failure -> RollResult.Failure
in it.criticalFailure -> RollResult.CriticalFailure
else -> RollResult.Dismissed
}
} ?: RollResult.Dismissed
// build the roll result.
lastRollResult = if (rollStep != null) {
RollResult.BoundedRollResult(
result = when (roll) {
in rollStep.criticalSuccess -> RollResult.BoundedRollResult.Result.CriticalSuccess
in rollStep.specialSuccess -> RollResult.BoundedRollResult.Result.SpecialSuccess
in rollStep.success -> RollResult.BoundedRollResult.Result.Success
in rollStep.criticalFailure -> RollResult.BoundedRollResult.Result.CriticalFailure
else -> RollResult.BoundedRollResult.Result.Failure
},
difficulty = when (_rollDifficulty.value?.difficulty) {
Difficulty.IMPOSSIBLE -> RollResult.BoundedRollResult.Difficulty.Impossible
Difficulty.HARD -> RollResult.BoundedRollResult.Difficulty.Hard
Difficulty.NORMAL -> RollResult.BoundedRollResult.Difficulty.Normal
else -> RollResult.BoundedRollResult.Difficulty.Easy
}
)
} else {
RollResult.BoundlessRollResult(
value = roll,
)
}
_rollResult.value = RollResultUio(
label = resultLabel ?: "",

View file

@ -37,6 +37,7 @@ import com.pixelized.desktop.lwa.ui.composable.character.characteristic.Characte
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialogViewModel
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeader
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeaderUio
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheet
@ -123,6 +124,10 @@ fun CharacterDetailPanel(
onSkill = {
scope.launch {
val result = roll.showRollOverlay(roll = it.roll)
detailViewModel.onSkillUse(
skillId = it.skillId,
result = result,
)
println("result: $result")
}
},

View file

@ -8,6 +8,9 @@ import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.desktop.lwa.ui.navigation.window.WindowController
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToCharacterSheetEdit
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult.BoundedRollResult.Difficulty
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult.BoundedRollResult.Result
import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@ -15,7 +18,6 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__edit__title
import org.jetbrains.compose.resources.getString
@ -40,7 +42,7 @@ class CharacterDetailViewModel(
characterSheetRepository.characterDetailFlow(characterSheetId = characterSheetId),
alterationRepository.fieldAlterationsFlow(characterSheetId = characterSheetId),
settingRepository.settingsFlow()
) { characterSheet, alterations, settings, ->
) { characterSheet, alterations, settings ->
characterDetailFactory.convertToCharacterDetailHeaderUio(
characterSheetId = characterSheetId,
characterSheet = characterSheet,
@ -100,12 +102,41 @@ class CharacterDetailViewModel(
displayedCharacterId.value = null
}
suspend fun onSkillUse(
skillId: String,
result: RollResult,
) {
val characterSheetId = displayedCharacterId.value ?: return
// check if the RollResult is a BoundedRollResult. can work with other roll result.
val roll = result as? RollResult.BoundedRollResult ?: return
// check if the roll is a success with some challenge.
val difficulty = roll.difficulty != Difficulty.Easy
val success = roll.result.let {
it == Result.Success || it == Result.SpecialSuccess || it == Result.CriticalSuccess
}
if (!success || !difficulty) return
// Get the skill that have been rolled.
val skill = characterSheetRepository
.characterDetail(characterSheetId = characterSheetId)
?.skill(id = skillId)
// Check the skill if needed.
if (skill?.used == false) {
network.share(
message = CharacterSheetEvent.UpdateSkillUsageEvent(
timestamp = System.currentTimeMillis(),
characterSheetId = characterSheetId,
skillId = skillId,
used = true,
)
)
}
}
suspend fun onSkillUse(
skillId: String,
used: Boolean,
) {
val characterSheetId = displayedCharacterId.value ?: return
network.share(
message = CharacterSheetEvent.UpdateSkillUsageEvent(
timestamp = System.currentTimeMillis(),

View file

@ -2,8 +2,8 @@ package com.pixelized.desktop.lwa.ui.screen.levelup
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult.BoundedRollResult.Result
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristicUio
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkillUio
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSubCharacteristicUio
@ -11,7 +11,6 @@ import com.pixelized.shared.lwa.model.AlteredCharacterSheet
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.campaign.Campaign
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import com.pixelized.shared.lwa.parser.expression.Expression
import com.pixelized.shared.lwa.usecase.ExpressionUseCase
@ -233,7 +232,7 @@ class LevelUpFactory(
fun convertToLevelUpSectionUio(
characterSheetId: String,
characterSheet: CharacterSheet?,
results: Map<String, RollResult>,
results: Map<String, RollResult.BoundedRollResult>,
): LevelUpSectionUio? {
if (characterSheet == null) return null
@ -286,7 +285,7 @@ class LevelUpFactory(
characterSheetId: String,
alteredCharacterSheet: AlteredCharacterSheet,
skill: CharacterSheet.Skill,
results: Map<String, RollResult>,
results: Map<String, RollResult.BoundedRollResult>,
): LevelUpSkillUio? {
if (!skill.used) return null
@ -326,8 +325,8 @@ class LevelUpFactory(
}
}
fun Map<String, RollResult>.isSkillLeveledUp(skillId: String): Boolean {
return this.getOrElse(skillId) { RollResult.Dismissed }.let {
it == RollResult.CriticalSuccess || it == RollResult.SpecialSuccess || it == RollResult.Success
}
fun Map<String, RollResult.BoundedRollResult>.isSkillLeveledUp(skillId: String): Boolean {
return this[skillId]?.let { roll ->
roll.result == Result.CriticalSuccess || roll.result == Result.SpecialSuccess || roll.result == Result.Success
} ?: false
}

View file

@ -31,7 +31,7 @@ class LevelUpViewModel(
private val _errors = MutableSharedFlow<ErrorSnackUio>()
val error: SharedFlow<ErrorSnackUio> = _errors
private val results = MutableStateFlow<Map<String, RollResult>>(emptyMap())
private val results = MutableStateFlow<Map<String, RollResult.BoundedRollResult>>(emptyMap())
private val selectedCharacteristicId = MutableStateFlow<String?>(null)
val header = combine(
@ -89,7 +89,7 @@ class LevelUpViewModel(
fun applyRollResult(skillId: String, result: RollResult) {
// Discard this case.
if (result == RollResult.Dismissed) return
if (result !is RollResult.BoundedRollResult) return
// If not save the roll result to the map.
results.value = results.value.toMutableMap().also {
it[skillId] = result