commit 45cf504bfbe3df241b0043418e8a0b29423e4e1d Author: Tim Evans (slaguru666) Date: Tue Mar 31 17:00:15 2026 +0100 chore: initial deploy — SLA Industries Foundry VTT Co-authored-by: slaguru666 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b24d620 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +node_modules/ +npm-debug.log* +.npm/ +dist/ +*.min.js +*.min.css +.DS_Store +.DS_Store? +._* +Thumbs.db +.vscode/ +.idea/ +*.swp +*.swo +worlds/ diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 0000000..5163981 --- /dev/null +++ b/lang/en.json @@ -0,0 +1,29 @@ +{ + "NPC_PORTRAIT_PACK.Settings.AutoAssign.Name": "Auto-Assign Portrait on NPC Creation", + "NPC_PORTRAIT_PACK.Settings.AutoAssign.Hint": "Automatically assigns a random portrait when a new NPC actor is created (GM only).", + + "NPC_PORTRAIT_PACK.Settings.MatchSpecies.Name": "Match Portrait to Species", + "NPC_PORTRAIT_PACK.Settings.MatchSpecies.Hint": "When auto-assigning, pick a portrait from the species pool matching the actor's Species field (e.g. Ebon, Frother, Shaktar). Falls back to the default category if no match is found.", + + "NPC_PORTRAIT_PACK.Settings.OnlyIfDefaultImage.Name": "Only Assign If No Custom Image", + "NPC_PORTRAIT_PACK.Settings.OnlyIfDefaultImage.Hint": "Skip auto-assignment if the actor already has a non-default portrait image.", + + "NPC_PORTRAIT_PACK.Settings.SetAvatar.Name": "Also Set Actor Avatar", + "NPC_PORTRAIT_PACK.Settings.SetAvatar.Hint": "When assigning a portrait, also update the Actor's avatar image (the portrait shown in the actor sheet sidebar), not just the prototype token image.", + + "NPC_PORTRAIT_PACK.Settings.DefaultCategory.Name": "Default Portrait Category", + "NPC_PORTRAIT_PACK.Settings.DefaultCategory.Hint": "Category used when auto-assigning and no species match is found, or when Match Portrait to Species is disabled.", + + "NPC_PORTRAIT_PACK.Dialog.Title": "NPC Portrait Picker", + "NPC_PORTRAIT_PACK.Dialog.RandomButton": "Random", + "NPC_PORTRAIT_PACK.Notifications.Assigned": "Portrait assigned to {actorName}.", + + "NPC_PORTRAIT_PACK.Generator.Title": "SLA Industries NPC Generator", + "NPC_PORTRAIT_PACK.Generator.Archetype": "Archetype", + "NPC_PORTRAIT_PACK.Generator.Species": "Species", + "NPC_PORTRAIT_PACK.Generator.Count": "Count", + "NPC_PORTRAIT_PACK.Generator.Folder": "Place in Folder", + "NPC_PORTRAIT_PACK.Generator.Generate": "Generate NPC", + "NPC_PORTRAIT_PACK.Generator.LastGenerated": "Last Generated", + "NPC_PORTRAIT_PACK.Generator.Success": "{count} NPC(s) created successfully." +} diff --git a/module.json b/module.json new file mode 100644 index 0000000..b00358c --- /dev/null +++ b/module.json @@ -0,0 +1,38 @@ +{ + "id": "npc-portrait-pack", + "title": "NPC Portrait Pack \u2014 SLA Industries", + "description": "490 curated NPC portrait images for the SLA Industries / SLA Mothership system. Automatically assigns a species-matched portrait to new NPC actors on creation. Exposes game.npcPortraitPack API for use by other modules.", + "version": "1.0.0", + "compatibility": { + "minimum": "11", + "verified": "13" + }, + "authors": [ + { + "name": "SLA Mothership", + "discord": "" + } + ], + "esmodules": [ + "scripts/npc-portrait-pack.mjs" + ], + "styles": [ + "styles/npc-portrait-pack.css" + ], + "languages": [ + { + "lang": "en", + "name": "English", + "path": "lang/en.json" + } + ], + "flags": { + "npc-portrait-pack": { + "provides-portraits": true, + "system": "sla-mothership" + } + }, + "url": "https://github.com/slaguru666/npc-portrait-pack", + "manifest": "https://raw.githubusercontent.com/slaguru666/npc-portrait-pack/main/module.json", + "download": "https://github.com/slaguru666/npc-portrait-pack/releases/latest/download/npc-portrait-pack.zip" +} \ No newline at end of file diff --git a/portraits/creature_01.png b/portraits/creature_01.png new file mode 100644 index 0000000..ac4ccbd Binary files /dev/null and b/portraits/creature_01.png differ diff --git a/portraits/creature_02.png b/portraits/creature_02.png new file mode 100644 index 0000000..2f3a754 Binary files /dev/null and b/portraits/creature_02.png differ diff --git a/portraits/creature_03.png b/portraits/creature_03.png new file mode 100644 index 0000000..c5ac844 Binary files /dev/null and b/portraits/creature_03.png differ diff --git a/portraits/creature_04.png b/portraits/creature_04.png new file mode 100644 index 0000000..b199068 Binary files /dev/null and b/portraits/creature_04.png differ diff --git a/portraits/creature_05.png b/portraits/creature_05.png new file mode 100644 index 0000000..c6397a4 Binary files /dev/null and b/portraits/creature_05.png differ diff --git a/portraits/creature_06.png b/portraits/creature_06.png new file mode 100644 index 0000000..b9da6ef Binary files /dev/null and b/portraits/creature_06.png differ diff --git a/portraits/creature_07.png b/portraits/creature_07.png new file mode 100644 index 0000000..7c93a05 Binary files /dev/null and b/portraits/creature_07.png differ diff --git a/portraits/creature_08.png b/portraits/creature_08.png new file mode 100644 index 0000000..a38e787 Binary files /dev/null and b/portraits/creature_08.png differ diff --git a/portraits/creature_09.png b/portraits/creature_09.png new file mode 100644 index 0000000..a72c1db Binary files /dev/null and b/portraits/creature_09.png differ diff --git a/portraits/creature_10.png b/portraits/creature_10.png new file mode 100644 index 0000000..9d94d7b Binary files /dev/null and b/portraits/creature_10.png differ diff --git a/portraits/creature_11.png b/portraits/creature_11.png new file mode 100644 index 0000000..34f053f Binary files /dev/null and b/portraits/creature_11.png differ diff --git a/portraits/creature_12.png b/portraits/creature_12.png new file mode 100644 index 0000000..f8d6c17 Binary files /dev/null and b/portraits/creature_12.png differ diff --git a/portraits/creature_13.png b/portraits/creature_13.png new file mode 100644 index 0000000..9feff2d Binary files /dev/null and b/portraits/creature_13.png differ diff --git a/portraits/creature_14.png b/portraits/creature_14.png new file mode 100644 index 0000000..ccb2495 Binary files /dev/null and b/portraits/creature_14.png differ diff --git a/portraits/creature_15.png b/portraits/creature_15.png new file mode 100644 index 0000000..627c108 Binary files /dev/null and b/portraits/creature_15.png differ diff --git a/portraits/creature_16.png b/portraits/creature_16.png new file mode 100644 index 0000000..89095d1 Binary files /dev/null and b/portraits/creature_16.png differ diff --git a/portraits/creature_17.png b/portraits/creature_17.png new file mode 100644 index 0000000..780ccf3 Binary files /dev/null and b/portraits/creature_17.png differ diff --git a/portraits/creature_18.png b/portraits/creature_18.png new file mode 100644 index 0000000..c25ef84 Binary files /dev/null and b/portraits/creature_18.png differ diff --git a/portraits/creature_19.png b/portraits/creature_19.png new file mode 100644 index 0000000..4cd8fcd Binary files /dev/null and b/portraits/creature_19.png differ diff --git a/portraits/creature_20.png b/portraits/creature_20.png new file mode 100644 index 0000000..f206c92 Binary files /dev/null and b/portraits/creature_20.png differ diff --git a/portraits/creature_21.png b/portraits/creature_21.png new file mode 100644 index 0000000..a1e6140 Binary files /dev/null and b/portraits/creature_21.png differ diff --git a/portraits/creature_22.png b/portraits/creature_22.png new file mode 100644 index 0000000..79363e4 Binary files /dev/null and b/portraits/creature_22.png differ diff --git a/portraits/creature_23.png b/portraits/creature_23.png new file mode 100644 index 0000000..82f65de Binary files /dev/null and b/portraits/creature_23.png differ diff --git a/portraits/creature_24.png b/portraits/creature_24.png new file mode 100644 index 0000000..ffe1ee4 Binary files /dev/null and b/portraits/creature_24.png differ diff --git a/portraits/ebon_female_01.png b/portraits/ebon_female_01.png new file mode 100644 index 0000000..9ea611b Binary files /dev/null and b/portraits/ebon_female_01.png differ diff --git a/portraits/ebon_female_02.png b/portraits/ebon_female_02.png new file mode 100644 index 0000000..24b303f Binary files /dev/null and b/portraits/ebon_female_02.png differ diff --git a/portraits/ebon_female_03.png b/portraits/ebon_female_03.png new file mode 100644 index 0000000..6ad2c79 Binary files /dev/null and b/portraits/ebon_female_03.png differ diff --git a/portraits/ebon_female_04.png b/portraits/ebon_female_04.png new file mode 100644 index 0000000..67cbccc Binary files /dev/null and b/portraits/ebon_female_04.png differ diff --git a/portraits/ebon_female_05.png b/portraits/ebon_female_05.png new file mode 100644 index 0000000..f211c48 Binary files /dev/null and b/portraits/ebon_female_05.png differ diff --git a/portraits/ebon_female_06.png b/portraits/ebon_female_06.png new file mode 100644 index 0000000..30803ba Binary files /dev/null and b/portraits/ebon_female_06.png differ diff --git a/portraits/ebon_female_07.png b/portraits/ebon_female_07.png new file mode 100644 index 0000000..2b2ab43 Binary files /dev/null and b/portraits/ebon_female_07.png differ diff --git a/portraits/ebon_female_08.png b/portraits/ebon_female_08.png new file mode 100644 index 0000000..5872e84 Binary files /dev/null and b/portraits/ebon_female_08.png differ diff --git a/portraits/ebon_female_09.png b/portraits/ebon_female_09.png new file mode 100644 index 0000000..b8f7a2d Binary files /dev/null and b/portraits/ebon_female_09.png differ diff --git a/portraits/ebon_female_10.png b/portraits/ebon_female_10.png new file mode 100644 index 0000000..bd4022b Binary files /dev/null and b/portraits/ebon_female_10.png differ diff --git a/portraits/ebon_female_11.png b/portraits/ebon_female_11.png new file mode 100644 index 0000000..4b5ee33 Binary files /dev/null and b/portraits/ebon_female_11.png differ diff --git a/portraits/ebon_female_12.png b/portraits/ebon_female_12.png new file mode 100644 index 0000000..8ff31d8 Binary files /dev/null and b/portraits/ebon_female_12.png differ diff --git a/portraits/ebon_female_13.png b/portraits/ebon_female_13.png new file mode 100644 index 0000000..26ff827 Binary files /dev/null and b/portraits/ebon_female_13.png differ diff --git a/portraits/ebon_female_14.png b/portraits/ebon_female_14.png new file mode 100644 index 0000000..30871ff Binary files /dev/null and b/portraits/ebon_female_14.png differ diff --git a/portraits/ebon_female_15.png b/portraits/ebon_female_15.png new file mode 100644 index 0000000..852d6c8 Binary files /dev/null and b/portraits/ebon_female_15.png differ diff --git a/portraits/ebon_female_16.png b/portraits/ebon_female_16.png new file mode 100644 index 0000000..01153a1 Binary files /dev/null and b/portraits/ebon_female_16.png differ diff --git a/portraits/ebon_female_17.png b/portraits/ebon_female_17.png new file mode 100644 index 0000000..d917122 Binary files /dev/null and b/portraits/ebon_female_17.png differ diff --git a/portraits/ebon_female_18.png b/portraits/ebon_female_18.png new file mode 100644 index 0000000..f0b7c88 Binary files /dev/null and b/portraits/ebon_female_18.png differ diff --git a/portraits/ebon_female_19.png b/portraits/ebon_female_19.png new file mode 100644 index 0000000..8cd1438 Binary files /dev/null and b/portraits/ebon_female_19.png differ diff --git a/portraits/ebon_female_20.png b/portraits/ebon_female_20.png new file mode 100644 index 0000000..99728b6 Binary files /dev/null and b/portraits/ebon_female_20.png differ diff --git a/portraits/ebon_female_21.png b/portraits/ebon_female_21.png new file mode 100644 index 0000000..8cd0586 Binary files /dev/null and b/portraits/ebon_female_21.png differ diff --git a/portraits/ebon_male_01.png b/portraits/ebon_male_01.png new file mode 100644 index 0000000..5f28e0e Binary files /dev/null and b/portraits/ebon_male_01.png differ diff --git a/portraits/ebon_male_02.png b/portraits/ebon_male_02.png new file mode 100644 index 0000000..e8726ee Binary files /dev/null and b/portraits/ebon_male_02.png differ diff --git a/portraits/ebon_male_03.png b/portraits/ebon_male_03.png new file mode 100644 index 0000000..a937ae0 Binary files /dev/null and b/portraits/ebon_male_03.png differ diff --git a/portraits/ebon_male_04.png b/portraits/ebon_male_04.png new file mode 100644 index 0000000..089ad5f Binary files /dev/null and b/portraits/ebon_male_04.png differ diff --git a/portraits/ebon_male_05.png b/portraits/ebon_male_05.png new file mode 100644 index 0000000..2651bcc Binary files /dev/null and b/portraits/ebon_male_05.png differ diff --git a/portraits/ebon_male_06.png b/portraits/ebon_male_06.png new file mode 100644 index 0000000..4f24ee5 Binary files /dev/null and b/portraits/ebon_male_06.png differ diff --git a/portraits/ebon_male_07.png b/portraits/ebon_male_07.png new file mode 100644 index 0000000..7d0edf3 Binary files /dev/null and b/portraits/ebon_male_07.png differ diff --git a/portraits/ebon_male_08.png b/portraits/ebon_male_08.png new file mode 100644 index 0000000..eb657a4 Binary files /dev/null and b/portraits/ebon_male_08.png differ diff --git a/portraits/ebon_male_09.png b/portraits/ebon_male_09.png new file mode 100644 index 0000000..bbc7a02 Binary files /dev/null and b/portraits/ebon_male_09.png differ diff --git a/portraits/ebon_male_10.png b/portraits/ebon_male_10.png new file mode 100644 index 0000000..74d95fb Binary files /dev/null and b/portraits/ebon_male_10.png differ diff --git a/portraits/ebon_male_11.png b/portraits/ebon_male_11.png new file mode 100644 index 0000000..126b7d4 Binary files /dev/null and b/portraits/ebon_male_11.png differ diff --git a/portraits/ebon_male_12.png b/portraits/ebon_male_12.png new file mode 100644 index 0000000..ba2a113 Binary files /dev/null and b/portraits/ebon_male_12.png differ diff --git a/portraits/ebon_male_13.png b/portraits/ebon_male_13.png new file mode 100644 index 0000000..44d5b1e Binary files /dev/null and b/portraits/ebon_male_13.png differ diff --git a/portraits/ebon_male_14.png b/portraits/ebon_male_14.png new file mode 100644 index 0000000..b492fda Binary files /dev/null and b/portraits/ebon_male_14.png differ diff --git a/portraits/ebon_male_15.png b/portraits/ebon_male_15.png new file mode 100644 index 0000000..e6fe6c6 Binary files /dev/null and b/portraits/ebon_male_15.png differ diff --git a/portraits/ebon_male_16.png b/portraits/ebon_male_16.png new file mode 100644 index 0000000..98b5ea7 Binary files /dev/null and b/portraits/ebon_male_16.png differ diff --git a/portraits/ebon_male_17.png b/portraits/ebon_male_17.png new file mode 100644 index 0000000..14b70ed Binary files /dev/null and b/portraits/ebon_male_17.png differ diff --git a/portraits/ebon_male_18.png b/portraits/ebon_male_18.png new file mode 100644 index 0000000..2b4f390 Binary files /dev/null and b/portraits/ebon_male_18.png differ diff --git a/portraits/frother_female_01.png b/portraits/frother_female_01.png new file mode 100644 index 0000000..1e97e0a Binary files /dev/null and b/portraits/frother_female_01.png differ diff --git a/portraits/frother_female_02.png b/portraits/frother_female_02.png new file mode 100644 index 0000000..196b4e4 Binary files /dev/null and b/portraits/frother_female_02.png differ diff --git a/portraits/frother_female_03.png b/portraits/frother_female_03.png new file mode 100644 index 0000000..33dcd71 Binary files /dev/null and b/portraits/frother_female_03.png differ diff --git a/portraits/frother_female_04.png b/portraits/frother_female_04.png new file mode 100644 index 0000000..55d7279 Binary files /dev/null and b/portraits/frother_female_04.png differ diff --git a/portraits/frother_female_05.png b/portraits/frother_female_05.png new file mode 100644 index 0000000..9b38a7c Binary files /dev/null and b/portraits/frother_female_05.png differ diff --git a/portraits/frother_female_06.png b/portraits/frother_female_06.png new file mode 100644 index 0000000..de10eb8 Binary files /dev/null and b/portraits/frother_female_06.png differ diff --git a/portraits/frother_female_07.png b/portraits/frother_female_07.png new file mode 100644 index 0000000..96f94d7 Binary files /dev/null and b/portraits/frother_female_07.png differ diff --git a/portraits/frother_female_08.png b/portraits/frother_female_08.png new file mode 100644 index 0000000..6caa187 Binary files /dev/null and b/portraits/frother_female_08.png differ diff --git a/portraits/frother_female_09.png b/portraits/frother_female_09.png new file mode 100644 index 0000000..6b557ea Binary files /dev/null and b/portraits/frother_female_09.png differ diff --git a/portraits/frother_female_10.png b/portraits/frother_female_10.png new file mode 100644 index 0000000..a5999aa Binary files /dev/null and b/portraits/frother_female_10.png differ diff --git a/portraits/frother_female_11.png b/portraits/frother_female_11.png new file mode 100644 index 0000000..5415f90 Binary files /dev/null and b/portraits/frother_female_11.png differ diff --git a/portraits/frother_female_12.png b/portraits/frother_female_12.png new file mode 100644 index 0000000..0eef495 Binary files /dev/null and b/portraits/frother_female_12.png differ diff --git a/portraits/frother_female_13.png b/portraits/frother_female_13.png new file mode 100644 index 0000000..e3303f5 Binary files /dev/null and b/portraits/frother_female_13.png differ diff --git a/portraits/frother_female_14.png b/portraits/frother_female_14.png new file mode 100644 index 0000000..5c9be08 Binary files /dev/null and b/portraits/frother_female_14.png differ diff --git a/portraits/frother_female_15.png b/portraits/frother_female_15.png new file mode 100644 index 0000000..00f5391 Binary files /dev/null and b/portraits/frother_female_15.png differ diff --git a/portraits/frother_female_16.png b/portraits/frother_female_16.png new file mode 100644 index 0000000..9265dee Binary files /dev/null and b/portraits/frother_female_16.png differ diff --git a/portraits/frother_female_17.png b/portraits/frother_female_17.png new file mode 100644 index 0000000..0236ba6 Binary files /dev/null and b/portraits/frother_female_17.png differ diff --git a/portraits/frother_female_18.png b/portraits/frother_female_18.png new file mode 100644 index 0000000..34c2e17 Binary files /dev/null and b/portraits/frother_female_18.png differ diff --git a/portraits/frother_female_19.png b/portraits/frother_female_19.png new file mode 100644 index 0000000..7de6c23 Binary files /dev/null and b/portraits/frother_female_19.png differ diff --git a/portraits/frother_female_20.png b/portraits/frother_female_20.png new file mode 100644 index 0000000..25fdc3c Binary files /dev/null and b/portraits/frother_female_20.png differ diff --git a/portraits/frother_female_21.png b/portraits/frother_female_21.png new file mode 100644 index 0000000..10f91d4 Binary files /dev/null and b/portraits/frother_female_21.png differ diff --git a/portraits/frother_male_01.png b/portraits/frother_male_01.png new file mode 100644 index 0000000..0d9b0e5 Binary files /dev/null and b/portraits/frother_male_01.png differ diff --git a/portraits/frother_male_02.png b/portraits/frother_male_02.png new file mode 100644 index 0000000..f1e0c00 Binary files /dev/null and b/portraits/frother_male_02.png differ diff --git a/portraits/frother_male_03.png b/portraits/frother_male_03.png new file mode 100644 index 0000000..3cddd53 Binary files /dev/null and b/portraits/frother_male_03.png differ diff --git a/portraits/frother_male_05.png b/portraits/frother_male_05.png new file mode 100644 index 0000000..c1f70ab Binary files /dev/null and b/portraits/frother_male_05.png differ diff --git a/portraits/frother_male_06.png b/portraits/frother_male_06.png new file mode 100644 index 0000000..325d426 Binary files /dev/null and b/portraits/frother_male_06.png differ diff --git a/portraits/frother_male_07.png b/portraits/frother_male_07.png new file mode 100644 index 0000000..4cc2497 Binary files /dev/null and b/portraits/frother_male_07.png differ diff --git a/portraits/frother_male_08.png b/portraits/frother_male_08.png new file mode 100644 index 0000000..47c8b2f Binary files /dev/null and b/portraits/frother_male_08.png differ diff --git a/portraits/frother_male_09.png b/portraits/frother_male_09.png new file mode 100644 index 0000000..f37e669 Binary files /dev/null and b/portraits/frother_male_09.png differ diff --git a/portraits/frother_male_10.png b/portraits/frother_male_10.png new file mode 100644 index 0000000..ef23851 Binary files /dev/null and b/portraits/frother_male_10.png differ diff --git a/portraits/frother_male_11.png b/portraits/frother_male_11.png new file mode 100644 index 0000000..306c1f0 Binary files /dev/null and b/portraits/frother_male_11.png differ diff --git a/portraits/frother_male_12.png b/portraits/frother_male_12.png new file mode 100644 index 0000000..47e1696 Binary files /dev/null and b/portraits/frother_male_12.png differ diff --git a/portraits/frother_male_13.png b/portraits/frother_male_13.png new file mode 100644 index 0000000..c727aad Binary files /dev/null and b/portraits/frother_male_13.png differ diff --git a/portraits/frother_male_14.png b/portraits/frother_male_14.png new file mode 100644 index 0000000..46b5100 Binary files /dev/null and b/portraits/frother_male_14.png differ diff --git a/portraits/frother_male_15.png b/portraits/frother_male_15.png new file mode 100644 index 0000000..4c2ce27 Binary files /dev/null and b/portraits/frother_male_15.png differ diff --git a/portraits/frother_male_16.png b/portraits/frother_male_16.png new file mode 100644 index 0000000..cd7e7ee Binary files /dev/null and b/portraits/frother_male_16.png differ diff --git a/portraits/frother_male_17.png b/portraits/frother_male_17.png new file mode 100644 index 0000000..3e09d26 Binary files /dev/null and b/portraits/frother_male_17.png differ diff --git a/portraits/frother_male_18.png b/portraits/frother_male_18.png new file mode 100644 index 0000000..f32bb0f Binary files /dev/null and b/portraits/frother_male_18.png differ diff --git a/portraits/frother_male_19.png b/portraits/frother_male_19.png new file mode 100644 index 0000000..c1274ea Binary files /dev/null and b/portraits/frother_male_19.png differ diff --git a/portraits/frother_male_20.png b/portraits/frother_male_20.png new file mode 100644 index 0000000..d97d1bf Binary files /dev/null and b/portraits/frother_male_20.png differ diff --git a/portraits/frother_male_21.png b/portraits/frother_male_21.png new file mode 100644 index 0000000..a892b85 Binary files /dev/null and b/portraits/frother_male_21.png differ diff --git a/portraits/frother_male_22.png b/portraits/frother_male_22.png new file mode 100644 index 0000000..6edf1bb Binary files /dev/null and b/portraits/frother_male_22.png differ diff --git a/portraits/frother_male_23.png b/portraits/frother_male_23.png new file mode 100644 index 0000000..cdc0bec Binary files /dev/null and b/portraits/frother_male_23.png differ diff --git a/portraits/frother_male_24.png b/portraits/frother_male_24.png new file mode 100644 index 0000000..767519f Binary files /dev/null and b/portraits/frother_male_24.png differ diff --git a/portraits/human_female_01.png b/portraits/human_female_01.png new file mode 100644 index 0000000..d8e9fc9 Binary files /dev/null and b/portraits/human_female_01.png differ diff --git a/portraits/human_female_02.png b/portraits/human_female_02.png new file mode 100644 index 0000000..0ca0a39 Binary files /dev/null and b/portraits/human_female_02.png differ diff --git a/portraits/human_female_03.png b/portraits/human_female_03.png new file mode 100644 index 0000000..fc4a9f0 Binary files /dev/null and b/portraits/human_female_03.png differ diff --git a/portraits/human_female_04.png b/portraits/human_female_04.png new file mode 100644 index 0000000..7be777b Binary files /dev/null and b/portraits/human_female_04.png differ diff --git a/portraits/human_female_05.png b/portraits/human_female_05.png new file mode 100644 index 0000000..dac1770 Binary files /dev/null and b/portraits/human_female_05.png differ diff --git a/portraits/human_female_06.png b/portraits/human_female_06.png new file mode 100644 index 0000000..9ebc524 Binary files /dev/null and b/portraits/human_female_06.png differ diff --git a/portraits/human_female_07.png b/portraits/human_female_07.png new file mode 100644 index 0000000..aab9fa8 Binary files /dev/null and b/portraits/human_female_07.png differ diff --git a/portraits/human_female_08.png b/portraits/human_female_08.png new file mode 100644 index 0000000..7782791 Binary files /dev/null and b/portraits/human_female_08.png differ diff --git a/portraits/human_female_09.png b/portraits/human_female_09.png new file mode 100644 index 0000000..647f748 Binary files /dev/null and b/portraits/human_female_09.png differ diff --git a/portraits/human_female_10.png b/portraits/human_female_10.png new file mode 100644 index 0000000..5b8d39a Binary files /dev/null and b/portraits/human_female_10.png differ diff --git a/portraits/human_female_11.png b/portraits/human_female_11.png new file mode 100644 index 0000000..1c46c1f Binary files /dev/null and b/portraits/human_female_11.png differ diff --git a/portraits/human_female_12.png b/portraits/human_female_12.png new file mode 100644 index 0000000..6f3f453 Binary files /dev/null and b/portraits/human_female_12.png differ diff --git a/portraits/human_female_13.png b/portraits/human_female_13.png new file mode 100644 index 0000000..8069c93 Binary files /dev/null and b/portraits/human_female_13.png differ diff --git a/portraits/human_female_14.png b/portraits/human_female_14.png new file mode 100644 index 0000000..9bcfe73 Binary files /dev/null and b/portraits/human_female_14.png differ diff --git a/portraits/human_female_15.png b/portraits/human_female_15.png new file mode 100644 index 0000000..b5d2aa8 Binary files /dev/null and b/portraits/human_female_15.png differ diff --git a/portraits/human_female_16.png b/portraits/human_female_16.png new file mode 100644 index 0000000..f112df6 Binary files /dev/null and b/portraits/human_female_16.png differ diff --git a/portraits/human_female_17.png b/portraits/human_female_17.png new file mode 100644 index 0000000..0b74592 Binary files /dev/null and b/portraits/human_female_17.png differ diff --git a/portraits/human_female_18.png b/portraits/human_female_18.png new file mode 100644 index 0000000..ebb3ec6 Binary files /dev/null and b/portraits/human_female_18.png differ diff --git a/portraits/human_male_01.png b/portraits/human_male_01.png new file mode 100644 index 0000000..9a638be Binary files /dev/null and b/portraits/human_male_01.png differ diff --git a/portraits/human_male_02.png b/portraits/human_male_02.png new file mode 100644 index 0000000..cad5b28 Binary files /dev/null and b/portraits/human_male_02.png differ diff --git a/portraits/human_male_03.png b/portraits/human_male_03.png new file mode 100644 index 0000000..82519ba Binary files /dev/null and b/portraits/human_male_03.png differ diff --git a/portraits/human_male_04.png b/portraits/human_male_04.png new file mode 100644 index 0000000..5fbe2f9 Binary files /dev/null and b/portraits/human_male_04.png differ diff --git a/portraits/human_male_06.png b/portraits/human_male_06.png new file mode 100644 index 0000000..f6921fc Binary files /dev/null and b/portraits/human_male_06.png differ diff --git a/portraits/human_male_07.png b/portraits/human_male_07.png new file mode 100644 index 0000000..3eda28b Binary files /dev/null and b/portraits/human_male_07.png differ diff --git a/portraits/human_male_08.png b/portraits/human_male_08.png new file mode 100644 index 0000000..17d00f1 Binary files /dev/null and b/portraits/human_male_08.png differ diff --git a/portraits/human_male_09.png b/portraits/human_male_09.png new file mode 100644 index 0000000..4a4893e Binary files /dev/null and b/portraits/human_male_09.png differ diff --git a/portraits/human_male_10.png b/portraits/human_male_10.png new file mode 100644 index 0000000..d7812a2 Binary files /dev/null and b/portraits/human_male_10.png differ diff --git a/portraits/human_male_11.png b/portraits/human_male_11.png new file mode 100644 index 0000000..3365e48 Binary files /dev/null and b/portraits/human_male_11.png differ diff --git a/portraits/human_male_12.png b/portraits/human_male_12.png new file mode 100644 index 0000000..62395d1 Binary files /dev/null and b/portraits/human_male_12.png differ diff --git a/portraits/human_male_14.png b/portraits/human_male_14.png new file mode 100644 index 0000000..fd6fa0f Binary files /dev/null and b/portraits/human_male_14.png differ diff --git a/portraits/human_male_15.png b/portraits/human_male_15.png new file mode 100644 index 0000000..40d1b65 Binary files /dev/null and b/portraits/human_male_15.png differ diff --git a/portraits/human_male_16.png b/portraits/human_male_16.png new file mode 100644 index 0000000..0992258 Binary files /dev/null and b/portraits/human_male_16.png differ diff --git a/portraits/human_male_17.png b/portraits/human_male_17.png new file mode 100644 index 0000000..a59b340 Binary files /dev/null and b/portraits/human_male_17.png differ diff --git a/portraits/human_male_18.png b/portraits/human_male_18.png new file mode 100644 index 0000000..998b2bb Binary files /dev/null and b/portraits/human_male_18.png differ diff --git a/portraits/human_male_19.png b/portraits/human_male_19.png new file mode 100644 index 0000000..1f39ea0 Binary files /dev/null and b/portraits/human_male_19.png differ diff --git a/portraits/human_male_20.png b/portraits/human_male_20.png new file mode 100644 index 0000000..1b95d15 Binary files /dev/null and b/portraits/human_male_20.png differ diff --git a/portraits/human_male_21.png b/portraits/human_male_21.png new file mode 100644 index 0000000..9a638be Binary files /dev/null and b/portraits/human_male_21.png differ diff --git a/portraits/human_male_22.png b/portraits/human_male_22.png new file mode 100644 index 0000000..cad5b28 Binary files /dev/null and b/portraits/human_male_22.png differ diff --git a/portraits/human_male_23.png b/portraits/human_male_23.png new file mode 100644 index 0000000..82519ba Binary files /dev/null and b/portraits/human_male_23.png differ diff --git a/portraits/human_male_24.png b/portraits/human_male_24.png new file mode 100644 index 0000000..5fbe2f9 Binary files /dev/null and b/portraits/human_male_24.png differ diff --git a/portraits/human_male_25.png b/portraits/human_male_25.png new file mode 100644 index 0000000..f6921fc Binary files /dev/null and b/portraits/human_male_25.png differ diff --git a/portraits/human_male_26.png b/portraits/human_male_26.png new file mode 100644 index 0000000..17d00f1 Binary files /dev/null and b/portraits/human_male_26.png differ diff --git a/portraits/human_male_27.png b/portraits/human_male_27.png new file mode 100644 index 0000000..3eda28b Binary files /dev/null and b/portraits/human_male_27.png differ diff --git a/portraits/human_male_28.png b/portraits/human_male_28.png new file mode 100644 index 0000000..4a4893e Binary files /dev/null and b/portraits/human_male_28.png differ diff --git a/portraits/human_male_29.png b/portraits/human_male_29.png new file mode 100644 index 0000000..d7812a2 Binary files /dev/null and b/portraits/human_male_29.png differ diff --git a/portraits/human_male_30.png b/portraits/human_male_30.png new file mode 100644 index 0000000..3365e48 Binary files /dev/null and b/portraits/human_male_30.png differ diff --git a/portraits/human_male_31.png b/portraits/human_male_31.png new file mode 100644 index 0000000..62395d1 Binary files /dev/null and b/portraits/human_male_31.png differ diff --git a/portraits/human_male_32.png b/portraits/human_male_32.png new file mode 100644 index 0000000..fd6fa0f Binary files /dev/null and b/portraits/human_male_32.png differ diff --git a/portraits/human_male_33.png b/portraits/human_male_33.png new file mode 100644 index 0000000..40d1b65 Binary files /dev/null and b/portraits/human_male_33.png differ diff --git a/portraits/human_male_34.png b/portraits/human_male_34.png new file mode 100644 index 0000000..0992258 Binary files /dev/null and b/portraits/human_male_34.png differ diff --git a/portraits/human_male_35.png b/portraits/human_male_35.png new file mode 100644 index 0000000..a59b340 Binary files /dev/null and b/portraits/human_male_35.png differ diff --git a/portraits/human_male_36.png b/portraits/human_male_36.png new file mode 100644 index 0000000..998b2bb Binary files /dev/null and b/portraits/human_male_36.png differ diff --git a/portraits/human_male_37.png b/portraits/human_male_37.png new file mode 100644 index 0000000..1f39ea0 Binary files /dev/null and b/portraits/human_male_37.png differ diff --git a/portraits/human_male_38.png b/portraits/human_male_38.png new file mode 100644 index 0000000..1b95d15 Binary files /dev/null and b/portraits/human_male_38.png differ diff --git a/portraits/index.json b/portraits/index.json new file mode 100644 index 0000000..4f22de8 --- /dev/null +++ b/portraits/index.json @@ -0,0 +1,5039 @@ +{ + "version": "1.0.0", + "count": 490, + "portraits": [ + { + "id": "creature_01", + "path": "modules/npc-portrait-pack/portraits/creature_01.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_02", + "path": "modules/npc-portrait-pack/portraits/creature_02.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_03", + "path": "modules/npc-portrait-pack/portraits/creature_03.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_04", + "path": "modules/npc-portrait-pack/portraits/creature_04.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_05", + "path": "modules/npc-portrait-pack/portraits/creature_05.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_06", + "path": "modules/npc-portrait-pack/portraits/creature_06.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_07", + "path": "modules/npc-portrait-pack/portraits/creature_07.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_08", + "path": "modules/npc-portrait-pack/portraits/creature_08.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_09", + "path": "modules/npc-portrait-pack/portraits/creature_09.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_10", + "path": "modules/npc-portrait-pack/portraits/creature_10.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_11", + "path": "modules/npc-portrait-pack/portraits/creature_11.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_12", + "path": "modules/npc-portrait-pack/portraits/creature_12.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_13", + "path": "modules/npc-portrait-pack/portraits/creature_13.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_14", + "path": "modules/npc-portrait-pack/portraits/creature_14.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_15", + "path": "modules/npc-portrait-pack/portraits/creature_15.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_16", + "path": "modules/npc-portrait-pack/portraits/creature_16.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_17", + "path": "modules/npc-portrait-pack/portraits/creature_17.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_18", + "path": "modules/npc-portrait-pack/portraits/creature_18.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_19", + "path": "modules/npc-portrait-pack/portraits/creature_19.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_20", + "path": "modules/npc-portrait-pack/portraits/creature_20.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_21", + "path": "modules/npc-portrait-pack/portraits/creature_21.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_22", + "path": "modules/npc-portrait-pack/portraits/creature_22.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_23", + "path": "modules/npc-portrait-pack/portraits/creature_23.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "creature_24", + "path": "modules/npc-portrait-pack/portraits/creature_24.png", + "category": "creature", + "race": "creature", + "gender": null, + "tags": [ + "creature" + ] + }, + { + "id": "ebon_female_01", + "path": "modules/npc-portrait-pack/portraits/ebon_female_01.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_02", + "path": "modules/npc-portrait-pack/portraits/ebon_female_02.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_03", + "path": "modules/npc-portrait-pack/portraits/ebon_female_03.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_04", + "path": "modules/npc-portrait-pack/portraits/ebon_female_04.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_05", + "path": "modules/npc-portrait-pack/portraits/ebon_female_05.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_06", + "path": "modules/npc-portrait-pack/portraits/ebon_female_06.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_07", + "path": "modules/npc-portrait-pack/portraits/ebon_female_07.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_08", + "path": "modules/npc-portrait-pack/portraits/ebon_female_08.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_09", + "path": "modules/npc-portrait-pack/portraits/ebon_female_09.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_10", + "path": "modules/npc-portrait-pack/portraits/ebon_female_10.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_11", + "path": "modules/npc-portrait-pack/portraits/ebon_female_11.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_12", + "path": "modules/npc-portrait-pack/portraits/ebon_female_12.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_13", + "path": "modules/npc-portrait-pack/portraits/ebon_female_13.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_14", + "path": "modules/npc-portrait-pack/portraits/ebon_female_14.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_15", + "path": "modules/npc-portrait-pack/portraits/ebon_female_15.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_16", + "path": "modules/npc-portrait-pack/portraits/ebon_female_16.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_17", + "path": "modules/npc-portrait-pack/portraits/ebon_female_17.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_18", + "path": "modules/npc-portrait-pack/portraits/ebon_female_18.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_19", + "path": "modules/npc-portrait-pack/portraits/ebon_female_19.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_20", + "path": "modules/npc-portrait-pack/portraits/ebon_female_20.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_female_21", + "path": "modules/npc-portrait-pack/portraits/ebon_female_21.png", + "category": "ebon", + "race": "ebon", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "ebon_male_01", + "path": "modules/npc-portrait-pack/portraits/ebon_male_01.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_02", + "path": "modules/npc-portrait-pack/portraits/ebon_male_02.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_03", + "path": "modules/npc-portrait-pack/portraits/ebon_male_03.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_04", + "path": "modules/npc-portrait-pack/portraits/ebon_male_04.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_05", + "path": "modules/npc-portrait-pack/portraits/ebon_male_05.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_06", + "path": "modules/npc-portrait-pack/portraits/ebon_male_06.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_07", + "path": "modules/npc-portrait-pack/portraits/ebon_male_07.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_08", + "path": "modules/npc-portrait-pack/portraits/ebon_male_08.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_09", + "path": "modules/npc-portrait-pack/portraits/ebon_male_09.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_10", + "path": "modules/npc-portrait-pack/portraits/ebon_male_10.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_11", + "path": "modules/npc-portrait-pack/portraits/ebon_male_11.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_12", + "path": "modules/npc-portrait-pack/portraits/ebon_male_12.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_13", + "path": "modules/npc-portrait-pack/portraits/ebon_male_13.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_14", + "path": "modules/npc-portrait-pack/portraits/ebon_male_14.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_15", + "path": "modules/npc-portrait-pack/portraits/ebon_male_15.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_16", + "path": "modules/npc-portrait-pack/portraits/ebon_male_16.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_17", + "path": "modules/npc-portrait-pack/portraits/ebon_male_17.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "ebon_male_18", + "path": "modules/npc-portrait-pack/portraits/ebon_male_18.png", + "category": "ebon", + "race": "ebon", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_female_01", + "path": "modules/npc-portrait-pack/portraits/frother_female_01.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_02", + "path": "modules/npc-portrait-pack/portraits/frother_female_02.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_03", + "path": "modules/npc-portrait-pack/portraits/frother_female_03.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_04", + "path": "modules/npc-portrait-pack/portraits/frother_female_04.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_05", + "path": "modules/npc-portrait-pack/portraits/frother_female_05.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_06", + "path": "modules/npc-portrait-pack/portraits/frother_female_06.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_07", + "path": "modules/npc-portrait-pack/portraits/frother_female_07.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_08", + "path": "modules/npc-portrait-pack/portraits/frother_female_08.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_09", + "path": "modules/npc-portrait-pack/portraits/frother_female_09.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_10", + "path": "modules/npc-portrait-pack/portraits/frother_female_10.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_11", + "path": "modules/npc-portrait-pack/portraits/frother_female_11.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_12", + "path": "modules/npc-portrait-pack/portraits/frother_female_12.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_13", + "path": "modules/npc-portrait-pack/portraits/frother_female_13.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_14", + "path": "modules/npc-portrait-pack/portraits/frother_female_14.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_15", + "path": "modules/npc-portrait-pack/portraits/frother_female_15.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_16", + "path": "modules/npc-portrait-pack/portraits/frother_female_16.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_17", + "path": "modules/npc-portrait-pack/portraits/frother_female_17.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_18", + "path": "modules/npc-portrait-pack/portraits/frother_female_18.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_19", + "path": "modules/npc-portrait-pack/portraits/frother_female_19.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_20", + "path": "modules/npc-portrait-pack/portraits/frother_female_20.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_female_21", + "path": "modules/npc-portrait-pack/portraits/frother_female_21.png", + "category": "frother", + "race": "frother", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "frother_male_01", + "path": "modules/npc-portrait-pack/portraits/frother_male_01.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_02", + "path": "modules/npc-portrait-pack/portraits/frother_male_02.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_03", + "path": "modules/npc-portrait-pack/portraits/frother_male_03.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_05", + "path": "modules/npc-portrait-pack/portraits/frother_male_05.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_06", + "path": "modules/npc-portrait-pack/portraits/frother_male_06.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_07", + "path": "modules/npc-portrait-pack/portraits/frother_male_07.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_08", + "path": "modules/npc-portrait-pack/portraits/frother_male_08.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_09", + "path": "modules/npc-portrait-pack/portraits/frother_male_09.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_10", + "path": "modules/npc-portrait-pack/portraits/frother_male_10.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_11", + "path": "modules/npc-portrait-pack/portraits/frother_male_11.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_12", + "path": "modules/npc-portrait-pack/portraits/frother_male_12.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_13", + "path": "modules/npc-portrait-pack/portraits/frother_male_13.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_14", + "path": "modules/npc-portrait-pack/portraits/frother_male_14.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_15", + "path": "modules/npc-portrait-pack/portraits/frother_male_15.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_16", + "path": "modules/npc-portrait-pack/portraits/frother_male_16.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_17", + "path": "modules/npc-portrait-pack/portraits/frother_male_17.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_18", + "path": "modules/npc-portrait-pack/portraits/frother_male_18.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_19", + "path": "modules/npc-portrait-pack/portraits/frother_male_19.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_20", + "path": "modules/npc-portrait-pack/portraits/frother_male_20.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_21", + "path": "modules/npc-portrait-pack/portraits/frother_male_21.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_22", + "path": "modules/npc-portrait-pack/portraits/frother_male_22.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_23", + "path": "modules/npc-portrait-pack/portraits/frother_male_23.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "frother_male_24", + "path": "modules/npc-portrait-pack/portraits/frother_male_24.png", + "category": "frother", + "race": "frother", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_female_01", + "path": "modules/npc-portrait-pack/portraits/human_female_01.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_02", + "path": "modules/npc-portrait-pack/portraits/human_female_02.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_03", + "path": "modules/npc-portrait-pack/portraits/human_female_03.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_04", + "path": "modules/npc-portrait-pack/portraits/human_female_04.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_05", + "path": "modules/npc-portrait-pack/portraits/human_female_05.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_06", + "path": "modules/npc-portrait-pack/portraits/human_female_06.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_07", + "path": "modules/npc-portrait-pack/portraits/human_female_07.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_08", + "path": "modules/npc-portrait-pack/portraits/human_female_08.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_09", + "path": "modules/npc-portrait-pack/portraits/human_female_09.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_10", + "path": "modules/npc-portrait-pack/portraits/human_female_10.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_11", + "path": "modules/npc-portrait-pack/portraits/human_female_11.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_12", + "path": "modules/npc-portrait-pack/portraits/human_female_12.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_13", + "path": "modules/npc-portrait-pack/portraits/human_female_13.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_14", + "path": "modules/npc-portrait-pack/portraits/human_female_14.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_15", + "path": "modules/npc-portrait-pack/portraits/human_female_15.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_16", + "path": "modules/npc-portrait-pack/portraits/human_female_16.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_17", + "path": "modules/npc-portrait-pack/portraits/human_female_17.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_female_18", + "path": "modules/npc-portrait-pack/portraits/human_female_18.png", + "category": "human", + "race": "human", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "human_male_01", + "path": "modules/npc-portrait-pack/portraits/human_male_01.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_02", + "path": "modules/npc-portrait-pack/portraits/human_male_02.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_03", + "path": "modules/npc-portrait-pack/portraits/human_male_03.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_04", + "path": "modules/npc-portrait-pack/portraits/human_male_04.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_06", + "path": "modules/npc-portrait-pack/portraits/human_male_06.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_07", + "path": "modules/npc-portrait-pack/portraits/human_male_07.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_08", + "path": "modules/npc-portrait-pack/portraits/human_male_08.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_09", + "path": "modules/npc-portrait-pack/portraits/human_male_09.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_10", + "path": "modules/npc-portrait-pack/portraits/human_male_10.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_11", + "path": "modules/npc-portrait-pack/portraits/human_male_11.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_12", + "path": "modules/npc-portrait-pack/portraits/human_male_12.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_14", + "path": "modules/npc-portrait-pack/portraits/human_male_14.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_15", + "path": "modules/npc-portrait-pack/portraits/human_male_15.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_16", + "path": "modules/npc-portrait-pack/portraits/human_male_16.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_17", + "path": "modules/npc-portrait-pack/portraits/human_male_17.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_18", + "path": "modules/npc-portrait-pack/portraits/human_male_18.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_19", + "path": "modules/npc-portrait-pack/portraits/human_male_19.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_20", + "path": "modules/npc-portrait-pack/portraits/human_male_20.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_21", + "path": "modules/npc-portrait-pack/portraits/human_male_21.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_22", + "path": "modules/npc-portrait-pack/portraits/human_male_22.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_23", + "path": "modules/npc-portrait-pack/portraits/human_male_23.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_24", + "path": "modules/npc-portrait-pack/portraits/human_male_24.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_25", + "path": "modules/npc-portrait-pack/portraits/human_male_25.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_26", + "path": "modules/npc-portrait-pack/portraits/human_male_26.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_27", + "path": "modules/npc-portrait-pack/portraits/human_male_27.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_28", + "path": "modules/npc-portrait-pack/portraits/human_male_28.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_29", + "path": "modules/npc-portrait-pack/portraits/human_male_29.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_30", + "path": "modules/npc-portrait-pack/portraits/human_male_30.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_31", + "path": "modules/npc-portrait-pack/portraits/human_male_31.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_32", + "path": "modules/npc-portrait-pack/portraits/human_male_32.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_33", + "path": "modules/npc-portrait-pack/portraits/human_male_33.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_34", + "path": "modules/npc-portrait-pack/portraits/human_male_34.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_35", + "path": "modules/npc-portrait-pack/portraits/human_male_35.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_36", + "path": "modules/npc-portrait-pack/portraits/human_male_36.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_37", + "path": "modules/npc-portrait-pack/portraits/human_male_37.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "human_male_38", + "path": "modules/npc-portrait-pack/portraits/human_male_38.png", + "category": "human", + "race": "human", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "mutant_01", + "path": "modules/npc-portrait-pack/portraits/mutant_01.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_02", + "path": "modules/npc-portrait-pack/portraits/mutant_02.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_03", + "path": "modules/npc-portrait-pack/portraits/mutant_03.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_04", + "path": "modules/npc-portrait-pack/portraits/mutant_04.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_05", + "path": "modules/npc-portrait-pack/portraits/mutant_05.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_06", + "path": "modules/npc-portrait-pack/portraits/mutant_06.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_07", + "path": "modules/npc-portrait-pack/portraits/mutant_07.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_08", + "path": "modules/npc-portrait-pack/portraits/mutant_08.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_09", + "path": "modules/npc-portrait-pack/portraits/mutant_09.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_10", + "path": "modules/npc-portrait-pack/portraits/mutant_10.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_11", + "path": "modules/npc-portrait-pack/portraits/mutant_11.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_12", + "path": "modules/npc-portrait-pack/portraits/mutant_12.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_13", + "path": "modules/npc-portrait-pack/portraits/mutant_13.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_14", + "path": "modules/npc-portrait-pack/portraits/mutant_14.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_15", + "path": "modules/npc-portrait-pack/portraits/mutant_15.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_16", + "path": "modules/npc-portrait-pack/portraits/mutant_16.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_17", + "path": "modules/npc-portrait-pack/portraits/mutant_17.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_18", + "path": "modules/npc-portrait-pack/portraits/mutant_18.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_19", + "path": "modules/npc-portrait-pack/portraits/mutant_19.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_20", + "path": "modules/npc-portrait-pack/portraits/mutant_20.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_21", + "path": "modules/npc-portrait-pack/portraits/mutant_21.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_22", + "path": "modules/npc-portrait-pack/portraits/mutant_22.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_23", + "path": "modules/npc-portrait-pack/portraits/mutant_23.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_24", + "path": "modules/npc-portrait-pack/portraits/mutant_24.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_25", + "path": "modules/npc-portrait-pack/portraits/mutant_25.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_26", + "path": "modules/npc-portrait-pack/portraits/mutant_26.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_27", + "path": "modules/npc-portrait-pack/portraits/mutant_27.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_28", + "path": "modules/npc-portrait-pack/portraits/mutant_28.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_29", + "path": "modules/npc-portrait-pack/portraits/mutant_29.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_30", + "path": "modules/npc-portrait-pack/portraits/mutant_30.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_31", + "path": "modules/npc-portrait-pack/portraits/mutant_31.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_32", + "path": "modules/npc-portrait-pack/portraits/mutant_32.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_33", + "path": "modules/npc-portrait-pack/portraits/mutant_33.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_34", + "path": "modules/npc-portrait-pack/portraits/mutant_34.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_35", + "path": "modules/npc-portrait-pack/portraits/mutant_35.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_36", + "path": "modules/npc-portrait-pack/portraits/mutant_36.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_37", + "path": "modules/npc-portrait-pack/portraits/mutant_37.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_38", + "path": "modules/npc-portrait-pack/portraits/mutant_38.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_39", + "path": "modules/npc-portrait-pack/portraits/mutant_39.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_40", + "path": "modules/npc-portrait-pack/portraits/mutant_40.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_41", + "path": "modules/npc-portrait-pack/portraits/mutant_41.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_42", + "path": "modules/npc-portrait-pack/portraits/mutant_42.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_43", + "path": "modules/npc-portrait-pack/portraits/mutant_43.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_44", + "path": "modules/npc-portrait-pack/portraits/mutant_44.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_45", + "path": "modules/npc-portrait-pack/portraits/mutant_45.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_46", + "path": "modules/npc-portrait-pack/portraits/mutant_46.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_47", + "path": "modules/npc-portrait-pack/portraits/mutant_47.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_48", + "path": "modules/npc-portrait-pack/portraits/mutant_48.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_49", + "path": "modules/npc-portrait-pack/portraits/mutant_49.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_50", + "path": "modules/npc-portrait-pack/portraits/mutant_50.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_51", + "path": "modules/npc-portrait-pack/portraits/mutant_51.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_52", + "path": "modules/npc-portrait-pack/portraits/mutant_52.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_53", + "path": "modules/npc-portrait-pack/portraits/mutant_53.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_54", + "path": "modules/npc-portrait-pack/portraits/mutant_54.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_55", + "path": "modules/npc-portrait-pack/portraits/mutant_55.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_56", + "path": "modules/npc-portrait-pack/portraits/mutant_56.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "mutant_57", + "path": "modules/npc-portrait-pack/portraits/mutant_57.png", + "category": "creature", + "race": "mutant", + "gender": null, + "tags": [ + "mutant", + "creature" + ] + }, + { + "id": "npc_1001_male_beanie", + "path": "modules/npc-portrait-pack/portraits/npc_1001_male_beanie.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "beanie" + ] + }, + { + "id": "npc_1002_female_wild_hair", + "path": "modules/npc-portrait-pack/portraits/npc_1002_female_wild_hair.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "wild", + "hair" + ] + }, + { + "id": "npc_1003_male_armored_helmet", + "path": "modules/npc-portrait-pack/portraits/npc_1003_male_armored_helmet.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "armored", + "helmet" + ] + }, + { + "id": "npc_1004_male_cyborg_mohawk", + "path": "modules/npc-portrait-pack/portraits/npc_1004_male_cyborg_mohawk.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "cyborg", + "mohawk" + ] + }, + { + "id": "npc_1005_female_uniform", + "path": "modules/npc-portrait-pack/portraits/npc_1005_female_uniform.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "uniform" + ] + }, + { + "id": "npc_1006_male_red_eye_cyborg", + "path": "modules/npc-portrait-pack/portraits/npc_1006_male_red_eye_cyborg.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "red", + "eye", + "cyborg" + ] + }, + { + "id": "npc_1007_elderly_hooded", + "path": "modules/npc-portrait-pack/portraits/npc_1007_elderly_hooded.png", + "category": "named_npc", + "race": "human", + "gender": null, + "tags": [ + "elderly", + "hooded" + ] + }, + { + "id": "npc_1008_male_cyborg_raider", + "path": "modules/npc-portrait-pack/portraits/npc_1008_male_cyborg_raider.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "cyborg", + "raider" + ] + }, + { + "id": "npc_1009_male_goggle_doctor", + "path": "modules/npc-portrait-pack/portraits/npc_1009_male_goggle_doctor.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "goggle", + "doctor" + ] + }, + { + "id": "npc_1010_female_tattoo_punk", + "path": "modules/npc-portrait-pack/portraits/npc_1010_female_tattoo_punk.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "tattoo", + "punk" + ] + }, + { + "id": "npc_1011_female_blazer_smile", + "path": "modules/npc-portrait-pack/portraits/npc_1011_female_blazer_smile.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "blazer", + "smile" + ] + }, + { + "id": "npc_1012_male_trench_detective", + "path": "modules/npc-portrait-pack/portraits/npc_1012_male_trench_detective.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "trench", + "detective" + ] + }, + { + "id": "npc_1013_male_gas_mask", + "path": "modules/npc-portrait-pack/portraits/npc_1013_male_gas_mask.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "gas", + "mask" + ] + }, + { + "id": "npc_1014_female_young_runner", + "path": "modules/npc-portrait-pack/portraits/npc_1014_female_young_runner.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "young", + "runner" + ] + }, + { + "id": "npc_1015_male_masked_soldier", + "path": "modules/npc-portrait-pack/portraits/npc_1015_male_masked_soldier.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "masked", + "soldier" + ] + }, + { + "id": "npc_1016_hooded_mystic", + "path": "modules/npc-portrait-pack/portraits/npc_1016_hooded_mystic.png", + "category": "named_npc", + "race": "human", + "gender": null, + "tags": [ + "hooded", + "mystic" + ] + }, + { + "id": "npc_1017_male_implant_veteran", + "path": "modules/npc-portrait-pack/portraits/npc_1017_male_implant_veteran.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "implant", + "veteran" + ] + }, + { + "id": "npc_1018_female_spiked_punk", + "path": "modules/npc-portrait-pack/portraits/npc_1018_female_spiked_punk.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "spiked", + "punk" + ] + }, + { + "id": "npc_1019_female_formal_officer", + "path": "modules/npc-portrait-pack/portraits/npc_1019_female_formal_officer.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "formal", + "officer" + ] + }, + { + "id": "npc_1020_male_hoodie_beanie", + "path": "modules/npc-portrait-pack/portraits/npc_1020_male_hoodie_beanie.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "hoodie", + "beanie" + ] + }, + { + "id": "npc_1021_female_visor_biker", + "path": "modules/npc-portrait-pack/portraits/npc_1021_female_visor_biker.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "visor", + "biker" + ] + }, + { + "id": "npc_1022_female_headset_operator", + "path": "modules/npc-portrait-pack/portraits/npc_1022_female_headset_operator.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "headset", + "operator" + ] + }, + { + "id": "npc_1023_male_suited_official", + "path": "modules/npc-portrait-pack/portraits/npc_1023_male_suited_official.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "suited", + "official" + ] + }, + { + "id": "npc_1024_female_scarf_civilian", + "path": "modules/npc-portrait-pack/portraits/npc_1024_female_scarf_civilian.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "scarf", + "civilian" + ] + }, + { + "id": "npc_1025_female_dread_punk", + "path": "modules/npc-portrait-pack/portraits/npc_1025_female_dread_punk.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "dread", + "punk" + ] + }, + { + "id": "npc_1026_female_sleek_bob", + "path": "modules/npc-portrait-pack/portraits/npc_1026_female_sleek_bob.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "sleek", + "bob" + ] + }, + { + "id": "npc_1027_male_armored_officer", + "path": "modules/npc-portrait-pack/portraits/npc_1027_male_armored_officer.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "armored", + "officer" + ] + }, + { + "id": "npc_1028_male_work_jacket", + "path": "modules/npc-portrait-pack/portraits/npc_1028_male_work_jacket.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "work", + "jacket" + ] + }, + { + "id": "npc_1029_male_street_punk", + "path": "modules/npc-portrait-pack/portraits/npc_1029_male_street_punk.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "street", + "punk" + ] + }, + { + "id": "npc_1030_female_glasses_executive", + "path": "modules/npc-portrait-pack/portraits/npc_1030_female_glasses_executive.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "glasses", + "executive" + ] + }, + { + "id": "npc_1031_male_smart_casual", + "path": "modules/npc-portrait-pack/portraits/npc_1031_male_smart_casual.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "smart", + "casual" + ] + }, + { + "id": "npc_1032_elderly_female_coat", + "path": "modules/npc-portrait-pack/portraits/npc_1032_elderly_female_coat.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "elderly", + "coat" + ] + }, + { + "id": "npc_1033_elderly_female_jacket", + "path": "modules/npc-portrait-pack/portraits/npc_1033_elderly_female_jacket.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "elderly", + "jacket" + ] + }, + { + "id": "npc_1034_female_mohawk_punk", + "path": "modules/npc-portrait-pack/portraits/npc_1034_female_mohawk_punk.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "mohawk", + "punk" + ] + }, + { + "id": "npc_1035_male_cameraman", + "path": "modules/npc-portrait-pack/portraits/npc_1035_male_cameraman.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "cameraman" + ] + }, + { + "id": "npc_1036_male_young_suit", + "path": "modules/npc-portrait-pack/portraits/npc_1036_male_young_suit.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "young", + "suit" + ] + }, + { + "id": "npc_1037_male_bucket_hat", + "path": "modules/npc-portrait-pack/portraits/npc_1037_male_bucket_hat.png", + "category": "named_npc", + "race": "human", + "gender": "male", + "tags": [ + "male", + "bucket", + "hat" + ] + }, + { + "id": "npc_1038_female_masked_raider", + "path": "modules/npc-portrait-pack/portraits/npc_1038_female_masked_raider.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "masked", + "raider" + ] + }, + { + "id": "npc_1039_female_blazer_professional", + "path": "modules/npc-portrait-pack/portraits/npc_1039_female_blazer_professional.png", + "category": "named_npc", + "race": "human", + "gender": "female", + "tags": [ + "female", + "blazer", + "professional" + ] + }, + { + "id": "portrait_01", + "path": "modules/npc-portrait-pack/portraits/portrait_01.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_02", + "path": "modules/npc-portrait-pack/portraits/portrait_02.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_03", + "path": "modules/npc-portrait-pack/portraits/portrait_03.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_04", + "path": "modules/npc-portrait-pack/portraits/portrait_04.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_06", + "path": "modules/npc-portrait-pack/portraits/portrait_06.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_07", + "path": "modules/npc-portrait-pack/portraits/portrait_07.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_08", + "path": "modules/npc-portrait-pack/portraits/portrait_08.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_09", + "path": "modules/npc-portrait-pack/portraits/portrait_09.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_10", + "path": "modules/npc-portrait-pack/portraits/portrait_10.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_11", + "path": "modules/npc-portrait-pack/portraits/portrait_11.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_12", + "path": "modules/npc-portrait-pack/portraits/portrait_12.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_14", + "path": "modules/npc-portrait-pack/portraits/portrait_14.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_15", + "path": "modules/npc-portrait-pack/portraits/portrait_15.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_16", + "path": "modules/npc-portrait-pack/portraits/portrait_16.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_17", + "path": "modules/npc-portrait-pack/portraits/portrait_17.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_18", + "path": "modules/npc-portrait-pack/portraits/portrait_18.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_19", + "path": "modules/npc-portrait-pack/portraits/portrait_19.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "portrait_20", + "path": "modules/npc-portrait-pack/portraits/portrait_20.png", + "category": "generic", + "race": "human", + "gender": null, + "tags": [] + }, + { + "id": "serial_killer_01", + "path": "modules/npc-portrait-pack/portraits/serial_killer_01.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_02", + "path": "modules/npc-portrait-pack/portraits/serial_killer_02.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_03", + "path": "modules/npc-portrait-pack/portraits/serial_killer_03.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_04", + "path": "modules/npc-portrait-pack/portraits/serial_killer_04.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_05", + "path": "modules/npc-portrait-pack/portraits/serial_killer_05.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_06", + "path": "modules/npc-portrait-pack/portraits/serial_killer_06.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_07", + "path": "modules/npc-portrait-pack/portraits/serial_killer_07.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_08", + "path": "modules/npc-portrait-pack/portraits/serial_killer_08.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_09", + "path": "modules/npc-portrait-pack/portraits/serial_killer_09.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_10", + "path": "modules/npc-portrait-pack/portraits/serial_killer_10.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_11", + "path": "modules/npc-portrait-pack/portraits/serial_killer_11.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_12", + "path": "modules/npc-portrait-pack/portraits/serial_killer_12.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_14", + "path": "modules/npc-portrait-pack/portraits/serial_killer_14.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_15", + "path": "modules/npc-portrait-pack/portraits/serial_killer_15.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_16", + "path": "modules/npc-portrait-pack/portraits/serial_killer_16.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_17", + "path": "modules/npc-portrait-pack/portraits/serial_killer_17.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_18", + "path": "modules/npc-portrait-pack/portraits/serial_killer_18.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_19", + "path": "modules/npc-portrait-pack/portraits/serial_killer_19.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_20", + "path": "modules/npc-portrait-pack/portraits/serial_killer_20.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_21", + "path": "modules/npc-portrait-pack/portraits/serial_killer_21.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_22", + "path": "modules/npc-portrait-pack/portraits/serial_killer_22.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_23", + "path": "modules/npc-portrait-pack/portraits/serial_killer_23.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_24", + "path": "modules/npc-portrait-pack/portraits/serial_killer_24.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_25", + "path": "modules/npc-portrait-pack/portraits/serial_killer_25.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_26", + "path": "modules/npc-portrait-pack/portraits/serial_killer_26.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_27", + "path": "modules/npc-portrait-pack/portraits/serial_killer_27.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_28", + "path": "modules/npc-portrait-pack/portraits/serial_killer_28.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_29", + "path": "modules/npc-portrait-pack/portraits/serial_killer_29.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_30", + "path": "modules/npc-portrait-pack/portraits/serial_killer_30.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_31", + "path": "modules/npc-portrait-pack/portraits/serial_killer_31.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_32", + "path": "modules/npc-portrait-pack/portraits/serial_killer_32.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_33", + "path": "modules/npc-portrait-pack/portraits/serial_killer_33.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_34", + "path": "modules/npc-portrait-pack/portraits/serial_killer_34.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_35", + "path": "modules/npc-portrait-pack/portraits/serial_killer_35.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_36", + "path": "modules/npc-portrait-pack/portraits/serial_killer_36.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_37", + "path": "modules/npc-portrait-pack/portraits/serial_killer_37.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_38", + "path": "modules/npc-portrait-pack/portraits/serial_killer_38.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "serial_killer_39", + "path": "modules/npc-portrait-pack/portraits/serial_killer_39.png", + "category": "serial_killer", + "race": "human", + "gender": null, + "tags": [ + "serial_killer", + "threat" + ] + }, + { + "id": "shaktar_female_01", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_01.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_02", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_02.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_03", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_03.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_04", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_04.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_05", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_05.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_06", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_06.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_07", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_07.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_08", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_08.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_09", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_09.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_10", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_10.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_11", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_11.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_12", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_12.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_13", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_13.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_14", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_14.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_15", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_15.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_16", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_16.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_17", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_17.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_female_18", + "path": "modules/npc-portrait-pack/portraits/shaktar_female_18.png", + "category": "shaktar", + "race": "shaktar", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "shaktar_male_01", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_01.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_02", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_02.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_03", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_03.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_04", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_04.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_05", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_05.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_06", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_06.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_07", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_07.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_08", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_08.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_09", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_09.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_10", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_10.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_11", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_11.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_12", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_12.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_13", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_13.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_14", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_14.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_15", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_15.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_16", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_16.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_17", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_17.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "shaktar_male_18", + "path": "modules/npc-portrait-pack/portraits/shaktar_male_18.png", + "category": "shaktar", + "race": "shaktar", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_female_01", + "path": "modules/npc-portrait-pack/portraits/stormer_female_01.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_02", + "path": "modules/npc-portrait-pack/portraits/stormer_female_02.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_03", + "path": "modules/npc-portrait-pack/portraits/stormer_female_03.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_04", + "path": "modules/npc-portrait-pack/portraits/stormer_female_04.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_05", + "path": "modules/npc-portrait-pack/portraits/stormer_female_05.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_06", + "path": "modules/npc-portrait-pack/portraits/stormer_female_06.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_07", + "path": "modules/npc-portrait-pack/portraits/stormer_female_07.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_08", + "path": "modules/npc-portrait-pack/portraits/stormer_female_08.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_09", + "path": "modules/npc-portrait-pack/portraits/stormer_female_09.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_10", + "path": "modules/npc-portrait-pack/portraits/stormer_female_10.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_11", + "path": "modules/npc-portrait-pack/portraits/stormer_female_11.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_12", + "path": "modules/npc-portrait-pack/portraits/stormer_female_12.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_13", + "path": "modules/npc-portrait-pack/portraits/stormer_female_13.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_14", + "path": "modules/npc-portrait-pack/portraits/stormer_female_14.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_15", + "path": "modules/npc-portrait-pack/portraits/stormer_female_15.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_16", + "path": "modules/npc-portrait-pack/portraits/stormer_female_16.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_17", + "path": "modules/npc-portrait-pack/portraits/stormer_female_17.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_18", + "path": "modules/npc-portrait-pack/portraits/stormer_female_18.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_19", + "path": "modules/npc-portrait-pack/portraits/stormer_female_19.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_female_20", + "path": "modules/npc-portrait-pack/portraits/stormer_female_20.png", + "category": "stormer", + "race": "stormer", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "stormer_male_01", + "path": "modules/npc-portrait-pack/portraits/stormer_male_01.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_02", + "path": "modules/npc-portrait-pack/portraits/stormer_male_02.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_03", + "path": "modules/npc-portrait-pack/portraits/stormer_male_03.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_04", + "path": "modules/npc-portrait-pack/portraits/stormer_male_04.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_05", + "path": "modules/npc-portrait-pack/portraits/stormer_male_05.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_06", + "path": "modules/npc-portrait-pack/portraits/stormer_male_06.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_07", + "path": "modules/npc-portrait-pack/portraits/stormer_male_07.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_08", + "path": "modules/npc-portrait-pack/portraits/stormer_male_08.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_09", + "path": "modules/npc-portrait-pack/portraits/stormer_male_09.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_10", + "path": "modules/npc-portrait-pack/portraits/stormer_male_10.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_11", + "path": "modules/npc-portrait-pack/portraits/stormer_male_11.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_12", + "path": "modules/npc-portrait-pack/portraits/stormer_male_12.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_13", + "path": "modules/npc-portrait-pack/portraits/stormer_male_13.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_14", + "path": "modules/npc-portrait-pack/portraits/stormer_male_14.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_15", + "path": "modules/npc-portrait-pack/portraits/stormer_male_15.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_16", + "path": "modules/npc-portrait-pack/portraits/stormer_male_16.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_17", + "path": "modules/npc-portrait-pack/portraits/stormer_male_17.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "stormer_male_18", + "path": "modules/npc-portrait-pack/portraits/stormer_male_18.png", + "category": "stormer", + "race": "stormer", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_female_01", + "path": "modules/npc-portrait-pack/portraits/waister_female_01.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_02", + "path": "modules/npc-portrait-pack/portraits/waister_female_02.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_03", + "path": "modules/npc-portrait-pack/portraits/waister_female_03.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_04", + "path": "modules/npc-portrait-pack/portraits/waister_female_04.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_05", + "path": "modules/npc-portrait-pack/portraits/waister_female_05.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_06", + "path": "modules/npc-portrait-pack/portraits/waister_female_06.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_07", + "path": "modules/npc-portrait-pack/portraits/waister_female_07.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_08", + "path": "modules/npc-portrait-pack/portraits/waister_female_08.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_09", + "path": "modules/npc-portrait-pack/portraits/waister_female_09.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_10", + "path": "modules/npc-portrait-pack/portraits/waister_female_10.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_11", + "path": "modules/npc-portrait-pack/portraits/waister_female_11.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_12", + "path": "modules/npc-portrait-pack/portraits/waister_female_12.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_13", + "path": "modules/npc-portrait-pack/portraits/waister_female_13.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_14", + "path": "modules/npc-portrait-pack/portraits/waister_female_14.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_15", + "path": "modules/npc-portrait-pack/portraits/waister_female_15.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_16", + "path": "modules/npc-portrait-pack/portraits/waister_female_16.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_17", + "path": "modules/npc-portrait-pack/portraits/waister_female_17.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_18", + "path": "modules/npc-portrait-pack/portraits/waister_female_18.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_19", + "path": "modules/npc-portrait-pack/portraits/waister_female_19.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_female_20", + "path": "modules/npc-portrait-pack/portraits/waister_female_20.png", + "category": "waister", + "race": "waister", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "waister_male_01", + "path": "modules/npc-portrait-pack/portraits/waister_male_01.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_02", + "path": "modules/npc-portrait-pack/portraits/waister_male_02.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_03", + "path": "modules/npc-portrait-pack/portraits/waister_male_03.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_04", + "path": "modules/npc-portrait-pack/portraits/waister_male_04.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_05", + "path": "modules/npc-portrait-pack/portraits/waister_male_05.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_06", + "path": "modules/npc-portrait-pack/portraits/waister_male_06.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_07", + "path": "modules/npc-portrait-pack/portraits/waister_male_07.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_08", + "path": "modules/npc-portrait-pack/portraits/waister_male_08.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_09", + "path": "modules/npc-portrait-pack/portraits/waister_male_09.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_10", + "path": "modules/npc-portrait-pack/portraits/waister_male_10.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_11", + "path": "modules/npc-portrait-pack/portraits/waister_male_11.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_12", + "path": "modules/npc-portrait-pack/portraits/waister_male_12.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_13", + "path": "modules/npc-portrait-pack/portraits/waister_male_13.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_14", + "path": "modules/npc-portrait-pack/portraits/waister_male_14.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_15", + "path": "modules/npc-portrait-pack/portraits/waister_male_15.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_16", + "path": "modules/npc-portrait-pack/portraits/waister_male_16.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_17", + "path": "modules/npc-portrait-pack/portraits/waister_male_17.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "waister_male_18", + "path": "modules/npc-portrait-pack/portraits/waister_male_18.png", + "category": "waister", + "race": "waister", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_female_01", + "path": "modules/npc-portrait-pack/portraits/wraith_female_01.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_02", + "path": "modules/npc-portrait-pack/portraits/wraith_female_02.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_03", + "path": "modules/npc-portrait-pack/portraits/wraith_female_03.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_04", + "path": "modules/npc-portrait-pack/portraits/wraith_female_04.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_05", + "path": "modules/npc-portrait-pack/portraits/wraith_female_05.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_06", + "path": "modules/npc-portrait-pack/portraits/wraith_female_06.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_07", + "path": "modules/npc-portrait-pack/portraits/wraith_female_07.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_08", + "path": "modules/npc-portrait-pack/portraits/wraith_female_08.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_09", + "path": "modules/npc-portrait-pack/portraits/wraith_female_09.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_10", + "path": "modules/npc-portrait-pack/portraits/wraith_female_10.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_11", + "path": "modules/npc-portrait-pack/portraits/wraith_female_11.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_12", + "path": "modules/npc-portrait-pack/portraits/wraith_female_12.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_13", + "path": "modules/npc-portrait-pack/portraits/wraith_female_13.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_14", + "path": "modules/npc-portrait-pack/portraits/wraith_female_14.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_15", + "path": "modules/npc-portrait-pack/portraits/wraith_female_15.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_16", + "path": "modules/npc-portrait-pack/portraits/wraith_female_16.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_17", + "path": "modules/npc-portrait-pack/portraits/wraith_female_17.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_18", + "path": "modules/npc-portrait-pack/portraits/wraith_female_18.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_19", + "path": "modules/npc-portrait-pack/portraits/wraith_female_19.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_20", + "path": "modules/npc-portrait-pack/portraits/wraith_female_20.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_21", + "path": "modules/npc-portrait-pack/portraits/wraith_female_21.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_22", + "path": "modules/npc-portrait-pack/portraits/wraith_female_22.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_23", + "path": "modules/npc-portrait-pack/portraits/wraith_female_23.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_24", + "path": "modules/npc-portrait-pack/portraits/wraith_female_24.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_25", + "path": "modules/npc-portrait-pack/portraits/wraith_female_25.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_26", + "path": "modules/npc-portrait-pack/portraits/wraith_female_26.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_27", + "path": "modules/npc-portrait-pack/portraits/wraith_female_27.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_28", + "path": "modules/npc-portrait-pack/portraits/wraith_female_28.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_29", + "path": "modules/npc-portrait-pack/portraits/wraith_female_29.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_30", + "path": "modules/npc-portrait-pack/portraits/wraith_female_30.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_31", + "path": "modules/npc-portrait-pack/portraits/wraith_female_31.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_32", + "path": "modules/npc-portrait-pack/portraits/wraith_female_32.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_33", + "path": "modules/npc-portrait-pack/portraits/wraith_female_33.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_34", + "path": "modules/npc-portrait-pack/portraits/wraith_female_34.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_35", + "path": "modules/npc-portrait-pack/portraits/wraith_female_35.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_36", + "path": "modules/npc-portrait-pack/portraits/wraith_female_36.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_37", + "path": "modules/npc-portrait-pack/portraits/wraith_female_37.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_38", + "path": "modules/npc-portrait-pack/portraits/wraith_female_38.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_39", + "path": "modules/npc-portrait-pack/portraits/wraith_female_39.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_female_40", + "path": "modules/npc-portrait-pack/portraits/wraith_female_40.png", + "category": "wraith", + "race": "wraith", + "gender": "female", + "tags": [ + "female" + ] + }, + { + "id": "wraith_male_01", + "path": "modules/npc-portrait-pack/portraits/wraith_male_01.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_02", + "path": "modules/npc-portrait-pack/portraits/wraith_male_02.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_03", + "path": "modules/npc-portrait-pack/portraits/wraith_male_03.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_04", + "path": "modules/npc-portrait-pack/portraits/wraith_male_04.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_05", + "path": "modules/npc-portrait-pack/portraits/wraith_male_05.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_06", + "path": "modules/npc-portrait-pack/portraits/wraith_male_06.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_07", + "path": "modules/npc-portrait-pack/portraits/wraith_male_07.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_08", + "path": "modules/npc-portrait-pack/portraits/wraith_male_08.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_09", + "path": "modules/npc-portrait-pack/portraits/wraith_male_09.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_10", + "path": "modules/npc-portrait-pack/portraits/wraith_male_10.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_11", + "path": "modules/npc-portrait-pack/portraits/wraith_male_11.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_14", + "path": "modules/npc-portrait-pack/portraits/wraith_male_14.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_21", + "path": "modules/npc-portrait-pack/portraits/wraith_male_21.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_24", + "path": "modules/npc-portrait-pack/portraits/wraith_male_24.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_25", + "path": "modules/npc-portrait-pack/portraits/wraith_male_25.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_26", + "path": "modules/npc-portrait-pack/portraits/wraith_male_26.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_27", + "path": "modules/npc-portrait-pack/portraits/wraith_male_27.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_28", + "path": "modules/npc-portrait-pack/portraits/wraith_male_28.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_29", + "path": "modules/npc-portrait-pack/portraits/wraith_male_29.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_30", + "path": "modules/npc-portrait-pack/portraits/wraith_male_30.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_31", + "path": "modules/npc-portrait-pack/portraits/wraith_male_31.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_32", + "path": "modules/npc-portrait-pack/portraits/wraith_male_32.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_33", + "path": "modules/npc-portrait-pack/portraits/wraith_male_33.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_34", + "path": "modules/npc-portrait-pack/portraits/wraith_male_34.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + }, + { + "id": "wraith_male_38", + "path": "modules/npc-portrait-pack/portraits/wraith_male_38.png", + "category": "wraith", + "race": "wraith", + "gender": "male", + "tags": [ + "male" + ] + } + ] +} \ No newline at end of file diff --git a/portraits/mutant_01.png b/portraits/mutant_01.png new file mode 100644 index 0000000..7208e38 Binary files /dev/null and b/portraits/mutant_01.png differ diff --git a/portraits/mutant_02.png b/portraits/mutant_02.png new file mode 100644 index 0000000..7b25cb7 Binary files /dev/null and b/portraits/mutant_02.png differ diff --git a/portraits/mutant_03.png b/portraits/mutant_03.png new file mode 100644 index 0000000..a68f3e2 Binary files /dev/null and b/portraits/mutant_03.png differ diff --git a/portraits/mutant_04.png b/portraits/mutant_04.png new file mode 100644 index 0000000..f652539 Binary files /dev/null and b/portraits/mutant_04.png differ diff --git a/portraits/mutant_05.png b/portraits/mutant_05.png new file mode 100644 index 0000000..0d662a7 Binary files /dev/null and b/portraits/mutant_05.png differ diff --git a/portraits/mutant_06.png b/portraits/mutant_06.png new file mode 100644 index 0000000..910a6ab Binary files /dev/null and b/portraits/mutant_06.png differ diff --git a/portraits/mutant_07.png b/portraits/mutant_07.png new file mode 100644 index 0000000..89781c3 Binary files /dev/null and b/portraits/mutant_07.png differ diff --git a/portraits/mutant_08.png b/portraits/mutant_08.png new file mode 100644 index 0000000..3129b15 Binary files /dev/null and b/portraits/mutant_08.png differ diff --git a/portraits/mutant_09.png b/portraits/mutant_09.png new file mode 100644 index 0000000..f8c876b Binary files /dev/null and b/portraits/mutant_09.png differ diff --git a/portraits/mutant_10.png b/portraits/mutant_10.png new file mode 100644 index 0000000..f4b9026 Binary files /dev/null and b/portraits/mutant_10.png differ diff --git a/portraits/mutant_11.png b/portraits/mutant_11.png new file mode 100644 index 0000000..b69a996 Binary files /dev/null and b/portraits/mutant_11.png differ diff --git a/portraits/mutant_12.png b/portraits/mutant_12.png new file mode 100644 index 0000000..003e901 Binary files /dev/null and b/portraits/mutant_12.png differ diff --git a/portraits/mutant_13.png b/portraits/mutant_13.png new file mode 100644 index 0000000..b770f32 Binary files /dev/null and b/portraits/mutant_13.png differ diff --git a/portraits/mutant_14.png b/portraits/mutant_14.png new file mode 100644 index 0000000..3e32f11 Binary files /dev/null and b/portraits/mutant_14.png differ diff --git a/portraits/mutant_15.png b/portraits/mutant_15.png new file mode 100644 index 0000000..cb9a530 Binary files /dev/null and b/portraits/mutant_15.png differ diff --git a/portraits/mutant_16.png b/portraits/mutant_16.png new file mode 100644 index 0000000..5fdf318 Binary files /dev/null and b/portraits/mutant_16.png differ diff --git a/portraits/mutant_17.png b/portraits/mutant_17.png new file mode 100644 index 0000000..4eaf050 Binary files /dev/null and b/portraits/mutant_17.png differ diff --git a/portraits/mutant_18.png b/portraits/mutant_18.png new file mode 100644 index 0000000..9518538 Binary files /dev/null and b/portraits/mutant_18.png differ diff --git a/portraits/mutant_19.png b/portraits/mutant_19.png new file mode 100644 index 0000000..23157e0 Binary files /dev/null and b/portraits/mutant_19.png differ diff --git a/portraits/mutant_20.png b/portraits/mutant_20.png new file mode 100644 index 0000000..aec8627 Binary files /dev/null and b/portraits/mutant_20.png differ diff --git a/portraits/mutant_21.png b/portraits/mutant_21.png new file mode 100644 index 0000000..19b8d86 Binary files /dev/null and b/portraits/mutant_21.png differ diff --git a/portraits/mutant_22.png b/portraits/mutant_22.png new file mode 100644 index 0000000..ffb3642 Binary files /dev/null and b/portraits/mutant_22.png differ diff --git a/portraits/mutant_23.png b/portraits/mutant_23.png new file mode 100644 index 0000000..754a843 Binary files /dev/null and b/portraits/mutant_23.png differ diff --git a/portraits/mutant_24.png b/portraits/mutant_24.png new file mode 100644 index 0000000..7106298 Binary files /dev/null and b/portraits/mutant_24.png differ diff --git a/portraits/mutant_25.png b/portraits/mutant_25.png new file mode 100644 index 0000000..869e3f7 Binary files /dev/null and b/portraits/mutant_25.png differ diff --git a/portraits/mutant_26.png b/portraits/mutant_26.png new file mode 100644 index 0000000..14abddf Binary files /dev/null and b/portraits/mutant_26.png differ diff --git a/portraits/mutant_27.png b/portraits/mutant_27.png new file mode 100644 index 0000000..3bfa091 Binary files /dev/null and b/portraits/mutant_27.png differ diff --git a/portraits/mutant_28.png b/portraits/mutant_28.png new file mode 100644 index 0000000..d1a2d85 Binary files /dev/null and b/portraits/mutant_28.png differ diff --git a/portraits/mutant_29.png b/portraits/mutant_29.png new file mode 100644 index 0000000..f32a541 Binary files /dev/null and b/portraits/mutant_29.png differ diff --git a/portraits/mutant_30.png b/portraits/mutant_30.png new file mode 100644 index 0000000..059d2a9 Binary files /dev/null and b/portraits/mutant_30.png differ diff --git a/portraits/mutant_31.png b/portraits/mutant_31.png new file mode 100644 index 0000000..cfdf2e9 Binary files /dev/null and b/portraits/mutant_31.png differ diff --git a/portraits/mutant_32.png b/portraits/mutant_32.png new file mode 100644 index 0000000..cab15a9 Binary files /dev/null and b/portraits/mutant_32.png differ diff --git a/portraits/mutant_33.png b/portraits/mutant_33.png new file mode 100644 index 0000000..e2f4d47 Binary files /dev/null and b/portraits/mutant_33.png differ diff --git a/portraits/mutant_34.png b/portraits/mutant_34.png new file mode 100644 index 0000000..75f1825 Binary files /dev/null and b/portraits/mutant_34.png differ diff --git a/portraits/mutant_35.png b/portraits/mutant_35.png new file mode 100644 index 0000000..1ae0e8b Binary files /dev/null and b/portraits/mutant_35.png differ diff --git a/portraits/mutant_36.png b/portraits/mutant_36.png new file mode 100644 index 0000000..69b08da Binary files /dev/null and b/portraits/mutant_36.png differ diff --git a/portraits/mutant_37.png b/portraits/mutant_37.png new file mode 100644 index 0000000..f526d2c Binary files /dev/null and b/portraits/mutant_37.png differ diff --git a/portraits/mutant_38.png b/portraits/mutant_38.png new file mode 100644 index 0000000..77060e2 Binary files /dev/null and b/portraits/mutant_38.png differ diff --git a/portraits/mutant_39.png b/portraits/mutant_39.png new file mode 100644 index 0000000..eca293e Binary files /dev/null and b/portraits/mutant_39.png differ diff --git a/portraits/mutant_40.png b/portraits/mutant_40.png new file mode 100644 index 0000000..120fcc9 Binary files /dev/null and b/portraits/mutant_40.png differ diff --git a/portraits/mutant_41.png b/portraits/mutant_41.png new file mode 100644 index 0000000..f2a5fce Binary files /dev/null and b/portraits/mutant_41.png differ diff --git a/portraits/mutant_42.png b/portraits/mutant_42.png new file mode 100644 index 0000000..d071110 Binary files /dev/null and b/portraits/mutant_42.png differ diff --git a/portraits/mutant_43.png b/portraits/mutant_43.png new file mode 100644 index 0000000..0e108c1 Binary files /dev/null and b/portraits/mutant_43.png differ diff --git a/portraits/mutant_44.png b/portraits/mutant_44.png new file mode 100644 index 0000000..080d1a1 Binary files /dev/null and b/portraits/mutant_44.png differ diff --git a/portraits/mutant_45.png b/portraits/mutant_45.png new file mode 100644 index 0000000..1fa04b9 Binary files /dev/null and b/portraits/mutant_45.png differ diff --git a/portraits/mutant_46.png b/portraits/mutant_46.png new file mode 100644 index 0000000..e5b95cf Binary files /dev/null and b/portraits/mutant_46.png differ diff --git a/portraits/mutant_47.png b/portraits/mutant_47.png new file mode 100644 index 0000000..7bd81eb Binary files /dev/null and b/portraits/mutant_47.png differ diff --git a/portraits/mutant_48.png b/portraits/mutant_48.png new file mode 100644 index 0000000..1ded942 Binary files /dev/null and b/portraits/mutant_48.png differ diff --git a/portraits/mutant_49.png b/portraits/mutant_49.png new file mode 100644 index 0000000..6d5fca9 Binary files /dev/null and b/portraits/mutant_49.png differ diff --git a/portraits/mutant_50.png b/portraits/mutant_50.png new file mode 100644 index 0000000..00dbcb9 Binary files /dev/null and b/portraits/mutant_50.png differ diff --git a/portraits/mutant_51.png b/portraits/mutant_51.png new file mode 100644 index 0000000..5b274fc Binary files /dev/null and b/portraits/mutant_51.png differ diff --git a/portraits/mutant_52.png b/portraits/mutant_52.png new file mode 100644 index 0000000..28271fa Binary files /dev/null and b/portraits/mutant_52.png differ diff --git a/portraits/mutant_53.png b/portraits/mutant_53.png new file mode 100644 index 0000000..6640a35 Binary files /dev/null and b/portraits/mutant_53.png differ diff --git a/portraits/mutant_54.png b/portraits/mutant_54.png new file mode 100644 index 0000000..4dc4739 Binary files /dev/null and b/portraits/mutant_54.png differ diff --git a/portraits/mutant_55.png b/portraits/mutant_55.png new file mode 100644 index 0000000..94a5883 Binary files /dev/null and b/portraits/mutant_55.png differ diff --git a/portraits/mutant_56.png b/portraits/mutant_56.png new file mode 100644 index 0000000..398f4b6 Binary files /dev/null and b/portraits/mutant_56.png differ diff --git a/portraits/mutant_57.png b/portraits/mutant_57.png new file mode 100644 index 0000000..d231d94 Binary files /dev/null and b/portraits/mutant_57.png differ diff --git a/portraits/npc_1001_male_beanie.png b/portraits/npc_1001_male_beanie.png new file mode 100644 index 0000000..9e9d637 Binary files /dev/null and b/portraits/npc_1001_male_beanie.png differ diff --git a/portraits/npc_1002_female_wild_hair.png b/portraits/npc_1002_female_wild_hair.png new file mode 100644 index 0000000..2489360 Binary files /dev/null and b/portraits/npc_1002_female_wild_hair.png differ diff --git a/portraits/npc_1003_male_armored_helmet.png b/portraits/npc_1003_male_armored_helmet.png new file mode 100644 index 0000000..0c6e121 Binary files /dev/null and b/portraits/npc_1003_male_armored_helmet.png differ diff --git a/portraits/npc_1004_male_cyborg_mohawk.png b/portraits/npc_1004_male_cyborg_mohawk.png new file mode 100644 index 0000000..3d34452 Binary files /dev/null and b/portraits/npc_1004_male_cyborg_mohawk.png differ diff --git a/portraits/npc_1005_female_uniform.png b/portraits/npc_1005_female_uniform.png new file mode 100644 index 0000000..f1c0a46 Binary files /dev/null and b/portraits/npc_1005_female_uniform.png differ diff --git a/portraits/npc_1006_male_red_eye_cyborg.png b/portraits/npc_1006_male_red_eye_cyborg.png new file mode 100644 index 0000000..cc40621 Binary files /dev/null and b/portraits/npc_1006_male_red_eye_cyborg.png differ diff --git a/portraits/npc_1007_elderly_hooded.png b/portraits/npc_1007_elderly_hooded.png new file mode 100644 index 0000000..412bf05 Binary files /dev/null and b/portraits/npc_1007_elderly_hooded.png differ diff --git a/portraits/npc_1008_male_cyborg_raider.png b/portraits/npc_1008_male_cyborg_raider.png new file mode 100644 index 0000000..532c00a Binary files /dev/null and b/portraits/npc_1008_male_cyborg_raider.png differ diff --git a/portraits/npc_1009_male_goggle_doctor.png b/portraits/npc_1009_male_goggle_doctor.png new file mode 100644 index 0000000..2bbb689 Binary files /dev/null and b/portraits/npc_1009_male_goggle_doctor.png differ diff --git a/portraits/npc_1010_female_tattoo_punk.png b/portraits/npc_1010_female_tattoo_punk.png new file mode 100644 index 0000000..95b916a Binary files /dev/null and b/portraits/npc_1010_female_tattoo_punk.png differ diff --git a/portraits/npc_1011_female_blazer_smile.png b/portraits/npc_1011_female_blazer_smile.png new file mode 100644 index 0000000..4dba99c Binary files /dev/null and b/portraits/npc_1011_female_blazer_smile.png differ diff --git a/portraits/npc_1012_male_trench_detective.png b/portraits/npc_1012_male_trench_detective.png new file mode 100644 index 0000000..ab4e900 Binary files /dev/null and b/portraits/npc_1012_male_trench_detective.png differ diff --git a/portraits/npc_1013_male_gas_mask.png b/portraits/npc_1013_male_gas_mask.png new file mode 100644 index 0000000..9a069d0 Binary files /dev/null and b/portraits/npc_1013_male_gas_mask.png differ diff --git a/portraits/npc_1014_female_young_runner.png b/portraits/npc_1014_female_young_runner.png new file mode 100644 index 0000000..9d2317f Binary files /dev/null and b/portraits/npc_1014_female_young_runner.png differ diff --git a/portraits/npc_1015_male_masked_soldier.png b/portraits/npc_1015_male_masked_soldier.png new file mode 100644 index 0000000..8f970aa Binary files /dev/null and b/portraits/npc_1015_male_masked_soldier.png differ diff --git a/portraits/npc_1016_hooded_mystic.png b/portraits/npc_1016_hooded_mystic.png new file mode 100644 index 0000000..7b5f634 Binary files /dev/null and b/portraits/npc_1016_hooded_mystic.png differ diff --git a/portraits/npc_1017_male_implant_veteran.png b/portraits/npc_1017_male_implant_veteran.png new file mode 100644 index 0000000..57a73b4 Binary files /dev/null and b/portraits/npc_1017_male_implant_veteran.png differ diff --git a/portraits/npc_1018_female_spiked_punk.png b/portraits/npc_1018_female_spiked_punk.png new file mode 100644 index 0000000..b4c199d Binary files /dev/null and b/portraits/npc_1018_female_spiked_punk.png differ diff --git a/portraits/npc_1019_female_formal_officer.png b/portraits/npc_1019_female_formal_officer.png new file mode 100644 index 0000000..796083f Binary files /dev/null and b/portraits/npc_1019_female_formal_officer.png differ diff --git a/portraits/npc_1020_male_hoodie_beanie.png b/portraits/npc_1020_male_hoodie_beanie.png new file mode 100644 index 0000000..e8a3bb2 Binary files /dev/null and b/portraits/npc_1020_male_hoodie_beanie.png differ diff --git a/portraits/npc_1021_female_visor_biker.png b/portraits/npc_1021_female_visor_biker.png new file mode 100644 index 0000000..ac3d017 Binary files /dev/null and b/portraits/npc_1021_female_visor_biker.png differ diff --git a/portraits/npc_1022_female_headset_operator.png b/portraits/npc_1022_female_headset_operator.png new file mode 100644 index 0000000..0ea132c Binary files /dev/null and b/portraits/npc_1022_female_headset_operator.png differ diff --git a/portraits/npc_1023_male_suited_official.png b/portraits/npc_1023_male_suited_official.png new file mode 100644 index 0000000..42aca5a Binary files /dev/null and b/portraits/npc_1023_male_suited_official.png differ diff --git a/portraits/npc_1024_female_scarf_civilian.png b/portraits/npc_1024_female_scarf_civilian.png new file mode 100644 index 0000000..3ae6482 Binary files /dev/null and b/portraits/npc_1024_female_scarf_civilian.png differ diff --git a/portraits/npc_1025_female_dread_punk.png b/portraits/npc_1025_female_dread_punk.png new file mode 100644 index 0000000..b02d8c1 Binary files /dev/null and b/portraits/npc_1025_female_dread_punk.png differ diff --git a/portraits/npc_1026_female_sleek_bob.png b/portraits/npc_1026_female_sleek_bob.png new file mode 100644 index 0000000..bd7190d Binary files /dev/null and b/portraits/npc_1026_female_sleek_bob.png differ diff --git a/portraits/npc_1027_male_armored_officer.png b/portraits/npc_1027_male_armored_officer.png new file mode 100644 index 0000000..99226bc Binary files /dev/null and b/portraits/npc_1027_male_armored_officer.png differ diff --git a/portraits/npc_1028_male_work_jacket.png b/portraits/npc_1028_male_work_jacket.png new file mode 100644 index 0000000..8e952a3 Binary files /dev/null and b/portraits/npc_1028_male_work_jacket.png differ diff --git a/portraits/npc_1029_male_street_punk.png b/portraits/npc_1029_male_street_punk.png new file mode 100644 index 0000000..cb99ee4 Binary files /dev/null and b/portraits/npc_1029_male_street_punk.png differ diff --git a/portraits/npc_1030_female_glasses_executive.png b/portraits/npc_1030_female_glasses_executive.png new file mode 100644 index 0000000..fc57adf Binary files /dev/null and b/portraits/npc_1030_female_glasses_executive.png differ diff --git a/portraits/npc_1031_male_smart_casual.png b/portraits/npc_1031_male_smart_casual.png new file mode 100644 index 0000000..5d6bbd5 Binary files /dev/null and b/portraits/npc_1031_male_smart_casual.png differ diff --git a/portraits/npc_1032_elderly_female_coat.png b/portraits/npc_1032_elderly_female_coat.png new file mode 100644 index 0000000..3740bba Binary files /dev/null and b/portraits/npc_1032_elderly_female_coat.png differ diff --git a/portraits/npc_1033_elderly_female_jacket.png b/portraits/npc_1033_elderly_female_jacket.png new file mode 100644 index 0000000..c36e9b4 Binary files /dev/null and b/portraits/npc_1033_elderly_female_jacket.png differ diff --git a/portraits/npc_1034_female_mohawk_punk.png b/portraits/npc_1034_female_mohawk_punk.png new file mode 100644 index 0000000..458558c Binary files /dev/null and b/portraits/npc_1034_female_mohawk_punk.png differ diff --git a/portraits/npc_1035_male_cameraman.png b/portraits/npc_1035_male_cameraman.png new file mode 100644 index 0000000..8129914 Binary files /dev/null and b/portraits/npc_1035_male_cameraman.png differ diff --git a/portraits/npc_1036_male_young_suit.png b/portraits/npc_1036_male_young_suit.png new file mode 100644 index 0000000..a6d423a Binary files /dev/null and b/portraits/npc_1036_male_young_suit.png differ diff --git a/portraits/npc_1037_male_bucket_hat.png b/portraits/npc_1037_male_bucket_hat.png new file mode 100644 index 0000000..89690e6 Binary files /dev/null and b/portraits/npc_1037_male_bucket_hat.png differ diff --git a/portraits/npc_1038_female_masked_raider.png b/portraits/npc_1038_female_masked_raider.png new file mode 100644 index 0000000..3ef0843 Binary files /dev/null and b/portraits/npc_1038_female_masked_raider.png differ diff --git a/portraits/npc_1039_female_blazer_professional.png b/portraits/npc_1039_female_blazer_professional.png new file mode 100644 index 0000000..14980ab Binary files /dev/null and b/portraits/npc_1039_female_blazer_professional.png differ diff --git a/portraits/portrait_01.png b/portraits/portrait_01.png new file mode 100644 index 0000000..72ae1a1 Binary files /dev/null and b/portraits/portrait_01.png differ diff --git a/portraits/portrait_02.png b/portraits/portrait_02.png new file mode 100644 index 0000000..36763fe Binary files /dev/null and b/portraits/portrait_02.png differ diff --git a/portraits/portrait_03.png b/portraits/portrait_03.png new file mode 100644 index 0000000..f20a7d3 Binary files /dev/null and b/portraits/portrait_03.png differ diff --git a/portraits/portrait_04.png b/portraits/portrait_04.png new file mode 100644 index 0000000..dae2bb5 Binary files /dev/null and b/portraits/portrait_04.png differ diff --git a/portraits/portrait_06.png b/portraits/portrait_06.png new file mode 100644 index 0000000..6c2e50e Binary files /dev/null and b/portraits/portrait_06.png differ diff --git a/portraits/portrait_07.png b/portraits/portrait_07.png new file mode 100644 index 0000000..c3a3393 Binary files /dev/null and b/portraits/portrait_07.png differ diff --git a/portraits/portrait_08.png b/portraits/portrait_08.png new file mode 100644 index 0000000..9a5ace4 Binary files /dev/null and b/portraits/portrait_08.png differ diff --git a/portraits/portrait_09.png b/portraits/portrait_09.png new file mode 100644 index 0000000..e46b216 Binary files /dev/null and b/portraits/portrait_09.png differ diff --git a/portraits/portrait_10.png b/portraits/portrait_10.png new file mode 100644 index 0000000..5b52d5c Binary files /dev/null and b/portraits/portrait_10.png differ diff --git a/portraits/portrait_11.png b/portraits/portrait_11.png new file mode 100644 index 0000000..06d59f7 Binary files /dev/null and b/portraits/portrait_11.png differ diff --git a/portraits/portrait_12.png b/portraits/portrait_12.png new file mode 100644 index 0000000..2713917 Binary files /dev/null and b/portraits/portrait_12.png differ diff --git a/portraits/portrait_14.png b/portraits/portrait_14.png new file mode 100644 index 0000000..0ece879 Binary files /dev/null and b/portraits/portrait_14.png differ diff --git a/portraits/portrait_15.png b/portraits/portrait_15.png new file mode 100644 index 0000000..8402a2a Binary files /dev/null and b/portraits/portrait_15.png differ diff --git a/portraits/portrait_16.png b/portraits/portrait_16.png new file mode 100644 index 0000000..a389f33 Binary files /dev/null and b/portraits/portrait_16.png differ diff --git a/portraits/portrait_17.png b/portraits/portrait_17.png new file mode 100644 index 0000000..866a516 Binary files /dev/null and b/portraits/portrait_17.png differ diff --git a/portraits/portrait_18.png b/portraits/portrait_18.png new file mode 100644 index 0000000..4ff2faf Binary files /dev/null and b/portraits/portrait_18.png differ diff --git a/portraits/portrait_19.png b/portraits/portrait_19.png new file mode 100644 index 0000000..905db89 Binary files /dev/null and b/portraits/portrait_19.png differ diff --git a/portraits/portrait_20.png b/portraits/portrait_20.png new file mode 100644 index 0000000..4d27cf3 Binary files /dev/null and b/portraits/portrait_20.png differ diff --git a/portraits/proc_ebon_01.png b/portraits/proc_ebon_01.png new file mode 100644 index 0000000..dc21cc3 Binary files /dev/null and b/portraits/proc_ebon_01.png differ diff --git a/portraits/proc_ebon_02.png b/portraits/proc_ebon_02.png new file mode 100644 index 0000000..4535f6b Binary files /dev/null and b/portraits/proc_ebon_02.png differ diff --git a/portraits/proc_ebon_03.png b/portraits/proc_ebon_03.png new file mode 100644 index 0000000..f58fb36 Binary files /dev/null and b/portraits/proc_ebon_03.png differ diff --git a/portraits/proc_ebon_04.png b/portraits/proc_ebon_04.png new file mode 100644 index 0000000..467e32c Binary files /dev/null and b/portraits/proc_ebon_04.png differ diff --git a/portraits/proc_ebon_05.png b/portraits/proc_ebon_05.png new file mode 100644 index 0000000..f0f1f8f Binary files /dev/null and b/portraits/proc_ebon_05.png differ diff --git a/portraits/proc_ebon_06.png b/portraits/proc_ebon_06.png new file mode 100644 index 0000000..aa0536c Binary files /dev/null and b/portraits/proc_ebon_06.png differ diff --git a/portraits/proc_ebon_07.png b/portraits/proc_ebon_07.png new file mode 100644 index 0000000..744d29e Binary files /dev/null and b/portraits/proc_ebon_07.png differ diff --git a/portraits/proc_ebon_08.png b/portraits/proc_ebon_08.png new file mode 100644 index 0000000..de2c279 Binary files /dev/null and b/portraits/proc_ebon_08.png differ diff --git a/portraits/proc_ebon_09.png b/portraits/proc_ebon_09.png new file mode 100644 index 0000000..39e066c Binary files /dev/null and b/portraits/proc_ebon_09.png differ diff --git a/portraits/proc_ebon_10.png b/portraits/proc_ebon_10.png new file mode 100644 index 0000000..b900220 Binary files /dev/null and b/portraits/proc_ebon_10.png differ diff --git a/portraits/proc_ebon_11.png b/portraits/proc_ebon_11.png new file mode 100644 index 0000000..10c4eb9 Binary files /dev/null and b/portraits/proc_ebon_11.png differ diff --git a/portraits/proc_ebon_12.png b/portraits/proc_ebon_12.png new file mode 100644 index 0000000..2510a78 Binary files /dev/null and b/portraits/proc_ebon_12.png differ diff --git a/portraits/proc_ebon_13.png b/portraits/proc_ebon_13.png new file mode 100644 index 0000000..7efee63 Binary files /dev/null and b/portraits/proc_ebon_13.png differ diff --git a/portraits/proc_ebon_14.png b/portraits/proc_ebon_14.png new file mode 100644 index 0000000..1cc20ec Binary files /dev/null and b/portraits/proc_ebon_14.png differ diff --git a/portraits/proc_ebon_15.png b/portraits/proc_ebon_15.png new file mode 100644 index 0000000..7f48c4f Binary files /dev/null and b/portraits/proc_ebon_15.png differ diff --git a/portraits/proc_ebon_16.png b/portraits/proc_ebon_16.png new file mode 100644 index 0000000..1ae1e9f Binary files /dev/null and b/portraits/proc_ebon_16.png differ diff --git a/portraits/proc_ebon_17.png b/portraits/proc_ebon_17.png new file mode 100644 index 0000000..c623efa Binary files /dev/null and b/portraits/proc_ebon_17.png differ diff --git a/portraits/proc_ebon_18.png b/portraits/proc_ebon_18.png new file mode 100644 index 0000000..a0beddf Binary files /dev/null and b/portraits/proc_ebon_18.png differ diff --git a/portraits/proc_ebon_19.png b/portraits/proc_ebon_19.png new file mode 100644 index 0000000..4f9cb55 Binary files /dev/null and b/portraits/proc_ebon_19.png differ diff --git a/portraits/proc_ebon_20.png b/portraits/proc_ebon_20.png new file mode 100644 index 0000000..9f029a9 Binary files /dev/null and b/portraits/proc_ebon_20.png differ diff --git a/portraits/proc_ebon_21.png b/portraits/proc_ebon_21.png new file mode 100644 index 0000000..cc3140c Binary files /dev/null and b/portraits/proc_ebon_21.png differ diff --git a/portraits/proc_ebon_22.png b/portraits/proc_ebon_22.png new file mode 100644 index 0000000..7f1b861 Binary files /dev/null and b/portraits/proc_ebon_22.png differ diff --git a/portraits/proc_ebon_23.png b/portraits/proc_ebon_23.png new file mode 100644 index 0000000..26ab9d6 Binary files /dev/null and b/portraits/proc_ebon_23.png differ diff --git a/portraits/proc_ebon_24.png b/portraits/proc_ebon_24.png new file mode 100644 index 0000000..7235a46 Binary files /dev/null and b/portraits/proc_ebon_24.png differ diff --git a/portraits/proc_ebon_25.png b/portraits/proc_ebon_25.png new file mode 100644 index 0000000..c2a62bc Binary files /dev/null and b/portraits/proc_ebon_25.png differ diff --git a/portraits/proc_ebon_26.png b/portraits/proc_ebon_26.png new file mode 100644 index 0000000..e49f402 Binary files /dev/null and b/portraits/proc_ebon_26.png differ diff --git a/portraits/proc_ebon_27.png b/portraits/proc_ebon_27.png new file mode 100644 index 0000000..4430d2b Binary files /dev/null and b/portraits/proc_ebon_27.png differ diff --git a/portraits/proc_ebon_28.png b/portraits/proc_ebon_28.png new file mode 100644 index 0000000..5c14cdb Binary files /dev/null and b/portraits/proc_ebon_28.png differ diff --git a/portraits/proc_ebon_29.png b/portraits/proc_ebon_29.png new file mode 100644 index 0000000..9bcf669 Binary files /dev/null and b/portraits/proc_ebon_29.png differ diff --git a/portraits/proc_ebon_30.png b/portraits/proc_ebon_30.png new file mode 100644 index 0000000..1177b6c Binary files /dev/null and b/portraits/proc_ebon_30.png differ diff --git a/portraits/proc_frother_01.png b/portraits/proc_frother_01.png new file mode 100644 index 0000000..cd201a2 Binary files /dev/null and b/portraits/proc_frother_01.png differ diff --git a/portraits/proc_frother_02.png b/portraits/proc_frother_02.png new file mode 100644 index 0000000..cc7f2db Binary files /dev/null and b/portraits/proc_frother_02.png differ diff --git a/portraits/proc_frother_03.png b/portraits/proc_frother_03.png new file mode 100644 index 0000000..f3d25ed Binary files /dev/null and b/portraits/proc_frother_03.png differ diff --git a/portraits/proc_frother_04.png b/portraits/proc_frother_04.png new file mode 100644 index 0000000..0d0e7fd Binary files /dev/null and b/portraits/proc_frother_04.png differ diff --git a/portraits/proc_frother_05.png b/portraits/proc_frother_05.png new file mode 100644 index 0000000..941e084 Binary files /dev/null and b/portraits/proc_frother_05.png differ diff --git a/portraits/proc_frother_06.png b/portraits/proc_frother_06.png new file mode 100644 index 0000000..5be7654 Binary files /dev/null and b/portraits/proc_frother_06.png differ diff --git a/portraits/proc_frother_07.png b/portraits/proc_frother_07.png new file mode 100644 index 0000000..97eff83 Binary files /dev/null and b/portraits/proc_frother_07.png differ diff --git a/portraits/proc_frother_08.png b/portraits/proc_frother_08.png new file mode 100644 index 0000000..480bd83 Binary files /dev/null and b/portraits/proc_frother_08.png differ diff --git a/portraits/proc_frother_09.png b/portraits/proc_frother_09.png new file mode 100644 index 0000000..29d654f Binary files /dev/null and b/portraits/proc_frother_09.png differ diff --git a/portraits/proc_frother_10.png b/portraits/proc_frother_10.png new file mode 100644 index 0000000..83c3229 Binary files /dev/null and b/portraits/proc_frother_10.png differ diff --git a/portraits/proc_frother_11.png b/portraits/proc_frother_11.png new file mode 100644 index 0000000..410585c Binary files /dev/null and b/portraits/proc_frother_11.png differ diff --git a/portraits/proc_frother_12.png b/portraits/proc_frother_12.png new file mode 100644 index 0000000..12c8571 Binary files /dev/null and b/portraits/proc_frother_12.png differ diff --git a/portraits/proc_frother_13.png b/portraits/proc_frother_13.png new file mode 100644 index 0000000..cf2099a Binary files /dev/null and b/portraits/proc_frother_13.png differ diff --git a/portraits/proc_frother_14.png b/portraits/proc_frother_14.png new file mode 100644 index 0000000..a09b26c Binary files /dev/null and b/portraits/proc_frother_14.png differ diff --git a/portraits/proc_frother_15.png b/portraits/proc_frother_15.png new file mode 100644 index 0000000..699264e Binary files /dev/null and b/portraits/proc_frother_15.png differ diff --git a/portraits/proc_frother_16.png b/portraits/proc_frother_16.png new file mode 100644 index 0000000..73d2ce7 Binary files /dev/null and b/portraits/proc_frother_16.png differ diff --git a/portraits/proc_frother_17.png b/portraits/proc_frother_17.png new file mode 100644 index 0000000..b3d2574 Binary files /dev/null and b/portraits/proc_frother_17.png differ diff --git a/portraits/proc_frother_18.png b/portraits/proc_frother_18.png new file mode 100644 index 0000000..648866c Binary files /dev/null and b/portraits/proc_frother_18.png differ diff --git a/portraits/proc_frother_19.png b/portraits/proc_frother_19.png new file mode 100644 index 0000000..553e847 Binary files /dev/null and b/portraits/proc_frother_19.png differ diff --git a/portraits/proc_frother_20.png b/portraits/proc_frother_20.png new file mode 100644 index 0000000..44dccf7 Binary files /dev/null and b/portraits/proc_frother_20.png differ diff --git a/portraits/proc_frother_21.png b/portraits/proc_frother_21.png new file mode 100644 index 0000000..44f0d24 Binary files /dev/null and b/portraits/proc_frother_21.png differ diff --git a/portraits/proc_frother_22.png b/portraits/proc_frother_22.png new file mode 100644 index 0000000..661ea95 Binary files /dev/null and b/portraits/proc_frother_22.png differ diff --git a/portraits/proc_frother_23.png b/portraits/proc_frother_23.png new file mode 100644 index 0000000..55698fd Binary files /dev/null and b/portraits/proc_frother_23.png differ diff --git a/portraits/proc_frother_24.png b/portraits/proc_frother_24.png new file mode 100644 index 0000000..8ad62db Binary files /dev/null and b/portraits/proc_frother_24.png differ diff --git a/portraits/proc_frother_25.png b/portraits/proc_frother_25.png new file mode 100644 index 0000000..a62427c Binary files /dev/null and b/portraits/proc_frother_25.png differ diff --git a/portraits/proc_human_01.png b/portraits/proc_human_01.png new file mode 100644 index 0000000..60fbf60 Binary files /dev/null and b/portraits/proc_human_01.png differ diff --git a/portraits/proc_human_02.png b/portraits/proc_human_02.png new file mode 100644 index 0000000..e8bcabd Binary files /dev/null and b/portraits/proc_human_02.png differ diff --git a/portraits/proc_human_03.png b/portraits/proc_human_03.png new file mode 100644 index 0000000..abe5857 Binary files /dev/null and b/portraits/proc_human_03.png differ diff --git a/portraits/proc_human_04.png b/portraits/proc_human_04.png new file mode 100644 index 0000000..78d7a9a Binary files /dev/null and b/portraits/proc_human_04.png differ diff --git a/portraits/proc_human_05.png b/portraits/proc_human_05.png new file mode 100644 index 0000000..9a89223 Binary files /dev/null and b/portraits/proc_human_05.png differ diff --git a/portraits/proc_human_06.png b/portraits/proc_human_06.png new file mode 100644 index 0000000..49536e7 Binary files /dev/null and b/portraits/proc_human_06.png differ diff --git a/portraits/proc_human_07.png b/portraits/proc_human_07.png new file mode 100644 index 0000000..1a764ae Binary files /dev/null and b/portraits/proc_human_07.png differ diff --git a/portraits/proc_human_08.png b/portraits/proc_human_08.png new file mode 100644 index 0000000..da08503 Binary files /dev/null and b/portraits/proc_human_08.png differ diff --git a/portraits/proc_human_09.png b/portraits/proc_human_09.png new file mode 100644 index 0000000..55539de Binary files /dev/null and b/portraits/proc_human_09.png differ diff --git a/portraits/proc_human_10.png b/portraits/proc_human_10.png new file mode 100644 index 0000000..4ac1093 Binary files /dev/null and b/portraits/proc_human_10.png differ diff --git a/portraits/proc_human_11.png b/portraits/proc_human_11.png new file mode 100644 index 0000000..ac60474 Binary files /dev/null and b/portraits/proc_human_11.png differ diff --git a/portraits/proc_human_12.png b/portraits/proc_human_12.png new file mode 100644 index 0000000..9fc795a Binary files /dev/null and b/portraits/proc_human_12.png differ diff --git a/portraits/proc_human_13.png b/portraits/proc_human_13.png new file mode 100644 index 0000000..58d7994 Binary files /dev/null and b/portraits/proc_human_13.png differ diff --git a/portraits/proc_human_14.png b/portraits/proc_human_14.png new file mode 100644 index 0000000..fda53de Binary files /dev/null and b/portraits/proc_human_14.png differ diff --git a/portraits/proc_human_15.png b/portraits/proc_human_15.png new file mode 100644 index 0000000..7af8bf7 Binary files /dev/null and b/portraits/proc_human_15.png differ diff --git a/portraits/proc_human_16.png b/portraits/proc_human_16.png new file mode 100644 index 0000000..09b48e0 Binary files /dev/null and b/portraits/proc_human_16.png differ diff --git a/portraits/proc_human_17.png b/portraits/proc_human_17.png new file mode 100644 index 0000000..4c6a694 Binary files /dev/null and b/portraits/proc_human_17.png differ diff --git a/portraits/proc_human_18.png b/portraits/proc_human_18.png new file mode 100644 index 0000000..044c7d2 Binary files /dev/null and b/portraits/proc_human_18.png differ diff --git a/portraits/proc_human_19.png b/portraits/proc_human_19.png new file mode 100644 index 0000000..3d0011d Binary files /dev/null and b/portraits/proc_human_19.png differ diff --git a/portraits/proc_human_20.png b/portraits/proc_human_20.png new file mode 100644 index 0000000..7b863eb Binary files /dev/null and b/portraits/proc_human_20.png differ diff --git a/portraits/proc_human_21.png b/portraits/proc_human_21.png new file mode 100644 index 0000000..82b3d4b Binary files /dev/null and b/portraits/proc_human_21.png differ diff --git a/portraits/proc_human_22.png b/portraits/proc_human_22.png new file mode 100644 index 0000000..27afe62 Binary files /dev/null and b/portraits/proc_human_22.png differ diff --git a/portraits/proc_human_23.png b/portraits/proc_human_23.png new file mode 100644 index 0000000..6df909c Binary files /dev/null and b/portraits/proc_human_23.png differ diff --git a/portraits/proc_human_24.png b/portraits/proc_human_24.png new file mode 100644 index 0000000..2139b3f Binary files /dev/null and b/portraits/proc_human_24.png differ diff --git a/portraits/proc_human_25.png b/portraits/proc_human_25.png new file mode 100644 index 0000000..2ef6e66 Binary files /dev/null and b/portraits/proc_human_25.png differ diff --git a/portraits/proc_human_26.png b/portraits/proc_human_26.png new file mode 100644 index 0000000..b240805 Binary files /dev/null and b/portraits/proc_human_26.png differ diff --git a/portraits/proc_human_27.png b/portraits/proc_human_27.png new file mode 100644 index 0000000..3343519 Binary files /dev/null and b/portraits/proc_human_27.png differ diff --git a/portraits/proc_human_28.png b/portraits/proc_human_28.png new file mode 100644 index 0000000..2b503ef Binary files /dev/null and b/portraits/proc_human_28.png differ diff --git a/portraits/proc_human_29.png b/portraits/proc_human_29.png new file mode 100644 index 0000000..32b63bf Binary files /dev/null and b/portraits/proc_human_29.png differ diff --git a/portraits/proc_human_30.png b/portraits/proc_human_30.png new file mode 100644 index 0000000..9a41ccf Binary files /dev/null and b/portraits/proc_human_30.png differ diff --git a/portraits/proc_human_31.png b/portraits/proc_human_31.png new file mode 100644 index 0000000..f112acf Binary files /dev/null and b/portraits/proc_human_31.png differ diff --git a/portraits/proc_human_32.png b/portraits/proc_human_32.png new file mode 100644 index 0000000..334249b Binary files /dev/null and b/portraits/proc_human_32.png differ diff --git a/portraits/proc_human_33.png b/portraits/proc_human_33.png new file mode 100644 index 0000000..061d829 Binary files /dev/null and b/portraits/proc_human_33.png differ diff --git a/portraits/proc_human_34.png b/portraits/proc_human_34.png new file mode 100644 index 0000000..4f93277 Binary files /dev/null and b/portraits/proc_human_34.png differ diff --git a/portraits/proc_human_35.png b/portraits/proc_human_35.png new file mode 100644 index 0000000..ca9c4b2 Binary files /dev/null and b/portraits/proc_human_35.png differ diff --git a/portraits/proc_human_36.png b/portraits/proc_human_36.png new file mode 100644 index 0000000..037e3fb Binary files /dev/null and b/portraits/proc_human_36.png differ diff --git a/portraits/proc_human_37.png b/portraits/proc_human_37.png new file mode 100644 index 0000000..9d40aec Binary files /dev/null and b/portraits/proc_human_37.png differ diff --git a/portraits/proc_human_38.png b/portraits/proc_human_38.png new file mode 100644 index 0000000..063d17b Binary files /dev/null and b/portraits/proc_human_38.png differ diff --git a/portraits/proc_human_39.png b/portraits/proc_human_39.png new file mode 100644 index 0000000..0eadcbd Binary files /dev/null and b/portraits/proc_human_39.png differ diff --git a/portraits/proc_human_40.png b/portraits/proc_human_40.png new file mode 100644 index 0000000..ce23372 Binary files /dev/null and b/portraits/proc_human_40.png differ diff --git a/portraits/proc_human_41.png b/portraits/proc_human_41.png new file mode 100644 index 0000000..4ebd87e Binary files /dev/null and b/portraits/proc_human_41.png differ diff --git a/portraits/proc_human_42.png b/portraits/proc_human_42.png new file mode 100644 index 0000000..091ed5e Binary files /dev/null and b/portraits/proc_human_42.png differ diff --git a/portraits/proc_human_43.png b/portraits/proc_human_43.png new file mode 100644 index 0000000..41b21d1 Binary files /dev/null and b/portraits/proc_human_43.png differ diff --git a/portraits/proc_human_44.png b/portraits/proc_human_44.png new file mode 100644 index 0000000..f012c5e Binary files /dev/null and b/portraits/proc_human_44.png differ diff --git a/portraits/proc_human_45.png b/portraits/proc_human_45.png new file mode 100644 index 0000000..2f92ef8 Binary files /dev/null and b/portraits/proc_human_45.png differ diff --git a/portraits/proc_human_46.png b/portraits/proc_human_46.png new file mode 100644 index 0000000..b527a22 Binary files /dev/null and b/portraits/proc_human_46.png differ diff --git a/portraits/proc_human_47.png b/portraits/proc_human_47.png new file mode 100644 index 0000000..d9f83c4 Binary files /dev/null and b/portraits/proc_human_47.png differ diff --git a/portraits/proc_human_48.png b/portraits/proc_human_48.png new file mode 100644 index 0000000..ce16f01 Binary files /dev/null and b/portraits/proc_human_48.png differ diff --git a/portraits/proc_human_49.png b/portraits/proc_human_49.png new file mode 100644 index 0000000..b06eff7 Binary files /dev/null and b/portraits/proc_human_49.png differ diff --git a/portraits/proc_human_50.png b/portraits/proc_human_50.png new file mode 100644 index 0000000..9931cb5 Binary files /dev/null and b/portraits/proc_human_50.png differ diff --git a/portraits/proc_shaktar_01.png b/portraits/proc_shaktar_01.png new file mode 100644 index 0000000..5984f8d Binary files /dev/null and b/portraits/proc_shaktar_01.png differ diff --git a/portraits/proc_shaktar_02.png b/portraits/proc_shaktar_02.png new file mode 100644 index 0000000..afab70c Binary files /dev/null and b/portraits/proc_shaktar_02.png differ diff --git a/portraits/proc_shaktar_03.png b/portraits/proc_shaktar_03.png new file mode 100644 index 0000000..d8cec42 Binary files /dev/null and b/portraits/proc_shaktar_03.png differ diff --git a/portraits/proc_shaktar_04.png b/portraits/proc_shaktar_04.png new file mode 100644 index 0000000..e87b112 Binary files /dev/null and b/portraits/proc_shaktar_04.png differ diff --git a/portraits/proc_shaktar_05.png b/portraits/proc_shaktar_05.png new file mode 100644 index 0000000..9c37675 Binary files /dev/null and b/portraits/proc_shaktar_05.png differ diff --git a/portraits/proc_shaktar_06.png b/portraits/proc_shaktar_06.png new file mode 100644 index 0000000..dfaf820 Binary files /dev/null and b/portraits/proc_shaktar_06.png differ diff --git a/portraits/proc_shaktar_07.png b/portraits/proc_shaktar_07.png new file mode 100644 index 0000000..0ef85aa Binary files /dev/null and b/portraits/proc_shaktar_07.png differ diff --git a/portraits/proc_shaktar_08.png b/portraits/proc_shaktar_08.png new file mode 100644 index 0000000..f3cf401 Binary files /dev/null and b/portraits/proc_shaktar_08.png differ diff --git a/portraits/proc_shaktar_09.png b/portraits/proc_shaktar_09.png new file mode 100644 index 0000000..a5321e9 Binary files /dev/null and b/portraits/proc_shaktar_09.png differ diff --git a/portraits/proc_shaktar_10.png b/portraits/proc_shaktar_10.png new file mode 100644 index 0000000..b9f379e Binary files /dev/null and b/portraits/proc_shaktar_10.png differ diff --git a/portraits/proc_shaktar_11.png b/portraits/proc_shaktar_11.png new file mode 100644 index 0000000..c3bf4c7 Binary files /dev/null and b/portraits/proc_shaktar_11.png differ diff --git a/portraits/proc_shaktar_12.png b/portraits/proc_shaktar_12.png new file mode 100644 index 0000000..85dfd95 Binary files /dev/null and b/portraits/proc_shaktar_12.png differ diff --git a/portraits/proc_shaktar_13.png b/portraits/proc_shaktar_13.png new file mode 100644 index 0000000..9886d8a Binary files /dev/null and b/portraits/proc_shaktar_13.png differ diff --git a/portraits/proc_shaktar_14.png b/portraits/proc_shaktar_14.png new file mode 100644 index 0000000..e5ff9a4 Binary files /dev/null and b/portraits/proc_shaktar_14.png differ diff --git a/portraits/proc_shaktar_15.png b/portraits/proc_shaktar_15.png new file mode 100644 index 0000000..0d05e13 Binary files /dev/null and b/portraits/proc_shaktar_15.png differ diff --git a/portraits/proc_shaktar_16.png b/portraits/proc_shaktar_16.png new file mode 100644 index 0000000..7e2247f Binary files /dev/null and b/portraits/proc_shaktar_16.png differ diff --git a/portraits/proc_shaktar_17.png b/portraits/proc_shaktar_17.png new file mode 100644 index 0000000..da795f3 Binary files /dev/null and b/portraits/proc_shaktar_17.png differ diff --git a/portraits/proc_shaktar_18.png b/portraits/proc_shaktar_18.png new file mode 100644 index 0000000..e6eb479 Binary files /dev/null and b/portraits/proc_shaktar_18.png differ diff --git a/portraits/proc_shaktar_19.png b/portraits/proc_shaktar_19.png new file mode 100644 index 0000000..2e5b2b6 Binary files /dev/null and b/portraits/proc_shaktar_19.png differ diff --git a/portraits/proc_shaktar_20.png b/portraits/proc_shaktar_20.png new file mode 100644 index 0000000..558facb Binary files /dev/null and b/portraits/proc_shaktar_20.png differ diff --git a/portraits/proc_threat_01.png b/portraits/proc_threat_01.png new file mode 100644 index 0000000..6782766 Binary files /dev/null and b/portraits/proc_threat_01.png differ diff --git a/portraits/proc_threat_02.png b/portraits/proc_threat_02.png new file mode 100644 index 0000000..52934ba Binary files /dev/null and b/portraits/proc_threat_02.png differ diff --git a/portraits/proc_threat_03.png b/portraits/proc_threat_03.png new file mode 100644 index 0000000..3782fa1 Binary files /dev/null and b/portraits/proc_threat_03.png differ diff --git a/portraits/proc_threat_04.png b/portraits/proc_threat_04.png new file mode 100644 index 0000000..00f5006 Binary files /dev/null and b/portraits/proc_threat_04.png differ diff --git a/portraits/proc_threat_05.png b/portraits/proc_threat_05.png new file mode 100644 index 0000000..8deb226 Binary files /dev/null and b/portraits/proc_threat_05.png differ diff --git a/portraits/proc_threat_06.png b/portraits/proc_threat_06.png new file mode 100644 index 0000000..8ef6f7b Binary files /dev/null and b/portraits/proc_threat_06.png differ diff --git a/portraits/proc_threat_07.png b/portraits/proc_threat_07.png new file mode 100644 index 0000000..6b7646b Binary files /dev/null and b/portraits/proc_threat_07.png differ diff --git a/portraits/proc_threat_08.png b/portraits/proc_threat_08.png new file mode 100644 index 0000000..e3e34bf Binary files /dev/null and b/portraits/proc_threat_08.png differ diff --git a/portraits/proc_threat_09.png b/portraits/proc_threat_09.png new file mode 100644 index 0000000..281e428 Binary files /dev/null and b/portraits/proc_threat_09.png differ diff --git a/portraits/proc_threat_10.png b/portraits/proc_threat_10.png new file mode 100644 index 0000000..19a4e59 Binary files /dev/null and b/portraits/proc_threat_10.png differ diff --git a/portraits/proc_threat_11.png b/portraits/proc_threat_11.png new file mode 100644 index 0000000..597048b Binary files /dev/null and b/portraits/proc_threat_11.png differ diff --git a/portraits/proc_threat_12.png b/portraits/proc_threat_12.png new file mode 100644 index 0000000..1492844 Binary files /dev/null and b/portraits/proc_threat_12.png differ diff --git a/portraits/proc_threat_13.png b/portraits/proc_threat_13.png new file mode 100644 index 0000000..1aef5a7 Binary files /dev/null and b/portraits/proc_threat_13.png differ diff --git a/portraits/proc_threat_14.png b/portraits/proc_threat_14.png new file mode 100644 index 0000000..20ca51c Binary files /dev/null and b/portraits/proc_threat_14.png differ diff --git a/portraits/proc_threat_15.png b/portraits/proc_threat_15.png new file mode 100644 index 0000000..93de2ab Binary files /dev/null and b/portraits/proc_threat_15.png differ diff --git a/portraits/proc_threat_16.png b/portraits/proc_threat_16.png new file mode 100644 index 0000000..0fb1169 Binary files /dev/null and b/portraits/proc_threat_16.png differ diff --git a/portraits/proc_threat_17.png b/portraits/proc_threat_17.png new file mode 100644 index 0000000..e91e727 Binary files /dev/null and b/portraits/proc_threat_17.png differ diff --git a/portraits/proc_threat_18.png b/portraits/proc_threat_18.png new file mode 100644 index 0000000..28457de Binary files /dev/null and b/portraits/proc_threat_18.png differ diff --git a/portraits/proc_threat_19.png b/portraits/proc_threat_19.png new file mode 100644 index 0000000..aff53cf Binary files /dev/null and b/portraits/proc_threat_19.png differ diff --git a/portraits/proc_threat_20.png b/portraits/proc_threat_20.png new file mode 100644 index 0000000..910c822 Binary files /dev/null and b/portraits/proc_threat_20.png differ diff --git a/portraits/proc_threat_21.png b/portraits/proc_threat_21.png new file mode 100644 index 0000000..d587742 Binary files /dev/null and b/portraits/proc_threat_21.png differ diff --git a/portraits/proc_threat_22.png b/portraits/proc_threat_22.png new file mode 100644 index 0000000..0222f69 Binary files /dev/null and b/portraits/proc_threat_22.png differ diff --git a/portraits/proc_threat_23.png b/portraits/proc_threat_23.png new file mode 100644 index 0000000..422e7b0 Binary files /dev/null and b/portraits/proc_threat_23.png differ diff --git a/portraits/proc_threat_24.png b/portraits/proc_threat_24.png new file mode 100644 index 0000000..c40b9e5 Binary files /dev/null and b/portraits/proc_threat_24.png differ diff --git a/portraits/proc_threat_25.png b/portraits/proc_threat_25.png new file mode 100644 index 0000000..59e9afa Binary files /dev/null and b/portraits/proc_threat_25.png differ diff --git a/portraits/proc_threat_26.png b/portraits/proc_threat_26.png new file mode 100644 index 0000000..261663e Binary files /dev/null and b/portraits/proc_threat_26.png differ diff --git a/portraits/proc_threat_27.png b/portraits/proc_threat_27.png new file mode 100644 index 0000000..c08f84e Binary files /dev/null and b/portraits/proc_threat_27.png differ diff --git a/portraits/proc_threat_28.png b/portraits/proc_threat_28.png new file mode 100644 index 0000000..5864c8d Binary files /dev/null and b/portraits/proc_threat_28.png differ diff --git a/portraits/proc_threat_29.png b/portraits/proc_threat_29.png new file mode 100644 index 0000000..1447dc7 Binary files /dev/null and b/portraits/proc_threat_29.png differ diff --git a/portraits/proc_threat_30.png b/portraits/proc_threat_30.png new file mode 100644 index 0000000..6115fb6 Binary files /dev/null and b/portraits/proc_threat_30.png differ diff --git a/portraits/proc_waister_01.png b/portraits/proc_waister_01.png new file mode 100644 index 0000000..4dd8920 Binary files /dev/null and b/portraits/proc_waister_01.png differ diff --git a/portraits/proc_waister_02.png b/portraits/proc_waister_02.png new file mode 100644 index 0000000..680bcf6 Binary files /dev/null and b/portraits/proc_waister_02.png differ diff --git a/portraits/proc_waister_03.png b/portraits/proc_waister_03.png new file mode 100644 index 0000000..6bd8229 Binary files /dev/null and b/portraits/proc_waister_03.png differ diff --git a/portraits/proc_waister_04.png b/portraits/proc_waister_04.png new file mode 100644 index 0000000..901717e Binary files /dev/null and b/portraits/proc_waister_04.png differ diff --git a/portraits/proc_waister_05.png b/portraits/proc_waister_05.png new file mode 100644 index 0000000..e3df3f4 Binary files /dev/null and b/portraits/proc_waister_05.png differ diff --git a/portraits/proc_waister_06.png b/portraits/proc_waister_06.png new file mode 100644 index 0000000..665b9cb Binary files /dev/null and b/portraits/proc_waister_06.png differ diff --git a/portraits/proc_waister_07.png b/portraits/proc_waister_07.png new file mode 100644 index 0000000..57b7d25 Binary files /dev/null and b/portraits/proc_waister_07.png differ diff --git a/portraits/proc_waister_08.png b/portraits/proc_waister_08.png new file mode 100644 index 0000000..e9cbade Binary files /dev/null and b/portraits/proc_waister_08.png differ diff --git a/portraits/proc_waister_09.png b/portraits/proc_waister_09.png new file mode 100644 index 0000000..0cadd82 Binary files /dev/null and b/portraits/proc_waister_09.png differ diff --git a/portraits/proc_waister_10.png b/portraits/proc_waister_10.png new file mode 100644 index 0000000..acfa5ba Binary files /dev/null and b/portraits/proc_waister_10.png differ diff --git a/portraits/proc_waister_11.png b/portraits/proc_waister_11.png new file mode 100644 index 0000000..52f81f9 Binary files /dev/null and b/portraits/proc_waister_11.png differ diff --git a/portraits/proc_waister_12.png b/portraits/proc_waister_12.png new file mode 100644 index 0000000..8c37a4d Binary files /dev/null and b/portraits/proc_waister_12.png differ diff --git a/portraits/proc_waister_13.png b/portraits/proc_waister_13.png new file mode 100644 index 0000000..499e99c Binary files /dev/null and b/portraits/proc_waister_13.png differ diff --git a/portraits/proc_waister_14.png b/portraits/proc_waister_14.png new file mode 100644 index 0000000..d2b18b8 Binary files /dev/null and b/portraits/proc_waister_14.png differ diff --git a/portraits/proc_waister_15.png b/portraits/proc_waister_15.png new file mode 100644 index 0000000..2144597 Binary files /dev/null and b/portraits/proc_waister_15.png differ diff --git a/portraits/proc_waister_16.png b/portraits/proc_waister_16.png new file mode 100644 index 0000000..a325fda Binary files /dev/null and b/portraits/proc_waister_16.png differ diff --git a/portraits/proc_waister_17.png b/portraits/proc_waister_17.png new file mode 100644 index 0000000..feea3a5 Binary files /dev/null and b/portraits/proc_waister_17.png differ diff --git a/portraits/proc_waister_18.png b/portraits/proc_waister_18.png new file mode 100644 index 0000000..c8fa1e4 Binary files /dev/null and b/portraits/proc_waister_18.png differ diff --git a/portraits/proc_waister_19.png b/portraits/proc_waister_19.png new file mode 100644 index 0000000..a387294 Binary files /dev/null and b/portraits/proc_waister_19.png differ diff --git a/portraits/proc_waister_20.png b/portraits/proc_waister_20.png new file mode 100644 index 0000000..8251529 Binary files /dev/null and b/portraits/proc_waister_20.png differ diff --git a/portraits/proc_wraith_01.png b/portraits/proc_wraith_01.png new file mode 100644 index 0000000..424bfab Binary files /dev/null and b/portraits/proc_wraith_01.png differ diff --git a/portraits/proc_wraith_02.png b/portraits/proc_wraith_02.png new file mode 100644 index 0000000..3da040a Binary files /dev/null and b/portraits/proc_wraith_02.png differ diff --git a/portraits/proc_wraith_03.png b/portraits/proc_wraith_03.png new file mode 100644 index 0000000..f92f6f3 Binary files /dev/null and b/portraits/proc_wraith_03.png differ diff --git a/portraits/proc_wraith_04.png b/portraits/proc_wraith_04.png new file mode 100644 index 0000000..21ad8b6 Binary files /dev/null and b/portraits/proc_wraith_04.png differ diff --git a/portraits/proc_wraith_05.png b/portraits/proc_wraith_05.png new file mode 100644 index 0000000..9c33d9c Binary files /dev/null and b/portraits/proc_wraith_05.png differ diff --git a/portraits/proc_wraith_06.png b/portraits/proc_wraith_06.png new file mode 100644 index 0000000..1242223 Binary files /dev/null and b/portraits/proc_wraith_06.png differ diff --git a/portraits/proc_wraith_07.png b/portraits/proc_wraith_07.png new file mode 100644 index 0000000..b3a8895 Binary files /dev/null and b/portraits/proc_wraith_07.png differ diff --git a/portraits/proc_wraith_08.png b/portraits/proc_wraith_08.png new file mode 100644 index 0000000..dbc35e2 Binary files /dev/null and b/portraits/proc_wraith_08.png differ diff --git a/portraits/proc_wraith_09.png b/portraits/proc_wraith_09.png new file mode 100644 index 0000000..607a104 Binary files /dev/null and b/portraits/proc_wraith_09.png differ diff --git a/portraits/proc_wraith_10.png b/portraits/proc_wraith_10.png new file mode 100644 index 0000000..a809219 Binary files /dev/null and b/portraits/proc_wraith_10.png differ diff --git a/portraits/proc_wraith_11.png b/portraits/proc_wraith_11.png new file mode 100644 index 0000000..1cb5c2a Binary files /dev/null and b/portraits/proc_wraith_11.png differ diff --git a/portraits/proc_wraith_12.png b/portraits/proc_wraith_12.png new file mode 100644 index 0000000..c6be11d Binary files /dev/null and b/portraits/proc_wraith_12.png differ diff --git a/portraits/proc_wraith_13.png b/portraits/proc_wraith_13.png new file mode 100644 index 0000000..50113b0 Binary files /dev/null and b/portraits/proc_wraith_13.png differ diff --git a/portraits/proc_wraith_14.png b/portraits/proc_wraith_14.png new file mode 100644 index 0000000..371facd Binary files /dev/null and b/portraits/proc_wraith_14.png differ diff --git a/portraits/proc_wraith_15.png b/portraits/proc_wraith_15.png new file mode 100644 index 0000000..3564da8 Binary files /dev/null and b/portraits/proc_wraith_15.png differ diff --git a/portraits/proc_wraith_16.png b/portraits/proc_wraith_16.png new file mode 100644 index 0000000..d6fce55 Binary files /dev/null and b/portraits/proc_wraith_16.png differ diff --git a/portraits/proc_wraith_17.png b/portraits/proc_wraith_17.png new file mode 100644 index 0000000..d520068 Binary files /dev/null and b/portraits/proc_wraith_17.png differ diff --git a/portraits/proc_wraith_18.png b/portraits/proc_wraith_18.png new file mode 100644 index 0000000..6e5e3cb Binary files /dev/null and b/portraits/proc_wraith_18.png differ diff --git a/portraits/proc_wraith_19.png b/portraits/proc_wraith_19.png new file mode 100644 index 0000000..6f45db0 Binary files /dev/null and b/portraits/proc_wraith_19.png differ diff --git a/portraits/proc_wraith_20.png b/portraits/proc_wraith_20.png new file mode 100644 index 0000000..82432ac Binary files /dev/null and b/portraits/proc_wraith_20.png differ diff --git a/portraits/proc_wraith_21.png b/portraits/proc_wraith_21.png new file mode 100644 index 0000000..d9adaab Binary files /dev/null and b/portraits/proc_wraith_21.png differ diff --git a/portraits/proc_wraith_22.png b/portraits/proc_wraith_22.png new file mode 100644 index 0000000..5410504 Binary files /dev/null and b/portraits/proc_wraith_22.png differ diff --git a/portraits/proc_wraith_23.png b/portraits/proc_wraith_23.png new file mode 100644 index 0000000..f06d4c6 Binary files /dev/null and b/portraits/proc_wraith_23.png differ diff --git a/portraits/proc_wraith_24.png b/portraits/proc_wraith_24.png new file mode 100644 index 0000000..4e10fc7 Binary files /dev/null and b/portraits/proc_wraith_24.png differ diff --git a/portraits/proc_wraith_25.png b/portraits/proc_wraith_25.png new file mode 100644 index 0000000..3204c89 Binary files /dev/null and b/portraits/proc_wraith_25.png differ diff --git a/portraits/serial_killer_01.png b/portraits/serial_killer_01.png new file mode 100644 index 0000000..8a7611a Binary files /dev/null and b/portraits/serial_killer_01.png differ diff --git a/portraits/serial_killer_02.png b/portraits/serial_killer_02.png new file mode 100644 index 0000000..9accff2 Binary files /dev/null and b/portraits/serial_killer_02.png differ diff --git a/portraits/serial_killer_03.png b/portraits/serial_killer_03.png new file mode 100644 index 0000000..5e6ac72 Binary files /dev/null and b/portraits/serial_killer_03.png differ diff --git a/portraits/serial_killer_04.png b/portraits/serial_killer_04.png new file mode 100644 index 0000000..0595f9a Binary files /dev/null and b/portraits/serial_killer_04.png differ diff --git a/portraits/serial_killer_05.png b/portraits/serial_killer_05.png new file mode 100644 index 0000000..e2d6ec3 Binary files /dev/null and b/portraits/serial_killer_05.png differ diff --git a/portraits/serial_killer_06.png b/portraits/serial_killer_06.png new file mode 100644 index 0000000..a021c70 Binary files /dev/null and b/portraits/serial_killer_06.png differ diff --git a/portraits/serial_killer_07.png b/portraits/serial_killer_07.png new file mode 100644 index 0000000..40c3dad Binary files /dev/null and b/portraits/serial_killer_07.png differ diff --git a/portraits/serial_killer_08.png b/portraits/serial_killer_08.png new file mode 100644 index 0000000..d04a90d Binary files /dev/null and b/portraits/serial_killer_08.png differ diff --git a/portraits/serial_killer_09.png b/portraits/serial_killer_09.png new file mode 100644 index 0000000..523dceb Binary files /dev/null and b/portraits/serial_killer_09.png differ diff --git a/portraits/serial_killer_10.png b/portraits/serial_killer_10.png new file mode 100644 index 0000000..2f95c4a Binary files /dev/null and b/portraits/serial_killer_10.png differ diff --git a/portraits/serial_killer_11.png b/portraits/serial_killer_11.png new file mode 100644 index 0000000..f101634 Binary files /dev/null and b/portraits/serial_killer_11.png differ diff --git a/portraits/serial_killer_12.png b/portraits/serial_killer_12.png new file mode 100644 index 0000000..f25b2c2 Binary files /dev/null and b/portraits/serial_killer_12.png differ diff --git a/portraits/serial_killer_14.png b/portraits/serial_killer_14.png new file mode 100644 index 0000000..6d4bc70 Binary files /dev/null and b/portraits/serial_killer_14.png differ diff --git a/portraits/serial_killer_15.png b/portraits/serial_killer_15.png new file mode 100644 index 0000000..66e0dc0 Binary files /dev/null and b/portraits/serial_killer_15.png differ diff --git a/portraits/serial_killer_16.png b/portraits/serial_killer_16.png new file mode 100644 index 0000000..c6fef1e Binary files /dev/null and b/portraits/serial_killer_16.png differ diff --git a/portraits/serial_killer_17.png b/portraits/serial_killer_17.png new file mode 100644 index 0000000..868b03f Binary files /dev/null and b/portraits/serial_killer_17.png differ diff --git a/portraits/serial_killer_18.png b/portraits/serial_killer_18.png new file mode 100644 index 0000000..88c6454 Binary files /dev/null and b/portraits/serial_killer_18.png differ diff --git a/portraits/serial_killer_19.png b/portraits/serial_killer_19.png new file mode 100644 index 0000000..a98e6cb Binary files /dev/null and b/portraits/serial_killer_19.png differ diff --git a/portraits/serial_killer_20.png b/portraits/serial_killer_20.png new file mode 100644 index 0000000..1a167fa Binary files /dev/null and b/portraits/serial_killer_20.png differ diff --git a/portraits/serial_killer_21.png b/portraits/serial_killer_21.png new file mode 100644 index 0000000..4416aa1 Binary files /dev/null and b/portraits/serial_killer_21.png differ diff --git a/portraits/serial_killer_22.png b/portraits/serial_killer_22.png new file mode 100644 index 0000000..3220dbf Binary files /dev/null and b/portraits/serial_killer_22.png differ diff --git a/portraits/serial_killer_23.png b/portraits/serial_killer_23.png new file mode 100644 index 0000000..3fb184b Binary files /dev/null and b/portraits/serial_killer_23.png differ diff --git a/portraits/serial_killer_24.png b/portraits/serial_killer_24.png new file mode 100644 index 0000000..d490b7e Binary files /dev/null and b/portraits/serial_killer_24.png differ diff --git a/portraits/serial_killer_25.png b/portraits/serial_killer_25.png new file mode 100644 index 0000000..32281f9 Binary files /dev/null and b/portraits/serial_killer_25.png differ diff --git a/portraits/serial_killer_26.png b/portraits/serial_killer_26.png new file mode 100644 index 0000000..ff9d317 Binary files /dev/null and b/portraits/serial_killer_26.png differ diff --git a/portraits/serial_killer_27.png b/portraits/serial_killer_27.png new file mode 100644 index 0000000..ee20edf Binary files /dev/null and b/portraits/serial_killer_27.png differ diff --git a/portraits/serial_killer_28.png b/portraits/serial_killer_28.png new file mode 100644 index 0000000..4aaa138 Binary files /dev/null and b/portraits/serial_killer_28.png differ diff --git a/portraits/serial_killer_29.png b/portraits/serial_killer_29.png new file mode 100644 index 0000000..35e0d39 Binary files /dev/null and b/portraits/serial_killer_29.png differ diff --git a/portraits/serial_killer_30.png b/portraits/serial_killer_30.png new file mode 100644 index 0000000..fe79043 Binary files /dev/null and b/portraits/serial_killer_30.png differ diff --git a/portraits/serial_killer_31.png b/portraits/serial_killer_31.png new file mode 100644 index 0000000..1e862c3 Binary files /dev/null and b/portraits/serial_killer_31.png differ diff --git a/portraits/serial_killer_32.png b/portraits/serial_killer_32.png new file mode 100644 index 0000000..ac5d88c Binary files /dev/null and b/portraits/serial_killer_32.png differ diff --git a/portraits/serial_killer_33.png b/portraits/serial_killer_33.png new file mode 100644 index 0000000..78c4ee3 Binary files /dev/null and b/portraits/serial_killer_33.png differ diff --git a/portraits/serial_killer_34.png b/portraits/serial_killer_34.png new file mode 100644 index 0000000..688dfdb Binary files /dev/null and b/portraits/serial_killer_34.png differ diff --git a/portraits/serial_killer_35.png b/portraits/serial_killer_35.png new file mode 100644 index 0000000..45d0d6f Binary files /dev/null and b/portraits/serial_killer_35.png differ diff --git a/portraits/serial_killer_36.png b/portraits/serial_killer_36.png new file mode 100644 index 0000000..c7a3435 Binary files /dev/null and b/portraits/serial_killer_36.png differ diff --git a/portraits/serial_killer_37.png b/portraits/serial_killer_37.png new file mode 100644 index 0000000..35aa07e Binary files /dev/null and b/portraits/serial_killer_37.png differ diff --git a/portraits/serial_killer_38.png b/portraits/serial_killer_38.png new file mode 100644 index 0000000..f4fc04d Binary files /dev/null and b/portraits/serial_killer_38.png differ diff --git a/portraits/serial_killer_39.png b/portraits/serial_killer_39.png new file mode 100644 index 0000000..e67eee8 Binary files /dev/null and b/portraits/serial_killer_39.png differ diff --git a/portraits/shaktar_female_01.png b/portraits/shaktar_female_01.png new file mode 100644 index 0000000..2cd57fe Binary files /dev/null and b/portraits/shaktar_female_01.png differ diff --git a/portraits/shaktar_female_02.png b/portraits/shaktar_female_02.png new file mode 100644 index 0000000..5402d50 Binary files /dev/null and b/portraits/shaktar_female_02.png differ diff --git a/portraits/shaktar_female_03.png b/portraits/shaktar_female_03.png new file mode 100644 index 0000000..540a84f Binary files /dev/null and b/portraits/shaktar_female_03.png differ diff --git a/portraits/shaktar_female_04.png b/portraits/shaktar_female_04.png new file mode 100644 index 0000000..3887e09 Binary files /dev/null and b/portraits/shaktar_female_04.png differ diff --git a/portraits/shaktar_female_05.png b/portraits/shaktar_female_05.png new file mode 100644 index 0000000..9fd5875 Binary files /dev/null and b/portraits/shaktar_female_05.png differ diff --git a/portraits/shaktar_female_06.png b/portraits/shaktar_female_06.png new file mode 100644 index 0000000..93e5e37 Binary files /dev/null and b/portraits/shaktar_female_06.png differ diff --git a/portraits/shaktar_female_07.png b/portraits/shaktar_female_07.png new file mode 100644 index 0000000..b192088 Binary files /dev/null and b/portraits/shaktar_female_07.png differ diff --git a/portraits/shaktar_female_08.png b/portraits/shaktar_female_08.png new file mode 100644 index 0000000..962d59b Binary files /dev/null and b/portraits/shaktar_female_08.png differ diff --git a/portraits/shaktar_female_09.png b/portraits/shaktar_female_09.png new file mode 100644 index 0000000..f8574a3 Binary files /dev/null and b/portraits/shaktar_female_09.png differ diff --git a/portraits/shaktar_female_10.png b/portraits/shaktar_female_10.png new file mode 100644 index 0000000..90f3bad Binary files /dev/null and b/portraits/shaktar_female_10.png differ diff --git a/portraits/shaktar_female_11.png b/portraits/shaktar_female_11.png new file mode 100644 index 0000000..71cf0c5 Binary files /dev/null and b/portraits/shaktar_female_11.png differ diff --git a/portraits/shaktar_female_12.png b/portraits/shaktar_female_12.png new file mode 100644 index 0000000..5db7269 Binary files /dev/null and b/portraits/shaktar_female_12.png differ diff --git a/portraits/shaktar_female_13.png b/portraits/shaktar_female_13.png new file mode 100644 index 0000000..61f97ad Binary files /dev/null and b/portraits/shaktar_female_13.png differ diff --git a/portraits/shaktar_female_14.png b/portraits/shaktar_female_14.png new file mode 100644 index 0000000..2004ce0 Binary files /dev/null and b/portraits/shaktar_female_14.png differ diff --git a/portraits/shaktar_female_15.png b/portraits/shaktar_female_15.png new file mode 100644 index 0000000..032c60f Binary files /dev/null and b/portraits/shaktar_female_15.png differ diff --git a/portraits/shaktar_female_16.png b/portraits/shaktar_female_16.png new file mode 100644 index 0000000..c30407c Binary files /dev/null and b/portraits/shaktar_female_16.png differ diff --git a/portraits/shaktar_female_17.png b/portraits/shaktar_female_17.png new file mode 100644 index 0000000..0698d43 Binary files /dev/null and b/portraits/shaktar_female_17.png differ diff --git a/portraits/shaktar_female_18.png b/portraits/shaktar_female_18.png new file mode 100644 index 0000000..96493ed Binary files /dev/null and b/portraits/shaktar_female_18.png differ diff --git a/portraits/shaktar_male_01.png b/portraits/shaktar_male_01.png new file mode 100644 index 0000000..e6c4bdc Binary files /dev/null and b/portraits/shaktar_male_01.png differ diff --git a/portraits/shaktar_male_02.png b/portraits/shaktar_male_02.png new file mode 100644 index 0000000..e4a4887 Binary files /dev/null and b/portraits/shaktar_male_02.png differ diff --git a/portraits/shaktar_male_03.png b/portraits/shaktar_male_03.png new file mode 100644 index 0000000..21e811c Binary files /dev/null and b/portraits/shaktar_male_03.png differ diff --git a/portraits/shaktar_male_04.png b/portraits/shaktar_male_04.png new file mode 100644 index 0000000..7613673 Binary files /dev/null and b/portraits/shaktar_male_04.png differ diff --git a/portraits/shaktar_male_05.png b/portraits/shaktar_male_05.png new file mode 100644 index 0000000..7c708df Binary files /dev/null and b/portraits/shaktar_male_05.png differ diff --git a/portraits/shaktar_male_06.png b/portraits/shaktar_male_06.png new file mode 100644 index 0000000..1079b90 Binary files /dev/null and b/portraits/shaktar_male_06.png differ diff --git a/portraits/shaktar_male_07.png b/portraits/shaktar_male_07.png new file mode 100644 index 0000000..a15445f Binary files /dev/null and b/portraits/shaktar_male_07.png differ diff --git a/portraits/shaktar_male_08.png b/portraits/shaktar_male_08.png new file mode 100644 index 0000000..f868e3b Binary files /dev/null and b/portraits/shaktar_male_08.png differ diff --git a/portraits/shaktar_male_09.png b/portraits/shaktar_male_09.png new file mode 100644 index 0000000..94b3e6d Binary files /dev/null and b/portraits/shaktar_male_09.png differ diff --git a/portraits/shaktar_male_10.png b/portraits/shaktar_male_10.png new file mode 100644 index 0000000..ca4849c Binary files /dev/null and b/portraits/shaktar_male_10.png differ diff --git a/portraits/shaktar_male_11.png b/portraits/shaktar_male_11.png new file mode 100644 index 0000000..bd47b6d Binary files /dev/null and b/portraits/shaktar_male_11.png differ diff --git a/portraits/shaktar_male_12.png b/portraits/shaktar_male_12.png new file mode 100644 index 0000000..f27912f Binary files /dev/null and b/portraits/shaktar_male_12.png differ diff --git a/portraits/shaktar_male_13.png b/portraits/shaktar_male_13.png new file mode 100644 index 0000000..e54bd6d Binary files /dev/null and b/portraits/shaktar_male_13.png differ diff --git a/portraits/shaktar_male_14.png b/portraits/shaktar_male_14.png new file mode 100644 index 0000000..40d62a7 Binary files /dev/null and b/portraits/shaktar_male_14.png differ diff --git a/portraits/shaktar_male_15.png b/portraits/shaktar_male_15.png new file mode 100644 index 0000000..0bee4d9 Binary files /dev/null and b/portraits/shaktar_male_15.png differ diff --git a/portraits/shaktar_male_16.png b/portraits/shaktar_male_16.png new file mode 100644 index 0000000..7cff990 Binary files /dev/null and b/portraits/shaktar_male_16.png differ diff --git a/portraits/shaktar_male_17.png b/portraits/shaktar_male_17.png new file mode 100644 index 0000000..5d83dee Binary files /dev/null and b/portraits/shaktar_male_17.png differ diff --git a/portraits/shaktar_male_18.png b/portraits/shaktar_male_18.png new file mode 100644 index 0000000..3333278 Binary files /dev/null and b/portraits/shaktar_male_18.png differ diff --git a/portraits/stormer_female_01.png b/portraits/stormer_female_01.png new file mode 100644 index 0000000..fe85562 Binary files /dev/null and b/portraits/stormer_female_01.png differ diff --git a/portraits/stormer_female_02.png b/portraits/stormer_female_02.png new file mode 100644 index 0000000..7c6e861 Binary files /dev/null and b/portraits/stormer_female_02.png differ diff --git a/portraits/stormer_female_03.png b/portraits/stormer_female_03.png new file mode 100644 index 0000000..665ff5c Binary files /dev/null and b/portraits/stormer_female_03.png differ diff --git a/portraits/stormer_female_04.png b/portraits/stormer_female_04.png new file mode 100644 index 0000000..2384f63 Binary files /dev/null and b/portraits/stormer_female_04.png differ diff --git a/portraits/stormer_female_05.png b/portraits/stormer_female_05.png new file mode 100644 index 0000000..e601530 Binary files /dev/null and b/portraits/stormer_female_05.png differ diff --git a/portraits/stormer_female_06.png b/portraits/stormer_female_06.png new file mode 100644 index 0000000..4a73ebe Binary files /dev/null and b/portraits/stormer_female_06.png differ diff --git a/portraits/stormer_female_07.png b/portraits/stormer_female_07.png new file mode 100644 index 0000000..258180a Binary files /dev/null and b/portraits/stormer_female_07.png differ diff --git a/portraits/stormer_female_08.png b/portraits/stormer_female_08.png new file mode 100644 index 0000000..e6794d8 Binary files /dev/null and b/portraits/stormer_female_08.png differ diff --git a/portraits/stormer_female_09.png b/portraits/stormer_female_09.png new file mode 100644 index 0000000..2a36b20 Binary files /dev/null and b/portraits/stormer_female_09.png differ diff --git a/portraits/stormer_female_10.png b/portraits/stormer_female_10.png new file mode 100644 index 0000000..22839fd Binary files /dev/null and b/portraits/stormer_female_10.png differ diff --git a/portraits/stormer_female_11.png b/portraits/stormer_female_11.png new file mode 100644 index 0000000..05d1a14 Binary files /dev/null and b/portraits/stormer_female_11.png differ diff --git a/portraits/stormer_female_12.png b/portraits/stormer_female_12.png new file mode 100644 index 0000000..0ee7648 Binary files /dev/null and b/portraits/stormer_female_12.png differ diff --git a/portraits/stormer_female_13.png b/portraits/stormer_female_13.png new file mode 100644 index 0000000..701dc0e Binary files /dev/null and b/portraits/stormer_female_13.png differ diff --git a/portraits/stormer_female_14.png b/portraits/stormer_female_14.png new file mode 100644 index 0000000..1e60fd6 Binary files /dev/null and b/portraits/stormer_female_14.png differ diff --git a/portraits/stormer_female_15.png b/portraits/stormer_female_15.png new file mode 100644 index 0000000..c28ce04 Binary files /dev/null and b/portraits/stormer_female_15.png differ diff --git a/portraits/stormer_female_16.png b/portraits/stormer_female_16.png new file mode 100644 index 0000000..2395d61 Binary files /dev/null and b/portraits/stormer_female_16.png differ diff --git a/portraits/stormer_female_17.png b/portraits/stormer_female_17.png new file mode 100644 index 0000000..0615a83 Binary files /dev/null and b/portraits/stormer_female_17.png differ diff --git a/portraits/stormer_female_18.png b/portraits/stormer_female_18.png new file mode 100644 index 0000000..a320ee9 Binary files /dev/null and b/portraits/stormer_female_18.png differ diff --git a/portraits/stormer_female_19.png b/portraits/stormer_female_19.png new file mode 100644 index 0000000..04d9660 Binary files /dev/null and b/portraits/stormer_female_19.png differ diff --git a/portraits/stormer_female_20.png b/portraits/stormer_female_20.png new file mode 100644 index 0000000..de59869 Binary files /dev/null and b/portraits/stormer_female_20.png differ diff --git a/portraits/stormer_male_01.png b/portraits/stormer_male_01.png new file mode 100644 index 0000000..abcf345 Binary files /dev/null and b/portraits/stormer_male_01.png differ diff --git a/portraits/stormer_male_02.png b/portraits/stormer_male_02.png new file mode 100644 index 0000000..65d1f13 Binary files /dev/null and b/portraits/stormer_male_02.png differ diff --git a/portraits/stormer_male_03.png b/portraits/stormer_male_03.png new file mode 100644 index 0000000..c51ee2e Binary files /dev/null and b/portraits/stormer_male_03.png differ diff --git a/portraits/stormer_male_04.png b/portraits/stormer_male_04.png new file mode 100644 index 0000000..d1bdf3c Binary files /dev/null and b/portraits/stormer_male_04.png differ diff --git a/portraits/stormer_male_05.png b/portraits/stormer_male_05.png new file mode 100644 index 0000000..e2a7da7 Binary files /dev/null and b/portraits/stormer_male_05.png differ diff --git a/portraits/stormer_male_06.png b/portraits/stormer_male_06.png new file mode 100644 index 0000000..235c8f3 Binary files /dev/null and b/portraits/stormer_male_06.png differ diff --git a/portraits/stormer_male_07.png b/portraits/stormer_male_07.png new file mode 100644 index 0000000..6154740 Binary files /dev/null and b/portraits/stormer_male_07.png differ diff --git a/portraits/stormer_male_08.png b/portraits/stormer_male_08.png new file mode 100644 index 0000000..f778423 Binary files /dev/null and b/portraits/stormer_male_08.png differ diff --git a/portraits/stormer_male_09.png b/portraits/stormer_male_09.png new file mode 100644 index 0000000..fc2181e Binary files /dev/null and b/portraits/stormer_male_09.png differ diff --git a/portraits/stormer_male_10.png b/portraits/stormer_male_10.png new file mode 100644 index 0000000..33fd0fd Binary files /dev/null and b/portraits/stormer_male_10.png differ diff --git a/portraits/stormer_male_11.png b/portraits/stormer_male_11.png new file mode 100644 index 0000000..c34c8e8 Binary files /dev/null and b/portraits/stormer_male_11.png differ diff --git a/portraits/stormer_male_12.png b/portraits/stormer_male_12.png new file mode 100644 index 0000000..da1f62f Binary files /dev/null and b/portraits/stormer_male_12.png differ diff --git a/portraits/stormer_male_13.png b/portraits/stormer_male_13.png new file mode 100644 index 0000000..1666199 Binary files /dev/null and b/portraits/stormer_male_13.png differ diff --git a/portraits/stormer_male_14.png b/portraits/stormer_male_14.png new file mode 100644 index 0000000..855f254 Binary files /dev/null and b/portraits/stormer_male_14.png differ diff --git a/portraits/stormer_male_15.png b/portraits/stormer_male_15.png new file mode 100644 index 0000000..ce4a20b Binary files /dev/null and b/portraits/stormer_male_15.png differ diff --git a/portraits/stormer_male_16.png b/portraits/stormer_male_16.png new file mode 100644 index 0000000..7f2f233 Binary files /dev/null and b/portraits/stormer_male_16.png differ diff --git a/portraits/stormer_male_17.png b/portraits/stormer_male_17.png new file mode 100644 index 0000000..606e7b1 Binary files /dev/null and b/portraits/stormer_male_17.png differ diff --git a/portraits/stormer_male_18.png b/portraits/stormer_male_18.png new file mode 100644 index 0000000..16d6c47 Binary files /dev/null and b/portraits/stormer_male_18.png differ diff --git a/portraits/waister_female_01.png b/portraits/waister_female_01.png new file mode 100644 index 0000000..262bebd Binary files /dev/null and b/portraits/waister_female_01.png differ diff --git a/portraits/waister_female_02.png b/portraits/waister_female_02.png new file mode 100644 index 0000000..a2f84da Binary files /dev/null and b/portraits/waister_female_02.png differ diff --git a/portraits/waister_female_03.png b/portraits/waister_female_03.png new file mode 100644 index 0000000..a3628d2 Binary files /dev/null and b/portraits/waister_female_03.png differ diff --git a/portraits/waister_female_04.png b/portraits/waister_female_04.png new file mode 100644 index 0000000..7823e19 Binary files /dev/null and b/portraits/waister_female_04.png differ diff --git a/portraits/waister_female_05.png b/portraits/waister_female_05.png new file mode 100644 index 0000000..231c398 Binary files /dev/null and b/portraits/waister_female_05.png differ diff --git a/portraits/waister_female_06.png b/portraits/waister_female_06.png new file mode 100644 index 0000000..ee94e75 Binary files /dev/null and b/portraits/waister_female_06.png differ diff --git a/portraits/waister_female_07.png b/portraits/waister_female_07.png new file mode 100644 index 0000000..da46da6 Binary files /dev/null and b/portraits/waister_female_07.png differ diff --git a/portraits/waister_female_08.png b/portraits/waister_female_08.png new file mode 100644 index 0000000..ec8e75f Binary files /dev/null and b/portraits/waister_female_08.png differ diff --git a/portraits/waister_female_09.png b/portraits/waister_female_09.png new file mode 100644 index 0000000..6aa9fb3 Binary files /dev/null and b/portraits/waister_female_09.png differ diff --git a/portraits/waister_female_10.png b/portraits/waister_female_10.png new file mode 100644 index 0000000..f49461b Binary files /dev/null and b/portraits/waister_female_10.png differ diff --git a/portraits/waister_female_11.png b/portraits/waister_female_11.png new file mode 100644 index 0000000..150ba2e Binary files /dev/null and b/portraits/waister_female_11.png differ diff --git a/portraits/waister_female_12.png b/portraits/waister_female_12.png new file mode 100644 index 0000000..6988be2 Binary files /dev/null and b/portraits/waister_female_12.png differ diff --git a/portraits/waister_female_13.png b/portraits/waister_female_13.png new file mode 100644 index 0000000..d434b66 Binary files /dev/null and b/portraits/waister_female_13.png differ diff --git a/portraits/waister_female_14.png b/portraits/waister_female_14.png new file mode 100644 index 0000000..c2e3cec Binary files /dev/null and b/portraits/waister_female_14.png differ diff --git a/portraits/waister_female_15.png b/portraits/waister_female_15.png new file mode 100644 index 0000000..7e56803 Binary files /dev/null and b/portraits/waister_female_15.png differ diff --git a/portraits/waister_female_16.png b/portraits/waister_female_16.png new file mode 100644 index 0000000..cb5648d Binary files /dev/null and b/portraits/waister_female_16.png differ diff --git a/portraits/waister_female_17.png b/portraits/waister_female_17.png new file mode 100644 index 0000000..0f6cce2 Binary files /dev/null and b/portraits/waister_female_17.png differ diff --git a/portraits/waister_female_18.png b/portraits/waister_female_18.png new file mode 100644 index 0000000..ba4a9d3 Binary files /dev/null and b/portraits/waister_female_18.png differ diff --git a/portraits/waister_female_19.png b/portraits/waister_female_19.png new file mode 100644 index 0000000..7815972 Binary files /dev/null and b/portraits/waister_female_19.png differ diff --git a/portraits/waister_female_20.png b/portraits/waister_female_20.png new file mode 100644 index 0000000..4c9cb43 Binary files /dev/null and b/portraits/waister_female_20.png differ diff --git a/portraits/waister_male_01.png b/portraits/waister_male_01.png new file mode 100644 index 0000000..b046099 Binary files /dev/null and b/portraits/waister_male_01.png differ diff --git a/portraits/waister_male_02.png b/portraits/waister_male_02.png new file mode 100644 index 0000000..470d99d Binary files /dev/null and b/portraits/waister_male_02.png differ diff --git a/portraits/waister_male_03.png b/portraits/waister_male_03.png new file mode 100644 index 0000000..c25e096 Binary files /dev/null and b/portraits/waister_male_03.png differ diff --git a/portraits/waister_male_04.png b/portraits/waister_male_04.png new file mode 100644 index 0000000..b1462fc Binary files /dev/null and b/portraits/waister_male_04.png differ diff --git a/portraits/waister_male_05.png b/portraits/waister_male_05.png new file mode 100644 index 0000000..c19d492 Binary files /dev/null and b/portraits/waister_male_05.png differ diff --git a/portraits/waister_male_06.png b/portraits/waister_male_06.png new file mode 100644 index 0000000..6f45e70 Binary files /dev/null and b/portraits/waister_male_06.png differ diff --git a/portraits/waister_male_07.png b/portraits/waister_male_07.png new file mode 100644 index 0000000..f09ee5a Binary files /dev/null and b/portraits/waister_male_07.png differ diff --git a/portraits/waister_male_08.png b/portraits/waister_male_08.png new file mode 100644 index 0000000..e3fe22c Binary files /dev/null and b/portraits/waister_male_08.png differ diff --git a/portraits/waister_male_09.png b/portraits/waister_male_09.png new file mode 100644 index 0000000..0db8077 Binary files /dev/null and b/portraits/waister_male_09.png differ diff --git a/portraits/waister_male_10.png b/portraits/waister_male_10.png new file mode 100644 index 0000000..3741843 Binary files /dev/null and b/portraits/waister_male_10.png differ diff --git a/portraits/waister_male_11.png b/portraits/waister_male_11.png new file mode 100644 index 0000000..a59b177 Binary files /dev/null and b/portraits/waister_male_11.png differ diff --git a/portraits/waister_male_12.png b/portraits/waister_male_12.png new file mode 100644 index 0000000..6da1e60 Binary files /dev/null and b/portraits/waister_male_12.png differ diff --git a/portraits/waister_male_13.png b/portraits/waister_male_13.png new file mode 100644 index 0000000..dcf7fbc Binary files /dev/null and b/portraits/waister_male_13.png differ diff --git a/portraits/waister_male_14.png b/portraits/waister_male_14.png new file mode 100644 index 0000000..289b2a4 Binary files /dev/null and b/portraits/waister_male_14.png differ diff --git a/portraits/waister_male_15.png b/portraits/waister_male_15.png new file mode 100644 index 0000000..ca29a49 Binary files /dev/null and b/portraits/waister_male_15.png differ diff --git a/portraits/waister_male_16.png b/portraits/waister_male_16.png new file mode 100644 index 0000000..d10267e Binary files /dev/null and b/portraits/waister_male_16.png differ diff --git a/portraits/waister_male_17.png b/portraits/waister_male_17.png new file mode 100644 index 0000000..a909775 Binary files /dev/null and b/portraits/waister_male_17.png differ diff --git a/portraits/waister_male_18.png b/portraits/waister_male_18.png new file mode 100644 index 0000000..3504474 Binary files /dev/null and b/portraits/waister_male_18.png differ diff --git a/portraits/wraith_female_01.png b/portraits/wraith_female_01.png new file mode 100644 index 0000000..57fb89f Binary files /dev/null and b/portraits/wraith_female_01.png differ diff --git a/portraits/wraith_female_02.png b/portraits/wraith_female_02.png new file mode 100644 index 0000000..c417d95 Binary files /dev/null and b/portraits/wraith_female_02.png differ diff --git a/portraits/wraith_female_03.png b/portraits/wraith_female_03.png new file mode 100644 index 0000000..59089a7 Binary files /dev/null and b/portraits/wraith_female_03.png differ diff --git a/portraits/wraith_female_04.png b/portraits/wraith_female_04.png new file mode 100644 index 0000000..159ca55 Binary files /dev/null and b/portraits/wraith_female_04.png differ diff --git a/portraits/wraith_female_05.png b/portraits/wraith_female_05.png new file mode 100644 index 0000000..0d6a7fe Binary files /dev/null and b/portraits/wraith_female_05.png differ diff --git a/portraits/wraith_female_06.png b/portraits/wraith_female_06.png new file mode 100644 index 0000000..5892546 Binary files /dev/null and b/portraits/wraith_female_06.png differ diff --git a/portraits/wraith_female_07.png b/portraits/wraith_female_07.png new file mode 100644 index 0000000..70e36f0 Binary files /dev/null and b/portraits/wraith_female_07.png differ diff --git a/portraits/wraith_female_08.png b/portraits/wraith_female_08.png new file mode 100644 index 0000000..14e7fef Binary files /dev/null and b/portraits/wraith_female_08.png differ diff --git a/portraits/wraith_female_09.png b/portraits/wraith_female_09.png new file mode 100644 index 0000000..c8a7d7c Binary files /dev/null and b/portraits/wraith_female_09.png differ diff --git a/portraits/wraith_female_10.png b/portraits/wraith_female_10.png new file mode 100644 index 0000000..5c79829 Binary files /dev/null and b/portraits/wraith_female_10.png differ diff --git a/portraits/wraith_female_11.png b/portraits/wraith_female_11.png new file mode 100644 index 0000000..556acbe Binary files /dev/null and b/portraits/wraith_female_11.png differ diff --git a/portraits/wraith_female_12.png b/portraits/wraith_female_12.png new file mode 100644 index 0000000..c764d5f Binary files /dev/null and b/portraits/wraith_female_12.png differ diff --git a/portraits/wraith_female_13.png b/portraits/wraith_female_13.png new file mode 100644 index 0000000..cf7bf7e Binary files /dev/null and b/portraits/wraith_female_13.png differ diff --git a/portraits/wraith_female_14.png b/portraits/wraith_female_14.png new file mode 100644 index 0000000..766d830 Binary files /dev/null and b/portraits/wraith_female_14.png differ diff --git a/portraits/wraith_female_15.png b/portraits/wraith_female_15.png new file mode 100644 index 0000000..005078f Binary files /dev/null and b/portraits/wraith_female_15.png differ diff --git a/portraits/wraith_female_16.png b/portraits/wraith_female_16.png new file mode 100644 index 0000000..7350411 Binary files /dev/null and b/portraits/wraith_female_16.png differ diff --git a/portraits/wraith_female_17.png b/portraits/wraith_female_17.png new file mode 100644 index 0000000..28b55c1 Binary files /dev/null and b/portraits/wraith_female_17.png differ diff --git a/portraits/wraith_female_18.png b/portraits/wraith_female_18.png new file mode 100644 index 0000000..2216ef6 Binary files /dev/null and b/portraits/wraith_female_18.png differ diff --git a/portraits/wraith_female_19.png b/portraits/wraith_female_19.png new file mode 100644 index 0000000..3b7c055 Binary files /dev/null and b/portraits/wraith_female_19.png differ diff --git a/portraits/wraith_female_20.png b/portraits/wraith_female_20.png new file mode 100644 index 0000000..74a6b71 Binary files /dev/null and b/portraits/wraith_female_20.png differ diff --git a/portraits/wraith_female_21.png b/portraits/wraith_female_21.png new file mode 100644 index 0000000..0a9336e Binary files /dev/null and b/portraits/wraith_female_21.png differ diff --git a/portraits/wraith_female_22.png b/portraits/wraith_female_22.png new file mode 100644 index 0000000..6812ece Binary files /dev/null and b/portraits/wraith_female_22.png differ diff --git a/portraits/wraith_female_23.png b/portraits/wraith_female_23.png new file mode 100644 index 0000000..063aed2 Binary files /dev/null and b/portraits/wraith_female_23.png differ diff --git a/portraits/wraith_female_24.png b/portraits/wraith_female_24.png new file mode 100644 index 0000000..e1a4875 Binary files /dev/null and b/portraits/wraith_female_24.png differ diff --git a/portraits/wraith_female_25.png b/portraits/wraith_female_25.png new file mode 100644 index 0000000..68cb283 Binary files /dev/null and b/portraits/wraith_female_25.png differ diff --git a/portraits/wraith_female_26.png b/portraits/wraith_female_26.png new file mode 100644 index 0000000..a273218 Binary files /dev/null and b/portraits/wraith_female_26.png differ diff --git a/portraits/wraith_female_27.png b/portraits/wraith_female_27.png new file mode 100644 index 0000000..f0f7af5 Binary files /dev/null and b/portraits/wraith_female_27.png differ diff --git a/portraits/wraith_female_28.png b/portraits/wraith_female_28.png new file mode 100644 index 0000000..79f421f Binary files /dev/null and b/portraits/wraith_female_28.png differ diff --git a/portraits/wraith_female_29.png b/portraits/wraith_female_29.png new file mode 100644 index 0000000..33dff08 Binary files /dev/null and b/portraits/wraith_female_29.png differ diff --git a/portraits/wraith_female_30.png b/portraits/wraith_female_30.png new file mode 100644 index 0000000..550050d Binary files /dev/null and b/portraits/wraith_female_30.png differ diff --git a/portraits/wraith_female_31.png b/portraits/wraith_female_31.png new file mode 100644 index 0000000..e3305e0 Binary files /dev/null and b/portraits/wraith_female_31.png differ diff --git a/portraits/wraith_female_32.png b/portraits/wraith_female_32.png new file mode 100644 index 0000000..cadfd83 Binary files /dev/null and b/portraits/wraith_female_32.png differ diff --git a/portraits/wraith_female_33.png b/portraits/wraith_female_33.png new file mode 100644 index 0000000..de70141 Binary files /dev/null and b/portraits/wraith_female_33.png differ diff --git a/portraits/wraith_female_34.png b/portraits/wraith_female_34.png new file mode 100644 index 0000000..bcc4552 Binary files /dev/null and b/portraits/wraith_female_34.png differ diff --git a/portraits/wraith_female_35.png b/portraits/wraith_female_35.png new file mode 100644 index 0000000..f543b51 Binary files /dev/null and b/portraits/wraith_female_35.png differ diff --git a/portraits/wraith_female_36.png b/portraits/wraith_female_36.png new file mode 100644 index 0000000..94effca Binary files /dev/null and b/portraits/wraith_female_36.png differ diff --git a/portraits/wraith_female_37.png b/portraits/wraith_female_37.png new file mode 100644 index 0000000..df9f45b Binary files /dev/null and b/portraits/wraith_female_37.png differ diff --git a/portraits/wraith_female_38.png b/portraits/wraith_female_38.png new file mode 100644 index 0000000..4f6c2f5 Binary files /dev/null and b/portraits/wraith_female_38.png differ diff --git a/portraits/wraith_female_39.png b/portraits/wraith_female_39.png new file mode 100644 index 0000000..6d1bf22 Binary files /dev/null and b/portraits/wraith_female_39.png differ diff --git a/portraits/wraith_female_40.png b/portraits/wraith_female_40.png new file mode 100644 index 0000000..ce7c26c Binary files /dev/null and b/portraits/wraith_female_40.png differ diff --git a/portraits/wraith_male_01.png b/portraits/wraith_male_01.png new file mode 100644 index 0000000..aa797ef Binary files /dev/null and b/portraits/wraith_male_01.png differ diff --git a/portraits/wraith_male_02.png b/portraits/wraith_male_02.png new file mode 100644 index 0000000..914fa6a Binary files /dev/null and b/portraits/wraith_male_02.png differ diff --git a/portraits/wraith_male_03.png b/portraits/wraith_male_03.png new file mode 100644 index 0000000..6abf6d4 Binary files /dev/null and b/portraits/wraith_male_03.png differ diff --git a/portraits/wraith_male_04.png b/portraits/wraith_male_04.png new file mode 100644 index 0000000..e91050d Binary files /dev/null and b/portraits/wraith_male_04.png differ diff --git a/portraits/wraith_male_05.png b/portraits/wraith_male_05.png new file mode 100644 index 0000000..123d14a Binary files /dev/null and b/portraits/wraith_male_05.png differ diff --git a/portraits/wraith_male_06.png b/portraits/wraith_male_06.png new file mode 100644 index 0000000..e3fe8cc Binary files /dev/null and b/portraits/wraith_male_06.png differ diff --git a/portraits/wraith_male_07.png b/portraits/wraith_male_07.png new file mode 100644 index 0000000..3ebea00 Binary files /dev/null and b/portraits/wraith_male_07.png differ diff --git a/portraits/wraith_male_08.png b/portraits/wraith_male_08.png new file mode 100644 index 0000000..b2e8444 Binary files /dev/null and b/portraits/wraith_male_08.png differ diff --git a/portraits/wraith_male_09.png b/portraits/wraith_male_09.png new file mode 100644 index 0000000..29a9dc7 Binary files /dev/null and b/portraits/wraith_male_09.png differ diff --git a/portraits/wraith_male_10.png b/portraits/wraith_male_10.png new file mode 100644 index 0000000..82ccc17 Binary files /dev/null and b/portraits/wraith_male_10.png differ diff --git a/portraits/wraith_male_11.png b/portraits/wraith_male_11.png new file mode 100644 index 0000000..63ca31f Binary files /dev/null and b/portraits/wraith_male_11.png differ diff --git a/portraits/wraith_male_14.png b/portraits/wraith_male_14.png new file mode 100644 index 0000000..485f521 Binary files /dev/null and b/portraits/wraith_male_14.png differ diff --git a/portraits/wraith_male_21.png b/portraits/wraith_male_21.png new file mode 100644 index 0000000..b92d2db Binary files /dev/null and b/portraits/wraith_male_21.png differ diff --git a/portraits/wraith_male_24.png b/portraits/wraith_male_24.png new file mode 100644 index 0000000..3208f5f Binary files /dev/null and b/portraits/wraith_male_24.png differ diff --git a/portraits/wraith_male_25.png b/portraits/wraith_male_25.png new file mode 100644 index 0000000..5b379dd Binary files /dev/null and b/portraits/wraith_male_25.png differ diff --git a/portraits/wraith_male_26.png b/portraits/wraith_male_26.png new file mode 100644 index 0000000..2c1839e Binary files /dev/null and b/portraits/wraith_male_26.png differ diff --git a/portraits/wraith_male_27.png b/portraits/wraith_male_27.png new file mode 100644 index 0000000..4cf60b1 Binary files /dev/null and b/portraits/wraith_male_27.png differ diff --git a/portraits/wraith_male_28.png b/portraits/wraith_male_28.png new file mode 100644 index 0000000..ad14219 Binary files /dev/null and b/portraits/wraith_male_28.png differ diff --git a/portraits/wraith_male_29.png b/portraits/wraith_male_29.png new file mode 100644 index 0000000..5beb2f3 Binary files /dev/null and b/portraits/wraith_male_29.png differ diff --git a/portraits/wraith_male_30.png b/portraits/wraith_male_30.png new file mode 100644 index 0000000..ba2e724 Binary files /dev/null and b/portraits/wraith_male_30.png differ diff --git a/portraits/wraith_male_31.png b/portraits/wraith_male_31.png new file mode 100644 index 0000000..b671901 Binary files /dev/null and b/portraits/wraith_male_31.png differ diff --git a/portraits/wraith_male_32.png b/portraits/wraith_male_32.png new file mode 100644 index 0000000..7b5f88a Binary files /dev/null and b/portraits/wraith_male_32.png differ diff --git a/portraits/wraith_male_33.png b/portraits/wraith_male_33.png new file mode 100644 index 0000000..ac77968 Binary files /dev/null and b/portraits/wraith_male_33.png differ diff --git a/portraits/wraith_male_34.png b/portraits/wraith_male_34.png new file mode 100644 index 0000000..dd146fb Binary files /dev/null and b/portraits/wraith_male_34.png differ diff --git a/portraits/wraith_male_38.png b/portraits/wraith_male_38.png new file mode 100644 index 0000000..2d663ba Binary files /dev/null and b/portraits/wraith_male_38.png differ diff --git a/scripts/build-index.mjs b/scripts/build-index.mjs new file mode 100644 index 0000000..1b38f47 --- /dev/null +++ b/scripts/build-index.mjs @@ -0,0 +1,83 @@ +// build-index.mjs — Node.js utility script +// Scans portraits/ and regenerates portraits/index.json +// Run with: node scripts/build-index.mjs +// (This script is NOT loaded by Foundry — it's a dev tool only.) + +import { readdir, writeFile } from 'fs/promises'; +import { join, extname, basename, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const MODULE_DIR = join(__dirname, '..'); +const PORTRAITS = join(MODULE_DIR, 'portraits'); +const MODULE_ID = 'npc-portrait-pack'; + +// ── Category classification ──────────────────────────────────────────── +function classify(filename) { + const name = basename(filename, '.png'); + + // Named NPC: npc_NNNN_description + if (/^npc_\d+_/.test(name)) { + const parts = name.split('_'); + const tags = []; + let gender = null; + for (const p of parts.slice(2)) { + if (p === 'male' || p === 'female') { gender = p; tags.unshift(p); } + else if (p === 'elderly') { tags.push('elderly'); } + else { tags.push(p); } + } + return { category: 'named_npc', race: 'human', gender, tags }; + } + + // Race + gender: race_gender_NN + const rg = name.match(/^(human|ebon|frother|shaktar|stormer|waister|wraith)_(male|female)_\d+$/); + if (rg) { + const [, race, gender] = rg; + return { category: race, race, gender, tags: [gender] }; + } + + if (/^creature_\d+$/.test(name)) return { category: 'creature', race: 'creature', gender: null, tags: ['creature'] }; + if (/^mutant_\d+$/.test(name)) return { category: 'creature', race: 'mutant', gender: null, tags: ['mutant','creature'] }; + if (/^serial_killer_\d+$/.test(name))return { category: 'serial_killer', race: 'human', gender: null, tags: ['serial_killer','threat'] }; + if (/^portrait_\d+$/.test(name)) return { category: 'generic', race: 'human', gender: null, tags: [] }; + + return { category: 'generic', race: 'unknown', gender: null, tags: [] }; +} + +// ── Main ─────────────────────────────────────────────────────────────── +async function buildIndex() { + const files = await readdir(PORTRAITS); + const pngFiles = files.filter(f => extname(f).toLowerCase() === '.png').sort(); + + const portraits = pngFiles.map(filename => { + const id = basename(filename, '.png'); + const { category, race, gender, tags } = classify(filename); + return { + id, + path: `modules/${MODULE_ID}/portraits/${filename}`, + category, + race, + gender, + tags, + }; + }); + + portraits.sort((a, b) => a.id.localeCompare(b.id)); + + const index = { version: '1.0.0', count: portraits.length, portraits }; + const outputPath = join(PORTRAITS, 'index.json'); + await writeFile(outputPath, JSON.stringify(index, null, 2), 'utf-8'); + + // Summary + const cats = portraits.reduce((acc, p) => { + acc[p.category] = (acc[p.category] ?? 0) + 1; + return acc; + }, {}); + console.log(`✓ Built index: ${portraits.length} portraits → ${outputPath}`); + console.log('\nBy category:'); + for (const [cat, count] of Object.entries(cats).sort()) { + console.log(` ${cat.padEnd(15)} ${count}`); + } +} + +buildIndex().catch(err => { console.error('Error:', err); process.exit(1); }); diff --git a/scripts/generate-portraits.js b/scripts/generate-portraits.js new file mode 100644 index 0000000..54dad06 --- /dev/null +++ b/scripts/generate-portraits.js @@ -0,0 +1,834 @@ +// generate-portraits.js +// Procedural B&W NPC portrait generator for SLA Industries +// Run: node generate-portraits.js +// Output: PNG files + updated index.json in the npc-portrait-pack module + +'use strict'; +const { createCanvas } = require('canvas'); +const fs = require('fs'); +const path = require('path'); + +const OUT_DIR = '/sessions/elegant-peaceful-meitner/mnt/FoundryVTT/Data/modules/npc-portrait-pack/portraits'; +const MODULE_ID = 'npc-portrait-pack'; +const W = 128, H = 192; // portrait dimensions + +// ── Seeded RNG (mulberry32) ────────────────────────────────────────────────── +function mkRng(seed) { + let s = seed >>> 0; + return { + next() { + s |= 0; s = s + 0x6D2B79F5 | 0; + let t = Math.imul(s ^ s >>> 15, 1 | s); + t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; + return ((t ^ t >>> 14) >>> 0) / 4294967296; + }, + range(lo, hi) { return Math.floor(this.next() * (hi - lo + 1)) + lo; }, + pick(arr) { return arr[Math.floor(this.next() * arr.length)]; }, + chance(p) { return this.next() < p; }, + }; +} + +// ── Colour palette ─────────────────────────────────────────────────────────── +const C = { + bg: '#080808', + skin: '#c8c8c8', + skinMid: '#a8a8a8', + skinDark: '#686868', + shadow: '#303030', + black: '#0a0a0a', + white: '#f0f0f0', + hilight: '#e8e8e8', + outline: '#000000', + implant: '#4488aa', + implantL: '#88ccee', + scar: '#888888', +}; + +// ── Helpers ────────────────────────────────────────────────────────────────── +function ellipse(ctx, cx, cy, rx, ry) { + ctx.beginPath(); + ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2); +} + +function fillEllipse(ctx, cx, cy, rx, ry, color) { + ctx.fillStyle = color; + ellipse(ctx, cx, cy, rx, ry); + ctx.fill(); +} + +function strokeEllipse(ctx, cx, cy, rx, ry, color, lw = 1.5) { + ctx.strokeStyle = color; + ctx.lineWidth = lw; + ellipse(ctx, cx, cy, rx, ry); + ctx.stroke(); +} + +function line(ctx, x1, y1, x2, y2, color, lw = 1.5) { + ctx.strokeStyle = color; + ctx.lineWidth = lw; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); +} + +function arcPath(ctx, cx, cy, rx, ry, startAng, endAng) { + ctx.beginPath(); + ctx.ellipse(cx, cy, rx, ry, 0, startAng, endAng); +} + +// Gradient fill for face shading +function makeFaceGrad(ctx, cx, cy, rx, ry) { + const g = ctx.createRadialGradient(cx - rx * 0.2, cy - ry * 0.25, ry * 0.05, + cx, cy, ry * 1.1); + g.addColorStop(0, C.hilight); + g.addColorStop(0.4, C.skin); + g.addColorStop(0.75, C.skinMid); + g.addColorStop(1, C.shadow); + return g; +} + +// ── Face shape definitions ─────────────────────────────────────────────────── +// Each returns {cx, cy, rx, ry, jawW, jawY} — jaw params for chin shape +const FACE_SHAPES = [ + // 0: oval — standard + (rng) => ({ + rx: rng.range(32, 42), ry: rng.range(50, 62), + jawW: rng.range(22, 30), type: 'oval' + }), + // 1: square / blocky + (rng) => ({ + rx: rng.range(38, 48), ry: rng.range(48, 58), + jawW: rng.range(32, 42), type: 'square' + }), + // 2: gaunt / narrow + (rng) => ({ + rx: rng.range(24, 34), ry: rng.range(56, 68), + jawW: rng.range(16, 24), type: 'gaunt' + }), + // 3: round + (rng) => ({ + rx: rng.range(36, 46), ry: rng.range(44, 54), + jawW: rng.range(28, 38), type: 'round' + }), + // 4: angular / alien (for ebon/wraith) + (rng) => ({ + rx: rng.range(26, 36), ry: rng.range(60, 74), + jawW: rng.range(14, 22), type: 'angular' + }), +]; + +function drawFace(ctx, rng, shapeIdx, cx, cy) { + const s = FACE_SHAPES[shapeIdx](rng); + const { rx, ry, type } = s; + + // Face silhouette + ctx.save(); + + if (type === 'square') { + // Rounded rectangle face + const x = cx - rx, y = cy - ry, w = rx * 2, h = ry * 2; + ctx.beginPath(); + ctx.roundRect(x, y, w, h, [rx * 0.35, rx * 0.35, rx * 0.55, rx * 0.55]); + } else if (type === 'angular') { + // Pointed / angular face + ctx.beginPath(); + ctx.moveTo(cx - rx, cy - ry * 0.2); + ctx.lineTo(cx - rx * 0.8, cy - ry); + ctx.lineTo(cx + rx * 0.8, cy - ry); + ctx.lineTo(cx + rx, cy - ry * 0.2); + ctx.lineTo(cx + rx * 0.5, cy + ry * 0.5); + ctx.lineTo(cx, cy + ry); + ctx.lineTo(cx - rx * 0.5, cy + ry * 0.5); + ctx.closePath(); + } else { + ellipse(ctx, cx, cy - ry * 0.05, rx, ry); + } + + // Fill with gradient + ctx.fillStyle = makeFaceGrad(ctx, cx, cy, rx, ry); + ctx.fill(); + ctx.strokeStyle = C.outline; + ctx.lineWidth = 1.8; + ctx.stroke(); + + ctx.restore(); + + return { rx, ry, type }; +} + +// ── Hair ───────────────────────────────────────────────────────────────────── +const HAIR_STYLES = [ + // 0: bald/shaved — just a slight shadow at hairline + (ctx, rng, cx, cy, rx, ry) => { + ctx.fillStyle = C.shadow; + ctx.globalAlpha = 0.25; + ctx.beginPath(); + ctx.ellipse(cx, cy - ry * 0.7, rx * 1.05, ry * 0.35, 0, Math.PI, 0); + ctx.fill(); + ctx.globalAlpha = 1; + }, + // 1: short crop + (ctx, rng, cx, cy, rx, ry) => { + const h = rng.range(10, 18); + ctx.fillStyle = C.black; + ctx.beginPath(); + ctx.ellipse(cx, cy - ry + h * 0.4, rx * 1.08, h, 0, Math.PI, 0); + ctx.fill(); + ctx.strokeStyle = C.outline; ctx.lineWidth = 1.5; + ctx.stroke(); + }, + // 2: mohawk + (ctx, rng, cx, cy, rx, ry) => { + const mw = rng.range(7, 14); + const mh = rng.range(22, 40); + ctx.fillStyle = C.black; + ctx.beginPath(); + ctx.moveTo(cx - mw * 0.5, cy - ry + 3); + ctx.lineTo(cx - mw * 0.8, cy - ry - mh * 0.6); + ctx.lineTo(cx, cy - ry - mh); + ctx.lineTo(cx + mw * 0.8, cy - ry - mh * 0.6); + ctx.lineTo(cx + mw * 0.5, cy - ry + 3); + ctx.closePath(); + ctx.fill(); + // shaved sides — shadow + ctx.fillStyle = C.shadow; + ctx.globalAlpha = 0.4; + ctx.beginPath(); + ctx.ellipse(cx - rx * 0.65, cy - ry * 0.4, rx * 0.38, ry * 0.3, 0, 0, Math.PI * 2); + ctx.fill(); + ctx.beginPath(); + ctx.ellipse(cx + rx * 0.65, cy - ry * 0.4, rx * 0.38, ry * 0.3, 0, 0, Math.PI * 2); + ctx.fill(); + ctx.globalAlpha = 1; + }, + // 3: undercut / shaved sides + top + (ctx, rng, cx, cy, rx, ry) => { + const tw = rng.range(rx * 0.45, rx * 0.75); + const th = rng.range(12, 22); + ctx.fillStyle = C.black; + ctx.beginPath(); + ctx.ellipse(cx, cy - ry + th * 0.3, tw, th, 0, Math.PI, 0); + ctx.fill(); + ctx.fillStyle = C.shadow; + ctx.globalAlpha = 0.35; + ctx.beginPath(); + ctx.ellipse(cx - rx * 0.7, cy - ry * 0.35, rx * 0.35, ry * 0.32, 0, 0, Math.PI * 2); + ctx.fill(); + ctx.beginPath(); + ctx.ellipse(cx + rx * 0.7, cy - ry * 0.35, rx * 0.35, ry * 0.32, 0, 0, Math.PI * 2); + ctx.fill(); + ctx.globalAlpha = 1; + }, + // 4: long hair (frames face, extends below ears) + (ctx, rng, cx, cy, rx, ry) => { + ctx.fillStyle = C.black; + // Top bulk + ctx.beginPath(); + ctx.ellipse(cx, cy - ry * 0.85, rx * 1.12, ry * 0.3, 0, Math.PI, 0); + ctx.fill(); + // Side curtains + const lx = cx - rx * 1.1, rx2 = cx + rx * 1.1; + const topY = cy - ry * 0.55, botY = cy + ry * 0.6; + ctx.beginPath(); + ctx.moveTo(lx + 4, topY); + ctx.lineTo(lx - 4, topY + (botY - topY) * 0.4); + ctx.lineTo(lx + 2, botY); + ctx.lineTo(lx + 14, botY - 6); + ctx.lineTo(lx + 12, topY); + ctx.closePath(); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(rx2 - 4, topY); + ctx.lineTo(rx2 + 4, topY + (botY - topY) * 0.4); + ctx.lineTo(rx2 - 2, botY); + ctx.lineTo(rx2 - 14, botY - 6); + ctx.lineTo(rx2 - 12, topY); + ctx.closePath(); + ctx.fill(); + }, + // 5: hood + (ctx, rng, cx, cy, rx, ry) => { + const pad = rng.range(8, 18); + ctx.fillStyle = C.black; + ctx.beginPath(); + ctx.ellipse(cx, cy - ry * 0.5, rx + pad, ry * 0.65 + pad, 0, Math.PI, 0); + ctx.fill(); + // inner hood shadow ring + ctx.strokeStyle = C.shadow; + ctx.lineWidth = 3; + ctx.globalAlpha = 0.6; + ctx.beginPath(); + ctx.ellipse(cx, cy - ry * 0.3, rx + pad * 0.3, ry * 0.55, 0, Math.PI, 0); + ctx.stroke(); + ctx.globalAlpha = 1; + }, + // 6: buzz + beard shadow + (ctx, rng, cx, cy, rx, ry) => { + ctx.fillStyle = C.black; + ctx.beginPath(); + ctx.ellipse(cx, cy - ry + 6, rx * 1.05, 12, 0, Math.PI, 0); + ctx.fill(); + // stubble on jaw + ctx.fillStyle = C.shadow; + ctx.globalAlpha = 0.5; + ctx.beginPath(); + ctx.ellipse(cx, cy + ry * 0.55, rx * 0.75, ry * 0.22, 0, 0, Math.PI); + ctx.fill(); + ctx.globalAlpha = 1; + }, + // 7: topknot + (ctx, rng, cx, cy, rx, ry) => { + const tw = rng.range(8, 15); + const th = rng.range(16, 28); + ctx.fillStyle = C.black; + ctx.beginPath(); + ctx.ellipse(cx, cy - ry - th * 0.3, tw, th * 0.5, 0, 0, Math.PI * 2); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(cx - tw, cy - ry - th * 0.3); + ctx.lineTo(cx - tw * 0.6, cy - ry + 4); + ctx.lineTo(cx + tw * 0.6, cy - ry + 4); + ctx.lineTo(cx + tw, cy - ry - th * 0.3); + ctx.closePath(); + ctx.fill(); + // shaved sides + ctx.fillStyle = C.shadow; + ctx.globalAlpha = 0.35; + ctx.beginPath(); + ctx.ellipse(cx - rx * 0.65, cy - ry * 0.4, rx * 0.38, ry * 0.28, 0, 0, Math.PI * 2); + ctx.fill(); + ctx.beginPath(); + ctx.ellipse(cx + rx * 0.65, cy - ry * 0.4, rx * 0.38, ry * 0.28, 0, 0, Math.PI * 2); + ctx.fill(); + ctx.globalAlpha = 1; + }, +]; + +// ── Eyebrows ───────────────────────────────────────────────────────────────── +function drawEyebrows(ctx, rng, cx, eyeY, spread, variant) { + const bY = eyeY - rng.range(7, 12); + const bW = rng.range(10, 16); + const lx = cx - spread, rx = cx + spread; + ctx.strokeStyle = C.black; + ctx.lineCap = 'round'; + + if (variant === 'angry') { + ctx.lineWidth = 2.5; + line(ctx, lx - bW, bY - 3, lx + bW, bY + 2, C.black, 2.5); + line(ctx, rx - bW, bY + 2, rx + bW, bY - 3, C.black, 2.5); + } else if (variant === 'raised') { + ctx.lineWidth = 2; + arcPath(ctx, lx, bY + 5, bW, 6, Math.PI * 1.15, Math.PI * 1.85); + ctx.stroke(); + arcPath(ctx, rx, bY + 5, bW, 6, Math.PI * 1.15, Math.PI * 1.85); + ctx.stroke(); + } else if (variant === 'flat') { + line(ctx, lx - bW, bY, lx + bW, bY, C.black, 2.5); + line(ctx, rx - bW, bY, rx + bW, bY, C.black, 2.5); + } else { + // default arch + ctx.lineWidth = 2.2; + arcPath(ctx, lx, bY + 4, bW * 0.9, 5, Math.PI * 1.1, Math.PI * 1.9); + ctx.stroke(); + arcPath(ctx, rx, bY + 4, bW * 0.9, 5, Math.PI * 1.1, Math.PI * 1.9); + ctx.stroke(); + } +} + +// ── Eyes ───────────────────────────────────────────────────────────────────── +function drawEyes(ctx, rng, cx, eyeY, spread, eyeType) { + const lx = cx - spread, rx = cx + spread; + const ew = rng.range(9, 13), eh = rng.range(5, 8); + + ctx.save(); + + if (eyeType === 'normal' || eyeType === 'intense') { + const ehMod = eyeType === 'intense' ? eh + 2 : eh; + for (const ex of [lx, rx]) { + // whites + fillEllipse(ctx, ex, eyeY, ew, ehMod, C.white); + // iris + fillEllipse(ctx, ex, eyeY, ew * 0.55, ehMod * 0.85, C.black); + // catchlight + fillEllipse(ctx, ex + ew * 0.18, eyeY - ehMod * 0.22, ew * 0.14, ehMod * 0.2, C.white); + // outline + strokeEllipse(ctx, ex, eyeY, ew, ehMod, C.outline, 1.2); + } + } else if (eyeType === 'narrow') { + for (const ex of [lx, rx]) { + fillEllipse(ctx, ex, eyeY, ew * 1.1, eh * 0.45, C.white); + fillEllipse(ctx, ex, eyeY, ew * 0.5, eh * 0.35, C.black); + strokeEllipse(ctx, ex, eyeY, ew * 1.1, eh * 0.45, C.outline, 1.2); + } + } else if (eyeType === 'cyborg_one') { + // Left = cybernetic, right = normal + drawCyborgEye(ctx, rng, lx, eyeY, ew); + fillEllipse(ctx, rx, eyeY, ew, eh, C.white); + fillEllipse(ctx, rx, eyeY, ew * 0.55, eh * 0.85, C.black); + fillEllipse(ctx, rx + ew * 0.18, eyeY - eh * 0.22, ew * 0.14, eh * 0.2, C.white); + strokeEllipse(ctx, rx, eyeY, ew, eh, C.outline, 1.2); + } else if (eyeType === 'cyborg_both') { + drawCyborgEye(ctx, rng, lx, eyeY, ew); + drawCyborgEye(ctx, rng, rx, eyeY, ew); + } else if (eyeType === 'visor') { + // Visor bar across both eyes + const vx = cx - spread - ew - 4, vy = eyeY - eh - 2; + const vw = spread * 2 + ew * 2 + 8, vh = eh * 2 + 4; + ctx.fillStyle = C.shadow; + ctx.fillRect(vx, vy, vw, vh); + // visor sheen + ctx.fillStyle = C.implantL; + ctx.globalAlpha = 0.35; + ctx.fillRect(vx, vy, vw, vh * 0.35); + ctx.globalAlpha = 1; + ctx.strokeStyle = C.outline; ctx.lineWidth = 1.5; + ctx.strokeRect(vx, vy, vw, vh); + line(ctx, vx + 4, eyeY, vx + vw - 4, eyeY, C.implant, 1); + } else if (eyeType === 'closed') { + for (const ex of [lx, rx]) { + line(ctx, ex - ew, eyeY, ex + ew, eyeY, C.black, 2); + } + } + + ctx.restore(); +} + +function drawCyborgEye(ctx, rng, ex, ey, ew) { + const r = ew * 0.9; + fillEllipse(ctx, ex, ey, r, r, C.black); + strokeEllipse(ctx, ex, ey, r, r, C.implant, 1.5); + // crosshair + ctx.strokeStyle = C.implantL; ctx.lineWidth = 0.8; + line(ctx, ex - r, ey, ex + r, ey, C.implantL, 0.8); + line(ctx, ex, ey - r, ex, ey + r, C.implantL, 0.8); + // inner ring + strokeEllipse(ctx, ex, ey, r * 0.45, r * 0.45, C.implant, 0.8); + // pupil dot + fillEllipse(ctx, ex, ey, r * 0.18, r * 0.18, C.implantL); +} + +// ── Nose ───────────────────────────────────────────────────────────────────── +function drawNose(ctx, rng, cx, noseY, rx, noseType) { + ctx.strokeStyle = C.skinDark; ctx.lineCap = 'round'; + const nw = rng.range(5, 10); + const nb = noseY - rng.range(10, 18); // bridge top + + if (noseType === 'straight') { + line(ctx, cx, nb, cx, noseY, C.skinDark, 1.2); + line(ctx, cx - nw, noseY + 2, cx, noseY, C.skinDark, 1.4); + line(ctx, cx + nw, noseY + 2, cx, noseY, C.skinDark, 1.4); + } else if (noseType === 'broad') { + line(ctx, cx, nb, cx, noseY - 4, C.skinDark, 1.2); + arcPath(ctx, cx, noseY - 2, nw * 1.3, 5, Math.PI, 0); + ctx.stroke(); + } else if (noseType === 'hooked') { + ctx.beginPath(); + ctx.moveTo(cx + 2, nb); + ctx.quadraticCurveTo(cx + 4, (nb + noseY) * 0.5, cx + nw * 0.3, noseY); + ctx.strokeStyle = C.skinDark; ctx.lineWidth = 1.3; + ctx.stroke(); + line(ctx, cx - nw * 0.7, noseY + 1, cx + nw * 0.3, noseY, C.skinDark, 1.3); + } else { + // button + fillEllipse(ctx, cx, noseY, nw * 0.8, 3.5, C.skinDark); + line(ctx, cx - nw * 0.5, noseY - 2, cx, noseY - 6, C.skinDark, 1); + line(ctx, cx + nw * 0.5, noseY - 2, cx, noseY - 6, C.skinDark, 1); + } +} + +// ── Mouth ───────────────────────────────────────────────────────────────────── +function drawMouth(ctx, rng, cx, mouthY, rx, mouthType, hasMask) { + if (hasMask) return; // covered + const mw = rng.range(10, 17); + ctx.lineCap = 'round'; + + if (mouthType === 'thin') { + line(ctx, cx - mw, mouthY, cx + mw, mouthY, C.black, 1.8); + } else if (mouthType === 'frown') { + ctx.strokeStyle = C.black; ctx.lineWidth = 1.8; + arcPath(ctx, cx, mouthY - 4, mw, 5, 0.15, Math.PI - 0.15); + ctx.stroke(); + } else if (mouthType === 'grimace') { + line(ctx, cx - mw, mouthY, cx + mw, mouthY, C.black, 2.2); + // teeth line + ctx.strokeStyle = C.shadow; ctx.lineWidth = 1; + for (let i = -mw + 3; i < mw - 3; i += 5) { + line(ctx, cx + i, mouthY, cx + i, mouthY + 3, C.shadow, 0.8); + } + ctx.fillStyle = C.shadow; + ctx.globalAlpha = 0.6; + ctx.beginPath(); + ctx.rect(cx - mw, mouthY, mw * 2, 4); + ctx.fill(); + ctx.globalAlpha = 1; + } else if (mouthType === 'sneer') { + ctx.strokeStyle = C.black; ctx.lineWidth = 1.8; + arcPath(ctx, cx + mw * 0.3, mouthY + 5, mw * 0.8, 5, Math.PI + 0.2, Math.PI * 2 - 0.1); + ctx.stroke(); + } else { + // slight curve + ctx.strokeStyle = C.black; ctx.lineWidth = 1.8; + ctx.beginPath(); + ctx.moveTo(cx - mw, mouthY); + ctx.quadraticCurveTo(cx, mouthY + rng.range(-3, 4), cx + mw, mouthY); + ctx.stroke(); + } +} + +// ── Accessories ─────────────────────────────────────────────────────────────── +function drawScar(ctx, rng, cx, cy, rx, ry) { + const type = rng.range(0, 3); + ctx.strokeStyle = C.scar; ctx.lineCap = 'round'; + if (type === 0) { + // diagonal scar across cheek + const sx = cx + (rng.chance(0.5) ? 1 : -1) * rng.range(5, 18); + const sy = cy - rng.range(5, 15); + const ang = rng.range(-40, 40) * Math.PI / 180; + const len = rng.range(15, 28); + line(ctx, sx, sy, sx + Math.cos(ang) * len, sy + Math.sin(ang) * len, C.scar, 1.2); + // stitch marks + for (let i = 0.2; i < 0.85; i += 0.18) { + const px = sx + Math.cos(ang) * len * i; + const py = sy + Math.sin(ang) * len * i; + const perp = ang + Math.PI / 2; + line(ctx, px + Math.cos(perp) * 2.5, py + Math.sin(perp) * 2.5, + px - Math.cos(perp) * 2.5, py - Math.sin(perp) * 2.5, C.scar, 0.8); + } + } else if (type === 1) { + // horizontal scar across nose bridge + line(ctx, cx - rng.range(8, 18), cy, cx + rng.range(8, 18), cy, C.scar, 1.5); + } else if (type === 2) { + // three-claw marks + const sy = cy - rng.range(0, 20); + const sx = cx - rng.range(10, 25); + for (let i = 0; i < 3; i++) { + line(ctx, sx + i * 5, sy - 10, sx + i * 5 + 4, sy + 15, C.scar, 1.1); + } + } else { + // burn / patch + ctx.fillStyle = C.skinDark; + ctx.globalAlpha = 0.5; + const bx = cx + rng.range(-20, 20), by = cy + rng.range(-15, 15); + fillEllipse(ctx, bx, by, rng.range(6, 12), rng.range(4, 9), C.skinDark); + ctx.globalAlpha = 1; + } +} + +function drawImplant(ctx, rng, cx, cy, rx, ry, type) { + if (type === 'forehead') { + const ix = cx + rng.range(-8, 8); + const iy = cy - ry * 0.62; + fillEllipse(ctx, ix, iy, 5, 4, C.implant); + strokeEllipse(ctx, ix, iy, 5, 4, C.implantL, 0.8); + fillEllipse(ctx, ix, iy, 2, 1.5, C.implantL); + } else if (type === 'cheek') { + const side = rng.chance(0.5) ? 1 : -1; + const ix = cx + side * rng.range(18, 28); + const iy = cy - rng.range(0, 10); + // ridge implant + ctx.fillStyle = C.implant; + ctx.beginPath(); + ctx.roundRect(ix - 8, iy - 3, 16, 6, 3); + ctx.fill(); + for (let s = -4; s <= 4; s += 4) { + strokeEllipse(ctx, ix + s, iy, 1.5, 1.5, C.implantL, 0.7); + } + } else if (type === 'temple') { + const side = rng.chance(0.5) ? 1 : -1; + const ix = cx + side * rx * 0.85; + const iy = cy - ry * 0.35; + ctx.fillStyle = C.black; + ctx.beginPath(); + ctx.roundRect(ix - 6, iy - 5, 12, 10, 2); + ctx.fill(); + ctx.strokeStyle = C.implant; ctx.lineWidth = 1; + ctx.beginPath(); + ctx.roundRect(ix - 6, iy - 5, 12, 10, 2); + ctx.stroke(); + fillEllipse(ctx, ix, iy, 3, 3, C.implant); + fillEllipse(ctx, ix, iy, 1.2, 1.2, C.implantL); + } else { + // ear/neck — done elsewhere + const ix = cx + (rng.chance(0.5) ? 1 : -1) * rx * 1.02; + const iy = cy + rng.range(-5, 15); + ctx.fillStyle = C.implant; + ctx.beginPath(); + ctx.roundRect(ix - 5, iy - 4, 10, 8, 2); + ctx.fill(); + } +} + +function drawRebreather(ctx, rng, cx, mouthY, rx) { + const mw = rng.range(22, 32), mh = rng.range(10, 16); + ctx.fillStyle = C.shadow; + ctx.beginPath(); + ctx.roundRect(cx - mw, mouthY - 4, mw * 2, mh, 4); + ctx.fill(); + ctx.strokeStyle = C.black; ctx.lineWidth = 1.5; + ctx.beginPath(); + ctx.roundRect(cx - mw, mouthY - 4, mw * 2, mh, 4); + ctx.stroke(); + // filter circles + for (let s of [-1, 1]) { + const fx = cx + s * mw * 0.55, fy = mouthY + mh * 0.2; + fillEllipse(ctx, fx, fy, 5, 4, C.black); + strokeEllipse(ctx, fx, fy, 5, 4, C.skinDark, 0.8); + fillEllipse(ctx, fx, fy, 2, 1.8, C.skinDark); + } + // centre vent + ctx.strokeStyle = C.skinDark; ctx.lineWidth = 0.8; + for (let i = -2; i <= 2; i++) { + line(ctx, cx + i * 3, mouthY + 2, cx + i * 3, mouthY + mh - 5, C.skinDark, 0.7); + } +} + +function drawNeck(ctx, cx, faceBotY, neckW) { + ctx.fillStyle = C.skin; + ctx.beginPath(); + ctx.moveTo(cx - neckW, faceBotY + 2); + ctx.lineTo(cx - neckW * 0.7, H); + ctx.lineTo(cx + neckW * 0.7, H); + ctx.lineTo(cx + neckW, faceBotY + 2); + ctx.closePath(); + ctx.fill(); + ctx.strokeStyle = C.outline; ctx.lineWidth = 1.5; + ctx.stroke(); + // collar shadow + ctx.fillStyle = C.shadow; + ctx.globalAlpha = 0.35; + ctx.beginPath(); + ctx.moveTo(cx - neckW * 0.7, H - 8); + ctx.lineTo(cx + neckW * 0.7, H - 8); + ctx.lineTo(cx + neckW * 1.0, H); + ctx.lineTo(cx - neckW * 1.0, H); + ctx.closePath(); + ctx.fill(); + ctx.globalAlpha = 1; +} + +function drawClothing(ctx, rng, cx, faceBot, clothingType) { + ctx.fillStyle = clothingType === 'armor' ? '#282828' : + clothingType === 'suit' ? '#1a1a1a' : C.shadow; + + if (clothingType === 'armor') { + // Shoulder armour plates + ctx.beginPath(); + ctx.moveTo(0, H); + ctx.lineTo(0, faceBot + 28); + ctx.lineTo(cx * 0.4, faceBot + 12); + ctx.lineTo(cx * 0.9, faceBot + 22); + ctx.lineTo(cx * 0.85, H); + ctx.closePath(); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(W, H); + ctx.lineTo(W, faceBot + 28); + ctx.lineTo(W - cx * 0.4, faceBot + 12); + ctx.lineTo(W - cx * 0.9, faceBot + 22); + ctx.lineTo(W - cx * 0.85, H); + ctx.closePath(); + ctx.fill(); + ctx.strokeStyle = C.skinDark; ctx.lineWidth = 1; + ctx.stroke(); + } else if (clothingType === 'suit') { + ctx.beginPath(); + ctx.moveTo(0, H); + ctx.lineTo(cx * 0.3, faceBot + 15); + ctx.lineTo(cx, faceBot + 25); + ctx.lineTo(W - cx * 0.3, faceBot + 15); + ctx.lineTo(W, H); + ctx.closePath(); + ctx.fill(); + // tie + ctx.fillStyle = C.black; + ctx.beginPath(); + ctx.moveTo(cx - 4, faceBot + 22); + ctx.lineTo(cx + 4, faceBot + 22); + ctx.lineTo(cx + 6, faceBot + 50); + ctx.lineTo(cx, faceBot + 58); + ctx.lineTo(cx - 6, faceBot + 50); + ctx.closePath(); + ctx.fill(); + } else { + // jacket / casual + ctx.beginPath(); + ctx.moveTo(0, H); + ctx.lineTo(cx * 0.2, faceBot + 20); + ctx.lineTo(cx, faceBot + 28); + ctx.lineTo(W - cx * 0.2, faceBot + 20); + ctx.lineTo(W, H); + ctx.closePath(); + ctx.fill(); + } +} + +function drawVignette(ctx) { + const g = ctx.createRadialGradient(W / 2, H / 2, H * 0.25, W / 2, H / 2, H * 0.85); + g.addColorStop(0, 'rgba(0,0,0,0)'); + g.addColorStop(0.7, 'rgba(0,0,0,0)'); + g.addColorStop(1, 'rgba(0,0,0,0.7)'); + ctx.fillStyle = g; + ctx.fillRect(0, 0, W, H); +} + +// ── Category-specific modifiers ─────────────────────────────────────────────── +const CATEGORY_PARAMS = { + human: { + shapeIdx: [0, 1, 2, 3], + hairStyles:[0,1,2,3,4,5,6,7], + eyeTypes: ['normal','normal','narrow','intense','cyborg_one','visor'], + clothing: ['jacket','armor','suit','jacket'], + }, + ebon: { + shapeIdx: [4, 4, 2, 4], // angular elongated + hairStyles:[0,0,3,5,7], // often bald or shaved + eyeTypes: ['intense','cyborg_one','cyborg_both','normal','visor'], + clothing: ['jacket','armor'], + skinTone: '#b8b8b8', + }, + frother: { + shapeIdx: [1, 1, 3], // square/blocky + hairStyles:[2, 3, 6, 7], // mohawk, undercut + eyeTypes: ['intense','normal','cyborg_one'], + clothing: ['armor','armor','jacket'], + }, + shaktar: { + shapeIdx: [4, 2, 4], + hairStyles:[0, 0, 5], // bald or hooded + eyeTypes: ['intense','narrow','cyborg_both'], + clothing: ['armor','jacket'], + skinTone: '#909090', + }, + waister: { + shapeIdx: [2, 4], // gaunt/angular + hairStyles:[0, 1, 5], + eyeTypes: ['intense','narrow'], + clothing: ['jacket'], + skinTone: '#b0b0b0', + }, + wraith: { + shapeIdx: [4, 2, 4], + hairStyles:[5, 5, 0, 7], // hoods, bald + eyeTypes: ['visor','cyborg_both','narrow','closed'], + clothing: ['jacket','armor'], + skinTone: '#d0d0d0', + }, + threat: { + shapeIdx: [1, 2, 3, 1], + hairStyles:[0, 2, 6, 3], + eyeTypes: ['intense','normal','cyborg_one'], + clothing: ['jacket','armor'], + }, +}; + +// ── Main portrait renderer ─────────────────────────────────────────────────── +function renderPortrait(seed, category) { + const canvas = createCanvas(W, H); + const ctx = canvas.getContext('2d'); + const rng = mkRng(seed); + const params = CATEGORY_PARAMS[category] ?? CATEGORY_PARAMS.human; + + // ── Background + ctx.fillStyle = C.bg; + ctx.fillRect(0, 0, W, H); + + // ── Layout constants + const cx = W / 2; + const shapeIdx = rng.pick(params.shapeIdx); + const tempShape = FACE_SHAPES[shapeIdx](mkRng(seed + 1)); + const { rx, ry } = tempShape; + + const faceY = Math.round(H * 0.46); + const faceBotY = faceY + ry; + const neckW = Math.round(rx * 0.52); + + // Skin tone override + if (params.skinTone) { + C.skin = params.skinTone; + // Rebuild gradient colors + C.skinMid = params.skinTone; + C.skinDark = '#666'; + C.hilight = '#ddd'; + } else { + C.skin = '#c8c8c8'; C.skinMid = '#a8a8a8'; C.skinDark = '#686868'; C.hilight = '#e8e8e8'; + } + + // ── Neck + clothing (behind face) + drawNeck(ctx, cx, faceBotY, neckW); + drawClothing(ctx, rng, cx, faceBotY, rng.pick(params.clothing)); + + // ── Hair (some styles go behind face) + const hairStyleIdx = rng.pick(params.hairStyles); + HAIR_STYLES[hairStyleIdx](ctx, rng, cx, faceY, rx, ry); + + // ── Face + const face = drawFace(ctx, rng, shapeIdx, cx, faceY); + + // ── Feature positions + const eyeSpread = Math.round(rx * 0.48); + const eyeY = faceY - ry * 0.18; + const noseY = faceY + ry * 0.22; + const mouthY = faceY + ry * 0.52; + + // ── Eyebrows + const browVariant = rng.pick(['default','angry','raised','flat']); + drawEyebrows(ctx, rng, cx, eyeY, eyeSpread, browVariant); + + // ── Eyes + const eyeType = rng.pick(params.eyeTypes); + drawEyes(ctx, rng, cx, eyeY, eyeSpread, eyeType); + + // ── Nose + const noseType = rng.pick(['straight','broad','hooked','button']); + drawNose(ctx, rng, cx, noseY, rx, noseType); + + // ── Rebreather / mask (30% chance; replaces mouth if present) + const hasMask = rng.chance(0.28) && eyeType !== 'visor'; + if (hasMask) { + drawRebreather(ctx, rng, cx, mouthY - 4, rx); + } else { + const mouthType = rng.pick(['thin','frown','grimace','sneer','curve']); + drawMouth(ctx, rng, cx, mouthY, rx, mouthType, false); + } + + // ── Accessories + if (rng.chance(0.35)) drawScar(ctx, rng, cx, faceY, rx, ry); + if (rng.chance(0.3) && eyeType !== 'cyborg_both') { + drawImplant(ctx, rng, cx, faceY, rx, ry, rng.pick(['forehead','cheek','temple','ear'])); + } + + // ── Vignette + drawVignette(ctx); + + return canvas; +} + +// ── Generation batch config ────────────────────────────────────────────────── +const BATCHES = [ + { prefix: 'proc_human', category: 'human', count: 50 }, + { prefix: 'proc_ebon', category: 'ebon', count: 30 }, + { prefix: 'proc_frother', category: 'frother', count: 25 }, + { prefix: 'proc_shaktar', category: 'shaktar', count: 20 }, + { prefix: 'proc_waister', category: 'waister', count: 20 }, + { prefix: 'proc_wraith', category: 'wraith', count: 25 }, + { prefix: 'proc_threat', category: 'threat', count: 30 }, +]; + +// ── Run ─────────────────────────────────────────────────────────────────────── +let total = 0; +let baseSeed = 0x5EED0000; + +for (const batch of BATCHES) { + for (let i = 1; i <= batch.count; i++) { + const filename = `${batch.prefix}_${String(i).padStart(2,'0')}.png`; + const filepath = path.join(OUT_DIR, filename); + const canvas = renderPortrait(baseSeed + total, batch.category); + const buf = canvas.toBuffer('image/png'); + fs.writeFileSync(filepath, buf); + total++; + } + process.stdout.write(` ✓ ${batch.prefix} (${batch.count})\n`); +} + +console.log(`\nGenerated ${total} procedural portraits → ${OUT_DIR}`); diff --git a/scripts/npc-generator.mjs b/scripts/npc-generator.mjs new file mode 100644 index 0000000..abc5837 --- /dev/null +++ b/scripts/npc-generator.mjs @@ -0,0 +1,998 @@ +// npc-generator.mjs — SLA Industries NPC Generator +// Builds ready-to-use NPC actors with stats, skills, gear, portrait, and background. + +import { MODULE_ID } from './settings.mjs'; + +// ============================================================================ +// Name tables — 485 first names + 500 surnames from SLA Industries Name Reference +// ============================================================================ + +// 485 SLA Industries first names (used across all human-presenting species) +const FIRST_NAMES = [ + 'Axel','Blade','Cain','Drex','Echo','Flux','Ghost','Havoc', + 'Irek','Judas','Knox','Lash','Mace','Nox','Onyx','Punk', + 'Quill','Raze','Scar','Thorn','Ulric','Vex','Wraith','Xane', + 'Yael','Zane','Arc','Brix','Colt','Dusk','Edge','Fang', + 'Grim','Haze','Itch','Jolt','Kron','Lurk','Mire','Nail', + 'Omen','Pyre','Quin','Rust','Slag','Taunt','Urge','Void', + 'Wrath','Xerx','Yank','Zero','Ash','Burn','Crag','Dread', + 'Elx','Fell','Gore','Hook','Inch','Jab','Kink','Leech', + 'Mort','Noose','Orge','Pox','Quake','Rift','Shiv','Taint', + 'Ugly','Vain','Weld','Xray','Yore','Zeal','Abis','Bale', + 'Crux','Dirge','Effi','Funk','Gust','Hilt','Ivor','Jest', + 'Kade','Lore','Munk','Nave','Odis','Prod','Qade','Rend', + 'Smog','Tusk','Ulna','Vark','Wisp','Xell','Yost','Zona', + 'Alek','Bren','Cass','Dace','Erin','Fenn','Gale','Holt', + 'Isak','Jace','Kael','Lind','Marc','Neal','Orin','Penn', + 'Rand','Sage','Tane','Ulan','Venn','Ward','Xord','Yule', + 'Zack','Amos','Boyd','Cole','Dale','Earl','Ford','Glen', + 'Hank','Ivan','Joel','Kent','Lane','Mark','Neil','Owen', + 'Paul','Reed','Seth','Todd','Umar','Wade','York','Zion', + 'Arno','Bram','Cruz','Deon','Erik','Finn','Greg','Hugo', + 'Ilan','Jude','Karl','Lars','Milo','Nils','Otto','Petr', + 'Rolf','Sven','Theo','Ulf','Vito','Wulf','Xavi','Yuri', + 'Zeno','Adan','Blas','Ciro','Dain','Egon','Falk','Gabe', + 'Hein','Ingo','Jaro','Knut','Leon','Maik','Niko','Olaf', + 'Pavo','Rudi','Simo','Timo','Uros','Vojo','Wolf','Xeno', + 'Yann','Zaid','Alec','Beau','Cade','Dean','Evan','Frey', + 'Gage','Hale','Iain','Jake','Kane','Luke','Matt','Nash', + 'Omar','Pike','Raja','Shay','Trev','Vane','Xion','Yago', + 'Zeke','Abel','Bink','Clex','Drax','Elen','Frex','Gron', + 'Hexx','Ilex','Jinn','Klex','Lorx','Nrex','Oxel','Prex', + 'Trex','Alva','Brex','Ceyx','Drey','Elox','Frox','Grex', + 'Ilox','Jrex','Krex','Lrex','Mrex','Nrox','Orex','Prox', + 'Trox','Abol','Brek','Cren','Drek','Erek','Frek','Grek', + 'Hrek','Trek','Krek','Lrek','Mrek','Nrek','Orek','Prek', + 'Srek','Urek','Vrek','Wrek','Xrek','Yrek','Zrek','Kata', + 'Sora','Mira','Lyra','Vera','Nora','Dara','Zara','Lara', + 'Kira','Yara','Tara','Sara','Cara','Fara','Gara','Hara', + 'Jara','Alix','Blix','Clix','Elix','Flix','Glix','Hlix', + 'Krix','Lrix','Mrix','Nrix','Orix','Prix','Trix','Urix', + 'Vrix','Wrix','Xrix','Yrix','Zrix','Nova','Rena','Sela', + 'Tela','Vela','Xela','Yela','Zela','Aela','Bela','Cela', + 'Dela','Fela','Gela','Hela','Iela','Jela','Kela','Lela', + 'Mela','Nela','Pela','Rela','Axia','Bria','Cria','Dria', + 'Eria','Fria','Gria','Iria','Jria','Kria','Lria','Mria', + 'Nria','Oria','Pria','Sria','Tria','Uria','Vria','Wria', + 'Xria','Yria','Zria','Alox','Blox','Clox','Elox','Flox', + 'Glox','Ilox','Jlox','Klox','Mlox','Nlox','Olox','Plox', + 'Rlox','Slox','Tlox','Ulox','Vlox','Wlox','Xlox','Ylox', + 'Zlox','Amox','Emox','Fmox','Gmox','Hmox','Imox','Jmox', + 'Kmox','Lmox','Nmox','Omox','Pmox','Rmox','Smox','Tmox', + 'Umox','Vmox','Wmox','Xmox','Ymox','Zmox','Anox','Bnox', + 'Cnox','Dnox','Enox','Fnox','Gnox','Hnox','Inox','Jnox', + 'Lnox','Mnox','Nnox','Onox','Pnox','Qnox','Rnox','Snox', + 'Tnox','Unox','Vnox','Wnox','Xnox','Ynox','Znox','Raxx', + 'Vexx','Zoxx','Paxx','Daxx','Taxx', +]; + +// 500 SLA-flavoured dystopian surnames +const SURNAMES = [ + 'Ashcroft','Blackmore','Carnage','Deathridge','Eviscer','Flayer','Gravemore','Hellgate', + 'Ironside','Junkyard','Killmore','Lacemore','Meatridge','Noosemore','Overkill','Painmore', + 'Quickdeath','Razormore','Slaughton','Tombmore','Undead','Vilemore','Woundmore','Xenocide', + 'Yarmore','Zeromore','Abattoir','Bloodmore','Crushmore','Darkmore','Edgemore','Fleshmore', + 'Gutmore','Hackmore','Icepick','Jawmore','Knifemore','Lungmore','Maggotmore','Nailmore', + 'Orbmore','Pilemore','Quarrymore','Rippmore','Slagmore','Tearmore','Ulcermore','Venommore', + 'Waxmore','Xoremore','Yardmore','Zoremore','Akemore','Balemore','Coldmore','Dirtmore', + 'Endmore','Foulmore','Grimmore','Holemore','Inkmore','Jailmore','Ketmore','Loremore', + 'Moldmore','Numbmore','Odmore','Pitmore','Quemore','Rotmore','Snagmore','Tormore', + 'Ulmore','Vastmore','Wiremore','Xetmore','Yoremore','Zetmore','Aldrich','Burnwick', + 'Cruelton','Darkwood','Edgerton','Frostmire','Greystone','Hollowmore','Ironwood','Jadwick', + 'Killwick','Lostwick','Mirewick','Nightwick','Oddwick','Painwick','Quailwick','Rustwick', + 'Skelwick','Thornwick','Umwick','Vilewick','Wormwick','Xawick','Yellwick','Zombwick', + 'Ashwick','Bladewick','Corewick','Deadwick','Eelwick','Fluxwick','Grimewick','Hatewick', + 'Icewick','Jawwick','Knoxwick','Lashwick','Morwick','Nailwick','Ogrewick','Poxwick', + 'Quakwick','Rifwick','Slumwick','Tombwick','Urgwick','Vexwick','Waxwick','Xerwick', + 'Yardwick','Zorewick','Abwick','Brixwick','Cruxwick','Dregwick','Exwick','Forwick', + 'Gustwick','Hiltwick','Ivorwick','Jestwick','Kadewick','Lorewick','Munkwick','Navewick', + 'Odinwick','Prodwick','Quadewick','Rendwick','Smogwick','Tuskwick','Ulnawick','Varkwick', + 'Wispwick','Xellwick','Yostwick','Zonawick','Aldmore','Brenmore','Cassmore','Dacemore', + 'Erinmore','Fennmore','Galemore','Holtmore','Isakmore','Jacemore','Kaelmore','Lindmore', + 'Marcmore','Nealmore','Orinmore','Pennmore','Randmore','Sagemore','Tanemore','Ulanmore', + 'Vennmore','Wardmore','Xordmore','Yulmore','Zackmore','Amosmore','Boydmore','Colemore', + 'Dalemore','Earlmore','Fordmore','Glenmore','Hankmore','Ivanmore','Joelmore','Kentmore', + 'Lanemore','Markmore','Neilmore','Owenmore','Paulmore','Reedmore','Sethmore','Toddmore', + 'Umarmore','Wademore','Yorkmore','Zionmore','Arnomore','Brammore','Cruzmore','Deonmore', + 'Erikmore','Finnmore','Gregmore','Hugomore','Ilanmore','Judemore','Karlmore','Larsmore', + 'Milomore','Nilsmore','Ottomore','Petrmore','Rolfmore','Svenmore','Theomore','Ulfmore', + 'Vitomore','Wulfmore','Xvimore','Yuriore','Zenomore','Adanmore','Blasmore','Ciromore', + 'Dainmore','Egonmore','Falkmore','Gabemore','Heinmore','Ingomore','Jaromore','Knutmore', + 'Leonmore','Maikmore','Nikomore','Olafmore','Pavomore','Rudimore','Simomore','Timomore', + 'Urosmore','Vojomore','Wolfmore','Xenomore','Yannmore','Zaidmore','Alecmore','Beaumore', + 'Cademore','Deanmore','Evanmore','Freymore','Gagemore','Halemore','Iainmore','Jakemore', + 'Kanemore','Lukemore','Mattmore','Nashmore','Omarmore','Pikemore','Rajamore','Shaywick', + 'Trevwick','Ulanwick','Vanewick','Wadewick','Xionwick','Yagowick','Zekewick','Abelwick', + 'Binkwick','Clexwick','Draxwick','Elenwick','Frexwick','Gronwick','Hexwick','Ilexwick', + 'Jinnwick','Klexwick','Lorxwick','Mnexwick','Nrexwick','Oxelwick','Prexwick','Qluxwick', + 'Rexwick','Srexwick','Trexwick','Urexwick','Vrexwick','Wrexwick','Xrexwick','Yrexwick', + 'Zrexwick','Alvwick','Brixton','Cexton','Drexton','Eloxton','Froxton','Grexton', + 'Hrexton','Iloxton','Jrexton','Krexton','Lrexton','Mrexton','Nroxton','Orexton', + 'Proxton','Qrexton','Rroxton','Sroxton','Troxton','Uroxton','Vroxton','Wroxton', + 'Xroxton','Yroxton','Zroxton','Abolt','Brekon','Crendon','Drekon','Erekon', + 'Frekon','Grekon','Hrekon','Irekon','Jrekon','Krekon','Lrekon','Mrekon', + 'Nrekon','Orekon','Prekon','Qrekon','Rrekon','Srekon','Trekon','Urekon', + 'Vrekon','Wrekon','Xrekon','Yrekon','Zrekon','Katawick','Sorawick','Mirawick', + 'Lyrawick','Verawick','Norawick','Darawick','Zarawick','Larawick','Kirawick','Yarawick', + 'Tarawick','Sarawick','Parawick','Barawick','Carawick','Farawick','Garawick','Harawick', + 'Jarawick','Alixmore','Blixmore','Clixmore','Dlixmore','Elixmore','Flixmore','Glixmore', + 'Hlixmore','Ilixmore','Jlixmore','Krixmore','Lrixmore','Mrixmore','Nrixmore','Orixmore', + 'Prixmore','Qrixmore','Rrixmore','Srixmore','Trixmore','Urixmore','Vrixmore','Wrixmore', + 'Xrixmore','Yrixmore','Zrixmore','Novawick','Renawick','Selawick','Telawick','Velawick', + 'Welawick','Xelawick','Yelawick','Zelawick','Aelawick','Belawick','Celawick','Delawick', + 'Eelawick','Felawick','Gelawick','Helawick','Ielawick','Jelawick','Kelawick','Lelawick', + 'Melawick','Nelawick','Oelawick','Pelawick','Qelawick','Relawick','Briawick','Criawick', + 'Driawick','Eriawick','Friawick','Griawick','Hriawick','Iriawick','Jriawick','Kriawick', + 'Lriawick','Mriawick','Nriawick','Oriawick','Priawick','Sriawick','Triawick','Uriawick', + 'Vriawick','Wriawick','Xriawick','Yriawick','Zriawick','Aloxwick','Bloxwick','Cloxwick', + 'Eloxwick','Floxwick','Gloxwick','Hloxwick','Iloxwick','Jloxwick','Kloxwick','Mloxwick', + 'Nloxwick','Oloxwick','Ploxwick','Rloxwick','Sloxwick','Tloxwick','Uloxwick','Vloxwick', + 'Wloxwick','Xloxwick','Yloxwick','Zloxwick','Amoxwick','Cmoxwick','Dmoxwick','Emoxwick', + 'Fmoxwick','Gmoxwick','Hmoxwick','Imoxwick','Jmoxwick','Kmoxwick','Lmoxwick','Mmoxwick', + 'Nmoxwick','Omoxwick', +]; + +// 250 serial killer / threat identities for the Mutant / Serial Killer archetype +const SERIAL_KILLER_NAMES = [ + 'Jack The Dissector','The Canker Street Carver','Mother Carnage','The Pale Butcher', + 'Severin Voss','The Apartment Bleeder','Grimshaw The Peeler','Sister Mortis', + 'The 700 Block Ripper','Creed The Hollow','Magda Filch','The Downpipe Strangler', + 'Elden Rake','The Meatpacking Saint','Nurse Lacewick','The Sump Killer', + 'Doktor Vein','The Canal Street Feeder','Abner Retch','The Sleeping God', + 'Kira The Opener','The Soft Tissue Sculptor','Father Gristle','The Smiling Patient', + 'Oswin Craw','The Tooth Collector','Lady Carnifax','The Quiet Orderly', + 'Brother Splint','The Ceiling Crawler','Yelena Dross','The Lunch Hour Killer', + 'Morten Peel','The Chimney Bather','Sera The Hollow','The Duct Work Ghoul', + 'Aldric Vane','The Body Farm Pilgrim','Sister Phlegm','The Corridor Creeper', + 'Thane Grippe','The Overflow Wraith','Magda Vesper','The Two-Floor Terror', + 'Reko The Saint','The Basement Surgeon','Brother Ache','The Office Block Phantom', + 'Drace Mortum','The Substation Bleeder','Ilsa Craven','The Stairwell Whisper', + 'Venan Pitch','The Loading Bay Killer','Mother Osse','The Transfer Tube Slasher', + 'Kyne Reverie','The Uptown Dissector','Father Rend','The Walkway Predator', + 'Shrake The Cutter','The Overflow District Flayer','Madra Vile','The Underpass Surgeon', + 'Solen Craw','The Airlock Garrotter','Sister Bile','The Power Cell Butcher', + 'Hektor Gnash','The Freight Elevator Killer','Brother Worm','The Recycling Centre Ripper', + 'Treva Mordax','The Tenement Saint','Doktor Puss','The Night Shift Killer', + 'Anya Vex','The Boiler Room Phantom','Father Crust','The Condemned Block Terror', + 'Samir Dread','The Rain Collector Killer','Mother Slake','The Long Corridor Predator', + 'Brix The Harvester','The Sub-Level Butcher','Sister Flay','The Cargo Hold Ripper', + 'Ulan Mort','The Work Camp Killer','Brother Gore','The Pressure Lock Garrotter', + 'Essa Writhe','The Tunnel System Surgeon','Doktor Scratch','The Generator Room Killer', + 'Lorn Vile','The Factory Floor Flayer','Mother Dreg','The Skyway Predator', + 'Kesh The Taker','The Water Tank Butcher','Father Ache','The Plaza Level Terror', + 'Nira Spite','The Underworld Saint','Sister Plague','The Transit Hub Ripper', + 'Drex The Silent','The Processing Plant Killer','Brother Peel','The Maintenance Shaft Ghoul', + 'Cela Void','The Archive Level Predator','Doktor Husk','The Night Canteen Killer', + 'Taun Fester','The Hab Block Surgeon','Mother Vex','The Overground Stalker', + 'Skar The Tender','The Pit Level Flayer','Father Necrox','The Downward Killer', + 'Zeal Mortis','The Sector 6 Ripper','Sister Drip','The Market Square Butcher', + 'Helm Lacerate','The Pipe Run Garrotter','Brother Wither','The Off-World Killer', + 'Elka Dissect','The Street Drain Predator','Doktor Flay','The Hab Level Surgeon', + 'Orn The Carver','The Data Corridor Killer','Mother Grippe','The Weight Room Ripper', + 'Caith Dread','The Sewer Saint','Father Craw','The Port Level Butcher', + 'Vrex The Bleeder','The Sub-Street Stalker','Sister Mire','The Pump Station Killer', + 'Akel Mortum','The Discharge Flayer','Brother Crust','The Skyview Garrotter', + 'Pelz The Hollow','The Hunger Level Killer','Doktor Dreg','The Night Ward Surgeon', + 'Thad Carnage','The Precinct Ripper','Mother Spite','The Slab Level Butcher', + 'Cren Vilemore','The Under-City Predator','Father Peel','The Clearance Level Killer', + 'Sora Dissect','The Waterway Stalker','Sister Bile','The Drop Zone Terror', + 'Lux The Opener','The Sector 8 Ripper','Brother Husk','The Med Bay Killer', + 'Ezra Mortax','The Night Crawler','Doktor Worm','The Pressure Dock Butcher', + 'Hesk Carnifax','The Enclosed Space Killer','Mother Lacerate','The Underplex Ripper', + 'Trix The Flayer','The Shift Change Predator','Father Spite','The Block Eight Killer', + 'Vera Dissect','The Grille Work Garrotter','Sister Fester','The Chasm Level Terror', + 'Orm The Carver','The Night Feed Killer','Brother Vile','The Inmate Level Flayer', + 'Keld Mortum','The Quiet Hours Stalker','Doktor Spine','The Slow Kill Surgeon', + 'Mace Carnage','The Sector Twelve Ripper','Mother Wither','The Grey Level Killer', + 'Dusk The Bleeder','The Contract Flayer','Father Gnash','The Contract Saint', + 'Rax Mortis','The Night Peel Killer','Sister Dreg','The Midnight Carver', + 'Udo The Hollow','The Long Night Stalker','Brother Vex','The Archive Ripper', + 'Noka Dissect','The Still Room Killer','Doktor Craven','The Sleep Level Butcher', + 'Phet The Opener','The Grease Level Predator','Mother Craw','The Skin Trade Killer', + 'Axel Mortax','The Null Level Ripper','Sister Spine','The Dawn Shift Butcher', + 'Kyre Carnifax','The Cold Room Surgeon','Father Fester','The Lockdown Garrotter', + 'Iden The Flayer','The Dead Zone Stalker','Brother Lacerate','The Sleep Cycle Killer', + 'Vrax Mortum','The Zero Level Ripper','Doktor Spite','The Hab Roof Predator', + 'Sela Dissect','The Dark Hours Flayer','Mother Husk','The Corridor Eight Killer', + 'Rend The Bleeder','The Processing Ripper','Father Worm','The Sub Nineteen Butcher', + 'Axia Carnage','The Weight Level Garrotter','Sister Craw','The Last Level Stalker', + 'Keth Mortis','The Overflow Carver', +]; + +// 35 unique street gang handles (from 250 gang entries in the name reference) +const GANG_HANDLES = [ + 'Splice','Raze','Dagger','Bleed','Gouge','Hack','Spike','Crux', + 'Lash','Grind','Stitch','Bite','Smash','Crack','Tear','Fray', + 'Stomp','Slash','Wreck','Burn','Strip','Crush','Pound','Snap', + 'Twist','Kick','Rip','Gnaw','Maul','Cleave','Gut','Shred', + 'Slice','Stab','Punch', +]; + +// ============================================================================ +// Archetype definitions +// ============================================================================ + +/** + * Roll an integer between min and max (inclusive). + */ +function roll(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +/** + * Pick a random element from an array. + */ +function pick(arr) { + return arr[Math.floor(Math.random() * arr.length)]; +} + +const ARCHETYPES = { + civilian: { + label: 'Civilian / Citizen', + icon: 'fas fa-user', + description: 'An ordinary denizen of the World of Progress — corp worker, street person, or shopkeeper.', + defaultSpecies: ['human', 'ebon'], + stats: { + strength: [20, 35], + speed: [20, 35], + intellect: [25, 40], + combat: [15, 30], + sanity: [30, 45], + fear: [30, 45], + body: [20, 35], + }, + armor: [0, 1], + stress: [3, 6], + resolve: [3, 6], + skills: [ + { name: 'Streetwise', rank: 'Trained', bonus: 10 }, + { name: 'Evaluate', rank: 'Trained', bonus: 5 }, + ], + weapons: [ + { name: 'Pocket Knife', damage: '1d5', ammo: 0, shots: 0, useAmmo: false, description: 'A small folding knife, more tool than weapon.' }, + ], + backgrounds: [ + 'Debt-ridden corp drone grinding out a living in the lower sectors.', + 'A recently downsized block resident with nothing left to lose.', + 'Street vendor scraping by selling reconditioned tech.', + 'Off-shift factory worker from SubUrban housing block Sub-G4.', + 'Former Shivers recruit who washed out of basic training.', + 'Cafeteria worker at a mid-tier Cloak Division facility.', + 'Sector maintenance technician with a drinking problem.', + 'A disillusioned clerk who has seen too many BPN reports.', + ], + operativeType: 'Civilian', + portraitCategory: null, // use species + }, + + shiver: { + label: 'Shiver Operative', + icon: 'fas fa-shield-alt', + description: 'SLA Industries paramilitary police — armed response, patrol units, or Sector Rangers.', + defaultSpecies: ['human', 'human', 'human', 'ebon', 'frother'], + stats: { + strength: [30, 45], + speed: [30, 42], + intellect: [25, 38], + combat: [35, 50], + sanity: [25, 40], + fear: [25, 40], + body: [30, 45], + }, + armor: [4, 6], + stress: [4, 8], + resolve: [4, 7], + skills: [ + { name: 'Firearms', rank: 'Trained', bonus: 10 }, + { name: 'Unarmed Combat', rank: 'Trained', bonus: 5 }, + { name: 'Streetwise', rank: 'Trained', bonus: 5 }, + { name: 'Interrogation', rank: 'Trained', bonus: 0 }, + ], + weapons: [ + { name: 'Blitzer SMG', damage: '1d10+3', ammo: 30, shots: 1, useAmmo: true, ammoType: 'SMG', description: 'Standard-issue SLA Shiver automatic weapon. Fires STD rounds.' }, + { name: 'GAU Automatic', damage: '1d10+2', ammo: 15, shots: 1, useAmmo: true, ammoType: 'Pistol', description: 'Compact sidearm issued to all Shiver operatives.' }, + ], + backgrounds: [ + 'Third-year patrol officer assigned to a mid-tier sector. Follows orders without question.', + 'Armed response unit activated after a Carrien incursion, still twitchy from the last call.', + 'Veteran Shiver with 12 years service, completely loyal to the Division.', + 'Sector Ranger on anti-gang operations. Carries two extra magazines at all times.', + 'Recently promoted to Sergeant, keen to make an impression on the squad.', + 'Tasked with escorting a BPN team. Privately unhappy about the assignment.', + 'Under-resourced patrol officer stretched across three blocks with a part-time partner.', + 'Former Corporate Security secondee, integrating awkwardly with the Shiver unit.', + ], + operativeType: 'Shiver', + portraitCategory: null, + }, + + carrien: { + label: 'Carrien', + icon: 'fas fa-skull-crossbones', + description: 'A feral Carrien — fast, savage, barely human. A pack threat from the deep undercity.', + defaultSpecies: ['human'], + stats: { + strength: [38, 55], + speed: [40, 58], + intellect: [5, 14], + combat: [38, 55], + sanity: [3, 10], + fear: [3, 10], + body: [32, 50], + }, + armor: [0, 2], + stress: [2, 3], + resolve: [1, 2], + skills: [ + { name: 'Melee Combat', rank: 'Expert', bonus: 15 }, + ], + weapons: [ + { name: 'Claws & Teeth', damage: '1d10+3', ammo: 0, shots: 0, useAmmo: false, description: 'Ragged nails, broken teeth, and the desperate strength of a cornered animal.' }, + ], + backgrounds: [ + 'Feral pack-runner. Attacks on smell and movement.', + 'A former citizen, now unrecognisable after years below-sector.', + 'Scout for a larger Carrien pack moving through the sector walls.', + 'Driven above-sector by something even it fears deeper in the undercity.', + 'Territorial. Will not retreat. Will not negotiate.', + 'Freshly turned — still wearing the remnants of corp worker clothes.', + ], + operativeType: 'Carrien', + portraitCategory: 'animal_unknown', // Carrien → non-human animal pool + isCreatureType: true, + }, + + gang: { + label: 'Street Gang', + icon: 'fas fa-people-group', + description: 'A block gang cell — generates multiple members at once with a shared street name and minimal stats.', + isGangGroup: true, // triggers group-creation mode + defaultSpecies: ['human'], + // Per-member stats (combat-focused only — the rest defaults) + stats: { + strength: [25, 42], + speed: [28, 42], + intellect: [15, 25], + combat: [28, 45], + sanity: [12, 25], + fear: [12, 25], + body: [28, 45], + }, + armor: [0, 3], + stress: [2, 4], + resolve: [2, 4], + skills: [ + { name: 'Melee Combat', rank: 'Trained', bonus: 10 }, + ], + // Gang weapons — each member gets ONE random pick from this list + gangWeapons: [ + { name: 'Power Blade', damage: '1d10+2', ammo: 0, shots: 0, useAmmo: false, description: 'A crude energy-edged blade.' }, + { name: 'Vibro-blade', damage: '1d10+1', ammo: 0, shots: 0, useAmmo: false, description: 'A buzzing, fast-edged street weapon.' }, + { name: 'Length of Pipe', damage: '1d6+3', ammo: 0, shots: 0, useAmmo: false, description: 'Blunt force trauma from a section of maintenance pipe.' }, + { name: 'Meat Cleaver', damage: '1d6+4', ammo: 0, shots: 0, useAmmo: false, description: 'Repurposed kitchen hardware.' }, + { name: 'Scrap Pistol', damage: '1d8', ammo: 6, shots: 1, useAmmo: true, ammoType: 'Pistol', description: 'Cobbled together from salvaged parts. May jam.' }, + { name: 'Sawn-off Shotgun', damage: '2d6', ammo: 2, shots: 1, useAmmo: true, ammoType: 'Shotgun', description: 'Close-range devastation. Extremely loud.' }, + { name: 'Knife', damage: '1d5+1', ammo: 0, shots: 0, useAmmo: false, description: 'A short, ugly blade worn on the hip.' }, + ], + operativeType: 'Gang Member', + portraitCategory: null, + }, + + corporate: { + label: 'Corporate Agent', + icon: 'fas fa-briefcase', + description: 'Suits, executives, corporate security, and Power Corp operatives.', + defaultSpecies: ['human', 'human', 'ebon', 'human', 'human'], + stats: { + strength: [22, 36], + speed: [22, 36], + intellect: [35, 52], + combat: [28, 42], + sanity: [32, 48], + fear: [32, 48], + body: [22, 36], + }, + armor: [1, 4], + stress: [3, 7], + resolve: [4, 8], + skills: [ + { name: 'Persuasion', rank: 'Trained', bonus: 10 }, + { name: 'Evaluate', rank: 'Expert', bonus: 10 }, + { name: 'Intimidation', rank: 'Trained', bonus: 5 }, + { name: 'Concealment', rank: 'Trained', bonus: 5 }, + ], + weapons: [ + { name: 'Concealed Sidearm', damage: '1d8', ammo: 8, shots: 1, useAmmo: true, ammoType: 'Pistol', description: 'A compact, high-quality pistol. Discreetly holstered.' }, + ], + backgrounds: [ + 'Mid-level executive overseeing a sensitive data review. Carries a deadlock chip.', + 'Corporate Security agent embedded in a BPN team to monitor SLA Operatives.', + 'White Corporation representative with interests in the local sector economy.', + 'Blue Corporation efficiency consultant. Currently under non-disclosure contract.', + 'Power Corp negotiator — calm, well-dressed, and absolutely ruthless.', + 'Corporate fixer tasked with cleaning up a situation that never officially happened.', + 'Loyal company man with three promotions and no conscience.', + 'Financial auditor dispatched to oversee a "restructuring" of sector resources.', + ], + operativeType: 'Corporate', + portraitCategory: null, + }, + + mutant: { + label: 'Mutant / Serial Killer', + icon: 'fas fa-biohazard', + description: 'Mutated threats, deranged killers, and Manchine-adjacent horrors from the deep sectors.', + defaultSpecies: ['human', 'human', 'human'], + stats: { + strength: [40, 58], + speed: [22, 38], + intellect: [8, 22], + combat: [40, 58], + sanity: [5, 18], + fear: [5, 18], + body: [42, 60], + }, + armor: [0, 4], + stress: [2, 4], + resolve: [1, 3], + skills: [ + { name: 'Melee Combat', rank: 'Expert', bonus: 10 }, + { name: 'Intimidation', rank: 'Expert', bonus: 5 }, + ], + weapons: [ + { name: 'Improvised Weapon', damage: '1d10+4', ammo: 0, shots: 0, useAmmo: false, description: 'A brutal tool of violence. Could be a length of pipe, a modified saw, or something worse.' }, + ], + backgrounds: [ + 'A deep-sector mutant driven to violence by prolonged exposure to unstable compounds.', + 'Serial killer active in the lower blocks for 18 months. Three kills confirmed, others suspected.', + 'Manchine-touched survivor of a containment breach. No longer fully human.', + 'A former SLA worker driven mad by a classified experiment gone catastrophically wrong.', + 'Something that used to be a person, before the sector chewed it up and spat it back out.', + 'Subject of an illegal gene-therapy programme. Currently at large and extremely dangerous.', + 'A traumatised war vet whose grip on reality dissolved years ago.', + 'DarkNight insurgent pushed past the point of sanity by their conditioning.', + ], + operativeType: 'Threat', + portraitCategory: 'mutant_unknown', // Serial killer / mutant pool + }, +}; + +const SPECIES_OPTIONS = [ + { value: 'random', label: 'Random (archetype-appropriate)' }, + { value: 'human', label: 'Human' }, + { value: 'ebon', label: 'Ebon' }, + { value: 'frother', label: 'Frother' }, + { value: 'shaktar', label: 'Shaktar' }, + { value: 'stormer', label: 'Stormer' }, + { value: 'waister', label: 'Waister' }, + { value: 'wraith', label: 'Wraith' }, +]; + +// ============================================================================ +// Name generation +// ============================================================================ + +function generateName(species, archetypeKey) { + // Serial killers / mutants get a pre-built infamous identity + if (archetypeKey === 'mutant') { + return pick(SERIAL_KILLER_NAMES); + } + + // Stormer names are alphanumeric designations + if (species === 'stormer') { + const designations = ['Storm','Bravo','Vector','Apex','Delta','Echo','Grid','Havoc','Iron','Jackal','Kilo','Lima']; + return `${pick(designations)}-${roll(1,9)}${roll(0,9)}`; + } + + // All other species: pick from the 485-name first-name pool + 500 surnames + const firstName = pick(FIRST_NAMES); + const surname = pick(SURNAMES); + return `${firstName} ${surname}`; +} + +// ============================================================================ +// Stat generation +// ============================================================================ + +function generateStats(archetype) { + const def = ARCHETYPES[archetype]; + const stats = {}; + for (const [key, [min, max]] of Object.entries(def.stats)) { + stats[key] = { value: roll(min, max), mod: 0 }; + } + return stats; +} + +// ============================================================================ +// Item creation helpers +// ============================================================================ + +function makeSkillItemData(skill) { + return { + name: skill.name, + type: 'skill', + system: { + rank: skill.rank, + bonus: skill.bonus ?? 0, + sla: { category: '', skillFamily: '', source: 'Generated' }, + description: '', + }, + }; +} + +function makeWeaponItemData(weapon) { + return { + name: weapon.name, + type: 'weapon', + system: { + damage: weapon.damage ?? '1d10', + ammo: weapon.ammo ?? 0, + shots: weapon.shots ?? 1, + curShots: weapon.ammo ?? 0, + shotsPerFire: 1, + useAmmo: weapon.useAmmo ?? false, + ammoType: weapon.ammoType ?? '', + ammoCalibre: '', + ammoTag: 'STD', + ammoLoadedType: 'STD', + ammoReserve: weapon.ammo ?? 0, + ammoReserveStd: weapon.ammo ?? 0, + ammoReserveAp: 0, + ammoReserveHe: 0, + ammoReserveHeap: 0, + wound: '', + critDmg: '', + woundEffect: '', + description: weapon.description ?? '', + }, + }; +} + +// ============================================================================ +// Main generation function +// ============================================================================ + +/** + * Generate one NPC actor document. + * @param {string} archetypeKey - Key from ARCHETYPES + * @param {string} speciesChoice - 'random' or a specific species + * @param {PortraitManager} manager + * @param {string|null} folderId + * @returns {Promise} + */ +async function generateNpc(archetypeKey, speciesChoice, manager, folderId) { + const def = ARCHETYPES[archetypeKey]; + const isCreature = def?.isCreatureType ?? (archetypeKey === 'mutant'); + + // Resolve species + const species = speciesChoice === 'random' + ? pick(def.defaultSpecies) + : speciesChoice; + + // Generate name + const name = generateName(species, archetypeKey); + + // Generate stats + const stats = generateStats(archetypeKey); + + // Health = body stat (simplified; Foundry system will recalculate in some builds) + const bodyVal = stats.body?.value ?? 30; + const healthVal = Math.max(5, Math.floor(bodyVal / 2)); + + // Armor + const armorVal = roll(def.armor[0], def.armor[1]); + + // Stress / Resolve + const stressMax = roll(def.stress[0], def.stress[1]); + const resolveMax = roll(def.resolve[0], def.resolve[1]); + + // Pick portrait + let portraitPath = null; + const portraitCat = def.portraitCategory; + + if (manager.portraits.length > 0) { + // Broad type map: species → human | mutant | animal (matches portrait-manager.mjs) + const SPECIES_TO_TYPE = { + human: 'human', ebon: 'human', stormer: 'human', + frother: 'mutant', + shaktar: 'animal', waister: 'animal', wraith: 'animal', + }; + + let portrait; + if (portraitCat) { + // Archetype has an explicit override (carrien → animal_unknown, mutant → mutant_unknown) + portrait = manager.getRandomPortrait({ category: portraitCat }); + } else if (isCreature) { + portrait = manager.getRandomPortrait({ category: 'animal_unknown' }); + } else { + // Map species to broad type, pick gendered or unknown pool + const broadType = SPECIES_TO_TYPE[species] ?? 'human'; + // Try gendered first (both male + female), then fall back to unknown + portrait = manager.getRandomPortrait({ + category: [`${broadType}_male`, `${broadType}_female`, `${broadType}_unknown`], + }); + } + portraitPath = portrait?.path ?? null; + } + + // Background + const background = pick(def.backgrounds); + + // Actor type: mutant/creature uses 'creature', everything else uses 'character' + const actorType = isCreature ? 'creature' : 'character'; + + // Build actor data + const actorData = { + name, + type: actorType, + img: portraitPath ?? 'icons/svg/mystery-man.svg', + folder: folderId ?? null, + system: { + health: { value: healthVal, min: 0, max: healthVal }, + biography: background, + notes: `Archetype: ${def.label}\nSpecies: ${species}\nOperative Type: ${def.operativeType}`, + stats, + other: { + stress: { value: 2, min: 2, max: stressMax }, + resolve: { value: resolveMax, min: 0, max: resolveMax }, + }, + sla: { + operativeType: { value: def.operativeType }, + species: { value: species.charAt(0).toUpperCase() + species.slice(1) }, + }, + }, + prototypeToken: { + name, + disposition: CONST.TOKEN_DISPOSITIONS.HOSTILE, + texture: { src: portraitPath ?? 'icons/svg/mystery-man.svg' }, + sight: { enabled: false }, + actorLink: false, + randomImg: false, + }, + }; + + // Create actor + const actor = await Actor.create(actorData); + if (!actor) return null; + + // Add armor stat update (armor is a stat on the character) + await actor.update({ 'system.stats.armor.value': armorVal }); + + // Create embedded item documents (skills + weapons) + const itemsToCreate = [ + ...def.skills.map(s => makeSkillItemData(s)), + ...def.weapons.map(w => makeWeaponItemData(w)), + ]; + + if (itemsToCreate.length > 0) { + await actor.createEmbeddedDocuments('Item', itemsToCreate); + } + + return actor; +} + +// ============================================================================ +// Gang group generation — creates a batch of lightweight grunt actors +// ============================================================================ + +/** + * Generate a street gang cell of `count` members. + * Each member gets one name, minimal combat stats, and one random weapon. + * @param {number} count + * @param {PortraitManager} manager + * @param {string|null} folderId + * @returns {Promise} + */ +async function generateGangMembers(count, manager, folderId) { + const def = ARCHETYPES.gang; + const created = []; + + // Track used handles in this batch to avoid duplicates + const usedHandles = new Set(); + + for (let i = 0; i < count; i++) { + // Pick a unique handle for this member + let handle; + const availableHandles = GANG_HANDLES.filter(h => !usedHandles.has(h)); + if (availableHandles.length > 0) { + handle = pick(availableHandles); + usedHandles.add(handle); + } else { + // Pool exhausted — append a number + handle = `${pick(GANG_HANDLES)}-${i + 1}`; + } + + // Roll minimal combat-relevant stats only + const combatVal = roll(def.stats.combat[0], def.stats.combat[1]); + const bodyVal = roll(def.stats.body[0], def.stats.body[1]); + const speedVal = roll(def.stats.speed[0], def.stats.speed[1]); + const strengthVal = roll(def.stats.strength[0],def.stats.strength[1]); + const healthVal = Math.max(4, Math.floor(bodyVal / 3)); + const armorVal = roll(def.armor[0], def.armor[1]); + + // Gang members are street humans — draw from human pool + let portraitPath = null; + if (manager.portraits.length > 0) { + const p = manager.getRandomPortrait({ category: ['human_male', 'human_female', 'human_unknown'] }); + portraitPath = p?.path ?? null; + } + + // Pick a random weapon from the gang weapons list + const weapon = pick(def.gangWeapons); + + const actorData = { + name: handle, + type: 'character', + img: portraitPath ?? 'icons/svg/mystery-man.svg', + folder: folderId ?? null, + system: { + health: { value: healthVal, min: 0, max: healthVal }, + biography: `Street gang member. No past worth mentioning.`, + notes: `Archetype: Street Gang\nArmor: ${armorVal}\nCombat: ${combatVal}`, + stats: { + strength: { value: strengthVal, mod: 0 }, + speed: { value: speedVal, mod: 0 }, + intellect: { value: roll(15, 25), mod: 0 }, + combat: { value: combatVal, mod: 0 }, + sanity: { value: roll(12, 22), mod: 0 }, + fear: { value: roll(12, 22), mod: 0 }, + body: { value: bodyVal, mod: 0 }, + armor: { value: armorVal, mod: 0, damageReduction: 0 }, + }, + other: { + stress: { value: 2, min: 2, max: roll(2, 4) }, + resolve: { value: 2, min: 0, max: roll(2, 4) }, + }, + sla: { + operativeType: { value: 'Gang Member' }, + species: { value: 'Human' }, + }, + }, + prototypeToken: { + name, + disposition: CONST.TOKEN_DISPOSITIONS.HOSTILE, + texture: { src: portraitPath ?? 'icons/svg/mystery-man.svg' }, + sight: { enabled: false }, + actorLink: false, + randomImg: false, + }, + }; + + const actor = await Actor.create(actorData); + if (!actor) continue; + + // Embed the weapon item + await actor.createEmbeddedDocuments('Item', [ makeWeaponItemData(weapon) ]); + + created.push(actor); + } + + return created; +} + +// ============================================================================ +// UI helpers +// ============================================================================ + +/** + * Show/hide the gang-size row and the standard count/species rows + * depending on whether the 'gang' archetype is selected. + * @param {HTMLElement} root + * @param {string} archetypeKey + */ +function _toggleGangRow(root, archetypeKey) { + const isGang = ARCHETYPES[archetypeKey]?.isGangGroup ?? false; + + const gangRow = root.querySelector('.gen-gang-size-row'); + const countRow = root.querySelector('.gen-count-species-row'); + const speciesRow = root.querySelector('.gen-species-field'); + + if (gangRow) gangRow.style.display = isGang ? '' : 'none'; + if (countRow) countRow.style.display = isGang ? 'none' : ''; + if (speciesRow) speciesRow.style.display = isGang ? 'none' : ''; +} + +// ============================================================================ +// NpcGeneratorDialog — FormApplication / ApplicationV2 adaptive dialog +// ============================================================================ + +function buildGeneratorClass() { + if (foundry.applications?.api?.ApplicationV2) { + const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; + + return class NpcGeneratorDialog extends HandlebarsApplicationMixin(ApplicationV2) { + + constructor(manager, opts = {}) { + super(opts); + this.manager = manager; + this._busy = false; + this._lastLog = []; + } + + static DEFAULT_OPTIONS = { + id: 'npc-generator-dialog', + tag: 'form', + classes: ['npc-portrait-pack', 'npc-generator'], + window: { + title: 'SLA Industries NPC Generator', + resizable: true, + }, + position: { width: 480, height: 'auto' }, + form: { + handler: NpcGeneratorDialog._onGenerate, + submitOnChange: false, + closeOnSubmit: false, + }, + }; + + static PARTS = { + form: { + template: `modules/${MODULE_ID}/templates/npc-generator.hbs`, + } + }; + + async _prepareContext(_opts) { + return { + archetypes: Object.entries(ARCHETYPES).map(([k, v]) => ({ value: k, label: v.label, icon: v.icon, description: v.description })), + species: SPECIES_OPTIONS, + log: this._lastLog, + }; + } + + _onRender(context, opts) { + super._onRender?.(context, opts); + const root = this.element; + + // Archetype card selection → update description + toggle gang-size row + root.querySelectorAll('[name="archetype"]').forEach(radio => { + radio.addEventListener('change', () => { + const key = radio.value; + const desc = ARCHETYPES[key]?.description ?? ''; + const el = root.querySelector('.archetype-description'); + if (el) el.textContent = desc; + // Toggle selected class + root.querySelectorAll('.archetype-card').forEach(c => c.classList.remove('selected')); + radio.closest('.archetype-card')?.classList.add('selected'); + // Show gang-size row only when gang is selected + _toggleGangRow(root, key); + }); + }); + + // Set initial state + const checked = root.querySelector('[name="archetype"]:checked'); + if (checked) _toggleGangRow(root, checked.value); + } + + static async _onGenerate(event, form, formData) { + if (this._busy) return; + this._busy = true; + this._lastLog = []; + + const data = foundry.utils.expandObject(formData.object); + const archetype = data.archetype ?? 'civilian'; + const species = data.species ?? 'random'; + const folderId = data.folderId || null; + const def = ARCHETYPES[archetype]; + + let created = []; + + if (def?.isGangGroup) { + // Street gang — batch creation of grunts + const gangSize = Math.max(2, Math.min(20, parseInt(data.gangSize ?? 5))); + created = await generateGangMembers(gangSize, this.manager, folderId); + for (const a of created) { + this._lastLog.push(`✓ ${a.name} (Gang Member)`); + } + } else { + // Individual NPC(s) + const count = Math.max(1, Math.min(10, parseInt(data.count ?? 1))); + for (let i = 0; i < count; i++) { + const actor = await generateNpc(archetype, species, this.manager, folderId); + if (actor) { + created.push(actor); + this._lastLog.push(`✓ Created: ${actor.name} (${def?.label ?? archetype})`); + } else { + this._lastLog.push(`✗ Failed to create NPC ${i + 1}`); + } + } + } + + this._busy = false; + ui.notifications.info(`NPC Generator: ${created.length} actor${created.length !== 1 ? 's' : ''} created.`); + await this.render(); + } + }; + } + + // Foundry v11/v12 — FormApplication fallback + return class NpcGeneratorDialog extends FormApplication { + + constructor(manager) { + super(); + this.manager = manager; + this._busy = false; + this._lastLog = []; + } + + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + id: 'npc-generator-dialog', + title: 'SLA Industries NPC Generator', + template: `modules/${MODULE_ID}/templates/npc-generator.hbs`, + width: 480, + height: 'auto', + resizable: true, + classes: ['npc-portrait-pack', 'npc-generator'], + }); + } + + getData() { + return { + archetypes: Object.entries(ARCHETYPES).map(([k, v]) => ({ value: k, label: v.label, icon: v.icon, description: v.description })), + species: SPECIES_OPTIONS, + log: this._lastLog, + }; + } + + activateListeners(html) { + super.activateListeners(html); + const root = html[0]; + + // Update description text + toggle gang row when archetype changes + html.find('[name="archetype"]').on('change', (ev) => { + const key = ev.currentTarget.value; + const desc = ARCHETYPES[key]?.description ?? ''; + html.find('.archetype-description').text(desc); + _toggleGangRow(root, key); + // Toggle selected class + html.find('.archetype-card').removeClass('selected'); + $(ev.currentTarget).closest('.archetype-card').addClass('selected'); + }); + + // Set initial state + const checked = root.querySelector('[name="archetype"]:checked'); + if (checked) _toggleGangRow(root, checked.value); + } + + async _updateObject(_event, formData) { + if (this._busy) return; + this._busy = true; + this._lastLog = []; + + const archetype = formData.archetype ?? 'civilian'; + const species = formData.species ?? 'random'; + const folderId = formData.folderId || null; + const def = ARCHETYPES[archetype]; + + let created = []; + + if (def?.isGangGroup) { + const gangSize = Math.max(2, Math.min(20, parseInt(formData.gangSize ?? 5))); + created = await generateGangMembers(gangSize, this.manager, folderId); + for (const a of created) { + this._lastLog.push(`✓ ${a.name} (Gang Member)`); + } + } else { + const count = Math.max(1, Math.min(10, parseInt(formData.count ?? 1))); + for (let i = 0; i < count; i++) { + const actor = await generateNpc(archetype, species, this.manager, folderId); + if (actor) { + created.push(actor); + this._lastLog.push(`✓ Created: ${actor.name} (${def?.label ?? archetype})`); + } else { + this._lastLog.push(`✗ Failed to create NPC ${i + 1}`); + } + } + } + + this._busy = false; + ui.notifications.info(`NPC Generator: ${created.length} actor${created.length !== 1 ? 's' : ''} created.`); + this.render(true); + } + }; +} + +export const NpcGeneratorDialog = buildGeneratorClass(); + +// Export archetype data for external use +export { ARCHETYPES, SPECIES_OPTIONS, generateNpc }; diff --git a/scripts/npc-portrait-pack.mjs b/scripts/npc-portrait-pack.mjs new file mode 100644 index 0000000..f8c0dbd --- /dev/null +++ b/scripts/npc-portrait-pack.mjs @@ -0,0 +1,138 @@ +// npc-portrait-pack.mjs — Main entry point +// Foundry VTT module: NPC Portrait Pack + Generator (SLA Industries edition) + +import { PortraitManager } from './portrait-manager.mjs'; +import { registerSettings, MODULE_ID } from './settings.mjs'; +import { PortraitPickerDialog } from './portrait-dialog.mjs'; +import { NpcGeneratorDialog } from './npc-generator.mjs'; + +// --------------------------------------------------------------------------- +// init — register settings before anything else +// --------------------------------------------------------------------------- +Hooks.once('init', () => { + registerSettings(); + console.log('NPC Portrait Pack | Initialised'); +}); + +// --------------------------------------------------------------------------- +// ready — load portrait index, expose public API +// --------------------------------------------------------------------------- +Hooks.once('ready', async () => { + const manager = new PortraitManager(); + await manager.loadIndex(); + + game.npcPortraitPack = { + getRandomPortrait: (opts = {}) => manager.getRandomPortrait(opts), + assignPortraitToActor: (actor, opts = {}) => manager.assignPortraitToActor(actor, opts), + filterPortraits: (opts = {}) => manager.filterPortraits(opts), + portraitOptsForActor: (actor) => manager.portraitOptsForActor(actor), + + openPickerDialog: (actor) => { + new PortraitPickerDialog(actor, manager).render(true); + }, + + openGeneratorDialog: () => { + new NpcGeneratorDialog(manager).render(true); + }, + + manager, + }; + + Hooks.callAll('npcPortraitPack.ready', game.npcPortraitPack); + console.log('NPC Portrait Pack | Ready → game.npcPortraitPack'); +}); + +// --------------------------------------------------------------------------- +// createActor hook — auto-assign portrait to new NPC actors +// --------------------------------------------------------------------------- +Hooks.on('createActor', async (actor, _options, userId) => { + if (game.userId !== userId) return; + if (!game.settings.get(MODULE_ID, 'autoAssign')) return; + if (actor.hasPlayerOwner || actor.type === 'ship') return; + + if (!game.npcPortraitPack) { + await new Promise(resolve => Hooks.once('npcPortraitPack.ready', resolve)); + } + + await game.npcPortraitPack.assignPortraitToActor(actor); +}); + +// --------------------------------------------------------------------------- +// Sheet header buttons — inject "Portrait" and "Generate NPC" buttons +// +// In Foundry v13, _getHeaderButtons() calls: +// Hooks.callAll(`get${this.constructor.name}HeaderButtons`, sheet, buttons) +// so `getActorSheetHeaderButtons` does NOT fire for subclasses. +// +// Instead we use renderActorSheet which fires for ALL ActorSheet subclasses, +// and inject our buttons directly into the DOM. +// --------------------------------------------------------------------------- +Hooks.on('renderActorSheet', (sheet, html, _data) => { + if (!game.user?.isGM) return; + const actor = sheet.actor ?? sheet.object; + if (!actor || actor.type === 'ship') return; + + // Resolve jQuery or plain DOM root + const root = html instanceof jQuery ? html[0] : html; + + // Find the header controls area — works for both appv1 and appv2 layouts + const headerBtns = root.querySelector('.window-header .header-button')?.parentElement + ?? root.querySelector('.header-controls') + ?? root.querySelector('.window-header'); + if (!headerBtns) return; + + // Avoid duplicate injection on re-renders + if (root.querySelector('.npp-portrait-btn')) return; + + // ── Portrait picker button ────────────────────────────────────── + const portraitBtn = document.createElement('a'); + portraitBtn.className = 'header-button control npp-portrait-btn'; + portraitBtn.title = 'Assign Portrait'; + portraitBtn.innerHTML = ''; + portraitBtn.addEventListener('click', (ev) => { + ev.preventDefault(); + if (!game.npcPortraitPack) return ui.notifications.warn('NPC Portrait Pack not ready.'); + game.npcPortraitPack.openPickerDialog(actor); + }); + + // ── Generator button (GM toolbar, not actor-specific) ────────── + // Only show on non-player-owned actors, or always on the toolbar + const genBtn = document.createElement('a'); + genBtn.className = 'header-button control npp-generate-btn'; + genBtn.title = 'Open NPC Generator'; + genBtn.innerHTML = ''; + genBtn.addEventListener('click', (ev) => { + ev.preventDefault(); + if (!game.npcPortraitPack) return ui.notifications.warn('NPC Portrait Pack not ready.'); + game.npcPortraitPack.openGeneratorDialog(); + }); + + // Prepend — so they appear before the default close/configure buttons + headerBtns.prepend(genBtn); + headerBtns.prepend(portraitBtn); +}); + +// --------------------------------------------------------------------------- +// Register a "Generate NPC" button in the Actors sidebar tab header +// --------------------------------------------------------------------------- +Hooks.on('renderActorDirectory', (_app, html) => { + if (!game.user?.isGM) return; + const root = html instanceof jQuery ? html[0] : html; + + if (root.querySelector('.npp-generate-sidebar-btn')) return; + + const header = root.querySelector('.directory-header .action-buttons') + ?? root.querySelector('.header-actions'); + if (!header) return; + + const btn = document.createElement('button'); + btn.className = 'npp-generate-sidebar-btn'; + btn.title = 'Generate SLA NPC'; + btn.innerHTML = ' Generate NPC'; + btn.addEventListener('click', () => { + if (!game.npcPortraitPack) return ui.notifications.warn('NPC Portrait Pack not ready.'); + game.npcPortraitPack.openGeneratorDialog(); + }); + + header.appendChild(btn); +}); diff --git a/scripts/portrait-dialog.mjs b/scripts/portrait-dialog.mjs new file mode 100644 index 0000000..eb5b99e --- /dev/null +++ b/scripts/portrait-dialog.mjs @@ -0,0 +1,177 @@ +// portrait-dialog.mjs — Portrait Picker dialog (Foundry v11/v12/v13 compatible) + +import { MODULE_ID } from './settings.mjs'; + +// The categories available in the picker UI, with display labels. +// Three broad aesthetic types: human | mutant | animal +const CATEGORIES = [ + { value: 'any', label: 'All Portraits (799)' }, + // ── Human-presenting ────────────────────────────── + { value: 'human_female', label: 'Human — Female (97)' }, + { value: 'human_male', label: 'Human — Male (98)' }, + { value: 'human_unknown', label: 'Human — Unknown gender (367)' }, + // ── Mutant / altered humans ──────────────────────── + { value: 'mutant_female', label: 'Mutant — Female (41)' }, + { value: 'mutant_male', label: 'Mutant — Male (41)' }, + { value: 'mutant_unknown', label: 'Mutant — Unknown gender (57)' }, + // ── Non-human / alien ───────────────────────────── + { value: 'animal_female', label: 'Animal / Alien — Female (38)' }, + { value: 'animal_male', label: 'Animal / Alien — Male (36)' }, + { value: 'animal_unknown', label: 'Animal / Alien — Unknown (24)' }, +]; + +// --------------------------------------------------------------------------- +// Foundry v13 uses ApplicationV2; v11/v12 uses FormApplication. +// We detect which is available and subclass accordingly. +// --------------------------------------------------------------------------- + +function buildPickerClass() { + // Foundry v13+ — ApplicationV2 available + if (foundry.applications?.api?.ApplicationV2) { + const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; + + return class PortraitPickerDialog extends HandlebarsApplicationMixin(ApplicationV2) { + + constructor(actor, manager, opts = {}) { + super(opts); + this.actor = actor; + this.manager = manager; + this._selectedCategory = game.settings.get(MODULE_ID, 'defaultCategory') ?? 'any'; + } + + static DEFAULT_OPTIONS = { + id: 'npc-portrait-picker', + tag: 'form', + classes: ['npc-portrait-pack', 'portrait-picker'], + window: { + title: 'NPC Portrait Picker', + resizable: true, + }, + position: { width: 720, height: 580 }, + actions: { + selectPortrait: PortraitPickerDialog._onSelectPortrait, + randomPortrait: PortraitPickerDialog._onRandomPortrait, + }, + }; + + static PARTS = { + form: { + template: `modules/${MODULE_ID}/templates/portrait-picker.hbs`, + } + }; + + async _prepareContext(_opts) { + const portraits = this.manager.filterPortraits({ category: this._selectedCategory }); + return { + actor: this.actor, + portraits, + categories: CATEGORIES, + selectedCategory: this._selectedCategory, + }; + } + + _onRender(context, opts) { + super._onRender?.(context, opts); + // Category dropdown change + this.element.querySelector('.category-filter')?.addEventListener('change', async (ev) => { + this._selectedCategory = ev.currentTarget.value; + await this.render(); + }); + } + + static async _onSelectPortrait(event, target) { + const path = target.dataset.path; + await this._applyPortrait(path); + } + + static async _onRandomPortrait(_event, _target) { + const portraits = this.manager.filterPortraits({ category: this._selectedCategory }); + if (!portraits.length) return; + const chosen = portraits[Math.floor(Math.random() * portraits.length)]; + await this._applyPortrait(chosen.path); + } + + async _applyPortrait(path) { + const setAvatar = game.settings.get(MODULE_ID, 'setAvatar'); + const updateData = { 'prototypeToken.texture.src': path }; + if (setAvatar) updateData.img = path; + await this.actor.update(updateData); + ui.notifications.info( + game.i18n.format('NPC_PORTRAIT_PACK.Notifications.Assigned', { actorName: this.actor.name }) + ); + this.close(); + } + }; + } + + // Foundry v11/v12 — FormApplication fallback + return class PortraitPickerDialog extends FormApplication { + + constructor(actor, manager) { + super(); + this.actor = actor; + this.manager = manager; + this._category = game.settings.get(MODULE_ID, 'defaultCategory') ?? 'any'; + } + + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + id: 'npc-portrait-picker', + title: 'NPC Portrait Picker', + template: `modules/${MODULE_ID}/templates/portrait-picker.hbs`, + width: 720, + height: 580, + resizable: true, + classes: ['npc-portrait-pack', 'portrait-picker'], + }); + } + + getData() { + const portraits = this.manager.filterPortraits({ category: this._category }); + return { + actor: this.actor, + portraits, + categories: CATEGORIES, + selectedCategory: this._category, + }; + } + + activateListeners(html) { + super.activateListeners(html); + + html.find('.portrait-thumb').on('click', async (ev) => { + const path = ev.currentTarget.dataset.path; + await this._applyPortrait(path); + }); + + html.find('.btn-random').on('click', async () => { + const portraits = this.manager.filterPortraits({ category: this._category }); + if (!portraits.length) return; + const chosen = portraits[Math.floor(Math.random() * portraits.length)]; + await this._applyPortrait(chosen.path); + }); + + html.find('.category-filter').on('change', (ev) => { + this._category = ev.currentTarget.value; + this.render(true); + }); + } + + async _applyPortrait(path) { + const setAvatar = game.settings.get(MODULE_ID, 'setAvatar'); + const updateData = { 'prototypeToken.texture.src': path }; + if (setAvatar) updateData.img = path; + await this.actor.update(updateData); + ui.notifications.info( + game.i18n.format('NPC_PORTRAIT_PACK.Notifications.Assigned', { actorName: this.actor.name }) + ); + this.close(); + } + + async _updateObject(_event, _formData) { + // Handled by activateListeners + } + }; +} + +export const PortraitPickerDialog = buildPickerClass(); diff --git a/scripts/portrait-manager.mjs b/scripts/portrait-manager.mjs new file mode 100644 index 0000000..2c0870c --- /dev/null +++ b/scripts/portrait-manager.mjs @@ -0,0 +1,259 @@ +/** + * portrait-manager.mjs + * Core portrait loading, filtering, and assignment logic. + * + * Portrait source : sla_images/portraits/backup_pics/ (799 portraits) + * Taxonomy (3 tiers): + * type → human | mutant | animal + * category → human_female | human_male | human_unknown + * mutant_female | mutant_male | mutant_unknown + * animal_female | animal_male | animal_unknown + * race → original SLA species (ebon, frother, shaktar, etc.) stored as tag + * + * SLA species → portrait type mapping: + * human, ebon, stormer → human + * frother → mutant + * shaktar, waister, wraith → animal + * creature / carrien (actor.type) → animal_unknown + * mutant / serial killer archetype → mutant_unknown + */ + +import { MODULE_ID } from './settings.mjs'; + +const INDEX_PATH = 'sla_images/portraits/backup_pics/index.json'; + +// ── Species → broad portrait type ──────────────────────────────────────────── + +const SPECIES_TO_TYPE = { + // Human-presenting + human: 'human', Human: 'human', + ebon: 'human', Ebon: 'human', + stormer: 'human', Stormer: 'human', + // Mutant / altered humans + frother: 'mutant', Frother: 'mutant', + // Non-human / alien + shaktar: 'animal', Shaktar: 'animal', + waister: 'animal', Waister: 'animal', + wraith: 'animal', Wraith: 'animal', + // prefixed variants + 'sla human': 'human', + 'sla ebon': 'human', + 'sla frother': 'mutant', + 'sla shaktar': 'animal', + 'sla stormer': 'human', + 'sla waister': 'animal', + 'sla wraith': 'animal', +}; + +// Default images — treated as "no portrait yet" +const DEFAULT_IMAGES = new Set([ + 'icons/svg/mystery-man.svg', + 'icons/svg/mystery-man-black.svg', + 'systems/sla-mothership/assets/icons/npc.png', + 'systems/sla-mothership/assets/icons/creature.png', + '', +]); + +export class PortraitManager { + + constructor() { + /** @type {Array<{id,path,category,type,race,gender,tags}>} */ + this.portraits = []; + this._loaded = false; + } + + // ── Loading ─────────────────────────────────────────────────────────────── + + async loadIndex() { + const stamp = Date.now(); + // Try with leading slash first (absolute from server root), then without (relative) + const urls = [`/${INDEX_PATH}?v=${stamp}`, `${INDEX_PATH}?v=${stamp}`]; + for (const url of urls) { + try { + const r = await fetch(url); + if (!r.ok) continue; + const data = await r.json(); + this.portraits = data.portraits ?? []; + this._loaded = true; + console.log(`NPC Portrait Pack | Loaded ${this.portraits.length} portraits (v${data.version}) from ${url}`); + return; + } catch (_) { + // try next url + } + } + console.error(`NPC Portrait Pack | Could not load index from ${INDEX_PATH}. Check the file exists in your FoundryVTT Data folder.`); + this.portraits = []; + } + + // ── Filtering ───────────────────────────────────────────────────────────── + + /** + * Filter portraits by options. + * @param {object} [opts] + * @param {string|string[]} [opts.category] e.g. 'human_female', ['animal_male','animal_female'] + * @param {string|string[]} [opts.type] broad type: 'human' | 'mutant' | 'animal' + * @param {string} [opts.gender] 'male' | 'female' — restricts within type + * @param {string[]} [opts.tags] extra tag filters + */ + filterPortraits({ category, type, gender, tags } = {}) { + let pool = [...this.portraits]; + + // Category filter (most specific) + if (category && category !== 'any') { + const cats = Array.isArray(category) ? category : [category]; + pool = pool.filter(p => cats.includes(p.category)); + } + + // Broad type filter + if (type && type !== 'any') { + const types = Array.isArray(type) ? type : [type]; + pool = pool.filter(p => types.includes(p.type)); + } + + // Gender filter — neutral portraits pass through regardless + if (gender && gender !== 'neutral') { + pool = pool.filter(p => p.gender === 'neutral' || p.gender === gender); + } + + // Tag filters + if (tags?.length) { + pool = pool.filter(p => tags.every(t => p.tags.includes(t))); + } + + return pool; + } + + // ── Selection ───────────────────────────────────────────────────────────── + + /** + * Pick one random portrait, falling back to the full library if the pool is empty. + * @returns {{ id, path, category, type, race, gender, tags } | null} + */ + getRandomPortrait(opts = {}) { + let pool = this.filterPortraits(opts); + + if (!pool.length && Object.keys(opts).some(k => opts[k])) { + // Widen to same broad type before falling back to everything + if (opts.category || opts.gender) { + const typeOpts = opts.type ? { type: opts.type } : {}; + pool = this.filterPortraits(typeOpts); + } + } + if (!pool.length) pool = this.portraits; + if (!pool.length) return null; + + return pool[Math.floor(Math.random() * pool.length)]; + } + + // ── Actor → portrait options ────────────────────────────────────────────── + + /** + * Derive best portrait filter for an actor based on type, species, gender. + * @param {Actor} actor + * @returns {{ category?: string|string[], type?: string, gender?: string } | null} + */ + portraitOptsForActor(actor) { + if (actor.type === 'ship') return null; + + // Creature-type actors (carrien, etc.) → animal + if (actor.type === 'creature') { + return { category: 'animal_unknown' }; + } + + const speciesRaw = ( + actor.system?.sla?.species?.value ?? + actor.system?.species?.value ?? '' + ).toLowerCase().trim(); + + const genderRaw = ( + actor.system?.sla?.gender?.value ?? + actor.system?.gender?.value ?? '' + ).toLowerCase().trim(); + + const broadType = SPECIES_TO_TYPE[speciesRaw] ?? 'human'; + const gender = genderRaw === 'female' ? 'female' + : genderRaw === 'male' ? 'male' + : null; + + if (gender) { + return { category: `${broadType}_${gender}`, gender }; + } + // No gender — use both gendered pools + unknown for this type + return { + category: [`${broadType}_male`, `${broadType}_female`, `${broadType}_unknown`], + }; + } + + // ── Assignment ──────────────────────────────────────────────────────────── + + /** + * Assign a portrait to an actor, respecting module settings. + * @param {Actor} actor + * @param {object} [overrideOpts] explicit filter opts (from generator / picker) + * @returns {Promise} chosen portrait entry + */ + async assignPortraitToActor(actor, overrideOpts = null) { + if (!actor) return null; + + if (game.settings.get(MODULE_ID, 'onlyIfDefaultImage')) { + const src = actor.prototypeToken?.texture?.src ?? actor.img ?? ''; + // Skip only if the actor already has one of OUR portraits assigned. + // Any other image (system defaults, blank, mystery-man, etc.) counts as "no portrait yet". + const alreadyHasOurPortrait = src && src.includes('backup_pics'); + if (alreadyHasOurPortrait) return null; + } + + let filterOpts; + if (overrideOpts) { + filterOpts = overrideOpts; + } else if (game.settings.get(MODULE_ID, 'matchSpecies')) { + filterOpts = this.portraitOptsForActor(actor); + if (!filterOpts) return null; + } else { + const cat = game.settings.get(MODULE_ID, 'defaultCategory'); + filterOpts = cat === 'any' ? {} : { category: cat }; + } + + const portrait = this.getRandomPortrait(filterOpts ?? {}); + if (!portrait) return null; + + const setAvatar = game.settings.get(MODULE_ID, 'setAvatar'); + const updateData = { 'prototypeToken.texture.src': portrait.path }; + if (setAvatar) updateData.img = portrait.path; + + await actor.update(updateData); + console.log(`NPC Portrait Pack | "${portrait.path}" → "${actor.name}" [${portrait.category}]`); + return portrait; + } + + // ── Category list for UI ────────────────────────────────────────────────── + + /** + * Returns sorted array of { key, label, count } for all categories. + */ + getCategoryList() { + const counts = {}; + for (const p of this.portraits) { + counts[p.category] = (counts[p.category] ?? 0) + 1; + } + // Totals per broad type + const typeTotals = { human: 0, mutant: 0, animal: 0 }; + for (const p of this.portraits) typeTotals[p.type] = (typeTotals[p.type] ?? 0) + 1; + + const LABELS = { + human_female: `Human — Female`, + human_male: `Human — Male`, + human_unknown: `Human — Unknown gender`, + mutant_female: `Mutant — Female`, + mutant_male: `Mutant — Male`, + mutant_unknown: `Mutant — Unknown gender`, + animal_female: `Animal / Alien — Female`, + animal_male: `Animal / Alien — Male`, + animal_unknown: `Animal / Alien — Unknown`, + }; + + return Object.entries(counts) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([key, count]) => ({ key, label: LABELS[key] ?? key, count })); + } +} diff --git a/scripts/settings.mjs b/scripts/settings.mjs new file mode 100644 index 0000000..de37072 --- /dev/null +++ b/scripts/settings.mjs @@ -0,0 +1,68 @@ +// settings.mjs — NPC Portrait Pack module settings + +export const MODULE_ID = 'npc-portrait-pack'; + +export function registerSettings() { + + // Auto-assign on NPC creation + game.settings.register(MODULE_ID, 'autoAssign', { + name: game.i18n.localize('NPC_PORTRAIT_PACK.Settings.AutoAssign.Name'), + hint: game.i18n.localize('NPC_PORTRAIT_PACK.Settings.AutoAssign.Hint'), + scope: 'world', + config: true, + type: Boolean, + default: true + }); + + // Match species/race when auto-assigning + game.settings.register(MODULE_ID, 'matchSpecies', { + name: game.i18n.localize('NPC_PORTRAIT_PACK.Settings.MatchSpecies.Name'), + hint: game.i18n.localize('NPC_PORTRAIT_PACK.Settings.MatchSpecies.Hint'), + scope: 'world', + config: true, + type: Boolean, + default: true + }); + + // Only assign if the actor currently has the default token/avatar image + game.settings.register(MODULE_ID, 'onlyIfDefaultImage', { + name: game.i18n.localize('NPC_PORTRAIT_PACK.Settings.OnlyIfDefaultImage.Name'), + hint: game.i18n.localize('NPC_PORTRAIT_PACK.Settings.OnlyIfDefaultImage.Hint'), + scope: 'world', + config: true, + type: Boolean, + default: true + }); + + // Set actor avatar image (not just token) + game.settings.register(MODULE_ID, 'setAvatar', { + name: game.i18n.localize('NPC_PORTRAIT_PACK.Settings.SetAvatar.Name'), + hint: game.i18n.localize('NPC_PORTRAIT_PACK.Settings.SetAvatar.Hint'), + scope: 'world', + config: true, + type: Boolean, + default: true + }); + + // Default category fallback when species matching is off + game.settings.register(MODULE_ID, 'defaultCategory', { + name: game.i18n.localize('NPC_PORTRAIT_PACK.Settings.DefaultCategory.Name'), + hint: game.i18n.localize('NPC_PORTRAIT_PACK.Settings.DefaultCategory.Hint'), + scope: 'world', + config: true, + type: String, + choices: { + 'any': 'Any — all 799 portraits', + 'human_female': 'Human — Female (97)', + 'human_male': 'Human — Male (98)', + 'human_unknown': 'Human — Unknown gender (367)', + 'mutant_female': 'Mutant — Female (41)', + 'mutant_male': 'Mutant — Male (41)', + 'mutant_unknown': 'Mutant — Unknown gender (57)', + 'animal_female': 'Animal / Alien — Female (38)', + 'animal_male': 'Animal / Alien — Male (36)', + 'animal_unknown': 'Animal / Alien — Unknown (24)', + }, + default: 'any' + }); +} diff --git a/styles/npc-portrait-pack.css b/styles/npc-portrait-pack.css new file mode 100644 index 0000000..80e8aed --- /dev/null +++ b/styles/npc-portrait-pack.css @@ -0,0 +1,302 @@ +/* npc-portrait-pack.css — Portrait Picker dialog styles */ + +/* ── Dialog container ──────────────────────────────────────────────── */ +.portrait-picker-container { + display: flex; + flex-direction: column; + height: 100%; + padding: 8px; + gap: 8px; + box-sizing: border-box; +} + +/* ── Toolbar (category filter + random button) ────────────────────── */ +.picker-toolbar { + display: flex; + align-items: center; + gap: 10px; + padding-bottom: 8px; + border-bottom: 1px solid #666; + flex-shrink: 0; +} + +.picker-toolbar label { + display: flex; + align-items: center; + gap: 6px; + font-size: 0.85rem; + font-weight: 600; + color: #ddd; +} + +.picker-toolbar .category-filter { + background: #1a1a1a; + color: #ddd; + border: 1px solid #555; + border-radius: 3px; + padding: 3px 6px; + font-size: 0.82rem; +} + +.picker-portrait-count { + margin-left: auto; + font-size: 0.75rem; + color: #888; + font-style: italic; +} + +/* ── Portrait grid ──────────────────────────────────────────────────── */ +.portrait-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); + gap: 6px; + overflow-y: auto; + flex: 1; + padding: 4px 2px; +} + +/* ── Individual portrait tile ───────────────────────────────────────── */ +.portrait-item { + border: 2px solid transparent; + border-radius: 4px; + cursor: pointer; + transition: border-color 0.12s ease, transform 0.1s ease; + background: #111; +} + +.portrait-item:hover { + border-color: #7aa; + transform: scale(1.06); + z-index: 2; + position: relative; +} + +.portrait-thumb { + width: 100%; + aspect-ratio: 1 / 1; + object-fit: cover; + border-radius: 2px; + display: block; + filter: contrast(1.05); +} + +/* ── Buttons ─────────────────────────────────────────────────────────── */ +.btn-random { + padding: 4px 12px; + background: #2a3a4a; + color: #cde; + border: 1px solid #557; + border-radius: 4px; + cursor: pointer; + font-size: 0.82rem; + font-weight: 600; + white-space: nowrap; +} + +.btn-random:hover { + background: #3a4a5a; + border-color: #88a; + color: #fff; +} + +/* ── Empty state ──────────────────────────────────────────────────────── */ +.no-portraits { + grid-column: 1 / -1; + text-align: center; + color: #777; + padding: 28px 8px; + font-style: italic; +} + +/* ── Sheet header buttons (injected via renderActorSheet) ────────────── */ +.npp-portrait-btn i, +.npp-generate-btn i { + margin-right: 3px; +} + +/* ── Actors sidebar generate button ──────────────────────────────────── */ +.npp-generate-sidebar-btn { + margin-top: 4px; + width: 100%; + padding: 4px 8px; + background: #2a3040; + color: #aac; + border: 1px solid #445; + border-radius: 3px; + cursor: pointer; + font-size: 0.8rem; + font-weight: 600; +} +.npp-generate-sidebar-btn:hover { + background: #3a4050; + color: #ddf; +} + +/* ═══════════════════════════════════════════════════════════════════════ + NPC GENERATOR DIALOG + ═══════════════════════════════════════════════════════════════════════ */ + +.npc-generator-container { + display: flex; + flex-direction: column; + gap: 12px; + padding: 10px; +} + +/* ── Section blocks ──────────────────────────────────────────────────── */ +.gen-section { + display: flex; + flex-direction: column; + gap: 6px; +} + +.gen-row { + flex-direction: row; + gap: 12px; +} + +.gen-field { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; +} + +.gen-field-sm { + flex: 0 0 80px; +} + +.gen-label { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + color: #aaa; +} + +/* ── Archetype cards grid ─────────────────────────────────────────────── */ +.archetype-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); + gap: 6px; +} + +.archetype-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + padding: 8px 4px 6px; + border: 1px solid #444; + border-radius: 5px; + background: #1c1c1c; + cursor: pointer; + font-size: 0.78rem; + color: #bbb; + transition: border-color 0.1s, background 0.1s; + text-align: center; + line-height: 1.2; +} + +.archetype-card input[type="radio"] { + display: none; +} + +.archetype-card i { + font-size: 1.2rem; + color: #88aacc; +} + +.archetype-card:has(input:checked), +.archetype-card.selected { + border-color: #6699bb; + background: #1e2a36; + color: #ddf; +} + +.archetype-card:hover { + border-color: #668; + background: #202030; +} + +.gen-hint { + font-size: 0.7rem; + font-weight: 400; + color: #666; + font-style: italic; + text-transform: none; + letter-spacing: 0; +} + +.archetype-description { + font-size: 0.78rem; + color: #888; + font-style: italic; + margin: 0; + min-height: 2.2em; +} + +/* ── Inputs ───────────────────────────────────────────────────────────── */ +.npc-generator select, +.npc-generator input[type="number"], +.npc-generator input[type="text"] { + background: #1a1a1a; + color: #ddd; + border: 1px solid #555; + border-radius: 3px; + padding: 4px 6px; + font-size: 0.85rem; + width: 100%; + box-sizing: border-box; +} + +/* ── Generate button ─────────────────────────────────────────────────── */ +.gen-actions { + flex-direction: row; + justify-content: flex-end; +} + +.btn-generate { + padding: 6px 18px; + background: #1e3a28; + color: #8dcc8d; + border: 1px solid #3a6a3a; + border-radius: 4px; + cursor: pointer; + font-size: 0.88rem; + font-weight: 700; + letter-spacing: 0.04em; +} + +.btn-generate:hover { + background: #264a30; + border-color: #5a9a5a; + color: #aeeaae; +} + +/* ── Output log ──────────────────────────────────────────────────────── */ +.gen-log { + border-top: 1px solid #333; + padding-top: 10px; +} + +.gen-log-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 3px; +} + +.gen-log-list li { + font-size: 0.78rem; + color: #8dcc8d; + padding: 2px 4px; + background: rgba(0,80,0,0.12); + border-radius: 2px; +} + +.gen-log-list li:not([class]) { + color: #cc8888; +} diff --git a/templates/npc-generator.hbs b/templates/npc-generator.hbs new file mode 100644 index 0000000..22a5e55 --- /dev/null +++ b/templates/npc-generator.hbs @@ -0,0 +1,68 @@ +
+ + {{!-- Archetype selection --}} +
+ +
+ {{#each archetypes}} + + {{/each}} +
+

An ordinary denizen of the World of Progress — corp worker, street person, or shopkeeper.

+
+ + {{!-- Species + Count (hidden when Street Gang is selected) --}} +
+
+ + +
+ +
+ + +
+
+ + {{!-- Gang size (only visible when Street Gang is selected) --}} + + + {{!-- Optional folder --}} +
+ + +
+ + {{!-- Generate button --}} +
+ +
+ + {{!-- Output log --}} + {{#if log.length}} +
+ +
    + {{#each log}} +
  • {{this}}
  • + {{/each}} +
+
+ {{/if}} + +
diff --git a/templates/portrait-picker.hbs b/templates/portrait-picker.hbs new file mode 100644 index 0000000..b816ab1 --- /dev/null +++ b/templates/portrait-picker.hbs @@ -0,0 +1,41 @@ +
+ + {{!-- Toolbar --}} +
+ + + + + {{portraits.length}} portraits +
+ + {{!-- Portrait grid --}} +
+ {{#each portraits}} +
+ {{this.id}} +
+ {{else}} +

No portraits found for this category.

+ {{/each}} +
+ +