Skip to content

Commit

Permalink
Add challenge rating field on monster registration (#320)
Browse files Browse the repository at this point in the history
* Find type damage too

* Add challenge rating field on monster registration

* Fix - Add challenge rating field on monster registration
  • Loading branch information
alexandregpereira authored Aug 17, 2024
1 parent 71d4ecf commit 15ed758
Show file tree
Hide file tree
Showing 16 changed files with 112 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ data class Monster(
val index: String,
val name: String = "",
val type: MonsterType = MonsterType.ABERRATION,
val challengeRating: Float = 0f,
val challengeRatingData: ChallengeRating = ChallengeRating(),
val imageData: MonsterImageData = MonsterImageData(),
val subtype: String? = null,
val group: String? = null,
Expand Down Expand Up @@ -51,16 +51,27 @@ data class Monster(
val status: MonsterStatus = MonsterStatus.Original,
) {

val xp: Int = challengeRatingToXp()
val xp: Int
get() = challengeRatingToXp()

val challengeRatingFormatted: String = challengeRating.getChallengeRatingFormatted()
val challengeRating: Float
get() = challengeRatingData.value

val challengeRatingFormatted: String
get() = challengeRatingData.value.getChallengeRatingFormatted()
}

enum class MonsterStatus {
Original, Edited, Clone, Imported
}

private fun Float.getChallengeRatingFormatted(): String {
data class ChallengeRating(
val value: Float = 0f,
val valueInString: String = value.toString(),
val formatted: String = value.getChallengeRatingFormatted()
)

fun Float.getChallengeRatingFormatted(): String {
if (this == 0f) return "0"

return if (this < 1) {
Expand All @@ -86,40 +97,45 @@ fun Monster.isComplete() = abilityScores.isNotEmpty()

private fun Monster.challengeRatingToXp(): Int {
return when (challengeRating) {
0.125f -> 25
0.25f -> 50
0.5f -> 100
1f -> 200
2f -> 450
3f -> 700
4f -> 1100
5f -> 1800
6f -> 2300
7f -> 2900
8f -> 3900
9f -> 5000
10f -> 5900
11f -> 7200
12f -> 8400
13f -> 10000
14f -> 11500
15f -> 13000
16f -> 15000
17f -> 18000
18f -> 20000
19f -> 22000
20f -> 25000
21f -> 33000
22f -> 41000
23f -> 50000
24f -> 62000
25f -> 75000
26f -> 90000
27f -> 105000
28f -> 120000
29f -> 135000
30f -> 155000
else -> 10
in 0.125f..0.24f -> 25
in 0.25f..0.49f -> 50
in 0.5f..0.9f -> 100
else -> when (val intValue = challengeRating.toInt()) {
0 -> 10
1 -> 200
2 -> 450
3 -> 700
4 -> 1100
5 -> 1800
6 -> 2300
7 -> 2900
8 -> 3900
9 -> 5000
10 -> 5900
11 -> 7200
12 -> 8400
13 -> 10000
14 -> 11500
15 -> 13000
16 -> 15000
17 -> 18000
18 -> 20000
19 -> 22000
20 -> 25000
21 -> 33000
22 -> 41000
23 -> 50000
24 -> 62000
25 -> 75000
26 -> 90000
27 -> 105000
28 -> 120000
29 -> 135000
30 -> 155000
else -> if (challengeRating > 30) {
(155000 + (intValue - 30) * 10000).coerceAtMost(300000)
} else 0
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package br.alexandregpereira.hunter.domain.usecase

import br.alexandregpereira.hunter.domain.model.AbilityDescription
import br.alexandregpereira.hunter.domain.model.Action
import br.alexandregpereira.hunter.domain.model.ChallengeRating
import br.alexandregpereira.hunter.domain.model.Color
import br.alexandregpereira.hunter.domain.model.MeasurementUnit
import br.alexandregpereira.hunter.domain.model.Monster
Expand Down Expand Up @@ -159,7 +160,7 @@ class SaveMonstersUseCaseTest {
index = "",
name = "",
type = MonsterType.CELESTIAL,
challengeRating = 0.0f,
challengeRatingData = ChallengeRating(0.0f),
imageData = MonsterImageData(
url = "something.png",
backgroundColor = Color(light = "", dark = ""),
Expand Down Expand Up @@ -213,7 +214,7 @@ class SaveMonstersUseCaseTest {
index = "",
name = "",
type = MonsterType.CELESTIAL,
challengeRating = 0.0f,
challengeRatingData = ChallengeRating(0.0f),
imageData = MonsterImageData(
url = "something.png",
backgroundColor = Color(light = "", dark = ""),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import br.alexandregpereira.hunter.data.monster.local.entity.MonsterEntity
import br.alexandregpereira.hunter.data.monster.local.entity.MonsterEntityStatus
import br.alexandregpereira.hunter.data.monster.spell.local.mapper.toDomain
import br.alexandregpereira.hunter.data.monster.spell.local.mapper.toEntity
import br.alexandregpereira.hunter.domain.model.ChallengeRating
import br.alexandregpereira.hunter.domain.model.Color
import br.alexandregpereira.hunter.domain.model.Monster
import br.alexandregpereira.hunter.domain.model.MonsterImageData
Expand Down Expand Up @@ -118,7 +119,7 @@ private fun MonsterEntity.toDomain(): Monster {
return Monster(
index = monster.index,
type = MonsterType.valueOf(monster.type),
challengeRating = monster.challengeRating,
challengeRatingData = ChallengeRating(monster.challengeRating),
name = monster.name,
imageData = MonsterImageData(
url = monster.imageUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package br.alexandregpereira.hunter.data.monster.remote.mapper

import br.alexandregpereira.hunter.data.monster.remote.model.MonsterDto
import br.alexandregpereira.hunter.data.monster.spell.remote.mapper.toDomain
import br.alexandregpereira.hunter.domain.model.ChallengeRating
import br.alexandregpereira.hunter.domain.model.Color
import br.alexandregpereira.hunter.domain.model.Monster
import br.alexandregpereira.hunter.domain.model.MonsterImageData
Expand All @@ -29,7 +30,7 @@ internal fun List<MonsterDto>.toDomain(): List<Monster> {
Monster(
index = it.index,
type = MonsterType.valueOf(it.type.name),
challengeRating = it.challengeRating,
challengeRatingData = ChallengeRating(it.challengeRating),
name = it.name,
imageData = MonsterImageData(
url = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import br.alexandregpereira.flow.test.assertHasNoMoreValues
import br.alexandregpereira.flow.test.assertNextValue
import br.alexandregpereira.flow.test.testFlows
import br.alexandregpereira.hunter.analytics.EmptyAnalytics
import br.alexandregpereira.hunter.domain.model.ChallengeRating
import br.alexandregpereira.hunter.domain.model.Monster
import br.alexandregpereira.hunter.domain.model.MonsterType
import br.alexandregpereira.hunter.domain.usecase.GetLastCompendiumScrollItemPositionUseCase
Expand Down Expand Up @@ -90,12 +91,12 @@ class MonsterCompendiumStateHolderTest {
Monster(
index = "zariel1",
name = "Zariel",
challengeRating = 0.5f,
challengeRatingData = ChallengeRating(0.5f),
).asItem(),
Monster(
index = "zariel2",
name = "Zariel",
challengeRating = 1f,
challengeRatingData = ChallengeRating(1f),
).asItem()
),
tableContent = listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import br.alexandregpereira.hunter.domain.model.ChallengeRating
import br.alexandregpereira.hunter.domain.model.Monster
import br.alexandregpereira.hunter.domain.model.MonsterType
import br.alexandregpereira.hunter.monster.compendium.domain.model.MonsterCompendiumItem
Expand Down Expand Up @@ -173,7 +174,7 @@ private fun MonsterContentPreviewScreenPreview() {
index = "usu+$it",
name = "Raphael Bolton",
type = MonsterType.ABERRATION,
challengeRating = 13f,
challengeRatingData = ChallengeRating(13f),
)
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package br.alexandregpereira.hunter.monster.registration.ui.form
import androidx.compose.foundation.lazy.LazyListScope
import br.alexandregpereira.hunter.monster.registration.MonsterInfoState
import br.alexandregpereira.hunter.monster.registration.ui.strings
import br.alexandregpereira.hunter.ui.compose.AppKeyboardType
import br.alexandregpereira.hunter.ui.compose.AppTextField
import br.alexandregpereira.hunter.ui.compose.PickerField

Expand Down Expand Up @@ -39,6 +40,16 @@ internal fun LazyListScope.MonsterHeaderForm(
}
)
}
formItem(key = keys.next()) {
AppTextField(
text = infoState.challengeRating,
label = strings.challengeRating,
keyboardType = AppKeyboardType.DECIMAL,
onValueChange = {
onMonsterChanged(infoState.copy(challengeRating = it))
}
)
}
formItem(key = keys.next()) {
PickerField(
value = infoState.type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ data class MonsterInfoState(
val isImageHorizontal: Boolean = false,
val typeIndex: Int = 0,
val typeOptions: List<String> = emptyList(),
val challengeRating: String = "",
) {
val type: String = typeOptions.getOrNull(typeIndex).orEmpty()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ interface MonsterRegistrationStrings {
val imageHorizontalSwitchLabel: String
val darkThemeSwitchLabel: String
val imageProportion: (String) -> String
val challengeRating: String
}

internal data class MonsterRegistrationEnStrings(
Expand Down Expand Up @@ -227,6 +228,7 @@ internal data class MonsterRegistrationEnStrings(
override val imageHorizontalSwitchLabel: String = "Landscape Image",
override val darkThemeSwitchLabel: String = "Preview Dark Theme",
override val imageProportion: (String) -> String = { "Proportion - $it" },
override val challengeRating: String = "Challenge Rating",
) : MonsterRegistrationStrings

internal data class MonsterRegistrationPtStrings(
Expand Down Expand Up @@ -340,6 +342,7 @@ internal data class MonsterRegistrationPtStrings(
override val imageHorizontalSwitchLabel: String = "Imagem Landscape",
override val darkThemeSwitchLabel: String = "Pré visualizar em Tema Escuro",
override val imageProportion: (String) -> String = { "Proporção - $it" },
override val challengeRating: String = "Nível de Desafio",
) : MonsterRegistrationStrings

fun MonsterRegistrationStrings(): MonsterRegistrationStrings = MonsterRegistrationEnStrings()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ package br.alexandregpereira.hunter.monster.registration.domain

import br.alexandregpereira.hunter.domain.model.AbilityDescription
import br.alexandregpereira.hunter.domain.model.Action
import br.alexandregpereira.hunter.domain.model.ChallengeRating
import br.alexandregpereira.hunter.domain.model.Color
import br.alexandregpereira.hunter.domain.model.Monster
import br.alexandregpereira.hunter.domain.model.getChallengeRatingFormatted
import br.alexandregpereira.hunter.domain.monster.spell.model.SpellUsage
import br.alexandregpereira.hunter.domain.monster.spell.model.Spellcasting
import kotlinx.coroutines.flow.Flow
Expand Down Expand Up @@ -66,9 +68,18 @@ private fun Monster.changeAbilityScoresModifier(): Monster {
imageData = monster.imageData.copy(
backgroundColor = monster.imageData.backgroundColor.normalizeColor(),
),
challengeRatingData = monster.challengeRatingData.normalizeChallengeRating()
).filterEmpties().createIndexes()
}

private fun ChallengeRating.normalizeChallengeRating(): ChallengeRating {
val newValue = (this.valueInString.toFloatOrNull() ?: 0f).coerceAtMost(50f)
return this.copy(
value = newValue,
formatted = newValue.getChallengeRatingFormatted()
)
}

private fun Color.normalizeColor(): Color {
val newColorLight = this.light.normalizeColorString()
val newColorDark = this.dark.normalizeColorString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ internal fun Monster.editBy(
name = state.info.name,
subtitle = state.info.subtitle,
group = state.info.group,
challengeRatingData = monster.challengeRatingData.copy(
valueInString = state.info.challengeRating,
),
imageData = monster.imageData.copy(
url = state.info.imageUrl,
backgroundColor = monster.imageData.backgroundColor.copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ internal fun Metadata.asState(strings: MonsterRegistrationStrings): MonsterState
isImageHorizontal = monster.imageData.isHorizontal,
typeIndex = MonsterType.entries.indexOf(monster.type),
typeOptions = MonsterType.entries.map { it.name(strings) },
challengeRating = monster.challengeRatingData.valueInString,
),
stats = StatsState(
armorClass = monster.stats.armorClass,
Expand Down Expand Up @@ -347,6 +348,7 @@ private fun MonsterState.createKeys(): List<String> {
add("monsterHeader-name")
add("monsterHeader-subtitle")
add("monsterHeader-group")
add("monsterHeader-challengeRating")
add("monsterHeader-type")
add(SectionTitle.Image.name)
add("monsterHeader-image")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,8 @@ internal class SearchMonstersByUseCase internal constructor(

private fun List<Damage>.containsByDamage(value: String): Boolean {
return this.any { damage ->
damage.name.removeAccents().contains(value, ignoreCase = true)
damage.name.removeAccents().contains(value, ignoreCase = true) ||
damage.type.name.contains(value, ignoreCase = true)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import br.alexandregpereira.hunter.domain.model.AbilityDescription
import br.alexandregpereira.hunter.domain.model.AbilityScore
import br.alexandregpereira.hunter.domain.model.AbilityScoreType
import br.alexandregpereira.hunter.domain.model.Action
import br.alexandregpereira.hunter.domain.model.ChallengeRating
import br.alexandregpereira.hunter.domain.model.Color
import br.alexandregpereira.hunter.domain.model.Condition
import br.alexandregpereira.hunter.domain.model.ConditionType
Expand Down Expand Up @@ -35,7 +36,7 @@ internal fun ShareMonster.toMonster(): Monster {
index = index,
name = name,
type = MonsterType.valueOf(type),
challengeRating = challengeRating,
challengeRatingData = ChallengeRating(challengeRating),
imageData = MonsterImageData(
url = imageUrl,
backgroundColor = Color(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ fun AppTextField(
keyboardType = when (keyboardType) {
AppKeyboardType.TEXT -> KeyboardType.Text
AppKeyboardType.NUMBER -> KeyboardType.Number
AppKeyboardType.DECIMAL -> KeyboardType.Decimal
},
capitalization = capitalization,
),
Expand Down Expand Up @@ -152,6 +153,7 @@ fun AppTextField(
keyboardType = when (keyboardType) {
AppKeyboardType.TEXT -> KeyboardType.Text
AppKeyboardType.NUMBER -> KeyboardType.Number
AppKeyboardType.DECIMAL -> KeyboardType.Decimal
},
capitalization = capitalization,
),
Expand Down Expand Up @@ -182,7 +184,8 @@ fun AppTextField(

enum class AppKeyboardType {
TEXT,
NUMBER
NUMBER,
DECIMAL
}

@Composable
Expand Down
Loading

0 comments on commit 15ed758

Please sign in to comment.