# Import Process & Schema Documentation > **Platform Note**: This documentation describes the import process and internal database schema for the **Android Application** (`com.majinnaibu.monstercards`). --- ## 1. Overview of the Import Process The Monster Cards Android application can import monster stat blocks formatted in JSON from `.monster` or `.monster.txt` files shared or opened from other applications (such as file managers, messaging apps, or browsers). ``` +-------------------------------------------------------+ | External .monster / .monster.txt file (JSON Payload) | +-------------------------------------------------------+ | v +-------------------------------------------------------+ | MainActivity (Intent Handling: ACTION_VIEW / SEND) | | - Extracts raw JSON string from Content/File URI | +-------------------------------------------------------+ | v +-------------------------------------------------------+ | MonsterImportFragment & MonsterImportViewModel | | - MonsterImportHelper.fromJSON(json) parses JSON | | - Maps JSON fields to internal Monster domain model | | - Renders UI preview with calculated AC, HP, & Markdown| +-------------------------------------------------------+ | v +-------------------------------------------------------+ | User clicks "Import" Menu Action | | - Generates new UUID | | - Persists Monster to Room Database (monsters table) | | - Rebuilds FTS index (monsters_fts) | +-------------------------------------------------------+ ``` --- ## 2. Source Format (.monster / .monster.txt) & File Extension Behavior The `.monster` file format is generated by Tetra-cube's D&D 5e Statblock Generator (`statblock-script.js`). ### File Saving Behavior (`SavedData.SaveToFile`) ```javascript SaveToFile: () => saveAs(new Blob([JSON.stringify(mon)], { type: "text/plain;charset=utf-8" }), mon.name.toLowerCase() + ".monster") ``` - **Desktop Browsers**: Save the file with the specified `.monster` filename. - **Android Mobile Browsers & DownloadManager**: Because the `Blob` uses MIME type `text/plain;charset=utf-8`, Android file handlers append a `.txt` extension, creating `.monster.txt` files. --- ## 3. JSON Parsing & Field Mapping (`MonsterImportHelper.java`) `MonsterImportHelper.fromJSON(String json)` uses Gson's `JsonParser` to parse the root JSON object and populate a `Monster` instance. ### Complete Field Mapping Reference | JSON Field Name | Target `Monster` Field | Type / Default | Description / Conversion Logic | | :--- | :--- | :--- | :--- | | `name` | `monster.name` | `String` | Monster name | | `size` | `monster.size` | `String` | Size string (e.g., `"medium"`, `"large"`) | | `type` | `monster.type` | `String` | Creature type (e.g., `"humanoid"`, `"dragon"`) | | `tag` | `monster.subtype` | `String` | Subtype / tag (e.g., `"elf"`, `"shapechanger"`) | | `alignment` | `monster.alignment` | `String` | Alignment string (e.g., `"neutral good"`) | | `hitDice` | `monster.hitDice` | `int` (default 1) | Number of hit dice | | `armorName` | `monster.armorType` | `ArmorType` | Converted via `ArmorTypeConverter.armorTypeFromStringValue()` | | `shieldBonus` | `monster.shieldBonus` | `int` | Shield AC bonus (+2 if present) | | `natArmorBonus` | `monster.naturalArmorBonus` | `int` | Additional natural armor AC bonus | | `otherArmorDesc` | `monster.otherArmorDescription` | `String` | Custom armor description string | | `speed` | `monster.walkSpeed` | `int` | Walking speed in feet | | `burrowSpeed` | `monster.burrowSpeed` | `int` | Burrowing speed in feet | | `climbSpeed` | `monster.climbSpeed` | `int` | Climbing speed in feet | | `flySpeed` | `monster.flySpeed` | `int` | Flying speed in feet | | `hover` | `monster.canHover` | `boolean` | Whether flying creature can hover | | `swimSpeed` | `monster.swimSpeed` | `int` | Swimming speed in feet | | `customHP` | `monster.hasCustomHP` | `boolean` | Flag for override custom HP string | | `customSpeed` | `monster.hasCustomSpeed` | `boolean` | Flag for override custom speed string | | `hpText` | `monster.customHPDescription` | `String` | Custom HP text description | | `speedDesc` | `monster.customSpeedDescription` | `String` | Custom speed text description | | `strPoints` | `monster.strengthScore` | `int` (default 10) | Strength ability score | | `dexPoints` | `monster.dexterityScore` | `int` (default 10) | Dexterity ability score | | `conPoints` | `monster.constitutionScore` | `int` (default 10) | Constitution ability score | | `intPoints` | `monster.intelligenceScore` | `int` (default 10) | Intelligence ability score | | `wisPoints` | `monster.wisdomScore` | `int` (default 10) | Wisdom ability score | | `chaPoints` | `monster.charismaScore` | `int` (default 10) | Charisma ability score | | `blindsight` | `monster.senses` | `Set` | Added as `"blindsight ft."` if `N > 0` | | `darkvision` | `monster.senses` | `Set` | Added as `"darkvision ft."` if `N > 0` | | `tremorsense` | `monster.senses` | `Set` | Added as `"tremorsense ft."` if `N > 0` | | `truesight` | `monster.senses` | `Set` | Added as `"truesight ft."` if `N > 0` | | `telepathy` | `monster.telepathyRange` | `int` | Telepathy range in feet | | `cr` | `monster.challengeRating` | `ChallengeRating` | Converted via `ChallengeRatingConverter` | | `customCr` | `monster.customChallengeRatingDescription` | `String` | Custom CR text | | `customProf` | `monster.customProficiencyBonus` | `int` | Custom proficiency bonus override | | `abilities` | `monster.abilities` | `List` | List of JSON objects `{ "name": "...", "desc": "..." }` | | `actions` | `monster.actions` | `List` | List of JSON objects `{ "name": "...", "desc": "..." }` | | `bonusActions` | `monster.actions` *(Future)* | `List` | Bonus actions array | | `reactions` | `monster.reactions` | `List` | List of JSON objects `{ "name": "...", "desc": "..." }` | | `legendaries` | `monster.legendaryActions` | `List` | List of JSON objects `{ "name": "...", "desc": "..." }` | | `mythics` | `monster.legendaryActions` *(Future)* | `List` | Mythic actions array | | `lairs` | `monster.lairActions` | `List` | List of JSON objects `{ "name": "...", "desc": "..." }` | | `regionals` | `monster.regionalActions` | `List` | List of JSON objects `{ "name": "...", "desc": "..." }` | | `sthrows` | Saving throw proficiencies | `ProficiencyType` | Array of objects `{ "name": "str"\|"dex"\|"con"\|"int"\|"wis"\|"cha" }`. Sets proficiency to `PROFICIENT`. | | `skills` | `monster.skills` | `Set` | Array of `{ "name": "...", "stat": "...", "note": " (ex)" }`. `note == " (ex)"` sets `EXPERTISE`. | | `damageTypes`, `specialdamage` | Immunities / Resistances / Vulnerabilities | `Set` | Array of `{ "name": "...", "type": "i"\|"r"\|"v" }`. Sorted into immunities (`"i"`), resistances (`"r"`), or vulnerabilities (`"v"`). | | `conditions` | `monster.conditionImmunities` | `Set` | Array of `{ "name": "..." }` | | `languages` | `monster.languages` | `Set` | Array of `{ "name": "...", "speaks": true\|false }` | | `understandsBut` | `monster.understandsButDescription` | `String` | Language qualifier text | --- ## 4. Preview & Database Persistence 1. **ViewModel Computation (`MonsterImportViewModel.java`)**: - Computes calculated stat block values: - **AC**: Calculated based on `armorType`, dexterity modifier, shield bonus, natural armor bonus, or custom description. - **HP**: Calculated as `floor(hitDice * ((dieSize + 1) / 2 + conModifier))` where `dieSize` is derived from size (e.g., Medium = d8, Large = d10). - **Dynamic Placeholders**: Replaces placeholders in trait descriptions (e.g., `[STR ATK]`, `[WIS SAVE]`) with computed attack bonuses and spell save DCs based on ability scores and proficiency bonuses. - **Markdown Rendering**: Trait and action descriptions are parsed through `CommonMarkHelper` to format inline HTML/Markdown for display. 2. **Persistence (`MonsterImportFragment.java`)**: - When the user taps the **Import** action menu button (`R.id.menu_action_import_monster`): 1. Assigns a new `UUID` to `monster.id`. 2. Calls `MonsterRepository.addMonster(monster)` which executes an `INSERT` into the Room database asynchronously on an IO thread via RxJava3. 3. Shows a `Snackbar` confirming import success and navigates to the library/detail view for the imported monster. --- ## 5. Internal Database Schema (`monsters` Table) The app uses **Room Database** (Database Name: `monsters`, Version: `5`). ### Table: `monsters` | Column Name | SQLite Data Type | Default Value | Description / Room TypeConverter | | :--- | :--- | :--- | :--- | | `id` | `TEXT` | Primary Key | UUID string (`UUIDConverter`) | | `name` | `TEXT` | `""` | Monster name | | `size` | `TEXT` | `""` | Size category (e.g. `"medium"`) | | `type` | `TEXT` | `""` | Creature type | | `subtype` | `TEXT` | `""` | Creature tag / subtype | | `alignment` | `TEXT` | `""` | Alignment | | `strength_score` | `INTEGER` | `10` | Strength score | | `strength_saving_throw_advantage` | `TEXT` | `"none"` | Advantage type (`"none"`, `"advantage"`, `"disadvantage"`) | | `strength_saving_throw_proficiency` | `TEXT` | `"none"` | Proficiency type (`"none"`, `"proficient"`, `"expertise"`) | | `dexterity_score` | `INTEGER` | `10` | Dexterity score | | `dexterity_saving_throw_advantage` | `TEXT` | `"none"` | Dexterity saving throw advantage | | `dexterity_saving_throw_proficiency` | `TEXT` | `"none"` | Dexterity saving throw proficiency | | `constitution_score` | `INTEGER` | `10` | Constitution score | | `constitution_saving_throw_advantage` | `TEXT` | `"none"` | Constitution saving throw advantage | | `constitution_saving_throw_proficiency` | `TEXT` | `"none"` | Constitution saving throw proficiency | | `intelligence_score` | `INTEGER` | `10` | Intelligence score | | `intelligence_saving_throw_advantage` | `TEXT` | `"none"` | Intelligence saving throw advantage | | `intelligence_saving_throw_proficiency` | `TEXT` | `"none"` | Intelligence saving throw proficiency | | `wisdom_score` | `INTEGER` | `10` | Wisdom score | | `wisdom_saving_throw_advantage` | `TEXT` | `"none"` | Wisdom saving throw advantage | | `wisdom_saving_throw_proficiency` | `TEXT` | `"none"` | Wisdom saving throw proficiency | | `charisma_score` | `INTEGER` | `10` | Charisma score | | `charisma_saving_throw_advantage` | `TEXT` | `"none"` | Charisma saving throw advantage | | `charisma_saving_throw_proficiency` | `TEXT` | `"none"` | Charisma saving throw proficiency | | `armor_type` | `TEXT` | `"none"` | String representation of `ArmorType` (`ArmorTypeConverter`) | | `shield_bonus` | `INTEGER` | `0` | Shield AC bonus | | `natural_armor_bonus` | `INTEGER` | `0` | Natural armor bonus | | `other_armor_description` | `TEXT` | `""` | Custom armor description | | `hit_dice` | `INTEGER` | `1` | Hit dice count | | `has_custom_hit_points` | `INTEGER` | `0` | Boolean flag (0 = false, 1 = true) | | `custom_hit_points_description` | `TEXT` | `""` | Override HP string | | `walk_speed` | `INTEGER` | `0` | Walk speed in ft | | `burrow_speed` | `INTEGER` | `0` | Burrow speed in ft | | `climb_speed` | `INTEGER` | `0` | Climb speed in ft | | `fly_speed` | `INTEGER` | `0` | Fly speed in ft | | `can_hover` | `INTEGER` | `0` | Boolean flag | | `swim_speed` | `INTEGER` | `0` | Swim speed in ft | | `has_custom_speed` | `INTEGER` | `0` | Boolean flag | | `custom_speed_description` | `TEXT` | NULL | Override speed string | | `challenge_rating` | `TEXT` | `"1"` | String representation of `ChallengeRating` (`ChallengeRatingConverter`) | | `custom_challenge_rating_description` | `TEXT` | `""` | Custom CR text | | `custom_proficiency_bonus` | `INTEGER` | `0` | Custom proficiency bonus override | | `telepathy_range` | `INTEGER` | `0` | Telepathy range in ft | | `understands_but_description` | `TEXT` | `""` | Language qualifier description | | `senses` | `TEXT` | `"[]"` | JSON array of strings (`SetOfStringConverter`) | | `skills` | `TEXT` | `"[]"` | JSON array of Skill objects (`SetOfSkillConverter`) | | `damage_immunities` | `TEXT` | `"[]"` | JSON array of strings (`SetOfStringConverter`) | | `damage_resistances` | `TEXT` | `"[]"` | JSON array of strings (`SetOfStringConverter`) | | `damage_vulnerabilities` | `TEXT` | `"[]"` | JSON array of strings (`SetOfStringConverter`) | | `condition_immunities` | `TEXT` | `"[]"` | JSON array of strings (`SetOfStringConverter`) | | `languages` | `TEXT` | `"[]"` | JSON array of Language objects (`SetOfLanguageConverter`) | | `abilities` | `TEXT` | `"[]"` | JSON array of Trait objects (`ListOfTraitsConverter`) | | `actions` | `TEXT` | `"[]"` | JSON array of Trait objects (`ListOfTraitsConverter`) | | `reactions` | `TEXT` | `"[]"` | JSON array of Trait objects (`ListOfTraitsConverter`) | | `lair_actions` | `TEXT` | `"[]"` | JSON array of Trait objects (`ListOfTraitsConverter`) | | `legendary_actions` | `TEXT` | `"[]"` | JSON array of Trait objects (`ListOfTraitsConverter`) | | `regional_actions` | `TEXT` | `"[]"` | JSON array of Trait objects (`ListOfTraitsConverter`) | ### Virtual FTS Table: `monsters_fts` Used for full-text search indexing: ```sql CREATE VIRTUAL TABLE IF NOT EXISTS `monsters_fts` USING FTS4(`name` TEXT, `size` TEXT, `type` TEXT, `subtype` TEXT, `alignment` TEXT, content=`monsters`); ``` --- ## 6. Component Objects Schema (JSON Serialized in DB) ### 1. `Trait` Used in `abilities`, `actions`, `reactions`, `lair_actions`, `legendary_actions`, and `regional_actions`: ```json { "name": "Keen Smell", "description": "The monster has advantage on Wisdom (Perception) checks that rely on smell." } ``` ### 2. `Skill` Used in `skills`: ```json { "name": "Perception", "abilityScore": "WISDOM", "advantageType": "NONE", "proficiencyType": "PROFICIENT" } ``` - **`abilityScore`**: `"STRENGTH"`, `"DEXTERITY"`, `"CONSTITUTION"`, `"INTELLIGENCE"`, `"WISDOM"`, `"CHARISMA"`. - **`advantageType`**: `"NONE"`, `"ADVANTAGE"`, `"DISADVANTAGE"`. - **`proficiencyType`**: `"NONE"`, `"PROFICIENT"`, `"EXPERTISE"`. ### 3. `Language` Used in `languages`: ```json { "mName": "Common", "mSpeaks": true } ``` - **`mName`**: Name of the language (e.g., `"Elvish"`). - **`mSpeaks`**: `true` if the creature can speak it; `false` if it only understands it.