Add characterSheet and Campaing to the server.
This commit is contained in:
parent
1e5f0d88ae
commit
495768e5fe
53 changed files with 879 additions and 513 deletions
|
|
@ -9,6 +9,8 @@ import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
|
|||
import com.pixelized.desktop.lwa.parser.word.WordParser
|
||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationStore
|
||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignStore
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetStore
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||
|
|
@ -29,8 +31,7 @@ import com.pixelized.desktop.lwa.ui.screen.network.NetworkFactory
|
|||
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
|
||||
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
|
||||
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
|
||||
import com.pixelized.shared.lwa.model.campaign.CampaignRepository
|
||||
import com.pixelized.shared.lwa.model.campaign.model.CampaignFactory
|
||||
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
|
||||
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.HttpClientEngine
|
||||
|
|
@ -78,6 +79,7 @@ val storeDependencies
|
|||
singleOf(::CharacterSheetStore)
|
||||
singleOf(::SettingsStore)
|
||||
singleOf(::AlterationStore)
|
||||
singleOf(::CampaignStore)
|
||||
}
|
||||
|
||||
val repositoryDependencies
|
||||
|
|
@ -97,7 +99,7 @@ val factoryDependencies
|
|||
factoryOf(::NetworkFactory)
|
||||
factoryOf(::SkillFieldFactory)
|
||||
factoryOf(::SettingsFactory)
|
||||
factoryOf(::CampaignFactory)
|
||||
factoryOf(::CampaignJsonFactory)
|
||||
}
|
||||
|
||||
val viewModelDependencies
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package com.pixelized.desktop.lwa.business
|
|||
import com.pixelized.desktop.lwa.parser.expression.Expression
|
||||
import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
|
||||
import com.pixelized.desktop.lwa.parser.word.Word
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
|
|
@ -32,9 +32,7 @@ class ExpressionUseCase(
|
|||
val bonus = context.evaluate(
|
||||
expression = skill.bonus?.let(expressionParser::parse),
|
||||
)
|
||||
val level = context.evaluate(
|
||||
expression = skill.level?.let(expressionParser::parse),
|
||||
)
|
||||
val level = max((skill.level - 1) * 5, 0)
|
||||
|
||||
return max(base + bonus + level + alterations, 0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,24 +4,24 @@ import com.pixelized.desktop.lwa.parser.expression.Expression
|
|||
import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
|
||||
import com.pixelized.desktop.lwa.repository.alteration.model.Alteration
|
||||
import com.pixelized.desktop.lwa.repository.alteration.model.AlterationMetadata
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.ARMOR
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.DEX
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.HEI
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.MOV
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId.STR
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.ACROBATICS_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.AID_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.ATHLETICS_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.BARGAIN_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.COMBAT_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.DISCRETION_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.INTIMIDATION_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.PERCEPTION_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.PERSUASION_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.SPIEL_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CommonSkillId.THROW_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.ARMOR
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.DEX
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.HEI
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.MOV
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.STR
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.ACROBATICS_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.AID_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.ATHLETICS_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.BARGAIN_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.COMBAT_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.DISCRETION_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.INTIMIDATION_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.PERCEPTION_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.PERSUASION_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.SPIEL_ID
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CommonSkillId.THROW_ID
|
||||
|
||||
class AlterationStore(
|
||||
private val expressionParser: ExpressionParser,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
package com.pixelized.desktop.lwa.repository.campaign
|
||||
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class CampaignRepository(
|
||||
store: CampaignStore,
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
|
||||
private val campaign = store.campaignFlow()
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = Campaign.EMPTY,
|
||||
)
|
||||
|
||||
fun campaignFlow(): StateFlow<Campaign> = campaign
|
||||
|
||||
fun characterInstance(id: String): StateFlow<Campaign.CharacterInstance> {
|
||||
return campaign
|
||||
.mapNotNull {
|
||||
it.characters[id]
|
||||
}
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = campaign.value.characters[id] ?: Campaign.CharacterInstance(
|
||||
characteristic = emptyMap(),
|
||||
usedSkill = emptyList(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package com.pixelized.desktop.lwa.repository.campaign
|
||||
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import com.pixelized.shared.lwa.model.campaign.CampaignJson
|
||||
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
|
||||
import com.pixelized.shared.lwa.protocol.MessageType
|
||||
import com.pixelized.shared.lwa.protocol.payload.UpdatePlayerCharacteristicMessage
|
||||
import com.pixelized.shared.lwa.usecase.CampaignUseCase
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.get
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class CampaignStore(
|
||||
private val network: NetworkRepository,
|
||||
private val factory: CampaignJsonFactory,
|
||||
private val useCase: CampaignUseCase,
|
||||
private val client: HttpClient,
|
||||
private val json: Json,
|
||||
) {
|
||||
private val flow = MutableStateFlow(value = Campaign.EMPTY)
|
||||
|
||||
init {
|
||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
scope.launch {
|
||||
flow.value = load()
|
||||
}
|
||||
scope.launch {
|
||||
network.data
|
||||
.mapNotNull { it.takeIf { it.type == MessageType.UpdatePlayerCharacteristic } }
|
||||
.map { json.decodeFromString<UpdatePlayerCharacteristicMessage>(it.value) }
|
||||
.collect {
|
||||
updateCharacteristic(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun campaignFlow(): StateFlow<Campaign> = flow
|
||||
|
||||
private suspend fun load(): Campaign {
|
||||
val request: CampaignJson = client
|
||||
.get("http://pixelized.freeboxos.fr:16030/campaign") // TODO
|
||||
.body()
|
||||
val data = factory.convertFromJson(json = request)
|
||||
return data
|
||||
}
|
||||
|
||||
private fun updateCharacteristic(
|
||||
message: UpdatePlayerCharacteristicMessage,
|
||||
) {
|
||||
val characters = flow.value.characters.toMutableMap()
|
||||
val character = characters[message.characterId] ?: Campaign.CharacterInstance(
|
||||
characteristic = emptyMap(),
|
||||
usedSkill = emptyList(),
|
||||
)
|
||||
characters[message.characterId] = useCase.updateCharacteristic(
|
||||
character = character,
|
||||
characteristic = message.characteristic,
|
||||
value = message.value
|
||||
)
|
||||
flow.value = flow.value.copy(characters = characters)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.pixelized.desktop.lwa.repository.characterSheet
|
||||
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
|
|
|||
|
|
@ -1,24 +1,31 @@
|
|||
package com.pixelized.desktop.lwa.repository.characterSheet
|
||||
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheetJson
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheetJsonFactory
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJson
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJsonFactory
|
||||
import com.pixelized.shared.lwa.protocol.MessageType
|
||||
import com.pixelized.shared.lwa.protocol.payload.UpdateSkillUsageMessage
|
||||
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class CharacterSheetStore(
|
||||
private val network: NetworkRepository,
|
||||
private val factory: CharacterSheetJsonFactory,
|
||||
private val useCase: CharacterSheetUseCase,
|
||||
private val client: HttpClient,
|
||||
private val json: Json,
|
||||
) {
|
||||
private val flow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
|
||||
|
||||
|
|
@ -27,21 +34,24 @@ class CharacterSheetStore(
|
|||
scope.launch {
|
||||
flow.value = load()
|
||||
}
|
||||
scope.launch {
|
||||
network.data
|
||||
.mapNotNull { it.takeIf { it.type == MessageType.UpdateSkillUsage } }
|
||||
.map { json.decodeFromString<UpdateSkillUsageMessage>(it.value) }
|
||||
.collect {
|
||||
updateCharacterSkillChange(
|
||||
characterId = it.characterId,
|
||||
skillId = it.skillId,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun characterSheetFlow(): StateFlow<List<CharacterSheet>> = flow
|
||||
|
||||
fun save(sheet: CharacterSheet) {
|
||||
|
||||
}
|
||||
|
||||
fun delete(id: String): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
suspend fun load(): List<CharacterSheet> {
|
||||
val request: List<CharacterSheetJson> = client
|
||||
.get("http://pixelized.freeboxos.fr:16030/characters")
|
||||
.get("http://pixelized.freeboxos.fr:16030/characters") // TODO
|
||||
.body()
|
||||
val data = request.map {
|
||||
factory.convertFromJson(json = it)
|
||||
|
|
@ -49,8 +59,19 @@ class CharacterSheetStore(
|
|||
return data
|
||||
}
|
||||
|
||||
sealed class CharacterSheetStoreException(root: Exception) : Exception(root)
|
||||
class JsonConversionException(root: Exception) : CharacterSheetStoreException(root)
|
||||
class FileWriteException(root: Exception) : CharacterSheetStoreException(root)
|
||||
class FileReadException(root: Exception) : CharacterSheetStoreException(root)
|
||||
private fun updateCharacterSkillChange(
|
||||
characterId: String,
|
||||
skillId: String,
|
||||
) {
|
||||
val characters = flow.value.toMutableList()
|
||||
val index = characters.indexOfFirst { it.id == characterId }
|
||||
|
||||
if (index > -1) {
|
||||
characters[index] = useCase.updateSkillUsage(
|
||||
character = characters[index],
|
||||
skillId = skillId,
|
||||
)
|
||||
flow.value = characters
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,10 @@ import com.pixelized.desktop.lwa.utils.extention.encodeToFrame
|
|||
import com.pixelized.shared.lwa.SERVER_PORT
|
||||
import com.pixelized.shared.lwa.protocol.Message
|
||||
import com.pixelized.shared.lwa.protocol.MessageType
|
||||
import com.pixelized.shared.lwa.protocol.payload.MessagePayload
|
||||
import com.pixelized.shared.lwa.protocol.payload.RollMessage
|
||||
import com.pixelized.shared.lwa.protocol.payload.UpdatePlayerCharacteristicMessage
|
||||
import com.pixelized.shared.lwa.protocol.payload.UpdateSkillUsageMessage
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.websocket.Frame
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -19,11 +23,13 @@ import kotlinx.coroutines.flow.SharedFlow
|
|||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class NetworkRepository(
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val client: HttpClient,
|
||||
private val json: Json,
|
||||
) {
|
||||
companion object {
|
||||
const val DEFAULT_PORT = SERVER_PORT
|
||||
|
|
@ -88,6 +94,33 @@ class NetworkRepository(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun share(
|
||||
playerName: String = settingsRepository.settings().playerName,
|
||||
payload: MessagePayload,
|
||||
) {
|
||||
if (status.value == Status.CONNECTED) {
|
||||
when (payload) {
|
||||
is RollMessage -> share(
|
||||
playerName = playerName,
|
||||
type = MessageType.Roll,
|
||||
content = json.encodeToString(payload),
|
||||
)
|
||||
|
||||
is UpdateSkillUsageMessage -> share(
|
||||
playerName = playerName,
|
||||
type = MessageType.UpdateSkillUsage,
|
||||
content = json.encodeToString(payload),
|
||||
)
|
||||
|
||||
is UpdatePlayerCharacteristicMessage -> share(
|
||||
playerName = playerName,
|
||||
type = MessageType.UpdatePlayerCharacteristic,
|
||||
content = json.encodeToString(payload),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun share(
|
||||
playerName: String = settingsRepository.settings().playerName,
|
||||
type: MessageType,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.pixelized.desktop.lwa.repository.roll_history
|
|||
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||
import com.pixelized.shared.lwa.protocol.MessageType
|
||||
import com.pixelized.shared.lwa.protocol.roll.RollMessage
|
||||
import com.pixelized.shared.lwa.protocol.payload.RollMessage
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
|
@ -28,14 +28,6 @@ class RollHistoryRepository(
|
|||
started = SharingStarted.Eagerly,
|
||||
)
|
||||
|
||||
init {
|
||||
scope.launch {
|
||||
network.data.collect {
|
||||
println(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun share(
|
||||
characterId: String,
|
||||
skillLabel: String,
|
||||
|
|
|
|||
|
|
@ -51,13 +51,13 @@ data class CharacterDetailUio(
|
|||
val name: String,
|
||||
val hp: String,
|
||||
val pp: String,
|
||||
val mov: String,
|
||||
)
|
||||
|
||||
@Stable
|
||||
data class CharacterDynDetailUio(
|
||||
val hp: String,
|
||||
val pp: String,
|
||||
val mov: String,
|
||||
)
|
||||
|
||||
@Composable
|
||||
|
|
@ -265,7 +265,7 @@ private fun CharacterHeader(
|
|||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.h6,
|
||||
text = dynDetail.value.mov,
|
||||
text = character.mov,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ import androidx.compose.runtime.remember
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance.Characteristic.Damage
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance.Characteristic.Power
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -17,24 +20,27 @@ import kotlinx.coroutines.flow.mapNotNull
|
|||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class CharacterDetailViewModel(
|
||||
private val repository: CharacterSheetRepository,
|
||||
private val alteration: AlterationRepository,
|
||||
private val characterRepository: CharacterSheetRepository,
|
||||
private val campaignRepository: CampaignRepository,
|
||||
private val alterationRepository: AlterationRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
private val displayedCharacterId = MutableStateFlow<String?>(null)
|
||||
|
||||
val detail: StateFlow<CharacterDetailUio?> = combine(
|
||||
displayedCharacterId,
|
||||
repository.characterSheetFlow(),
|
||||
) { id, sheets ->
|
||||
characterRepository.characterSheetFlow(),
|
||||
campaignRepository.campaignFlow(),
|
||||
) { id, sheets, campaign ->
|
||||
val sheet = sheets.firstOrNull { it.id == id }
|
||||
if (sheet == null) return@combine null
|
||||
CharacterDetailUio(
|
||||
id = sheet.id,
|
||||
portrait = sheet.portrait,
|
||||
name = sheet.name,
|
||||
hp = "${sheet.maxHp}",
|
||||
pp = "${sheet.maxPp}",
|
||||
hp = "${sheet.hp - (campaign.characters[id]?.characteristic?.get(Damage) ?: 0)}",
|
||||
pp = "${sheet.pp - (campaign.characters[id]?.characteristic?.get(Power) ?: 0)}",
|
||||
mov = "${sheet.movement}"
|
||||
)
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
|
|
@ -46,22 +52,19 @@ class CharacterDetailViewModel(
|
|||
@Stable
|
||||
fun collectDynamicDetailAsState(id: String): State<CharacterDynDetailUio> {
|
||||
val flow = remember(id) {
|
||||
repository.characterSheetFlow(id = id)
|
||||
campaignRepository.characterInstance(id = id)
|
||||
}
|
||||
return remember(id) {
|
||||
flow.mapNotNull { sheet ->
|
||||
if (sheet == null) return@mapNotNull null
|
||||
CharacterDynDetailUio(
|
||||
hp = sheet.currentHp.toString(),
|
||||
pp = sheet.currentPp.toString(),
|
||||
mov = sheet.movement.toString(),
|
||||
hp = sheet.characteristic[Damage].toString(),
|
||||
pp = sheet.characteristic[Power].toString(),
|
||||
)
|
||||
}
|
||||
}.collectAsState(
|
||||
initial = CharacterDynDetailUio(
|
||||
hp = flow.value?.maxHp?.toString() ?: "",
|
||||
pp = flow.value?.maxPp?.toString() ?: "",
|
||||
mov = flow.value?.movement?.toString() ?: "",
|
||||
CharacterDynDetailUio(
|
||||
hp = flow.value.characteristic[Damage].toString(),
|
||||
pp = flow.value.characteristic[Power].toString(),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ import org.jetbrains.compose.resources.painterResource
|
|||
data class PlayerPortraitUio(
|
||||
val id: String,
|
||||
val portrait: String?,
|
||||
val hp: Int,
|
||||
val damage: Int,
|
||||
val maxHp: Int,
|
||||
val pp: Int,
|
||||
val usedPp: Int,
|
||||
val maxPp: Int,
|
||||
)
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ fun PlayerPortrait(
|
|||
Text(
|
||||
modifier = Modifier.padding(bottom = 2.dp),
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = "${character.hp}/${character.maxHp}",
|
||||
text = "${character.maxHp - character.damage}/${character.maxHp}",
|
||||
)
|
||||
}
|
||||
Row(
|
||||
|
|
@ -111,7 +111,7 @@ fun PlayerPortrait(
|
|||
Text(
|
||||
modifier = Modifier.padding(bottom = 2.dp),
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = "${character.pp}/${character.maxPp}",
|
||||
text = "${character.maxPp - character.usedPp}/${character.maxPp}",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,34 +8,42 @@ import androidx.compose.runtime.State
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||
import com.pixelized.shared.lwa.model.campaign.character
|
||||
import com.pixelized.shared.lwa.model.campaign.damage
|
||||
import com.pixelized.shared.lwa.model.campaign.power
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class PlayerRibbonViewModel(
|
||||
private val rollHistoryRepository: RollHistoryRepository,
|
||||
characterRepository: CharacterSheetRepository,
|
||||
campaignRepository: CampaignRepository,
|
||||
) : ViewModel() {
|
||||
val characters: StateFlow<List<PlayerPortraitUio>> = characterRepository.characterSheetFlow()
|
||||
.map { sheets ->
|
||||
sheets.map { sheet ->
|
||||
PlayerPortraitUio(
|
||||
id = sheet.id,
|
||||
portrait = sheet.thumbnail,
|
||||
hp = sheet.currentHp,
|
||||
maxHp = sheet.maxHp,
|
||||
pp = sheet.currentPp,
|
||||
maxPp = sheet.maxPp,
|
||||
)
|
||||
}
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = emptyList()
|
||||
)
|
||||
val characters: StateFlow<List<PlayerPortraitUio>> = combine(
|
||||
characterRepository.characterSheetFlow(),
|
||||
campaignRepository.campaignFlow(),
|
||||
) { sheets, campaign ->
|
||||
sheets.map { sheet ->
|
||||
val instance = campaign.character(id = sheet.id)
|
||||
PlayerPortraitUio(
|
||||
id = sheet.id,
|
||||
portrait = sheet.thumbnail,
|
||||
damage = instance.damage,
|
||||
maxHp = sheet.hp,
|
||||
usedPp = instance.power,
|
||||
maxPp = sheet.pp,
|
||||
)
|
||||
}
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = emptyList()
|
||||
)
|
||||
|
||||
private val rolls = hashMapOf<String, MutableState<PlayerPortraitRollUio?>>()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.characterSheet.detail
|
||||
|
||||
import com.pixelized.desktop.lwa.business.ExpressionUseCase
|
||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
|
||||
import com.pixelized.desktop.lwa.repository.alteration.model.FieldAlteration
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId
|
||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Node
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import com.pixelized.shared.lwa.model.campaign.character
|
||||
import com.pixelized.shared.lwa.model.campaign.damage
|
||||
import com.pixelized.shared.lwa.model.campaign.power
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__cha
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__con
|
||||
|
|
@ -45,7 +49,7 @@ class CharacterSheetFactory(
|
|||
|
||||
suspend fun convertToUio(
|
||||
sheet: CharacterSheet?,
|
||||
diminished: Int,
|
||||
campaign: Campaign,
|
||||
alterations: Map<String, List<FieldAlteration>>,
|
||||
): CharacterSheetPageUio? {
|
||||
if (sheet == null) return null
|
||||
|
|
@ -56,6 +60,10 @@ class CharacterSheetFactory(
|
|||
} ?: 0
|
||||
}
|
||||
|
||||
val maxHp = sheet.hp + alterations[CharacteristicId.HP].sum()
|
||||
val maxPp = sheet.pp + alterations[CharacteristicId.PP].sum()
|
||||
val instance = campaign.character(sheet.id)
|
||||
|
||||
return CharacterSheetPageUio(
|
||||
id = sheet.id,
|
||||
name = sheet.name,
|
||||
|
|
@ -145,7 +153,7 @@ class CharacterSheetFactory(
|
|||
Characteristic(
|
||||
id = CharacteristicId.HP,
|
||||
label = getString(Res.string.character_sheet__sub_characteristics__hit_point),
|
||||
value = "${sheet.currentHp}/${sheet.maxHp + alterations[CharacteristicId.HP].sum()}",
|
||||
value = "${maxHp - instance.damage}/${maxHp}",
|
||||
tooltips = TooltipUio(
|
||||
title = getString(Res.string.character_sheet__sub_characteristics__hit_point),
|
||||
description = getString(Res.string.tooltip__sub_characteristics__hit_point),
|
||||
|
|
@ -155,7 +163,7 @@ class CharacterSheetFactory(
|
|||
Characteristic(
|
||||
id = CharacteristicId.PP,
|
||||
label = getString(Res.string.character_sheet__sub_characteristics__power_point),
|
||||
value = "${sheet.currentPp}/${sheet.maxPp + alterations[CharacteristicId.PP].sum()}",
|
||||
value = "${maxPp - instance.power}/${maxPp}",
|
||||
tooltips = TooltipUio(
|
||||
title = getString(Res.string.character_sheet__sub_characteristics__power_point),
|
||||
description = getString(Res.string.tooltip__sub_characteristics__power_point),
|
||||
|
|
@ -205,6 +213,7 @@ class CharacterSheetFactory(
|
|||
),
|
||||
commonSkills = sheet.commonSkills.map { skill ->
|
||||
Node(
|
||||
id = skill.id,
|
||||
label = skill.label,
|
||||
value = skillUseCase.computeSkillValue(
|
||||
sheet = sheet,
|
||||
|
|
@ -222,6 +231,7 @@ class CharacterSheetFactory(
|
|||
},
|
||||
specialSKills = sheet.specialSkills.map { skill ->
|
||||
Node(
|
||||
id = skill.id,
|
||||
label = skill.label,
|
||||
tooltips = skill.description?.takeIf { it.isNotBlank() }?.let { description ->
|
||||
TooltipUio(
|
||||
|
|
@ -239,6 +249,7 @@ class CharacterSheetFactory(
|
|||
},
|
||||
magicsSkills = sheet.magicSkills.map { skill ->
|
||||
Node(
|
||||
id = skill.id,
|
||||
label = skill.label,
|
||||
tooltips = skill.description?.takeIf { it.isNotBlank() }?.let { description ->
|
||||
TooltipUio(
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ data class CharacterSheetPageUio(
|
|||
|
||||
@Stable
|
||||
data class Node(
|
||||
val id: String,
|
||||
val label: String,
|
||||
val value: Int,
|
||||
val tooltips: TooltipUio? = null,
|
||||
|
|
|
|||
|
|
@ -9,27 +9,40 @@ import androidx.compose.ui.text.input.TextFieldValue
|
|||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetDestination
|
||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetDestination
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.CharacterSheetDeleteConfirmationDialogUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialogUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.StatChangeDialogUio
|
||||
import com.pixelized.desktop.lwa.utils.extention.collectAsState
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance.Characteristic
|
||||
import com.pixelized.shared.lwa.model.campaign.damage
|
||||
import com.pixelized.shared.lwa.model.campaign.power
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import com.pixelized.shared.lwa.protocol.payload.UpdatePlayerCharacteristicMessage
|
||||
|
||||
import com.pixelized.shared.lwa.protocol.payload.UpdateSkillUsageMessage
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__diminished__label
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__hit_point
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__power_point
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
private typealias CSDCDialogUio = CharacterSheetDeleteConfirmationDialogUio
|
||||
|
||||
class CharacterSheetViewModel(
|
||||
private val repository: CharacterSheetRepository,
|
||||
private val characterRepository: CharacterSheetRepository,
|
||||
private val campaignRepository: CampaignRepository,
|
||||
private val network: NetworkRepository,
|
||||
private val json: Json,
|
||||
private val alteration: AlterationRepository,
|
||||
private val factory: CharacterSheetFactory,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
|
|
@ -49,7 +62,7 @@ class CharacterSheetViewModel(
|
|||
private val _diminishedDialog = mutableStateOf<DiminishedStatDialogUio?>(null)
|
||||
val diminishedDialog: State<DiminishedStatDialogUio?> get() = _diminishedDialog
|
||||
|
||||
private val diminishedValueFlow = repository.characterDiminishedFlow(id = argument.id)
|
||||
private val diminishedValueFlow = characterRepository.characterDiminishedFlow(id = argument.id)
|
||||
val diminishedValue: State<Int?>
|
||||
@Composable
|
||||
get() = diminishedValueFlow.collectAsState { it ->
|
||||
|
|
@ -57,53 +70,46 @@ class CharacterSheetViewModel(
|
|||
}
|
||||
|
||||
private val sheetFlow = combine(
|
||||
repository.characterSheetFlow(id = argument.id),
|
||||
repository.characterDiminishedFlow(id = argument.id),
|
||||
characterRepository.characterSheetFlow(id = argument.id),
|
||||
campaignRepository.campaignFlow(),
|
||||
alteration.alterations(characterId = argument.id),
|
||||
transform = { sheet, diminished, alterations ->
|
||||
factory.convertToUio(sheet = sheet, diminished = diminished, alterations = alterations)
|
||||
transform = { sheet, campaign, alterations ->
|
||||
factory.convertToUio(
|
||||
sheet = sheet,
|
||||
campaign = campaign,
|
||||
alterations = alterations
|
||||
)
|
||||
},
|
||||
).stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = null,
|
||||
)
|
||||
val sheet: State<CharacterSheetPageUio?>
|
||||
@Composable
|
||||
get() = sheetFlow.collectAsState(
|
||||
initial = null,
|
||||
context = viewModelScope.coroutineContext,
|
||||
)
|
||||
get() = sheetFlow.collectAsState()
|
||||
|
||||
fun toggleWolf() {
|
||||
alteration.toggle(argument.id, "65e37d32-3031-4bf8-9369-d2c45d2efac0")
|
||||
}
|
||||
|
||||
fun deleteCharacter(id: String) {
|
||||
repository.delete(id = id)
|
||||
characterRepository.delete(id = id)
|
||||
}
|
||||
|
||||
fun onUseSkill(skill: CharacterSheetPageUio.Node) {
|
||||
repository.characterSheetFlow(id = argument.id).value?.let { sheet ->
|
||||
|
||||
val skills = sheet.commonSkills.map {
|
||||
if (it.label == skill.label) it.copy(used = it.used.not()) else it
|
||||
}
|
||||
val occupations = sheet.specialSkills.map {
|
||||
if (it.label == skill.label) it.copy(used = it.used.not()) else it
|
||||
}
|
||||
val magics = sheet.magicSkills.map {
|
||||
if (it.label == skill.label) it.copy(used = it.used.not()) else it
|
||||
}
|
||||
|
||||
repository.save(
|
||||
characterSheet = sheet.copy(
|
||||
commonSkills = skills,
|
||||
specialSkills = occupations,
|
||||
magicSkills = magics,
|
||||
viewModelScope.launch {
|
||||
network.share(
|
||||
payload = UpdateSkillUsageMessage(
|
||||
characterId = argument.id,
|
||||
skillId = skill.id,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun showConfirmCharacterDeletionDialog() {
|
||||
repository.characterSheetFlow(id = argument.id).value?.let { sheet ->
|
||||
characterRepository.characterSheetFlow(id = argument.id).value?.let { sheet ->
|
||||
_displayDeleteConfirmationDialog.value = CharacterSheetDeleteConfirmationDialogUio(
|
||||
id = sheet.id,
|
||||
name = sheet.name,
|
||||
|
|
@ -116,35 +122,36 @@ class CharacterSheetViewModel(
|
|||
}
|
||||
|
||||
suspend fun showSubCharacteristicDialog(id: String) {
|
||||
repository.characterSheetFlow(id = argument.id).value?.let { sheet ->
|
||||
characterRepository.characterSheetFlow(id = argument.id).value?.let { sheet ->
|
||||
val instance = campaignRepository.characterInstance(id = argument.id).value
|
||||
_statChangeDialog.value = when (id) {
|
||||
CharacterSheet.CharacteristicId.HP -> {
|
||||
val value = mutableStateOf(
|
||||
"${sheet.currentHp}".let {
|
||||
"${sheet.hp - instance.damage}".let {
|
||||
TextFieldValue(text = it, selection = TextRange(it.length))
|
||||
}
|
||||
)
|
||||
StatChangeDialogUio(
|
||||
id = CharacterSheet.CharacteristicId.HP,
|
||||
id = Characteristic.Damage,
|
||||
label = getString(resource = Res.string.character_sheet_edit__sub_characteristics__hit_point),
|
||||
value = { value.value },
|
||||
onValueChange = { value.value = it },
|
||||
maxValue = "${sheet.maxHp}",
|
||||
maxValue = "${sheet.hp}",
|
||||
)
|
||||
}
|
||||
|
||||
CharacterSheet.CharacteristicId.PP -> {
|
||||
val value = mutableStateOf(
|
||||
"${sheet.currentPp}".let {
|
||||
"${sheet.power - instance.power}".let {
|
||||
TextFieldValue(text = it, selection = TextRange(it.length))
|
||||
}
|
||||
)
|
||||
StatChangeDialogUio(
|
||||
id = CharacterSheet.CharacteristicId.PP,
|
||||
id = Characteristic.Power,
|
||||
label = getString(resource = Res.string.character_sheet_edit__sub_characteristics__power_point),
|
||||
value = { value.value },
|
||||
onValueChange = { value.value = it },
|
||||
maxValue = "${sheet.maxPp}",
|
||||
maxValue = "${sheet.power}",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -158,29 +165,24 @@ class CharacterSheetViewModel(
|
|||
}
|
||||
|
||||
fun changeSubCharacteristic(
|
||||
characteristicId: String,
|
||||
characteristicId: Characteristic,
|
||||
value: Int,
|
||||
) {
|
||||
val sheet = repository.characterSheetFlow(id = argument.id).value
|
||||
val updated = when (characteristicId) {
|
||||
CharacterSheet.CharacteristicId.HP -> sheet?.copy(
|
||||
currentHp = max(
|
||||
0,
|
||||
min(sheet.maxHp, value)
|
||||
viewModelScope.launch {
|
||||
val sheet = characterRepository.characterSheetFlow(id = argument.id).value
|
||||
if (sheet != null) {
|
||||
network.share(
|
||||
payload = UpdatePlayerCharacteristicMessage(
|
||||
characterId = argument.id,
|
||||
characteristic = characteristicId,
|
||||
value = when (characteristicId) {
|
||||
Characteristic.Damage -> sheet.hp - value
|
||||
Characteristic.Power -> sheet.pp - value
|
||||
else -> sheet.movement - value
|
||||
},
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
CharacterSheet.CharacteristicId.PP -> sheet?.copy(
|
||||
currentPp = max(
|
||||
0,
|
||||
min(sheet.maxPp, value)
|
||||
)
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
updated?.let {
|
||||
repository.save(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +195,7 @@ class CharacterSheetViewModel(
|
|||
}
|
||||
|
||||
suspend fun showDiminishedDialog() {
|
||||
val diminished = repository.characterDiminishedFlow(id = argument.id).value
|
||||
val diminished = characterRepository.characterDiminishedFlow(id = argument.id).value
|
||||
val textFieldValue =
|
||||
mutableStateOf(TextFieldValue("$diminished", selection = TextRange(index = 0)))
|
||||
_diminishedDialog.value = DiminishedStatDialogUio(
|
||||
|
|
@ -215,7 +217,7 @@ class CharacterSheetViewModel(
|
|||
|
||||
fun changeDiminished(dialog: DiminishedStatDialogUio) {
|
||||
val value = dialog.value().text.toIntOrNull() ?: 0
|
||||
repository.setDiminishedForCharacter(
|
||||
characterRepository.setDiminishedForCharacter(
|
||||
id = dialog.id,
|
||||
diminished = value,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import androidx.compose.ui.text.input.TextFieldValue
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
|
||||
import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
|
||||
|
|
@ -49,7 +50,7 @@ import org.jetbrains.compose.resources.stringResource
|
|||
|
||||
@Stable
|
||||
data class StatChangeDialogUio(
|
||||
val id: String,
|
||||
val id: Campaign.CharacterInstance.Characteristic,
|
||||
val label: String,
|
||||
val value: () -> TextFieldValue,
|
||||
val onValueChange: (TextFieldValue) -> Unit,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.preview
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet.CharacteristicId
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.occupation
|
|||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionFieldUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.BaseSkillFieldUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.SimpleFieldUio
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__action_label
|
||||
|
|
@ -41,13 +41,6 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sk
|
|||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__special_title
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__spiel
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__throw
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__armor
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__damage_bonus
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__hp_grow
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__learning
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__max_hit_point
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__max_power_point
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__movement
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import java.util.UUID
|
||||
import kotlin.math.max
|
||||
|
|
@ -60,56 +53,57 @@ class CharacterSheetEditFactory(
|
|||
currentSheet: CharacterSheet?,
|
||||
editedSheet: CharacterSheetEditPageUio,
|
||||
): CharacterSheet {
|
||||
val editedMaxHp = editedSheet.maxHp.unpack()?.toIntOrNull() ?: currentSheet?.maxHp ?: 0
|
||||
val editedMaxPp = editedSheet.maxPp.unpack()?.toIntOrNull() ?: currentSheet?.maxPp ?: 0
|
||||
|
||||
val level = currentSheet?.level ?: 1
|
||||
val strength = editedSheet.strength.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.strength
|
||||
?: 0
|
||||
val dexterity = editedSheet.dexterity.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.dexterity
|
||||
?: 0
|
||||
val constitution = editedSheet.constitution.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.constitution
|
||||
?: 0
|
||||
val height = editedSheet.height.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.height
|
||||
?: 0
|
||||
val intelligence = editedSheet.intelligence.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.intelligence
|
||||
?: 0
|
||||
val power = editedSheet.power.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.power
|
||||
?: 0
|
||||
val charisma = editedSheet.charisma.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.charisma
|
||||
?: 0
|
||||
|
||||
return CharacterSheet(
|
||||
id = editedSheet.id,
|
||||
name = editedSheet.name.value.value,
|
||||
portrait = currentSheet?.portrait,
|
||||
thumbnail = currentSheet?.thumbnail,
|
||||
strength = editedSheet.strength.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.strength
|
||||
?: 0,
|
||||
dexterity = editedSheet.dexterity.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.dexterity
|
||||
?: 0,
|
||||
constitution = editedSheet.constitution.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.constitution
|
||||
?: 0,
|
||||
height = editedSheet.height.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.height
|
||||
?: 0,
|
||||
intelligence = editedSheet.intelligence.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.intelligence
|
||||
?: 0,
|
||||
power = editedSheet.power.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.power
|
||||
?: 0,
|
||||
charisma = editedSheet.charisma.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.charisma
|
||||
?: 0,
|
||||
overrideMovement = editedSheet.movement.value.value.value.isNotBlank(),
|
||||
movement = editedSheet.movement.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.movement
|
||||
?: 10,
|
||||
overrideMaxHp = editedSheet.maxHp.value.value.value.isNotBlank(),
|
||||
maxHp = editedMaxHp,
|
||||
currentHp = currentSheet?.currentHp?.coerceAtMost(editedMaxHp) ?: editedMaxHp,
|
||||
overrideMaxPP = editedSheet.maxPp.value.value.value.isNotBlank(),
|
||||
maxPp = editedMaxPp,
|
||||
currentPp = currentSheet?.currentPp?.coerceAtMost(editedMaxPp) ?: editedMaxPp,
|
||||
overrideDamageBonus = editedSheet.damageBonus.value.value.value.isNotBlank(),
|
||||
damageBonus = editedSheet.damageBonus.unpack()
|
||||
?: currentSheet?.damageBonus
|
||||
?: "",
|
||||
overrideArmor = editedSheet.armor.value.value.value.isNotBlank(),
|
||||
armor = editedSheet.armor.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.armor
|
||||
?: 0,
|
||||
overrideLearning = editedSheet.learning.value.value.value.isNotBlank(),
|
||||
learning = editedSheet.learning.unpack()?.toIntOrNull() ?: 0,
|
||||
overrideHpGrow = editedSheet.hpGrow.value.value.value.isNotBlank(),
|
||||
hpGrow = editedSheet.hpGrow.unpack()?.toIntOrNull() ?: 0,
|
||||
level = level,
|
||||
strength = strength,
|
||||
dexterity = dexterity,
|
||||
constitution = constitution,
|
||||
height = height,
|
||||
intelligence = intelligence,
|
||||
power = power,
|
||||
charisma = charisma,
|
||||
hp = characterSheetUseCase.defaultMaxHp(
|
||||
constitution = constitution,
|
||||
height = height,
|
||||
level = level
|
||||
),
|
||||
pp = characterSheetUseCase.defaultMaxPower(power = power),
|
||||
movement = characterSheetUseCase.defaultMovement(),
|
||||
damageBonus = characterSheetUseCase.defaultDamageBonus(
|
||||
strength = strength,
|
||||
height = height
|
||||
),
|
||||
armor = characterSheetUseCase.defaultArmor(),
|
||||
learning = characterSheetUseCase.defaultLearning(intelligence = intelligence),
|
||||
hpGrow = characterSheetUseCase.defaultHpGrow(constitution = constitution),
|
||||
commonSkills = editedSheet.commonSkills.map { editedSkill ->
|
||||
val currentSkill = currentSheet?.commonSkills?.firstOrNull {
|
||||
it.id == editedSkill.id
|
||||
|
|
@ -120,7 +114,7 @@ class CharacterSheetEditFactory(
|
|||
description = currentSkill?.description,
|
||||
base = "${editedSkill.base.value}",
|
||||
bonus = editedSkill.bonus.value.value.takeIf { it.isNotBlank() },
|
||||
level = editedSkill.level.value.value.takeIf { it.isNotBlank() },
|
||||
level = editedSkill.level.value.value.toIntOrNull() ?: 0,
|
||||
occupation = editedSkill.option.checked.value,
|
||||
used = currentSkill?.used ?: false,
|
||||
)
|
||||
|
|
@ -135,7 +129,7 @@ class CharacterSheetEditFactory(
|
|||
description = editedSkill.description.value.value,
|
||||
base = editedSkill.base.value.value,
|
||||
bonus = editedSkill.bonus.value.value.takeIf { it.isNotBlank() },
|
||||
level = editedSkill.level.value.value.takeIf { it.isNotBlank() },
|
||||
level = editedSkill.level.value.value.toIntOrNull() ?: 0,
|
||||
occupation = editedSkill.options.occupation,
|
||||
used = currentSkill?.used ?: false,
|
||||
)
|
||||
|
|
@ -150,7 +144,7 @@ class CharacterSheetEditFactory(
|
|||
description = editedSkill.description.value.value,
|
||||
base = editedSkill.base.value.value,
|
||||
bonus = editedSkill.bonus.value.value.takeIf { it.isNotBlank() },
|
||||
level = editedSkill.level.value.value.takeIf { it.isNotBlank() },
|
||||
level = editedSkill.level.value.value.toIntOrNull() ?: 0,
|
||||
occupation = editedSkill.options.occupation,
|
||||
used = currentSkill?.used ?: false,
|
||||
)
|
||||
|
|
@ -159,6 +153,8 @@ class CharacterSheetEditFactory(
|
|||
CharacterSheet.Roll(
|
||||
id = it.id,
|
||||
label = it.label.value.value,
|
||||
description = null, // TODO
|
||||
canBeCritical = false, // TODO
|
||||
roll = it.action.value.value,
|
||||
)
|
||||
},
|
||||
|
|
@ -244,77 +240,6 @@ class CharacterSheetEditFactory(
|
|||
intelligence = int,
|
||||
power = pow,
|
||||
charisma = cha,
|
||||
movement = SimpleFieldUio(
|
||||
label = getString(Res.string.character_sheet_edit__sub_characteristics__movement),
|
||||
value = skillFieldFactory.createWrapper(
|
||||
value = if (sheet?.overrideMovement == true) "${sheet.movement}" else "",
|
||||
placeholder = derivedStateOf {
|
||||
"${characterSheetUseCase.defaultMovement()}"
|
||||
},
|
||||
)
|
||||
),
|
||||
maxHp = SimpleFieldUio(
|
||||
label = getString(Res.string.character_sheet_edit__sub_characteristics__max_hit_point),
|
||||
value = skillFieldFactory.createWrapper(
|
||||
value = if (sheet?.overrideMaxHp == true) "${sheet.maxHp}" else "",
|
||||
placeholder = derivedStateOf {
|
||||
"${
|
||||
characterSheetUseCase.defaultMaxHp(
|
||||
constitution = con(),
|
||||
height = hei()
|
||||
)
|
||||
}"
|
||||
},
|
||||
)
|
||||
),
|
||||
maxPp = SimpleFieldUio(
|
||||
label = getString(Res.string.character_sheet_edit__sub_characteristics__max_power_point),
|
||||
value = skillFieldFactory.createWrapper(
|
||||
value = if (sheet?.overrideMaxPP == true) "${sheet.maxPp}" else "",
|
||||
placeholder = derivedStateOf {
|
||||
"${characterSheetUseCase.defaultMaxPower(power = pow())}"
|
||||
},
|
||||
)
|
||||
),
|
||||
damageBonus = SimpleFieldUio(
|
||||
label = getString(Res.string.character_sheet_edit__sub_characteristics__damage_bonus),
|
||||
value = skillFieldFactory.createWrapper(
|
||||
value = if (sheet?.overrideDamageBonus == true) sheet.damageBonus else "",
|
||||
placeholder = derivedStateOf {
|
||||
characterSheetUseCase.defaultDamageBonus(
|
||||
strength = str(),
|
||||
height = hei()
|
||||
)
|
||||
},
|
||||
)
|
||||
),
|
||||
armor = SimpleFieldUio(
|
||||
label = getString(Res.string.character_sheet_edit__sub_characteristics__armor),
|
||||
value = skillFieldFactory.createWrapper(
|
||||
value = if (sheet?.overrideArmor == true) "${sheet.armor}" else "",
|
||||
placeholder = derivedStateOf {
|
||||
"${characterSheetUseCase.defaultArmor()}"
|
||||
},
|
||||
)
|
||||
),
|
||||
learning = SimpleFieldUio(
|
||||
label = getString(Res.string.character_sheet_edit__sub_characteristics__learning),
|
||||
value = skillFieldFactory.createWrapper(
|
||||
value = if (sheet?.overrideLearning == true) "${sheet.learning}" else "",
|
||||
placeholder = derivedStateOf {
|
||||
"${characterSheetUseCase.defaultLearning(intelligence = int())}"
|
||||
},
|
||||
)
|
||||
),
|
||||
hpGrow = SimpleFieldUio(
|
||||
label = getString(Res.string.character_sheet_edit__sub_characteristics__hp_grow),
|
||||
value = skillFieldFactory.createWrapper(
|
||||
value = if (sheet?.overrideHpGrow == true) "${sheet.hpGrow}" else "",
|
||||
placeholder = derivedStateOf {
|
||||
"${characterSheetUseCase.defaultHpGrow(constitution = con())}"
|
||||
},
|
||||
)
|
||||
),
|
||||
commonSkills = listOf(
|
||||
createBaseSkill(
|
||||
sheet = sheet,
|
||||
|
|
@ -421,7 +346,7 @@ class CharacterSheetEditFactory(
|
|||
labelValue = skill.label,
|
||||
baseValue = skill.base,
|
||||
bonusValue = skill.bonus ?: "",
|
||||
levelValue = skill.level ?: "",
|
||||
levelValue = skill.level.takeIf { it > 0 }?.toString() ?: "",
|
||||
options = run {
|
||||
val current = sheet.specialSkills.firstOrNull { it.id == skill.id }
|
||||
listOf(
|
||||
|
|
@ -439,7 +364,7 @@ class CharacterSheetEditFactory(
|
|||
labelValue = skill.label,
|
||||
baseValue = skill.base,
|
||||
bonusValue = skill.bonus ?: "",
|
||||
levelValue = skill.level ?: "",
|
||||
levelValue = skill.level.takeIf { it > 0 }?.toString() ?: "",
|
||||
options = run {
|
||||
val current = sheet.magicSkills.firstOrNull { it.id == skill.id }
|
||||
listOf(
|
||||
|
|
@ -487,7 +412,7 @@ class CharacterSheetEditFactory(
|
|||
),
|
||||
level = skillFieldFactory.createWrapper(
|
||||
label = mutableStateOf(getString(Res.string.character_sheet_edit__skills__level_label)),
|
||||
value = skill?.level ?: "",
|
||||
value = skill?.level?.takeIf { it > 0 }?.toString() ?: "",
|
||||
),
|
||||
option = skillFieldFactory.occupationOption(skill?.occupation ?: false),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -67,13 +67,6 @@ data class CharacterSheetEditPageUio(
|
|||
val intelligence: SimpleFieldUio,
|
||||
val power: SimpleFieldUio,
|
||||
val charisma: SimpleFieldUio,
|
||||
val movement: SimpleFieldUio,
|
||||
val maxHp: SimpleFieldUio,
|
||||
val maxPp: SimpleFieldUio,
|
||||
val damageBonus: SimpleFieldUio,
|
||||
val armor: SimpleFieldUio,
|
||||
val learning: SimpleFieldUio,
|
||||
val hpGrow: SimpleFieldUio,
|
||||
val commonSkills: List<BaseSkillFieldUio>,
|
||||
val specialSkills: List<SkillFieldUio>,
|
||||
val magicSkills: List<SkillFieldUio>,
|
||||
|
|
@ -89,17 +82,6 @@ data class CharacterSheetEditPageUio(
|
|||
power,
|
||||
charisma,
|
||||
)
|
||||
|
||||
val subCharacteristics
|
||||
get() = listOf(
|
||||
movement,
|
||||
maxHp,
|
||||
maxPp,
|
||||
damageBonus,
|
||||
armor,
|
||||
learning,
|
||||
hpGrow,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -215,27 +197,6 @@ fun CharacterSheetEdit(
|
|||
}
|
||||
}
|
||||
}
|
||||
DecoratedBox(
|
||||
modifier = Modifier.weight(weight = 1f),
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = stringResource(Res.string.character_sheet_edit__sub_characteristics__title),
|
||||
)
|
||||
form.subCharacteristics.forEach {
|
||||
SimpleField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
field = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecoratedBox(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetReposit
|
|||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio
|
||||
import com.pixelized.desktop.lwa.ui.screen.roll.DifficultyUio.Difficulty
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ koin = "4.0.0"
|
|||
turtle = "0.5.0"
|
||||
logback = "1.5.11"
|
||||
coil = "3.1.0"
|
||||
filament-android = "1.17.1"
|
||||
|
||||
[plugins]
|
||||
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
|
||||
|
|
@ -50,4 +51,5 @@ turtle = { group = "com.lordcodes.turtle", name = "turtle", version.ref = "turtl
|
|||
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
|
||||
|
||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||
coil-network = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||
coil-network = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||
filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filament-android" }
|
||||
|
|
@ -22,4 +22,5 @@ dependencies {
|
|||
implementation(libs.ktor.server.websockets)
|
||||
implementation(libs.ktor.server.negotiation)
|
||||
implementation(libs.ktor.serialization.json)
|
||||
implementation(libs.filament.android)
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
import com.pixelized.server.lwa.model.character.CharacterSheetRepository
|
||||
import com.pixelized.server.lwa.model.campaign.CampaignService
|
||||
import com.pixelized.server.lwa.model.campaign.CampaignStore
|
||||
import com.pixelized.server.lwa.model.character.CharacterSheetService
|
||||
import com.pixelized.server.lwa.model.character.CharacterSheetStore
|
||||
import com.pixelized.server.lwa.server.Engine
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
|
@ -9,17 +12,25 @@ val serverModuleDependencies
|
|||
factoryDependencies,
|
||||
useCaseDependencies,
|
||||
storeDependencies,
|
||||
repositoryDependencies,
|
||||
serviceDependencies,
|
||||
engineDependencies,
|
||||
)
|
||||
|
||||
val engineDependencies
|
||||
get() = module {
|
||||
singleOf(::Engine)
|
||||
}
|
||||
|
||||
val storeDependencies
|
||||
get() = module {
|
||||
singleOf(::CharacterSheetStore)
|
||||
singleOf(::CampaignStore)
|
||||
}
|
||||
|
||||
val repositoryDependencies
|
||||
val serviceDependencies
|
||||
get() = module {
|
||||
singleOf(::CharacterSheetRepository)
|
||||
singleOf(::CharacterSheetService)
|
||||
singleOf(::CampaignService)
|
||||
}
|
||||
|
||||
val factoryDependencies
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package com.pixelized.server.lwa.model.campaign
|
||||
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import com.pixelized.shared.lwa.model.campaign.CampaignJson
|
||||
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
|
||||
import com.pixelized.shared.lwa.protocol.payload.UpdatePlayerCharacteristicMessage
|
||||
import com.pixelized.shared.lwa.usecase.CampaignUseCase
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class CampaignService(
|
||||
private val store: CampaignStore,
|
||||
private val factory: CampaignJsonFactory,
|
||||
private val useCase: CampaignUseCase,
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
private val campaign = store.campaignFlow().stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = Campaign.EMPTY,
|
||||
)
|
||||
|
||||
fun campaign(): CampaignJson {
|
||||
return campaign.value.let(factory::convertToJson)
|
||||
}
|
||||
|
||||
suspend fun update(
|
||||
message: UpdatePlayerCharacteristicMessage,
|
||||
) {
|
||||
// fetch all the current campaign character
|
||||
val characters = campaign.value.characters.toMutableMap()
|
||||
// update the corresponding character using the usecase
|
||||
characters[message.characterId] = useCase.updateCharacteristic(
|
||||
character = characters[message.characterId] ?: Campaign.CharacterInstance(
|
||||
characteristic = emptyMap(),
|
||||
usedSkill = emptyList(),
|
||||
),
|
||||
characteristic = message.characteristic,
|
||||
value = message.value,
|
||||
)
|
||||
// save the campaign to the disk + update the flow.
|
||||
store.save(
|
||||
campaign = campaign.value.copy(characters = characters)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package com.pixelized.server.lwa.model.campaign
|
||||
|
||||
import com.pixelized.shared.lwa.campaignPath
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import com.pixelized.shared.lwa.model.campaign.CampaignJson
|
||||
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
|
||||
import 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
|
||||
|
||||
class CampaignStore(
|
||||
private val factory: CampaignJsonFactory,
|
||||
private val json: Json,
|
||||
) {
|
||||
private val directory = File(campaignPath()).also { it.mkdirs() }
|
||||
private val flow = MutableStateFlow(value = Campaign.EMPTY)
|
||||
|
||||
init {
|
||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
scope.launch {
|
||||
flow.value = load()
|
||||
}
|
||||
}
|
||||
|
||||
fun campaignFlow(): StateFlow<Campaign> = flow
|
||||
|
||||
suspend fun load(): Campaign {
|
||||
return try {
|
||||
val json = file().readText(charset = Charsets.UTF_8)
|
||||
if (json.isBlank()) Campaign.EMPTY
|
||||
val campaign = this.json.decodeFromString<CampaignJson>(json)
|
||||
factory.convertFromJson(campaign)
|
||||
} catch (exception: Exception) {
|
||||
Campaign.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun save(campaign: Campaign) {
|
||||
// convert the data to json format
|
||||
val json = try {
|
||||
factory.convertToJson(data = campaign).let(json::encodeToString)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
// write the file
|
||||
try {
|
||||
val file = file()
|
||||
file.writeText(
|
||||
text = json,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
flow.value = campaign
|
||||
}
|
||||
|
||||
sealed class CampaignStoreException(root: Exception) : Exception(root)
|
||||
class JsonConversionException(root: Exception) : CampaignStoreException(root)
|
||||
class FileWriteException(root: Exception) : CampaignStoreException(root)
|
||||
|
||||
private fun file(): File {
|
||||
return File("${campaignPath()}campaign.json")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
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,41 @@
|
|||
package com.pixelized.server.lwa.model.character
|
||||
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJson
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJsonFactory
|
||||
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class CharacterSheetService(
|
||||
private val store: CharacterSheetStore,
|
||||
private val factory: CharacterSheetJsonFactory,
|
||||
private val useCase: CharacterSheetUseCase,
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
private val sheets = store.characterSheetFlow().stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = emptyList()
|
||||
)
|
||||
|
||||
fun character(): List<CharacterSheetJson> {
|
||||
return sheets.value.map(factory::convertToJson)
|
||||
}
|
||||
|
||||
fun characterSkillChange(
|
||||
characterId: String,
|
||||
skillId: String,
|
||||
) {
|
||||
val character = sheets.value.firstOrNull { it.id == characterId }
|
||||
if (character != null) {
|
||||
val update = useCase.updateSkillUsage(
|
||||
character = character,
|
||||
skillId = skillId,
|
||||
)
|
||||
store.save(sheet = update)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
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 com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJson
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJsonFactory
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
|
@ -17,9 +17,9 @@ import java.text.Collator
|
|||
|
||||
class CharacterSheetStore(
|
||||
private val factory: CharacterSheetJsonFactory,
|
||||
private val jsonFormatter: Json,
|
||||
private val json: Json,
|
||||
) {
|
||||
private val characterDirectory = File(characterStorePath()).also { it.mkdirs() }
|
||||
private val directory = File(characterStorePath()).also { it.mkdirs() }
|
||||
private val flow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
|
||||
|
||||
init {
|
||||
|
|
@ -39,7 +39,7 @@ class CharacterSheetStore(
|
|||
fun save(sheet: CharacterSheet) {
|
||||
// convert the character sheet into json format.
|
||||
val json = try {
|
||||
factory.convertToJson(sheet = sheet).let(jsonFormatter::encodeToString)
|
||||
factory.convertToJson(sheet = sheet).let(json::encodeToString)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@ class CharacterSheetStore(
|
|||
JsonConversionException::class,
|
||||
)
|
||||
suspend fun load(): List<CharacterSheet> {
|
||||
return characterDirectory
|
||||
return directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
val json = try {
|
||||
|
|
@ -98,7 +98,7 @@ class CharacterSheetStore(
|
|||
return@mapNotNull null
|
||||
}
|
||||
try {
|
||||
val sheet = jsonFormatter.decodeFromString<CharacterSheetJson>(json)
|
||||
val sheet = this.json.decodeFromString<CharacterSheetJson>(json)
|
||||
factory.convertFromJson(sheet)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
package com.pixelized.server.lwa.server
|
||||
|
||||
import com.pixelized.server.lwa.model.campaign.CampaignService
|
||||
import com.pixelized.server.lwa.model.character.CharacterSheetService
|
||||
import com.pixelized.shared.lwa.protocol.Message
|
||||
import com.pixelized.shared.lwa.protocol.MessageType
|
||||
import com.pixelized.shared.lwa.protocol.payload.UpdatePlayerCharacteristicMessage
|
||||
import com.pixelized.shared.lwa.protocol.payload.UpdateSkillUsageMessage
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class Engine(
|
||||
private val characterService: CharacterSheetService,
|
||||
private val campaignService: CampaignService,
|
||||
private val json: Json,
|
||||
) {
|
||||
suspend fun handle(message: Message) {
|
||||
println(message)
|
||||
|
||||
when (message.type) {
|
||||
MessageType.Roll -> {
|
||||
Unit // Nothing to do here.
|
||||
}
|
||||
|
||||
MessageType.UpdateSkillUsage -> {
|
||||
val data: UpdateSkillUsageMessage = json.decodeFromString(message.value)
|
||||
characterService.characterSkillChange(
|
||||
characterId = data.characterId,
|
||||
skillId = data.skillId
|
||||
)
|
||||
}
|
||||
|
||||
MessageType.UpdatePlayerCharacteristic -> {
|
||||
val data: UpdatePlayerCharacteristicMessage = json.decodeFromString(message.value)
|
||||
campaignService.update(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,9 @@ package com.pixelized.server.lwa.server
|
|||
|
||||
import com.pixelized.server.lwa.extention.decodeFromFrame
|
||||
import com.pixelized.server.lwa.extention.encodeToFrame
|
||||
import com.pixelized.server.lwa.model.character.CharacterSheetRepository
|
||||
import com.pixelized.server.lwa.model.campaign.CampaignService
|
||||
import com.pixelized.server.lwa.model.character.CharacterSheetService
|
||||
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
|
||||
|
|
@ -65,24 +65,34 @@ class LocalServer {
|
|||
masking = false
|
||||
}
|
||||
|
||||
val repository by inject<CharacterSheetRepository>()
|
||||
val factory by inject<CharacterSheetJsonFactory>()
|
||||
val engine by inject<Engine>()
|
||||
val characterService by inject<CharacterSheetService>()
|
||||
val campaignService by inject<CampaignService>()
|
||||
|
||||
routing {
|
||||
get(
|
||||
path = "/",
|
||||
body = {
|
||||
call.respondText(contentType = ContentType.Text.Html) {
|
||||
"<a href=\"http://127.0.0.1:16030/characters\">characters</a>"
|
||||
"""<html><body><ul>
|
||||
<li><a href="http://127.0.0.1:16030/characters">characters</a></li>
|
||||
<li><a href="http://127.0.0.1:16030/campaign">campaign</a></li>
|
||||
</ul></body></html>"""
|
||||
}
|
||||
}
|
||||
)
|
||||
get(
|
||||
path = "/characters",
|
||||
body = {
|
||||
val body = repository.characterSheetFlow().value.map(factory::convertToJson)
|
||||
call.respond(body)
|
||||
call.respond(characterService.character())
|
||||
},
|
||||
)
|
||||
get(
|
||||
path = "/campaign",
|
||||
body = {
|
||||
call.respond(campaignService.campaign())
|
||||
}
|
||||
)
|
||||
webSocket(
|
||||
path = "/ws",
|
||||
handler = {
|
||||
|
|
@ -97,7 +107,8 @@ class LocalServer {
|
|||
incoming.consumeEach { frame ->
|
||||
if (frame is Frame.Text) {
|
||||
val message = Json.decodeFromFrame(frame = frame)
|
||||
println(message)
|
||||
// log the message
|
||||
engine.handle(message)
|
||||
// broadcast to clients the message
|
||||
outgoingMessageBuffer.emit(message)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.pixelized.shared.lwa
|
||||
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheetJsonFactory
|
||||
import com.pixelized.shared.lwa.model.campaign.CampaignJsonFactory
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJsonFactory
|
||||
import com.pixelized.shared.lwa.usecase.CampaignUseCase
|
||||
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.module.dsl.factoryOf
|
||||
|
|
@ -26,9 +28,11 @@ val toolsDependencies
|
|||
val factoryDependencies
|
||||
get() = module {
|
||||
factoryOf(::CharacterSheetJsonFactory)
|
||||
factoryOf(::CampaignJsonFactory)
|
||||
}
|
||||
|
||||
val useCaseDependencies
|
||||
get() = module {
|
||||
factoryOf(::CharacterSheetUseCase)
|
||||
factoryOf(::CampaignUseCase)
|
||||
}
|
||||
|
|
@ -34,4 +34,13 @@ fun characterStorePath(
|
|||
OperatingSystem.Windows -> "${storePath(os = os)}characters\\"
|
||||
OperatingSystem.Macintosh -> "${storePath(os = os)}characters/"
|
||||
}
|
||||
}
|
||||
|
||||
fun campaignPath(
|
||||
os: OperatingSystem = OperatingSystem.current,
|
||||
): String {
|
||||
return when (os) {
|
||||
OperatingSystem.Windows -> "${storePath(os = os)}campaign\\"
|
||||
OperatingSystem.Macintosh -> "${storePath(os = os)}campaign/"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.pixelized.shared.lwa.model.campaign
|
||||
|
||||
data class Campaign(
|
||||
val characters: Map<String, CharacterInstance>,
|
||||
) {
|
||||
data class CharacterInstance(
|
||||
val characteristic: Map<Characteristic, Int>,
|
||||
val usedSkill: List<String>,
|
||||
) {
|
||||
enum class Characteristic {
|
||||
Damage,
|
||||
Power,
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val EMPTY = Campaign(
|
||||
characters = emptyMap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun Campaign.character(id: String): Campaign.CharacterInstance {
|
||||
return characters[id] ?: Campaign.CharacterInstance(
|
||||
characteristic = emptyMap(),
|
||||
usedSkill = emptyList(),
|
||||
)
|
||||
}
|
||||
|
||||
val Campaign.CharacterInstance.damage
|
||||
get() = characteristic[Campaign.CharacterInstance.Characteristic.Damage] ?: 0
|
||||
|
||||
val Campaign.CharacterInstance.power
|
||||
get() = characteristic[Campaign.CharacterInstance.Characteristic.Power] ?: 0
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.shared.lwa.model.campaign.model
|
||||
package com.pixelized.shared.lwa.model.campaign
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.pixelized.shared.lwa.model.campaign
|
||||
|
||||
|
||||
class CampaignJsonFactory {
|
||||
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 { entry ->
|
||||
entry.key to Campaign.CharacterInstance(
|
||||
characteristic = entry.value.characteristic.map { char ->
|
||||
when (char.key) {
|
||||
CampaignJsonV1.CharacterInstanceJson.Characteristic.Damage -> Campaign.CharacterInstance.Characteristic.Damage
|
||||
CampaignJsonV1.CharacterInstanceJson.Characteristic.Power -> Campaign.CharacterInstance.Characteristic.Power
|
||||
} to char.value
|
||||
}.toMap(),
|
||||
usedSkill = entry.value.usedSkill,
|
||||
)
|
||||
}.toMap()
|
||||
)
|
||||
}
|
||||
|
||||
fun convertToJson(
|
||||
data: Campaign,
|
||||
): CampaignJson {
|
||||
return CampaignJsonV1(
|
||||
characters = data.characters.map { entry ->
|
||||
entry.key to CampaignJsonV1.CharacterInstanceJson(
|
||||
characteristic = entry.value.characteristic.map { char ->
|
||||
when (char.key) {
|
||||
Campaign.CharacterInstance.Characteristic.Damage -> CampaignJsonV1.CharacterInstanceJson.Characteristic.Damage
|
||||
Campaign.CharacterInstance.Characteristic.Power -> CampaignJsonV1.CharacterInstanceJson.Characteristic.Power
|
||||
} to char.value
|
||||
}.toMap(),
|
||||
usedSkill = entry.value.usedSkill,
|
||||
)
|
||||
}.toMap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.pixelized.shared.lwa.model.campaign
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CampaignJsonV1(
|
||||
val characters: Map<String, CharacterInstanceJson>,
|
||||
) : CampaignJson {
|
||||
|
||||
@Serializable
|
||||
data class CharacterInstanceJson(
|
||||
val characteristic: Map<Characteristic, Int>,
|
||||
val usedSkill: List<String>,
|
||||
) {
|
||||
enum class Characteristic {
|
||||
Damage,
|
||||
Power,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package com.pixelized.shared.lwa.model.campaign
|
||||
|
||||
import com.pixelized.shared.lwa.model.campaign.model.CampaignFactory
|
||||
|
||||
class CampaignRepository(
|
||||
private val factory: CampaignFactory,
|
||||
) {
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
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,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
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,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
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,10 +1,11 @@
|
|||
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||
package com.pixelized.shared.lwa.model.characterSheet
|
||||
|
||||
data class CharacterSheet(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val portrait: String?,
|
||||
val thumbnail: String?,
|
||||
val level: Int,
|
||||
// characteristics
|
||||
val strength: Int,
|
||||
val dexterity: Int,
|
||||
|
|
@ -14,21 +15,12 @@ data class CharacterSheet(
|
|||
val power: Int,
|
||||
val charisma: Int,
|
||||
// sub characteristics
|
||||
val overrideMovement: Boolean,
|
||||
val movement: Int,
|
||||
val currentHp: Int,
|
||||
val overrideMaxHp: Boolean,
|
||||
val maxHp: Int,
|
||||
val currentPp: Int,
|
||||
val overrideMaxPP: Boolean,
|
||||
val maxPp: Int,
|
||||
val overrideDamageBonus: Boolean,
|
||||
val hp: Int,
|
||||
val pp: Int,
|
||||
val damageBonus: String,
|
||||
val overrideArmor: Boolean,
|
||||
val armor: Int,
|
||||
val overrideLearning: Boolean,
|
||||
val learning: Int,
|
||||
val overrideHpGrow: Boolean,
|
||||
val hpGrow: Int,
|
||||
// skills
|
||||
val commonSkills: List<Skill>,
|
||||
|
|
@ -43,7 +35,7 @@ data class CharacterSheet(
|
|||
val description: String?,
|
||||
val base: String,
|
||||
val bonus: String?,
|
||||
val level: String?,
|
||||
val level: Int,
|
||||
val occupation: Boolean,
|
||||
val used: Boolean,
|
||||
)
|
||||
|
|
@ -51,6 +43,8 @@ data class CharacterSheet(
|
|||
data class Roll(
|
||||
val id: String,
|
||||
val label: String,
|
||||
val description: String?,
|
||||
val canBeCritical: Boolean,
|
||||
val roll: String,
|
||||
)
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||
package com.pixelized.shared.lwa.model.characterSheet
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||
package com.pixelized.shared.lwa.model.characterSheet
|
||||
|
||||
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||
|
||||
|
|
@ -22,6 +22,7 @@ class CharacterSheetJsonFactory(
|
|||
name = json.name,
|
||||
portrait = json.portrait,
|
||||
thumbnail = json.thumbnail,
|
||||
level = json.level,
|
||||
strength = json.strength,
|
||||
dexterity = json.dexterity,
|
||||
constitution = json.constitution,
|
||||
|
|
@ -29,28 +30,26 @@ class CharacterSheetJsonFactory(
|
|||
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(
|
||||
movement = defaultMovement(),
|
||||
hp = defaultMaxHp(
|
||||
constitution = json.constitution,
|
||||
height = json.height,
|
||||
level = json.level
|
||||
),
|
||||
currentPp = json.currentPP,
|
||||
overrideMaxPP = json.maxPP != null,
|
||||
maxPp = json.maxPP ?: defaultMaxPower(power = json.power),
|
||||
overrideDamageBonus = json.damageBonus != null,
|
||||
damageBonus = json.damageBonus ?: defaultDamageBonus(
|
||||
pp = defaultMaxPower(
|
||||
power = json.power,
|
||||
),
|
||||
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),
|
||||
armor = defaultArmor(),
|
||||
learning = defaultLearning(
|
||||
intelligence = json.intelligence,
|
||||
),
|
||||
hpGrow = defaultHpGrow(
|
||||
constitution = json.constitution,
|
||||
),
|
||||
commonSkills = json.skills.map {
|
||||
CharacterSheet.Skill(
|
||||
id = it.id,
|
||||
|
|
@ -91,6 +90,8 @@ class CharacterSheetJsonFactory(
|
|||
CharacterSheet.Roll(
|
||||
id = it.id,
|
||||
label = it.label,
|
||||
description = it.description,
|
||||
canBeCritical = it.canBeCritical,
|
||||
roll = it.roll,
|
||||
)
|
||||
},
|
||||
|
|
@ -105,6 +106,7 @@ class CharacterSheetJsonFactory(
|
|||
name = sheet.name,
|
||||
thumbnail = sheet.thumbnail,
|
||||
portrait = sheet.portrait,
|
||||
level = sheet.level,
|
||||
strength = sheet.strength,
|
||||
dexterity = sheet.dexterity,
|
||||
constitution = sheet.constitution,
|
||||
|
|
@ -112,15 +114,6 @@ class CharacterSheetJsonFactory(
|
|||
intelligence = sheet.intelligence,
|
||||
power = sheet.power,
|
||||
charisma = sheet.charisma,
|
||||
movement = if (sheet.overrideMovement) sheet.movement else null,
|
||||
currentHp = sheet.currentHp,
|
||||
maxHp = if (sheet.overrideMaxHp) sheet.maxHp else null,
|
||||
currentPP = sheet.currentPp,
|
||||
maxPP = if (sheet.overrideMaxPP) sheet.maxPp else null,
|
||||
damageBonus = if (sheet.overrideDamageBonus) sheet.damageBonus else null,
|
||||
armor = if (sheet.overrideArmor) sheet.armor else null,
|
||||
learning = if (sheet.overrideLearning) sheet.learning else null,
|
||||
hpGrowf = if (sheet.overrideHpGrow) sheet.hpGrow else null,
|
||||
skills = sheet.commonSkills.map {
|
||||
CharacterSheetJsonV1.Skill(
|
||||
id = it.id,
|
||||
|
|
@ -161,6 +154,8 @@ class CharacterSheetJsonFactory(
|
|||
CharacterSheetJsonV1.Roll(
|
||||
id = it.id,
|
||||
label = it.label,
|
||||
description = it.description,
|
||||
canBeCritical = it.canBeCritical,
|
||||
roll = it.roll,
|
||||
)
|
||||
},
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||
package com.pixelized.shared.lwa.model.characterSheet
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -8,6 +8,7 @@ data class CharacterSheetJsonV1(
|
|||
val name: String,
|
||||
val portrait: String?,
|
||||
val thumbnail: String?,
|
||||
val level: Int,
|
||||
// characteristics
|
||||
val strength: Int,
|
||||
val dexterity: Int,
|
||||
|
|
@ -16,16 +17,6 @@ data class CharacterSheetJsonV1(
|
|||
val intelligence: Int,
|
||||
val power: Int,
|
||||
val charisma: Int,
|
||||
// sub characteristics
|
||||
val movement: Int?,
|
||||
val currentHp: Int,
|
||||
val maxHp: Int?,
|
||||
val currentPP: Int,
|
||||
val maxPP: Int?,
|
||||
val damageBonus: String?,
|
||||
val armor: Int?,
|
||||
val learning: Int?,
|
||||
val hpGrowf: Int?,
|
||||
// skills
|
||||
val skills: List<Skill>,
|
||||
// occupations
|
||||
|
|
@ -43,7 +34,7 @@ data class CharacterSheetJsonV1(
|
|||
val description: String?,
|
||||
val base: String,
|
||||
val bonus: String?,
|
||||
val level: String?,
|
||||
val level: Int,
|
||||
val occupation: Boolean,
|
||||
val used: Boolean,
|
||||
)
|
||||
|
|
@ -52,6 +43,8 @@ data class CharacterSheetJsonV1(
|
|||
data class Roll(
|
||||
val id: String,
|
||||
val label: String,
|
||||
val description: String?,
|
||||
val canBeCritical: Boolean,
|
||||
val roll: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.pixelized.shared.lwa.protocol
|
||||
|
||||
enum class MessageType {
|
||||
Roll
|
||||
Roll,
|
||||
UpdateSkillUsage,
|
||||
UpdatePlayerCharacteristic,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
package com.pixelized.shared.lwa.protocol.payload
|
||||
|
||||
sealed interface MessagePayload
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.shared.lwa.protocol.roll
|
||||
package com.pixelized.shared.lwa.protocol.payload
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -10,4 +10,4 @@ data class RollMessage(
|
|||
val rollDifficulty: String?,
|
||||
val rollValue: Int,
|
||||
val rollSuccessLimit: Int?,
|
||||
)
|
||||
) : MessagePayload
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.pixelized.shared.lwa.protocol.payload
|
||||
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UpdatePlayerCharacteristicMessage(
|
||||
val characterId: String,
|
||||
val characteristic: Campaign.CharacterInstance.Characteristic,
|
||||
val value: Int,
|
||||
) : MessagePayload
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.pixelized.shared.lwa.protocol.payload
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UpdateSkillUsageMessage(
|
||||
val characterId: String,
|
||||
val skillId: String,
|
||||
) : MessagePayload
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.pixelized.shared.lwa.usecase
|
||||
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
|
||||
class CampaignUseCase {
|
||||
|
||||
fun updateCharacteristic(
|
||||
character: Campaign.CharacterInstance,
|
||||
characteristic: Campaign.CharacterInstance.Characteristic,
|
||||
value: Int,
|
||||
): Campaign.CharacterInstance {
|
||||
return character.copy(
|
||||
characteristic = character.characteristic.toMutableMap().also {
|
||||
it[characteristic] = value
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.pixelized.shared.lwa.usecase
|
||||
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.max
|
||||
|
||||
|
|
@ -14,8 +15,10 @@ class CharacterSheetUseCase {
|
|||
fun defaultMaxHp(
|
||||
constitution: Int,
|
||||
height: Int,
|
||||
level: Int,
|
||||
): Int {
|
||||
return (ceil((constitution + height) / 2f).toInt())
|
||||
val add = max(defaultHpGrow(constitution = constitution) * (level - 1), 0)
|
||||
return (ceil((constitution + height) / 2f).toInt()) + add
|
||||
}
|
||||
|
||||
fun defaultMaxPower(
|
||||
|
|
@ -53,4 +56,33 @@ class CharacterSheetUseCase {
|
|||
fun defaultHpGrow(constitution: Int): Int {
|
||||
return (constitution / 3)
|
||||
}
|
||||
|
||||
fun updateSkillUsage(
|
||||
character: CharacterSheet,
|
||||
skillId: String,
|
||||
): CharacterSheet {
|
||||
return character.copy(
|
||||
commonSkills = character.commonSkills.map { skill ->
|
||||
if (skill.id == skillId) {
|
||||
skill.copy(used = skill.used.not())
|
||||
} else {
|
||||
skill
|
||||
}
|
||||
},
|
||||
specialSkills = character.specialSkills.map { skill ->
|
||||
if (skill.id == skillId) {
|
||||
skill.copy(used = skill.used.not())
|
||||
} else {
|
||||
skill
|
||||
}
|
||||
},
|
||||
magicSkills = character.magicSkills.map { skill ->
|
||||
if (skill.id == skillId) {
|
||||
skill.copy(used = skill.used.not())
|
||||
} else {
|
||||
skill
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue