Refactor project data to allow server handling.
This commit is contained in:
		
							parent
							
								
									3c8eecdab5
								
							
						
					
					
						commit
						1e5f0d88ae
					
				
					 58 changed files with 742 additions and 469 deletions
				
			
		| 
						 | 
				
			
			@ -44,6 +44,7 @@ import com.pixelized.desktop.lwa.repository.network.NetworkRepository.Status
 | 
			
		|||
import com.pixelized.desktop.lwa.ui.navigation.screen.MainNavHost
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CampaignScreen
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.CharacterSheetMainNavHost
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryPage
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.theme.LwaTheme
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +54,7 @@ import lwacharactersheet.composeapp.generated.resources.network__disconnect__mes
 | 
			
		|||
import org.jetbrains.compose.resources.getString
 | 
			
		||||
import org.jetbrains.compose.ui.tooling.preview.Preview
 | 
			
		||||
import org.koin.compose.koinInject
 | 
			
		||||
import org.koin.compose.viewmodel.koinViewModel
 | 
			
		||||
 | 
			
		||||
val LocalWindowController = compositionLocalOf<WindowController> {
 | 
			
		||||
    error("Local Window Controller is not yet ready")
 | 
			
		||||
| 
						 | 
				
			
			@ -125,8 +127,8 @@ fun ApplicationScope.App() {
 | 
			
		|||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        content = {
 | 
			
		||||
//                            MainNavHost()
 | 
			
		||||
                            CampaignScreen()
 | 
			
		||||
                            MainNavHost()
 | 
			
		||||
//                            CampaignScreen()
 | 
			
		||||
                        }
 | 
			
		||||
                    )
 | 
			
		||||
                    NetworkSnackHandler(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,21 +1,24 @@
 | 
			
		|||
package com.pixelized.desktop.lwa
 | 
			
		||||
 | 
			
		||||
import com.pixelized.desktop.lwa.business.CharacterSheetUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.business.ExpressionUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.business.RollUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.business.SettingsUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.business.SkillStepUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.business.ExpressionUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetJsonFactory
 | 
			
		||||
import com.pixelized.desktop.lwa.parser.dice.DiceParser
 | 
			
		||||
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.characterSheet.CharacterSheetRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetStore
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.SkillDescriptionFactory
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.settings.SettingsFactory
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationStore
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.settings.SettingsStore
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbonViewModel
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetFactory
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetViewModel
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.CharacterSheetEditFactory
 | 
			
		||||
| 
						 | 
				
			
			@ -26,32 +29,48 @@ 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.desktop.lwa.parser.dice.DiceParser
 | 
			
		||||
import com.pixelized.desktop.lwa.parser.word.WordParser
 | 
			
		||||
import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbonViewModel
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import com.pixelized.shared.lwa.model.campaign.CampaignRepository
 | 
			
		||||
import com.pixelized.shared.lwa.model.campaign.model.CampaignFactory
 | 
			
		||||
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
 | 
			
		||||
import io.ktor.client.HttpClient
 | 
			
		||||
import io.ktor.client.engine.HttpClientEngine
 | 
			
		||||
import io.ktor.client.engine.okhttp.OkHttp
 | 
			
		||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
 | 
			
		||||
import io.ktor.client.plugins.websocket.WebSockets
 | 
			
		||||
import io.ktor.serialization.kotlinx.json.json
 | 
			
		||||
import org.koin.core.module.dsl.factoryOf
 | 
			
		||||
import org.koin.core.module.dsl.singleOf
 | 
			
		||||
import org.koin.core.module.dsl.viewModelOf
 | 
			
		||||
import org.koin.dsl.module
 | 
			
		||||
 | 
			
		||||
val moduleDependencies
 | 
			
		||||
val appModuleDependencies
 | 
			
		||||
    get() = listOf(
 | 
			
		||||
        toolsDependencies,
 | 
			
		||||
        parserDependencies,
 | 
			
		||||
        factoryDependencies,
 | 
			
		||||
        useCaseDependencies,
 | 
			
		||||
        storeDependencies,
 | 
			
		||||
        repositoryDependencies,
 | 
			
		||||
        viewModelDependencies,
 | 
			
		||||
        toolsDependencies,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
val toolsDependencies
 | 
			
		||||
    get() = module {
 | 
			
		||||
        factory { Json { explicitNulls = false } }
 | 
			
		||||
        single<HttpClientEngine> {
 | 
			
		||||
            OkHttp.create()
 | 
			
		||||
        }
 | 
			
		||||
        single {
 | 
			
		||||
            HttpClient(
 | 
			
		||||
                engine = get()
 | 
			
		||||
            ) {
 | 
			
		||||
                install(WebSockets) {
 | 
			
		||||
                    pingIntervalMillis = 20_000
 | 
			
		||||
                }
 | 
			
		||||
                install(ContentNegotiation) {
 | 
			
		||||
                    json(get())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
val storeDependencies
 | 
			
		||||
| 
						 | 
				
			
			@ -68,17 +87,17 @@ val repositoryDependencies
 | 
			
		|||
        singleOf(::RollHistoryRepository)
 | 
			
		||||
        singleOf(::SettingsRepository)
 | 
			
		||||
        singleOf(::AlterationRepository)
 | 
			
		||||
        singleOf(::CampaignRepository)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
val factoryDependencies
 | 
			
		||||
    get() = module {
 | 
			
		||||
        factoryOf(::CharacterSheetFactory)
 | 
			
		||||
        factoryOf(::CharacterSheetEditFactory)
 | 
			
		||||
        factoryOf(::CharacterSheetJsonFactory)
 | 
			
		||||
        factoryOf(::NetworkFactory)
 | 
			
		||||
        factoryOf(::SkillFieldFactory)
 | 
			
		||||
        factoryOf(::SkillDescriptionFactory)
 | 
			
		||||
        factoryOf(::SettingsFactory)
 | 
			
		||||
        factoryOf(::CampaignFactory)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
val viewModelDependencies
 | 
			
		||||
| 
						 | 
				
			
			@ -106,6 +125,6 @@ val useCaseDependencies
 | 
			
		|||
        factoryOf(::SkillStepUseCase)
 | 
			
		||||
        factoryOf(::RollUseCase)
 | 
			
		||||
        factoryOf(::ExpressionUseCase)
 | 
			
		||||
        factoryOf(::CharacterSheetUseCase)
 | 
			
		||||
        factoryOf(::SettingsUseCase)
 | 
			
		||||
        factoryOf(::CharacterSheetUseCase)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,56 +0,0 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.business
 | 
			
		||||
 | 
			
		||||
import kotlin.math.ceil
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
 | 
			
		||||
class CharacterSheetUseCase {
 | 
			
		||||
 | 
			
		||||
    fun normalize(value: Int): Int {
 | 
			
		||||
        return value - value % 5 // (truncate(value.toFloat() / 5f) * 5f).toInt()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun defaultMovement(): Int = 10
 | 
			
		||||
 | 
			
		||||
    fun defaultMaxHp(
 | 
			
		||||
        constitution: Int,
 | 
			
		||||
        height: Int,
 | 
			
		||||
    ): Int {
 | 
			
		||||
        return (ceil((constitution + height) / 2f).toInt())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun defaultMaxPower(
 | 
			
		||||
        power: Int,
 | 
			
		||||
    ): Int {
 | 
			
		||||
        return power
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun defaultDamageBonus(
 | 
			
		||||
        strength: Int,
 | 
			
		||||
        height: Int,
 | 
			
		||||
    ): String {
 | 
			
		||||
        return defaultDamageBonus(sum = strength + height)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun defaultDamageBonus(
 | 
			
		||||
        sum: Int,
 | 
			
		||||
    ): String {
 | 
			
		||||
        return when {
 | 
			
		||||
            sum < 12 -> "-1d6"
 | 
			
		||||
            sum in 12..17 -> "-1d4"
 | 
			
		||||
            sum in 18..22 -> "+0"
 | 
			
		||||
            sum in 23..29 -> "+1d4"
 | 
			
		||||
            sum in 30..39 -> "+1d6"
 | 
			
		||||
            else -> "+2d6"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun defaultArmor(): Int = 0
 | 
			
		||||
 | 
			
		||||
    fun defaultLearning(intelligence: Int): Int {
 | 
			
		||||
        return max(0, (intelligence - 10) * 2)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun defaultHpGrow(constitution: Int): Int {
 | 
			
		||||
        return (constitution / 3)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,8 +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.desktop.lwa.repository.alteration.model.FieldAlteration
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
			
		||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
import kotlin.math.min
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,37 +0,0 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository
 | 
			
		||||
 | 
			
		||||
enum class OperatingSystem(
 | 
			
		||||
    val home: String = System.getProperty("user.home"),
 | 
			
		||||
) {
 | 
			
		||||
    Windows,
 | 
			
		||||
    Macintosh;
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val current: OperatingSystem = run {
 | 
			
		||||
            val name = System.getProperty("os.name")
 | 
			
		||||
            when {
 | 
			
		||||
                name.contains(other = "win", ignoreCase = true) -> Windows
 | 
			
		||||
                name.contains(other = "mac", ignoreCase = true) -> Macintosh
 | 
			
		||||
                else -> error("Unsupported operating system: $name")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun storePath(
 | 
			
		||||
    os: OperatingSystem = OperatingSystem.current,
 | 
			
		||||
): String {
 | 
			
		||||
    return when (os) {
 | 
			
		||||
        OperatingSystem.Windows -> "${os.home}\\AppData\\Roaming\\Pixelized\\"
 | 
			
		||||
        OperatingSystem.Macintosh -> "${os.home}/Library/Pixelized/"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun characterStorePath(
 | 
			
		||||
    os: OperatingSystem = OperatingSystem.current,
 | 
			
		||||
): String {
 | 
			
		||||
    return when (os) {
 | 
			
		||||
        OperatingSystem.Windows -> "${storePath(os = os)}characters\\"
 | 
			
		||||
        OperatingSystem.Macintosh -> "${storePath(os = os)}characters/"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId.ARMOR
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId.DEX
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId.HEI
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId.MOV
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId.STR
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.ACROBATICS_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.AID_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.ATHLETICS_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.BARGAIN_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.COMBAT_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.DISCRETION_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.INTIMIDATION_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.PERCEPTION_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.PERSUASION_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.SPIEL_ID
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.THROW_ID
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
class AlterationStore(
 | 
			
		||||
    private val expressionParser: ExpressionParser,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,174 +0,0 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository.characterSheet
 | 
			
		||||
 | 
			
		||||
import com.pixelized.desktop.lwa.business.CharacterSheetUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheetJson
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheetJsonV1
 | 
			
		||||
 | 
			
		||||
class CharacterSheetJsonFactory(
 | 
			
		||||
    private val skillDescriptionFactory: SkillDescriptionFactory,
 | 
			
		||||
    private val characterSheetUseCase: CharacterSheetUseCase,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    fun convertToJson(
 | 
			
		||||
        sheet: CharacterSheet,
 | 
			
		||||
    ): CharacterSheetJson {
 | 
			
		||||
        val json = CharacterSheetJsonV1(
 | 
			
		||||
            id = sheet.id,
 | 
			
		||||
            name = sheet.name,
 | 
			
		||||
            thumbnail = sheet.thumbnail,
 | 
			
		||||
            portrait = sheet.portrait,
 | 
			
		||||
            strength = sheet.strength,
 | 
			
		||||
            dexterity = sheet.dexterity,
 | 
			
		||||
            constitution = sheet.constitution,
 | 
			
		||||
            height = sheet.height,
 | 
			
		||||
            intelligence = sheet.intelligence,
 | 
			
		||||
            power = sheet.power,
 | 
			
		||||
            charisma = sheet.charisma,
 | 
			
		||||
            movement = if (sheet.overrideMovement) sheet.movement else null,
 | 
			
		||||
            currentHp = sheet.currentHp,
 | 
			
		||||
            maxHp = if (sheet.overrideMaxHp) sheet.maxHp else null,
 | 
			
		||||
            currentPP = sheet.currentPp,
 | 
			
		||||
            maxPP = if (sheet.overrideMaxPP) sheet.maxPp else null,
 | 
			
		||||
            damageBonus = if (sheet.overrideDamageBonus) sheet.damageBonus else null,
 | 
			
		||||
            armor = if (sheet.overrideArmor) sheet.armor else null,
 | 
			
		||||
            learning = if (sheet.overrideLearning) sheet.learning else null,
 | 
			
		||||
            hpGrowf = if (sheet.overrideHpGrow) sheet.hpGrow else null,
 | 
			
		||||
            skills = sheet.commonSkills.map {
 | 
			
		||||
                CharacterSheetJsonV1.Skill(
 | 
			
		||||
                    id = it.id,
 | 
			
		||||
                    label = it.label,
 | 
			
		||||
                    description = null,
 | 
			
		||||
                    base = it.base,
 | 
			
		||||
                    bonus = it.bonus,
 | 
			
		||||
                    level = it.level,
 | 
			
		||||
                    occupation = it.occupation,
 | 
			
		||||
                    used = it.used,
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            occupations = sheet.specialSkills.map {
 | 
			
		||||
                CharacterSheetJsonV1.Skill(
 | 
			
		||||
                    id = it.id,
 | 
			
		||||
                    label = it.label,
 | 
			
		||||
                    description = it.description,
 | 
			
		||||
                    base = it.base,
 | 
			
		||||
                    bonus = it.bonus,
 | 
			
		||||
                    level = it.level,
 | 
			
		||||
                    occupation = it.occupation,
 | 
			
		||||
                    used = it.used,
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            magics = sheet.magicSkills.map {
 | 
			
		||||
                CharacterSheetJsonV1.Skill(
 | 
			
		||||
                    id = it.id,
 | 
			
		||||
                    label = it.label,
 | 
			
		||||
                    description = it.description,
 | 
			
		||||
                    base = it.base,
 | 
			
		||||
                    bonus = it.bonus,
 | 
			
		||||
                    level = it.level,
 | 
			
		||||
                    occupation = it.occupation,
 | 
			
		||||
                    used = it.used,
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            rolls = sheet.actions.map {
 | 
			
		||||
                CharacterSheetJsonV1.Roll(
 | 
			
		||||
                    id = it.id,
 | 
			
		||||
                    label = it.label,
 | 
			
		||||
                    roll = it.roll,
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        return json
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun convertFromJson(
 | 
			
		||||
        json: CharacterSheetJson,
 | 
			
		||||
    ): CharacterSheet {
 | 
			
		||||
        return when (json) {
 | 
			
		||||
            is CharacterSheetJsonV1 -> convertFromV1(json = json)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun convertFromV1(
 | 
			
		||||
        json: CharacterSheetJsonV1,
 | 
			
		||||
    ): CharacterSheet = characterSheetUseCase.run {
 | 
			
		||||
        CharacterSheet(
 | 
			
		||||
            id = json.id,
 | 
			
		||||
            name = json.name,
 | 
			
		||||
            portrait = json.portrait,
 | 
			
		||||
            thumbnail = json.thumbnail,
 | 
			
		||||
            strength = json.strength,
 | 
			
		||||
            dexterity = json.dexterity,
 | 
			
		||||
            constitution = json.constitution,
 | 
			
		||||
            height = json.height,
 | 
			
		||||
            intelligence = json.intelligence,
 | 
			
		||||
            power = json.power,
 | 
			
		||||
            charisma = json.charisma,
 | 
			
		||||
            overrideMovement = json.movement != null,
 | 
			
		||||
            movement = json.movement ?: defaultMovement(),
 | 
			
		||||
            currentHp = json.currentHp,
 | 
			
		||||
            overrideMaxHp = json.maxHp != null,
 | 
			
		||||
            maxHp = json.maxHp ?: defaultMaxHp(
 | 
			
		||||
                constitution = json.constitution,
 | 
			
		||||
                height = json.height,
 | 
			
		||||
            ),
 | 
			
		||||
            currentPp = json.currentPP,
 | 
			
		||||
            overrideMaxPP = json.maxPP != null,
 | 
			
		||||
            maxPp = json.maxPP ?: defaultMaxPower(power = json.power),
 | 
			
		||||
            overrideDamageBonus = json.damageBonus != null,
 | 
			
		||||
            damageBonus = json.damageBonus ?: defaultDamageBonus(
 | 
			
		||||
                strength = json.strength,
 | 
			
		||||
                height = json.height,
 | 
			
		||||
            ),
 | 
			
		||||
            overrideArmor = json.armor != null,
 | 
			
		||||
            armor = json.armor ?: defaultArmor(),
 | 
			
		||||
            overrideLearning = json.learning != null,
 | 
			
		||||
            learning = json.learning ?: defaultLearning(intelligence = json.intelligence),
 | 
			
		||||
            overrideHpGrow = json.hpGrowf != null,
 | 
			
		||||
            hpGrow = json.hpGrowf ?: defaultHpGrow(constitution = json.constitution),
 | 
			
		||||
            commonSkills = json.skills.map {
 | 
			
		||||
                CharacterSheet.Skill(
 | 
			
		||||
                    id = it.id,
 | 
			
		||||
                    label = it.label,
 | 
			
		||||
                    description = skillDescriptionFactory.baseSkillDescription(id = json.id),
 | 
			
		||||
                    base = it.base,
 | 
			
		||||
                    bonus = it.bonus,
 | 
			
		||||
                    level = it.level,
 | 
			
		||||
                    occupation = it.occupation,
 | 
			
		||||
                    used = it.used,
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            specialSkills = json.occupations.map {
 | 
			
		||||
                CharacterSheet.Skill(
 | 
			
		||||
                    id = it.id,
 | 
			
		||||
                    label = it.label,
 | 
			
		||||
                    description = it.description,
 | 
			
		||||
                    base = it.base,
 | 
			
		||||
                    bonus = it.bonus,
 | 
			
		||||
                    level = it.level,
 | 
			
		||||
                    occupation = it.occupation,
 | 
			
		||||
                    used = it.used,
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            magicSkills = json.magics.map {
 | 
			
		||||
                CharacterSheet.Skill(
 | 
			
		||||
                    id = it.id,
 | 
			
		||||
                    label = it.label,
 | 
			
		||||
                    description = it.description,
 | 
			
		||||
                    base = it.base,
 | 
			
		||||
                    bonus = it.bonus,
 | 
			
		||||
                    level = it.level,
 | 
			
		||||
                    occupation = it.occupation,
 | 
			
		||||
                    used = it.used,
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            actions = json.rolls.map {
 | 
			
		||||
                CharacterSheet.Roll(
 | 
			
		||||
                    id = it.id,
 | 
			
		||||
                    label = it.label,
 | 
			
		||||
                    roll = it.roll,
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository.characterSheet
 | 
			
		||||
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
			
		||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
| 
						 | 
				
			
			@ -53,11 +53,11 @@ class CharacterSheetRepository(
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fun save(characterSheet: CharacterSheet) {
 | 
			
		||||
        store.save(sheet = characterSheet)
 | 
			
		||||
//        store.save(sheet = characterSheet)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun delete(id: String) {
 | 
			
		||||
        store.delete(id = id)
 | 
			
		||||
//        store.delete(id = id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setDiminishedForCharacter(id: String, diminished: Int) {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,5 +65,4 @@ class CharacterSheetRepository(
 | 
			
		|||
            this[id] = diminished
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,24 +1,25 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository.characterSheet
 | 
			
		||||
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheetJson
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterStorePath
 | 
			
		||||
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 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.launch
 | 
			
		||||
import kotlinx.serialization.encodeToString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.text.Collator
 | 
			
		||||
 | 
			
		||||
class CharacterSheetStore(
 | 
			
		||||
    private val factory: CharacterSheetJsonFactory,
 | 
			
		||||
    private val jsonFormatter: Json,
 | 
			
		||||
    private val client: HttpClient,
 | 
			
		||||
) {
 | 
			
		||||
    private val characterDirectory = File(characterStorePath()).also { it.mkdirs() }
 | 
			
		||||
    private val flow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
| 
						 | 
				
			
			@ -30,85 +31,22 @@ class CharacterSheetStore(
 | 
			
		|||
 | 
			
		||||
    fun characterSheetFlow(): StateFlow<List<CharacterSheet>> = flow
 | 
			
		||||
 | 
			
		||||
    @Throws(
 | 
			
		||||
        CharacterSheetStoreException::class,
 | 
			
		||||
        FileWriteException::class,
 | 
			
		||||
        JsonConversionException::class,
 | 
			
		||||
    )
 | 
			
		||||
    fun save(sheet: CharacterSheet) {
 | 
			
		||||
        // convert the character sheet into json format.
 | 
			
		||||
        val json = try {
 | 
			
		||||
            factory.convertToJson(sheet = sheet).let(jsonFormatter::encodeToString)
 | 
			
		||||
        } catch (exception: Exception) {
 | 
			
		||||
            throw JsonConversionException(root = exception)
 | 
			
		||||
        }
 | 
			
		||||
        // write the character file.
 | 
			
		||||
        try {
 | 
			
		||||
            val file = characterSheetFile(id = sheet.id)
 | 
			
		||||
            file.writeText(
 | 
			
		||||
                text = json,
 | 
			
		||||
                charset = Charsets.UTF_8,
 | 
			
		||||
            )
 | 
			
		||||
        } catch (exception: Exception) {
 | 
			
		||||
            throw FileWriteException(root = exception)
 | 
			
		||||
        }
 | 
			
		||||
        // Update the dataflow.
 | 
			
		||||
        flow.value = flow.value
 | 
			
		||||
            .toMutableList()
 | 
			
		||||
            .also { data ->
 | 
			
		||||
                val index = data.indexOfFirst { it.id == sheet.id }
 | 
			
		||||
                if (index >= 0) {
 | 
			
		||||
                    data[index] = sheet
 | 
			
		||||
                } else {
 | 
			
		||||
                    data.add(sheet)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .sortedWith(compareBy(Collator.getInstance()) { it.name })
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun delete(id: String): Boolean {
 | 
			
		||||
        val file = characterSheetFile(id = id)
 | 
			
		||||
        flow.value = flow.value.toMutableList()
 | 
			
		||||
            .also { data ->
 | 
			
		||||
                data.removeIf { it.id == id }
 | 
			
		||||
            }
 | 
			
		||||
            .sortedBy {
 | 
			
		||||
                it.name
 | 
			
		||||
            }
 | 
			
		||||
        return file.delete()
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Throws(
 | 
			
		||||
        CharacterSheetStoreException::class,
 | 
			
		||||
        FileReadException::class,
 | 
			
		||||
        JsonConversionException::class,
 | 
			
		||||
    )
 | 
			
		||||
    suspend fun load(): List<CharacterSheet> {
 | 
			
		||||
        return characterDirectory
 | 
			
		||||
            .listFiles()
 | 
			
		||||
            ?.mapNotNull { file ->
 | 
			
		||||
                val json = try {
 | 
			
		||||
                    file.readText(charset = Charsets.UTF_8)
 | 
			
		||||
                } catch (exception: Exception) {
 | 
			
		||||
                    throw FileReadException(root = exception)
 | 
			
		||||
                }
 | 
			
		||||
                // Guard, if the json is blank no character have been save, ignore this file.
 | 
			
		||||
                if (json.isBlank()) {
 | 
			
		||||
                    return@mapNotNull null
 | 
			
		||||
                }
 | 
			
		||||
                try {
 | 
			
		||||
                    val sheet = jsonFormatter.decodeFromString<CharacterSheetJson>(json)
 | 
			
		||||
                    factory.convertFromJson(sheet)
 | 
			
		||||
                } catch (exception: Exception) {
 | 
			
		||||
                    throw JsonConversionException(root = exception)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ?.sortedWith(compareBy(Collator.getInstance()) { it.name })
 | 
			
		||||
            ?: emptyList()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun characterSheetFile(id: String): File {
 | 
			
		||||
        return File("${characterStorePath()}${id}.json")
 | 
			
		||||
        val request: List<CharacterSheetJson> = client
 | 
			
		||||
            .get("http://pixelized.freeboxos.fr:16030/characters")
 | 
			
		||||
            .body()
 | 
			
		||||
        val data = request.map {
 | 
			
		||||
            factory.convertFromJson(json = it)
 | 
			
		||||
        }
 | 
			
		||||
        return data
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class CharacterSheetStoreException(root: Exception) : Exception(root)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,47 +0,0 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository.characterSheet
 | 
			
		||||
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.Res
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__acrobatics
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__aid
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__athletics
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__bargain
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__combat
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__discretion
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__dodge
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__empathy
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__grab
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__intimidation
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__perception
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__persuasion
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__search
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__sleight_of_hand
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__spiel
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.tooltip__skills__throw
 | 
			
		||||
import org.jetbrains.compose.resources.getString
 | 
			
		||||
 | 
			
		||||
class SkillDescriptionFactory {
 | 
			
		||||
 | 
			
		||||
    suspend fun baseSkillDescription(id: String): String? {
 | 
			
		||||
        return when (id) {
 | 
			
		||||
            CharacterSheet.CommonSkillId.COMBAT_ID -> getString(Res.string.tooltip__skills__combat)
 | 
			
		||||
            CharacterSheet.CommonSkillId.DODGE_ID -> getString(Res.string.tooltip__skills__dodge)
 | 
			
		||||
            CharacterSheet.CommonSkillId.GRAB_ID -> getString(Res.string.tooltip__skills__grab)
 | 
			
		||||
            CharacterSheet.CommonSkillId.THROW_ID -> getString(Res.string.tooltip__skills__throw)
 | 
			
		||||
            CharacterSheet.CommonSkillId.ATHLETICS_ID -> getString(Res.string.tooltip__skills__athletics)
 | 
			
		||||
            CharacterSheet.CommonSkillId.ACROBATICS_ID -> getString(Res.string.tooltip__skills__acrobatics)
 | 
			
		||||
            CharacterSheet.CommonSkillId.PERCEPTION_ID -> getString(Res.string.tooltip__skills__perception)
 | 
			
		||||
            CharacterSheet.CommonSkillId.SEARCH_ID -> getString(Res.string.tooltip__skills__search)
 | 
			
		||||
            CharacterSheet.CommonSkillId.EMPATHY_ID -> getString(Res.string.tooltip__skills__empathy)
 | 
			
		||||
            CharacterSheet.CommonSkillId.PERSUASION_ID -> getString(Res.string.tooltip__skills__persuasion)
 | 
			
		||||
            CharacterSheet.CommonSkillId.INTIMIDATION_ID -> getString(Res.string.tooltip__skills__intimidation)
 | 
			
		||||
            CharacterSheet.CommonSkillId.SPIEL_ID -> getString(Res.string.tooltip__skills__spiel)
 | 
			
		||||
            CharacterSheet.CommonSkillId.BARGAIN_ID -> getString(Res.string.tooltip__skills__bargain)
 | 
			
		||||
            CharacterSheet.CommonSkillId.DISCRETION_ID -> getString(Res.string.tooltip__skills__discretion)
 | 
			
		||||
            CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID -> getString(Res.string.tooltip__skills__sleight_of_hand)
 | 
			
		||||
            CharacterSheet.CommonSkillId.AID_ID -> getString(Res.string.tooltip__skills__aid)
 | 
			
		||||
            else -> null
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,92 +0,0 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository.characterSheet.model
 | 
			
		||||
 | 
			
		||||
data class CharacterSheet(
 | 
			
		||||
    val id: String,
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val portrait: String?,
 | 
			
		||||
    val thumbnail: String?,
 | 
			
		||||
    // characteristics
 | 
			
		||||
    val strength: Int,
 | 
			
		||||
    val dexterity: Int,
 | 
			
		||||
    val constitution: Int,
 | 
			
		||||
    val height: Int,
 | 
			
		||||
    val intelligence: Int,
 | 
			
		||||
    val power: Int,
 | 
			
		||||
    val charisma: Int,
 | 
			
		||||
    // sub characteristics
 | 
			
		||||
    val overrideMovement: Boolean,
 | 
			
		||||
    val movement: Int,
 | 
			
		||||
    val currentHp: Int,
 | 
			
		||||
    val overrideMaxHp: Boolean,
 | 
			
		||||
    val maxHp: Int,
 | 
			
		||||
    val currentPp: Int,
 | 
			
		||||
    val overrideMaxPP: Boolean,
 | 
			
		||||
    val maxPp: Int,
 | 
			
		||||
    val overrideDamageBonus: Boolean,
 | 
			
		||||
    val damageBonus: String,
 | 
			
		||||
    val overrideArmor: Boolean,
 | 
			
		||||
    val armor: Int,
 | 
			
		||||
    val overrideLearning: Boolean,
 | 
			
		||||
    val learning: Int,
 | 
			
		||||
    val overrideHpGrow: Boolean,
 | 
			
		||||
    val hpGrow: Int,
 | 
			
		||||
    // skills
 | 
			
		||||
    val commonSkills: List<Skill>,
 | 
			
		||||
    val specialSkills: List<Skill>,
 | 
			
		||||
    val magicSkills: List<Skill>,
 | 
			
		||||
    // actions
 | 
			
		||||
    val actions: List<Roll>,
 | 
			
		||||
) {
 | 
			
		||||
    data class Skill(
 | 
			
		||||
        val id: String,
 | 
			
		||||
        val label: String,
 | 
			
		||||
        val description: String?,
 | 
			
		||||
        val base: String,
 | 
			
		||||
        val bonus: String?,
 | 
			
		||||
        val level: String?,
 | 
			
		||||
        val occupation: Boolean,
 | 
			
		||||
        val used: Boolean,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class Roll(
 | 
			
		||||
        val id: String,
 | 
			
		||||
        val label: String,
 | 
			
		||||
        val roll: String,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    object CommonSkillId {
 | 
			
		||||
        const val COMBAT_ID = "COMBAT"
 | 
			
		||||
        const val DODGE_ID = "DODGE"
 | 
			
		||||
        const val GRAB_ID = "GRAB"
 | 
			
		||||
        const val THROW_ID = "THROW"
 | 
			
		||||
        const val ATHLETICS_ID = "ATHLETICS"
 | 
			
		||||
        const val ACROBATICS_ID = "ACROBATICS"
 | 
			
		||||
        const val PERCEPTION_ID = "PERCEPTION"
 | 
			
		||||
        const val SEARCH_ID = "SEARCH"
 | 
			
		||||
        const val EMPATHY_ID = "EMPATHY"
 | 
			
		||||
        const val PERSUASION_ID = "PERSUASION"
 | 
			
		||||
        const val INTIMIDATION_ID = "INTIMIDATION"
 | 
			
		||||
        const val SPIEL_ID = "SPIEL"
 | 
			
		||||
        const val BARGAIN_ID = "BARGAIN"
 | 
			
		||||
        const val DISCRETION_ID = "DISCRETION"
 | 
			
		||||
        const val SLEIGHT_OF_HAND_ID = "SLEIGHT_OF_HAND"
 | 
			
		||||
        const val AID_ID = "AID"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    object CharacteristicId {
 | 
			
		||||
        const val STR = "STR"
 | 
			
		||||
        const val DEX = "DEX"
 | 
			
		||||
        const val CON = "CON"
 | 
			
		||||
        const val HEI = "HEI"
 | 
			
		||||
        const val INT = "INT"
 | 
			
		||||
        const val POW = "POW"
 | 
			
		||||
        const val CHA = "CHA"
 | 
			
		||||
        const val MOV = "MOV"
 | 
			
		||||
        const val HP = "HP"
 | 
			
		||||
        const val PP = "PP"
 | 
			
		||||
        const val DMG = "DMG"
 | 
			
		||||
        const val ARMOR = "ARMOR"
 | 
			
		||||
        const val LB = "LEARNING"
 | 
			
		||||
        const val GHP = "HP_GROW"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +0,0 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository.characterSheet.model
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
sealed interface CharacterSheetJson
 | 
			
		||||
| 
						 | 
				
			
			@ -1,57 +0,0 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository.characterSheet.model
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class CharacterSheetJsonV1(
 | 
			
		||||
    val id: String,
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val portrait: String?,
 | 
			
		||||
    val thumbnail: String?,
 | 
			
		||||
    // characteristics
 | 
			
		||||
    val strength: Int,
 | 
			
		||||
    val dexterity: Int,
 | 
			
		||||
    val constitution: Int,
 | 
			
		||||
    val height: Int,
 | 
			
		||||
    val intelligence: Int,
 | 
			
		||||
    val power: Int,
 | 
			
		||||
    val charisma: Int,
 | 
			
		||||
    // sub characteristics
 | 
			
		||||
    val movement: Int?,
 | 
			
		||||
    val currentHp: Int,
 | 
			
		||||
    val maxHp: Int?,
 | 
			
		||||
    val currentPP: Int,
 | 
			
		||||
    val maxPP: Int?,
 | 
			
		||||
    val damageBonus: String?,
 | 
			
		||||
    val armor: Int?,
 | 
			
		||||
    val learning: Int?,
 | 
			
		||||
    val hpGrowf: Int?,
 | 
			
		||||
    // skills
 | 
			
		||||
    val skills: List<Skill>,
 | 
			
		||||
    // occupations
 | 
			
		||||
    val occupations: List<Skill>,
 | 
			
		||||
    // magic skill
 | 
			
		||||
    val magics: List<Skill>,
 | 
			
		||||
    // attack
 | 
			
		||||
    val rolls: List<Roll>,
 | 
			
		||||
) : CharacterSheetJson {
 | 
			
		||||
 | 
			
		||||
    @Serializable
 | 
			
		||||
    data class Skill(
 | 
			
		||||
        val id: String,
 | 
			
		||||
        val label: String,
 | 
			
		||||
        val description: String?,
 | 
			
		||||
        val base: String,
 | 
			
		||||
        val bonus: String?,
 | 
			
		||||
        val level: String?,
 | 
			
		||||
        val occupation: Boolean,
 | 
			
		||||
        val used: Boolean,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @Serializable
 | 
			
		||||
    data class Roll(
 | 
			
		||||
        val id: String,
 | 
			
		||||
        val label: String,
 | 
			
		||||
        val roll: String,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +1,12 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository.network
 | 
			
		||||
 | 
			
		||||
//import com.pixelized.desktop.lwa.repository.network.helper.server
 | 
			
		||||
//import io.ktor.server.engine.EmbeddedServer
 | 
			
		||||
//import io.ktor.server.netty.NettyApplicationEngine
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.network.helper.client
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.network.helper.connectWebSocket
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.utils.extention.decodeFromFrame
 | 
			
		||||
import com.pixelized.desktop.lwa.utils.extention.encodeToFrame
 | 
			
		||||
import com.pixelized.server.lwa.SERVER_PORT
 | 
			
		||||
import com.pixelized.server.lwa.protocol.Message
 | 
			
		||||
import com.pixelized.server.lwa.protocol.MessageType
 | 
			
		||||
import com.pixelized.shared.lwa.SERVER_PORT
 | 
			
		||||
import com.pixelized.shared.lwa.protocol.Message
 | 
			
		||||
import com.pixelized.shared.lwa.protocol.MessageType
 | 
			
		||||
import io.ktor.client.HttpClient
 | 
			
		||||
import io.ktor.websocket.Frame
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
| 
						 | 
				
			
			@ -25,10 +21,9 @@ import kotlinx.coroutines.launch
 | 
			
		|||
import kotlinx.coroutines.runBlocking
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
 | 
			
		||||
typealias Client = HttpClient
 | 
			
		||||
 | 
			
		||||
class NetworkRepository(
 | 
			
		||||
    private val settingsRepository: SettingsRepository,
 | 
			
		||||
    private val client: HttpClient,
 | 
			
		||||
) {
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val DEFAULT_PORT = SERVER_PORT
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +32,6 @@ class NetworkRepository(
 | 
			
		|||
 | 
			
		||||
    private val scope = CoroutineScope(Dispatchers.IO)
 | 
			
		||||
    private var networkJob: Job? = null
 | 
			
		||||
    private var client: Client? = null
 | 
			
		||||
 | 
			
		||||
    private val outgoingMessageBuffer = MutableSharedFlow<Message>()
 | 
			
		||||
    private val incomingMessageBuffer = MutableSharedFlow<Message>()
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +40,6 @@ class NetworkRepository(
 | 
			
		|||
    private val _status = MutableStateFlow(Status.DISCONNECTED)
 | 
			
		||||
    val status: StateFlow<Status> get() = _status
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fun connect(
 | 
			
		||||
        host: String,
 | 
			
		||||
        port: Int,
 | 
			
		||||
| 
						 | 
				
			
			@ -54,12 +47,10 @@ class NetworkRepository(
 | 
			
		|||
        onFailure: (Exception) -> Unit = { },
 | 
			
		||||
        onClose: () -> Unit = { },
 | 
			
		||||
    ) {
 | 
			
		||||
        client = client()
 | 
			
		||||
 | 
			
		||||
        networkJob?.cancel()
 | 
			
		||||
        networkJob = scope.launch {
 | 
			
		||||
            try {
 | 
			
		||||
                client?.connectWebSocket(host = host, port = port) {
 | 
			
		||||
                client.connectWebSocket(host = host, port = port) {
 | 
			
		||||
                    _status.value = Status.CONNECTED
 | 
			
		||||
                    onConnect()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +84,7 @@ class NetworkRepository(
 | 
			
		|||
    fun disconnect() {
 | 
			
		||||
        networkJob?.cancel()
 | 
			
		||||
        scope.launch {
 | 
			
		||||
            client?.close()
 | 
			
		||||
            client.close()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,26 +1,15 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository.network.helper
 | 
			
		||||
 | 
			
		||||
import io.ktor.client.HttpClient
 | 
			
		||||
import io.ktor.client.engine.cio.CIO
 | 
			
		||||
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
 | 
			
		||||
import io.ktor.client.plugins.websocket.WebSockets
 | 
			
		||||
import io.ktor.client.plugins.websocket.webSocket
 | 
			
		||||
import io.ktor.http.HttpMethod
 | 
			
		||||
 | 
			
		||||
// https://ktor.io/docs/client-websockets.html#handle-session
 | 
			
		||||
fun client(): HttpClient {
 | 
			
		||||
    val client = HttpClient(CIO) {
 | 
			
		||||
        install(WebSockets) {
 | 
			
		||||
            pingIntervalMillis = 20_000
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun HttpClient.connectWebSocket(
 | 
			
		||||
    host: String,
 | 
			
		||||
    port: Int,
 | 
			
		||||
    block: suspend DefaultClientWebSocketSession.() -> Unit
 | 
			
		||||
    block: suspend DefaultClientWebSocketSession.() -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    webSocket(
 | 
			
		||||
        method = HttpMethod.Get,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.repository.roll_history
 | 
			
		||||
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
 | 
			
		||||
import com.pixelized.server.lwa.protocol.MessageType
 | 
			
		||||
import com.pixelized.server.lwa.protocol.roll.RollMessage
 | 
			
		||||
import com.pixelized.shared.lwa.protocol.MessageType
 | 
			
		||||
import com.pixelized.shared.lwa.protocol.roll.RollMessage
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.flow.SharedFlow
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ package com.pixelized.desktop.lwa.repository.settings
 | 
			
		|||
import com.pixelized.desktop.lwa.business.SettingsUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.settings.model.Settings
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJson
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.storePath
 | 
			
		||||
import com.pixelized.shared.lwa.storePath
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ package com.pixelized.desktop.lwa.ui.navigation.screen
 | 
			
		|||
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.CompositionLocalProvider
 | 
			
		||||
import androidx.compose.runtime.LaunchedEffect
 | 
			
		||||
import androidx.compose.runtime.compositionLocalOf
 | 
			
		||||
import androidx.navigation.NavHostController
 | 
			
		||||
import androidx.navigation.compose.NavHost
 | 
			
		||||
| 
						 | 
				
			
			@ -9,6 +10,8 @@ import androidx.navigation.compose.rememberNavController
 | 
			
		|||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.MainDestination
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableMainPage
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableNetworkPage
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
 | 
			
		||||
import org.koin.compose.viewmodel.koinViewModel
 | 
			
		||||
 | 
			
		||||
val LocalScreenController = compositionLocalOf<NavHostController> {
 | 
			
		||||
    error("MainNavHost controller is not yet ready")
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +21,12 @@ val LocalScreenController = compositionLocalOf<NavHostController> {
 | 
			
		|||
fun MainNavHost(
 | 
			
		||||
    controller: NavHostController = rememberNavController(),
 | 
			
		||||
    startDestination: String = MainDestination.navigationRoute(),
 | 
			
		||||
    networkViewModel: NetworkViewModel = koinViewModel(),
 | 
			
		||||
) {
 | 
			
		||||
    LaunchedEffect(Unit) {
 | 
			
		||||
        networkViewModel.connect()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CompositionLocalProvider(
 | 
			
		||||
        LocalScreenController provides controller,
 | 
			
		||||
    ) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,9 +83,7 @@ fun CampaignScreen(
 | 
			
		|||
            },
 | 
			
		||||
            leftOverlay = {
 | 
			
		||||
                PlayerRibbon(
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .padding(all = 8.dp)
 | 
			
		||||
                        .fillMaxHeight(),
 | 
			
		||||
                    modifier = Modifier.fillMaxHeight(),
 | 
			
		||||
                    onCharacter = {
 | 
			
		||||
                        characterDetailViewModel.showCharacter(id = it)
 | 
			
		||||
                    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -141,7 +141,7 @@ private fun Background(
 | 
			
		|||
) {
 | 
			
		||||
    Surface(
 | 
			
		||||
        modifier = modifier.fillMaxSize(),
 | 
			
		||||
        color = MaterialTheme.lwa.color.elevatedSurface,
 | 
			
		||||
        color = MaterialTheme.lwa.colorScheme.elevatedSurface,
 | 
			
		||||
    ) {
 | 
			
		||||
//        Image(
 | 
			
		||||
//            modifier = Modifier.fillMaxSize().drawWithContent {
 | 
			
		||||
| 
						 | 
				
			
			@ -200,7 +200,7 @@ private fun CharacterHeader(
 | 
			
		|||
            ) {
 | 
			
		||||
                Icon(
 | 
			
		||||
                    painter = painterResource(Res.drawable.ic_close_24dp),
 | 
			
		||||
                    tint = MaterialTheme.lwa.color.base.primary,
 | 
			
		||||
                    tint = MaterialTheme.lwa.colorScheme.base.primary,
 | 
			
		||||
                    contentDescription = null,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -220,7 +220,7 @@ private fun CharacterHeader(
 | 
			
		|||
                Text(
 | 
			
		||||
                    modifier = Modifier.alignByBaseline(),
 | 
			
		||||
                    style = MaterialTheme.typography.h6,
 | 
			
		||||
                    color = MaterialTheme.lwa.color.base.primary,
 | 
			
		||||
                    color = MaterialTheme.lwa.colorScheme.base.primary,
 | 
			
		||||
                    fontWeight = FontWeight.Bold,
 | 
			
		||||
                    text = dynDetail.value.hp,
 | 
			
		||||
                )
 | 
			
		||||
| 
						 | 
				
			
			@ -243,7 +243,7 @@ private fun CharacterHeader(
 | 
			
		|||
                Text(
 | 
			
		||||
                    modifier = Modifier.alignByBaseline(),
 | 
			
		||||
                    style = MaterialTheme.typography.h6,
 | 
			
		||||
                    color = MaterialTheme.lwa.color.base.primary,
 | 
			
		||||
                    color = MaterialTheme.lwa.colorScheme.base.primary,
 | 
			
		||||
                    fontWeight = FontWeight.Bold,
 | 
			
		||||
                    text = dynDetail.value.pp,
 | 
			
		||||
                )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,6 @@ package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
 | 
			
		|||
 | 
			
		||||
import androidx.compose.foundation.clickable
 | 
			
		||||
import androidx.compose.foundation.layout.Arrangement
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
			
		||||
| 
						 | 
				
			
			@ -20,13 +19,13 @@ import androidx.compose.ui.Modifier
 | 
			
		|||
import androidx.compose.ui.draw.clip
 | 
			
		||||
import androidx.compose.ui.draw.drawWithContent
 | 
			
		||||
import androidx.compose.ui.graphics.Brush
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.graphics.FilterQuality
 | 
			
		||||
import androidx.compose.ui.layout.ContentScale
 | 
			
		||||
import androidx.compose.ui.unit.DpSize
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import coil3.compose.AsyncImage
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.theme.lwa
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.Res
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
 | 
			
		||||
| 
						 | 
				
			
			@ -42,19 +41,15 @@ data class PlayerPortraitUio(
 | 
			
		|||
    val maxPp: Int,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
object PlayerPortrait {
 | 
			
		||||
    object Default {
 | 
			
		||||
        val size = DpSize(96.dp, 128.dp)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun PlayerPortrait(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    size: DpSize = PlayerPortrait.Default.size,
 | 
			
		||||
    size: DpSize,
 | 
			
		||||
    character: PlayerPortraitUio,
 | 
			
		||||
    onCharacter: (id: String) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    val colorScheme = MaterialTheme.lwa.colorScheme
 | 
			
		||||
 | 
			
		||||
    DecoratedBox(
 | 
			
		||||
        modifier = modifier
 | 
			
		||||
            .size(size = size)
 | 
			
		||||
| 
						 | 
				
			
			@ -76,11 +71,11 @@ fun PlayerPortrait(
 | 
			
		|||
                    drawRect(
 | 
			
		||||
                        brush = Brush.verticalGradient(
 | 
			
		||||
                            listOf(
 | 
			
		||||
                                Color.Black.copy(alpha = 0.0f),
 | 
			
		||||
                                Color.Black.copy(alpha = 0.0f),
 | 
			
		||||
                                Color.Black.copy(alpha = 0.0f),
 | 
			
		||||
                                Color.Black.copy(alpha = 0.5f),
 | 
			
		||||
                                Color.Black.copy(alpha = 0.8f),
 | 
			
		||||
                                colorScheme.elevatedSurface.copy(alpha = 0.0f),
 | 
			
		||||
                                colorScheme.elevatedSurface.copy(alpha = 0.0f),
 | 
			
		||||
                                colorScheme.elevatedSurface.copy(alpha = 0.0f),
 | 
			
		||||
                                colorScheme.elevatedSurface.copy(alpha = 0.5f),
 | 
			
		||||
                                colorScheme.elevatedSurface.copy(alpha = 0.8f),
 | 
			
		||||
                            )
 | 
			
		||||
                        )
 | 
			
		||||
                    )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,28 +9,45 @@ import androidx.compose.animation.core.spring
 | 
			
		|||
import androidx.compose.animation.fadeIn
 | 
			
		||||
import androidx.compose.animation.fadeOut
 | 
			
		||||
import androidx.compose.animation.togetherWith
 | 
			
		||||
import androidx.compose.foundation.ExperimentalFoundationApi
 | 
			
		||||
import androidx.compose.foundation.PointerMatcher
 | 
			
		||||
import androidx.compose.foundation.clickable
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.aspectRatio
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.foundation.layout.size
 | 
			
		||||
import androidx.compose.foundation.onClick
 | 
			
		||||
import androidx.compose.foundation.shape.CircleShape
 | 
			
		||||
import androidx.compose.material.Icon
 | 
			
		||||
import androidx.compose.material.MaterialTheme
 | 
			
		||||
import androidx.compose.material.Text
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.LaunchedEffect
 | 
			
		||||
import androidx.compose.runtime.Stable
 | 
			
		||||
import androidx.compose.runtime.State
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.clip
 | 
			
		||||
import androidx.compose.ui.geometry.Offset
 | 
			
		||||
import androidx.compose.ui.graphics.Shadow
 | 
			
		||||
import androidx.compose.ui.graphics.graphicsLayer
 | 
			
		||||
import androidx.compose.ui.input.pointer.PointerButton
 | 
			
		||||
import androidx.compose.ui.text.font.FontWeight
 | 
			
		||||
import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.unit.DpSize
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.Res
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp
 | 
			
		||||
import org.jetbrains.compose.resources.painterResource
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class PlayerPortraitRollUio(
 | 
			
		||||
    val characterId: String,
 | 
			
		||||
    val value: Int?,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class PlayerPortraitRollAnimation(
 | 
			
		||||
    val alpha: Animatable<Float, AnimationVector1D> = Animatable(0f),
 | 
			
		||||
| 
						 | 
				
			
			@ -38,13 +55,19 @@ data class PlayerPortraitRollAnimation(
 | 
			
		|||
    val scale: Animatable<Float, AnimationVector1D> = Animatable(1f),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@OptIn(ExperimentalFoundationApi::class)
 | 
			
		||||
@Composable
 | 
			
		||||
fun PlayerPortraitRoll(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    value: Int?,
 | 
			
		||||
    size: DpSize,
 | 
			
		||||
    value: PlayerPortraitRollUio?,
 | 
			
		||||
    onLeftClick: (PlayerPortraitRollUio) -> Unit,
 | 
			
		||||
    onRightClick: (PlayerPortraitRollUio) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    AnimatedContent(
 | 
			
		||||
        modifier = modifier.graphicsLayer { clip = false },
 | 
			
		||||
        modifier = modifier
 | 
			
		||||
            .size(size = size)
 | 
			
		||||
            .graphicsLayer { clip = false },
 | 
			
		||||
        targetState = value,
 | 
			
		||||
        transitionSpec = {
 | 
			
		||||
            val enter = fadeIn()
 | 
			
		||||
| 
						 | 
				
			
			@ -61,33 +84,41 @@ fun PlayerPortraitRoll(
 | 
			
		|||
            },
 | 
			
		||||
            contentAlignment = Alignment.Center,
 | 
			
		||||
        ) {
 | 
			
		||||
            when (it) {
 | 
			
		||||
                null -> Unit
 | 
			
		||||
                else -> {
 | 
			
		||||
                    Icon(
 | 
			
		||||
                        modifier = Modifier
 | 
			
		||||
                            .graphicsLayer {
 | 
			
		||||
                                this.alpha = 0.8f
 | 
			
		||||
                                this.rotationZ = animation.rotation.value
 | 
			
		||||
                            }
 | 
			
		||||
                            .size(48.dp),
 | 
			
		||||
                        painter = painterResource(Res.drawable.ic_d20_24dp),
 | 
			
		||||
                        tint = MaterialTheme.colors.primary,
 | 
			
		||||
                        contentDescription = null,
 | 
			
		||||
                    )
 | 
			
		||||
                    Text(
 | 
			
		||||
                        style = MaterialTheme.typography.h5.copy(
 | 
			
		||||
                            shadow = Shadow(
 | 
			
		||||
                                color = MaterialTheme.colors.surface,
 | 
			
		||||
                                offset = Offset.Zero,
 | 
			
		||||
                                blurRadius = 8f,
 | 
			
		||||
                            )
 | 
			
		||||
                        ),
 | 
			
		||||
                        textAlign = TextAlign.Center,
 | 
			
		||||
                        color = MaterialTheme.colors.onSurface,
 | 
			
		||||
                        text = it.toString()
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            if (it != null) {
 | 
			
		||||
                Icon(
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .graphicsLayer {
 | 
			
		||||
                            this.alpha = 0.8f
 | 
			
		||||
                            this.rotationZ = animation.rotation.value
 | 
			
		||||
                        }
 | 
			
		||||
                        .fillMaxWidth()
 | 
			
		||||
                        .aspectRatio(1f)
 | 
			
		||||
                        .padding(all = 8.dp)
 | 
			
		||||
                        .clip(shape = CircleShape)
 | 
			
		||||
                        .onClick(
 | 
			
		||||
                            matcher = PointerMatcher.mouse(PointerButton.Secondary),
 | 
			
		||||
                            onClick = { onRightClick(it) },
 | 
			
		||||
                        ).clickable {
 | 
			
		||||
                            onLeftClick(it)
 | 
			
		||||
                        }
 | 
			
		||||
                        .padding(all = 8.dp),
 | 
			
		||||
                    painter = painterResource(Res.drawable.ic_d20_24dp),
 | 
			
		||||
                    tint = MaterialTheme.colors.primary,
 | 
			
		||||
                    contentDescription = null,
 | 
			
		||||
                )
 | 
			
		||||
                Text(
 | 
			
		||||
                    style = MaterialTheme.typography.h5.copy(
 | 
			
		||||
                        shadow = Shadow(
 | 
			
		||||
                            color = MaterialTheme.colors.surface,
 | 
			
		||||
                            offset = Offset.Zero,
 | 
			
		||||
                            blurRadius = 8f,
 | 
			
		||||
                        )
 | 
			
		||||
                    ),
 | 
			
		||||
                    fontWeight = FontWeight.SemiBold,
 | 
			
		||||
                    textAlign = TextAlign.Center,
 | 
			
		||||
                    color = MaterialTheme.colors.onSurface,
 | 
			
		||||
                    text = it.value.toString()
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,26 +1,36 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.Arrangement
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.PaddingValues
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.foundation.layout.size
 | 
			
		||||
import androidx.compose.foundation.lazy.LazyColumn
 | 
			
		||||
import androidx.compose.foundation.lazy.items
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.collectAsState
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.unit.DpSize
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import org.koin.compose.viewmodel.koinViewModel
 | 
			
		||||
 | 
			
		||||
object PlayerRibbon {
 | 
			
		||||
    object Default {
 | 
			
		||||
        val size = DpSize(96.dp, 128.dp)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun PlayerRibbon(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    playerRibbonViewModel: PlayerRibbonViewModel = koinViewModel(),
 | 
			
		||||
    padding: PaddingValues = PaddingValues(all = 8.dp),
 | 
			
		||||
    onCharacter: (id: String) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    val characters = playerRibbonViewModel.characters.collectAsState()
 | 
			
		||||
 | 
			
		||||
    LazyColumn(
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
        contentPadding = padding,
 | 
			
		||||
        verticalArrangement = Arrangement.spacedBy(space = 8.dp)
 | 
			
		||||
    ) {
 | 
			
		||||
        items(
 | 
			
		||||
| 
						 | 
				
			
			@ -29,15 +39,19 @@ fun PlayerRibbon(
 | 
			
		|||
        ) {
 | 
			
		||||
            Row {
 | 
			
		||||
                PlayerPortrait(
 | 
			
		||||
                    size = PlayerRibbon.Default.size,
 | 
			
		||||
                    character = it,
 | 
			
		||||
                    onCharacter = onCharacter,
 | 
			
		||||
                )
 | 
			
		||||
                PlayerPortraitRoll(
 | 
			
		||||
                    modifier = Modifier.size(
 | 
			
		||||
                        width = 64.dp,
 | 
			
		||||
                        height = PlayerPortrait.Default.size.height
 | 
			
		||||
                    ),
 | 
			
		||||
                    size = PlayerRibbon.Default.size,
 | 
			
		||||
                    value = playerRibbonViewModel.roll(characterId = it.id).value,
 | 
			
		||||
                    onRightClick = {
 | 
			
		||||
                        playerRibbonViewModel.onPortraitRollRightClick(characterId = it.characterId)
 | 
			
		||||
                    },
 | 
			
		||||
                    onLeftClick = {
 | 
			
		||||
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,11 +2,10 @@ package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
 | 
			
		|||
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.LaunchedEffect
 | 
			
		||||
import androidx.compose.runtime.MutableState
 | 
			
		||||
import androidx.compose.runtime.Stable
 | 
			
		||||
import androidx.compose.runtime.State
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.saveable.rememberSaveable
 | 
			
		||||
import androidx.lifecycle.ViewModel
 | 
			
		||||
import androidx.lifecycle.viewModelScope
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
 | 
			
		||||
| 
						 | 
				
			
			@ -38,30 +37,26 @@ class PlayerRibbonViewModel(
 | 
			
		|||
            initialValue = emptyList()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    private val _rolls: HashMap<String, Int?> = hashMapOf()
 | 
			
		||||
    val rolls: StateFlow<Map<String, Int?>> = rollHistoryRepository.rolls
 | 
			
		||||
        .map {
 | 
			
		||||
            _rolls[it.characterId] = it.rollValue
 | 
			
		||||
            _rolls
 | 
			
		||||
        }.stateIn(
 | 
			
		||||
            scope = viewModelScope,
 | 
			
		||||
            started = SharingStarted.Eagerly,
 | 
			
		||||
            initialValue = emptyMap()
 | 
			
		||||
        )
 | 
			
		||||
    private val rolls = hashMapOf<String, MutableState<PlayerPortraitRollUio?>>()
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
    @Stable
 | 
			
		||||
    fun roll(characterId: String): State<Int?> {
 | 
			
		||||
        val state = rememberSaveable(characterId) {
 | 
			
		||||
            mutableStateOf<Int?>(null)
 | 
			
		||||
        }
 | 
			
		||||
    fun roll(characterId: String): State<PlayerPortraitRollUio?> {
 | 
			
		||||
        val state = rolls.getOrPut(characterId) { mutableStateOf(null) }
 | 
			
		||||
        LaunchedEffect(characterId) {
 | 
			
		||||
            rollHistoryRepository.rolls.collect {
 | 
			
		||||
                if (it.characterId == characterId) {
 | 
			
		||||
                    state.value = it.rollValue
 | 
			
		||||
                    state.value = PlayerPortraitRollUio(
 | 
			
		||||
                        characterId = characterId,
 | 
			
		||||
                        value = it.rollValue,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return state
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onPortraitRollRightClick(characterId: String) {
 | 
			
		||||
        rolls[characterId]?.value = null
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3,9 +3,8 @@ 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.desktop.lwa.repository.characterSheet.SkillDescriptionFactory
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId
 | 
			
		||||
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.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Node
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.Res
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +41,6 @@ import org.jetbrains.compose.resources.getString
 | 
			
		|||
class CharacterSheetFactory(
 | 
			
		||||
    private val skillUseCase: ExpressionUseCase,
 | 
			
		||||
    private val expressionUseCase: ExpressionUseCase,
 | 
			
		||||
    private val skillDescriptionFactory: SkillDescriptionFactory,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    suspend fun convertToUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -213,7 +211,7 @@ class CharacterSheetFactory(
 | 
			
		|||
                        skill = skill,
 | 
			
		||||
                        alterations = alterations[skill.id].sum(),
 | 
			
		||||
                    ),
 | 
			
		||||
                    tooltips = skillDescriptionFactory.baseSkillDescription(id = skill.id)?.let {
 | 
			
		||||
                    tooltips = skill.description?.let {
 | 
			
		||||
                        TooltipUio(
 | 
			
		||||
                            title = skill.label,
 | 
			
		||||
                            description = it,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ 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.characterSheet.CharacterSheetRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
			
		||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId
 | 
			
		||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,14 +3,13 @@ package com.pixelized.desktop.lwa.ui.screen.characterSheet.edit
 | 
			
		|||
import androidx.compose.runtime.State
 | 
			
		||||
import androidx.compose.runtime.derivedStateOf
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import com.pixelized.desktop.lwa.business.CharacterSheetUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.SkillDescriptionFactory
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.SkillFieldFactory
 | 
			
		||||
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.usecase.CharacterSheetUseCase
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.Res
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__action_label
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__name_label
 | 
			
		||||
| 
						 | 
				
			
			@ -54,9 +53,8 @@ import java.util.UUID
 | 
			
		|||
import kotlin.math.max
 | 
			
		||||
 | 
			
		||||
class CharacterSheetEditFactory(
 | 
			
		||||
    private val characterSheetUseCase: CharacterSheetUseCase,
 | 
			
		||||
    private val skillFieldFactory: SkillFieldFactory,
 | 
			
		||||
    private val skillDescriptionFactory: SkillDescriptionFactory,
 | 
			
		||||
    private val characterSheetUseCase: CharacterSheetUseCase,
 | 
			
		||||
) {
 | 
			
		||||
    suspend fun updateCharacterSheet(
 | 
			
		||||
        currentSheet: CharacterSheet?,
 | 
			
		||||
| 
						 | 
				
			
			@ -119,7 +117,7 @@ class CharacterSheetEditFactory(
 | 
			
		|||
                CharacterSheet.Skill(
 | 
			
		||||
                    id = editedSkill.id,
 | 
			
		||||
                    label = editedSkill.label,
 | 
			
		||||
                    description = skillDescriptionFactory.baseSkillDescription(editedSkill.id),
 | 
			
		||||
                    description = currentSkill?.description,
 | 
			
		||||
                    base = "${editedSkill.base.value}",
 | 
			
		||||
                    bonus = editedSkill.bonus.value.value.takeIf { it.isNotBlank() },
 | 
			
		||||
                    level = editedSkill.level.value.value.takeIf { it.isNotBlank() },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,8 +4,8 @@ import androidx.compose.runtime.State
 | 
			
		|||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.lifecycle.SavedStateHandle
 | 
			
		||||
import androidx.lifecycle.ViewModel
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetEditDestination
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetEditDestination
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.SkillFieldFactory
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionFieldUio
 | 
			
		||||
import kotlinx.coroutines.runBlocking
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,11 +4,11 @@ import androidx.compose.runtime.Composable
 | 
			
		|||
import androidx.compose.runtime.State
 | 
			
		||||
import androidx.lifecycle.ViewModel
 | 
			
		||||
import com.lordcodes.turtle.shellRun
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.OperatingSystem
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.storePath
 | 
			
		||||
import com.pixelized.desktop.lwa.utils.extention.collectAsState
 | 
			
		||||
import com.pixelized.shared.lwa.OperatingSystem
 | 
			
		||||
import com.pixelized.shared.lwa.storePath
 | 
			
		||||
 | 
			
		||||
class MainPageViewModel(
 | 
			
		||||
    repository: CharacterSheetRepository,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,13 +6,13 @@ import androidx.compose.animation.core.spring
 | 
			
		|||
import androidx.compose.runtime.State
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.lifecycle.ViewModel
 | 
			
		||||
import com.pixelized.desktop.lwa.business.SkillStepUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.business.ExpressionUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.business.SkillStepUseCase
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
			
		||||
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 kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.coroutineScope
 | 
			
		||||
import kotlinx.coroutines.delay
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,8 +5,8 @@ import androidx.compose.runtime.mutableStateOf
 | 
			
		|||
import androidx.lifecycle.ViewModel
 | 
			
		||||
import androidx.lifecycle.viewModelScope
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
			
		||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
 | 
			
		||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
 | 
			
		||||
import kotlinx.coroutines.flow.combine
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +28,8 @@ class RollHistoryViewModel(
 | 
			
		|||
                    add(
 | 
			
		||||
                        index = 0,
 | 
			
		||||
                        element = RollHistoryItemUio(
 | 
			
		||||
                            character = sheets.firstOrNull { it.id == content.characterId }?.name ?: "",
 | 
			
		||||
                            character = sheets.firstOrNull { it.id == content.characterId }?.name
 | 
			
		||||
                                ?: "",
 | 
			
		||||
                            skillLabel = content.skillLabel,
 | 
			
		||||
                            rollDifficulty = content.rollDifficulty,
 | 
			
		||||
                            resultLabel = content.resultLabel,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,7 @@ val MaterialTheme.lwa: LwaTheme
 | 
			
		|||
 | 
			
		||||
@Stable
 | 
			
		||||
data class LwaTheme(
 | 
			
		||||
    val color: LwaColorTheme,
 | 
			
		||||
    val colorScheme: LwaColorTheme,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +30,7 @@ fun LwaTheme(
 | 
			
		|||
    val lwaColorTheme = darkLwaColorTheme()
 | 
			
		||||
    val theme = remember {
 | 
			
		||||
        LwaTheme(
 | 
			
		||||
            color = lwaColorTheme,
 | 
			
		||||
            colorScheme = lwaColorTheme,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.utils.extention
 | 
			
		||||
 | 
			
		||||
import com.pixelized.server.lwa.protocol.Message
 | 
			
		||||
import com.pixelized.shared.lwa.protocol.Message
 | 
			
		||||
import io.ktor.websocket.Frame
 | 
			
		||||
import io.ktor.websocket.readText
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue