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
|
|
@ -14,6 +14,8 @@ kotlin {
|
||||||
val desktopMain by getting
|
val desktopMain by getting
|
||||||
|
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
|
// common
|
||||||
|
implementation(projects.shared)
|
||||||
// compose
|
// compose
|
||||||
implementation(compose.runtime)
|
implementation(compose.runtime)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
|
|
@ -26,19 +28,18 @@ kotlin {
|
||||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||||
implementation(libs.androidx.navigation.compose)
|
implementation(libs.androidx.navigation.compose)
|
||||||
// injection
|
// injection
|
||||||
api(libs.koin.core)
|
|
||||||
implementation(libs.koin.compose)
|
implementation(libs.koin.compose)
|
||||||
implementation(libs.koin.compose.viewmodel)
|
implementation(libs.koin.compose.viewmodel)
|
||||||
// composable component.
|
// composable component.
|
||||||
implementation(libs.coil.compose)
|
implementation(libs.coil.compose)
|
||||||
implementation(libs.coil.network)
|
implementation(libs.coil.network)
|
||||||
// common
|
|
||||||
implementation(projects.shared)
|
|
||||||
// network
|
// network
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
implementation(libs.ktor.serialization.json)
|
||||||
implementation(libs.ktor.client.core)
|
implementation(libs.ktor.client.core)
|
||||||
implementation(libs.ktor.client.cio)
|
implementation(libs.ktor.client.okhttp)
|
||||||
implementation(libs.ktor.client.websockets)
|
implementation(libs.ktor.client.websockets)
|
||||||
|
implementation(libs.ktor.client.negotiation)
|
||||||
// shell
|
// shell
|
||||||
implementation(libs.turtle)
|
implementation(libs.turtle)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,8 @@
|
||||||
-keep class kotlinx.coroutines.** { *; }
|
-keep class kotlinx.coroutines.** { *; }
|
||||||
|
|
||||||
# OkHttp comming from COIL.
|
# OkHttp comming from COIL.
|
||||||
-dontwarn okhttp3.internal.platform.**
|
-dontwarn okhttp3.internal.platform.**
|
||||||
|
|
||||||
|
# Serialization
|
||||||
|
-keep class io.ktor.serialization.kotlinx.json.** { *; }
|
||||||
|
-keep class com.pixelized.shared.lwa.model.** { *; }
|
||||||
|
|
@ -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.navigation.screen.MainNavHost
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CampaignScreen
|
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.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.screen.rollhistory.RollHistoryPage
|
||||||
import com.pixelized.desktop.lwa.ui.theme.LwaTheme
|
import com.pixelized.desktop.lwa.ui.theme.LwaTheme
|
||||||
import kotlinx.coroutines.launch
|
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.resources.getString
|
||||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||||
import org.koin.compose.koinInject
|
import org.koin.compose.koinInject
|
||||||
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
val LocalWindowController = compositionLocalOf<WindowController> {
|
val LocalWindowController = compositionLocalOf<WindowController> {
|
||||||
error("Local Window Controller is not yet ready")
|
error("Local Window Controller is not yet ready")
|
||||||
|
|
@ -125,8 +127,8 @@ fun ApplicationScope.App() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
// MainNavHost()
|
MainNavHost()
|
||||||
CampaignScreen()
|
// CampaignScreen()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
NetworkSnackHandler(
|
NetworkSnackHandler(
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,24 @@
|
||||||
package com.pixelized.desktop.lwa
|
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.RollUseCase
|
||||||
import com.pixelized.desktop.lwa.business.SettingsUseCase
|
import com.pixelized.desktop.lwa.business.SettingsUseCase
|
||||||
import com.pixelized.desktop.lwa.business.SkillStepUseCase
|
import com.pixelized.desktop.lwa.business.SkillStepUseCase
|
||||||
import com.pixelized.desktop.lwa.business.ExpressionUseCase
|
import com.pixelized.desktop.lwa.parser.dice.DiceParser
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetJsonFactory
|
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.CharacterSheetRepository
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetStore
|
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.network.NetworkRepository
|
||||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||||
import com.pixelized.desktop.lwa.repository.settings.SettingsFactory
|
import com.pixelized.desktop.lwa.repository.settings.SettingsFactory
|
||||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
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.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.CharacterSheetFactory
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetViewModel
|
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.CharacterSheetEditFactory
|
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.network.NetworkViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
|
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
|
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
|
||||||
import com.pixelized.desktop.lwa.parser.dice.DiceParser
|
import com.pixelized.shared.lwa.model.campaign.CampaignRepository
|
||||||
import com.pixelized.desktop.lwa.parser.word.WordParser
|
import com.pixelized.shared.lwa.model.campaign.model.CampaignFactory
|
||||||
import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
|
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
|
import io.ktor.client.HttpClient
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
|
import io.ktor.client.engine.HttpClientEngine
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbonViewModel
|
import io.ktor.client.engine.okhttp.OkHttp
|
||||||
import kotlinx.serialization.json.Json
|
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.factoryOf
|
||||||
import org.koin.core.module.dsl.singleOf
|
import org.koin.core.module.dsl.singleOf
|
||||||
import org.koin.core.module.dsl.viewModelOf
|
import org.koin.core.module.dsl.viewModelOf
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val moduleDependencies
|
val appModuleDependencies
|
||||||
get() = listOf(
|
get() = listOf(
|
||||||
toolsDependencies,
|
|
||||||
parserDependencies,
|
parserDependencies,
|
||||||
factoryDependencies,
|
factoryDependencies,
|
||||||
useCaseDependencies,
|
useCaseDependencies,
|
||||||
storeDependencies,
|
storeDependencies,
|
||||||
repositoryDependencies,
|
repositoryDependencies,
|
||||||
viewModelDependencies,
|
viewModelDependencies,
|
||||||
|
toolsDependencies,
|
||||||
)
|
)
|
||||||
|
|
||||||
val toolsDependencies
|
val toolsDependencies
|
||||||
get() = module {
|
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
|
val storeDependencies
|
||||||
|
|
@ -68,17 +87,17 @@ val repositoryDependencies
|
||||||
singleOf(::RollHistoryRepository)
|
singleOf(::RollHistoryRepository)
|
||||||
singleOf(::SettingsRepository)
|
singleOf(::SettingsRepository)
|
||||||
singleOf(::AlterationRepository)
|
singleOf(::AlterationRepository)
|
||||||
|
singleOf(::CampaignRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
val factoryDependencies
|
val factoryDependencies
|
||||||
get() = module {
|
get() = module {
|
||||||
factoryOf(::CharacterSheetFactory)
|
factoryOf(::CharacterSheetFactory)
|
||||||
factoryOf(::CharacterSheetEditFactory)
|
factoryOf(::CharacterSheetEditFactory)
|
||||||
factoryOf(::CharacterSheetJsonFactory)
|
|
||||||
factoryOf(::NetworkFactory)
|
factoryOf(::NetworkFactory)
|
||||||
factoryOf(::SkillFieldFactory)
|
factoryOf(::SkillFieldFactory)
|
||||||
factoryOf(::SkillDescriptionFactory)
|
|
||||||
factoryOf(::SettingsFactory)
|
factoryOf(::SettingsFactory)
|
||||||
|
factoryOf(::CampaignFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
val viewModelDependencies
|
val viewModelDependencies
|
||||||
|
|
@ -106,6 +125,6 @@ val useCaseDependencies
|
||||||
factoryOf(::SkillStepUseCase)
|
factoryOf(::SkillStepUseCase)
|
||||||
factoryOf(::RollUseCase)
|
factoryOf(::RollUseCase)
|
||||||
factoryOf(::ExpressionUseCase)
|
factoryOf(::ExpressionUseCase)
|
||||||
factoryOf(::CharacterSheetUseCase)
|
|
||||||
factoryOf(::SettingsUseCase)
|
factoryOf(::SettingsUseCase)
|
||||||
|
factoryOf(::CharacterSheetUseCase)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.Expression
|
||||||
import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
|
import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
|
||||||
import com.pixelized.desktop.lwa.parser.word.Word
|
import com.pixelized.desktop.lwa.parser.word.Word
|
||||||
import com.pixelized.desktop.lwa.repository.alteration.model.FieldAlteration
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.parser.expression.ExpressionParser
|
||||||
import com.pixelized.desktop.lwa.repository.alteration.model.Alteration
|
import com.pixelized.desktop.lwa.repository.alteration.model.Alteration
|
||||||
import com.pixelized.desktop.lwa.repository.alteration.model.AlterationMetadata
|
import com.pixelized.desktop.lwa.repository.alteration.model.AlterationMetadata
|
||||||
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.repository.characterSheet.model.CharacterSheet.CharacteristicId.ARMOR
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.ARMOR
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId.DEX
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.DEX
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId.HEI
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.HEI
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId.MOV
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.MOV
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CharacteristicId.STR
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.STR
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.ACROBATICS_ID
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.ACROBATICS_ID
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.AID_ID
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.AID_ID
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.ATHLETICS_ID
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.ATHLETICS_ID
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.BARGAIN_ID
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.BARGAIN_ID
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.COMBAT_ID
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.COMBAT_ID
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.DISCRETION_ID
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.DISCRETION_ID
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.INTIMIDATION_ID
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.INTIMIDATION_ID
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.PERCEPTION_ID
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.PERCEPTION_ID
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.PERSUASION_ID
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.PERSUASION_ID
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet.CommonSkillId.SPIEL_ID
|
import com.pixelized.shared.lwa.model.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.CommonSkillId.THROW_ID
|
||||||
|
|
||||||
class AlterationStore(
|
class AlterationStore(
|
||||||
private val expressionParser: ExpressionParser,
|
private val expressionParser: ExpressionParser,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.pixelized.desktop.lwa.repository.characterSheet
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
|
@ -53,11 +53,11 @@ class CharacterSheetRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun save(characterSheet: CharacterSheet) {
|
fun save(characterSheet: CharacterSheet) {
|
||||||
store.save(sheet = characterSheet)
|
// store.save(sheet = characterSheet)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delete(id: String) {
|
fun delete(id: String) {
|
||||||
store.delete(id = id)
|
// store.delete(id = id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDiminishedForCharacter(id: String, diminished: Int) {
|
fun setDiminishedForCharacter(id: String, diminished: Int) {
|
||||||
|
|
@ -65,5 +65,4 @@ class CharacterSheetRepository(
|
||||||
this[id] = diminished
|
this[id] = diminished
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,24 +1,25 @@
|
||||||
package com.pixelized.desktop.lwa.repository.characterSheet
|
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 com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheetJson
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheetJson
|
||||||
import com.pixelized.desktop.lwa.repository.characterStorePath
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.File
|
|
||||||
import java.text.Collator
|
|
||||||
|
|
||||||
class CharacterSheetStore(
|
class CharacterSheetStore(
|
||||||
private val factory: CharacterSheetJsonFactory,
|
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())
|
private val flow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
@ -30,85 +31,22 @@ class CharacterSheetStore(
|
||||||
|
|
||||||
fun characterSheetFlow(): StateFlow<List<CharacterSheet>> = flow
|
fun characterSheetFlow(): StateFlow<List<CharacterSheet>> = flow
|
||||||
|
|
||||||
@Throws(
|
|
||||||
CharacterSheetStoreException::class,
|
|
||||||
FileWriteException::class,
|
|
||||||
JsonConversionException::class,
|
|
||||||
)
|
|
||||||
fun save(sheet: CharacterSheet) {
|
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 {
|
fun delete(id: String): Boolean {
|
||||||
val file = characterSheetFile(id = id)
|
return false
|
||||||
flow.value = flow.value.toMutableList()
|
|
||||||
.also { data ->
|
|
||||||
data.removeIf { it.id == id }
|
|
||||||
}
|
|
||||||
.sortedBy {
|
|
||||||
it.name
|
|
||||||
}
|
|
||||||
return file.delete()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(
|
|
||||||
CharacterSheetStoreException::class,
|
|
||||||
FileReadException::class,
|
|
||||||
JsonConversionException::class,
|
|
||||||
)
|
|
||||||
suspend fun load(): List<CharacterSheet> {
|
suspend fun load(): List<CharacterSheet> {
|
||||||
return characterDirectory
|
val request: List<CharacterSheetJson> = client
|
||||||
.listFiles()
|
.get("http://pixelized.freeboxos.fr:16030/characters")
|
||||||
?.mapNotNull { file ->
|
.body()
|
||||||
val json = try {
|
val data = request.map {
|
||||||
file.readText(charset = Charsets.UTF_8)
|
factory.convertFromJson(json = it)
|
||||||
} catch (exception: Exception) {
|
}
|
||||||
throw FileReadException(root = exception)
|
return data
|
||||||
}
|
|
||||||
// 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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class CharacterSheetStoreException(root: Exception) : Exception(root)
|
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,16 +1,12 @@
|
||||||
package com.pixelized.desktop.lwa.repository.network
|
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.network.helper.connectWebSocket
|
||||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
||||||
import com.pixelized.desktop.lwa.utils.extention.decodeFromFrame
|
import com.pixelized.desktop.lwa.utils.extention.decodeFromFrame
|
||||||
import com.pixelized.desktop.lwa.utils.extention.encodeToFrame
|
import com.pixelized.desktop.lwa.utils.extention.encodeToFrame
|
||||||
import com.pixelized.server.lwa.SERVER_PORT
|
import com.pixelized.shared.lwa.SERVER_PORT
|
||||||
import com.pixelized.server.lwa.protocol.Message
|
import com.pixelized.shared.lwa.protocol.Message
|
||||||
import com.pixelized.server.lwa.protocol.MessageType
|
import com.pixelized.shared.lwa.protocol.MessageType
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.websocket.Frame
|
import io.ktor.websocket.Frame
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
@ -25,10 +21,9 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
typealias Client = HttpClient
|
|
||||||
|
|
||||||
class NetworkRepository(
|
class NetworkRepository(
|
||||||
private val settingsRepository: SettingsRepository,
|
private val settingsRepository: SettingsRepository,
|
||||||
|
private val client: HttpClient,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
const val DEFAULT_PORT = SERVER_PORT
|
const val DEFAULT_PORT = SERVER_PORT
|
||||||
|
|
@ -37,7 +32,6 @@ class NetworkRepository(
|
||||||
|
|
||||||
private val scope = CoroutineScope(Dispatchers.IO)
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
private var networkJob: Job? = null
|
private var networkJob: Job? = null
|
||||||
private var client: Client? = null
|
|
||||||
|
|
||||||
private val outgoingMessageBuffer = MutableSharedFlow<Message>()
|
private val outgoingMessageBuffer = MutableSharedFlow<Message>()
|
||||||
private val incomingMessageBuffer = MutableSharedFlow<Message>()
|
private val incomingMessageBuffer = MutableSharedFlow<Message>()
|
||||||
|
|
@ -46,7 +40,6 @@ class NetworkRepository(
|
||||||
private val _status = MutableStateFlow(Status.DISCONNECTED)
|
private val _status = MutableStateFlow(Status.DISCONNECTED)
|
||||||
val status: StateFlow<Status> get() = _status
|
val status: StateFlow<Status> get() = _status
|
||||||
|
|
||||||
|
|
||||||
fun connect(
|
fun connect(
|
||||||
host: String,
|
host: String,
|
||||||
port: Int,
|
port: Int,
|
||||||
|
|
@ -54,12 +47,10 @@ class NetworkRepository(
|
||||||
onFailure: (Exception) -> Unit = { },
|
onFailure: (Exception) -> Unit = { },
|
||||||
onClose: () -> Unit = { },
|
onClose: () -> Unit = { },
|
||||||
) {
|
) {
|
||||||
client = client()
|
|
||||||
|
|
||||||
networkJob?.cancel()
|
networkJob?.cancel()
|
||||||
networkJob = scope.launch {
|
networkJob = scope.launch {
|
||||||
try {
|
try {
|
||||||
client?.connectWebSocket(host = host, port = port) {
|
client.connectWebSocket(host = host, port = port) {
|
||||||
_status.value = Status.CONNECTED
|
_status.value = Status.CONNECTED
|
||||||
onConnect()
|
onConnect()
|
||||||
|
|
||||||
|
|
@ -93,7 +84,7 @@ class NetworkRepository(
|
||||||
fun disconnect() {
|
fun disconnect() {
|
||||||
networkJob?.cancel()
|
networkJob?.cancel()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
client?.close()
|
client.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,15 @@
|
||||||
package com.pixelized.desktop.lwa.repository.network.helper
|
package com.pixelized.desktop.lwa.repository.network.helper
|
||||||
|
|
||||||
import io.ktor.client.HttpClient
|
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.DefaultClientWebSocketSession
|
||||||
import io.ktor.client.plugins.websocket.WebSockets
|
|
||||||
import io.ktor.client.plugins.websocket.webSocket
|
import io.ktor.client.plugins.websocket.webSocket
|
||||||
import io.ktor.http.HttpMethod
|
import io.ktor.http.HttpMethod
|
||||||
|
|
||||||
// https://ktor.io/docs/client-websockets.html#handle-session
|
// 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(
|
suspend fun HttpClient.connectWebSocket(
|
||||||
host: String,
|
host: String,
|
||||||
port: Int,
|
port: Int,
|
||||||
block: suspend DefaultClientWebSocketSession.() -> Unit
|
block: suspend DefaultClientWebSocketSession.() -> Unit,
|
||||||
) {
|
) {
|
||||||
webSocket(
|
webSocket(
|
||||||
method = HttpMethod.Get,
|
method = HttpMethod.Get,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package com.pixelized.desktop.lwa.repository.roll_history
|
package com.pixelized.desktop.lwa.repository.roll_history
|
||||||
|
|
||||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||||
import com.pixelized.server.lwa.protocol.MessageType
|
import com.pixelized.shared.lwa.protocol.MessageType
|
||||||
import com.pixelized.server.lwa.protocol.roll.RollMessage
|
import com.pixelized.shared.lwa.protocol.roll.RollMessage
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
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.business.SettingsUseCase
|
||||||
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
||||||
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJson
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
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.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
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.MainDestination
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableMainPage
|
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.navigation.screen.destination.composableNetworkPage
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
|
||||||
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
val LocalScreenController = compositionLocalOf<NavHostController> {
|
val LocalScreenController = compositionLocalOf<NavHostController> {
|
||||||
error("MainNavHost controller is not yet ready")
|
error("MainNavHost controller is not yet ready")
|
||||||
|
|
@ -18,7 +21,12 @@ val LocalScreenController = compositionLocalOf<NavHostController> {
|
||||||
fun MainNavHost(
|
fun MainNavHost(
|
||||||
controller: NavHostController = rememberNavController(),
|
controller: NavHostController = rememberNavController(),
|
||||||
startDestination: String = MainDestination.navigationRoute(),
|
startDestination: String = MainDestination.navigationRoute(),
|
||||||
|
networkViewModel: NetworkViewModel = koinViewModel(),
|
||||||
) {
|
) {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
networkViewModel.connect()
|
||||||
|
}
|
||||||
|
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalScreenController provides controller,
|
LocalScreenController provides controller,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -83,9 +83,7 @@ fun CampaignScreen(
|
||||||
},
|
},
|
||||||
leftOverlay = {
|
leftOverlay = {
|
||||||
PlayerRibbon(
|
PlayerRibbon(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxHeight(),
|
||||||
.padding(all = 8.dp)
|
|
||||||
.fillMaxHeight(),
|
|
||||||
onCharacter = {
|
onCharacter = {
|
||||||
characterDetailViewModel.showCharacter(id = it)
|
characterDetailViewModel.showCharacter(id = it)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ private fun Background(
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
color = MaterialTheme.lwa.color.elevatedSurface,
|
color = MaterialTheme.lwa.colorScheme.elevatedSurface,
|
||||||
) {
|
) {
|
||||||
// Image(
|
// Image(
|
||||||
// modifier = Modifier.fillMaxSize().drawWithContent {
|
// modifier = Modifier.fillMaxSize().drawWithContent {
|
||||||
|
|
@ -200,7 +200,7 @@ private fun CharacterHeader(
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(Res.drawable.ic_close_24dp),
|
painter = painterResource(Res.drawable.ic_close_24dp),
|
||||||
tint = MaterialTheme.lwa.color.base.primary,
|
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -220,7 +220,7 @@ private fun CharacterHeader(
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.alignByBaseline(),
|
modifier = Modifier.alignByBaseline(),
|
||||||
style = MaterialTheme.typography.h6,
|
style = MaterialTheme.typography.h6,
|
||||||
color = MaterialTheme.lwa.color.base.primary,
|
color = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
text = dynDetail.value.hp,
|
text = dynDetail.value.hp,
|
||||||
)
|
)
|
||||||
|
|
@ -243,7 +243,7 @@ private fun CharacterHeader(
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.alignByBaseline(),
|
modifier = Modifier.alignByBaseline(),
|
||||||
style = MaterialTheme.typography.h6,
|
style = MaterialTheme.typography.h6,
|
||||||
color = MaterialTheme.lwa.color.base.primary,
|
color = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
text = dynDetail.value.pp,
|
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.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.clip
|
||||||
import androidx.compose.ui.draw.drawWithContent
|
import androidx.compose.ui.draw.drawWithContent
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.FilterQuality
|
import androidx.compose.ui.graphics.FilterQuality
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.unit.DpSize
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
|
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.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
|
||||||
|
|
@ -42,19 +41,15 @@ data class PlayerPortraitUio(
|
||||||
val maxPp: Int,
|
val maxPp: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
object PlayerPortrait {
|
|
||||||
object Default {
|
|
||||||
val size = DpSize(96.dp, 128.dp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayerPortrait(
|
fun PlayerPortrait(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
size: DpSize = PlayerPortrait.Default.size,
|
size: DpSize,
|
||||||
character: PlayerPortraitUio,
|
character: PlayerPortraitUio,
|
||||||
onCharacter: (id: String) -> Unit,
|
onCharacter: (id: String) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
val colorScheme = MaterialTheme.lwa.colorScheme
|
||||||
|
|
||||||
DecoratedBox(
|
DecoratedBox(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.size(size = size)
|
.size(size = size)
|
||||||
|
|
@ -76,11 +71,11 @@ fun PlayerPortrait(
|
||||||
drawRect(
|
drawRect(
|
||||||
brush = Brush.verticalGradient(
|
brush = Brush.verticalGradient(
|
||||||
listOf(
|
listOf(
|
||||||
Color.Black.copy(alpha = 0.0f),
|
colorScheme.elevatedSurface.copy(alpha = 0.0f),
|
||||||
Color.Black.copy(alpha = 0.0f),
|
colorScheme.elevatedSurface.copy(alpha = 0.0f),
|
||||||
Color.Black.copy(alpha = 0.0f),
|
colorScheme.elevatedSurface.copy(alpha = 0.0f),
|
||||||
Color.Black.copy(alpha = 0.5f),
|
colorScheme.elevatedSurface.copy(alpha = 0.5f),
|
||||||
Color.Black.copy(alpha = 0.8f),
|
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.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.togetherWith
|
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.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.layout.size
|
||||||
|
import androidx.compose.foundation.onClick
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Shadow
|
import androidx.compose.ui.graphics.Shadow
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
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.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class PlayerPortraitRollUio(
|
||||||
|
val characterId: String,
|
||||||
|
val value: Int?,
|
||||||
|
)
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class PlayerPortraitRollAnimation(
|
data class PlayerPortraitRollAnimation(
|
||||||
val alpha: Animatable<Float, AnimationVector1D> = Animatable(0f),
|
val alpha: Animatable<Float, AnimationVector1D> = Animatable(0f),
|
||||||
|
|
@ -38,13 +55,19 @@ data class PlayerPortraitRollAnimation(
|
||||||
val scale: Animatable<Float, AnimationVector1D> = Animatable(1f),
|
val scale: Animatable<Float, AnimationVector1D> = Animatable(1f),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayerPortraitRoll(
|
fun PlayerPortraitRoll(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
value: Int?,
|
size: DpSize,
|
||||||
|
value: PlayerPortraitRollUio?,
|
||||||
|
onLeftClick: (PlayerPortraitRollUio) -> Unit,
|
||||||
|
onRightClick: (PlayerPortraitRollUio) -> Unit,
|
||||||
) {
|
) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
modifier = modifier.graphicsLayer { clip = false },
|
modifier = modifier
|
||||||
|
.size(size = size)
|
||||||
|
.graphicsLayer { clip = false },
|
||||||
targetState = value,
|
targetState = value,
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
val enter = fadeIn()
|
val enter = fadeIn()
|
||||||
|
|
@ -61,33 +84,41 @@ fun PlayerPortraitRoll(
|
||||||
},
|
},
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
when (it) {
|
if (it != null) {
|
||||||
null -> Unit
|
Icon(
|
||||||
else -> {
|
modifier = Modifier
|
||||||
Icon(
|
.graphicsLayer {
|
||||||
modifier = Modifier
|
this.alpha = 0.8f
|
||||||
.graphicsLayer {
|
this.rotationZ = animation.rotation.value
|
||||||
this.alpha = 0.8f
|
}
|
||||||
this.rotationZ = animation.rotation.value
|
.fillMaxWidth()
|
||||||
}
|
.aspectRatio(1f)
|
||||||
.size(48.dp),
|
.padding(all = 8.dp)
|
||||||
painter = painterResource(Res.drawable.ic_d20_24dp),
|
.clip(shape = CircleShape)
|
||||||
tint = MaterialTheme.colors.primary,
|
.onClick(
|
||||||
contentDescription = null,
|
matcher = PointerMatcher.mouse(PointerButton.Secondary),
|
||||||
)
|
onClick = { onRightClick(it) },
|
||||||
Text(
|
).clickable {
|
||||||
style = MaterialTheme.typography.h5.copy(
|
onLeftClick(it)
|
||||||
shadow = Shadow(
|
}
|
||||||
color = MaterialTheme.colors.surface,
|
.padding(all = 8.dp),
|
||||||
offset = Offset.Zero,
|
painter = painterResource(Res.drawable.ic_d20_24dp),
|
||||||
blurRadius = 8f,
|
tint = MaterialTheme.colors.primary,
|
||||||
)
|
contentDescription = null,
|
||||||
),
|
)
|
||||||
textAlign = TextAlign.Center,
|
Text(
|
||||||
color = MaterialTheme.colors.onSurface,
|
style = MaterialTheme.typography.h5.copy(
|
||||||
text = it.toString()
|
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
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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.Row
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
|
object PlayerRibbon {
|
||||||
|
object Default {
|
||||||
|
val size = DpSize(96.dp, 128.dp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayerRibbon(
|
fun PlayerRibbon(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
playerRibbonViewModel: PlayerRibbonViewModel = koinViewModel(),
|
playerRibbonViewModel: PlayerRibbonViewModel = koinViewModel(),
|
||||||
|
padding: PaddingValues = PaddingValues(all = 8.dp),
|
||||||
onCharacter: (id: String) -> Unit,
|
onCharacter: (id: String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val characters = playerRibbonViewModel.characters.collectAsState()
|
val characters = playerRibbonViewModel.characters.collectAsState()
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
contentPadding = padding,
|
||||||
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
|
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
|
|
@ -29,15 +39,19 @@ fun PlayerRibbon(
|
||||||
) {
|
) {
|
||||||
Row {
|
Row {
|
||||||
PlayerPortrait(
|
PlayerPortrait(
|
||||||
|
size = PlayerRibbon.Default.size,
|
||||||
character = it,
|
character = it,
|
||||||
onCharacter = onCharacter,
|
onCharacter = onCharacter,
|
||||||
)
|
)
|
||||||
PlayerPortraitRoll(
|
PlayerPortraitRoll(
|
||||||
modifier = Modifier.size(
|
size = PlayerRibbon.Default.size,
|
||||||
width = 64.dp,
|
|
||||||
height = PlayerPortrait.Default.size.height
|
|
||||||
),
|
|
||||||
value = playerRibbonViewModel.roll(characterId = it.id).value,
|
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.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||||
|
|
@ -38,30 +37,26 @@ class PlayerRibbonViewModel(
|
||||||
initialValue = emptyList()
|
initialValue = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
private val _rolls: HashMap<String, Int?> = hashMapOf()
|
private val rolls = hashMapOf<String, MutableState<PlayerPortraitRollUio?>>()
|
||||||
val rolls: StateFlow<Map<String, Int?>> = rollHistoryRepository.rolls
|
|
||||||
.map {
|
|
||||||
_rolls[it.characterId] = it.rollValue
|
|
||||||
_rolls
|
|
||||||
}.stateIn(
|
|
||||||
scope = viewModelScope,
|
|
||||||
started = SharingStarted.Eagerly,
|
|
||||||
initialValue = emptyMap()
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Stable
|
@Stable
|
||||||
fun roll(characterId: String): State<Int?> {
|
fun roll(characterId: String): State<PlayerPortraitRollUio?> {
|
||||||
val state = rememberSaveable(characterId) {
|
val state = rolls.getOrPut(characterId) { mutableStateOf(null) }
|
||||||
mutableStateOf<Int?>(null)
|
|
||||||
}
|
|
||||||
LaunchedEffect(characterId) {
|
LaunchedEffect(characterId) {
|
||||||
rollHistoryRepository.rolls.collect {
|
rollHistoryRepository.rolls.collect {
|
||||||
if (it.characterId == characterId) {
|
if (it.characterId == characterId) {
|
||||||
state.value = it.rollValue
|
state.value = PlayerPortraitRollUio(
|
||||||
|
characterId = characterId,
|
||||||
|
value = it.rollValue,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state
|
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.business.ExpressionUseCase
|
||||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
|
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
|
||||||
import com.pixelized.desktop.lwa.repository.alteration.model.FieldAlteration
|
import com.pixelized.desktop.lwa.repository.alteration.model.FieldAlteration
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.SkillDescriptionFactory
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId
|
||||||
import com.pixelized.desktop.lwa.repository.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.Characteristic
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Node
|
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Node
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
|
@ -42,7 +41,6 @@ import org.jetbrains.compose.resources.getString
|
||||||
class CharacterSheetFactory(
|
class CharacterSheetFactory(
|
||||||
private val skillUseCase: ExpressionUseCase,
|
private val skillUseCase: ExpressionUseCase,
|
||||||
private val expressionUseCase: ExpressionUseCase,
|
private val expressionUseCase: ExpressionUseCase,
|
||||||
private val skillDescriptionFactory: SkillDescriptionFactory,
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun convertToUio(
|
suspend fun convertToUio(
|
||||||
|
|
@ -213,7 +211,7 @@ class CharacterSheetFactory(
|
||||||
skill = skill,
|
skill = skill,
|
||||||
alterations = alterations[skill.id].sum(),
|
alterations = alterations[skill.id].sum(),
|
||||||
),
|
),
|
||||||
tooltips = skillDescriptionFactory.baseSkillDescription(id = skill.id)?.let {
|
tooltips = skill.description?.let {
|
||||||
TooltipUio(
|
TooltipUio(
|
||||||
title = skill.label,
|
title = skill.label,
|
||||||
description = it,
|
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.ui.navigation.screen.destination.CharacterSheetDestination
|
||||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
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.CharacterSheetDeleteConfirmationDialogUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialogUio
|
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.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.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.remember
|
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
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
|
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.State
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
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.SkillFieldFactory
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.occupation
|
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.ActionFieldUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.BaseSkillFieldUio
|
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.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.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__action_label
|
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__action_label
|
||||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__name_label
|
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__name_label
|
||||||
|
|
@ -54,9 +53,8 @@ import java.util.UUID
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
class CharacterSheetEditFactory(
|
class CharacterSheetEditFactory(
|
||||||
private val characterSheetUseCase: CharacterSheetUseCase,
|
|
||||||
private val skillFieldFactory: SkillFieldFactory,
|
private val skillFieldFactory: SkillFieldFactory,
|
||||||
private val skillDescriptionFactory: SkillDescriptionFactory,
|
private val characterSheetUseCase: CharacterSheetUseCase,
|
||||||
) {
|
) {
|
||||||
suspend fun updateCharacterSheet(
|
suspend fun updateCharacterSheet(
|
||||||
currentSheet: CharacterSheet?,
|
currentSheet: CharacterSheet?,
|
||||||
|
|
@ -119,7 +117,7 @@ class CharacterSheetEditFactory(
|
||||||
CharacterSheet.Skill(
|
CharacterSheet.Skill(
|
||||||
id = editedSkill.id,
|
id = editedSkill.id,
|
||||||
label = editedSkill.label,
|
label = editedSkill.label,
|
||||||
description = skillDescriptionFactory.baseSkillDescription(editedSkill.id),
|
description = currentSkill?.description,
|
||||||
base = "${editedSkill.base.value}",
|
base = "${editedSkill.base.value}",
|
||||||
bonus = editedSkill.bonus.value.value.takeIf { it.isNotBlank() },
|
bonus = editedSkill.bonus.value.value.takeIf { it.isNotBlank() },
|
||||||
level = editedSkill.level.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.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
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.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.common.SkillFieldFactory
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionFieldUio
|
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionFieldUio
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.lordcodes.turtle.shellRun
|
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.characterSheet.CharacterSheetRepository
|
||||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
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.desktop.lwa.utils.extention.collectAsState
|
||||||
|
import com.pixelized.shared.lwa.OperatingSystem
|
||||||
|
import com.pixelized.shared.lwa.storePath
|
||||||
|
|
||||||
class MainPageViewModel(
|
class MainPageViewModel(
|
||||||
repository: CharacterSheetRepository,
|
repository: CharacterSheetRepository,
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@ import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.pixelized.desktop.lwa.business.SkillStepUseCase
|
|
||||||
import com.pixelized.desktop.lwa.business.ExpressionUseCase
|
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.CharacterSheetRepository
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
|
|
||||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
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.characterSheet.detail.CharacterSheetPageUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.roll.DifficultyUio.Difficulty
|
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.Job
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
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.repository.roll_history.RollHistoryRepository
|
||||||
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
@ -28,7 +28,8 @@ class RollHistoryViewModel(
|
||||||
add(
|
add(
|
||||||
index = 0,
|
index = 0,
|
||||||
element = RollHistoryItemUio(
|
element = RollHistoryItemUio(
|
||||||
character = sheets.firstOrNull { it.id == content.characterId }?.name ?: "",
|
character = sheets.firstOrNull { it.id == content.characterId }?.name
|
||||||
|
?: "",
|
||||||
skillLabel = content.skillLabel,
|
skillLabel = content.skillLabel,
|
||||||
rollDifficulty = content.rollDifficulty,
|
rollDifficulty = content.rollDifficulty,
|
||||||
resultLabel = content.resultLabel,
|
resultLabel = content.resultLabel,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ val MaterialTheme.lwa: LwaTheme
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class LwaTheme(
|
data class LwaTheme(
|
||||||
val color: LwaColorTheme,
|
val colorScheme: LwaColorTheme,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -30,7 +30,7 @@ fun LwaTheme(
|
||||||
val lwaColorTheme = darkLwaColorTheme()
|
val lwaColorTheme = darkLwaColorTheme()
|
||||||
val theme = remember {
|
val theme = remember {
|
||||||
LwaTheme(
|
LwaTheme(
|
||||||
color = lwaColorTheme,
|
colorScheme = lwaColorTheme,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.pixelized.desktop.lwa.utils.extention
|
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.Frame
|
||||||
import io.ktor.websocket.readText
|
import io.ktor.websocket.readText
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
package com.pixelized.desktop.lwa
|
package com.pixelized.desktop.lwa
|
||||||
|
|
||||||
import androidx.compose.ui.window.application
|
import androidx.compose.ui.window.application
|
||||||
|
import com.pixelized.shared.lwa.sharedModuleDependencies
|
||||||
import org.koin.compose.KoinContext
|
import org.koin.compose.KoinContext
|
||||||
|
import org.koin.core.context.loadKoinModules
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import javax.swing.UIManager
|
import javax.swing.UIManager
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
|
||||||
startKoin {
|
startKoin {
|
||||||
modules(modules = moduleDependencies)
|
modules(modules = sharedModuleDependencies + appModuleDependencies)
|
||||||
}
|
}
|
||||||
application {
|
application {
|
||||||
KoinContext {
|
KoinContext {
|
||||||
|
|
|
||||||
|
|
@ -31,15 +31,20 @@ kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-co
|
||||||
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-json" }
|
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-json" }
|
||||||
|
|
||||||
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
||||||
|
koin-ktor = { module = "io.insert-koin:koin-ktor", version.ref = "koin" }
|
||||||
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" }
|
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" }
|
||||||
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" }
|
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" }
|
||||||
|
|
||||||
|
ktor-serialization-json = { group = 'io.ktor', name = 'ktor-serialization-kotlinx-json', version.ref = "ktor" }
|
||||||
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
|
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
|
||||||
ktor-client-cio = { group = 'io.ktor', name = "ktor-client-cio", version.ref = "ktor" }
|
ktor-client-cio = { group = 'io.ktor', name = "ktor-client-cio", version.ref = "ktor" }
|
||||||
|
ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" }
|
||||||
ktor-client-websockets = { group = 'io.ktor', name = "ktor-client-websockets", version.ref = "ktor" }
|
ktor-client-websockets = { group = 'io.ktor', name = "ktor-client-websockets", version.ref = "ktor" }
|
||||||
|
ktor-client-negotiation = { group = 'io.ktor', name = 'ktor-client-content-negotiation', version.ref = "ktor" }
|
||||||
ktor-server-core = { group = 'io.ktor', name = "ktor-server-core", version.ref = "ktor" }
|
ktor-server-core = { group = 'io.ktor', name = "ktor-server-core", version.ref = "ktor" }
|
||||||
ktor-server-netty = { group = 'io.ktor', name = "ktor-server-netty", version.ref = "ktor" }
|
ktor-server-netty = { group = 'io.ktor', name = "ktor-server-netty", version.ref = "ktor" }
|
||||||
ktor-server-websockets = { group = 'io.ktor', name = "ktor-server-websockets", version.ref = "ktor" }
|
ktor-server-websockets = { group = 'io.ktor', name = "ktor-server-websockets", version.ref = "ktor" }
|
||||||
|
ktor-server-negotiation = { group = 'io.ktor', name = 'ktor-server-content-negotiation', version.ref = "ktor" }
|
||||||
|
|
||||||
turtle = { group = "com.lordcodes.turtle", name = "turtle", version.ref = "turtle" }
|
turtle = { group = "com.lordcodes.turtle", name = "turtle", version.ref = "turtle" }
|
||||||
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
|
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,11 @@ application {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.shared)
|
implementation(projects.shared)
|
||||||
implementation(libs.kotlinx.serialization.json)
|
|
||||||
implementation(libs.logback)
|
implementation(libs.logback)
|
||||||
|
implementation(libs.koin.ktor)
|
||||||
implementation(libs.ktor.server.core)
|
implementation(libs.ktor.server.core)
|
||||||
implementation(libs.ktor.server.netty)
|
implementation(libs.ktor.server.netty)
|
||||||
implementation(libs.ktor.server.websockets)
|
implementation(libs.ktor.server.websockets)
|
||||||
|
implementation(libs.ktor.server.negotiation)
|
||||||
|
implementation(libs.ktor.serialization.json)
|
||||||
}
|
}
|
||||||
38
server/src/main/kotlin/Module.kt
Normal file
38
server/src/main/kotlin/Module.kt
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import com.pixelized.server.lwa.model.character.CharacterSheetRepository
|
||||||
|
import com.pixelized.server.lwa.model.character.CharacterSheetStore
|
||||||
|
import org.koin.core.module.dsl.singleOf
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val serverModuleDependencies
|
||||||
|
get() = listOf(
|
||||||
|
parserDependencies,
|
||||||
|
factoryDependencies,
|
||||||
|
useCaseDependencies,
|
||||||
|
storeDependencies,
|
||||||
|
repositoryDependencies,
|
||||||
|
)
|
||||||
|
|
||||||
|
val storeDependencies
|
||||||
|
get() = module {
|
||||||
|
singleOf(::CharacterSheetStore)
|
||||||
|
}
|
||||||
|
|
||||||
|
val repositoryDependencies
|
||||||
|
get() = module {
|
||||||
|
singleOf(::CharacterSheetRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
val factoryDependencies
|
||||||
|
get() = module {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val parserDependencies
|
||||||
|
get() = module {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val useCaseDependencies
|
||||||
|
get() = module {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package com.pixelized.server.lwa
|
package com.pixelized.server.lwa
|
||||||
|
|
||||||
import com.pixelized.server.lwa.server.LocalServer
|
import com.pixelized.server.lwa.server.LocalServer
|
||||||
|
import com.pixelized.shared.lwa.sharedModuleDependencies
|
||||||
|
import org.koin.core.context.startKoin
|
||||||
|
import org.koin.java.KoinJavaComponent.inject
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
LocalServer().create().start()
|
LocalServer().create().start()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.pixelized.server.lwa.extention
|
package com.pixelized.server.lwa.extention
|
||||||
|
|
||||||
import com.pixelized.server.lwa.protocol.Message
|
import com.pixelized.shared.lwa.protocol.Message
|
||||||
import io.ktor.websocket.Frame
|
import io.ktor.websocket.Frame
|
||||||
import io.ktor.websocket.readText
|
import io.ktor.websocket.readText
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.pixelized.server.lwa.model.character
|
||||||
|
|
||||||
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||||
|
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.stateIn
|
||||||
|
|
||||||
|
class CharacterSheetRepository(
|
||||||
|
store: CharacterSheetStore,
|
||||||
|
) {
|
||||||
|
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
|
|
||||||
|
private val sheets = store.characterSheetFlow()
|
||||||
|
.stateIn(
|
||||||
|
scope = scope,
|
||||||
|
started = SharingStarted.Eagerly,
|
||||||
|
initialValue = emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun characterSheetFlow(): StateFlow<List<CharacterSheet>> {
|
||||||
|
return sheets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
package com.pixelized.server.lwa.model.character
|
||||||
|
|
||||||
|
import com.pixelized.shared.lwa.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 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 characterDirectory = File(characterStorePath()).also { it.mkdirs() }
|
||||||
|
private val flow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
|
||||||
|
|
||||||
|
init {
|
||||||
|
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
|
scope.launch {
|
||||||
|
flow.value = load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
@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")
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,25 @@
|
||||||
package com.pixelized.server.lwa.server
|
package com.pixelized.server.lwa.server
|
||||||
|
|
||||||
import com.pixelized.server.lwa.SERVER_PORT
|
|
||||||
import com.pixelized.server.lwa.extention.decodeFromFrame
|
import com.pixelized.server.lwa.extention.decodeFromFrame
|
||||||
import com.pixelized.server.lwa.extention.encodeToFrame
|
import com.pixelized.server.lwa.extention.encodeToFrame
|
||||||
import com.pixelized.server.lwa.protocol.Message
|
import com.pixelized.server.lwa.model.character.CharacterSheetRepository
|
||||||
|
import com.pixelized.shared.lwa.SERVER_PORT
|
||||||
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheetJsonFactory
|
||||||
|
import com.pixelized.shared.lwa.protocol.Message
|
||||||
|
import com.pixelized.shared.lwa.sharedModuleDependencies
|
||||||
|
import io.ktor.http.ContentType
|
||||||
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
import io.ktor.server.application.install
|
import io.ktor.server.application.install
|
||||||
import io.ktor.server.engine.EmbeddedServer
|
import io.ktor.server.engine.EmbeddedServer
|
||||||
import io.ktor.server.engine.embeddedServer
|
import io.ktor.server.engine.embeddedServer
|
||||||
import io.ktor.server.netty.Netty
|
import io.ktor.server.netty.Netty
|
||||||
import io.ktor.server.netty.NettyApplicationEngine
|
import io.ktor.server.netty.NettyApplicationEngine
|
||||||
|
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.response.respondText
|
||||||
|
import io.ktor.server.routing.get
|
||||||
import io.ktor.server.routing.routing
|
import io.ktor.server.routing.routing
|
||||||
import io.ktor.server.websocket.DefaultWebSocketServerSession
|
|
||||||
import io.ktor.server.websocket.WebSockets
|
import io.ktor.server.websocket.WebSockets
|
||||||
import io.ktor.server.websocket.pingPeriod
|
import io.ktor.server.websocket.pingPeriod
|
||||||
import io.ktor.server.websocket.timeout
|
import io.ktor.server.websocket.timeout
|
||||||
|
|
@ -21,6 +30,9 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.koin.ktor.ext.inject
|
||||||
|
import org.koin.ktor.plugin.Koin
|
||||||
|
import serverModuleDependencies
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
// https://ktor.io/docs/server-websockets.html#handle-multiple-session
|
// https://ktor.io/docs/server-websockets.html#handle-multiple-session
|
||||||
|
|
@ -28,33 +40,78 @@ typealias Server = EmbeddedServer<NettyApplicationEngine, NettyApplicationEngine
|
||||||
|
|
||||||
class LocalServer {
|
class LocalServer {
|
||||||
private var server: Server? = null
|
private var server: Server? = null
|
||||||
private val json = Json { explicitNulls = true }
|
|
||||||
private val outgoingMessageBuffer = MutableSharedFlow<Message>()
|
private val outgoingMessageBuffer = MutableSharedFlow<Message>()
|
||||||
|
|
||||||
fun create(): LocalServer {
|
fun create(
|
||||||
server = build {
|
port: Int = SERVER_PORT, // 16030
|
||||||
val job = launch {
|
): LocalServer {
|
||||||
// send local message to the clients
|
server = embeddedServer(
|
||||||
outgoingMessageBuffer.collect { message ->
|
factory = Netty,
|
||||||
send(json.encodeToFrame(message))
|
port = port,
|
||||||
|
module = {
|
||||||
|
install(Koin) {
|
||||||
|
modules(sharedModuleDependencies + serverModuleDependencies)
|
||||||
|
}
|
||||||
|
|
||||||
|
val json by inject<Json>()
|
||||||
|
install(ContentNegotiation) {
|
||||||
|
json(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
install(WebSockets) {
|
||||||
|
pingPeriod = 15.seconds
|
||||||
|
timeout = 15.seconds
|
||||||
|
maxFrameSize = Long.MAX_VALUE
|
||||||
|
masking = false
|
||||||
|
}
|
||||||
|
|
||||||
|
val repository by inject<CharacterSheetRepository>()
|
||||||
|
val factory by inject<CharacterSheetJsonFactory>()
|
||||||
|
routing {
|
||||||
|
get(
|
||||||
|
path = "/",
|
||||||
|
body = {
|
||||||
|
call.respondText(contentType = ContentType.Text.Html) {
|
||||||
|
"<a href=\"http://127.0.0.1:16030/characters\">characters</a>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
get(
|
||||||
|
path = "/characters",
|
||||||
|
body = {
|
||||||
|
val body = repository.characterSheetFlow().value.map(factory::convertToJson)
|
||||||
|
call.respond(body)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
webSocket(
|
||||||
|
path = "/ws",
|
||||||
|
handler = {
|
||||||
|
val job = launch {
|
||||||
|
// send local message to the clients
|
||||||
|
outgoingMessageBuffer.collect { message ->
|
||||||
|
send(json.encodeToFrame(message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runCatching {
|
||||||
|
// watching for clients incoming message
|
||||||
|
incoming.consumeEach { frame ->
|
||||||
|
if (frame is Frame.Text) {
|
||||||
|
val message = Json.decodeFromFrame(frame = frame)
|
||||||
|
println(message)
|
||||||
|
// broadcast to clients the message
|
||||||
|
outgoingMessageBuffer.emit(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.onFailure { exception ->
|
||||||
|
println("WebSocket exception: ${exception.localizedMessage}")
|
||||||
|
}.also {
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runCatching {
|
)
|
||||||
// watching for clients incoming message
|
|
||||||
incoming.consumeEach { frame ->
|
|
||||||
if (frame is Frame.Text) {
|
|
||||||
val message = Json.decodeFromFrame(frame = frame)
|
|
||||||
println(message)
|
|
||||||
// broadcast to clients the message
|
|
||||||
outgoingMessageBuffer.emit(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.onFailure { exception ->
|
|
||||||
println("WebSocket exception: ${exception.localizedMessage}")
|
|
||||||
}.also {
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,28 +127,4 @@ class LocalServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun build(
|
|
||||||
port: Int = SERVER_PORT,
|
|
||||||
handler: suspend DefaultWebSocketServerSession.() -> Unit,
|
|
||||||
): EmbeddedServer<NettyApplicationEngine, NettyApplicationEngine.Configuration> {
|
|
||||||
return embeddedServer(
|
|
||||||
factory = Netty,
|
|
||||||
port = port,
|
|
||||||
module = {
|
|
||||||
install(WebSockets) {
|
|
||||||
pingPeriod = 15.seconds
|
|
||||||
timeout = 15.seconds
|
|
||||||
maxFrameSize = Long.MAX_VALUE
|
|
||||||
masking = false
|
|
||||||
}
|
|
||||||
routing {
|
|
||||||
webSocket(
|
|
||||||
path = "/ws",
|
|
||||||
handler = handler,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ kotlin {
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
|
implementation(libs.koin.core)
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
package com.pixelized.server.lwa
|
|
||||||
|
|
||||||
const val SERVER_PORT = 16030
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package com.pixelized.server.lwa.protocol
|
|
||||||
|
|
||||||
enum class MessageType {
|
|
||||||
Roll
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package com.pixelized.shared.lwa
|
||||||
|
|
||||||
|
const val SERVER_PORT = 16030
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.pixelized.shared.lwa
|
||||||
|
|
||||||
|
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheetJsonFactory
|
||||||
|
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.koin.core.module.dsl.factoryOf
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val sharedModuleDependencies
|
||||||
|
get() = listOf(
|
||||||
|
toolsDependencies,
|
||||||
|
factoryDependencies,
|
||||||
|
useCaseDependencies,
|
||||||
|
)
|
||||||
|
|
||||||
|
val toolsDependencies
|
||||||
|
get() = module {
|
||||||
|
factory {
|
||||||
|
Json {
|
||||||
|
explicitNulls = false
|
||||||
|
prettyPrint = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val factoryDependencies
|
||||||
|
get() = module {
|
||||||
|
factoryOf(::CharacterSheetJsonFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
val useCaseDependencies
|
||||||
|
get() = module {
|
||||||
|
factoryOf(::CharacterSheetUseCase)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.desktop.lwa.repository
|
package com.pixelized.shared.lwa
|
||||||
|
|
||||||
enum class OperatingSystem(
|
enum class OperatingSystem(
|
||||||
val home: String = System.getProperty("user.home"),
|
val home: String = System.getProperty("user.home"),
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.pixelized.shared.lwa.model.campaign
|
||||||
|
|
||||||
|
import com.pixelized.shared.lwa.model.campaign.model.CampaignFactory
|
||||||
|
|
||||||
|
class CampaignRepository(
|
||||||
|
private val factory: CampaignFactory,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.pixelized.shared.lwa.model.campaign.model
|
||||||
|
|
||||||
|
data class Campaign(
|
||||||
|
val characters: List<CharacterInstance>,
|
||||||
|
) {
|
||||||
|
data class CharacterInstance(
|
||||||
|
val damage: Int,
|
||||||
|
val usedPower: Int,
|
||||||
|
val usedMovement: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.pixelized.shared.lwa.model.campaign.model
|
||||||
|
|
||||||
|
class CampaignFactory {
|
||||||
|
fun convertFromJson(
|
||||||
|
json: CampaignJson,
|
||||||
|
): Campaign {
|
||||||
|
return when (json) {
|
||||||
|
is CampaignJsonV1 -> convertFromV1(json = json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertFromV1(
|
||||||
|
json: CampaignJsonV1,
|
||||||
|
): Campaign {
|
||||||
|
return Campaign(
|
||||||
|
characters = json.characters.map {
|
||||||
|
Campaign.CharacterInstance(
|
||||||
|
damage = it.damage,
|
||||||
|
usedPower = it.usedPower,
|
||||||
|
usedMovement = it.usedMovement,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertToJson(
|
||||||
|
data: Campaign,
|
||||||
|
): CampaignJson {
|
||||||
|
return CampaignJsonV1(
|
||||||
|
characters = data.characters.map {
|
||||||
|
CampaignJsonV1.CharacterInstanceJson(
|
||||||
|
damage = it.damage,
|
||||||
|
usedPower = it.usedPower,
|
||||||
|
usedMovement = it.usedMovement,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.pixelized.shared.lwa.model.campaign.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed interface CampaignJson
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.pixelized.shared.lwa.model.campaign.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CampaignJsonV1(
|
||||||
|
val characters: List<CharacterInstanceJson>,
|
||||||
|
) : CampaignJson {
|
||||||
|
@Serializable
|
||||||
|
data class CharacterInstanceJson(
|
||||||
|
val damage: Int,
|
||||||
|
val usedPower: Int,
|
||||||
|
val usedMovement: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.desktop.lwa.repository.characterSheet.model
|
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||||
|
|
||||||
data class CharacterSheet(
|
data class CharacterSheet(
|
||||||
val id: String,
|
val id: String,
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.desktop.lwa.repository.characterSheet.model
|
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
@ -1,14 +1,101 @@
|
||||||
package com.pixelized.desktop.lwa.repository.characterSheet
|
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||||
|
|
||||||
|
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||||
|
|
||||||
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(
|
class CharacterSheetJsonFactory(
|
||||||
private val skillDescriptionFactory: SkillDescriptionFactory,
|
|
||||||
private val characterSheetUseCase: CharacterSheetUseCase,
|
private val characterSheetUseCase: CharacterSheetUseCase,
|
||||||
) {
|
) {
|
||||||
|
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 = it.description,
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun convertToJson(
|
fun convertToJson(
|
||||||
sheet: CharacterSheet,
|
sheet: CharacterSheet,
|
||||||
|
|
@ -80,95 +167,4 @@ class CharacterSheetJsonFactory(
|
||||||
)
|
)
|
||||||
return json
|
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,4 +1,4 @@
|
||||||
package com.pixelized.desktop.lwa.repository.characterSheet.model
|
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.server.lwa.protocol
|
package com.pixelized.shared.lwa.protocol
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.pixelized.shared.lwa.protocol
|
||||||
|
|
||||||
|
enum class MessageType {
|
||||||
|
Roll
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.server.lwa.protocol.roll
|
package com.pixelized.shared.lwa.protocol.roll
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.desktop.lwa.business
|
package com.pixelized.shared.lwa.usecase
|
||||||
|
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
Loading…
Add table
Add a link
Reference in a new issue