Add characteristic + common / special / magic skill to detail panel.
This commit is contained in:
parent
b6b135cd40
commit
b6d02c21be
28 changed files with 633 additions and 135 deletions
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:pathData="M491,621q70,0 119,-45t49,-109q0,-57 -36.5,-96.5T534,331q-47,0 -79.5,30T422,435q0,19 7.5,37t21.5,33l57,-57q-3,-2 -4.5,-5t-1.5,-7q0,-11 9,-17.5t23,-6.5q20,0 33,16.5t13,39.5q0,31 -25.5,52.5T492,542q-47,0 -79.5,-38T380,411q0,-29 11,-55.5t31,-46.5l-57,-57q-32,31 -49,72t-17,86q0,88 56,149.5T491,621ZM240,880v-172q-57,-52 -88.5,-121.5T120,440q0,-150 105,-255t255,-105q125,0 221.5,73.5T827,345l52,205q5,19 -7,34.5T840,600h-80v120q0,33 -23.5,56.5T680,800h-80v80h-80v-160h160v-200h108l-38,-155q-23,-91 -98,-148t-172,-57q-116,0 -198,81t-82,197q0,60 24.5,114t69.5,96l26,24v208h-80ZM494,520Z"
|
||||
android:fillColor="#5f6368"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:pathData="M440,459ZM440,840L313,726q-72,-65 -123.5,-116t-85,-96q-33.5,-45 -49,-87T40,339q0,-94 63,-156.5T260,120q52,0 99,22t81,62q34,-40 81,-62t99,-22q81,0 136,45.5T831,280h-85q-18,-40 -53,-60t-73,-20q-51,0 -88,27.5T463,300h-46q-31,-45 -70.5,-72.5T260,200q-57,0 -98.5,39.5T120,339q0,33 14,67t50,78.5q36,44.5 98,104T440,732q26,-23 61,-53t56,-50l9,9 19.5,19.5L605,677l9,9q-22,20 -56,49.5T498,788l-58,52ZM720,680v-120L600,560v-80h120v-120h80v120h120v80L800,560v120h-80Z"
|
||||
android:fillColor="#5f6368"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:pathData="M480,880q-139,-35 -229.5,-159.5T160,444v-244l320,-120 320,120v244q0,152 -90.5,276.5T480,880ZM480,796q104,-33 172,-132t68,-220v-189l-240,-90 -240,90v189q0,121 68,220t172,132ZM480,480Z"
|
||||
android:fillColor="#5f6368"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:pathData="M762,864 L645,748l-88,88 -28,-28q-23,-23 -23,-57t23,-57l169,-169q23,-23 57,-23t57,23l28,28 -88,88 116,117q12,12 12,28t-12,28l-50,50q-12,12 -28,12t-28,-12ZM880,236L426,690l5,4q23,23 23,57t-23,57l-28,28 -88,-88L198,864q-12,12 -28,12t-28,-12l-50,-50q-12,-12 -12,-28t12,-28l116,-117 -88,-88 28,-28q23,-23 57,-23t57,23l4,5 454,-454h160v160ZM334,377l24,-23 23,-24 -23,24 -24,23ZM278,434L80,236v-160h160l198,198 -57,56 -174,-174h-47v47l174,174 -56,57ZM370,633 L800,203v-47h-47L323,586l47,47ZM370,633 L346,610 323,586 346,610 370,633Z"
|
||||
android:fillColor="#5f6368"/>
|
||||
</vector>
|
||||
|
|
@ -15,6 +15,7 @@ import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
|||
import com.pixelized.desktop.lwa.repository.settings.SettingsStore
|
||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
|
||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
|
||||
|
|
@ -106,6 +107,7 @@ val factoryDependencies
|
|||
|
||||
val viewModelDependencies
|
||||
get() = module {
|
||||
viewModelOf(::CampaignViewModel)
|
||||
viewModelOf(::MainPageViewModel)
|
||||
viewModelOf(::CharacterSheetViewModel)
|
||||
viewModelOf(::CharacterSheetEditViewModel)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.pixelized.desktop.lwa.repository.alteration
|
|||
|
||||
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.campaign.Campaign.CharacterInstance
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
|
@ -20,7 +20,7 @@ class AlterationRepository(
|
|||
private val store: AlterationStore,
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
private val activeAlterationMapFlow: StateFlow<Map<Campaign.CharacterInstance.Id, Map<String, List<FieldAlteration>>>> =
|
||||
private val activeAlterationMapFlow: StateFlow<Map<CharacterInstance.Id, Map<String, List<FieldAlteration>>>> =
|
||||
combine(
|
||||
store.alterations,
|
||||
store.active,
|
||||
|
|
@ -38,17 +38,36 @@ class AlterationRepository(
|
|||
)
|
||||
|
||||
fun alterationsFlow(
|
||||
characterId: Campaign.CharacterInstance.Id,
|
||||
characterId: CharacterInstance.Id,
|
||||
): Flow<Map<String, List<FieldAlteration>>> {
|
||||
return activeAlterationMapFlow.map { it[characterId] ?: emptyMap() }
|
||||
}
|
||||
|
||||
fun alterations(
|
||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
||||
characterInstanceId: CharacterInstance.Id,
|
||||
): Map<String, List<FieldAlteration>> {
|
||||
return activeAlterationMapFlow.value[characterInstanceId] ?: emptyMap()
|
||||
}
|
||||
|
||||
suspend fun updateActiveAlterations(
|
||||
characterInstanceId: CharacterInstance.Id,
|
||||
) {
|
||||
store.updateActiveAlterations(
|
||||
characterInstanceId = characterInstanceId,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun toggleActiveAlteration(
|
||||
characterInstanceId: CharacterInstance.Id,
|
||||
alterationId: String,
|
||||
) {
|
||||
// alteration was active for the character toggle it off.
|
||||
store.toggleActiveAlteration(
|
||||
characterInstance = characterInstanceId,
|
||||
alterationId = alterationId,
|
||||
)
|
||||
}
|
||||
|
||||
private fun transformToAlterationFieldMap(
|
||||
alterations: Map<String, Alteration>,
|
||||
actives: List<String>,
|
||||
|
|
@ -71,15 +90,4 @@ class AlterationRepository(
|
|||
}
|
||||
return fieldAlterations
|
||||
}
|
||||
|
||||
suspend fun toggleActiveAlteration(
|
||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
||||
alterationId: String,
|
||||
) {
|
||||
// alteration was active for the character toggle it off.
|
||||
store.toggleActiveAlteration(
|
||||
characterInstance = characterInstanceId,
|
||||
alterationId = alterationId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,16 @@ class AlterationStore(
|
|||
_alterations.value = loadAlteration()
|
||||
}
|
||||
|
||||
suspend fun updateActiveAlterations(
|
||||
characterInstanceId: CharacterInstance.Id,
|
||||
) {
|
||||
_active.value = _active.value.toMutableMap().also {
|
||||
it[characterInstanceId] = loadActiveAlterations(
|
||||
characterInstanceId = characterInstanceId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadAlteration(): Map<String, Alteration> {
|
||||
val request = client.alterations()
|
||||
val data = request.map { alterationFactory.convertFromJson(json = it) }
|
||||
|
|
@ -54,9 +64,6 @@ class AlterationStore(
|
|||
characterSheetId = characterInstanceId.characterSheetId,
|
||||
instanceId = characterInstanceId.instanceId,
|
||||
)
|
||||
_active.value = _active.value.toMutableMap().also {
|
||||
it[characterInstanceId] = request
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,8 +109,9 @@ class CharacterSheetStore(
|
|||
|
||||
is UpdateSkillUsageMessage -> {
|
||||
updateCharacterSkillChange(
|
||||
characterId = payload.characterId,
|
||||
characterId = payload.characterSheetId,
|
||||
skillId = payload.skillId,
|
||||
used = payload.used,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -121,10 +122,12 @@ class CharacterSheetStore(
|
|||
private suspend fun updateCharacterSkillChange(
|
||||
characterId: String,
|
||||
skillId: String,
|
||||
used: Boolean,
|
||||
) {
|
||||
val character = useCase.updateSkillUsage(
|
||||
character = characterDetail(characterId = characterId),
|
||||
skillId = skillId,
|
||||
used = used,
|
||||
)
|
||||
_detailFlow.update(character)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -23,6 +23,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.key
|
||||
|
|
@ -51,6 +52,7 @@ import org.koin.compose.viewmodel.koinViewModel
|
|||
|
||||
@Composable
|
||||
fun CampaignScreen(
|
||||
campaignViewModel: CampaignViewModel = koinViewModel(),
|
||||
characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
|
||||
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel = koinViewModel(),
|
||||
dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(),
|
||||
|
|
@ -59,6 +61,7 @@ fun CampaignScreen(
|
|||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
networkViewModel.connect()
|
||||
campaignViewModel.init()
|
||||
}
|
||||
|
||||
KeyHandler {
|
||||
|
|
@ -87,7 +90,7 @@ fun CampaignScreen(
|
|||
top = {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.height(32.dp)
|
||||
// .height(32.dp)
|
||||
.fillMaxWidth(),
|
||||
elevation = 1.dp,
|
||||
) {
|
||||
|
|
@ -97,7 +100,7 @@ fun CampaignScreen(
|
|||
bottom = {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.height(48.dp)
|
||||
// .height(48.dp)
|
||||
.fillMaxWidth(),
|
||||
elevation = 1.dp,
|
||||
) {
|
||||
|
|
@ -120,7 +123,8 @@ fun CampaignScreen(
|
|||
modifier = Modifier
|
||||
.width(width = 128.dp * 4)
|
||||
.fillMaxHeight()
|
||||
.padding(all = 8.dp),
|
||||
.padding(all = 8.dp)
|
||||
.clip(shape = remember { RoundedCornerShape(16.dp) }),
|
||||
blurController = blurController,
|
||||
detailViewModel = characterDetailViewModel,
|
||||
rollViewModel = rollViewModel,
|
||||
|
|
@ -180,9 +184,11 @@ fun CampaignScreen(
|
|||
dismissedViewModel.changeDiminished(
|
||||
dialog = diminished
|
||||
)
|
||||
blurController.hide()
|
||||
dismissedViewModel.hideDiminishedDialog()
|
||||
},
|
||||
onDismissRequest = {
|
||||
blurController.hide()
|
||||
dismissedViewModel.hideDiminishedDialog()
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class CampaignViewModel(
|
||||
private val characterRepository: CharacterSheetRepository,
|
||||
private val alterationRepository: AlterationRepository,
|
||||
private val campaignRepository: CampaignRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
suspend fun init() {
|
||||
|
||||
viewModelScope.launch {
|
||||
campaignRepository.campaignFlow.collectLatest {
|
||||
it.characters.keys.forEach { id ->
|
||||
characterRepository.characterDetail(
|
||||
characterSheetId = id.characterSheetId,
|
||||
forceUpdate = true,
|
||||
)
|
||||
alterationRepository.updateActiveAlterations(
|
||||
characterInstanceId = id,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import androidx.compose.animation.fadeOut
|
|||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
|
|
@ -21,19 +22,16 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
|
||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
|
||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialog
|
||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogUio
|
||||
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
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetCharacteristicUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetSkillUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
|
||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||
|
|
@ -68,6 +66,7 @@ fun CharacterDetailPanel(
|
|||
},
|
||||
onDiminished = {
|
||||
scope.launch {
|
||||
blurController.show()
|
||||
characterDiminishedViewModel.showDiminishedDialog(
|
||||
characterInstanceId = it
|
||||
)
|
||||
|
|
@ -92,14 +91,22 @@ fun CharacterDetailPanel(
|
|||
}
|
||||
},
|
||||
onCharacteristic = {
|
||||
rollViewModel.prepareRoll(
|
||||
characterSheetId = detail.value.characterInstanceId?.characterSheetId!!,
|
||||
label = it.label,
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = (it.value.toIntOrNull() ?: 0) * 5,
|
||||
)
|
||||
blurController.show()
|
||||
rollViewModel.prepareRoll(roll = it.roll)
|
||||
rollViewModel.showOverlay()
|
||||
blurController.show()
|
||||
},
|
||||
onSkill = {
|
||||
rollViewModel.prepareRoll(roll = it.roll)
|
||||
rollViewModel.showOverlay()
|
||||
blurController.show()
|
||||
},
|
||||
onUseSkill = {
|
||||
scope.launch {
|
||||
detailViewModel.onSkillUse(
|
||||
skillId = it.skillId,
|
||||
used = it.used,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -113,6 +120,8 @@ fun CharacterDetailAnimatedPanel(
|
|||
onHp: (id: Campaign.CharacterInstance.Id) -> Unit,
|
||||
onPp: (id: Campaign.CharacterInstance.Id) -> Unit,
|
||||
onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit,
|
||||
onSkill: (CharacterDetailSheetSkillUio) -> Unit,
|
||||
onUseSkill: (CharacterDetailSheetSkillUio) -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier,
|
||||
|
|
@ -148,6 +157,8 @@ fun CharacterDetailAnimatedPanel(
|
|||
onHp = { onHp(it.characterInstanceId) },
|
||||
onPp = { onPp(it.characterInstanceId) },
|
||||
onCharacteristic = onCharacteristic,
|
||||
onSkill = onSkill,
|
||||
onUseSkill = onUseSkill,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -166,14 +177,19 @@ fun CharacterDetailContent(
|
|||
onHp: () -> Unit,
|
||||
onPp: () -> Unit,
|
||||
onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit,
|
||||
onSkill: (CharacterDetailSheetSkillUio) -> Unit,
|
||||
onUseSkill: (CharacterDetailSheetSkillUio) -> Unit,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
color = MaterialTheme.lwa.colorScheme.elevatedSurface,
|
||||
color = MaterialTheme.lwa.colorScheme.elevated.base1dp,
|
||||
) {
|
||||
Column {
|
||||
CharacterDetailHeader(
|
||||
modifier = Modifier.padding(start = 16.dp).fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.background(color = MaterialTheme.lwa.colorScheme.elevated.base2dp)
|
||||
.padding(bottom = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
header = header,
|
||||
onDismissRequest = onDismissRequest,
|
||||
onDiminished = onDiminished,
|
||||
|
|
@ -184,9 +200,12 @@ fun CharacterDetailContent(
|
|||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.verticalScroll(state = rememberScrollState())
|
||||
.padding(all = 16.dp),
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(top = 8.dp, bottom = 16.dp),
|
||||
sheet = sheet,
|
||||
onCharacteristic = onCharacteristic,
|
||||
onSkill = onSkill,
|
||||
onUseSkill = onUseSkill,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,16 @@ package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail
|
|||
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeaderUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetCharacteristicUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetSkillUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheetUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.roll.RollActionUio
|
||||
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
|
||||
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import com.pixelized.shared.lwa.model.campaign.damage
|
||||
import com.pixelized.shared.lwa.model.campaign.power
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import com.pixelized.shared.lwa.usecase.ExpressionUseCase
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__cha
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__con
|
||||
|
|
@ -26,9 +29,11 @@ import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics
|
|||
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__power
|
||||
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__strength
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import java.text.Collator
|
||||
|
||||
class CharacterDetailFactory(
|
||||
private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory,
|
||||
private val expressionUseCase: ExpressionUseCase,
|
||||
) {
|
||||
fun convertToCharacterDetailHeaderUio(
|
||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
||||
|
|
@ -54,14 +59,17 @@ class CharacterDetailFactory(
|
|||
maxHp = "$maxHp",
|
||||
pp = "${maxPp - characterInstance.power}",
|
||||
maxPp = "$maxPp",
|
||||
mov = "${alteredCharacterSheet.movement}"
|
||||
mov = "${alteredCharacterSheet.movement}",
|
||||
armor = "${alteredCharacterSheet.armor}",
|
||||
bonus = alteredCharacterSheet.damageBonus,
|
||||
grow = "${alteredCharacterSheet.hpGrow}",
|
||||
learn = "${alteredCharacterSheet.learning}",
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun convertToCharacterDetailSheetUio(
|
||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
||||
characterSheet: CharacterSheet?,
|
||||
characterInstance: Campaign.CharacterInstance,
|
||||
alterations: Map<String, List<FieldAlteration>>,
|
||||
): CharacterDetailSheetUio? {
|
||||
if (characterSheet == null) return null
|
||||
|
|
@ -81,6 +89,12 @@ class CharacterDetailFactory(
|
|||
title = getString(Res.string.character_sheet__characteristics__str),
|
||||
description = getString(Res.string.tooltip__characteristics__strength),
|
||||
),
|
||||
roll = RollActionUio(
|
||||
characterSheetId = characterSheet.id,
|
||||
label = getString(Res.string.character_sheet__characteristics__str),
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = alteredCharacterSheet.strength * 5,
|
||||
),
|
||||
),
|
||||
CharacterDetailSheetCharacteristicUio(
|
||||
label = getString(Res.string.character_sheet__characteristics__dex),
|
||||
|
|
@ -89,6 +103,12 @@ class CharacterDetailFactory(
|
|||
title = getString(Res.string.character_sheet__characteristics__dex),
|
||||
description = getString(Res.string.tooltip__characteristics__dexterity),
|
||||
),
|
||||
roll = RollActionUio(
|
||||
characterSheetId = characterSheet.id,
|
||||
label = getString(Res.string.character_sheet__characteristics__dex),
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = alteredCharacterSheet.dexterity * 5,
|
||||
),
|
||||
),
|
||||
CharacterDetailSheetCharacteristicUio(
|
||||
label = getString(Res.string.character_sheet__characteristics__con),
|
||||
|
|
@ -97,6 +117,12 @@ class CharacterDetailFactory(
|
|||
title = getString(Res.string.character_sheet__characteristics__con),
|
||||
description = getString(Res.string.tooltip__characteristics__constitution),
|
||||
),
|
||||
roll = RollActionUio(
|
||||
characterSheetId = characterSheet.id,
|
||||
label = getString(Res.string.character_sheet__characteristics__con),
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = alteredCharacterSheet.constitution * 5,
|
||||
),
|
||||
),
|
||||
CharacterDetailSheetCharacteristicUio(
|
||||
label = getString(Res.string.character_sheet__characteristics__hei),
|
||||
|
|
@ -105,6 +131,12 @@ class CharacterDetailFactory(
|
|||
title = getString(Res.string.character_sheet__characteristics__hei),
|
||||
description = getString(Res.string.tooltip__characteristics__height),
|
||||
),
|
||||
roll = RollActionUio(
|
||||
characterSheetId = characterSheet.id,
|
||||
label = getString(Res.string.character_sheet__characteristics__hei),
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = alteredCharacterSheet.height * 5,
|
||||
),
|
||||
),
|
||||
CharacterDetailSheetCharacteristicUio(
|
||||
label = getString(Res.string.character_sheet__characteristics__int),
|
||||
|
|
@ -113,6 +145,12 @@ class CharacterDetailFactory(
|
|||
title = getString(Res.string.character_sheet__characteristics__int),
|
||||
description = getString(Res.string.tooltip__characteristics__intelligence),
|
||||
),
|
||||
roll = RollActionUio(
|
||||
characterSheetId = characterSheet.id,
|
||||
label = getString(Res.string.character_sheet__characteristics__int),
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = alteredCharacterSheet.intelligence * 5,
|
||||
),
|
||||
),
|
||||
CharacterDetailSheetCharacteristicUio(
|
||||
label = getString(Res.string.character_sheet__characteristics__pow),
|
||||
|
|
@ -121,6 +159,12 @@ class CharacterDetailFactory(
|
|||
title = getString(Res.string.character_sheet__characteristics__pow),
|
||||
description = getString(Res.string.tooltip__characteristics__power),
|
||||
),
|
||||
roll = RollActionUio(
|
||||
characterSheetId = characterSheet.id,
|
||||
label = getString(Res.string.character_sheet__characteristics__pow),
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = alteredCharacterSheet.power * 5,
|
||||
),
|
||||
),
|
||||
CharacterDetailSheetCharacteristicUio(
|
||||
label = getString(Res.string.character_sheet__characteristics__cha),
|
||||
|
|
@ -129,8 +173,89 @@ class CharacterDetailFactory(
|
|||
title = getString(Res.string.character_sheet__characteristics__cha),
|
||||
description = getString(Res.string.tooltip__characteristics__charisma),
|
||||
),
|
||||
roll = RollActionUio(
|
||||
characterSheetId = characterSheet.id,
|
||||
label = getString(Res.string.character_sheet__characteristics__cha),
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = alteredCharacterSheet.charisma * 5,
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
commonSkills = characterSheet.commonSkills.map { skill ->
|
||||
val value = expressionUseCase.computeSkillValue(
|
||||
sheet = characterSheet,
|
||||
skill = skill,
|
||||
alterations = alterations,
|
||||
)
|
||||
CharacterDetailSheetSkillUio(
|
||||
skillId = skill.id,
|
||||
label = skill.label,
|
||||
value = "$value",
|
||||
used = skill.used,
|
||||
tooltips = skill.description?.let {
|
||||
TooltipUio(
|
||||
title = skill.label,
|
||||
description = it,
|
||||
)
|
||||
},
|
||||
roll = RollActionUio(
|
||||
characterSheetId = characterInstanceId.characterSheetId,
|
||||
label = skill.label,
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = value,
|
||||
),
|
||||
)
|
||||
}.sortedWith(compareBy(Collator.getInstance()) { it.label }),
|
||||
characterSheet.specialSkills.map { skill ->
|
||||
val value = expressionUseCase.computeSkillValue(
|
||||
sheet = characterSheet,
|
||||
skill = skill,
|
||||
alterations = alterations,
|
||||
)
|
||||
CharacterDetailSheetSkillUio(
|
||||
skillId = skill.id,
|
||||
label = skill.label,
|
||||
value = "$value",
|
||||
used = skill.used,
|
||||
tooltips = skill.description?.let {
|
||||
TooltipUio(
|
||||
title = skill.label,
|
||||
description = it,
|
||||
)
|
||||
},
|
||||
roll = RollActionUio(
|
||||
characterSheetId = characterInstanceId.characterSheetId,
|
||||
label = skill.label,
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = value,
|
||||
),
|
||||
)
|
||||
}.sortedWith(compareBy(Collator.getInstance()) { it.label }),
|
||||
magicSkill = characterSheet.magicSkills.map { skill ->
|
||||
val value = expressionUseCase.computeSkillValue(
|
||||
sheet = characterSheet,
|
||||
skill = skill,
|
||||
alterations = alterations,
|
||||
)
|
||||
CharacterDetailSheetSkillUio(
|
||||
skillId = skill.id,
|
||||
label = skill.label,
|
||||
value = "$value",
|
||||
used = skill.used,
|
||||
tooltips = skill.description?.let {
|
||||
TooltipUio(
|
||||
title = skill.label,
|
||||
description = it,
|
||||
)
|
||||
},
|
||||
roll = RollActionUio(
|
||||
characterSheetId = characterInstanceId.characterSheetId,
|
||||
label = skill.label,
|
||||
rollAction = "1d100",
|
||||
rollSuccessValue = value,
|
||||
),
|
||||
)
|
||||
}.sortedWith(compareBy(Collator.getInstance()) { it.label }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,9 @@ import androidx.lifecycle.viewModelScope
|
|||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import com.pixelized.shared.lwa.protocol.websocket.payload.UpdateSkillUsageMessage
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -18,6 +20,7 @@ class CharacterDetailViewModel(
|
|||
private val campaignRepository: CampaignRepository,
|
||||
private val alterationRepository: AlterationRepository,
|
||||
private val characterDetailFactory: CharacterDetailFactory,
|
||||
private val network: NetworkRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
private val displayedCharacterId = MutableStateFlow<Campaign.CharacterInstance.Id?>(null)
|
||||
|
|
@ -25,7 +28,6 @@ class CharacterDetailViewModel(
|
|||
val detail: StateFlow<CharacterDetailPanelUio> = displayedCharacterId
|
||||
.map { characterInstanceId ->
|
||||
if (characterInstanceId == null) return@map empty()
|
||||
|
||||
CharacterDetailPanelUio(
|
||||
characterInstanceId = characterInstanceId,
|
||||
header = combine(
|
||||
|
|
@ -45,14 +47,12 @@ class CharacterDetailViewModel(
|
|||
initialValue = null,
|
||||
),
|
||||
sheet = combine(
|
||||
campaignRepository.characterInstanceFlow(id = characterInstanceId),
|
||||
characterSheetRepository.characterDetailFlow(characterId = characterInstanceId.characterSheetId),
|
||||
alterationRepository.alterationsFlow(characterId = characterInstanceId),
|
||||
) { characterInstance, characterSheet, alterations ->
|
||||
) { characterSheet, alterations ->
|
||||
characterDetailFactory.convertToCharacterDetailSheetUio(
|
||||
characterInstanceId = characterInstanceId,
|
||||
characterSheet = characterSheet,
|
||||
characterInstance = characterInstance,
|
||||
alterations = alterations,
|
||||
)
|
||||
}.stateIn(
|
||||
|
|
@ -76,6 +76,20 @@ class CharacterDetailViewModel(
|
|||
displayedCharacterId.value = null
|
||||
}
|
||||
|
||||
suspend fun onSkillUse(
|
||||
skillId: String,
|
||||
used: Boolean,
|
||||
) {
|
||||
val characterSheetId = displayedCharacterId.value?.characterSheetId ?: return
|
||||
network.share(
|
||||
payload = UpdateSkillUsageMessage(
|
||||
characterSheetId = characterSheetId,
|
||||
skillId = skillId,
|
||||
used = used.not(),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun empty() = CharacterDetailPanelUio(
|
||||
characterInstanceId = null,
|
||||
header = MutableStateFlow(null),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
|
|
@ -19,14 +20,19 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_cognition_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_heart_plus_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_near_me
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_shield_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_skull_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_swords_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
|
||||
|
|
@ -40,11 +46,16 @@ data class CharacterDetailHeaderUio(
|
|||
val pp: String,
|
||||
val maxPp: String,
|
||||
val mov: String,
|
||||
val armor: String,
|
||||
val bonus: String,
|
||||
val grow: String,
|
||||
val learn: String,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun CharacterDetailHeader(
|
||||
modifier: Modifier = Modifier,
|
||||
iconSize: Dp = 14.dp,
|
||||
header: State<CharacterDetailHeaderUio?>,
|
||||
onDismissRequest: () -> Unit,
|
||||
onDiminished: () -> Unit,
|
||||
|
|
@ -54,7 +65,7 @@ fun CharacterDetailHeader(
|
|||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row {
|
||||
Row(modifier = Modifier.padding(start = 16.dp)) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f)
|
||||
.align(alignment = Alignment.CenterVertically),
|
||||
|
|
@ -84,14 +95,17 @@ fun CharacterDetailHeader(
|
|||
}
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 12.dp),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 16.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.clip(shape = CircleShape).clickable { onHp() },
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
||||
modifier = Modifier
|
||||
.padding(bottom = 4.dp, end = 2.dp)
|
||||
.size(size = iconSize),
|
||||
painter = painterResource(Res.drawable.ic_heart_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
|
|
@ -114,7 +128,9 @@ fun CharacterDetailHeader(
|
|||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
||||
modifier = Modifier
|
||||
.padding(bottom = 4.dp, end = 2.dp)
|
||||
.size(size = iconSize),
|
||||
painter = painterResource(Res.drawable.ic_water_drop_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
|
|
@ -132,11 +148,16 @@ fun CharacterDetailHeader(
|
|||
text = "/${header.value?.maxPp ?: ""}",
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
||||
modifier = Modifier
|
||||
.padding(bottom = 4.dp, end = 2.dp)
|
||||
.size(size = iconSize),
|
||||
painter = painterResource(Res.drawable.ic_near_me),
|
||||
contentDescription = null,
|
||||
)
|
||||
|
|
@ -151,6 +172,70 @@ fun CharacterDetailHeader(
|
|||
text = "m",
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 4.dp, end = 2.dp)
|
||||
.size(size = iconSize),
|
||||
painter = painterResource(Res.drawable.ic_shield_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.h6,
|
||||
text = header.value?.armor ?: "",
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 4.dp, end = 2.dp)
|
||||
.size(size = iconSize),
|
||||
painter = painterResource(Res.drawable.ic_swords_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.h6,
|
||||
text = header.value?.bonus ?: "",
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 4.dp, end = 2.dp)
|
||||
.size(size = iconSize),
|
||||
painter = painterResource(Res.drawable.ic_heart_plus_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.h6,
|
||||
text = header.value?.grow ?: "",
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 4.dp, end = 2.dp)
|
||||
.size(size = iconSize),
|
||||
painter = painterResource(Res.drawable.ic_cognition_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.h6,
|
||||
text = header.value?.learn ?: "",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
|
||||
@Stable
|
||||
data class CharacterDetailSheetUio(
|
||||
val characterInstanceId: Campaign.CharacterInstance.Id,
|
||||
val characteristics: List<CharacterDetailSheetCharacteristicUio>,
|
||||
val commonSkills: List<CharacterDetailSheetSkillUio>,
|
||||
val specialSkill: List<CharacterDetailSheetSkillUio>,
|
||||
val magicSkill: List<CharacterDetailSheetSkillUio>,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun CharacterDetailSheet(
|
||||
modifier: Modifier = Modifier,
|
||||
sheet: State<CharacterDetailSheetUio?>,
|
||||
onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit,
|
||||
onSkill: (CharacterDetailSheetSkillUio) -> Unit,
|
||||
onUseSkill: (CharacterDetailSheetSkillUio) -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
|
||||
) {
|
||||
sheet.value?.characteristics?.forEach {
|
||||
CharacterDetailSheetCharacteristic(
|
||||
modifier = Modifier.size(width = 80.dp, height = 120.dp),
|
||||
characteristic = it,
|
||||
onClick = { onCharacteristic(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
|
||||
) {
|
||||
DecoratedBox(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Column {
|
||||
sheet.value?.commonSkills?.forEach { skill ->
|
||||
CharacterDetailSheetSkill(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
skill = skill,
|
||||
onSkill = onSkill,
|
||||
onUse = onUseSkill,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Column {
|
||||
sheet.value?.specialSkill?.forEach { skill ->
|
||||
CharacterDetailSheetSkill(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
skill = skill,
|
||||
onSkill = onSkill,
|
||||
onUse = onUseSkill,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Column {
|
||||
sheet.value?.magicSkill?.forEach { skill ->
|
||||
CharacterDetailSheetSkill(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
skill = skill,
|
||||
onSkill = onSkill,
|
||||
onUse = onUseSkill,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15,12 +15,14 @@ import androidx.compose.ui.unit.dp
|
|||
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
|
||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout
|
||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.roll.RollActionUio
|
||||
|
||||
@Stable
|
||||
data class CharacterDetailSheetCharacteristicUio(
|
||||
val value: String,
|
||||
val label: String,
|
||||
val tooltips: TooltipUio?,
|
||||
val tooltips: TooltipUio,
|
||||
val roll: RollActionUio,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Checkbox
|
||||
import androidx.compose.material.CheckboxDefaults
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout
|
||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.roll.RollActionUio
|
||||
|
||||
@Stable
|
||||
data class CharacterDetailSheetSkillUio(
|
||||
val skillId: String,
|
||||
val label: String,
|
||||
val value: String,
|
||||
val used: Boolean,
|
||||
val tooltips: TooltipUio?,
|
||||
val roll: RollActionUio,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun CharacterDetailSheetSkill(
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(start = 8.dp),
|
||||
skill: CharacterDetailSheetSkillUio,
|
||||
onSkill: (CharacterDetailSheetSkillUio) -> Unit,
|
||||
onUse: (CharacterDetailSheetSkillUio) -> Unit,
|
||||
) {
|
||||
TooltipLayout(
|
||||
tooltip = skill.tooltips,
|
||||
content = {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = { onSkill(skill) })
|
||||
.padding(paddingValues = paddingValues)
|
||||
.then(other = modifier),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.body1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = skill.label
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.body1,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colors.primary,
|
||||
text = skill.value,
|
||||
)
|
||||
Checkbox(
|
||||
modifier = Modifier.size(size = 32.dp),
|
||||
checked = skill.used,
|
||||
colors = CheckboxDefaults.colors(
|
||||
checkedColor = MaterialTheme.colors.primary,
|
||||
),
|
||||
onCheckedChange = { onUse(skill) },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet
|
||||
|
||||
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.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
|
||||
@Stable
|
||||
data class CharacterDetailSheetUio(
|
||||
val characterInstanceId: Campaign.CharacterInstance.Id,
|
||||
val characteristics: List<CharacterDetailSheetCharacteristicUio>,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun CharacterDetailSheet(
|
||||
modifier: Modifier = Modifier,
|
||||
sheet: State<CharacterDetailSheetUio?>,
|
||||
onCharacteristic: (CharacterDetailSheetCharacteristicUio) -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
|
||||
) {
|
||||
sheet.value?.characteristics?.forEach {
|
||||
CharacterDetailSheetCharacteristic(
|
||||
modifier = Modifier.size(width = 80.dp, height = 120.dp),
|
||||
characteristic = it,
|
||||
onClick = { onCharacteristic(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
|
|
@ -10,6 +12,7 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
|
|
@ -52,10 +55,11 @@ fun PlayerPortrait(
|
|||
) {
|
||||
val colorScheme = MaterialTheme.lwa.colorScheme
|
||||
|
||||
DecoratedBox(
|
||||
Box (
|
||||
modifier = modifier
|
||||
.size(size = size)
|
||||
.clip(shape = remember { RoundedCornerShape(8.dp) })
|
||||
.background(color = MaterialTheme.lwa.colorScheme.elevated.base1dp)
|
||||
.clickable { onCharacter(character.id) },
|
||||
) {
|
||||
AsyncImage(
|
||||
|
|
@ -73,17 +77,17 @@ fun PlayerPortrait(
|
|||
drawRect(
|
||||
brush = Brush.verticalGradient(
|
||||
listOf(
|
||||
colorScheme.elevatedSurface.copy(alpha = 0.0f),
|
||||
colorScheme.elevatedSurface.copy(alpha = 0.0f),
|
||||
colorScheme.elevatedSurface.copy(alpha = 0.0f),
|
||||
colorScheme.elevatedSurface.copy(alpha = 0.5f),
|
||||
colorScheme.elevatedSurface.copy(alpha = 0.8f),
|
||||
colorScheme.elevated.base1dp.copy(alpha = 0.0f),
|
||||
colorScheme.elevated.base1dp.copy(alpha = 0.0f),
|
||||
colorScheme.elevated.base1dp.copy(alpha = 0.0f),
|
||||
colorScheme.elevated.base1dp.copy(alpha = 0.5f),
|
||||
colorScheme.elevated.base1dp.copy(alpha = 0.8f),
|
||||
)
|
||||
)
|
||||
)
|
||||
drawContent()
|
||||
}
|
||||
.padding(all = 2.dp),
|
||||
.padding(vertical = 2.dp, horizontal = 4.dp),
|
||||
verticalArrangement = Arrangement.aligned(alignment = Alignment.Bottom),
|
||||
) {
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -62,19 +62,6 @@ class PlayerRibbonViewModel(
|
|||
|
||||
private val rolls = hashMapOf<String, MutableState<PlayerPortraitRollUio?>>()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
campaignRepository.campaignFlow.collectLatest {
|
||||
it.characters.keys.forEach { id ->
|
||||
characterRepository.characterDetail(
|
||||
characterSheetId = id.characterSheetId,
|
||||
forceUpdate = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Stable
|
||||
fun roll(characterSheetId: String): State<PlayerPortraitRollUio?> {
|
||||
|
|
|
|||
|
|
@ -84,8 +84,9 @@ class CharacterSheetViewModel(
|
|||
viewModelScope.launch {
|
||||
network.share(
|
||||
payload = UpdateSkillUsageMessage(
|
||||
characterId = argument.characterInstanceId.characterSheetId,
|
||||
characterSheetId = argument.characterInstanceId.characterSheetId,
|
||||
skillId = skill.id,
|
||||
used = skill.used.not(),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.roll
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
|
||||
@Stable
|
||||
data class RollActionUio(
|
||||
val characterSheetId: String,
|
||||
val label: String,
|
||||
val rollAction: String,
|
||||
val rollSuccessValue: Int?,
|
||||
)
|
||||
|
|
@ -97,6 +97,15 @@ class RollViewModel(
|
|||
}
|
||||
|
||||
fun prepareRoll(
|
||||
roll: RollActionUio,
|
||||
) = prepareRoll(
|
||||
characterSheetId = roll.characterSheetId,
|
||||
label = roll.label,
|
||||
rollAction = roll.rollAction,
|
||||
rollSuccessValue = roll.rollSuccessValue,
|
||||
)
|
||||
|
||||
private fun prepareRoll(
|
||||
characterSheetId: String,
|
||||
label: String,
|
||||
rollAction: String,
|
||||
|
|
|
|||
|
|
@ -14,21 +14,34 @@ import kotlin.math.ln
|
|||
@Stable
|
||||
data class LwaColorTheme(
|
||||
val base: Colors,
|
||||
val elevatedSurface: Color,
|
||||
)
|
||||
val elevated: Elevated,
|
||||
) {
|
||||
@Stable
|
||||
data class Elevated(
|
||||
val base1dp: Color,
|
||||
val base2dp: Color,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Stable
|
||||
fun darkLwaColorTheme(
|
||||
base: Colors = darkColors(),
|
||||
elevatedSurface: Color = base.calculateElevatedColor(
|
||||
color = base.surface,
|
||||
onColor = base.onSurface,
|
||||
elevation = 1.dp,
|
||||
elevated: LwaColorTheme.Elevated = LwaColorTheme.Elevated(
|
||||
base1dp = base.calculateElevatedColor(
|
||||
color = base.surface,
|
||||
onColor = base.onSurface,
|
||||
elevation = 1.dp,
|
||||
),
|
||||
base2dp = base.calculateElevatedColor(
|
||||
color = base.surface,
|
||||
onColor = base.onSurface,
|
||||
elevation = 2.dp,
|
||||
),
|
||||
),
|
||||
): LwaColorTheme = LwaColorTheme(
|
||||
base = base,
|
||||
elevatedSurface = elevatedSurface,
|
||||
elevated = elevated,
|
||||
)
|
||||
|
||||
@ReadOnlyComposable
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.pixelized.server.lwa.model.character
|
||||
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJson
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJsonFactory
|
||||
import com.pixelized.shared.lwa.protocol.rest.CharacterPreviewJson
|
||||
|
|
@ -39,7 +38,7 @@ class CharacterSheetService(
|
|||
return store.save(sheet = factory.convertFromJson(character))
|
||||
}
|
||||
|
||||
fun deleteCharacter(characterId: String) : Boolean {
|
||||
fun deleteCharacter(characterId: String): Boolean {
|
||||
return store.delete(id = characterId)
|
||||
}
|
||||
|
||||
|
|
@ -74,13 +73,15 @@ class CharacterSheetService(
|
|||
}
|
||||
|
||||
fun updateCharacterSkillUsage(
|
||||
characterId: String,
|
||||
characterSheetId: String,
|
||||
skillId: String,
|
||||
used: Boolean,
|
||||
) {
|
||||
sheets[characterId]?.let { character ->
|
||||
sheets[characterSheetId]?.let { character ->
|
||||
val update = useCase.updateSkillUsage(
|
||||
character = character,
|
||||
skillId = skillId,
|
||||
used = used,
|
||||
)
|
||||
store.save(sheet = update)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,9 @@ class Engine(
|
|||
}
|
||||
|
||||
is UpdateSkillUsageMessage -> characterService.updateCharacterSkillUsage(
|
||||
characterId = data.characterId,
|
||||
skillId = data.skillId
|
||||
characterSheetId = data.characterSheetId,
|
||||
skillId = data.skillId,
|
||||
used = data.used,
|
||||
)
|
||||
|
||||
RestSynchronisation.Campaign -> Unit // Handle in the Rest
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import kotlinx.serialization.Serializable
|
|||
|
||||
@Serializable
|
||||
data class UpdateSkillUsageMessage(
|
||||
val characterId: String,
|
||||
val characterSheetId: String,
|
||||
val skillId: String,
|
||||
val used: Boolean,
|
||||
) : MessagePayload
|
||||
|
|
@ -89,16 +89,17 @@ class CharacterSheetUseCase {
|
|||
fun updateSkillUsage(
|
||||
character: CharacterSheet,
|
||||
skillId: String,
|
||||
used: Boolean,
|
||||
): CharacterSheet {
|
||||
return character.copy(
|
||||
commonSkills = character.commonSkills.map { skill ->
|
||||
skill.takeIf { skill.id == skillId }?.copy(used = skill.used.not()) ?: skill
|
||||
skill.takeIf { skill.id == skillId }?.copy(used = used) ?: skill
|
||||
},
|
||||
specialSkills = character.specialSkills.map { skill ->
|
||||
skill.takeIf { skill.id == skillId }?.copy(used = skill.used.not()) ?: skill
|
||||
skill.takeIf { skill.id == skillId }?.copy(used = used) ?: skill
|
||||
},
|
||||
magicSkills = character.magicSkills.map { skill ->
|
||||
skill.takeIf { skill.id == skillId }?.copy(used = skill.used.not()) ?: skill
|
||||
skill.takeIf { skill.id == skillId }?.copy(used = used) ?: skill
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue