Add characterSheet and Campaing to the server.
This commit is contained in:
		
							parent
							
								
									1e5f0d88ae
								
							
						
					
					
						commit
						495768e5fe
					
				
					 53 changed files with 879 additions and 513 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(),
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(),
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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}",
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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?>>()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -110,6 +110,7 @@ data class CharacterSheetPageUio(
 | 
			
		|||
 | 
			
		||||
    @Stable
 | 
			
		||||
    data class Node(
 | 
			
		||||
        val id: String,
 | 
			
		||||
        val label: String,
 | 
			
		||||
        val value: Int,
 | 
			
		||||
        val tooltips: TooltipUio? = null,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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),
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue