Refactor project data to allow server handling.
This commit is contained in:
parent
3c8eecdab5
commit
1e5f0d88ae
58 changed files with 742 additions and 469 deletions
|
|
@ -8,6 +8,7 @@ kotlin {
|
|||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
implementation(libs.koin.core)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
package com.pixelized.server.lwa
|
||||
|
||||
const val SERVER_PORT = 16030
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package com.pixelized.server.lwa.protocol
|
||||
|
||||
enum class MessageType {
|
||||
Roll
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.pixelized.shared.lwa
|
||||
|
||||
const val SERVER_PORT = 16030
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.pixelized.shared.lwa
|
||||
|
||||
import com.pixelized.shared.lwa.model.characterSheet.model.CharacterSheetJsonFactory
|
||||
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.module.dsl.factoryOf
|
||||
import org.koin.dsl.module
|
||||
|
||||
val sharedModuleDependencies
|
||||
get() = listOf(
|
||||
toolsDependencies,
|
||||
factoryDependencies,
|
||||
useCaseDependencies,
|
||||
)
|
||||
|
||||
val toolsDependencies
|
||||
get() = module {
|
||||
factory {
|
||||
Json {
|
||||
explicitNulls = false
|
||||
prettyPrint = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val factoryDependencies
|
||||
get() = module {
|
||||
factoryOf(::CharacterSheetJsonFactory)
|
||||
}
|
||||
|
||||
val useCaseDependencies
|
||||
get() = module {
|
||||
factoryOf(::CharacterSheetUseCase)
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.pixelized.shared.lwa
|
||||
|
||||
enum class OperatingSystem(
|
||||
val home: String = System.getProperty("user.home"),
|
||||
) {
|
||||
Windows,
|
||||
Macintosh;
|
||||
|
||||
companion object {
|
||||
val current: OperatingSystem = run {
|
||||
val name = System.getProperty("os.name")
|
||||
when {
|
||||
name.contains(other = "win", ignoreCase = true) -> Windows
|
||||
name.contains(other = "mac", ignoreCase = true) -> Macintosh
|
||||
else -> error("Unsupported operating system: $name")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun storePath(
|
||||
os: OperatingSystem = OperatingSystem.current,
|
||||
): String {
|
||||
return when (os) {
|
||||
OperatingSystem.Windows -> "${os.home}\\AppData\\Roaming\\Pixelized\\"
|
||||
OperatingSystem.Macintosh -> "${os.home}/Library/Pixelized/"
|
||||
}
|
||||
}
|
||||
|
||||
fun characterStorePath(
|
||||
os: OperatingSystem = OperatingSystem.current,
|
||||
): String {
|
||||
return when (os) {
|
||||
OperatingSystem.Windows -> "${storePath(os = os)}characters\\"
|
||||
OperatingSystem.Macintosh -> "${storePath(os = os)}characters/"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.pixelized.shared.lwa.model.campaign
|
||||
|
||||
import com.pixelized.shared.lwa.model.campaign.model.CampaignFactory
|
||||
|
||||
class CampaignRepository(
|
||||
private val factory: CampaignFactory,
|
||||
) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.pixelized.shared.lwa.model.campaign.model
|
||||
|
||||
data class Campaign(
|
||||
val characters: List<CharacterInstance>,
|
||||
) {
|
||||
data class CharacterInstance(
|
||||
val damage: Int,
|
||||
val usedPower: Int,
|
||||
val usedMovement: Int,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.pixelized.shared.lwa.model.campaign.model
|
||||
|
||||
class CampaignFactory {
|
||||
fun convertFromJson(
|
||||
json: CampaignJson,
|
||||
): Campaign {
|
||||
return when (json) {
|
||||
is CampaignJsonV1 -> convertFromV1(json = json)
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertFromV1(
|
||||
json: CampaignJsonV1,
|
||||
): Campaign {
|
||||
return Campaign(
|
||||
characters = json.characters.map {
|
||||
Campaign.CharacterInstance(
|
||||
damage = it.damage,
|
||||
usedPower = it.usedPower,
|
||||
usedMovement = it.usedMovement,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertToJson(
|
||||
data: Campaign,
|
||||
): CampaignJson {
|
||||
return CampaignJsonV1(
|
||||
characters = data.characters.map {
|
||||
CampaignJsonV1.CharacterInstanceJson(
|
||||
damage = it.damage,
|
||||
usedPower = it.usedPower,
|
||||
usedMovement = it.usedMovement,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.pixelized.shared.lwa.model.campaign.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed interface CampaignJson
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.pixelized.shared.lwa.model.campaign.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CampaignJsonV1(
|
||||
val characters: List<CharacterInstanceJson>,
|
||||
) : CampaignJson {
|
||||
@Serializable
|
||||
data class CharacterInstanceJson(
|
||||
val damage: Int,
|
||||
val usedPower: Int,
|
||||
val usedMovement: Int,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||
|
||||
data class CharacterSheet(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val portrait: String?,
|
||||
val thumbnail: String?,
|
||||
// characteristics
|
||||
val strength: Int,
|
||||
val dexterity: Int,
|
||||
val constitution: Int,
|
||||
val height: Int,
|
||||
val intelligence: Int,
|
||||
val power: Int,
|
||||
val charisma: Int,
|
||||
// sub characteristics
|
||||
val overrideMovement: Boolean,
|
||||
val movement: Int,
|
||||
val currentHp: Int,
|
||||
val overrideMaxHp: Boolean,
|
||||
val maxHp: Int,
|
||||
val currentPp: Int,
|
||||
val overrideMaxPP: Boolean,
|
||||
val maxPp: Int,
|
||||
val overrideDamageBonus: Boolean,
|
||||
val damageBonus: String,
|
||||
val overrideArmor: Boolean,
|
||||
val armor: Int,
|
||||
val overrideLearning: Boolean,
|
||||
val learning: Int,
|
||||
val overrideHpGrow: Boolean,
|
||||
val hpGrow: Int,
|
||||
// skills
|
||||
val commonSkills: List<Skill>,
|
||||
val specialSkills: List<Skill>,
|
||||
val magicSkills: List<Skill>,
|
||||
// actions
|
||||
val actions: List<Roll>,
|
||||
) {
|
||||
data class Skill(
|
||||
val id: String,
|
||||
val label: String,
|
||||
val description: String?,
|
||||
val base: String,
|
||||
val bonus: String?,
|
||||
val level: String?,
|
||||
val occupation: Boolean,
|
||||
val used: Boolean,
|
||||
)
|
||||
|
||||
data class Roll(
|
||||
val id: String,
|
||||
val label: String,
|
||||
val roll: String,
|
||||
)
|
||||
|
||||
object CommonSkillId {
|
||||
const val COMBAT_ID = "COMBAT"
|
||||
const val DODGE_ID = "DODGE"
|
||||
const val GRAB_ID = "GRAB"
|
||||
const val THROW_ID = "THROW"
|
||||
const val ATHLETICS_ID = "ATHLETICS"
|
||||
const val ACROBATICS_ID = "ACROBATICS"
|
||||
const val PERCEPTION_ID = "PERCEPTION"
|
||||
const val SEARCH_ID = "SEARCH"
|
||||
const val EMPATHY_ID = "EMPATHY"
|
||||
const val PERSUASION_ID = "PERSUASION"
|
||||
const val INTIMIDATION_ID = "INTIMIDATION"
|
||||
const val SPIEL_ID = "SPIEL"
|
||||
const val BARGAIN_ID = "BARGAIN"
|
||||
const val DISCRETION_ID = "DISCRETION"
|
||||
const val SLEIGHT_OF_HAND_ID = "SLEIGHT_OF_HAND"
|
||||
const val AID_ID = "AID"
|
||||
}
|
||||
|
||||
object CharacteristicId {
|
||||
const val STR = "STR"
|
||||
const val DEX = "DEX"
|
||||
const val CON = "CON"
|
||||
const val HEI = "HEI"
|
||||
const val INT = "INT"
|
||||
const val POW = "POW"
|
||||
const val CHA = "CHA"
|
||||
const val MOV = "MOV"
|
||||
const val HP = "HP"
|
||||
const val PP = "PP"
|
||||
const val DMG = "DMG"
|
||||
const val ARMOR = "ARMOR"
|
||||
const val LB = "LEARNING"
|
||||
const val GHP = "HP_GROW"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed interface CharacterSheetJson
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||
|
||||
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
|
||||
|
||||
|
||||
class CharacterSheetJsonFactory(
|
||||
private val characterSheetUseCase: CharacterSheetUseCase,
|
||||
) {
|
||||
suspend fun convertFromJson(
|
||||
json: CharacterSheetJson,
|
||||
): CharacterSheet {
|
||||
return when (json) {
|
||||
is CharacterSheetJsonV1 -> convertFromV1(json = json)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun convertFromV1(
|
||||
json: CharacterSheetJsonV1,
|
||||
): CharacterSheet = characterSheetUseCase.run {
|
||||
CharacterSheet(
|
||||
id = json.id,
|
||||
name = json.name,
|
||||
portrait = json.portrait,
|
||||
thumbnail = json.thumbnail,
|
||||
strength = json.strength,
|
||||
dexterity = json.dexterity,
|
||||
constitution = json.constitution,
|
||||
height = json.height,
|
||||
intelligence = json.intelligence,
|
||||
power = json.power,
|
||||
charisma = json.charisma,
|
||||
overrideMovement = json.movement != null,
|
||||
movement = json.movement ?: defaultMovement(),
|
||||
currentHp = json.currentHp,
|
||||
overrideMaxHp = json.maxHp != null,
|
||||
maxHp = json.maxHp ?: defaultMaxHp(
|
||||
constitution = json.constitution,
|
||||
height = json.height,
|
||||
),
|
||||
currentPp = json.currentPP,
|
||||
overrideMaxPP = json.maxPP != null,
|
||||
maxPp = json.maxPP ?: defaultMaxPower(power = json.power),
|
||||
overrideDamageBonus = json.damageBonus != null,
|
||||
damageBonus = json.damageBonus ?: defaultDamageBonus(
|
||||
strength = json.strength,
|
||||
height = json.height,
|
||||
),
|
||||
overrideArmor = json.armor != null,
|
||||
armor = json.armor ?: defaultArmor(),
|
||||
overrideLearning = json.learning != null,
|
||||
learning = json.learning ?: defaultLearning(intelligence = json.intelligence),
|
||||
overrideHpGrow = json.hpGrowf != null,
|
||||
hpGrow = json.hpGrowf ?: defaultHpGrow(constitution = json.constitution),
|
||||
commonSkills = json.skills.map {
|
||||
CharacterSheet.Skill(
|
||||
id = it.id,
|
||||
label = it.label,
|
||||
description = it.description,
|
||||
base = it.base,
|
||||
bonus = it.bonus,
|
||||
level = it.level,
|
||||
occupation = it.occupation,
|
||||
used = it.used,
|
||||
)
|
||||
},
|
||||
specialSkills = json.occupations.map {
|
||||
CharacterSheet.Skill(
|
||||
id = it.id,
|
||||
label = it.label,
|
||||
description = it.description,
|
||||
base = it.base,
|
||||
bonus = it.bonus,
|
||||
level = it.level,
|
||||
occupation = it.occupation,
|
||||
used = it.used,
|
||||
)
|
||||
},
|
||||
magicSkills = json.magics.map {
|
||||
CharacterSheet.Skill(
|
||||
id = it.id,
|
||||
label = it.label,
|
||||
description = it.description,
|
||||
base = it.base,
|
||||
bonus = it.bonus,
|
||||
level = it.level,
|
||||
occupation = it.occupation,
|
||||
used = it.used,
|
||||
)
|
||||
},
|
||||
actions = json.rolls.map {
|
||||
CharacterSheet.Roll(
|
||||
id = it.id,
|
||||
label = it.label,
|
||||
roll = it.roll,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun convertToJson(
|
||||
sheet: CharacterSheet,
|
||||
): CharacterSheetJson {
|
||||
val json = CharacterSheetJsonV1(
|
||||
id = sheet.id,
|
||||
name = sheet.name,
|
||||
thumbnail = sheet.thumbnail,
|
||||
portrait = sheet.portrait,
|
||||
strength = sheet.strength,
|
||||
dexterity = sheet.dexterity,
|
||||
constitution = sheet.constitution,
|
||||
height = sheet.height,
|
||||
intelligence = sheet.intelligence,
|
||||
power = sheet.power,
|
||||
charisma = sheet.charisma,
|
||||
movement = if (sheet.overrideMovement) sheet.movement else null,
|
||||
currentHp = sheet.currentHp,
|
||||
maxHp = if (sheet.overrideMaxHp) sheet.maxHp else null,
|
||||
currentPP = sheet.currentPp,
|
||||
maxPP = if (sheet.overrideMaxPP) sheet.maxPp else null,
|
||||
damageBonus = if (sheet.overrideDamageBonus) sheet.damageBonus else null,
|
||||
armor = if (sheet.overrideArmor) sheet.armor else null,
|
||||
learning = if (sheet.overrideLearning) sheet.learning else null,
|
||||
hpGrowf = if (sheet.overrideHpGrow) sheet.hpGrow else null,
|
||||
skills = sheet.commonSkills.map {
|
||||
CharacterSheetJsonV1.Skill(
|
||||
id = it.id,
|
||||
label = it.label,
|
||||
description = null,
|
||||
base = it.base,
|
||||
bonus = it.bonus,
|
||||
level = it.level,
|
||||
occupation = it.occupation,
|
||||
used = it.used,
|
||||
)
|
||||
},
|
||||
occupations = sheet.specialSkills.map {
|
||||
CharacterSheetJsonV1.Skill(
|
||||
id = it.id,
|
||||
label = it.label,
|
||||
description = it.description,
|
||||
base = it.base,
|
||||
bonus = it.bonus,
|
||||
level = it.level,
|
||||
occupation = it.occupation,
|
||||
used = it.used,
|
||||
)
|
||||
},
|
||||
magics = sheet.magicSkills.map {
|
||||
CharacterSheetJsonV1.Skill(
|
||||
id = it.id,
|
||||
label = it.label,
|
||||
description = it.description,
|
||||
base = it.base,
|
||||
bonus = it.bonus,
|
||||
level = it.level,
|
||||
occupation = it.occupation,
|
||||
used = it.used,
|
||||
)
|
||||
},
|
||||
rolls = sheet.actions.map {
|
||||
CharacterSheetJsonV1.Roll(
|
||||
id = it.id,
|
||||
label = it.label,
|
||||
roll = it.roll,
|
||||
)
|
||||
},
|
||||
)
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.pixelized.shared.lwa.model.characterSheet.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CharacterSheetJsonV1(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val portrait: String?,
|
||||
val thumbnail: String?,
|
||||
// characteristics
|
||||
val strength: Int,
|
||||
val dexterity: Int,
|
||||
val constitution: Int,
|
||||
val height: Int,
|
||||
val intelligence: Int,
|
||||
val power: Int,
|
||||
val charisma: Int,
|
||||
// sub characteristics
|
||||
val movement: Int?,
|
||||
val currentHp: Int,
|
||||
val maxHp: Int?,
|
||||
val currentPP: Int,
|
||||
val maxPP: Int?,
|
||||
val damageBonus: String?,
|
||||
val armor: Int?,
|
||||
val learning: Int?,
|
||||
val hpGrowf: Int?,
|
||||
// skills
|
||||
val skills: List<Skill>,
|
||||
// occupations
|
||||
val occupations: List<Skill>,
|
||||
// magic skill
|
||||
val magics: List<Skill>,
|
||||
// attack
|
||||
val rolls: List<Roll>,
|
||||
) : CharacterSheetJson {
|
||||
|
||||
@Serializable
|
||||
data class Skill(
|
||||
val id: String,
|
||||
val label: String,
|
||||
val description: String?,
|
||||
val base: String,
|
||||
val bonus: String?,
|
||||
val level: String?,
|
||||
val occupation: Boolean,
|
||||
val used: Boolean,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Roll(
|
||||
val id: String,
|
||||
val label: String,
|
||||
val roll: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.server.lwa.protocol
|
||||
package com.pixelized.shared.lwa.protocol
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.pixelized.shared.lwa.protocol
|
||||
|
||||
enum class MessageType {
|
||||
Roll
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.server.lwa.protocol.roll
|
||||
package com.pixelized.shared.lwa.protocol.roll
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package com.pixelized.shared.lwa.usecase
|
||||
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.max
|
||||
|
||||
class CharacterSheetUseCase {
|
||||
|
||||
fun normalize(value: Int): Int {
|
||||
return value - value % 5 // (truncate(value.toFloat() / 5f) * 5f).toInt()
|
||||
}
|
||||
|
||||
fun defaultMovement(): Int = 10
|
||||
|
||||
fun defaultMaxHp(
|
||||
constitution: Int,
|
||||
height: Int,
|
||||
): Int {
|
||||
return (ceil((constitution + height) / 2f).toInt())
|
||||
}
|
||||
|
||||
fun defaultMaxPower(
|
||||
power: Int,
|
||||
): Int {
|
||||
return power
|
||||
}
|
||||
|
||||
fun defaultDamageBonus(
|
||||
strength: Int,
|
||||
height: Int,
|
||||
): String {
|
||||
return defaultDamageBonus(sum = strength + height)
|
||||
}
|
||||
|
||||
fun defaultDamageBonus(
|
||||
sum: Int,
|
||||
): String {
|
||||
return when {
|
||||
sum < 12 -> "-1d6"
|
||||
sum in 12..17 -> "-1d4"
|
||||
sum in 18..22 -> "+0"
|
||||
sum in 23..29 -> "+1d4"
|
||||
sum in 30..39 -> "+1d6"
|
||||
else -> "+2d6"
|
||||
}
|
||||
}
|
||||
|
||||
fun defaultArmor(): Int = 0
|
||||
|
||||
fun defaultLearning(intelligence: Int): Int {
|
||||
return max(0, (intelligence - 10) * 2)
|
||||
}
|
||||
|
||||
fun defaultHpGrow(constitution: Int): Int {
|
||||
return (constitution / 3)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue