Merge branch 'feature/remodel' into 'main'
Feature/remodel See merge request pixelized/LwaCharacterSheet!3
This commit is contained in:
commit
1db6e593d7
25 changed files with 369 additions and 472 deletions
|
|
@ -48,14 +48,11 @@ class DataSyncViewModel(
|
||||||
.combine(campaignRepository.campaignFlow) { _, campaign: Campaign -> campaign }
|
.combine(campaignRepository.campaignFlow) { _, campaign: Campaign -> campaign }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.onEach { campaign ->
|
.onEach { campaign ->
|
||||||
(campaign.characters.keys + campaign.npcs.keys).forEach { id ->
|
campaign.instances.keys.forEach { id ->
|
||||||
characterRepository.characterDetail(
|
characterRepository.characterDetail(
|
||||||
characterSheetId = id.characterSheetId,
|
characterSheetId = id.characterSheetId,
|
||||||
forceUpdate = true,
|
forceUpdate = true,
|
||||||
)
|
)
|
||||||
alterationRepository.updateActiveAlterations(
|
|
||||||
characterInstanceId = id,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
|
|
|
||||||
|
|
@ -26,17 +26,4 @@ interface LwaClient {
|
||||||
suspend fun campaignRemoveNpc(characterSheetId: String, instanceId: Int)
|
suspend fun campaignRemoveNpc(characterSheetId: String, instanceId: Int)
|
||||||
|
|
||||||
suspend fun alterations(): List<AlterationJson>
|
suspend fun alterations(): List<AlterationJson>
|
||||||
|
|
||||||
suspend fun activeAlterations(
|
|
||||||
prefix: Char,
|
|
||||||
characterSheetId: String,
|
|
||||||
instanceId: Int,
|
|
||||||
): List<String>
|
|
||||||
|
|
||||||
suspend fun toggleActiveAlterations(
|
|
||||||
prefix: Char,
|
|
||||||
characterSheetId: String,
|
|
||||||
instanceId: Int,
|
|
||||||
alterationId: String,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
@ -73,24 +73,4 @@ class LwaClientImpl(
|
||||||
override suspend fun alterations(): List<AlterationJson> = client
|
override suspend fun alterations(): List<AlterationJson> = client
|
||||||
.get("$root/alterations")
|
.get("$root/alterations")
|
||||||
.body()
|
.body()
|
||||||
|
|
||||||
override suspend fun activeAlterations(
|
|
||||||
prefix: Char,
|
|
||||||
characterSheetId: String,
|
|
||||||
instanceId: Int,
|
|
||||||
): List<String> = client
|
|
||||||
.get("$root/alterations/active?characterSheetId=$characterSheetId&instanceId=$instanceId&prefix=$prefix")
|
|
||||||
.body()
|
|
||||||
|
|
||||||
override suspend fun toggleActiveAlterations(
|
|
||||||
prefix: Char,
|
|
||||||
characterSheetId: String,
|
|
||||||
instanceId: Int,
|
|
||||||
alterationId: String,
|
|
||||||
) = client
|
|
||||||
.put("$root/alterations/active/toggle?characterSheetId=$characterSheetId&instanceId=$instanceId&prefix=$prefix") {
|
|
||||||
contentType(ContentType.Application.Json)
|
|
||||||
setBody(alterationId)
|
|
||||||
}
|
|
||||||
.body<Unit>()
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package com.pixelized.desktop.lwa.repository.alteration
|
package com.pixelized.desktop.lwa.repository.alteration
|
||||||
|
|
||||||
|
import com.pixelized.desktop.lwa.repository.campaign.CampaignStore
|
||||||
import com.pixelized.shared.lwa.model.alteration.Alteration
|
import com.pixelized.shared.lwa.model.alteration.Alteration
|
||||||
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
|
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
|
||||||
|
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||||
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance
|
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
@ -13,22 +15,20 @@ import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
// Theses typealias are there for readability only.
|
|
||||||
private typealias AlterationId = String
|
|
||||||
|
|
||||||
class AlterationRepository(
|
class AlterationRepository(
|
||||||
private val store: AlterationStore,
|
private val campaignStore: CampaignStore,
|
||||||
|
private val alterationStore: AlterationStore,
|
||||||
) {
|
) {
|
||||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
private val activeAlterationMapFlow: StateFlow<Map<CharacterInstance.Id, Map<String, List<FieldAlteration>>>> =
|
private val activeAlterationMapFlow: StateFlow<Map<CharacterInstance.Id, Map<String, List<FieldAlteration>>>> =
|
||||||
combine(
|
combine(
|
||||||
store.alterations,
|
alterationStore.alterationsFlow,
|
||||||
store.active,
|
campaignStore.campaignFlow,
|
||||||
) { alterations, actives ->
|
) { alterations, campaign: Campaign ->
|
||||||
actives.map { activeEntry ->
|
campaign.instances.map { activeEntry ->
|
||||||
activeEntry.key to transformToAlterationFieldMap(
|
activeEntry.key to transformToAlterationFieldMap(
|
||||||
alterations = alterations,
|
alterations = alterations,
|
||||||
actives = activeEntry.value
|
actives = activeEntry.value.alterations,
|
||||||
)
|
)
|
||||||
}.toMap()
|
}.toMap()
|
||||||
}.stateIn(
|
}.stateIn(
|
||||||
|
|
@ -49,32 +49,13 @@ class AlterationRepository(
|
||||||
return activeAlterationMapFlow.value[characterInstanceId] ?: emptyMap()
|
return activeAlterationMapFlow.value[characterInstanceId] ?: emptyMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateActiveAlterations(
|
|
||||||
characterInstanceId: CharacterInstance.Id,
|
|
||||||
) {
|
|
||||||
store.updateActiveAlterations(
|
|
||||||
characterInstanceId = characterInstanceId,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun toggleActiveAlteration(
|
|
||||||
characterInstanceId: CharacterInstance.Id,
|
|
||||||
alterationId: String,
|
|
||||||
) {
|
|
||||||
// alteration was active for the character toggle it off.
|
|
||||||
store.toggleActiveAlteration(
|
|
||||||
characterInstanceId = characterInstanceId,
|
|
||||||
alterationId = alterationId,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun transformToAlterationFieldMap(
|
private fun transformToAlterationFieldMap(
|
||||||
alterations: Map<String, Alteration>,
|
alterations: Map<String, Alteration>,
|
||||||
actives: List<String>,
|
actives: List<String>,
|
||||||
): Map<String, List<FieldAlteration>> {
|
): Map<String, List<FieldAlteration>> {
|
||||||
val fieldAlterations = hashMapOf<String, MutableList<FieldAlteration>>()
|
val fieldAlterations = hashMapOf<String, MutableList<FieldAlteration>>()
|
||||||
actives.forEach { id: AlterationId ->
|
actives.forEach { alterationId ->
|
||||||
alterations[id]?.let { alteration ->
|
alterations[alterationId]?.let { alteration ->
|
||||||
alteration.fields.forEach { field ->
|
alteration.fields.forEach { field ->
|
||||||
fieldAlterations
|
fieldAlterations
|
||||||
.getOrPut(field.fieldId) { mutableListOf() }
|
.getOrPut(field.fieldId) { mutableListOf() }
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
package com.pixelized.desktop.lwa.repository.alteration
|
package com.pixelized.desktop.lwa.repository.alteration
|
||||||
|
|
||||||
import com.pixelized.desktop.lwa.network.LwaClient
|
import com.pixelized.desktop.lwa.network.LwaClient
|
||||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
|
||||||
import com.pixelized.shared.lwa.model.alteration.Alteration
|
import com.pixelized.shared.lwa.model.alteration.Alteration
|
||||||
import com.pixelized.shared.lwa.model.alteration.AlterationJsonFactory
|
import com.pixelized.shared.lwa.model.alteration.AlterationJsonFactory
|
||||||
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance
|
|
||||||
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
|
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
|
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.ToggleActiveAlteration
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
|
@ -17,38 +12,20 @@ import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class AlterationStore(
|
class AlterationStore(
|
||||||
private val alterationFactory: AlterationJsonFactory,
|
private val alterationFactory: AlterationJsonFactory,
|
||||||
private val campaignJsonFactory: CampaignJsonFactory,
|
|
||||||
private val network: NetworkRepository,
|
|
||||||
private val client: LwaClient,
|
private val client: LwaClient,
|
||||||
) {
|
) {
|
||||||
private val _alterations = MutableStateFlow<Map<String, Alteration>>(emptyMap())
|
private val _alterationsFlow = MutableStateFlow<Map<String, Alteration>>(emptyMap())
|
||||||
val alterations: StateFlow<Map<String, Alteration>> = _alterations
|
val alterationsFlow: StateFlow<Map<String, Alteration>> = _alterationsFlow
|
||||||
|
|
||||||
private val _active = MutableStateFlow<Map<CharacterInstance.Id, List<String>>>(emptyMap())
|
|
||||||
val active: StateFlow<Map<CharacterInstance.Id, List<String>>> get() = _active
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
scope.launch {
|
scope.launch {
|
||||||
updateAlterations()
|
updateAlterations()
|
||||||
}
|
}
|
||||||
scope.launch {
|
|
||||||
network.data.collect(::handleMessage)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateAlterations() {
|
private suspend fun updateAlterations() {
|
||||||
_alterations.value = loadAlteration()
|
_alterationsFlow.value = loadAlteration()
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun updateActiveAlterations(
|
|
||||||
characterInstanceId: CharacterInstance.Id,
|
|
||||||
) {
|
|
||||||
_active.value = _active.value.toMutableMap().also {
|
|
||||||
it[characterInstanceId] = loadActiveAlterations(
|
|
||||||
characterInstanceId = characterInstanceId,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadAlteration(): Map<String, Alteration> {
|
private suspend fun loadAlteration(): Map<String, Alteration> {
|
||||||
|
|
@ -57,69 +34,11 @@ class AlterationStore(
|
||||||
return data.associateBy { it.id }
|
return data.associateBy { it.id }
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadActiveAlterations(
|
|
||||||
characterInstanceId: CharacterInstance.Id,
|
|
||||||
): List<String> {
|
|
||||||
val request = client.activeAlterations(
|
|
||||||
prefix = characterInstanceId.prefix,
|
|
||||||
characterSheetId = characterInstanceId.characterSheetId,
|
|
||||||
instanceId = characterInstanceId.instanceId,
|
|
||||||
)
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
|
|
||||||
fun alterations(): Collection<Alteration> {
|
fun alterations(): Collection<Alteration> {
|
||||||
return alterations.value.values
|
return alterationsFlow.value.values
|
||||||
}
|
}
|
||||||
|
|
||||||
fun alteration(alterationId: String): Alteration? {
|
fun alteration(alterationId: String): Alteration? {
|
||||||
return alterations.value[alterationId]
|
return alterationsFlow.value[alterationId]
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun toggleActiveAlteration(
|
|
||||||
characterInstanceId: CharacterInstance.Id,
|
|
||||||
alterationId: String,
|
|
||||||
) {
|
|
||||||
client.toggleActiveAlterations(
|
|
||||||
prefix = characterInstanceId.prefix,
|
|
||||||
characterSheetId = characterInstanceId.characterSheetId,
|
|
||||||
instanceId = characterInstanceId.instanceId,
|
|
||||||
alterationId = alterationId,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun handleMessage(message: SocketMessage) {
|
|
||||||
when (message) {
|
|
||||||
is ToggleActiveAlteration -> {
|
|
||||||
setActiveAlteration(
|
|
||||||
characterInstanceId = CharacterInstance.Id(
|
|
||||||
prefix = message.prefix,
|
|
||||||
characterSheetId = message.characterSheetId,
|
|
||||||
instanceId = message.instanceId,
|
|
||||||
),
|
|
||||||
alterationId = message.alterationId,
|
|
||||||
active = message.active,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun setActiveAlteration(
|
|
||||||
characterInstanceId: CharacterInstance.Id,
|
|
||||||
alterationId: String,
|
|
||||||
active: Boolean,
|
|
||||||
) {
|
|
||||||
_active.value = _active.value.toMutableMap().also { map ->
|
|
||||||
map[characterInstanceId] = map[characterInstanceId]?.toMutableList()
|
|
||||||
?.also {
|
|
||||||
when {
|
|
||||||
it.contains(alterationId) && !active -> it.remove(alterationId)
|
|
||||||
!it.contains(alterationId) && active -> it.add(alterationId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?: listOfNotNull(if (active) alterationId else null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -130,6 +130,11 @@ class CampaignStore(
|
||||||
characterInstanceId = instanceId,
|
characterInstanceId = instanceId,
|
||||||
diminished = message.diminished,
|
diminished = message.diminished,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is CampaignMessage.ToggleActiveAlteration -> updateAlterations(
|
||||||
|
characterInstanceId = instanceId,
|
||||||
|
alterationId = message.alterationId,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,8 +167,8 @@ class CampaignStore(
|
||||||
) {
|
) {
|
||||||
val campaign = _campaignFlow.value
|
val campaign = _campaignFlow.value
|
||||||
|
|
||||||
when {
|
when (characterInstanceId.prefix) {
|
||||||
campaign.characters.containsKey(characterInstanceId) -> {
|
Campaign.CharacterInstance.Id.PLAYER -> {
|
||||||
val characters = campaign.characters.toMutableMap().also {
|
val characters = campaign.characters.toMutableMap().also {
|
||||||
it[characterInstanceId] = useCase.updateCharacteristic(
|
it[characterInstanceId] = useCase.updateCharacteristic(
|
||||||
instance = campaign.character(id = characterInstanceId),
|
instance = campaign.character(id = characterInstanceId),
|
||||||
|
|
@ -174,7 +179,7 @@ class CampaignStore(
|
||||||
_campaignFlow.value = _campaignFlow.value.copy(characters = characters)
|
_campaignFlow.value = _campaignFlow.value.copy(characters = characters)
|
||||||
}
|
}
|
||||||
|
|
||||||
campaign.npcs.containsKey(characterInstanceId) -> {
|
Campaign.CharacterInstance.Id.NPC -> {
|
||||||
val npcs = campaign.npcs.toMutableMap().also {
|
val npcs = campaign.npcs.toMutableMap().also {
|
||||||
it[characterInstanceId] = useCase.updateCharacteristic(
|
it[characterInstanceId] = useCase.updateCharacteristic(
|
||||||
instance = campaign.npc(id = characterInstanceId),
|
instance = campaign.npc(id = characterInstanceId),
|
||||||
|
|
@ -193,8 +198,8 @@ class CampaignStore(
|
||||||
) {
|
) {
|
||||||
val campaign = _campaignFlow.value
|
val campaign = _campaignFlow.value
|
||||||
|
|
||||||
when {
|
when (characterInstanceId.prefix) {
|
||||||
campaign.characters.containsKey(characterInstanceId) -> {
|
Campaign.CharacterInstance.Id.PLAYER -> {
|
||||||
val characters = campaign.characters.toMutableMap().also {
|
val characters = campaign.characters.toMutableMap().also {
|
||||||
it[characterInstanceId] = useCase.updateDiminished(
|
it[characterInstanceId] = useCase.updateDiminished(
|
||||||
instance = campaign.character(id = characterInstanceId),
|
instance = campaign.character(id = characterInstanceId),
|
||||||
|
|
@ -204,7 +209,7 @@ class CampaignStore(
|
||||||
_campaignFlow.value = _campaignFlow.value.copy(characters = characters)
|
_campaignFlow.value = _campaignFlow.value.copy(characters = characters)
|
||||||
}
|
}
|
||||||
|
|
||||||
campaign.npcs.containsKey(characterInstanceId) -> {
|
Campaign.CharacterInstance.Id.NPC -> {
|
||||||
val npcs = campaign.npcs.toMutableMap().also {
|
val npcs = campaign.npcs.toMutableMap().also {
|
||||||
it[characterInstanceId] = useCase.updateDiminished(
|
it[characterInstanceId] = useCase.updateDiminished(
|
||||||
instance = campaign.npc(id = characterInstanceId),
|
instance = campaign.npc(id = characterInstanceId),
|
||||||
|
|
@ -216,6 +221,53 @@ class CampaignStore(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun updateAlterations(
|
||||||
|
characterInstanceId: Campaign.CharacterInstance.Id,
|
||||||
|
alterationId: String,
|
||||||
|
) {
|
||||||
|
val campaign = _campaignFlow.value
|
||||||
|
|
||||||
|
when (characterInstanceId.prefix) {
|
||||||
|
Campaign.CharacterInstance.Id.PLAYER -> {
|
||||||
|
// fetch all the current campaign character
|
||||||
|
val characters = campaign.characters.toMutableMap()
|
||||||
|
// update the corresponding character alterations
|
||||||
|
characters[characterInstanceId]?.let { character ->
|
||||||
|
characters[characterInstanceId] = character.copy(
|
||||||
|
alterations = character.alterations.toMutableList().also { alterations ->
|
||||||
|
if (alterations.contains(alterationId)) {
|
||||||
|
alterations.remove(alterationId)
|
||||||
|
} else {
|
||||||
|
alterations.add(alterationId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// update the flow.
|
||||||
|
_campaignFlow.value = _campaignFlow.value.copy(characters = characters)
|
||||||
|
}
|
||||||
|
|
||||||
|
Campaign.CharacterInstance.Id.NPC -> {
|
||||||
|
// fetch all the current campaign character
|
||||||
|
val characters = campaign.npcs.toMutableMap()
|
||||||
|
// update the corresponding character alterations
|
||||||
|
characters[characterInstanceId]?.let { character ->
|
||||||
|
characters[characterInstanceId] = character.copy(
|
||||||
|
alterations = character.alterations.toMutableList().also { alterations ->
|
||||||
|
if (alterations.contains(alterationId)) {
|
||||||
|
alterations.remove(alterationId)
|
||||||
|
} else {
|
||||||
|
alterations.add(alterationId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// update the flow.
|
||||||
|
_campaignFlow.value = _campaignFlow.value.copy(npcs = characters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
private fun MutableStateFlow<Campaign>.update(campaign: Campaign): Campaign {
|
private fun MutableStateFlow<Campaign>.update(campaign: Campaign): Campaign {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.RestSynchronisation
|
import com.pixelized.shared.lwa.protocol.websocket.RestSynchronisation
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.RollMessage
|
import com.pixelized.shared.lwa.protocol.websocket.RollMessage
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
|
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.ToggleActiveAlteration
|
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.UpdateSkillUsageMessage
|
import com.pixelized.shared.lwa.protocol.websocket.UpdateSkillUsageMessage
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_down
|
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_down
|
||||||
|
|
@ -65,45 +64,48 @@ class TextMessageFactory(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is CampaignMessage.UpdateDiminished -> {
|
is CampaignMessage -> when (message) {
|
||||||
val sheetPreview = characterSheetRepository
|
is CampaignMessage.UpdateDiminished -> {
|
||||||
.characterPreview(characterId = message.characterSheetId)
|
val sheetPreview = characterSheetRepository
|
||||||
?: return null
|
.characterPreview(characterId = message.characterSheetId)
|
||||||
|
?: return null
|
||||||
|
|
||||||
DiminishedTextMessageUio(
|
DiminishedTextMessageUio(
|
||||||
id = "${message.timestamp}-$id-Diminished",
|
id = "${message.timestamp}-$id-Diminished",
|
||||||
timestamp = formatTime.format(time),
|
timestamp = formatTime.format(time),
|
||||||
character = sheetPreview.name,
|
character = sheetPreview.name,
|
||||||
diminished = message.diminished,
|
diminished = message.diminished,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is CampaignMessage.UpdateCharacteristic -> {
|
is CampaignMessage.UpdateCharacteristic -> {
|
||||||
val sheet = characterSheetRepository.characterDetail(
|
val sheet = characterSheetRepository.characterDetail(
|
||||||
characterSheetId = message.characterSheetId,
|
characterSheetId = message.characterSheetId,
|
||||||
) ?: return null
|
) ?: return null
|
||||||
|
|
||||||
// We are talking about damage / consumption there so old value have to be put first.
|
// We are talking about damage / consumption there so old value have to be put first.
|
||||||
val value = message.oldValue - message.newValue
|
val value = message.oldValue - message.newValue
|
||||||
|
|
||||||
CharacteristicTextMessageUio(
|
CharacteristicTextMessageUio(
|
||||||
id = "${message.timestamp}-$id-Characteristic",
|
id = "${message.timestamp}-$id-Characteristic",
|
||||||
timestamp = formatTime.format(time),
|
timestamp = formatTime.format(time),
|
||||||
label = when {
|
label = when {
|
||||||
message.characteristic == Damage && value < 0 -> Res.string.chat__characteristic_change__hp_down
|
message.characteristic == Damage && value < 0 -> Res.string.chat__characteristic_change__hp_down
|
||||||
message.characteristic == Damage -> Res.string.chat__characteristic_change__hp_up
|
message.characteristic == Damage -> Res.string.chat__characteristic_change__hp_up
|
||||||
message.characteristic == Power && value < 0 -> Res.string.chat__characteristic_change__pp_down
|
message.characteristic == Power && value < 0 -> Res.string.chat__characteristic_change__pp_down
|
||||||
else -> Res.string.chat__characteristic_change__pp_up
|
else -> Res.string.chat__characteristic_change__pp_up
|
||||||
},
|
},
|
||||||
character = sheet.name,
|
character = sheet.name,
|
||||||
value = abs(value),
|
value = abs(value),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is CampaignMessage.ToggleActiveAlteration -> null // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
is RestSynchronisation.Campaign -> null
|
is RestSynchronisation.Campaign -> null
|
||||||
is RestSynchronisation.CharacterSheetDelete -> null
|
is RestSynchronisation.CharacterSheetDelete -> null
|
||||||
is RestSynchronisation.CharacterSheetUpdate -> null
|
is RestSynchronisation.CharacterSheetUpdate -> null
|
||||||
is ToggleActiveAlteration -> null
|
|
||||||
is UpdateSkillUsageMessage -> null
|
is UpdateSkillUsageMessage -> null
|
||||||
is GameMasterEvent -> null
|
is GameMasterEvent -> null
|
||||||
is GameEvent.DisplayPortrait -> null
|
is GameEvent.DisplayPortrait -> null
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,13 @@
|
||||||
package com.pixelized.server.lwa.model.alteration
|
package com.pixelized.server.lwa.model.alteration
|
||||||
|
|
||||||
import com.pixelized.shared.lwa.model.alteration.AlterationJson
|
import com.pixelized.shared.lwa.model.alteration.AlterationJson
|
||||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
|
||||||
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
|
|
||||||
class AlterationService(
|
class AlterationService(
|
||||||
private val store: AlterationStore,
|
store: AlterationStore,
|
||||||
private val campaignJsonFactory: CampaignJsonFactory,
|
|
||||||
) {
|
) {
|
||||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
private val alterationsFlow = store.alterationsFlow()
|
||||||
private val alterations = store.alterationsFlow()
|
|
||||||
private val actives = store.activeFlow()
|
|
||||||
.map { data ->
|
|
||||||
data.mapKeys { it: Map.Entry<String, List<String>> ->
|
|
||||||
campaignJsonFactory.characterInstanceIdFromJson(characterInstanceIdJson = it.key)
|
|
||||||
}
|
|
||||||
}.stateIn(
|
|
||||||
scope = scope,
|
|
||||||
started = SharingStarted.Eagerly,
|
|
||||||
initialValue = emptyMap()
|
|
||||||
)
|
|
||||||
|
|
||||||
fun alterations(): List<AlterationJson> {
|
fun alterations(): List<AlterationJson> {
|
||||||
return alterations.value
|
return alterationsFlow.value
|
||||||
}
|
|
||||||
|
|
||||||
fun active(
|
|
||||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
|
||||||
): List<String> {
|
|
||||||
return actives.value[characterInstanceId] ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isAlterationActive(
|
|
||||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
|
||||||
alterationId: String,
|
|
||||||
): Boolean {
|
|
||||||
return actives.value[characterInstanceId]?.contains(alterationId) ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun toggleActiveAlteration(
|
|
||||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
|
||||||
alterationId: String,
|
|
||||||
): Boolean {
|
|
||||||
return store.toggleActiveAlteration(
|
|
||||||
characterInstanceId = characterInstanceId,
|
|
||||||
alterationId = alterationId,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package com.pixelized.server.lwa.model.alteration
|
package com.pixelized.server.lwa.model.alteration
|
||||||
|
|
||||||
import com.pixelized.shared.lwa.model.alteration.AlterationJson
|
import com.pixelized.shared.lwa.model.alteration.AlterationJson
|
||||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
|
||||||
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
|
|
||||||
import com.pixelized.shared.lwa.utils.PathProvider
|
import com.pixelized.shared.lwa.utils.PathProvider
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
@ -15,12 +13,10 @@ import java.io.File
|
||||||
|
|
||||||
class AlterationStore(
|
class AlterationStore(
|
||||||
private val pathProvider: PathProvider,
|
private val pathProvider: PathProvider,
|
||||||
private val campaignJsonFactory: CampaignJsonFactory,
|
|
||||||
private val json: Json,
|
private val json: Json,
|
||||||
) {
|
) {
|
||||||
private val directory = File(pathProvider.alterationsPath()).also { it.mkdirs() }
|
private val directory = File(pathProvider.alterationsPath()).also { it.mkdirs() }
|
||||||
private val alterationsFlow = MutableStateFlow<List<AlterationJson>>(emptyList())
|
private val alterationsFlow = MutableStateFlow<List<AlterationJson>>(emptyList())
|
||||||
private val activeFlow = MutableStateFlow<Map<String, List<String>>>(emptyMap())
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// build a coroutine scope for async calls
|
// build a coroutine scope for async calls
|
||||||
|
|
@ -28,113 +24,44 @@ class AlterationStore(
|
||||||
// load the initial data
|
// load the initial data
|
||||||
scope.launch {
|
scope.launch {
|
||||||
updateAlterations()
|
updateAlterations()
|
||||||
updateActiveAlterations()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun alterationsFlow(): StateFlow<List<AlterationJson>> = alterationsFlow
|
fun alterationsFlow(): StateFlow<List<AlterationJson>> = alterationsFlow
|
||||||
|
|
||||||
fun activeFlow(): StateFlow<Map<String, List<String>>> = activeFlow
|
|
||||||
|
|
||||||
private fun updateAlterations() {
|
private fun updateAlterations() {
|
||||||
alterationsFlow.value = loadAlterations()
|
alterationsFlow.value = try {
|
||||||
}
|
loadAlterations()
|
||||||
|
|
||||||
private fun updateActiveAlterations() {
|
|
||||||
activeFlow.value = loadActiveAlterations()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadAlterations(): List<AlterationJson> {
|
|
||||||
return try {
|
|
||||||
val alterationFile = file()
|
|
||||||
val json = alterationFile.readText(charset = Charsets.UTF_8)
|
|
||||||
if (json.isBlank()) error("alterations file is empty")
|
|
||||||
this.json.decodeFromString<List<AlterationJson>>(json)
|
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
// TODO log exception
|
println(exception) // TODO proper exception handling
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadActiveAlterations(): Map<String, List<String>> {
|
@Throws(
|
||||||
val mainFile = file()
|
FileReadException::class,
|
||||||
val jsonExt = ".json"
|
JsonConversionException::class,
|
||||||
|
)
|
||||||
|
private fun loadAlterations(): List<AlterationJson> {
|
||||||
return directory
|
return directory
|
||||||
.listFiles()
|
.listFiles()
|
||||||
?.filter { file ->
|
|
||||||
// guard ignore the main alteration file and non json files.
|
|
||||||
file.name != mainFile.name && file.name.contains(jsonExt)
|
|
||||||
}
|
|
||||||
?.mapNotNull { file ->
|
?.mapNotNull { file ->
|
||||||
// read the alteration file.
|
|
||||||
val json = try {
|
val json = try {
|
||||||
file.readText(charset = Charsets.UTF_8)
|
file.readText(charset = Charsets.UTF_8)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
throw FileReadException(root = exception)
|
throw FileReadException(root = exception)
|
||||||
}
|
}
|
||||||
|
// Guard, if the json is blank no alteration have been save, ignore this file.
|
||||||
|
if (json.isBlank()) {
|
||||||
|
return@mapNotNull null
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
val alterationIds = this.json.decodeFromString<List<String>>(json)
|
this.json.decodeFromString<AlterationJson>(json)
|
||||||
val characterInstanceId = file.name.dropLast(n = jsonExt.length)
|
|
||||||
characterInstanceId to alterationIds
|
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
// TODO log exception
|
|
||||||
throw JsonConversionException(root = exception)
|
throw JsonConversionException(root = exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?.toMap()
|
?: emptyList()
|
||||||
?: emptyMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toggleActiveAlteration(
|
|
||||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
|
||||||
alterationId: String,
|
|
||||||
): Boolean {
|
|
||||||
val id = campaignJsonFactory.convertToJson(id = characterInstanceId)
|
|
||||||
// toggle the activation state
|
|
||||||
val characterActiveAlterationIds = activeFlow.value[id]
|
|
||||||
?.toMutableList()
|
|
||||||
?.toggle(alterationId = alterationId)
|
|
||||||
?: listOf(alterationId)
|
|
||||||
// build the json string to save
|
|
||||||
val json = try {
|
|
||||||
this.json.encodeToString(characterActiveAlterationIds)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
throw JsonConversionException(root = exception)
|
|
||||||
}
|
|
||||||
// write the file
|
|
||||||
try {
|
|
||||||
val file = file(id = id)
|
|
||||||
file.writeText(
|
|
||||||
text = json,
|
|
||||||
charset = Charsets.UTF_8,
|
|
||||||
)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
throw FileWriteException(root = exception)
|
|
||||||
}
|
|
||||||
// Update the dataflow.
|
|
||||||
activeFlow.value = activeFlow.value.toMutableMap().also {
|
|
||||||
it[id] = characterActiveAlterationIds
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun file(): File {
|
|
||||||
return File("${pathProvider.alterationsPath()}alterations.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun file(
|
|
||||||
id: String,
|
|
||||||
): File {
|
|
||||||
return File("${pathProvider.alterationsPath()}$id.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun MutableList<String>.toggle(alterationId: String): MutableList<String> {
|
|
||||||
if (contains(alterationId)) {
|
|
||||||
remove(alterationId)
|
|
||||||
} else {
|
|
||||||
add(alterationId)
|
|
||||||
}
|
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class AlterationStoreException(root: Exception) : Exception(root)
|
sealed class AlterationStoreException(root: Exception) : Exception(root)
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,40 @@ class CampaignService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun removeInstance(
|
||||||
|
characterSheetId: String,
|
||||||
|
): Boolean {
|
||||||
|
// fetch all the current campaign character
|
||||||
|
val characterIds = campaign.characters
|
||||||
|
.filterKeys { it.characterSheetId == characterSheetId }
|
||||||
|
.keys
|
||||||
|
val npcIds = campaign.npcs
|
||||||
|
.filterKeys { it.characterSheetId == characterSheetId }
|
||||||
|
.keys
|
||||||
|
|
||||||
|
// check if the character is in the campaign.
|
||||||
|
if (characterIds.isEmpty() && npcIds.isEmpty()) return false
|
||||||
|
|
||||||
|
// update the corresponding character
|
||||||
|
val characters = campaign.characters.toMutableMap()
|
||||||
|
val npcs = campaign.npcs.toMutableMap()
|
||||||
|
characterIds.forEach(characters::remove)
|
||||||
|
npcIds.forEach(npcs::remove)
|
||||||
|
|
||||||
|
// save the campaign to the disk + update the flow.
|
||||||
|
return try {
|
||||||
|
store.save(
|
||||||
|
campaign = campaign.copy(
|
||||||
|
characters = characters,
|
||||||
|
npcs = npcs,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
true
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun setScene(
|
suspend fun setScene(
|
||||||
scene: Campaign.Scene,
|
scene: Campaign.Scene,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
|
@ -142,8 +176,8 @@ class CampaignService(
|
||||||
characteristic: Campaign.CharacterInstance.Characteristic,
|
characteristic: Campaign.CharacterInstance.Characteristic,
|
||||||
value: Int,
|
value: Int,
|
||||||
) {
|
) {
|
||||||
when {
|
when (characterInstanceId.prefix) {
|
||||||
campaign.characters.containsKey(characterInstanceId) -> {
|
Campaign.CharacterInstance.Id.PLAYER -> {
|
||||||
// fetch all the current campaign character
|
// fetch all the current campaign character
|
||||||
val characters = campaign.characters.toMutableMap()
|
val characters = campaign.characters.toMutableMap()
|
||||||
// update the corresponding character using the use case.
|
// update the corresponding character using the use case.
|
||||||
|
|
@ -158,7 +192,7 @@ class CampaignService(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
campaign.npcs.containsKey(characterInstanceId) -> {
|
Campaign.CharacterInstance.Id.NPC -> {
|
||||||
// fetch all the current campaign character
|
// fetch all the current campaign character
|
||||||
val npcs = campaign.npcs.toMutableMap()
|
val npcs = campaign.npcs.toMutableMap()
|
||||||
// update the corresponding character using the use case.
|
// update the corresponding character using the use case.
|
||||||
|
|
@ -179,8 +213,8 @@ class CampaignService(
|
||||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
characterInstanceId: Campaign.CharacterInstance.Id,
|
||||||
diminished: Int,
|
diminished: Int,
|
||||||
) {
|
) {
|
||||||
when {
|
when (characterInstanceId.prefix) {
|
||||||
campaign.characters.containsKey(characterInstanceId) -> {
|
Campaign.CharacterInstance.Id.PLAYER -> {
|
||||||
// fetch all the current campaign character
|
// fetch all the current campaign character
|
||||||
val characters = campaign.characters.toMutableMap()
|
val characters = campaign.characters.toMutableMap()
|
||||||
// update the corresponding character using the use case.
|
// update the corresponding character using the use case.
|
||||||
|
|
@ -194,7 +228,7 @@ class CampaignService(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
campaign.npcs.containsKey(characterInstanceId) -> {
|
Campaign.CharacterInstance.Id.NPC -> {
|
||||||
// fetch all the current campaign character
|
// fetch all the current campaign character
|
||||||
val npcs = campaign.npcs.toMutableMap()
|
val npcs = campaign.npcs.toMutableMap()
|
||||||
// update the corresponding character using the use case.
|
// update the corresponding character using the use case.
|
||||||
|
|
@ -210,6 +244,55 @@ class CampaignService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun toggleAlteration(
|
||||||
|
characterInstanceId: Campaign.CharacterInstance.Id,
|
||||||
|
alterationId: String,
|
||||||
|
) {
|
||||||
|
when (characterInstanceId.prefix) {
|
||||||
|
Campaign.CharacterInstance.Id.PLAYER -> {
|
||||||
|
// fetch all the current campaign character
|
||||||
|
val characters = campaign.characters.toMutableMap()
|
||||||
|
// update the corresponding character alterations
|
||||||
|
characters[characterInstanceId]?.let { character ->
|
||||||
|
characters[characterInstanceId] = character.copy(
|
||||||
|
alterations = character.alterations.toMutableList().also { alterations ->
|
||||||
|
if (alterations.contains(alterationId)) {
|
||||||
|
alterations.remove(alterationId)
|
||||||
|
} else {
|
||||||
|
alterations.add(alterationId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// save the campaign to the disk + update the flow.
|
||||||
|
store.save(
|
||||||
|
campaign = campaign.copy(characters = characters)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Campaign.CharacterInstance.Id.NPC -> {
|
||||||
|
// fetch all the current campaign character
|
||||||
|
val characters = campaign.npcs.toMutableMap()
|
||||||
|
// update the corresponding character alterations
|
||||||
|
characters[characterInstanceId]?.let { character ->
|
||||||
|
characters[characterInstanceId] = character.copy(
|
||||||
|
alterations = character.alterations.toMutableList().also { alterations ->
|
||||||
|
if (alterations.contains(alterationId)) {
|
||||||
|
alterations.remove(alterationId)
|
||||||
|
} else {
|
||||||
|
alterations.add(alterationId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// save the campaign to the disk + update the flow.
|
||||||
|
store.save(
|
||||||
|
campaign = campaign.copy(npcs = characters)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun updateToggleParty() {
|
suspend fun updateToggleParty() {
|
||||||
store.save(
|
store.save(
|
||||||
campaign = campaign.copy(
|
campaign = campaign.copy(
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class CampaignStore(
|
||||||
private val factory: CampaignJsonFactory,
|
private val factory: CampaignJsonFactory,
|
||||||
private val json: Json,
|
private val json: Json,
|
||||||
) {
|
) {
|
||||||
private val flow = MutableStateFlow(value = Campaign.EMPTY)
|
private val campaignFlow = MutableStateFlow(value = Campaign.EMPTY)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// create the directory if needed.
|
// create the directory if needed.
|
||||||
|
|
@ -27,28 +27,51 @@ class CampaignStore(
|
||||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
// load the initial data
|
// load the initial data
|
||||||
scope.launch {
|
scope.launch {
|
||||||
update()
|
updateCampaignFromDisk()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun campaignFlow(): StateFlow<Campaign> = flow
|
fun campaignFlow(): StateFlow<Campaign> = campaignFlow
|
||||||
|
|
||||||
suspend fun update() {
|
private fun updateCampaignFromDisk() {
|
||||||
flow.value = load()
|
campaignFlow.value = try {
|
||||||
}
|
loadCampaign()
|
||||||
|
|
||||||
suspend fun load(): Campaign {
|
|
||||||
return try {
|
|
||||||
val json = file().readText(charset = Charsets.UTF_8)
|
|
||||||
if (json.isBlank()) error("Campaign file is empty")
|
|
||||||
val campaign = this.json.decodeFromString<CampaignJson>(json)
|
|
||||||
factory.convertFromJson(campaign)
|
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
|
println(exception) // TODO proper exception handling
|
||||||
Campaign.EMPTY
|
Campaign.EMPTY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun save(campaign: Campaign) {
|
@Throws(
|
||||||
|
FileReadException::class,
|
||||||
|
JsonConversionException::class,
|
||||||
|
)
|
||||||
|
fun loadCampaign(): Campaign {
|
||||||
|
val file = file()
|
||||||
|
val json = try {
|
||||||
|
file.readText(charset = Charsets.UTF_8)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
throw FileReadException(root = exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guard, if the file is empty we load a default campaign.
|
||||||
|
if (json.isBlank()) return Campaign.EMPTY
|
||||||
|
|
||||||
|
val campaign = try {
|
||||||
|
val data = this.json.decodeFromString<CampaignJson>(json)
|
||||||
|
factory.convertFromJson(data)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
throw JsonConversionException(root = exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
return campaign
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(
|
||||||
|
JsonConversionException::class,
|
||||||
|
FileWriteException::class,
|
||||||
|
)
|
||||||
|
fun save(campaign: Campaign) {
|
||||||
// convert the data to json format
|
// convert the data to json format
|
||||||
val json = try {
|
val json = try {
|
||||||
factory.convertToJson(data = campaign).let(json::encodeToString)
|
factory.convertToJson(data = campaign).let(json::encodeToString)
|
||||||
|
|
@ -66,12 +89,13 @@ class CampaignStore(
|
||||||
throw FileWriteException(root = exception)
|
throw FileWriteException(root = exception)
|
||||||
}
|
}
|
||||||
// Update the dataflow.
|
// Update the dataflow.
|
||||||
flow.value = campaign
|
campaignFlow.value = campaign
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class CampaignStoreException(root: Exception) : Exception(root)
|
sealed class CampaignStoreException(root: Exception) : Exception(root)
|
||||||
class JsonConversionException(root: Exception) : CampaignStoreException(root)
|
class JsonConversionException(root: Exception) : CampaignStoreException(root)
|
||||||
class FileWriteException(root: Exception) : CampaignStoreException(root)
|
class FileWriteException(root: Exception) : CampaignStoreException(root)
|
||||||
|
class FileReadException(root: Exception) : CampaignStoreException(root)
|
||||||
|
|
||||||
private fun file(): File {
|
private fun file(): File {
|
||||||
return File("${pathProvider.campaignPath()}campaign.json")
|
return File("${pathProvider.campaignPath()}campaign.json")
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class CharacterSheetService(
|
||||||
) {
|
) {
|
||||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
private val sheets get() = sheetsFlow.value
|
private val sheets get() = sheetsFlow.value
|
||||||
private val sheetsFlow = store.characterSheetFlow()
|
private val sheetsFlow = store.characterSheetsFlow()
|
||||||
.map { entry -> entry.associateBy { character -> character.id } }
|
.map { entry -> entry.associateBy { character -> character.id } }
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = scope,
|
scope = scope,
|
||||||
|
|
@ -38,8 +38,8 @@ class CharacterSheetService(
|
||||||
return store.save(sheet = factory.convertFromJson(character))
|
return store.save(sheet = factory.convertFromJson(character))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteCharacterSheet(characterId: String): Boolean {
|
fun deleteCharacterSheet(characterSheetId: String): Boolean {
|
||||||
return store.delete(id = characterId)
|
return store.delete(id = characterSheetId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data manipulation through WebSocket.
|
// Data manipulation through WebSocket.
|
||||||
|
|
|
||||||
|
|
@ -20,74 +20,33 @@ class CharacterSheetStore(
|
||||||
private val json: Json,
|
private val json: Json,
|
||||||
) {
|
) {
|
||||||
private val directory = File(pathProvider.characterStorePath()).also { it.mkdirs() }
|
private val directory = File(pathProvider.characterStorePath()).also { it.mkdirs() }
|
||||||
private val flow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
|
private val characterSheetsFlow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
// build a coroutine scope for async calls
|
||||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
|
// load the initial data
|
||||||
scope.launch {
|
scope.launch {
|
||||||
flow.value = load()
|
updateCharacterSheets()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun characterSheetFlow(): StateFlow<List<CharacterSheet>> = flow
|
fun characterSheetsFlow(): StateFlow<List<CharacterSheet>> = characterSheetsFlow
|
||||||
|
|
||||||
@Throws(
|
private suspend fun updateCharacterSheets() {
|
||||||
CharacterSheetStoreException::class,
|
characterSheetsFlow.value = try {
|
||||||
FileWriteException::class,
|
loadCharacterSheets()
|
||||||
JsonConversionException::class,
|
|
||||||
)
|
|
||||||
fun save(sheet: CharacterSheet) {
|
|
||||||
// convert the character sheet into json format.
|
|
||||||
val json = try {
|
|
||||||
factory.convertToJson(sheet = sheet).let(json::encodeToString)
|
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
throw JsonConversionException(root = exception)
|
println(exception) // TODO proper exception handling
|
||||||
|
emptyList()
|
||||||
}
|
}
|
||||||
// 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)
|
|
||||||
val deleted = file.delete()
|
|
||||||
if (deleted) {
|
|
||||||
flow.value = flow.value.toMutableList()
|
|
||||||
.also { data ->
|
|
||||||
data.removeIf { it.id == id }
|
|
||||||
}
|
|
||||||
.sortedBy {
|
|
||||||
it.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return deleted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(
|
@Throws(
|
||||||
CharacterSheetStoreException::class,
|
|
||||||
FileReadException::class,
|
FileReadException::class,
|
||||||
JsonConversionException::class,
|
JsonConversionException::class,
|
||||||
)
|
)
|
||||||
suspend fun load(): List<CharacterSheet> {
|
suspend fun loadCharacterSheets(): List<CharacterSheet> {
|
||||||
return directory
|
return directory
|
||||||
.listFiles()
|
.listFiles()
|
||||||
?.mapNotNull { file ->
|
?.mapNotNull { file ->
|
||||||
|
|
@ -111,6 +70,56 @@ class CharacterSheetStore(
|
||||||
?: emptyList()
|
?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(
|
||||||
|
FileWriteException::class,
|
||||||
|
JsonConversionException::class,
|
||||||
|
)
|
||||||
|
fun save(sheet: CharacterSheet) {
|
||||||
|
// convert the character sheet into json format.
|
||||||
|
val json = try {
|
||||||
|
factory.convertToJson(sheet = sheet).let(json::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.
|
||||||
|
characterSheetsFlow.value = characterSheetsFlow.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)
|
||||||
|
val deleted = file.delete()
|
||||||
|
if (deleted) {
|
||||||
|
characterSheetsFlow.value = characterSheetsFlow.value.toMutableList()
|
||||||
|
.also { data ->
|
||||||
|
data.removeIf { it.id == id }
|
||||||
|
}
|
||||||
|
.sortedBy {
|
||||||
|
it.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deleted
|
||||||
|
}
|
||||||
|
|
||||||
private fun characterSheetFile(id: String): File {
|
private fun characterSheetFile(id: String): File {
|
||||||
return File("${pathProvider.characterStorePath()}${id}.json")
|
return File("${pathProvider.characterStorePath()}${id}.json")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@ import com.pixelized.server.lwa.model.character.CharacterSheetService
|
||||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||||
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
|
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.CampaignMessage
|
import com.pixelized.shared.lwa.protocol.websocket.CampaignMessage
|
||||||
|
import com.pixelized.shared.lwa.protocol.websocket.CampaignMessage.ToggleActiveAlteration
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.GameEvent
|
import com.pixelized.shared.lwa.protocol.websocket.GameEvent
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent
|
import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.RestSynchronisation
|
import com.pixelized.shared.lwa.protocol.websocket.RestSynchronisation
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.RollMessage
|
import com.pixelized.shared.lwa.protocol.websocket.RollMessage
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
|
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.ToggleActiveAlteration
|
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.UpdateSkillUsageMessage
|
import com.pixelized.shared.lwa.protocol.websocket.UpdateSkillUsageMessage
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
|
||||||
|
|
@ -45,6 +45,11 @@ class Engine(
|
||||||
characterInstanceId = instanceId,
|
characterInstanceId = instanceId,
|
||||||
diminished = message.diminished,
|
diminished = message.diminished,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is ToggleActiveAlteration -> campaignService.toggleAlteration(
|
||||||
|
characterInstanceId = instanceId,
|
||||||
|
alterationId = message.alterationId,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
package com.pixelized.server.lwa.server
|
package com.pixelized.server.lwa.server
|
||||||
|
|
||||||
|
|
||||||
import com.pixelized.server.lwa.server.rest.alteration.getActiveAlteration
|
import com.pixelized.server.lwa.server.rest.alteration.getAlterations
|
||||||
import com.pixelized.server.lwa.server.rest.alteration.getAlteration
|
|
||||||
import com.pixelized.server.lwa.server.rest.alteration.putActiveAlteration
|
|
||||||
import com.pixelized.server.lwa.server.rest.campaign.removeCampaignCharacter
|
|
||||||
import com.pixelized.server.lwa.server.rest.campaign.deleteCampaignNpc
|
import com.pixelized.server.lwa.server.rest.campaign.deleteCampaignNpc
|
||||||
import com.pixelized.server.lwa.server.rest.campaign.getCampaign
|
import com.pixelized.server.lwa.server.rest.campaign.getCampaign
|
||||||
import com.pixelized.server.lwa.server.rest.campaign.putCampaignCharacter
|
import com.pixelized.server.lwa.server.rest.campaign.putCampaignCharacter
|
||||||
import com.pixelized.server.lwa.server.rest.campaign.putCampaignNpc
|
import com.pixelized.server.lwa.server.rest.campaign.putCampaignNpc
|
||||||
import com.pixelized.server.lwa.server.rest.campaign.putCampaignScene
|
import com.pixelized.server.lwa.server.rest.campaign.putCampaignScene
|
||||||
|
import com.pixelized.server.lwa.server.rest.campaign.putToggleAlteration
|
||||||
|
import com.pixelized.server.lwa.server.rest.campaign.removeCampaignCharacter
|
||||||
import com.pixelized.server.lwa.server.rest.character.deleteCharacter
|
import com.pixelized.server.lwa.server.rest.character.deleteCharacter
|
||||||
import com.pixelized.server.lwa.server.rest.character.getCharacter
|
import com.pixelized.server.lwa.server.rest.character.getCharacter
|
||||||
import com.pixelized.server.lwa.server.rest.character.getCharacters
|
import com.pixelized.server.lwa.server.rest.character.getCharacters
|
||||||
|
|
@ -86,7 +85,7 @@ class LocalServer {
|
||||||
val frame = Frame.Text(text = data)
|
val frame = Frame.Text(text = data)
|
||||||
try {
|
try {
|
||||||
send(frame)
|
send(frame)
|
||||||
} catch (exception : Exception) {
|
} catch (exception: Exception) {
|
||||||
// TODO
|
// TODO
|
||||||
println("WebSocket exception: ${exception.localizedMessage}")
|
println("WebSocket exception: ${exception.localizedMessage}")
|
||||||
}
|
}
|
||||||
|
|
@ -112,6 +111,10 @@ class LocalServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
get(
|
||||||
|
path = "/alterations",
|
||||||
|
body = engine.getAlterations(),
|
||||||
|
)
|
||||||
get(
|
get(
|
||||||
path = "/characters",
|
path = "/characters",
|
||||||
body = engine.getCharacters(),
|
body = engine.getCharacters(),
|
||||||
|
|
@ -135,6 +138,10 @@ class LocalServer {
|
||||||
path = "",
|
path = "",
|
||||||
body = engine.getCampaign(),
|
body = engine.getCampaign(),
|
||||||
)
|
)
|
||||||
|
put(
|
||||||
|
path = "/toggleAlteration",
|
||||||
|
body = engine.putToggleAlteration(),
|
||||||
|
)
|
||||||
route(path = "/character") {
|
route(path = "/character") {
|
||||||
put(
|
put(
|
||||||
path = "/update",
|
path = "/update",
|
||||||
|
|
@ -160,20 +167,6 @@ class LocalServer {
|
||||||
body = engine.putCampaignScene(),
|
body = engine.putCampaignScene(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
route(path = "/alterations") {
|
|
||||||
get(
|
|
||||||
path = "",
|
|
||||||
body = engine.getAlteration(),
|
|
||||||
)
|
|
||||||
get(
|
|
||||||
path = "/active",
|
|
||||||
body = engine.getActiveAlteration(),
|
|
||||||
)
|
|
||||||
put(
|
|
||||||
path = "/active/toggle",
|
|
||||||
body = engine.putActiveAlteration(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package com.pixelized.server.lwa.server.rest.alteration
|
|
||||||
|
|
||||||
import com.pixelized.server.lwa.server.Engine
|
|
||||||
import com.pixelized.server.lwa.utils.extentions.characterInstanceId
|
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import io.ktor.server.response.respond
|
|
||||||
import io.ktor.server.response.respondText
|
|
||||||
|
|
||||||
fun Engine.getActiveAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
|
|
||||||
return {
|
|
||||||
try {
|
|
||||||
// get the query parameter
|
|
||||||
val characterInstanceId = call.queryParameters.characterInstanceId
|
|
||||||
// fetch the data from the service
|
|
||||||
val data = alterationService.active(characterInstanceId = characterInstanceId)
|
|
||||||
// respond to the client.
|
|
||||||
call.respond(data)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
call.respondText(
|
|
||||||
text = exception.localizedMessage,
|
|
||||||
status = HttpStatusCode.UnprocessableEntity,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.pixelized.server.lwa.server.rest.alteration
|
||||||
import com.pixelized.server.lwa.server.Engine
|
import com.pixelized.server.lwa.server.Engine
|
||||||
import io.ktor.server.response.respond
|
import io.ktor.server.response.respond
|
||||||
|
|
||||||
fun Engine.getAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
|
fun Engine.getAlterations(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
|
||||||
return {
|
return {
|
||||||
call.respond(alterationService.alterations())
|
call.respond(alterationService.alterations())
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
package com.pixelized.server.lwa.server.rest.alteration
|
package com.pixelized.server.lwa.server.rest.campaign
|
||||||
|
|
||||||
import com.pixelized.server.lwa.server.Engine
|
import com.pixelized.server.lwa.server.Engine
|
||||||
import com.pixelized.server.lwa.utils.extentions.characterInstanceId
|
import com.pixelized.server.lwa.utils.extentions.characterInstanceId
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.ToggleActiveAlteration
|
import com.pixelized.shared.lwa.protocol.websocket.CampaignMessage
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.server.request.receive
|
import io.ktor.server.request.receive
|
||||||
import io.ktor.server.response.respondText
|
import io.ktor.server.response.respondText
|
||||||
|
|
||||||
fun Engine.putActiveAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
|
fun Engine.putToggleAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
|
||||||
return {
|
return {
|
||||||
try {
|
try {
|
||||||
// get the query parameter
|
// get the query parameter
|
||||||
|
|
@ -17,30 +17,31 @@ fun Engine.putActiveAlteration(): suspend io.ktor.server.routing.RoutingContext.
|
||||||
val alterationId = call.receive<String>()
|
val alterationId = call.receive<String>()
|
||||||
|
|
||||||
// Update the alteration
|
// Update the alteration
|
||||||
val updated = alterationService.toggleActiveAlteration(
|
campaignService.toggleAlteration(
|
||||||
characterInstanceId = characterInstanceId,
|
characterInstanceId = characterInstanceId,
|
||||||
alterationId = alterationId,
|
alterationId = alterationId,
|
||||||
)
|
)
|
||||||
if (!updated) {
|
|
||||||
error("Unexpected error occurred when toggling the alteration (id:$alterationId) for the character (id:$characterInstanceId)")
|
|
||||||
}
|
|
||||||
// build the Http response & send it
|
// build the Http response & send it
|
||||||
call.respondText(
|
call.respondText(
|
||||||
text = "$HttpStatusCode.Accepted",
|
text = "$HttpStatusCode.Accepted",
|
||||||
status = HttpStatusCode.Accepted,
|
status = HttpStatusCode.Accepted,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val isAlterationActive = campaignService.campaign()
|
||||||
|
.instances[characterInstanceId]
|
||||||
|
?.alterations
|
||||||
|
?.contains(alterationId)
|
||||||
|
?: false
|
||||||
|
|
||||||
// share the modification to all client through the websocket.
|
// share the modification to all client through the websocket.
|
||||||
webSocket.emit(
|
webSocket.emit(
|
||||||
ToggleActiveAlteration(
|
CampaignMessage.ToggleActiveAlteration(
|
||||||
timestamp = System.currentTimeMillis(),
|
timestamp = System.currentTimeMillis(),
|
||||||
prefix = characterInstanceId.prefix,
|
prefix = characterInstanceId.prefix,
|
||||||
characterSheetId = characterInstanceId.characterSheetId,
|
characterSheetId = characterInstanceId.characterSheetId,
|
||||||
instanceId = characterInstanceId.instanceId,
|
instanceId = characterInstanceId.instanceId,
|
||||||
alterationId = alterationId,
|
alterationId = alterationId,
|
||||||
active = alterationService.isAlterationActive(
|
active = isAlterationActive,
|
||||||
characterInstanceId = characterInstanceId,
|
|
||||||
alterationId = alterationId
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
|
|
@ -11,9 +11,10 @@ fun Engine.deleteCharacter(): suspend io.ktor.server.routing.RoutingContext.() -
|
||||||
val characterSheetId = call.parameters.characterSheetId
|
val characterSheetId = call.parameters.characterSheetId
|
||||||
|
|
||||||
val deleted = characterService.deleteCharacterSheet(
|
val deleted = characterService.deleteCharacterSheet(
|
||||||
characterId = characterSheetId
|
characterSheetId = characterSheetId
|
||||||
|
) && campaignService.removeInstance(
|
||||||
|
characterSheetId = characterSheetId,
|
||||||
)
|
)
|
||||||
// TODO campaign & alteration cleanup.
|
|
||||||
|
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
call.respondText(
|
call.respondText(
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,11 @@ data class Campaign(
|
||||||
val scene: Scene,
|
val scene: Scene,
|
||||||
val options: Options,
|
val options: Options,
|
||||||
) {
|
) {
|
||||||
|
val instances = characters + npcs
|
||||||
|
|
||||||
data class CharacterInstance(
|
data class CharacterInstance(
|
||||||
val characteristic: Map<Characteristic, Int>,
|
val characteristic: Map<Characteristic, Int>,
|
||||||
|
val alterations: List<String>,
|
||||||
val diminished: Int,
|
val diminished: Int,
|
||||||
) {
|
) {
|
||||||
data class Id(
|
data class Id(
|
||||||
|
|
@ -39,6 +42,7 @@ data class Campaign(
|
||||||
companion object {
|
companion object {
|
||||||
fun empty() = CharacterInstance(
|
fun empty() = CharacterInstance(
|
||||||
characteristic = emptyMap(),
|
characteristic = emptyMap(),
|
||||||
|
alterations = emptyList(),
|
||||||
diminished = 0,
|
diminished = 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ data class CampaignJsonV1(
|
||||||
@Serializable
|
@Serializable
|
||||||
data class CharacterInstanceJsonV1(
|
data class CharacterInstanceJsonV1(
|
||||||
val characteristic: Map<CharacteristicV1, Int>,
|
val characteristic: Map<CharacteristicV1, Int>,
|
||||||
|
val alterations: List<String>?,
|
||||||
val diminished: Int?,
|
val diminished: Int?,
|
||||||
) : CampaignJson.CharacterInstanceJson {
|
) : CampaignJson.CharacterInstanceJson {
|
||||||
enum class CharacteristicV1 : CampaignJson.CharacterInstanceJson.CharacteristicJson {
|
enum class CharacteristicV1 : CampaignJson.CharacterInstanceJson.CharacteristicJson {
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ class CampaignJsonFactory(
|
||||||
characteristic = data.characteristic
|
characteristic = data.characteristic
|
||||||
.map { char -> convertToJson(characteristic = char.key) to char.value }
|
.map { char -> convertToJson(characteristic = char.key) to char.value }
|
||||||
.toMap(),
|
.toMap(),
|
||||||
|
alterations = data.alterations,
|
||||||
diminished = data.diminished,
|
diminished = data.diminished,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ class CampaignJsonV1Factory {
|
||||||
characteristic = characterInstanceJson.characteristic
|
characteristic = characterInstanceJson.characteristic
|
||||||
.mapKeys { convertFromV1(characteristicJson = it.key) }
|
.mapKeys { convertFromV1(characteristicJson = it.key) }
|
||||||
.toMap(),
|
.toMap(),
|
||||||
|
alterations = characterInstanceJson.alterations ?: emptyList(),
|
||||||
diminished = characterInstanceJson.diminished ?: 0,
|
diminished = characterInstanceJson.diminished ?: 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,4 +28,14 @@ sealed interface CampaignMessage : SocketMessage, CharacterInstanceIdMessage {
|
||||||
override val instanceId: Int,
|
override val instanceId: Int,
|
||||||
val diminished: Int,
|
val diminished: Int,
|
||||||
) : CampaignMessage
|
) : CampaignMessage
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ToggleActiveAlteration(
|
||||||
|
override val timestamp: Long,
|
||||||
|
override val characterSheetId: String,
|
||||||
|
override val prefix: Char,
|
||||||
|
override val instanceId: Int,
|
||||||
|
val alterationId: String,
|
||||||
|
val active: Boolean,
|
||||||
|
) : CampaignMessage, CharacterInstanceIdMessage
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
package com.pixelized.shared.lwa.protocol.websocket
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ToggleActiveAlteration(
|
|
||||||
override val timestamp: Long,
|
|
||||||
override val characterSheetId: String,
|
|
||||||
override val prefix: Char,
|
|
||||||
override val instanceId: Int,
|
|
||||||
val alterationId: String,
|
|
||||||
val active: Boolean,
|
|
||||||
) : SocketMessage, CharacterInstanceIdMessage
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue