From a6a8d56962384d759a479e97c9b5f7cec153ea27 Mon Sep 17 00:00:00 2001 From: shahondin1624 Date: Fri, 13 Mar 2026 14:17:44 +0100 Subject: [PATCH] feat: add Create New Character with confirmation (Closes #30) (#73) --- .../shahondin1624/lib/components/TestTags.kt | 6 ++ .../lib/components/settings/Settings.kt | 56 +++++++++++++++ .../org/shahondin1624/model/Defaults.kt | 36 ++++++++++ .../org/shahondin1624/NewCharacterTest.kt | 71 +++++++++++++++++++ 4 files changed, 169 insertions(+) create mode 100644 sharedUI/src/commonTest/kotlin/org/shahondin1624/NewCharacterTest.kt diff --git a/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/TestTags.kt b/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/TestTags.kt index 58f87eb..4dc8b35 100644 --- a/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/TestTags.kt +++ b/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/TestTags.kt @@ -77,6 +77,12 @@ object TestTags { const val SETTINGS_THEME_DARK = "settings_theme_dark" const val SETTINGS_EXPORT_BUTTON = "settings_export_button" const val SETTINGS_IMPORT_BUTTON = "settings_import_button" + const val SETTINGS_NEW_CHARACTER_BUTTON = "settings_new_character_button" + + // --- New character confirmation dialog --- + const val NEW_CHARACTER_CONFIRM_DIALOG = "new_character_confirm_dialog" + const val NEW_CHARACTER_CONFIRM = "new_character_confirm" + const val NEW_CHARACTER_DISMISS = "new_character_dismiss" // --- Export dialog --- const val EXPORT_DIALOG = "export_dialog" diff --git a/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/settings/Settings.kt b/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/settings/Settings.kt index c1e86d6..595e7d4 100644 --- a/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/settings/Settings.kt +++ b/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/settings/Settings.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.Upload import androidx.compose.material3.* @@ -19,6 +20,7 @@ import androidx.compose.ui.unit.dp import org.shahondin1624.lib.components.TestTags import org.shahondin1624.lib.functions.DataLoader import org.shahondin1624.model.charactermodel.ShadowrunCharacter +import org.shahondin1624.model.createNewCharacter import org.shahondin1624.theme.LocalThemePreference import org.shahondin1624.theme.ThemePreference @@ -33,6 +35,38 @@ fun SettingsPage( var themePreference by LocalThemePreference.current var showExportDialog by remember { mutableStateOf(false) } var showImportDialog by remember { mutableStateOf(false) } + var showNewCharacterConfirm by remember { mutableStateOf(false) } + + // New character confirmation dialog + if (showNewCharacterConfirm && onImportCharacter != null) { + AlertDialog( + onDismissRequest = { showNewCharacterConfirm = false }, + modifier = Modifier.testTag(TestTags.NEW_CHARACTER_CONFIRM_DIALOG), + title = { Text("Create New Character") }, + text = { + Text("This will replace your current character with a blank one. Any unsaved changes will be lost. Are you sure?") + }, + confirmButton = { + TextButton( + onClick = { + onImportCharacter(createNewCharacter()) + showNewCharacterConfirm = false + }, + modifier = Modifier.testTag(TestTags.NEW_CHARACTER_CONFIRM) + ) { + Text("Create") + } + }, + dismissButton = { + TextButton( + onClick = { showNewCharacterConfirm = false }, + modifier = Modifier.testTag(TestTags.NEW_CHARACTER_DISMISS) + ) { + Text("Cancel") + } + } + ) + } // Export dialog if (showExportDialog && character != null) { @@ -163,6 +197,28 @@ fun SettingsPage( Text("Import") } } + HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp)) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + OutlinedButton( + onClick = { showNewCharacterConfirm = true }, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.SETTINGS_NEW_CHARACTER_BUTTON) + ) { + Icon( + Icons.Default.Add, + contentDescription = null, + modifier = Modifier.size(18.dp) + ) + Spacer(Modifier.width(8.dp)) + Text("New Character") + } + } } } } diff --git a/sharedUI/src/commonMain/kotlin/org/shahondin1624/model/Defaults.kt b/sharedUI/src/commonMain/kotlin/org/shahondin1624/model/Defaults.kt index fe74448..92d08b8 100644 --- a/sharedUI/src/commonMain/kotlin/org/shahondin1624/model/Defaults.kt +++ b/sharedUI/src/commonMain/kotlin/org/shahondin1624/model/Defaults.kt @@ -22,6 +22,42 @@ val EXAMPLE_ATTRIBUTES = Attributes( edge = 2 ) +private val DEFAULT_ATTRIBUTES = Attributes( + body = Attribute(AttributeType.Body, 1), + agility = Attribute(AttributeType.Agility, 1), + reaction = Attribute(AttributeType.Reaction, 1), + strength = Attribute(AttributeType.Strength, 1), + willpower = Attribute(AttributeType.Willpower, 1), + logic = Attribute(AttributeType.Logic, 1), + intuition = Attribute(AttributeType.Intuition, 1), + charisma = Attribute(AttributeType.Charisma, 1), + edge = 1 +) + +/** + * Creates a fresh blank character with all attributes at 1, all talents at rating 0, + * and default character data. Used for the "New Character" action. + */ +fun createNewCharacter(): ShadowrunCharacter = ShadowrunCharacter( + attributes = DEFAULT_ATTRIBUTES, + talents = Talents(createAllProvidedTalents()), + characterData = CharacterData( + concept = "", + nuyen = 0, + essence = 6.0f, + name = "New Character", + metatype = Metatype.Human, + age = 0, + gender = "", + streetCred = 0, + notoriety = 0, + publicAwareness = 0, + totalKarma = 0, + currentKarma = 0 + ), + damageMonitor = createDamageMonitor(DEFAULT_ATTRIBUTES), +) + val EXAMPLE_CHARACTER: ShadowrunCharacter = ShadowrunCharacter( attributes = EXAMPLE_ATTRIBUTES, talents = Talents(createAllProvidedTalents()), diff --git a/sharedUI/src/commonTest/kotlin/org/shahondin1624/NewCharacterTest.kt b/sharedUI/src/commonTest/kotlin/org/shahondin1624/NewCharacterTest.kt new file mode 100644 index 0000000..8e056b8 --- /dev/null +++ b/sharedUI/src/commonTest/kotlin/org/shahondin1624/NewCharacterTest.kt @@ -0,0 +1,71 @@ +package org.shahondin1624 + +import org.shahondin1624.model.characterdata.Metatype +import org.shahondin1624.model.createNewCharacter +import kotlin.test.Test +import kotlin.test.assertEquals + +class NewCharacterTest { + + @Test + fun newCharacterHasDefaultName() { + val char = createNewCharacter() + assertEquals("New Character", char.characterData.name) + } + + @Test + fun newCharacterHasAllAttributesAtOne() { + val char = createNewCharacter() + val attrs = char.attributes.getAllAttributes() + for (attr in attrs) { + assertEquals(1, attr.value, "Attribute ${attr.type.name} should be 1") + } + } + + @Test + fun newCharacterHasEdgeOne() { + val char = createNewCharacter() + assertEquals(1, char.attributes.edge) + } + + @Test + fun newCharacterHasAllTalentsAtZero() { + val char = createNewCharacter() + for (talent in char.talents.talents) { + assertEquals(0, talent.value, "Talent ${talent.name} should be 0") + } + } + + @Test + fun newCharacterHasZeroKarma() { + val char = createNewCharacter() + assertEquals(0, char.characterData.totalKarma) + assertEquals(0, char.characterData.currentKarma) + } + + @Test + fun newCharacterHasZeroNuyen() { + val char = createNewCharacter() + assertEquals(0, char.characterData.nuyen) + } + + @Test + fun newCharacterHasFullEssence() { + val char = createNewCharacter() + assertEquals(6.0f, char.characterData.essence) + } + + @Test + fun newCharacterIsHumanByDefault() { + val char = createNewCharacter() + assertEquals(Metatype.Human, char.characterData.metatype) + } + + @Test + fun newCharacterHasZeroReputation() { + val char = createNewCharacter() + assertEquals(0, char.characterData.streetCred) + assertEquals(0, char.characterData.notoriety) + assertEquals(0, char.characterData.publicAwareness) + } +}