Add characterSheet and Campaing to the server.

This commit is contained in:
Thomas Andres Gomez 2025-02-22 21:25:08 +01:00
parent 1e5f0d88ae
commit 495768e5fe
53 changed files with 879 additions and 513 deletions

View file

@ -9,6 +9,8 @@ import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
import com.pixelized.desktop.lwa.parser.word.WordParser
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
import com.pixelized.desktop.lwa.repository.alteration.AlterationStore
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
import com.pixelized.desktop.lwa.repository.campaign.CampaignStore
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetStore
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
@ -29,8 +31,7 @@ import com.pixelized.desktop.lwa.ui.screen.network.NetworkFactory
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
import com.pixelized.shared.lwa.model.campaign.CampaignRepository
import com.pixelized.shared.lwa.model.campaign.model.CampaignFactory
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
import io.ktor.client.HttpClient
import io.ktor.client.engine.HttpClientEngine
@ -78,6 +79,7 @@ val storeDependencies
singleOf(::CharacterSheetStore)
singleOf(::SettingsStore)
singleOf(::AlterationStore)
singleOf(::CampaignStore)
}
val repositoryDependencies
@ -97,7 +99,7 @@ val factoryDependencies
factoryOf(::NetworkFactory)
factoryOf(::SkillFieldFactory)
factoryOf(::SettingsFactory)
factoryOf(::CampaignFactory)
factoryOf(::CampaignJsonFactory)
}
val viewModelDependencies

View file

@ -4,7 +4,7 @@ package com.pixelized.desktop.lwa.business
import com.pixelized.desktop.lwa.parser.expression.Expression
import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
import com.pixelized.desktop.lwa.parser.word.Word
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import kotlin.math.max
import kotlin.math.min
@ -32,9 +32,7 @@ class ExpressionUseCase(
val bonus = context.evaluate(
expression = skill.bonus?.let(expressionParser::parse),
)
val level = context.evaluate(
expression = skill.level?.let(expressionParser::parse),
)
val level = max((skill.level - 1) * 5, 0)
return max(base + bonus + level + alterations, 0)
}

View file

@ -4,24 +4,24 @@ import com.pixelized.desktop.lwa.parser.expression.Expression
import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
import com.pixelized.desktop.lwa.repository.alteration.model.Alteration
import com.pixelized.desktop.lwa.repository.alteration.model.AlterationMetadata
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.ARMOR
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.DEX
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.HEI
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.MOV
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.STR
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.ACROBATICS_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.AID_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.ATHLETICS_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.BARGAIN_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.COMBAT_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.DISCRETION_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.INTIMIDATION_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.PERCEPTION_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.PERSUASION_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.SPIEL_ID
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.THROW_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.ARMOR
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.DEX
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.HEI
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.MOV
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.STR
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.ACROBATICS_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.AID_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.ATHLETICS_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.BARGAIN_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.COMBAT_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.DISCRETION_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.INTIMIDATION_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.PERCEPTION_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.PERSUASION_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.SPIEL_ID
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.THROW_ID
class AlterationStore(
private val expressionParser: ExpressionParser,

View file

@ -0,0 +1,40 @@
package com.pixelized.desktop.lwa.repository.campaign
import com.pixelized.shared.lwa.model.campaign.Campaign
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
class CampaignRepository(
store: CampaignStore,
) {
private val scope = CoroutineScope(Dispatchers.IO + Job())
private val campaign = store.campaignFlow()
.stateIn(
scope = scope,
started = SharingStarted.Eagerly,
initialValue = Campaign.EMPTY,
)
fun campaignFlow(): StateFlow<Campaign> = campaign
fun characterInstance(id: String): StateFlow<Campaign.CharacterInstance> {
return campaign
.mapNotNull {
it.characters[id]
}
.stateIn(
scope = scope,
started = SharingStarted.Eagerly,
initialValue = campaign.value.characters[id] ?: Campaign.CharacterInstance(
characteristic = emptyMap(),
usedSkill = emptyList(),
)
)
}
}

View file

@ -0,0 +1,72 @@
package com.pixelized.desktop.lwa.repository.campaign
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJson
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
import com.pixelized.shared.lwa.protocol.MessageType
import com.pixelized.shared.lwa.protocol.payload.UpdatePlayerCharacteristicMessage
import com.pixelized.shared.lwa.usecase.CampaignUseCase
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
class CampaignStore(
private val network: NetworkRepository,
private val factory: CampaignJsonFactory,
private val useCase: CampaignUseCase,
private val client: HttpClient,
private val json: Json,
) {
private val flow = MutableStateFlow(value = Campaign.EMPTY)
init {
val scope = CoroutineScope(Dispatchers.IO + Job())
scope.launch {
flow.value = load()
}
scope.launch {
network.data
.mapNotNull { it.takeIf { it.type == MessageType.UpdatePlayerCharacteristic } }
.map { json.decodeFromString<UpdatePlayerCharacteristicMessage>(it.value) }
.collect {
updateCharacteristic(it)
}
}
}
fun campaignFlow(): StateFlow<Campaign> = flow
private suspend fun load(): Campaign {
val request: CampaignJson = client
.get("http://pixelized.freeboxos.fr:16030/campaign") // TODO
.body()
val data = factory.convertFromJson(json = request)
return data
}
private fun updateCharacteristic(
message: UpdatePlayerCharacteristicMessage,
) {
val characters = flow.value.characters.toMutableMap()
val character = characters[message.characterId] ?: Campaign.CharacterInstance(
characteristic = emptyMap(),
usedSkill = emptyList(),
)
characters[message.characterId] = useCase.updateCharacteristic(
character = character,
characteristic = message.characteristic,
value = message.value
)
flow.value = flow.value.copy(characters = characters)
}
}

View file

@ -1,6 +1,6 @@
package com.pixelized.desktop.lwa.repository.characterSheet
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job

View file

@ -1,24 +1,31 @@
package com.pixelized.desktop.lwa.repository.characterSheet
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheetJson
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheetJsonFactory
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
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.MessageType
import com.pixelized.shared.lwa.protocol.payload.UpdateSkillUsageMessage
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
class CharacterSheetStore(
private val network: NetworkRepository,
private val factory: CharacterSheetJsonFactory,
private val useCase: CharacterSheetUseCase,
private val client: HttpClient,
private val json: Json,
) {
private val flow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
@ -27,21 +34,24 @@ class CharacterSheetStore(
scope.launch {
flow.value = load()
}
scope.launch {
network.data
.mapNotNull { it.takeIf { it.type == MessageType.UpdateSkillUsage } }
.map { json.decodeFromString<UpdateSkillUsageMessage>(it.value) }
.collect {
updateCharacterSkillChange(
characterId = it.characterId,
skillId = it.skillId,
)
}
}
}
fun characterSheetFlow(): StateFlow<List<CharacterSheet>> = flow
fun save(sheet: CharacterSheet) {
}
fun delete(id: String): Boolean {
return false
}
suspend fun load(): List<CharacterSheet> {
val request: List<CharacterSheetJson> = client
.get("http://pixelized.freeboxos.fr:16030/characters")
.get("http://pixelized.freeboxos.fr:16030/characters") // TODO
.body()
val data = request.map {
factory.convertFromJson(json = it)
@ -49,8 +59,19 @@ class CharacterSheetStore(
return data
}
sealed class CharacterSheetStoreException(root: Exception) : Exception(root)
class JsonConversionException(root: Exception) : CharacterSheetStoreException(root)
class FileWriteException(root: Exception) : CharacterSheetStoreException(root)
class FileReadException(root: Exception) : CharacterSheetStoreException(root)
private fun updateCharacterSkillChange(
characterId: String,
skillId: String,
) {
val characters = flow.value.toMutableList()
val index = characters.indexOfFirst { it.id == characterId }
if (index > -1) {
characters[index] = useCase.updateSkillUsage(
character = characters[index],
skillId = skillId,
)
flow.value = characters
}
}
}

View file

@ -7,6 +7,10 @@ import com.pixelized.desktop.lwa.utils.extention.encodeToFrame
import com.pixelized.shared.lwa.SERVER_PORT
import com.pixelized.shared.lwa.protocol.Message
import com.pixelized.shared.lwa.protocol.MessageType
import com.pixelized.shared.lwa.protocol.payload.MessagePayload
import com.pixelized.shared.lwa.protocol.payload.RollMessage
import com.pixelized.shared.lwa.protocol.payload.UpdatePlayerCharacteristicMessage
import com.pixelized.shared.lwa.protocol.payload.UpdateSkillUsageMessage
import io.ktor.client.HttpClient
import io.ktor.websocket.Frame
import kotlinx.coroutines.CoroutineScope
@ -19,11 +23,13 @@ import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
class NetworkRepository(
private val settingsRepository: SettingsRepository,
private val client: HttpClient,
private val json: Json,
) {
companion object {
const val DEFAULT_PORT = SERVER_PORT
@ -88,6 +94,33 @@ class NetworkRepository(
}
}
suspend fun share(
playerName: String = settingsRepository.settings().playerName,
payload: MessagePayload,
) {
if (status.value == Status.CONNECTED) {
when (payload) {
is RollMessage -> share(
playerName = playerName,
type = MessageType.Roll,
content = json.encodeToString(payload),
)
is UpdateSkillUsageMessage -> share(
playerName = playerName,
type = MessageType.UpdateSkillUsage,
content = json.encodeToString(payload),
)
is UpdatePlayerCharacteristicMessage -> share(
playerName = playerName,
type = MessageType.UpdatePlayerCharacteristic,
content = json.encodeToString(payload),
)
}
}
}
suspend fun share(
playerName: String = settingsRepository.settings().playerName,
type: MessageType,

View file

@ -2,7 +2,7 @@ package com.pixelized.desktop.lwa.repository.roll_history
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.shared.lwa.protocol.MessageType
import com.pixelized.shared.lwa.protocol.roll.RollMessage
import com.pixelized.shared.lwa.protocol.payload.RollMessage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharedFlow
@ -28,14 +28,6 @@ class RollHistoryRepository(
started = SharingStarted.Eagerly,
)
init {
scope.launch {
network.data.collect {
println(it)
}
}
}
suspend fun share(
characterId: String,
skillLabel: String,

View file

@ -51,13 +51,13 @@ data class CharacterDetailUio(
val name: String,
val hp: String,
val pp: String,
val mov: String,
)
@Stable
data class CharacterDynDetailUio(
val hp: String,
val pp: String,
val mov: String,
)
@Composable
@ -265,7 +265,7 @@ private fun CharacterHeader(
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.h6,
text = dynDetail.value.mov,
text = character.mov,
)
Text(
modifier = Modifier.alignByBaseline(),

View file

@ -8,7 +8,10 @@ import androidx.compose.runtime.remember
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 com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance.Characteristic.Damage
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance.Characteristic.Power
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@ -17,24 +20,27 @@ import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
class CharacterDetailViewModel(
private val repository: CharacterSheetRepository,
private val alteration: AlterationRepository,
private val characterRepository: CharacterSheetRepository,
private val campaignRepository: CampaignRepository,
private val alterationRepository: AlterationRepository,
) : ViewModel() {
private val displayedCharacterId = MutableStateFlow<String?>(null)
val detail: StateFlow<CharacterDetailUio?> = combine(
displayedCharacterId,
repository.characterSheetFlow(),
) { id, sheets ->
characterRepository.characterSheetFlow(),
campaignRepository.campaignFlow(),
) { id, sheets, campaign ->
val sheet = sheets.firstOrNull { it.id == id }
if (sheet == null) return@combine null
CharacterDetailUio(
id = sheet.id,
portrait = sheet.portrait,
name = sheet.name,
hp = "${sheet.maxHp}",
pp = "${sheet.maxPp}",
hp = "${sheet.hp - (campaign.characters[id]?.characteristic?.get(Damage) ?: 0)}",
pp = "${sheet.pp - (campaign.characters[id]?.characteristic?.get(Power) ?: 0)}",
mov = "${sheet.movement}"
)
}.stateIn(
scope = viewModelScope,
@ -46,22 +52,19 @@ class CharacterDetailViewModel(
@Stable
fun collectDynamicDetailAsState(id: String): State<CharacterDynDetailUio> {
val flow = remember(id) {
repository.characterSheetFlow(id = id)
campaignRepository.characterInstance(id = id)
}
return remember(id) {
flow.mapNotNull { sheet ->
if (sheet == null) return@mapNotNull null
CharacterDynDetailUio(
hp = sheet.currentHp.toString(),
pp = sheet.currentPp.toString(),
mov = sheet.movement.toString(),
hp = sheet.characteristic[Damage].toString(),
pp = sheet.characteristic[Power].toString(),
)
}
}.collectAsState(
initial = CharacterDynDetailUio(
hp = flow.value?.maxHp?.toString() ?: "",
pp = flow.value?.maxPp?.toString() ?: "",
mov = flow.value?.movement?.toString() ?: "",
CharacterDynDetailUio(
hp = flow.value.characteristic[Damage].toString(),
pp = flow.value.characteristic[Power].toString(),
)
)
}

View file

@ -35,9 +35,9 @@ import org.jetbrains.compose.resources.painterResource
data class PlayerPortraitUio(
val id: String,
val portrait: String?,
val hp: Int,
val damage: Int,
val maxHp: Int,
val pp: Int,
val usedPp: Int,
val maxPp: Int,
)
@ -96,7 +96,7 @@ fun PlayerPortrait(
Text(
modifier = Modifier.padding(bottom = 2.dp),
style = MaterialTheme.typography.caption,
text = "${character.hp}/${character.maxHp}",
text = "${character.maxHp - character.damage}/${character.maxHp}",
)
}
Row(
@ -111,7 +111,7 @@ fun PlayerPortrait(
Text(
modifier = Modifier.padding(bottom = 2.dp),
style = MaterialTheme.typography.caption,
text = "${character.pp}/${character.maxPp}",
text = "${character.maxPp - character.usedPp}/${character.maxPp}",
)
}
}

View file

@ -8,34 +8,42 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
import com.pixelized.shared.lwa.model.campaign.character
import com.pixelized.shared.lwa.model.campaign.damage
import com.pixelized.shared.lwa.model.campaign.power
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
class PlayerRibbonViewModel(
private val rollHistoryRepository: RollHistoryRepository,
characterRepository: CharacterSheetRepository,
campaignRepository: CampaignRepository,
) : ViewModel() {
val characters: StateFlow<List<PlayerPortraitUio>> = characterRepository.characterSheetFlow()
.map { sheets ->
sheets.map { sheet ->
PlayerPortraitUio(
id = sheet.id,
portrait = sheet.thumbnail,
hp = sheet.currentHp,
maxHp = sheet.maxHp,
pp = sheet.currentPp,
maxPp = sheet.maxPp,
)
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = emptyList()
)
val characters: StateFlow<List<PlayerPortraitUio>> = combine(
characterRepository.characterSheetFlow(),
campaignRepository.campaignFlow(),
) { sheets, campaign ->
sheets.map { sheet ->
val instance = campaign.character(id = sheet.id)
PlayerPortraitUio(
id = sheet.id,
portrait = sheet.thumbnail,
damage = instance.damage,
maxHp = sheet.hp,
usedPp = instance.power,
maxPp = sheet.pp,
)
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = emptyList()
)
private val rolls = hashMapOf<String, MutableState<PlayerPortraitRollUio?>>()

View file

@ -1,12 +1,16 @@
package com.pixelized.desktop.lwa.ui.screen.characterSheet.detail
import com.pixelized.desktop.lwa.business.ExpressionUseCase
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.repository.alteration.model.FieldAlteration
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Node
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.character
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.model.characterSheet.CharacterSheet.CharacteristicId
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__cha
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__con
@ -45,7 +49,7 @@ class CharacterSheetFactory(
suspend fun convertToUio(
sheet: CharacterSheet?,
diminished: Int,
campaign: Campaign,
alterations: Map<String, List<FieldAlteration>>,
): CharacterSheetPageUio? {
if (sheet == null) return null
@ -56,6 +60,10 @@ class CharacterSheetFactory(
} ?: 0
}
val maxHp = sheet.hp + alterations[CharacteristicId.HP].sum()
val maxPp = sheet.pp + alterations[CharacteristicId.PP].sum()
val instance = campaign.character(sheet.id)
return CharacterSheetPageUio(
id = sheet.id,
name = sheet.name,
@ -145,7 +153,7 @@ class CharacterSheetFactory(
Characteristic(
id = CharacteristicId.HP,
label = getString(Res.string.character_sheet__sub_characteristics__hit_point),
value = "${sheet.currentHp}/${sheet.maxHp + alterations[CharacteristicId.HP].sum()}",
value = "${maxHp - instance.damage}/${maxHp}",
tooltips = TooltipUio(
title = getString(Res.string.character_sheet__sub_characteristics__hit_point),
description = getString(Res.string.tooltip__sub_characteristics__hit_point),
@ -155,7 +163,7 @@ class CharacterSheetFactory(
Characteristic(
id = CharacteristicId.PP,
label = getString(Res.string.character_sheet__sub_characteristics__power_point),
value = "${sheet.currentPp}/${sheet.maxPp + alterations[CharacteristicId.PP].sum()}",
value = "${maxPp - instance.power}/${maxPp}",
tooltips = TooltipUio(
title = getString(Res.string.character_sheet__sub_characteristics__power_point),
description = getString(Res.string.tooltip__sub_characteristics__power_point),
@ -205,6 +213,7 @@ class CharacterSheetFactory(
),
commonSkills = sheet.commonSkills.map { skill ->
Node(
id = skill.id,
label = skill.label,
value = skillUseCase.computeSkillValue(
sheet = sheet,
@ -222,6 +231,7 @@ class CharacterSheetFactory(
},
specialSKills = sheet.specialSkills.map { skill ->
Node(
id = skill.id,
label = skill.label,
tooltips = skill.description?.takeIf { it.isNotBlank() }?.let { description ->
TooltipUio(
@ -239,6 +249,7 @@ class CharacterSheetFactory(
},
magicsSkills = sheet.magicSkills.map { skill ->
Node(
id = skill.id,
label = skill.label,
tooltips = skill.description?.takeIf { it.isNotBlank() }?.let { description ->
TooltipUio(

View file

@ -110,6 +110,7 @@ data class CharacterSheetPageUio(
@Stable
data class Node(
val id: String,
val label: String,
val value: Int,
val tooltips: TooltipUio? = null,

View file

@ -9,27 +9,40 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetDestination
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.shared.lwa.model.characterSheet.model.CharacterSheet
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetDestination
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.CharacterSheetDeleteConfirmationDialogUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialogUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.StatChangeDialogUio
import com.pixelized.desktop.lwa.utils.extention.collectAsState
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance.Characteristic
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.protocol.payload.UpdatePlayerCharacteristicMessage
import com.pixelized.shared.lwa.protocol.payload.UpdateSkillUsageMessage
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet__diminished__label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__hit_point
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__power_point
import org.jetbrains.compose.resources.getString
import kotlin.math.max
import kotlin.math.min
private typealias CSDCDialogUio = CharacterSheetDeleteConfirmationDialogUio
class CharacterSheetViewModel(
private val repository: CharacterSheetRepository,
private val characterRepository: CharacterSheetRepository,
private val campaignRepository: CampaignRepository,
private val network: NetworkRepository,
private val json: Json,
private val alteration: AlterationRepository,
private val factory: CharacterSheetFactory,
savedStateHandle: SavedStateHandle,
@ -49,7 +62,7 @@ class CharacterSheetViewModel(
private val _diminishedDialog = mutableStateOf<DiminishedStatDialogUio?>(null)
val diminishedDialog: State<DiminishedStatDialogUio?> get() = _diminishedDialog
private val diminishedValueFlow = repository.characterDiminishedFlow(id = argument.id)
private val diminishedValueFlow = characterRepository.characterDiminishedFlow(id = argument.id)
val diminishedValue: State<Int?>
@Composable
get() = diminishedValueFlow.collectAsState { it ->
@ -57,53 +70,46 @@ class CharacterSheetViewModel(
}
private val sheetFlow = combine(
repository.characterSheetFlow(id = argument.id),
repository.characterDiminishedFlow(id = argument.id),
characterRepository.characterSheetFlow(id = argument.id),
campaignRepository.campaignFlow(),
alteration.alterations(characterId = argument.id),
transform = { sheet, diminished, alterations ->
factory.convertToUio(sheet = sheet, diminished = diminished, alterations = alterations)
transform = { sheet, campaign, alterations ->
factory.convertToUio(
sheet = sheet,
campaign = campaign,
alterations = alterations
)
},
).stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = null,
)
val sheet: State<CharacterSheetPageUio?>
@Composable
get() = sheetFlow.collectAsState(
initial = null,
context = viewModelScope.coroutineContext,
)
get() = sheetFlow.collectAsState()
fun toggleWolf() {
alteration.toggle(argument.id, "65e37d32-3031-4bf8-9369-d2c45d2efac0")
}
fun deleteCharacter(id: String) {
repository.delete(id = id)
characterRepository.delete(id = id)
}
fun onUseSkill(skill: CharacterSheetPageUio.Node) {
repository.characterSheetFlow(id = argument.id).value?.let { sheet ->
val skills = sheet.commonSkills.map {
if (it.label == skill.label) it.copy(used = it.used.not()) else it
}
val occupations = sheet.specialSkills.map {
if (it.label == skill.label) it.copy(used = it.used.not()) else it
}
val magics = sheet.magicSkills.map {
if (it.label == skill.label) it.copy(used = it.used.not()) else it
}
repository.save(
characterSheet = sheet.copy(
commonSkills = skills,
specialSkills = occupations,
magicSkills = magics,
viewModelScope.launch {
network.share(
payload = UpdateSkillUsageMessage(
characterId = argument.id,
skillId = skill.id,
)
)
}
}
fun showConfirmCharacterDeletionDialog() {
repository.characterSheetFlow(id = argument.id).value?.let { sheet ->
characterRepository.characterSheetFlow(id = argument.id).value?.let { sheet ->
_displayDeleteConfirmationDialog.value = CharacterSheetDeleteConfirmationDialogUio(
id = sheet.id,
name = sheet.name,
@ -116,35 +122,36 @@ class CharacterSheetViewModel(
}
suspend fun showSubCharacteristicDialog(id: String) {
repository.characterSheetFlow(id = argument.id).value?.let { sheet ->
characterRepository.characterSheetFlow(id = argument.id).value?.let { sheet ->
val instance = campaignRepository.characterInstance(id = argument.id).value
_statChangeDialog.value = when (id) {
CharacterSheet.CharacteristicId.HP -> {
val value = mutableStateOf(
"${sheet.currentHp}".let {
"${sheet.hp - instance.damage}".let {
TextFieldValue(text = it, selection = TextRange(it.length))
}
)
StatChangeDialogUio(
id = CharacterSheet.CharacteristicId.HP,
id = Characteristic.Damage,
label = getString(resource = Res.string.character_sheet_edit__sub_characteristics__hit_point),
value = { value.value },
onValueChange = { value.value = it },
maxValue = "${sheet.maxHp}",
maxValue = "${sheet.hp}",
)
}
CharacterSheet.CharacteristicId.PP -> {
val value = mutableStateOf(
"${sheet.currentPp}".let {
"${sheet.power - instance.power}".let {
TextFieldValue(text = it, selection = TextRange(it.length))
}
)
StatChangeDialogUio(
id = CharacterSheet.CharacteristicId.PP,
id = Characteristic.Power,
label = getString(resource = Res.string.character_sheet_edit__sub_characteristics__power_point),
value = { value.value },
onValueChange = { value.value = it },
maxValue = "${sheet.maxPp}",
maxValue = "${sheet.power}",
)
}
@ -158,29 +165,24 @@ class CharacterSheetViewModel(
}
fun changeSubCharacteristic(
characteristicId: String,
characteristicId: Characteristic,
value: Int,
) {
val sheet = repository.characterSheetFlow(id = argument.id).value
val updated = when (characteristicId) {
CharacterSheet.CharacteristicId.HP -> sheet?.copy(
currentHp = max(
0,
min(sheet.maxHp, value)
viewModelScope.launch {
val sheet = characterRepository.characterSheetFlow(id = argument.id).value
if (sheet != null) {
network.share(
payload = UpdatePlayerCharacteristicMessage(
characterId = argument.id,
characteristic = characteristicId,
value = when (characteristicId) {
Characteristic.Damage -> sheet.hp - value
Characteristic.Power -> sheet.pp - value
else -> sheet.movement - value
},
),
)
)
CharacterSheet.CharacteristicId.PP -> sheet?.copy(
currentPp = max(
0,
min(sheet.maxPp, value)
)
)
else -> null
}
updated?.let {
repository.save(it)
}
}
}
@ -193,7 +195,7 @@ class CharacterSheetViewModel(
}
suspend fun showDiminishedDialog() {
val diminished = repository.characterDiminishedFlow(id = argument.id).value
val diminished = characterRepository.characterDiminishedFlow(id = argument.id).value
val textFieldValue =
mutableStateOf(TextFieldValue("$diminished", selection = TextRange(index = 0)))
_diminishedDialog.value = DiminishedStatDialogUio(
@ -215,7 +217,7 @@ class CharacterSheetViewModel(
fun changeDiminished(dialog: DiminishedStatDialogUio) {
val value = dialog.value().text.toIntOrNull() ?: 0
repository.setDiminishedForCharacter(
characterRepository.setDiminishedForCharacter(
id = dialog.id,
diminished = value,
)

View file

@ -42,6 +42,7 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.shared.lwa.model.campaign.Campaign
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
@ -49,7 +50,7 @@ import org.jetbrains.compose.resources.stringResource
@Stable
data class StatChangeDialogUio(
val id: String,
val id: Campaign.CharacterInstance.Characteristic,
val label: String,
val value: () -> TextFieldValue,
val onValueChange: (TextFieldValue) -> Unit,

View file

@ -3,7 +3,7 @@ package com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic

View file

@ -8,7 +8,7 @@ import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.occupation
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionFieldUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.BaseSkillFieldUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.SimpleFieldUio
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__action_label
@ -41,13 +41,6 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sk
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__special_title
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__spiel
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__throw
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__armor
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__damage_bonus
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__hp_grow
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__learning
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__max_hit_point
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__max_power_point
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__movement
import org.jetbrains.compose.resources.getString
import java.util.UUID
import kotlin.math.max
@ -60,56 +53,57 @@ class CharacterSheetEditFactory(
currentSheet: CharacterSheet?,
editedSheet: CharacterSheetEditPageUio,
): CharacterSheet {
val editedMaxHp = editedSheet.maxHp.unpack()?.toIntOrNull() ?: currentSheet?.maxHp ?: 0
val editedMaxPp = editedSheet.maxPp.unpack()?.toIntOrNull() ?: currentSheet?.maxPp ?: 0
val level = currentSheet?.level ?: 1
val strength = editedSheet.strength.unpack()?.toIntOrNull()
?: currentSheet?.strength
?: 0
val dexterity = editedSheet.dexterity.unpack()?.toIntOrNull()
?: currentSheet?.dexterity
?: 0
val constitution = editedSheet.constitution.unpack()?.toIntOrNull()
?: currentSheet?.constitution
?: 0
val height = editedSheet.height.unpack()?.toIntOrNull()
?: currentSheet?.height
?: 0
val intelligence = editedSheet.intelligence.unpack()?.toIntOrNull()
?: currentSheet?.intelligence
?: 0
val power = editedSheet.power.unpack()?.toIntOrNull()
?: currentSheet?.power
?: 0
val charisma = editedSheet.charisma.unpack()?.toIntOrNull()
?: currentSheet?.charisma
?: 0
return CharacterSheet(
id = editedSheet.id,
name = editedSheet.name.value.value,
portrait = currentSheet?.portrait,
thumbnail = currentSheet?.thumbnail,
strength = editedSheet.strength.unpack()?.toIntOrNull()
?: currentSheet?.strength
?: 0,
dexterity = editedSheet.dexterity.unpack()?.toIntOrNull()
?: currentSheet?.dexterity
?: 0,
constitution = editedSheet.constitution.unpack()?.toIntOrNull()
?: currentSheet?.constitution
?: 0,
height = editedSheet.height.unpack()?.toIntOrNull()
?: currentSheet?.height
?: 0,
intelligence = editedSheet.intelligence.unpack()?.toIntOrNull()
?: currentSheet?.intelligence
?: 0,
power = editedSheet.power.unpack()?.toIntOrNull()
?: currentSheet?.power
?: 0,
charisma = editedSheet.charisma.unpack()?.toIntOrNull()
?: currentSheet?.charisma
?: 0,
overrideMovement = editedSheet.movement.value.value.value.isNotBlank(),
movement = editedSheet.movement.unpack()?.toIntOrNull()
?: currentSheet?.movement
?: 10,
overrideMaxHp = editedSheet.maxHp.value.value.value.isNotBlank(),
maxHp = editedMaxHp,
currentHp = currentSheet?.currentHp?.coerceAtMost(editedMaxHp) ?: editedMaxHp,
overrideMaxPP = editedSheet.maxPp.value.value.value.isNotBlank(),
maxPp = editedMaxPp,
currentPp = currentSheet?.currentPp?.coerceAtMost(editedMaxPp) ?: editedMaxPp,
overrideDamageBonus = editedSheet.damageBonus.value.value.value.isNotBlank(),
damageBonus = editedSheet.damageBonus.unpack()
?: currentSheet?.damageBonus
?: "",
overrideArmor = editedSheet.armor.value.value.value.isNotBlank(),
armor = editedSheet.armor.unpack()?.toIntOrNull()
?: currentSheet?.armor
?: 0,
overrideLearning = editedSheet.learning.value.value.value.isNotBlank(),
learning = editedSheet.learning.unpack()?.toIntOrNull() ?: 0,
overrideHpGrow = editedSheet.hpGrow.value.value.value.isNotBlank(),
hpGrow = editedSheet.hpGrow.unpack()?.toIntOrNull() ?: 0,
level = level,
strength = strength,
dexterity = dexterity,
constitution = constitution,
height = height,
intelligence = intelligence,
power = power,
charisma = charisma,
hp = characterSheetUseCase.defaultMaxHp(
constitution = constitution,
height = height,
level = level
),
pp = characterSheetUseCase.defaultMaxPower(power = power),
movement = characterSheetUseCase.defaultMovement(),
damageBonus = characterSheetUseCase.defaultDamageBonus(
strength = strength,
height = height
),
armor = characterSheetUseCase.defaultArmor(),
learning = characterSheetUseCase.defaultLearning(intelligence = intelligence),
hpGrow = characterSheetUseCase.defaultHpGrow(constitution = constitution),
commonSkills = editedSheet.commonSkills.map { editedSkill ->
val currentSkill = currentSheet?.commonSkills?.firstOrNull {
it.id == editedSkill.id
@ -120,7 +114,7 @@ class CharacterSheetEditFactory(
description = currentSkill?.description,
base = "${editedSkill.base.value}",
bonus = editedSkill.bonus.value.value.takeIf { it.isNotBlank() },
level = editedSkill.level.value.value.takeIf { it.isNotBlank() },
level = editedSkill.level.value.value.toIntOrNull() ?: 0,
occupation = editedSkill.option.checked.value,
used = currentSkill?.used ?: false,
)
@ -135,7 +129,7 @@ class CharacterSheetEditFactory(
description = editedSkill.description.value.value,
base = editedSkill.base.value.value,
bonus = editedSkill.bonus.value.value.takeIf { it.isNotBlank() },
level = editedSkill.level.value.value.takeIf { it.isNotBlank() },
level = editedSkill.level.value.value.toIntOrNull() ?: 0,
occupation = editedSkill.options.occupation,
used = currentSkill?.used ?: false,
)
@ -150,7 +144,7 @@ class CharacterSheetEditFactory(
description = editedSkill.description.value.value,
base = editedSkill.base.value.value,
bonus = editedSkill.bonus.value.value.takeIf { it.isNotBlank() },
level = editedSkill.level.value.value.takeIf { it.isNotBlank() },
level = editedSkill.level.value.value.toIntOrNull() ?: 0,
occupation = editedSkill.options.occupation,
used = currentSkill?.used ?: false,
)
@ -159,6 +153,8 @@ class CharacterSheetEditFactory(
CharacterSheet.Roll(
id = it.id,
label = it.label.value.value,
description = null, // TODO
canBeCritical = false, // TODO
roll = it.action.value.value,
)
},
@ -244,77 +240,6 @@ class CharacterSheetEditFactory(
intelligence = int,
power = pow,
charisma = cha,
movement = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__movement),
value = skillFieldFactory.createWrapper(
value = if (sheet?.overrideMovement == true) "${sheet.movement}" else "",
placeholder = derivedStateOf {
"${characterSheetUseCase.defaultMovement()}"
},
)
),
maxHp = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__max_hit_point),
value = skillFieldFactory.createWrapper(
value = if (sheet?.overrideMaxHp == true) "${sheet.maxHp}" else "",
placeholder = derivedStateOf {
"${
characterSheetUseCase.defaultMaxHp(
constitution = con(),
height = hei()
)
}"
},
)
),
maxPp = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__max_power_point),
value = skillFieldFactory.createWrapper(
value = if (sheet?.overrideMaxPP == true) "${sheet.maxPp}" else "",
placeholder = derivedStateOf {
"${characterSheetUseCase.defaultMaxPower(power = pow())}"
},
)
),
damageBonus = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__damage_bonus),
value = skillFieldFactory.createWrapper(
value = if (sheet?.overrideDamageBonus == true) sheet.damageBonus else "",
placeholder = derivedStateOf {
characterSheetUseCase.defaultDamageBonus(
strength = str(),
height = hei()
)
},
)
),
armor = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__armor),
value = skillFieldFactory.createWrapper(
value = if (sheet?.overrideArmor == true) "${sheet.armor}" else "",
placeholder = derivedStateOf {
"${characterSheetUseCase.defaultArmor()}"
},
)
),
learning = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__learning),
value = skillFieldFactory.createWrapper(
value = if (sheet?.overrideLearning == true) "${sheet.learning}" else "",
placeholder = derivedStateOf {
"${characterSheetUseCase.defaultLearning(intelligence = int())}"
},
)
),
hpGrow = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__hp_grow),
value = skillFieldFactory.createWrapper(
value = if (sheet?.overrideHpGrow == true) "${sheet.hpGrow}" else "",
placeholder = derivedStateOf {
"${characterSheetUseCase.defaultHpGrow(constitution = con())}"
},
)
),
commonSkills = listOf(
createBaseSkill(
sheet = sheet,
@ -421,7 +346,7 @@ class CharacterSheetEditFactory(
labelValue = skill.label,
baseValue = skill.base,
bonusValue = skill.bonus ?: "",
levelValue = skill.level ?: "",
levelValue = skill.level.takeIf { it > 0 }?.toString() ?: "",
options = run {
val current = sheet.specialSkills.firstOrNull { it.id == skill.id }
listOf(
@ -439,7 +364,7 @@ class CharacterSheetEditFactory(
labelValue = skill.label,
baseValue = skill.base,
bonusValue = skill.bonus ?: "",
levelValue = skill.level ?: "",
levelValue = skill.level.takeIf { it > 0 }?.toString() ?: "",
options = run {
val current = sheet.magicSkills.firstOrNull { it.id == skill.id }
listOf(
@ -487,7 +412,7 @@ class CharacterSheetEditFactory(
),
level = skillFieldFactory.createWrapper(
label = mutableStateOf(getString(Res.string.character_sheet_edit__skills__level_label)),
value = skill?.level ?: "",
value = skill?.level?.takeIf { it > 0 }?.toString() ?: "",
),
option = skillFieldFactory.occupationOption(skill?.occupation ?: false),
)

View file

@ -67,13 +67,6 @@ data class CharacterSheetEditPageUio(
val intelligence: SimpleFieldUio,
val power: SimpleFieldUio,
val charisma: SimpleFieldUio,
val movement: SimpleFieldUio,
val maxHp: SimpleFieldUio,
val maxPp: SimpleFieldUio,
val damageBonus: SimpleFieldUio,
val armor: SimpleFieldUio,
val learning: SimpleFieldUio,
val hpGrow: SimpleFieldUio,
val commonSkills: List<BaseSkillFieldUio>,
val specialSkills: List<SkillFieldUio>,
val magicSkills: List<SkillFieldUio>,
@ -89,17 +82,6 @@ data class CharacterSheetEditPageUio(
power,
charisma,
)
val subCharacteristics
get() = listOf(
movement,
maxHp,
maxPp,
damageBonus,
armor,
learning,
hpGrow,
)
}
@Composable
@ -215,27 +197,6 @@ fun CharacterSheetEdit(
}
}
}
DecoratedBox(
modifier = Modifier.weight(weight = 1f),
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
modifier = Modifier.padding(vertical = 8.dp),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.caption,
text = stringResource(Res.string.character_sheet_edit__sub_characteristics__title),
)
form.subCharacteristics.forEach {
SimpleField(
modifier = Modifier.fillMaxWidth(),
field = it,
)
}
}
}
}
DecoratedBox(

View file

@ -12,7 +12,7 @@ import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetReposit
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio
import com.pixelized.desktop.lwa.ui.screen.roll.DifficultyUio.Difficulty
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay

View file

@ -6,7 +6,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch