LevelUp feature.

This commit is contained in:
Thomas Andres Gomez 2025-03-07 15:49:36 +01:00
parent 84515e6d57
commit 16b2b49f03
75 changed files with 2532 additions and 777 deletions

View file

@ -50,6 +50,8 @@ class AlteredCharacterSheet(
val name: String = sheet.name
val shouldLevelUp: Boolean = sheet.shouldLevelUp
val portrait: String?
get() = alterations[PORTRAIT]
?.firstNotNullOfOrNull { (it.expression as? Expression.UrlExpression)?.url }
@ -104,7 +106,7 @@ class AlteredCharacterSheet(
val damageBonus: String
get() {
val initial = sheetUseCase.damageBonus(
val initial = sheetUseCase.meleeBonusDamage(
strength = strength,
height = height,
)
@ -128,8 +130,7 @@ class AlteredCharacterSheet(
private fun List<FieldAlteration>?.sum() = this?.sumOf {
expressionUseCase.computeExpression(
sheet = sheet,
alterations = alterations,
sheet = this@AlteredCharacterSheet,
expression = it.expression
)
} ?: 0

View file

@ -6,6 +6,7 @@ data class CharacterSheet(
val portrait: String?,
val thumbnail: String?,
val level: Int,
val shouldLevelUp: Boolean,
// characteristics
val strength: Int,
val dexterity: Int,

View file

@ -24,6 +24,7 @@ class CharacterSheetJsonFactory(
portrait = json.portrait,
thumbnail = json.thumbnail,
level = json.level,
shouldLevelUp = json.shouldLevelUp ?: false,
strength = json.strength,
dexterity = json.dexterity,
constitution = json.constitution,
@ -100,6 +101,7 @@ class CharacterSheetJsonFactory(
thumbnail = sheet.thumbnail,
portrait = sheet.portrait,
level = sheet.level,
shouldLevelUp = sheet.shouldLevelUp,
strength = sheet.strength,
dexterity = sheet.dexterity,
constitution = sheet.constitution,

View file

@ -9,6 +9,7 @@ data class CharacterSheetJsonV1(
val portrait: String?,
val thumbnail: String?,
val level: Int,
val shouldLevelUp: Boolean?,
// characteristics
val strength: Int,
val dexterity: Int,

View file

@ -59,6 +59,14 @@ sealed interface Expression {
}
}
data class Floor5(
val expression: Expression?,
) : Expression {
override fun toString(): String {
return "floor5($expression)"
}
}
data class Inversion(
val expression: Expression,
) : Expression {

View file

@ -180,6 +180,25 @@ class ExpressionParser(
return Expression.Maximum(first, second)
}
"floor5" -> {
// consume the '(' character
stack.moveCursor()
// evaluate the content of the first parameter.
val expression = evaluate()
// check that the expression is well formed, need a ).
guard(stack.peek() == ')') {
Error.ExpectedOperator(
expected = ')',
actual = stack.peek(),
expression = stack.input
)
}
// consume the ')' character
stack.moveCursor()
// build the final function expression
return Expression.Floor5(expression)
}
else -> {
val value = token.toIntOrNull()
if (value != null) {

View file

@ -1,17 +1,36 @@
package com.pixelized.shared.lwa.protocol.websocket.payload
import kotlinx.serialization.Serializable
import java.util.UUID
@Serializable
data class RollMessage(
val characterId: String,
val id: RollId,
val characterSheetId: String,
val skillLabel: String,
val rollValue: Int,
val resultLabel: String? = null,
val rollDifficulty: String? = null,
val rollValue: Int,
val rollSuccessLimit: Int? = null,
val critical: Critical?,
val critical: Critical? = null,
) : MessagePayload {
@Serializable
data class RollId(
val rollId: String,
val timestamp: Long,
) {
companion object {
fun create(
rollId: String = UUID.randomUUID().toString(),
timestamp: Long = System.currentTimeMillis(),
) = RollId(
rollId = rollId,
timestamp = timestamp,
)
}
}
enum class Critical {
CRITICAL_SUCCESS,
SPECIAL_SUCCESS,

View file

@ -27,14 +27,14 @@ class CharacterSheetUseCase {
return power
}
fun damageBonus(
fun meleeBonusDamage(
strength: Int,
height: Int,
): String {
return damageBonus(sum = strength + height)
return meleeBonusDamage(sum = strength + height)
}
fun damageBonus(
fun meleeBonusDamage(
sum: Int,
): String {
return when {
@ -47,6 +47,26 @@ class CharacterSheetUseCase {
}
}
fun distanceBonusDamage(
strength: Int,
height: Int,
): String {
return distanceBonusDamage(sum = strength + height)
}
fun distanceBonusDamage(
sum: Int,
): String {
return when {
sum < 12 -> "-1d3"
sum in 12..17 -> "-1d2"
sum in 18..22 -> "+0"
sum in 23..29 -> "+1d2"
sum in 30..39 -> "+1d3"
else -> "+2d3"
}
}
fun armor(): Int = 0
fun learning(intelligence: Int): Int {

View file

@ -1,15 +1,9 @@
package com.pixelized.shared.lwa.usecase
import com.pixelized.shared.lwa.model.AlteredCharacterSheet
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.CHA
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.CON
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.INT
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.POW
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.STR
import com.pixelized.shared.lwa.parser.expression.Expression
import com.pixelized.shared.lwa.parser.expression.ExpressionParser
import com.pixelized.shared.lwa.parser.word.Word
@ -22,15 +16,13 @@ class ExpressionUseCase(
private val rollUseCase: RollUseCase,
) {
fun computeSkillValue(
sheet: CharacterSheet,
sheet: AlteredCharacterSheet,
skill: CharacterSheet.Skill,
alterations: Map<String, List<FieldAlteration>>,
diminished: Int,
skill: CharacterSheet.Skill,
): Int {
val context = Context(
sheet = sheet,
skill = skill,
alterations = alterations,
)
val base: Int = context.evaluate(
@ -56,15 +48,13 @@ class ExpressionUseCase(
}
fun computeRoll(
sheet: CharacterSheet,
alterations: Map<String, List<FieldAlteration>>,
sheet: AlteredCharacterSheet,
expression: String,
): Int {
print("Roll::$expression::")
val roll = expressionParser.parse(input = expression)?.let {
computeExpression(
sheet = sheet,
alterations = alterations,
expression = it,
)
} ?: 0
@ -73,14 +63,11 @@ class ExpressionUseCase(
}
fun computeExpression(
sheet: CharacterSheet,
alterations: Map<String, List<FieldAlteration>>,
sheet: AlteredCharacterSheet,
expression: Expression,
): Int {
val context = Context(
sheet = sheet,
skill = null,
alterations = alterations,
)
return context.evaluate(
expression = expression,
@ -113,11 +100,15 @@ class ExpressionUseCase(
}
is Expression.Maximum -> {
min(evaluate(expression.first), evaluate(expression.second))
max(evaluate(expression.first), evaluate(expression.second))
}
is Expression.Minimum -> {
max(evaluate(expression.first), evaluate(expression.second))
min(evaluate(expression.first), evaluate(expression.second))
}
is Expression.Floor5 -> {
evaluate(expression.expression).let { it - it % 5 }
}
is Expression.Flat -> {
@ -135,29 +126,29 @@ class ExpressionUseCase(
is Expression.WordExpression -> when (expression.word.type) {
Word.Type.BDC -> evaluate(
expression = expressionParser.parse(
characterSheetUseCase.damageBonus(
strength = sheet.strength + alterations[STR].sum(),
height = sheet.height + alterations[HEI].sum(),
characterSheetUseCase.meleeBonusDamage(
strength = sheet.strength,
height = sheet.height,
)
)
)
Word.Type.BDD -> evaluate(
expression = expressionParser.parse(
characterSheetUseCase.damageBonus(
strength = sheet.strength + alterations[STR].sum(),
height = sheet.height + alterations[HEI].sum(),
characterSheetUseCase.distanceBonusDamage(
strength = sheet.strength,
height = sheet.height,
)
)
)
Word.Type.STR -> sheet.strength + alterations[STR].sum()
Word.Type.DEX -> sheet.dexterity + alterations[DEX].sum()
Word.Type.CON -> sheet.constitution + alterations[CON].sum()
Word.Type.HEI -> sheet.height + alterations[HEI].sum()
Word.Type.INT -> sheet.intelligence + alterations[INT].sum()
Word.Type.POW -> sheet.power + alterations[POW].sum()
Word.Type.CHA -> sheet.charisma + alterations[CHA].sum()
Word.Type.STR -> sheet.strength
Word.Type.DEX -> sheet.dexterity
Word.Type.CON -> sheet.constitution
Word.Type.HEI -> sheet.height
Word.Type.INT -> sheet.intelligence
Word.Type.POW -> sheet.power
Word.Type.CHA -> sheet.charisma
}
null -> 0
@ -165,9 +156,7 @@ class ExpressionUseCase(
}
data class Context(
val sheet: CharacterSheet,
val skill: CharacterSheet.Skill?,
val alterations: Map<String, List<FieldAlteration>>,
val sheet: AlteredCharacterSheet,
)
companion object {