├── .gitattributes ├── .gitignore ├── Readme.md ├── external content ├── Champd Up Sounds │ ├── completelyforgettable.ogg │ ├── completelyforgettable_response.ogg │ ├── muteyouraudio.ogg │ ├── muteyouraudio_response.ogg │ ├── stayawayfromme.ogg │ ├── stayawayfromme_response.ogg │ ├── yogsothoth.ogg │ └── yogsothoth_response.ogg ├── Quiplash 3 Sounds │ ├── Tyrotoxism.ogg │ ├── heallyeah_response.ogg │ ├── ididntblank.ogg │ ├── igotnewsforya_response.ogg │ ├── sexi_response.ogg │ ├── shortodyssey.ogg │ ├── yeahobviously_response.ogg │ └── zombie.ogg └── Talking Points Pictures │ ├── Are you alright.jpg │ ├── Not Milky Way.jpg │ ├── face.jpg │ ├── job well done.jpg │ └── younglings.jpg ├── jppc.py ├── sample_custom_content_to_import.json └── screenshots ├── Quiplash3.PNG └── TalkingPoints2.PNG /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | __pycache__/jppc.cpython-39.pyc 3 | build/jppc/Analysis-00.toc 4 | build/jppc/base_library.zip 5 | build/jppc/EXE-00.toc 6 | build/jppc/jppc.exe.manifest 7 | build/jppc/PKG-00.pkg 8 | build/jppc/PKG-00.toc 9 | build/jppc/PYZ-00.pyz 10 | build/jppc/PYZ-00.toc 11 | build/jppc/Tree-00.toc 12 | build/jppc/Tree-01.toc 13 | build/jppc/warn-jppc.txt 14 | build/jppc/xref-jppc.html 15 | dist/jppc.exe 16 | jppc.spec 17 | dist/Jackbox Party Pack Custom.exe 18 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # What is this? 2 | 3 | It's a program for adding your own content (stuff like prompts, certain sound files, etc.) to the Jackbox Party Pack 7. 4 | 5 | WARNING: I've really only tested this on Windows, it might not work on other operating systems. 6 | 7 | You may also want to keep a backup of the Jackbox Party Pack 7's files if you're not using Steam, 8 | otherwise you're going to have to uninstall and reinstall your entire game if something goes wrong. 9 | 10 | ## Some screenshots of custom content in the Jackbox Party Pack 7: 11 | 12 | ![Talking Points](https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/main/screenshots/TalkingPoints2.PNG) 13 | ![Quiplash3](https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/main/screenshots/Quiplash3.PNG) 14 | 15 | Check out the video [demo](https://youtu.be/4YO2SM21eIo). 16 | 17 | # Install instructions 18 | 19 | ## If you're running on Windows: 20 | - Go to the [releases](https://github.com/ambiguousname/jackbox-custom-content/releases) page and download the .ZIP file. 21 | - Extract the contents of the .ZIP file into the "games" directory of your Jackbox Party Pack 7 install folder. (For Steam on Windows that's typically C:\Program Files (x86)\Steam\steamapps\common\The Jackbox Party Pack 7\games) 22 | - Run "Jackbox Party Pack Custom.exe". 23 | - Please read the rest of this Readme for clearer instructions if you ever get confused. 24 | 25 | ## Otherwise: 26 | - [Install Python 3.9+](https://www.python.org/) 27 | - Clone this repository 28 | - Move "jppc.py" to the "games" directory of your Jackbox Party Pack 7 install folder (You can look up where the game is stored yourself, you're very smart). 29 | - Run jppc.py in the terminal of your choice. 30 | - Please read the rest of this Readme for clearer instructions if you ever get confused. 31 | 32 | ## To add the sample custom content: 33 | Before installing, the sample custom content does contain some adult... jokes (I wrote them very late in the evening, the level of comedy will vary significantly)? So be warned if you want to import it. 34 | 35 | - Add Jackbox Party Pack Custom.exe or jppc.py to the "games" directory of the Jackbox Party Pack 7 folder. 36 | - Run jppc.py or Jackbox Party Pack Custom.exe 37 | - Select the "Import/Reimport Content" option. 38 | - Select the "sample_custom_content.json" file. 39 | - Click "Import". 40 | - To view your new content, click on "View/Edit Content", then click "All Games". 41 | 42 | Just so you know, the sample custom content is meant to be played with other content in the mix (there isn't enough sample custom content to last a full game with 8 players). While you can use the "Only Custom" menu option to get rid of the game's own content files and play with only the sample content, it's not recommended. 43 | 44 | ## Common things to watch out for: 45 | ### Tags 46 | 47 | The \ tag is used in prompts to signify a fill in the blank question. Like with `My is too big!`. 48 | 49 | The \ tag is used to signify that this prompt uses the name of some random player in the game, like with `Hey, has a problem of too much .`. To use the \ tag, you should also check the checkbox that says "Includes Player Name", otherwise the \ tag won't work. 50 | 51 | ### Custom Files 52 | 53 | You'll often see a "Browse" button for some games, which allows you to add your own custom files (like .OGG or .JPG files) for the game. These are entirely optional, but if you're going to make a custom file, please save your custom files into a folder called "external content" located in the same file as jppc.py or Jackbox Content Custom.exe. This way, if you're going to share your content with someone else, the software will recognize that you want this content to be shared with other people. Just make sure to share both "custom_content.json" and the "external content" folder (and make sure they're in the same location). 54 | 55 | If you're confused about anything else, please read this Readme. Please. 56 | 57 | # Features 58 | 59 | ## Editing content 60 | 61 | If you want to change specific parts of your content or delete content, you're going to want to edit that content in the View/Edit Content option. You should note that you can select multiple pieces of content to edit, view, or delete. 62 | 63 | ### The "Make New Content" Button 64 | 65 | If you've made changes to content, but you want to save those changes to a new piece of content (rather than editing the existing piece of content), push this button to make your changes into a new piece of content. 66 | 67 | ## Importing content and/or Sharing Content 68 | 69 | If you copy the external_content.json file that appears in the same folder as jppc.py or Jackbox Party Pack Custom.exe and send those files to someone else, they can then import that content, using the "Import/Reimport Content" option. 70 | 71 | The import content feature will only import certain custom files like .JPGs or .OGGs if those files are stored in the folder ./external content/, if that folder is in the same location as jppc.py or Jackbox Party Pack Custom.exe. 72 | 73 | Additionally, you may have to edit imported content for Champ'd Up, since certain prompts are connected to other prompts with specific IDs, which may be changed on import. 74 | 75 | ## Using only custom content in a game 76 | 77 | This is not at all recommended. If you have less than a certain amount of content for the game to pull from, the game will not continue. It's better to mix in your custom content with the existing content. If you still want to only use custom content for your game, you can use the "Only Use Custom Content" option from the main menu to delete all existing game content. 78 | 79 | This option isn't recommended for any game unless that game has at least the number of content described here: 80 | - Blather 'Round - At least 36 Words. 12 Words of "easy" difficulty, 12 of "medium" difficulty, and 12 of "hard" difficulty. 81 | - Champ'd Up - At least 8 Round 1 Prompts, 8 Round 2 Prompts, 8 Round 2.5 Prompts 82 | - Quiplash 3 - At least 8 Round 1 Prompts, 8 Round 2 Prompts, 1 Round 3 Prompt 83 | - Talking Points - At least 24 Prompts, 24 Pictures, 24 Slide Transitions 84 | 85 | # Potential Questions/Problems 86 | 87 | ## Doesn't Quiplash 3 already have a way for you to make your own questions? 88 | 89 | Yes, but there are a few differences. For one: making your own "episodes" in Quiplash 3 means you have to manually select them. If you add custom questions using this program, they'll get inserted into the normal rotation of questions. For two: using this program will allow you to add more stuff not available in Quiplash 3's "episodes" feature, like audio files to read the questions and custom responses to certain answers. And this program can be used for more games than Quiplash 3. 90 | 91 | ## I accidentally added content multiple times, can I remove it? 92 | 93 | There's a delete option in "View/Edit Content". You can select multiple pieces of content to delete. 94 | 95 | ## HELP, EVERYTHING IS BROKEN AND/OR I REMOVED ALL NON-CUSTOM CONTENT AND CAN'T GET IT BACK 96 | 97 | If you're using Steam, go to the Jackbox Party Pack 7 in your Steam Library. Right click on the game's icon or name, click "Properties". 98 | In the popup window, click on "Local Files". Then click "Verify integrity of game files..." That should fix everything. 99 | 100 | ### Important note if you've clicked "verify integrity of game files..." 101 | 102 | That means all your custom prompts have been removed from the game. To get your custom prompts back, you'll have to follow a couple of steps. 103 | 1. Use the "Import/Reimport" option available in the menu 104 | 2. Select "custom_content.json" from the file browser. 105 | 3. Import. 106 | 107 | ## EVERYTHING'S STILL BROKEN 108 | 109 | Uninstall and reinstall the Jackbox Party Pack 7 110 | 111 | ## NOPE, IT STILL DOESN'T WORK 112 | 113 | Delete everything from your Jackbox Party Pack 7/games folder, then find the "Verify integrity of game files..." button and click it. 114 | 115 | ## The .EXE file is way too slow 116 | 117 | If the .EXE is too slow for you, you can just follow the steps for cloning the repository and using the .PY file. 118 | 119 | ## I don't have Windows, and I don't want to install Python 120 | 121 | As of right now, you're just going to have to install Python and install jppc.py on your OS of choice. I'm using PyInstaller to compile my code, and PyInstaller is not able to cross-compile. 122 | 123 | ## Why does this program sometimes use weird names for each game/content? 124 | 125 | I have here a handy conversion for the games and their weird names: 126 | - BlankyBlank - Blather 'Round 127 | - BlankyBlankPasswords - Word 128 | - BlankyBlankSentenceStructures - Category 129 | - BlankyBlankWordLists - Descriptor 130 | - JackboxTalks - Talking Points 131 | - JackboxTalksPicture - Picture 132 | - JackboxTalksTitle - Prompt 133 | - JackboxTalksSignpost - Slide Transition 134 | - Quiplash3 - Quiplash 3 135 | - Quiplash3Round1Question - Round 1 Question 136 | - Quiplash3Round2Question - Round 2 Question 137 | - Quiplash3FinalQuestion - Final Round Question 138 | - Quiplash3SafetyQuips - Safety Quips 139 | - World Champions - Champ'd Up 140 | - WorldChampionsRound - Round 1 141 | - WorldChampionsRoundSecondHalfA - Round 2 142 | - WorldChampionsRoundSecondHalfB - Round 2.5 143 | 144 | The program does this because that's what the folders for each game are called. 145 | 146 | ## Why isn't Devils and the Details included on that list? 147 | 148 | An excellent question. For now, I'm not going to bother supporting custom Devils and the Details content for a few reasons: 149 | 1. The Devils and the Details' game files have a different file structure compared to every other game in the party pack, meaning it would be harder for me to add support 150 | 2. Those game files use a language called "EVERYDASIC" (similar to BASIC, ha ha, get it?), which I'm going to have to deconstruct and fiddle around with if I want to add support. 151 | 3. I don't think the game is that great anyway, and so all that effort for Devils and the Details doesn't seem worth it. 152 | 153 | # Custom content guides 154 | 155 | ## Making custom content for Blather Round 156 | 157 | Blather 'Round is one of the most customizable games for the Jackbox Party Pack 7. As such, it has a lot of confusing content options. Here's a description of what each content means, along with some descriptions for the options: 158 | 159 | ### Word 160 | The word that the player is trying to guess. 161 | #### Word/Phrase Category 162 | The category to describe the word/phrase. Default options are person, place, thing, or story. If you want to add your own broad category, see Category. 163 | #### Subcategory 164 | You can put anything you want here. Just add one word that adds a little bit more detail than the previous category (e.g., `tv` for `Yu Gi Oh!`, `athlete` for `LeBron James`, `animal` for `Walrus`). You should use an existing subcategory (See [the wiki](https://github.com/ambiguousname/jackbox-custom-content/wiki/Possible-Blather-Round-Subcategories-(Sorted-by-category))). If you're going to make up your own subcategory, please see Descriptor for making your own descriptive sentences. 165 | #### Difficulty 166 | I'm pretty sure you can put whatever you want, but it's recommended to put `easy` for things that are fairly common knowledge (e.g., Australia, Office Space), `medium` for things that require more specific knowledge (e.g., Walrus, Marianas Trench, Les Mis), and `hard` for things that require very specific knowledge (e.g., Diff'rent Strokes, Mr. Snuffleupagus) 167 | #### Forbidden Words 168 | Hardly ever used, but if you have some common words that occur in your word/phrase or some really good descriptors (Like `murder` in `Murder She Wrote` or `big` and `dude` in `Big Lebowsky`), then you should put in those words here. 169 | #### Tailored Words 170 | Words that are tailor made to more accurately describe the word/phrase. First describe the descriptor (put into brackets: \), then the specific word (separate by |, so: `|word`). What are the categories/words? Well, you can make your own in the Descriptor menu. If you want to use pre-existing words, search [the wiki](https://github.com/ambiguousname/jackbox-custom-content/wiki/Blather-Round-Desciptor-Words-List). You should see each descriptor (listed under `name`), along with a list of words to match that descriptor (for instance, if I had `Pompeii`, I would write `|sad||structure||land||firey||tourism||ruin`, etc.) 171 | 172 | ### Category 173 | A *broad* category meant to describe the general idea of a word (ideally person/place/thing/story work well, so making a new category isn't recommended) 174 | #### Structures 175 | The sentence structures used to give hints about what the thing is about. Use \ tags (e.g., \, \) for each thing you have to fill in the blank for (again, go to [the wiki](https://github.com/ambiguousname/jackbox-custom-content/wiki/Blather-Round-Desciptor-Words-List) to see the words you can use, or add your own with Descriptor). Separate each entry by |. 176 | 177 | ### Descriptor 178 | You have three options: Describing Adjectives/Nouns/Verbs to apply to a category, sentences to respond to other people's guesses (like `It's very similar to ____!`), or descriptor words meant for \ tags (to be used in the Tailored Words section for a Word). The steps for making each are similar. 179 | #### Descriptor name 180 | How you name the descriptor will (I think) determine how that descriptor is used. 181 | - If I'm making a specific group of words (adjectives, nouns, or verbs) that pair with a category, I'd name the Descriptor `CATEGORY-VERB/ADJECTIVE/NOUN-SIMPLE/COMPLEX`. Where you write in the category name, whether you're using a verb, adjective, or noun, and whether the list of words is simple or complex. Something is considered `complex` if it has relatively simple words (I trust you to use your own judgement here). So if I were making a list of verbs that matched with category `story` with verbs like `runs`, `eats`, `lives with`, etc., I'd call it `story-verb-simple` 182 | - If I'm making a responding sentence to a subcategory, I name it: `response-sentence-CATEGORY-SUBCATEGORY`. You can remove the `-SUBCATEGORY` if you want to make a responding sentence to an overall category. So for instance, if I wanted to list possible responding sentences to something that has a category of `place` and `tv`, I'd write `response-sentence-place-tv`. 183 | - If I'm making a descriptor words for a \ tag (to be used by Category and Word content), I'd call it whatever I'd like (as long as it's hyphenated). So if I were to make a bunch of words describing odors I'd call it `smells-simple`, or something like that. 184 | #### Words List 185 | The list of words (or sentences) that you're using for the Descriptor. If you're writing a list of words, you can use \ tags to refer to other descriptors. Separate each word/sentence with |. If you consider a word or sentence to be essential to a descriptor, add a `T|` in front to signify that the word/sentence is essential: 186 | - If I'm writing something for `story-verb-simple`, I write something like: `runs|eats|lives with|T|discovers|T|learns`, etc. 187 | - If I'm writing something for `response-sentence-place-tv`, I'd write something like: `T|It's something like|T|It's a fictional version of|T|It reminds me of`, etc. 188 | - If I'm writing something for `smells-simple`, I'd write something like `gross||nasty|lemony` 189 | #### Max Choices 190 | If a player is making a selection on what words to choose, is there a set limit to how much they get to pick? (Please write something like 1, 2, or 3) 191 | - For something like `story-verb-simple`, you should set this to 1, 2, or 3 since you're probably going to use a verb once in a sentence (1), an adjective maybe three times (3), and a noun maybe twice (2). 192 | - For something like `response-sentence-place-tv`, set this to 1, since you're only going to pick one sentence. 193 | - For something like `smells-simple`, don't set this at all, since the game will automatically decide a limit for descriptors regarding \ tags. 194 | #### Placeholder text 195 | Generally, the placeholder text used when you can't get a sentence or a word there. Usually, it's something like `blank` (for non plural words), `blanks` (for plural words), and `blanky` (for sentences). 196 | - For `story-verb-simple`, the placeholder would be `blanks` (since almost every word/phrase is plural) 197 | - For `response-sentence-place-tv`, the placeholder would be `blanky` (since everything in the words list is a sentence) 198 | - For `smells-simple`, the placeholder would be `blank` (since every word is singular) 199 | 200 | ## Making custom responses to specific text for Quiplash 3: 201 | 202 | So, you may notice that in Quiplash 3 the announcer will sometimes react to a specific prompt. You can do this too! 203 | 204 | This only works for Round 1 and 2 questions. You can't have custom responses for Final Round questions. 205 | 206 | Let's say you have a prompt like: 207 | 208 | `Oh no, my dog ate my !` 209 | 210 | And you want a specific response if someone says "homework". 211 | 212 | In the "What to filter field", you'd put: 213 | 214 | ` Homework| homework|hw| hw` 215 | 216 | You should already know that Jackbox uses tags like \ and \ for questions, and so for their responses to specific answers, 217 | they use the tags \ (Like "the", "a", "an", "a massive", "a lot of", etc.), \ (Like "I", "My", "His", "Her", "I've got", "this", "that", etc.), and (Like "having a", "craving a", "needing a", "downing a", "guzzling"). To see what kinds of answers tags like \ will give, check [the wiki](https://github.com/ambiguousname/jackbox-custom-content/wiki). You should also separate possible answers by a "|" sign. 218 | 219 | You should also try to anticipate alternate answers, like abbreviations or misspellings. Let's look at another example. 220 | 221 | For the question "What skin tags probably taste like", Jackbox has: `
chicken | chicken|
chiken | chiken|
chikin | chikin` as a filter, trying to detect things like "A chicken", "chicken", "A chiken", "chiken", etc. 222 | 223 | Note the spaces in between the "|" signs in the example above. It's just a formatting thing, I don't think it really matters. You can add spaces only if you want to. 224 | 225 | Once you've added the filtering, then you can add your response audio as a .ogg file. Jackbox also requires a transcript of your response (I think for captioning purposes), so you should write out what you've said in the "Transcript of your response: " field. -------------------------------------------------------------------------------- /external content/Champd Up Sounds/completelyforgettable.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Champd Up Sounds/completelyforgettable.ogg -------------------------------------------------------------------------------- /external content/Champd Up Sounds/completelyforgettable_response.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Champd Up Sounds/completelyforgettable_response.ogg -------------------------------------------------------------------------------- /external content/Champd Up Sounds/muteyouraudio.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Champd Up Sounds/muteyouraudio.ogg -------------------------------------------------------------------------------- /external content/Champd Up Sounds/muteyouraudio_response.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Champd Up Sounds/muteyouraudio_response.ogg -------------------------------------------------------------------------------- /external content/Champd Up Sounds/stayawayfromme.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Champd Up Sounds/stayawayfromme.ogg -------------------------------------------------------------------------------- /external content/Champd Up Sounds/stayawayfromme_response.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Champd Up Sounds/stayawayfromme_response.ogg -------------------------------------------------------------------------------- /external content/Champd Up Sounds/yogsothoth.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Champd Up Sounds/yogsothoth.ogg -------------------------------------------------------------------------------- /external content/Champd Up Sounds/yogsothoth_response.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Champd Up Sounds/yogsothoth_response.ogg -------------------------------------------------------------------------------- /external content/Quiplash 3 Sounds/Tyrotoxism.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Quiplash 3 Sounds/Tyrotoxism.ogg -------------------------------------------------------------------------------- /external content/Quiplash 3 Sounds/heallyeah_response.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Quiplash 3 Sounds/heallyeah_response.ogg -------------------------------------------------------------------------------- /external content/Quiplash 3 Sounds/ididntblank.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Quiplash 3 Sounds/ididntblank.ogg -------------------------------------------------------------------------------- /external content/Quiplash 3 Sounds/igotnewsforya_response.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Quiplash 3 Sounds/igotnewsforya_response.ogg -------------------------------------------------------------------------------- /external content/Quiplash 3 Sounds/sexi_response.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Quiplash 3 Sounds/sexi_response.ogg -------------------------------------------------------------------------------- /external content/Quiplash 3 Sounds/shortodyssey.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Quiplash 3 Sounds/shortodyssey.ogg -------------------------------------------------------------------------------- /external content/Quiplash 3 Sounds/yeahobviously_response.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Quiplash 3 Sounds/yeahobviously_response.ogg -------------------------------------------------------------------------------- /external content/Quiplash 3 Sounds/zombie.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Quiplash 3 Sounds/zombie.ogg -------------------------------------------------------------------------------- /external content/Talking Points Pictures/Are you alright.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Talking Points Pictures/Are you alright.jpg -------------------------------------------------------------------------------- /external content/Talking Points Pictures/Not Milky Way.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Talking Points Pictures/Not Milky Way.jpg -------------------------------------------------------------------------------- /external content/Talking Points Pictures/face.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Talking Points Pictures/face.jpg -------------------------------------------------------------------------------- /external content/Talking Points Pictures/job well done.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Talking Points Pictures/job well done.jpg -------------------------------------------------------------------------------- /external content/Talking Points Pictures/younglings.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/external content/Talking Points Pictures/younglings.jpg -------------------------------------------------------------------------------- /jppc.py: -------------------------------------------------------------------------------- 1 | import PySimpleGUI as sg 2 | import json 3 | import os 4 | from shutil import copyfile, rmtree 5 | 6 | def id_gen(values): #id_gen needs a values dict to work with 7 | ids = None #Start IDs from 100k (to make it distingusihable from other IDs), go from there. 8 | id_dict = None 9 | if os.path.exists("./custom_content.json"): 10 | ids = open("./custom_content.json", 'r+') 11 | id_dict = json.load(ids) 12 | else: 13 | ids = open("./custom_content.json", 'w') 14 | id_dict = {} 15 | 16 | _id = None 17 | if type(id_dict) == None: 18 | _id = "100000" 19 | else: 20 | _id = str(100000 + len(id_dict.keys())) 21 | values.update({"id": _id}) #Need to store the id twice so that things can work. The .jet files need a reference to the id. 22 | id_dict[_id] = {"id": _id, "values": values} 23 | ids.seek(0) 24 | ids.truncate() 25 | new_json = json.dumps(id_dict) 26 | ids.write(new_json) 27 | ids.close() 28 | return _id 29 | 30 | class CustomContentWindow(object): 31 | 32 | def __init__(self, *args, **kwargs): 33 | self.window_layout = args[3] 34 | self.game = args[0] 35 | self.content_type = args[1] 36 | self.descriptor_text_name = args[2] 37 | 38 | def create_content(self, values, _id=None, window_suppress=False): 39 | new_content = CustomContent(values, self.game, self.content_type, values[self.descriptor_text_name], id=None if _id == None else _id) 40 | new_content.save_to_custom_content() 41 | for content in self.window_layout["content_list"]: 42 | if content["type"] == "json": 43 | new_content.write_to_json(None if not "path" in content else content["path"], False if not "delete" in content else content["delete"]) 44 | if content["type"] == "files": 45 | kwargs = content["files"]["kwargs"] 46 | args = content["files"]["args"] 47 | new_content.add_custom_files(*args, **kwargs) 48 | if content["type"] == "CustomData": 49 | data = content["func"](new_content.values) 50 | kwargs = content["kwargs"] 51 | new_content.add_custom_files(data, **kwargs) 52 | if window_suppress == False: 53 | sg.Popup("Content Created, ID: " + new_content.id) 54 | return new_content.id 55 | 56 | def create_window(self, *args, **kwargs): 57 | existing_data = None 58 | if "existing_data" in kwargs and kwargs["existing_data"] != None: 59 | existing_data = kwargs["existing_data"] 60 | if "import_filter" in self.window_layout: 61 | existing_data = self.window_layout["import_filter"](kwargs["existing_data"]) 62 | if self.window_layout and "layout_list" in self.window_layout: 63 | self.file_browse_keys = [] 64 | layout = [] 65 | for item in self.window_layout["layout_list"]: 66 | layout_item = [] 67 | if "text" in item: 68 | layout_item.append(sg.Text(item["text"])) 69 | if "input" in item: 70 | for index, input_type in enumerate(item["input"]): 71 | new_kwargs = {} 72 | if "kwargs" in input_type: 73 | new_kwargs = input_type["kwargs"].copy() 74 | for new_kwarg in new_kwargs: 75 | if (type(new_kwargs[new_kwarg]) == str and "existing_data" in new_kwargs[new_kwarg]) or input_type["type"] == sg.Checkbox: 76 | if existing_data != None: 77 | if type(new_kwargs[new_kwarg]) == str and len(new_kwargs[new_kwarg].split("|")) > 1: 78 | new_kwargs[new_kwarg] = existing_data[new_kwargs[new_kwarg].split("|")[1]] 79 | else: 80 | new_kwargs[new_kwarg] = existing_data[input_type["param_name"]] 81 | else: 82 | new_kwargs[new_kwarg] = new_kwargs["regular_default"] 83 | if "regular_default" in new_kwargs: 84 | new_kwargs.pop("regular_default") 85 | if input_type["type"] == sg.FileBrowse: 86 | self.file_browse_keys.append(item["input"][index - 1]["param_name"]) 87 | exclude = [sg.FileBrowse, sg.Checkbox] 88 | new_input = input_type["type"](input_type["default_value"] if existing_data == None or input_type["type"] in exclude else existing_data[input_type["param_name"]], key=input_type["param_name"], **new_kwargs) 89 | layout_item.append(new_input) 90 | layout.append(layout_item) 91 | layout.append([sg.Button("Ok") if existing_data == None else sg.Button("Edit"), sg.Button("Make New Content") if existing_data != None else sg.Text(), sg.Button("Go Back") if existing_data == None or ("Go Back" in existing_data and existing_data["Go Back"] == True) else sg.Button("Exit")]) 92 | window = sg.Window(self.content_type if existing_data == None else "Editing " + existing_data["id"], layout) 93 | while True: 94 | event, values = window.read() 95 | if event == sg.WIN_CLOSED or event == "Exit": 96 | break 97 | if event == "Ok" or event == "Edit" or event == "Make New Content": 98 | new_values = values 99 | for value in new_values: 100 | if type(new_values[value]) == str: 101 | new_values[value] = new_values[value].replace("\n", "") 102 | if "filter" in self.window_layout: 103 | new_values = self.window_layout["filter"](new_values) 104 | _id = None 105 | if existing_data != None and event != "Make New Content": 106 | _id = existing_data["id"] 107 | new_content = self.create_content(new_values, _id) 108 | window.close() 109 | go_back = True 110 | if existing_data != None and not "Go Back" in existing_data: 111 | go_back = False 112 | elif existing_data != None and "Go Back" in existing_data: 113 | go_back = existing_data["Go Back"] 114 | existing_data = new_values 115 | existing_data["Go Back"] = go_back 116 | existing_data["id"] = new_content 117 | self.create_window(existing_data=existing_data) 118 | if event == "Go Back": 119 | window.close() 120 | window_mapping[self.window_layout["previous_window"]].run() 121 | window.close() 122 | else: 123 | raise Exception("Did not Instantiate CustomContentWindow with 'window_layout' kwarg.") 124 | 125 | class CustomContent(object): 126 | def __init__(self, *args, **kwargs): #values, game, content_type, descriptor_text, _id=None 127 | self.values = {"game": args[1], "content_type": args[2], "descriptor_text": args[3]} #We stores these in .values because .values is written to custom_content.json for content editing. 128 | self.values.update(args[0]) 129 | if "id" in kwargs and type(kwargs["id"]) == str and kwargs["id"].isnumeric(): 130 | self.id = kwargs["id"] 131 | else: 132 | self.id = id_gen(self.values) 133 | self.values.update({"id": self.id}) 134 | 135 | def write_to_json(self, p=None, delete=False, data=None): 136 | path = "" 137 | if p: 138 | path = p 139 | else: 140 | path = "./" + self.values["game"] + "/content/" + self.values["content_type"] + ".jet" 141 | if data != None: 142 | if os.path.exists(path): 143 | os.remove(path) 144 | if delete == False: 145 | custom_data = open(path, "w") 146 | custom_data.write(json.dumps(data.values)) 147 | custom_data.close() 148 | elif os.path.exists(path): #Are we making a new .JSON file, or are we appending to an existing .JSON file? 149 | jf = open(path, "r", encoding="utf-8") 150 | json_file = json.load(jf) 151 | if delete == True: 152 | if self.values in json_file["content"]: 153 | json_file["content"].remove(self.values) 154 | else: 155 | sg.Popup("Sorry, that content doesn't appear to exist in the game's files. Try saving custom_content.json somewhere else, using the Reset All Custom Content feature, and then importing custom_content.json back in.") 156 | else: 157 | #Work backwards. It's faster that way. 158 | for i in range(len(json_file["content"]) - 1, -1, -1): 159 | cont = json_file["content"][i] 160 | if cont["id"] == self.id: 161 | json_file["content"].pop(i) 162 | break 163 | if int(cont["id"]) < 100000: 164 | break 165 | json_file["content"].append(self.values) 166 | jf.close() 167 | #Close and reopen to write, because writing with utf-8 encoding gets... weird. 168 | jf = open(path, "w") 169 | jf.write(json.dumps(json_file)) 170 | jf.close() 171 | else: 172 | jf = open(path, "w") 173 | if delete != True: 174 | jf.write(json.dumps(self.values)) 175 | jf.close() 176 | 177 | def save_to_custom_content(self): #Save to custom_content.json file which keeps track of everything. Call this first before adding content. We have values as an argument to pass any additional values. 178 | ids = open("./custom_content.json", "r+") 179 | content = json.load(ids) 180 | if self.id in content: 181 | content[self.id].update({"id": self.id, "values": self.values}) 182 | else: 183 | content[self.id] = {"id": self.id, "values": self.values} 184 | ids.seek(0) 185 | ids.truncate() 186 | ids.write(json.dumps(content)) 187 | ids.close() 188 | 189 | def add_custom_files(self, *args, **kwargs): #Construct the path from what we already know. 190 | path = "" 191 | if "path" in kwargs: 192 | path = kwargs["path"] 193 | else: 194 | path = "./" + self.values["game"] + "/content/" + self.values["content_type"] + "/" + self.id 195 | if os.path.exists(path) and not ("adding_other_files" in kwargs and kwargs["adding_other_files"] == True): #If there's a folder here, but we're not selecting a custom path 196 | rmtree(path) 197 | if not ("delete" in kwargs and kwargs["delete"] == True): 198 | if not (os.path.exists(path)): 199 | os.mkdir(path) 200 | for file in args: 201 | if type(file) == dict and "path" in file: #If we're just copying a file 202 | file_path = file["path"] 203 | name = file["name"] 204 | if name == "id": 205 | name = self.id + file["extension"] 206 | if file_path == "param_name": 207 | file_path = self.values[file["param_name"]] 208 | if(os.path.exists(file_path) and os.path.isfile(file_path)): #Only add this if the file's path exists. 209 | file_path = os.path.realpath(file_path) 210 | copyfile(file_path, path + "/" + name) #From shutil 211 | elif file_path != "./" and file_path != "": 212 | sg.Popup("The file: " + file_path + " does not exist. Custom content added anyway (please edit content #: " + self.id + " in the view/edit content menu).") 213 | else: #If we're going to be writing a custom file from like a .JSON or whatever. 214 | if isinstance(file, CustomContent): 215 | if os.path.exists(path + "data.jet"): 216 | os.remove(path + "data.jet") 217 | file.write_to_json(path + "data.jet", False, args[0]) 218 | elif 'str' in file: #Just making sure there are no files that have an empty path. "str" is if a file has specific data that we're writing. 219 | f = open(path + file['name'], "w+") 220 | f.write(file['str']) 221 | f.close() 222 | 223 | class CustomData(CustomContent): 224 | def __init__(self): 225 | super() 226 | self.values = {"fields": []} 227 | 228 | def add_data(self, t, v, n): 229 | # Thanks to: https://github.com/ambiguousname/jackbox-custom-content/issues/10#issue-1893415234 230 | # t is type (B) for Boolean, (S) for string, (A) for audio, etc. 231 | # v is for value 232 | # n is for the name (a descriptor saying what the data is for) 233 | self.values["fields"].append({ 234 | "t": t, 235 | "v": v, 236 | "n": n 237 | }) 238 | 239 | class SelectionWindow(): 240 | def __init__(self, title, layout_list, selector, previous_window = None): #back_closes should be if we replace the "Go Back" button with "Close" 241 | self.layout_list = layout_list 242 | self.title = title 243 | self.layout_list = layout_list 244 | self.list_key = layout_list[2] 245 | self.selector = selector 246 | self.previous_window = previous_window 247 | 248 | def run(self, inputs=None): #Have to add inputs as an argument because the "Ok" event needs to pass a set of values for determining stuff. So the run function needs a second argument, but will never actually use it. 249 | n_layout = [[sg.Text(self.layout_list[0])], [sg.Listbox(self.layout_list[1], size=(30, 10), select_mode=sg.LISTBOX_SELECT_MODE_BROWSE, key=self.layout_list[2])], [sg.Button('Ok'), sg.Button('Exit' if not self.previous_window != None else 'Go Back')]] 250 | window = sg.Window(self.title, n_layout) 251 | while True: 252 | event, values = window.read() 253 | if event == sg.WIN_CLOSED or event == "Exit": 254 | break 255 | if event == "Ok": 256 | if len(values[self.list_key]) == 1 and values[self.list_key][0] in self.selector: 257 | window.close() 258 | func = self.selector.get(values[self.list_key][0]) 259 | func(values[self.list_key][0]) #What we need the "inputs" argument for. 260 | break 261 | elif "all" in self.selector and len(values[self.list_key]) == 1: 262 | window.close() 263 | func = self.selector.get("all") 264 | func(values[self.list_key][0]) 265 | break 266 | if event == "Go Back" and self.previous_window: 267 | window.close() 268 | if hasattr(window_mapping[self.previous_window], "run"): 269 | window_mapping[self.previous_window].run() 270 | else: 271 | window_mapping[self.previous_window]() 272 | break 273 | window.close() 274 | 275 | #Stuff for file management 276 | 277 | def edit_content_window(selected=None): #Selected goes unused because of how SelectWindow works. 278 | previous_window = content_type_mapping 279 | if len(selected.split(" ")) >= 3 and selected.split(" ")[1] == "All" and selected.split(" ")[2] == "Content": 280 | previous_window = selected.split(" ")[0] 281 | elif selected != "All Games": 282 | for game in content_type_mapping: 283 | for content_type in content_type_mapping[game]: 284 | if content_type == selected: 285 | previous_window = game 286 | break 287 | if os.path.exists("./custom_content.json"): 288 | ids = open("./custom_content.json", 'r+') 289 | content = json.load(ids) 290 | content_list = [] 291 | for item in content: 292 | if content[item]["values"]["content_type"] == selected or selected == "All Games" or (len(selected.split(" ")) >= 3 and selected.split(" ")[1] == "All" and selected.split(" ")[2] == "Content" and content[item]["values"]["game"] == selected.split(" ")[0]): 293 | content_list.append(content[item]["id"] + ": " + content[item]["values"]["content_type"] + " - " + content[item]["values"]["descriptor_text"]) 294 | layout = [[sg.Text("Choose Content to Edit/Delete:")], [sg.Listbox(content_list, key="content_selection", size=(100, 25), select_mode=sg.LISTBOX_SELECT_MODE_EXTENDED)], [sg.Button("Edit"), sg.Button("Delete"), sg.Button("Show Folder"), sg.Button("Go Back")]] 295 | window = sg.Window("Choose Content to Edit/Delete", layout) 296 | while True: 297 | event, values = window.read() 298 | if event == sg.WIN_CLOSED: 299 | break 300 | if event == "Show Folder": 301 | _id = values["content_selection"][0].split(":")[0] 302 | existing_data = content[_id]["values"] 303 | path = os.path.realpath("./" + existing_data["game"] + "/content/" + existing_data["content_type"] + "/" + existing_data["id"]) 304 | if "custom_file_path" in existing_data: 305 | path = os.path.realpath(existing_data["custom_file_path"]) 306 | if (os.path.exists(path)): 307 | if(os.path.isfile(path)): 308 | path = path + "/../" 309 | os.startfile(path) 310 | else: 311 | sg.Popup("This content cannot be found in an easily accessible folder.") 312 | if event == "Edit": 313 | for item in values["content_selection"]: 314 | _id = item.split(":")[0] 315 | existing_data = content[_id]["values"] 316 | content_type_mapping[existing_data["game"]][existing_data["content_type"]].create_window(existing_data=existing_data) 317 | window.close() 318 | ids.close() 319 | edit_content_window(selected) 320 | break 321 | if event == "Delete": 322 | for item in values["content_selection"]: 323 | _id = item.split(":")[0] 324 | custom_content = CustomContent(content[_id]["values"], content[_id]["values"]["game"], content[_id]["values"]["content_type"], content[_id]["values"]["content_type"], content[_id]["values"]["descriptor_text"], id=_id) #Setting None because values already has the game, type, and descriptor_text. 325 | #Remove the content from the custom_content JSON file 326 | content.pop(_id) 327 | #Remove the content from the game's master .JET file 328 | custom_content.write_to_json(None, True) #Delete the JSON file, using the pre-existing path. 329 | #Remove the content's custom folder (will do nothing if one doesn't exist) 330 | custom_content.add_custom_files(delete=True) 331 | ids.seek(0) 332 | ids.truncate() 333 | if len(content.keys()) != 0: 334 | ids.write(json.dumps(content)) 335 | ids.close() 336 | else: 337 | ids.close() 338 | os.remove("./custom_content.json") 339 | window.close() 340 | ids.close() 341 | sg.Popup("Content deleted!") 342 | edit_content_window(selected) #To update the list of content 343 | break 344 | if event == "Go Back": 345 | ids.close() 346 | window.close() 347 | if selected == "All Games": 348 | edit_content() 349 | else: 350 | game_content_select(previous_window) 351 | break 352 | ids.close() 353 | window.close() 354 | else: 355 | sg.Popup("Sorry, no content to edit.") 356 | if selected == "All Games": 357 | edit_content() 358 | else: 359 | game_content_select(previous_window) 360 | 361 | def game_content_select(selected=None): 362 | layout_list = tuple(content_type_mapping[selected].keys()) + tuple([selected + " All Content"]) 363 | select_window = SelectionWindow("Select content type to view/edit for " + selected + ".", ["Select content type to view/edit for " + selected + ".", layout_list, "edit_content_type_selector"], { 364 | "all": edit_content_window 365 | }, "edit_content") 366 | select_window.run() 367 | 368 | def edit_content(selected=None): 369 | layout_list = tuple(content_type_mapping.keys()) + tuple(["All Games"]) 370 | select_window = SelectionWindow("Select a game to view/edit content for.", ["Select game to view/edit content for.", layout_list, "edit_content_selector"], { 371 | "all": game_content_select, 372 | "All Games": edit_content_window 373 | }, "main_window") 374 | select_window.run() 375 | 376 | def import_content(path="./custom_content.json"): 377 | if os.path.exists(path): 378 | new_ids = open(path, "r") 379 | new_content = json.load(new_ids) 380 | new_ids.close() 381 | is_copying = False 382 | if not os.path.exists("./custom_content.json"): 383 | copyfile(path, "./custom_content.json") 384 | is_copying = True 385 | if os.path.realpath(path) == os.path.realpath("./custom_content.json"): 386 | is_copying = True 387 | ids = open("./custom_content.json", "r+") 388 | content = json.load(ids) 389 | content_keys = list(content.keys()) 390 | content_keys.sort() 391 | latest_id = 100000 if is_copying == True else int(content_keys[-1]) + 1 392 | for i in new_content: 393 | content_id = str(latest_id + int(i) - 100000) 394 | n_c = new_content[i] 395 | content[content_id] = n_c 396 | n_c["id"] = content_id 397 | n_c["values"]["id"] = content_id 398 | content_type_mapping[n_c["values"]["game"]][n_c["values"]["content_type"]].create_content(n_c["values"], content_id, True) #Will bug you with popups rn. 399 | ids.seek(0) 400 | ids.truncate() 401 | ids.write(json.dumps(content)) 402 | ids.close() 403 | 404 | def import_content_window(selected=None): 405 | layout = [[sg.Text("To share content for import, share custom_content.json (from the same folder as Jackbox Party Pack Custom.exe). NOTE: See the readme for importing files like .OGGs or .JPGs.")], 406 | [sg.Text("If you've restored your game's files to their original condition, select custom_content.json to reimport.")], 407 | [sg.Text("If that file has been shared with you, select it here.: "), sg.InputText(key="custom-files"), sg.FileBrowse(file_types=((".JSON", "*.json"), ("ALL Types", "*.*")))], [sg.Button("Import"), sg.Button("Go Back")]] 408 | window = sg.Window("Select File to Import", layout) 409 | while True: 410 | event, values = window.read() 411 | if event == sg.WIN_CLOSED: 412 | break 413 | if event == "Import": 414 | if os.path.exists(values["custom-files"]) and os.path.splitext(values["custom-files"])[1].lower() == ".json": 415 | import_content(values["custom-files"]) 416 | sg.Popup("Custom content imported. View the files in the edit menu.") 417 | else: 418 | sg.Popup("That file doesn't exist, or it isn't a .json file.") 419 | if event == "Go Back": 420 | window.close() 421 | main_window.run() 422 | break 423 | window.close() 424 | 425 | def game_content_del(game, content_type): 426 | content = content_type_mapping[game][content_type].window_layout["content_list"] 427 | for item in content: 428 | file_type = item["type"] 429 | if file_type == "json": #WE ONLY NEED THE .JSON FILE TO DELETE, IDIOT! 430 | path = "./" + game + "/content/" + content_type + ".jet" 431 | jet_file = open(path, "r", encoding="utf-8") 432 | n_json_file = json.load(jet_file) 433 | new_content_list = [] 434 | for content_piece in n_json_file["content"]: 435 | if int(content_piece["id"]) >= 100000: 436 | new_content_list.append(content_piece) 437 | n_json_file["content"] = new_content_list 438 | jet_file.close() 439 | jet_file = open(path, "w") 440 | jet_file.truncate() 441 | jet_file.write(json.dumps(n_json_file)) 442 | jet_file.close() 443 | 444 | def del_by_content(game): 445 | layout = [[sg.Text("Again, are you absolutely sure you want to do this?")], 446 | [sg.Text("Please select the content type(s) you'd like to remove: "), sg.Listbox(([item for item in content_type_mapping[game]]), size=(50, 4), key="content_choice", select_mode=sg.LISTBOX_SELECT_MODE_MULTIPLE)], [sg.Checkbox("I am absolutely sure I want to do this. Please delete the non-custom content types I've selected.", key="sure")], 447 | [sg.Button("Ok"), sg.Button("Go Back")]] 448 | window = sg.Window("Delete content types", layout) 449 | while True: 450 | event, values = window.read() 451 | if event == sg.WIN_CLOSED: 452 | break 453 | if event == "Go Back": 454 | window.close() 455 | del_all_else() 456 | break 457 | if event == "Ok": 458 | if values["sure"] == True: 459 | for content_type in values["content_choice"]: 460 | game_content_del(game, content_type) 461 | sg.Popup("Non-Custom Content deleted for: " + str(values["content_choice"])) 462 | 463 | def del_all_else(selected=None): 464 | # Detect if Only Custom has already been used: 465 | games = [item for item in content_type_mapping] 466 | deleted_set = set() 467 | for game in games: 468 | for c_t in content_type_mapping[game]: 469 | c_type = content_type_mapping[game][c_t] 470 | for file_export in c_type.window_layout["content_list"]: 471 | if file_export["type"] == "json": 472 | path = "./" + c_type.game + "/content/" + c_type.content_type + ".jet" 473 | if os.path.exists(path): 474 | c_type_json = open(path, "r", encoding="utf-8") 475 | c_type_json_read = json.load(c_type_json) 476 | c_type_json.close() 477 | contains_non_custom = False 478 | for content_item in c_type_json_read["content"]: 479 | if int(content_item["id"]) < 100000: 480 | contains_non_custom = True 481 | break 482 | if contains_non_custom == False: 483 | deleted_set.add(c_t) 484 | deleted_text = "" 485 | if len(deleted_set) != 0: 486 | deleted_text = "The following non-custom content types have already been removed: \n" + "\n".join(deleted_set) 487 | layout = [[sg.Text("Are you absolutely sure you want to do this?")], [sg.Text("This option will effectively delete all the game's content files so that you can only play with your own custom content. Please make sure you have backups.")], 488 | [sg.Text(deleted_text)], [sg.Text("Please select the game whose content you'd like to remove: "), sg.Listbox((games), size=(50, 4), key="game_choice", select_mode=sg.LISTBOX_SELECT_MODE_BROWSE)], [sg.Checkbox("I am absolutely sure I want to do this. Please delete all non-custom content for the game I have selected.", key="sure")], [sg.Button("Ok"), sg.Button("Remove Non-Custom Content By Type"), sg.Button("Go Back")]] 489 | window = sg.Window("Delete non-custom content", layout) 490 | while True: 491 | event, values = window.read() 492 | if event == sg.WIN_CLOSED: 493 | break 494 | if event == "Go Back": 495 | window.close() 496 | main_window.run() 497 | break 498 | if event == "Ok": 499 | if values["sure"] == True: 500 | for game in values["game_choice"]: 501 | for content_type in content_type_mapping[game]: 502 | game_content_del(game, content_type) 503 | sg.Popup("Non-Custom Content deleted for: " + str(values["game_choice"])) 504 | if event == "Remove Non-Custom Content By Type": 505 | window.close() 506 | for game in values["game_choice"]: 507 | del_by_content(game) 508 | window.close() 509 | 510 | 511 | #Stuff for Quiplash 3 512 | 513 | def create_quiplash_data_jet(values): 514 | data = CustomData() 515 | data.add_data("B", "true" if values["response_filter"] != "" else "false", "HasJokeAudio") 516 | data.add_data("S", values["response_filter"], "Keywords") 517 | data.add_data("A", "response", "KeywordResponseAudio") #Included even though there might not be response audio 518 | data.add_data("S", values["response_transcript"], "KeywordResponseText") 519 | data.add_data("B", "true" if values["prompt"] != "" else "false", "HasPromptAudio") 520 | data.add_data("A", "prompt", "PromptAudio") #I think this is asking for the file name of the audio. I think I can leave this in if the audio doesn't exist, because some prompts don't have response audio, but we include the above line. 521 | data.add_data("S", values["prompt"], "PromptText") 522 | data.add_data("S", "|".join(values["safetyQuips"]), "SafetyQuips") 523 | return data 524 | 525 | def round_filter(values): 526 | new_values = values 527 | new_values["safetyQuips"] = values["safetyQuips"].split("|") 528 | return new_values 529 | 530 | def round_import(values): 531 | new_values = values 532 | new_values["safetyQuips"] = "|".join(values["safetyQuips"]) 533 | return new_values 534 | 535 | round_prompt_layout = { 536 | "previous_window": "quiplash_prompt", 537 | "layout_list": [{"text": "Prompt Text: ", "input": [ 538 | { 539 | "type": sg.InputText, 540 | "default_value": "Hey, needs to .", 541 | "param_name": "prompt", 542 | "kwargs": {"size": (50, 1)} 543 | } 544 | ]}, {"text": "Safety Quips (separate by |):", "input": [ 545 | { 546 | "type": sg.Multiline, 547 | "default_value": "learn how the prompt system works|learn how safety quips work|eat all my garbage", 548 | "param_name": "safetyQuips", 549 | "kwargs": {"size": (50, 1)} 550 | } 551 | ]}, {"input": [ 552 | { 553 | "type": sg.Checkbox, 554 | "default_value": "Includes Player Name", 555 | "kwargs": {"default": "existing_data", "regular_default": True}, 556 | "param_name": "includesPlayerName" 557 | }, 558 | { 559 | "type": sg.Checkbox, 560 | "default_value": "Contains Adult Content", 561 | "kwargs": {"default": "existing_data", "regular_default": False}, 562 | "param_name": "x" 563 | }, { 564 | "type": sg.Checkbox, 565 | "default_value": "Content is US-Specific", 566 | "kwargs": {"default": "existing_data", "regular_default": False}, 567 | "param_name": "us" 568 | } 569 | ]}, {"text": ".ogg files of you reading the prompt (Optional):", "input": [ 570 | { 571 | "type": sg.InputText, 572 | "default_value": "", 573 | "param_name": "prompt_sound" 574 | }, { 575 | "type": sg.FileBrowse, 576 | "default_value": "Browse", 577 | "kwargs": { 578 | "file_types": [(".OGG", "*.ogg"), ("ALL Files", "*.*")] 579 | }, 580 | "param_name": "prompt_file_browse"} 581 | ]}, {"text": "Add a response to specific text (Very optional, see Readme for information):", "input": [ 582 | { 583 | "type": sg.InputText, 584 | "default_value": "", 585 | "param_name": "response_sound" 586 | }, { 587 | "type": sg.FileBrowse, 588 | "default_value": "Browse", 589 | "kwargs": { 590 | "file_types": [(".OGG", "*.ogg"), ("ALL Files", "*.*")] 591 | }, 592 | "param_name": "response_file_browse" 593 | } 594 | ]}, {"text": "What to filter (See Readme): ", "input": [ 595 | { 596 | "type": sg.InputText, 597 | "default_value": "", 598 | "param_name": "response_filter" 599 | } 600 | ]}, {"text": "Transcript of your response: ", "input": [ 601 | { 602 | "type": sg.InputText, 603 | "default_value": "", 604 | "param_name": "response_transcript" 605 | } 606 | ]}], 607 | "content_list": [ 608 | {"type": "json"}, #Write to master .JET file 609 | {"type": "CustomData", "func": create_quiplash_data_jet, "kwargs": {}}, 610 | {"type": "files", "files": { 611 | "args": [{"path": "param_name", "param_name": "prompt_sound", "name": "prompt.ogg"}, {"path": "param_name", "param_name": "response_sound", "name": "response.ogg"}], 612 | "kwargs": {"adding_other_files": True} 613 | }} 614 | ], 615 | "filter": round_filter, 616 | "import_filter": round_import 617 | } 618 | 619 | round_prompt_1 = CustomContentWindow("Quiplash3", "Quiplash3Round1Question", "prompt", round_prompt_layout) 620 | 621 | round_prompt_2 = CustomContentWindow("Quiplash3", "Quiplash3Round2Question", "prompt", round_prompt_layout) 622 | 623 | def round_final_filter(values): 624 | new_values = values 625 | formatted_quips = [] 626 | safety_quip = new_values["safetyQuips"].split("|") 627 | for i in range(0, len(safety_quip), 3): 628 | if not (i + 3 > len(safety_quip)): 629 | formatted_quips.append(safety_quip[i] + "|" + safety_quip[i + 1] + "|" + safety_quip[i + 2]) 630 | new_values["safetyQuips"] = formatted_quips 631 | new_values["response_filter"] = "" 632 | new_values["response_transcript"] = "" 633 | return new_values 634 | 635 | def round_final_import(values): 636 | new_values = values 637 | new_values["safetyQuips"] = "|".join(values["safetyQuips"]) 638 | return new_values 639 | 640 | round_prompt_final = CustomContentWindow("Quiplash3", "Quiplash3FinalQuestion", "prompt", { 641 | "previous_window": "quiplash_prompt", 642 | "layout_list": [{"text": "Prompt Text: ", "input": [ 643 | { 644 | "type": sg.InputText, 645 | "default_value": "'s three favorite words.", 646 | "param_name": "prompt" 647 | } 648 | ]}, {"text": "Safety Quip(s) (separate by |):", "input": [ 649 | { 650 | "type": sg.Multiline, 651 | "default_value": "learning|safety|quips|wait|sorry|what|what|is|love", 652 | "param_name": "safetyQuips" 653 | } 654 | ]}, {"input": [ 655 | { 656 | "type": sg.Checkbox, 657 | "default_value": "Includes Player Name", 658 | "kwargs": {"default": "existing_data", "regular_default": True}, 659 | "param_name": "includesPlayerName" 660 | }, { 661 | "type": sg.Checkbox, 662 | "default_value": "Contains Adult Content", 663 | "kwargs": {"default": "existing_data", "regular_default": False}, 664 | "param_name": "x" 665 | }, { 666 | "type": sg.Checkbox, 667 | "default_value": "Content is US-Specific", 668 | "kwargs": {"default": "existing_data", "regular_default": False}, 669 | "param_name": "us" 670 | } 671 | ]}, {"text": ".ogg file of you reading the prompt (Optional):", "input": [ 672 | { 673 | "type": sg.InputText, 674 | "default_value": "", 675 | "param_name": "prompt_sound" 676 | }, { 677 | "type": sg.FileBrowse, 678 | "default_value": "Browse", 679 | "kwargs": { 680 | "file_types": [(".OGG", "*.ogg"), ("ALL Files", "*.*")] 681 | }, 682 | "param_name": "prompt_file_browse" 683 | } 684 | ]}], 685 | "content_list": [ 686 | {"type": "json"}, 687 | {"type": "CustomData", "func": create_quiplash_data_jet, "kwargs": {}}, 688 | {"type": "files", "files": { 689 | "args": [{"path": "param_name", "param_name": "prompt_sound", "name": "prompt.ogg"}], 690 | "kwargs": {"adding_other_files": True} 691 | }} 692 | ], 693 | "filter": round_final_filter, 694 | "import_filter": round_final_import 695 | }) 696 | 697 | safety_quip = CustomContentWindow("Quiplash3", "Quiplash3SafetyQuips", "value", { 698 | "previous_window": "quiplash_3", 699 | "layout_list": [{"text": "Safety Quip Text (Should be generic): ", "input": [ 700 | { 701 | "type": sg.InputText, 702 | "default_value": "", 703 | "param_name": "value" 704 | } 705 | ]}], 706 | "content_list": [{"type": "json"}] 707 | }) 708 | 709 | quiplash_prompt = SelectionWindow("Choose a Round", ["Choose a round.", ("Round 1", "Round 2", "Final Round"), "quiplash3_round_number"], { 710 | "Round 1": round_prompt_1.create_window, 711 | "Round 2": round_prompt_2.create_window, 712 | "Final Round": round_prompt_final.create_window 713 | }, "quiplash_3") 714 | 715 | quiplash_3 = SelectionWindow("Quiplash 3 Content Selection", ["Please select the type of content", ("Prompt", "Safety Quip"), "quiplash3_content_type"], { 716 | "Prompt": quiplash_prompt.run, 717 | "Safety Quip": safety_quip.create_window 718 | }, "create_content") 719 | 720 | #Stuff for Talking Points 721 | 722 | def talking_points_picture_filter(values): 723 | new_values = values 724 | if new_values["low_res_path"] == "": 725 | new_values["low_res_path"] = new_values["file_path"] 726 | new_values["custom_file_path"] = "./JackboxTalks/content/JackboxTalksPicture/" 727 | return new_values 728 | 729 | talking_points_picture = CustomContentWindow("JackboxTalks", "JackboxTalksPicture", "name", { 730 | "previous_window": "talking_points", 731 | "layout_list": [{"text": "Choose a .JPG file (will show up on your mobile device as a black photo, but it will appear in the game itself): ", "input": [ 732 | { 733 | "type": sg.InputText, 734 | "default_value": "", 735 | "param_name": "file_path" 736 | }, { 737 | "type": sg.FileBrowse, 738 | "default_value": "Browse", 739 | "param_name": "photo_file_browse", 740 | "kwargs": { 741 | "file_types": [(".JPG", "*.jpg"), ("ALL Files", "*.*")] 742 | } 743 | } 744 | ]}, {"text": "Low Res .JPG (recommended, will use higher-res picture if not given): ", "input": [ 745 | { 746 | "type": sg.InputText, 747 | "default_value": "", 748 | "param_name": "low_res_path" 749 | }, { 750 | "type": sg.FileBrowse, 751 | "default_value": "Browse", 752 | "param_name": "low_res_file_browse", 753 | "kwargs": { 754 | "file_types": [(".JPG", "*.jpg"), ("ALL Files", "*.*")] 755 | } 756 | } 757 | ]}, {"text": "Description of the Picture (required): ", "input": [ 758 | { 759 | "type": sg.InputText, 760 | "default_value": "", 761 | "param_name": "name" 762 | } 763 | ]}, {"input": [ 764 | { 765 | "type": sg.Checkbox, 766 | "default_value": "Picture contains adult content", 767 | "kwargs": {"default": "existing_data", "regular_default": False}, 768 | "param_name": "x" 769 | } 770 | ]}], 771 | "content_list": [ 772 | {"type": "json"}, 773 | {"type": "files", "files": { 774 | "args": [{"path": "param_name", "param_name": "file_path", "name": "id", "extension": ".jpg"}], 775 | "kwargs": {"path": "./JackboxTalks/content/JackboxTalksPicture", "adding_other_files": True} 776 | }}, 777 | {"type": "files", "files": { 778 | "args": [{"path": "param_name", "param_name": "low_res_path", "name": "id", "extension": ".jpg"}], 779 | "kwargs": {"path": "./JackboxTalks/content/JackboxTalksPictureLow", "adding_other_files": True} 780 | }} 781 | ], 782 | "filter": talking_points_picture_filter 783 | }) 784 | 785 | def talking_points_prompt_import(values): 786 | new_values = values 787 | slide_transitions = "" 788 | transitions = values["signposts"] 789 | for index, item in enumerate(transitions): 790 | slide_transitions += item["position"][0] + "," + item["signpost"] + ("|" if index < len(transitions) - 1 else "") 791 | new_values["signposts"] = slide_transitions 792 | new_values["safetyAnswers"] = "|".join(values["safetyAnswers"]) 793 | return new_values 794 | 795 | def talking_points_prompt_filter(values): 796 | new_values = values 797 | transitions = values["signposts"] 798 | transitions_list = [] 799 | if transitions != "" and (transitions[0] == "e" or transitions[0] == "m"): 800 | transitions = values["signposts"].split("|") 801 | for item in transitions: 802 | if len(item) > 2 and ("e," in item or "m," in item): 803 | position = item[0] 804 | signpost = item[2:] #Ignore the m, and e, 805 | transitions_list.append({"position": "end" if position == "e" else "middle", "signpost": signpost}) 806 | new_values["signposts"] = transitions_list 807 | new_values["safetyAnswers"] = values["safetyAnswers"].split("|") 808 | return new_values 809 | 810 | talking_points_prompt = CustomContentWindow("JackboxTalks", "JackboxTalksTitle", "title", { 811 | "previous_window": "talking_points", 812 | "layout_list": [{"text": "Prompt: ", "input": [ 813 | { 814 | "type": sg.InputText, 815 | "default_value": "I'm about to do what you're all afraid of. That's right, I'm going to: ", 816 | "param_name": "title", 817 | "kwargs": {"size": (75, 1)} 818 | } 819 | ]}, {"text": "Safety Answers (separate by |): ", "input": [ 820 | { 821 | "type": sg.Multiline, 822 | "default_value": "Do absolutely nothing|Eat a snake live on camera|Downvote a post on reddit", 823 | "param_name": "safetyAnswers" 824 | } 825 | ]}, {"text": "Slide Transitions (separate by |, add (m,) for Middle of presentation, (e,) for End of presentation at the beginning for each transition. Slide transitions are optional.):"}, {"input": [ 826 | { 827 | "type": sg.Multiline, 828 | "default_value": "m,For those of you questioning my reasons, I was motivated by this...|m,For those of you who object, here's why you're all powerless to stop me...|m,If you're concerned about permissions, I have all the power I need from this...|e,Now for the Finale: What you're about to see next will ultimately prove my superiority...|m,What I'm about to say is actually banned in about 20 countries, so pay close attention...|e,For those of you at home, imitate exactly what you're about to hear and see...|e,Now it's flex time, and I'm going to flex with this...|e,I have no words for what you're about to witness, only vague and confusing noises/hand movements...|m,For this amazing feat, I will make use of this as a centerpiece...|m,For my performance, I will be requiring the aid of this...|m,It's nearly time, and to gauge your excitement, I will be using this...", 829 | "kwargs": {"size": (100, 5)}, 830 | "param_name": "signposts" 831 | } 832 | ]}, {"input": [ 833 | { 834 | "type": sg.Checkbox, 835 | "default_value": "Contains adult content", 836 | "kwargs": {"default": "existing_data", "regular_default": False}, 837 | "param_name": "x" 838 | } 839 | ]}], 840 | "content_list": [ 841 | {"type": "json"} 842 | ], 843 | "filter": talking_points_prompt_filter, 844 | "import_filter": talking_points_prompt_import 845 | }) 846 | 847 | def talking_points_slide_transition_filter(values): 848 | new_values = values 849 | new_values["position"] = values["position"][0] 850 | new_values["select_position"] = [values["position"]] 851 | return new_values 852 | 853 | def talking_points_slide_transition_import(values): 854 | new_values = values 855 | new_values["select_position"] = [values["position"]] 856 | new_values["position"] = ("middle", "end") 857 | return new_values 858 | 859 | talking_points_slide_transition = CustomContentWindow("JackboxTalks", "JackboxTalksSignpost", "signpost", { 860 | "previous_window": "talking_points", 861 | "layout_list": [{"text": "Transition Text: ", "input": [ 862 | { 863 | "type": sg.InputText, 864 | "default_value": "Of course, now I hear you ask \"Do you have any evidence?\" Well sure...", 865 | "param_name": "signpost", 866 | "kwargs": {"size": (100, 1)} 867 | } 868 | ]}, {"text": "Position of transition:", "input": [ 869 | { 870 | "type": sg.Listbox, 871 | "default_value": ("middle", "end"), 872 | "kwargs": {"default_values": "existing_data|select_position", "size": (20, 2), "regular_default": "middle"}, 873 | "param_name": "position", 874 | "position": "middle" 875 | } 876 | ]}, {"input": [ 877 | { 878 | "type": sg.Checkbox, 879 | "default_value": "Contains Adult Content", 880 | "param_name": "x", 881 | "kwargs": {"default": "existing_data", "regular_default": False} 882 | } 883 | ]}], 884 | "content_list": [ 885 | {"type": "json"} 886 | ], 887 | "filter": talking_points_slide_transition_filter, 888 | "import_filter": talking_points_slide_transition_import 889 | }) 890 | 891 | talking_points = SelectionWindow("Talking Points Content Selection", ["Please select the type of content", ("Picture", "Prompt", "Slide Transition"), "talking_points_content_type"], { 892 | "Picture": talking_points_picture.create_window, 893 | "Prompt": talking_points_prompt.create_window, 894 | "Slide Transition": talking_points_slide_transition.create_window 895 | }, "create_content") 896 | 897 | # Champ'd Up Stuff 898 | 899 | def champd_up_data_jet(values): 900 | data = CustomData() 901 | data.add_data("B", "true" if values["contest_path"] != "" else "false", "HasContestAudio") 902 | data.add_data("A", "contest", "ContestAudio") 903 | data.add_data("S", values["contest"], "ContestText") #Included even though there might not be response audio 904 | data.add_data("S", values["gameText"], "GameText") 905 | data.add_data("B", "true" if values["response_path"] != "" else "false", "HasResponseAudio") 906 | data.add_data("A", "response", "ResponseAudio") #I think this is asking for the file name of the audio. I think I can leave this in if the audio doesn't exist, because some prompts don't have response audio, but we include the above line. 907 | data.add_data("S", values["response_transcript"], "ResponseText") 908 | return data 909 | 910 | champd_up_round_layout = { 911 | "previous_window": "champd_up", 912 | "layout_list": [{"text": "Contest Name: ", "input": [ 913 | { 914 | "type": sg.InputText, 915 | "default_value": "The Champion of 's Nightmares", 916 | "param_name": "contest" 917 | } 918 | ]}, {"text": "A shorter description (that doesn't include the word \"Champion\"): ", "input": [ 919 | { 920 | "type": sg.InputText, 921 | "default_value": "'s Nightmares", 922 | "param_name": "gameText" 923 | } 924 | ]}, {"text": ".OGG file of you reading the contest name (recommended)", "input": [ 925 | { 926 | "type": sg.InputText, 927 | "default_value": "", 928 | "param_name": "contest_path" 929 | }, { 930 | "type": sg.FileBrowse, 931 | "default_value": "Browse", 932 | "param_name": "contest_browse", 933 | "kwargs": { 934 | "file_types": [(".OGG", "*.ogg"), ("ALL Files", "*.*")] 935 | } 936 | } 937 | ]}, {"text": ".OGG file of you reacting to the prompt (something like 'Oooh! Scary!') (recommended)", "input": [ 938 | { 939 | "type": sg.InputText, 940 | "default_value": "", 941 | "param_name": "response_path" 942 | }, { 943 | "type": sg.FileBrowse, 944 | "default_value": "Browse", 945 | "param_name": "response_browse", 946 | "kwargs": { 947 | "file_types": [(".OGG", "*.ogg"), ("ALL Files", "*.*")] 948 | } 949 | } 950 | ]}, 951 | {"text": "Transcript of your reaction: ", "input": [ 952 | { 953 | "type": sg.InputText, 954 | "default_value": "", 955 | "param_name": "response_transcript" 956 | } 957 | ]}, 958 | {"input": [ 959 | { 960 | "type": sg.Checkbox, 961 | "default_value": "Includes Player Name", 962 | "kwargs": {"default": "existing_data", "regular_default": True}, 963 | "param_name": "includesPlayerName" 964 | } 965 | ]}, {"input": [ 966 | { 967 | "type": sg.Checkbox, 968 | "default_value": "Includes Adult Content", 969 | "kwargs": {"default": "existing_data", "regular_default": False}, 970 | "param_name": "x" 971 | } 972 | ]}, {"input": [ 973 | { 974 | "type": sg.Checkbox, 975 | "default_value": "Content is US-Specific", 976 | "kwargs": {"default": "existing_data", "regular_default": False}, 977 | "param_name": "us" 978 | } 979 | ]}], 980 | "content_list": [ 981 | {"type": "json"}, 982 | {"type": "CustomData", "func": champd_up_data_jet, "kwargs": {}}, 983 | {"type": "files", "files": { 984 | "args": [{"path": "param_name", "param_name": "contest_path", "name": "contest.ogg"}, {"path": "param_name", "param_name": "response_path", "name": "response.ogg"}], 985 | "kwargs": {"adding_other_files": True} 986 | }} 987 | ] 988 | } 989 | 990 | champd_up_round_1 = CustomContentWindow("WorldChampions", "WorldChampionsRound", "contest", champd_up_round_layout) 991 | 992 | champd_up_round_2_5_layout = { 993 | "previous_window": "champd_up", 994 | "layout_list": [{"text": "Contest Name: ", "input": [ 995 | { 996 | "type": sg.InputText, 997 | "default_value": "The Champion of 's Nightmares", 998 | "param_name": "contest" 999 | } 1000 | ]}, {"text": "A shorter description (that doesn't include the word \"Champion\"): ", "input": [ 1001 | { 1002 | "type": sg.InputText, 1003 | "default_value": "'s Nightmares", 1004 | "param_name": "gameText" 1005 | } 1006 | ]}, {"text": ".OGG file of you reading the contest name (recommended)", "input": [ 1007 | { 1008 | "type": sg.InputText, 1009 | "default_value": "", 1010 | "param_name": "contest_path" 1011 | }, { 1012 | "type": sg.FileBrowse, 1013 | "default_value": "Browse", 1014 | "param_name": "contest_browse", 1015 | "kwargs": { 1016 | "file_types": [(".OGG", "*.ogg"), ("ALL Files", "*.*")] 1017 | } 1018 | } 1019 | ]}, {"input": [ 1020 | { 1021 | "type": sg.Checkbox, 1022 | "default_value": "Includes Player Name", 1023 | "kwargs": {"default": "existing_data", "regular_default": True}, 1024 | "param_name": "includesPlayerName" 1025 | } 1026 | ]}, {"input": [ 1027 | { 1028 | "type": sg.Checkbox, 1029 | "default_value": "Includes Adult Content", 1030 | "kwargs": {"default": "existing_data", "regular_default": False}, 1031 | "param_name": "x" 1032 | } 1033 | ]}, {"input": [ 1034 | { 1035 | "type": sg.Checkbox, 1036 | "default_value": "Content is US-Specific", 1037 | "kwargs": {"default": "existing_data", "regular_default": False}, 1038 | "param_name": "us" 1039 | } 1040 | ]}], 1041 | "content_list": [ 1042 | {"type": "json"}, 1043 | {"type": "CustomData", "func": champd_up_data_jet, "kwargs": {}}, 1044 | {"type": "files", "files": { 1045 | "args": [{"path": "param_name", "param_name": "contest_path", "name": "contest.ogg"}, {"path": "param_name", "param_name": "response_path", "name": "response.ogg"}], 1046 | "kwargs": {"adding_other_files": True} 1047 | }} 1048 | ] 1049 | } 1050 | 1051 | def champd_up_round_import(values): 1052 | new_values = values 1053 | new_values["linkedPrompts"] = "|".join(values["linkedPrompts"]) 1054 | return new_values 1055 | 1056 | def champd_up_round_filter(values): 1057 | new_values = values 1058 | new_values["linkedPrompts"] = values["linkedPrompts"].split("|") 1059 | return new_values 1060 | 1061 | champd_up_round_2 = CustomContentWindow("WorldChampions", "WorldChampionsSecondHalfA", "contest", { 1062 | "previous_window": "champd_up", 1063 | "layout_list": [{"text": "Contest Name: ", "input": [ 1064 | { 1065 | "type": sg.InputText, 1066 | "default_value": "The Champion of ", 1067 | "param_name": "contest" 1068 | } 1069 | ]}, {"text": "A shorter description (that doesn't include the word \"Champion\"): ", "input": [ 1070 | { 1071 | "type": sg.InputText, 1072 | "default_value": "'s Champion", 1073 | "param_name": "gameText" 1074 | } 1075 | ]}, {"text": "List of connected prompts (separate by |): ", "input": [ 1076 | { 1077 | "type": sg.InputText, 1078 | "default_value": "500000|500002|500120", 1079 | "param_name": "linkedPrompts" 1080 | } 1081 | ]}, {"text": ".OGG file of you reading the contest name (recommended)", "input": [ 1082 | { 1083 | "type": sg.InputText, 1084 | "default_value": "", 1085 | "param_name": "contest_path" 1086 | }, { 1087 | "type": sg.FileBrowse, 1088 | "default_value": "Browse", 1089 | "param_name": "contest_browse", 1090 | "kwargs": { 1091 | "file_types": [(".OGG", "*.ogg"), ("ALL Files", "*.*")] 1092 | } 1093 | } 1094 | ]}, {"text": ".OGG file of you reacting to the prompt (something like 'Oooh! Scary!') (recommended)", "input": [ 1095 | { 1096 | "type": sg.InputText, 1097 | "default_value": "", 1098 | "param_name": "response_path" 1099 | }, { 1100 | "type": sg.FileBrowse, 1101 | "default_value": "Browse", 1102 | "param_name": "response_browse", 1103 | "kwargs": { 1104 | "file_types": [(".OGG", "*.ogg"), ("ALL Files", "*.*")] 1105 | } 1106 | } 1107 | ]}, {"text": "Transcript of your reaction: ", "input": [ 1108 | { 1109 | "type": sg.InputText, 1110 | "default_value": "", 1111 | "param_name": "response_transcript" 1112 | } 1113 | ]}, {"input": [ 1114 | { 1115 | "type": sg.Checkbox, 1116 | "default_value": "Includes Player Name", 1117 | "kwargs": {"default": "existing_data", "regular_default": True}, 1118 | "param_name": "includesPlayerName" 1119 | } 1120 | ]}, {"input": [ 1121 | { 1122 | "type": sg.Checkbox, 1123 | "default_value": "Includes Adult Content", 1124 | "kwargs": {"default": "existing_data", "regular_default": False}, 1125 | "param_name": "x" 1126 | } 1127 | ]}, {"input": [ 1128 | { 1129 | "type": sg.Checkbox, 1130 | "default_value": "Content is US-Specific", 1131 | "kwargs": {"default": "existing_data", "regular_default": False}, 1132 | "param_name": "us" 1133 | } 1134 | ]}], 1135 | "content_list": [ 1136 | {"type": "json"}, 1137 | {"type": "CustomData", "func": champd_up_data_jet, "kwargs": {}}, 1138 | {"type": "files", "files": { 1139 | "args": [{"path": "param_name", "param_name": "contest_path", "name": "contest.ogg"}, {"path": "param_name", "param_name": "response_path", "name": "response.ogg"}], 1140 | "kwargs": {"adding_other_files": True} 1141 | }} 1142 | ], 1143 | "filter": champd_up_round_filter, 1144 | "import_filter": champd_up_round_import 1145 | }) 1146 | 1147 | champd_up_round_2_5 = CustomContentWindow("WorldChampions", "WorldChampionsSecondHalfB", "contest", champd_up_round_2_5_layout) 1148 | 1149 | champd_up = SelectionWindow("Champ'd Up Content Selection", ["Please select the type of content", ("Round 1 Prompt", "Round 2 Prompt", "Round 2.5 Prompt"), "champd_up_content_type"], { 1150 | "Round 1 Prompt": champd_up_round_1.create_window, 1151 | "Round 2 Prompt": champd_up_round_2.create_window, 1152 | "Round 2.5 Prompt": champd_up_round_2_5.create_window 1153 | }, "create_content") 1154 | 1155 | #Blather Round stuff 1156 | 1157 | def blather_round_word_filter(values): 1158 | new_values = values 1159 | new_values["alternateSpellings"] = values["alternateSpellings"].split("|") 1160 | new_values["forbiddenWords"] = values["forbiddenWords"].split("|") 1161 | if new_values["forbiddenWords"] == [""]: 1162 | new_values["forbiddenWords"] = [] 1163 | tailored_words = [] 1164 | old_tailored_words = values["tailoredWords"].split("|") 1165 | for i in range(0, len(old_tailored_words), 2): 1166 | tailored_words.append({"list": old_tailored_words[i], "word": old_tailored_words[i + 1]}) 1167 | new_values["tailoredWords"] = tailored_words 1168 | return new_values 1169 | 1170 | def blather_round_word_import(values): 1171 | new_values = values 1172 | new_values["alternateSpellings"] = "|".join(values["alternateSpellings"]) 1173 | new_values["forbiddenWords"] = "|".join(values["forbiddenWords"]) 1174 | tailored_words_str = "" 1175 | for index, item in enumerate(values["tailoredWords"]): 1176 | tailored_words_str += item["list"] + "|" + item["word"] + ("|" if index < len(values["tailoredWords"]) - 1 else "") 1177 | new_values["tailoredWords"] = tailored_words_str 1178 | return new_values 1179 | 1180 | def blather_round_word_data_jet(values): 1181 | data = CustomData() 1182 | data.add_data("S", values["password"], "Password") 1183 | data.add_data("S", values["category"], "Category") 1184 | if values["subcategory"] != "": 1185 | data.add_data("S", values["subcategory"], "Subcategory") 1186 | data.add_data("S", values["difficulty"], "Difficulty") 1187 | data.add_data("S", "|".join(values["forbiddenWords"]), "ForbiddenWords") 1188 | data.add_data("S", "|".join(values["alternateSpellings"]), "AlternateSpellings") 1189 | return data 1190 | 1191 | blather_round_word = CustomContentWindow("BlankyBlank", "BlankyBlankPasswords", "password", { 1192 | "previous_window": "blather_round", 1193 | "layout_list": [{"text": "Word/Phrase to use: ", "input": [ 1194 | { 1195 | "type": sg.InputText, 1196 | "default_value": "R'lyeh", 1197 | "param_name": "password" 1198 | } 1199 | ]}, {"text": "Alternate spellings (separate by |): ", "input": [ 1200 | { 1201 | "type": sg.Multiline, 1202 | "default_value": "Rlyeh|Ryleh|R'yleh|R'leyh|Rehly|Rl'yeh|Ry'leh|Relyh|Rly'eh", 1203 | "param_name": "alternateSpellings" 1204 | } 1205 | ]}, {"text": "Word/Phrase Category: ", "input": [ 1206 | { 1207 | "type": sg.InputText, 1208 | "default_value": "place", 1209 | "param_name": "category" 1210 | } 1211 | ]}, {"text": "Subcategory (optional): ", "input":[ 1212 | { 1213 | "type": sg.InputText, 1214 | "default_value": "city", 1215 | "param_name": "subcategory" 1216 | } 1217 | ]}, {"text": "Difficulty (easy, medium, or hard): ", "input": [ 1218 | { 1219 | "type": sg.InputText, 1220 | "default_value": "hard", 1221 | "param_name": "difficulty" 1222 | } 1223 | ]}, {"text": "Forbidden Words (separate by |): ", "input": [ 1224 | { 1225 | "type": sg.Multiline, 1226 | "default_value": "", 1227 | "param_name": "forbiddenWords" 1228 | } 1229 | ]}, {"text": "Tailored Words (separate by |): ", "input": [ 1230 | { 1231 | "type": sg.Multiline, 1232 | "default_value": "|underwater||sea||insanity||evil||horror||fiction", 1233 | "param_name": "tailoredWords" 1234 | } 1235 | ]}, {"input": [ 1236 | { 1237 | "type": sg.Checkbox, 1238 | "default_value": "Content is US-Specific", 1239 | "kwargs": {"default": "existing_data", "regular_default": False}, 1240 | "param_name": "us" 1241 | } 1242 | ]}], 1243 | "content_list": [ 1244 | {"type": "json"}, 1245 | {"type": "CustomData", "func": blather_round_word_data_jet, "kwargs": {}} 1246 | ], 1247 | "filter": blather_round_word_filter, 1248 | "import_filter": blather_round_word_import 1249 | }) 1250 | 1251 | def blather_round_category_filter(values): 1252 | new_values = values 1253 | new_values["structures"] = values["structures"].split("|") 1254 | return new_values 1255 | 1256 | def blather_round_category_import(values): 1257 | new_values = values 1258 | new_values["structures"] = "|".join(values["structures"]) 1259 | return new_values 1260 | 1261 | def blather_round_category_data_jet(values): 1262 | data = CustomData() 1263 | data.add_data("S", values["category"], "Category") 1264 | data.add_data("S", "|".join(values["structures"]), "Structures") 1265 | return data 1266 | 1267 | blather_round_category = CustomContentWindow("BlankyBlank", "BlankyBlankSentenceStructures", "category", { 1268 | "previous_window": "blather_round", 1269 | "layout_list": [{"text": "Broad Category Name: ", "input": [ 1270 | { 1271 | "type": sg.InputText, 1272 | "default_value": "art", 1273 | "param_name": "category" 1274 | } 1275 | ]}, {"text": "Sentence Structures (separate by |): ", "input": [ 1276 | { 1277 | "type": sg.Multiline, 1278 | "default_value": "It's .|It .|Talk about !|It .|Oh, !|Quite simply, it's .", 1279 | "param_name": "structures", 1280 | "kwargs": {"size": (50, 10)} 1281 | } 1282 | ]}], 1283 | "content_list": [ 1284 | {"type": "json"}, 1285 | {"type": "CustomData", "func": blather_round_category_data_jet, "kwargs": {}} 1286 | ], 1287 | "filter": blather_round_category_filter, 1288 | "import_filter": blather_round_category_import 1289 | }) 1290 | 1291 | def blather_round_descriptor_filter(values): 1292 | new_values = values 1293 | new_values["amount"] = "" 1294 | new_values["optional"] = False 1295 | words_list = values["words"].split("|") 1296 | new_words = [] 1297 | next_true = False 1298 | for item in words_list: 1299 | if item == "T": 1300 | next_true = True 1301 | else: 1302 | new_words.append({"word": item, "alwaysChoose": next_true}) 1303 | if next_true == True: 1304 | next_true = False 1305 | new_values["words"] = new_words 1306 | return new_values 1307 | 1308 | def blather_round_descriptor_import(values): 1309 | new_values = values 1310 | words_str = "" 1311 | for index, item in enumerate(new_values["words"]): 1312 | if item["alwaysChoose"] == True: 1313 | words_str += "T|" 1314 | words_str += item["word"] + ("|" if index < len(new_values["words"]) - 1 else "") 1315 | new_values["words"] = words_str 1316 | return new_values 1317 | 1318 | def blather_round_descriptor_data_jet(values): 1319 | data = CustomData() 1320 | data.add_data("S", values["name"], "Name") 1321 | data.add_data("B", values["optional"], "Optional") 1322 | data.add_data("S", values["amount"], "Amount") 1323 | data.add_data("S", values["maxChoices"], "MaxChoices") 1324 | data.add_data("S", values["placeholder"], "Placeholder") 1325 | return data 1326 | 1327 | blather_round_descriptor = CustomContentWindow("BlankyBlank", "BlankyBlankWordLists", "name", { 1328 | "previous_window": "blather_round", 1329 | "layout_list": [{"text": "Descriptor name (hyphenate, please): ", "input": [ 1330 | { 1331 | "type": sg.InputText, 1332 | "default_value": "hyphenated-descriptor (Please read README)", 1333 | "param_name": "name" 1334 | } 1335 | ]}, {"text": "Words List: ", "input": [ 1336 | { 1337 | "type": sg.Multiline, 1338 | "default_value": "list|of|separated|T|words|or|sentences", 1339 | "param_name": "words", 1340 | "kwargs": {"size": (50, 5)} 1341 | } 1342 | ]}, {"text": "Maximum number of choices (leave blank for none): ", "input": [ 1343 | { 1344 | "type": sg.InputText, 1345 | "default_value": "", 1346 | "param_name": "maxChoices" 1347 | } 1348 | ]}, {"text": "Placeholder text (blank, blanks, or blanky): ", "input": [ 1349 | { 1350 | "type": sg.InputText, 1351 | "default_value": "blank", 1352 | "param_name": "placeholder" 1353 | } 1354 | ]}], 1355 | "content_list": [ 1356 | {"type": "json"}, 1357 | {"type": "CustomData", "func": blather_round_descriptor_data_jet, "kwargs": {}} 1358 | ], 1359 | "filter": blather_round_descriptor_filter, 1360 | "import_filter": blather_round_descriptor_import 1361 | }) 1362 | 1363 | blather_round = SelectionWindow("Blather 'Round Content Selection", ["Please select the type of content (Read Readme)", ("Word", "Category", "Descriptors"), "blather_round_content_type"], { 1364 | "Word": blather_round_word.create_window, 1365 | "Category": blather_round_category.create_window, 1366 | "Descriptors": blather_round_descriptor.create_window 1367 | }, "create_content") 1368 | 1369 | #Main Menu stuff 1370 | 1371 | create_content = SelectionWindow("Select a game", ["Select a game.", ("Blather 'Round", "Champ'd Up", "Quiplash 3", "Talking Points"), "game"],{ 1372 | "Blather 'Round": blather_round.run, 1373 | "Talking Points": talking_points.run, 1374 | "Quiplash 3": quiplash_3.run, 1375 | "Champ'd Up": champd_up.run 1376 | }, "main_window") 1377 | 1378 | main_window = SelectionWindow("Select an option", ["Please click on an option, then click OK.", ("Create Custom Content", "View/Edit Content", "Import/Reimport Content", "Only Use Custom Content"), "option"], { 1379 | "Create Custom Content": create_content.run, 1380 | "View/Edit Content": edit_content, 1381 | "Import/Reimport Content": import_content_window, 1382 | "Only Use Custom Content": del_all_else 1383 | }) 1384 | 1385 | window_mapping = { #Used for backing out of stuff. 1386 | "quiplash_prompt": quiplash_prompt, 1387 | "quiplash_3": quiplash_3, 1388 | "create_content": create_content, 1389 | "main_window": main_window, 1390 | "talking_points": talking_points, 1391 | "champd_up": champd_up, 1392 | "blather_round": blather_round, 1393 | "edit_content": edit_content 1394 | } 1395 | 1396 | content_type_mapping = { #Used in editing content to change data. 1397 | "BlankyBlank": { 1398 | "BlankyBlankPasswords": blather_round_word, 1399 | "BlankyBlankSentenceStructures": blather_round_category, 1400 | "BlankyBlankWordLists": blather_round_descriptor 1401 | }, 1402 | "JackboxTalks":{ 1403 | "JackboxTalksPicture": talking_points_picture, 1404 | "JackboxTalksTitle": talking_points_prompt, 1405 | "JackboxTalksSignpost": talking_points_slide_transition 1406 | }, 1407 | "Quiplash3":{ 1408 | "Quiplash3Round1Question": round_prompt_1, 1409 | "Quiplash3Round2Question": round_prompt_2, 1410 | "Quiplash3FinalQuestion": round_prompt_final, 1411 | "Quiplash3SafetyQuips": safety_quip 1412 | }, 1413 | "WorldChampions": { 1414 | "WorldChampionsRound": champd_up_round_1, 1415 | "WorldChampionsSecondHalfA": champd_up_round_2, 1416 | "WorldChampionsSecondHalfB": champd_up_round_2_5 1417 | } 1418 | } 1419 | 1420 | if os.path.realpath("../games") == os.getcwd() and os.path.exists("./Quiplash3") and os.path.exists("./Everyday"): #Checks to see if we're in the right folder. 1421 | main_window.run() 1422 | else: 1423 | sg.Popup("File not in The Jackbox Party Pack 7/games folder. Please see the installation instructions of the Readme for folder location.") 1424 | -------------------------------------------------------------------------------- /sample_custom_content_to_import.json: -------------------------------------------------------------------------------- 1 | {"100000": {"id": "100000", "values": {"game": "WorldChampions", "content_type": "WorldChampionsRound", "descriptor_text": "The Champion of Being Completely Forgettable", "contest": "The Champion of Being Completely Forgettable", "gameText": "Being Completely Forgettable", "contest_path": "./external content/Champd Up Sounds/completelyforgettable.ogg/", "contest_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Champd Up Sounds/completelyforgettable.ogg", "response_path": "./external content/Champd Up Sounds/completelyforgettable_response.ogg/", "response_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Champd Up Sounds/completelyforgettable_response.ogg", "response_transcript": "Explain This, Science!", "includesPlayerName": false, "x": false, "us": false, "id": "100000"}}, "100001": {"id": "100001", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankSentenceStructures", "descriptor_text": "scheme", "category": "scheme", "structures": ["It's .", "With this scheme, I will render .", "Tomorrow, .", "My plan .", "My scheme would be for ."], "id": "100001"}}, "100002": {"id": "100002", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankWordLists", "descriptor_text": "scheme-adjective-simple", "name": "scheme-adjective-simple", "words": [{"word": "not", "alwaysChoose": true}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}], "maxChoices": "3", "placeholder": "blank", "amount": "", "optional": false, "id": "100002"}}, "100003": {"id": "100003", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankWordLists", "descriptor_text": "scheme-noun-simple", "name": "scheme-noun-simple", "words": [{"word": "", "alwaysChoose": true}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}], "maxChoices": "2", "placeholder": "blank", "amount": "", "optional": false, "id": "100003"}}, "100004": {"id": "100004", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankWordLists", "descriptor_text": "scheme", "name": "scheme", "words": [{"word": "scheme", "alwaysChoose": false}, {"word": "plan", "alwaysChoose": false}, {"word": "idea", "alwaysChoose": false}, {"word": "plot", "alwaysChoose": false}, {"word": "theft", "alwaysChoose": false}, {"word": "destruction", "alwaysChoose": false}, {"word": "ray", "alwaysChoose": false}, {"word": "minion", "alwaysChoose": false}, {"word": "annihilation", "alwaysChoose": false}, {"word": "megastructure", "alwaysChoose": false}, {"word": "replacement", "alwaysChoose": false}, {"word": "doomsday", "alwaysChoose": false}, {"word": "fake", "alwaysChoose": false}, {"word": "real", "alwaysChoose": false}], "maxChoices": "", "placeholder": "blank", "amount": "", "optional": false, "id": "100004"}}, "100005": {"id": "100005", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankWordLists", "descriptor_text": "scheme-adjective-complex", "name": "scheme-adjective-complex", "words": [{"word": "", "alwaysChoose": true}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}], "maxChoices": "3", "placeholder": "blank", "amount": "", "optional": false, "id": "100005"}}, "100006": {"id": "100006", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankWordLists", "descriptor_text": "scheme-noun-complex", "name": "scheme-noun-complex", "words": [{"word": "", "alwaysChoose": true}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}], "maxChoices": "2", "placeholder": "blank", "amount": "", "optional": false, "id": "100006"}}, "100007": {"id": "100007", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankWordLists", "descriptor_text": "scheme-verb-simple", "name": "scheme-verb-simple", "words": [{"word": "are", "alwaysChoose": false}, {"word": "has", "alwaysChoose": false}, {"word": "does", "alwaysChoose": false}, {"word": "steals", "alwaysChoose": false}, {"word": "destroys", "alwaysChoose": false}, {"word": "helps", "alwaysChoose": false}, {"word": "renders useless", "alwaysChoose": false}, {"word": "strikes fear into", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}, {"word": "", "alwaysChoose": false}], "maxChoices": "1", "placeholder": "blanks", "amount": "", "optional": false, "id": "100007"}}, "100008": {"id": "100008", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankPasswords", "descriptor_text": "steal the moon", "password": "steal the moon", "alternateSpellings": ["moon theft", "theft of the moon", "stealing the moon", "despicable me", "steal moon"], "category": "scheme", "subcategory": "", "difficulty": "easy", "forbiddenWords": ["moon"], "tailoredWords": [{"list": "", "word": "theft"}, {"list": "", "word": "space"}, {"list": "", "word": "silver"}, {"list": "", "word": "disastrous"}, {"list": "", "word": "body of water"}], "us": false, "id": "100008"}}, "100009": {"id": "100009", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankWordLists", "descriptor_text": "response-sentence-scheme", "name": "response-sentence-scheme", "words": [{"word": "It's a lot like", "alwaysChoose": true}, {"word": "It's kinda similar to", "alwaysChoose": true}, {"word": "It's nothing like", "alwaysChoose": true}, {"word": "It has the same vibe as", "alwaysChoose": true}, {"word": "It's close to", "alwaysChoose": true}, {"word": "It's more evil than", "alwaysChoose": true}, {"word": "It's more good than", "alwaysChoose": true}, {"word": "It's more general than", "alwaysChoose": true}, {"word": "It's more specific than", "alwaysChoose": true}], "maxChoices": "1", "placeholder": "blanky", "amount": "", "optional": false, "id": "100009"}}, "100010": {"id": "100010", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankPasswords", "descriptor_text": "start a ponzi scheme", "password": "start a ponzi scheme", "alternateSpellings": ["pyramid scheme", "ponzi scheme", "start a pyramid scheme", "ponzi", "pyramid"], "category": "scheme", "subcategory": "", "difficulty": "medium", "forbiddenWords": [], "tailoredWords": [{"list": "", "word": "rich"}, {"list": "", "word": "scam"}, {"list": "", "word": "bankruptcy"}, {"list": "", "word": "tiptop"}], "us": false, "id": "100010"}}, "100011": {"id": "100011", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankPasswords", "descriptor_text": "build a death star", "password": "build a death star", "alternateSpellings": ["annihilaser", "starkiller", "starkiller base", "death star"], "category": "scheme", "subcategory": "", "difficulty": "easy", "forbiddenWords": [], "tailoredWords": [{"list": "", "word": "dying"}, {"list": "", "word": "space"}, {"list": "", "word": "destruction"}, {"list": "", "word": "machine"}, {"list": "", "word": "megastructure"}], "us": false, "id": "100011"}}, "100012": {"id": "100012", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankPasswords", "descriptor_text": "replacing cherry centers with mayonnaise", "password": "replacing cherry centers with mayonnaise", "alternateSpellings": ["replacing cherry centers with mayonaise", "cherry to mayonnaise", "cherry to mayonaise", "replacing the cherry from chocolate covered cherries with mayonnaise", "mayonnaise", "mayonaise", "simpsons did it"], "category": "scheme", "subcategory": "", "difficulty": "hard", "forbiddenWords": [], "tailoredWords": [{"list": "", "word": "ridiculous"}, {"list": "", "word": "red"}, {"list": "", "word": "yellow"}, {"list": "", "word": "snack"}, {"list": "", "word": "contraption"}, {"list": "", "word": "replacement"}], "us": false, "id": "100012"}}, "100013": {"id": "100013", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankPasswords", "descriptor_text": "freeze the earth", "password": "freeze the earth", "alternateSpellings": ["mr freeze", "mr. freeze", "frozen earth", "opposite global warming", "ice age", "freezing the earth", "scorcher vi: global meltdown"], "category": "scheme", "subcategory": "", "difficulty": "easy", "forbiddenWords": [], "tailoredWords": [{"list": "", "word": "cold"}, {"list": "", "word": "ray"}, {"list": "", "word": "earthy"}, {"list": "", "word": "doomsday"}], "us": false, "id": "100013"}}, "100014": {"id": "100014", "values": {"game": "BlankyBlank", "content_type": "BlankyBlankPasswords", "descriptor_text": "Wood laundering", "password": "Wood laundering", "alternateSpellings": ["wood theft", "illegal logging"], "category": "scheme", "subcategory": "", "difficulty": "hard", "forbiddenWords": ["wood"], "tailoredWords": [{"list": "", "word": "theft"}, {"list": "", "word": "forest"}], "us": false, "id": "100014"}}, "100015": {"id": "100015", "values": {"game": "WorldChampions", "content_type": "WorldChampionsRound", "descriptor_text": "The Champion of Stay Away From Me", "contest": "The Champion of Stay Away From Me", "gameText": "Stay Away From Me", "contest_path": "./external content/Champd Up Sounds/stayawayfromme.ogg/", "contest_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Champd Up Sounds/stayawayfromme.ogg", "response_path": "./external content/Champd Up Sounds/stayawayfromme_response.ogg/", "response_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Champd Up Sounds/stayawayfromme_response.ogg", "response_transcript": "The Champion of Come", "includesPlayerName": false, "x": true, "us": false, "id": "100015"}}, "100016": {"id": "100016", "values": {"game": "WorldChampions", "content_type": "WorldChampionsRound", "descriptor_text": "The Champion of MUTE YOUR AUDIO SCREEEEEEEEE", "contest": "The Champion of MUTE YOUR AUDIO SCREEEEEEEEE", "gameText": "MUTE YOUR AUDIO SCREEEEEEEEE", "contest_path": "./external content/Champd Up Sounds/muteyouraudio.ogg/", "contest_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Champd Up Sounds/muteyouraudio.ogg", "response_path": "./external content/Champd Up Sounds/muteyouraudio_response.ogg/", "response_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Champd Up Sounds/muteyouraudio_response.ogg", "response_transcript": "Look at all of that PSHHHHHHHHHHHHHHHHHH", "includesPlayerName": false, "x": false, "us": false, "id": "100016"}}, "100017": {"id": "100017", "values": {"game": "WorldChampions", "content_type": "WorldChampionsRound", "descriptor_text": "The Champion of Being Responsible", "contest": "The Champion of Being Responsible", "gameText": "Being Responsible, Kiddos", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100017"}}, "100018": {"id": "100018", "values": {"game": "WorldChampions", "content_type": "WorldChampionsRound", "descriptor_text": "The Champion of Universal Truth", "contest": "The Champion of Universal Truth", "gameText": "Universal Truth", "contest_path": "./external content/Champd Up Sounds/yogsothoth.ogg/", "contest_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Champd Up Sounds/yogsothoth.ogg", "response_path": "./external content/Champd Up Sounds/yogsothoth_response.ogg/", "response_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Champd Up Sounds/yogsothoth_response.ogg", "response_transcript": "I mean, \"ugh!\", am I right?", "includesPlayerName": false, "x": false, "us": false, "id": "100018"}}, "100019": {"id": "100019", "values": {"game": "WorldChampions", "content_type": "WorldChampionsRound", "descriptor_text": "The Champion of Not Getting It", "contest": "The Champion of Not Getting It", "gameText": "Not Getting It", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100019"}}, "100020": {"id": "100020", "values": {"game": "WorldChampions", "content_type": "WorldChampionsRound", "descriptor_text": "The Champion of Meta", "contest": "The Champion of Meta", "gameText": "Meta", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100020"}}, "100021": {"id": "100021", "values": {"game": "WorldChampions", "content_type": "WorldChampionsRound", "descriptor_text": "The Champion of Obscure Supernatural Powers", "contest": "The Champion of Obscure Supernatural Powers", "gameText": "Obscure Supernatural Powers", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100021"}}, "100022": {"id": "100022", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfB", "descriptor_text": "The Champion of Champions", "contest": "The Champion of Champions", "gameText": "Champions", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100022"}}, "100023": {"id": "100023", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfB", "descriptor_text": "The Champion of That One Movie, You Know The One", "contest": "The Champion of That One Movie, You Know The One", "gameText": "That One Movie", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100023"}}, "100024": {"id": "100024", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfB", "descriptor_text": "The Champion of Being Really Really Small", "contest": "The Champion of Being Really Really Small", "gameText": "Being Really Really Small", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": true, "us": false, "id": "100024"}}, "100025": {"id": "100025", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfB", "descriptor_text": "The Champion of Discovering the Shocking Truth of ", "contest": "The Champion of Discovering the Shocking Truth of ", "gameText": "Discovering the Shocking Truth of ", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": true, "x": false, "us": false, "id": "100025"}}, "100026": {"id": "100026", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfB", "descriptor_text": "The Champion of Yog-Sothoth, Born of the Nameless Mist, Progenitor of the Dunwich Horror", "contest": "The Champion of Yog-Sothoth, Born of the Nameless Mist, Progenitor of the Dunwich Horror", "gameText": "Yog-Sothoth, Born of the Nameless Mist, Progenitor of the Dunwich Horror", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": true, "us": false, "id": "100026"}}, "100027": {"id": "100027", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfB", "descriptor_text": "The Champion of Spellinngg Correction", "contest": "The Champion of Spellinngg Correction", "gameText": "Spellinngg Correction", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100027"}}, "100028": {"id": "100028", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfB", "descriptor_text": "The Champion of Obscure Knowledge", "contest": "The Champion of Obscure Knowledge", "gameText": "Obscure Knowledge", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100028"}}, "100029": {"id": "100029", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfB", "descriptor_text": "The Champion of PAIN", "contest": "The Champion of PAIN", "gameText": "PAIN", "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": true, "us": false, "id": "100029"}}, "100030": {"id": "100030", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfA", "descriptor_text": "The Champion of Repetition", "contest": "The Champion of Repetition", "gameText": "Repetition", "linkedPrompts": ["100022"], "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100030"}}, "100031": {"id": "100031", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfA", "descriptor_text": "The Champion of r/gaming", "contest": "The Champion of r/gaming", "gameText": "r/gaming", "linkedPrompts": [""], "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100031"}}, "100032": {"id": "100032", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfA", "descriptor_text": "The Champion of XKCD", "contest": "The Champion of XKCD", "gameText": "XKCD", "linkedPrompts": [""], "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100032"}}, "100033": {"id": "100033", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfA", "descriptor_text": "The Champion of the Round Table", "contest": "The Champion of the Round Table", "gameText": "Round Table", "linkedPrompts": [""], "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100033"}}, "100034": {"id": "100034", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfA", "descriptor_text": "The Champion of Self Discovery", "contest": "The Champion of Self Discovery", "gameText": "Self Discovery", "linkedPrompts": ["100026"], "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100034"}}, "100035": {"id": "100035", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfA", "descriptor_text": "The Champion of \"What Could Possibly Go Wrong?\"", "contest": "The Champion of \"What Could Possibly Go Wrong?\"", "gameText": "\"What Could Possibly Go Wrong?\"", "linkedPrompts": [""], "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100035"}}, "100036": {"id": "100036", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfA", "descriptor_text": "The Champion of Shut Up", "contest": "The Champion of Shut Up", "gameText": "Shut Up", "linkedPrompts": [""], "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": false, "x": false, "us": false, "id": "100036"}}, "100037": {"id": "100037", "values": {"game": "WorldChampions", "content_type": "WorldChampionsSecondHalfA", "descriptor_text": "The Champion of What's Behind Right Now", "contest": "The Champion of What's Behind Right Now", "gameText": "What's Behind Right Now", "linkedPrompts": [""], "contest_path": "", "contest_browse": "", "response_path": "", "response_browse": "", "response_transcript": "", "includesPlayerName": true, "x": false, "us": false, "id": "100037"}}, "100038": {"id": "100038", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksTitle", "descriptor_text": "Through the power of flextape, you too can achieve .", "title": "Through the power of flextape, you too can achieve .", "x": false, "safetyAnswers": ["infinite knowledge", "ultimate power", "thanoscopter"], "signposts": [{"position": "middle", "signpost": "I see you all scoffing. Prepare shut up thyself with this..."}, {"position": "middle", "signpost": "And now for the part where I stick my tongue out at you because you cannot recognize my genius..."}, {"position": "middle", "signpost": "The next step is complicated, so try not to be embarrassed by what I am about to show you..."}, {"position": "middle", "signpost": "Ask yourself, \"How might I get this flex tape?\" Let me show you..."}, {"position": "middle", "signpost": "With this next step, you will be closer to achieving everything you've ever wanted..."}, {"position": "end", "signpost": "Now for the grand finale, where I flex with my flextape..."}, {"position": "end", "signpost": "To cap this all off, you will recognize my power with this..."}, {"position": "end", "signpost": "With everything laid out in front of you dolts, I can now reveal the ultimate prize..."}], "id": "100038"}}, "100039": {"id": "100039", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksTitle", "descriptor_text": "My Real Name Is Actually , And I'm Here To Promote My New Product Line.", "title": "My Real Name Is Actually , And I'm Here To Promote My New Product Line.", "safetyAnswers": ["Your Name Here", "Ryan Reynolds", "Pllblhhhbttt Bpbghhht III"], "signposts": [{"position": "middle", "signpost": "My favorite item has to be..."}, {"position": "middle", "signpost": "The product line's costs are of course significantly affected by..."}, {"position": "middle", "signpost": "The end goal of this is to raise money for this..."}, {"position": "middle", "signpost": "Why create a product line? I was inspired by this..."}, {"position": "end", "signpost": "In conclusion, please buy my products. Otherwise, this will happen..."}, {"position": "middle", "signpost": "You should trust me because my line has the full support of this..."}, {"position": "end", "signpost": "So please invest. If you do, you'll recieve this..."}, {"position": "end", "signpost": "I actually hate all of these products, but my hand was forced by this..."}], "x": false, "id": "100039"}}, "100040": {"id": "100040", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksTitle", "descriptor_text": "I forgot the topic of this presentation. I think it had something to do with .", "title": "I forgot the topic of this presentation. I think it had something to do with .", "safetyAnswers": ["gamers", "the upcoming apocalypse", "me forgetting the topic of this presentation"], "signposts": [{"position": "middle", "signpost": "Now I have absolutely no idea what this next slide is about. I think it has something to do with this..."}, {"position": "middle", "signpost": "I think I forgot because of this..."}, {"position": "middle", "signpost": "Now, I need a little audience participation to make sense of this..."}, {"position": "middle", "signpost": "What may or may not be on this next slide will utterly shock you..."}, {"position": "end", "signpost": "Okay, after reviewing a few slides I think I know what this is about..."}, {"position": "end", "signpost": "I have no clue what I just witnessed. Maybe we can make sense of it with this..."}, {"position": "end", "signpost": "And so after all that, I think we managed to learn this..."}], "x": false, "id": "100040"}}, "100041": {"id": "100041", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksTitle", "descriptor_text": "Instead of presenting something, I'm going to sing something. Can someone start playing ?", "title": "Instead of presenting something, I'm going to sing something. Can someone start playing ?", "safetyAnswers": ["Bohemian Rhapsody", "I Will Always Love You", "All Star", "any song by Wham!"], "signposts": [{"position": "middle", "signpost": "Feel free to sing along."}, {"position": "middle", "signpost": "I'M NOT SEEING ENOUGH MOVEMENT"}, {"position": "middle", "signpost": "Music to my earholes."}, {"position": "middle", "signpost": "Can I see some support for our presenter?"}, {"position": "middle", "signpost": "Keep it going!"}, {"position": "end", "signpost": "PITCH. PERFECT."}, {"position": "end", "signpost": "I think I'm gonna cry, that was beautiful."}, {"position": "end", "signpost": "I am very envious from what I just witnessed."}, {"position": "end", "signpost": "Time to end it on a high note if you haven't already."}, {"position": "end", "signpost": "Can we give a hand to our presenter?"}], "x": false, "id": "100041"}}, "100042": {"id": "100042", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksTitle", "descriptor_text": "Stop the presentation. My archenemy, is in the room.", "title": "Stop the presentation. My archenemy, is in the room.", "safetyAnswers": ["that guy from across the street", "a single carpet stain", "Constantine the Great"], "signposts": [{"position": "middle", "signpost": "We have to be very careful with what I'm about to do next..."}, {"position": "middle", "signpost": "This next slide shows my archenemy in their most dangerous form..."}, {"position": "middle", "signpost": "You may be wondering what created my nemesis. The short version of it is this..."}, {"position": "middle", "signpost": "Watch me as I prepare to deal with my archenemy, using this..."}, {"position": "end", "signpost": "With everything done, I will now vanquish my nemesis using this..."}, {"position": "end", "signpost": "With everything done, we move onto this..."}, {"position": "end", "signpost": "NOW MY WRATH IS UNLEASHED, ESPECIALLY BY THIS..."}], "x": true, "id": "100042"}}, "100043": {"id": "100043", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksPicture", "descriptor_text": "Someone being concerned for others' safety", "file_path": "./external content/Talking Points Pictures/Are you alright.jpg/", "photo_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Talking Points Pictures/Are you alright.jpg", "low_res_path": "./external content/Talking Points Pictures/Are you alright.jpg/", "low_res_file_browse": "", "name": "Someone being concerned for others' safety", "x": false, "custom_file_path": "./JackboxTalks/content/JackboxTalksPicture/", "id": "100043"}}, "100044": {"id": "100044", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksPicture", "descriptor_text": "Up close face", "file_path": "./external content/Talking Points Pictures/face.jpg/", "photo_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Talking Points Pictures/face.jpg", "low_res_path": "./external content/Talking Points Pictures/face.jpg/", "low_res_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Talking Points Pictures/face.jpg", "name": "Up close face", "x": false, "custom_file_path": "./JackboxTalks/content/JackboxTalksPicture/", "id": "100044"}}, "100045": {"id": "100045", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksPicture", "descriptor_text": "Guy meeting new people", "file_path": "./external content/Talking Points Pictures/job well done.jpg/", "photo_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Talking Points Pictures/job well done.jpg", "low_res_path": "./external content/Talking Points Pictures/job well done.jpg/", "low_res_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Talking Points Pictures/job well done.jpg", "name": "Guy meeting new people", "x": true, "custom_file_path": "./JackboxTalks/content/JackboxTalksPicture/", "id": "100045"}}, "100046": {"id": "100046", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksPicture", "descriptor_text": "Not the Milky Way Galaxy", "file_path": "./external content/Talking Points Pictures/Not Milky Way.jpg/", "photo_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Talking Points Pictures/Not Milky Way.jpg", "low_res_path": "./external content/Talking Points Pictures/Not Milky Way.jpg/", "low_res_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Talking Points Pictures/Not Milky Way.jpg", "name": "Not the Milky Way Galaxy", "x": false, "custom_file_path": "./JackboxTalks/content/JackboxTalksPicture/", "id": "100046"}}, "100047": {"id": "100047", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksPicture", "descriptor_text": "Hilarious Joke About The Younglings", "file_path": "./external content/Talking Points Pictures/younglings.jpg/", "photo_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Talking Points Pictures/younglings.jpg", "low_res_path": "./external content/Talking Points Pictures/younglings.jpg/", "low_res_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Talking Points Pictures/younglings.jpg", "name": "Hilarious Joke About The Younglings", "x": true, "custom_file_path": "./JackboxTalks/content/JackboxTalksPicture/", "id": "100047"}}, "100048": {"id": "100048", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksSignpost", "descriptor_text": "My next slide will seem a little extreme, but it's something you have to see for yourself...", "signpost": "My next slide will seem a little extreme, but it's something you have to see for yourself...", "position": "middle", "x": false, "id": "100048"}}, "100049": {"id": "100049", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksSignpost", "descriptor_text": "Let's dive into an analogy, between my current subject and this...", "signpost": "Let's dive into an analogy, between my current subject and this...", "position": "middle", "x": false, "select_position": ["middle"], "id": "100049"}}, "100050": {"id": "100050", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksSignpost", "descriptor_text": "I am become this, destroyer of worlds...", "signpost": "I am become this, destroyer of worlds...", "position": "middle", "x": false, "select_position": ["middle"], "id": "100050"}}, "100051": {"id": "100051", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksSignpost", "descriptor_text": "What you see next will become my catchphrase for the rest of this presentation...", "signpost": "What you see next will become my catchphrase for the rest of this presentation...", "position": "middle", "x": false, "select_position": ["middle"], "id": "100051"}}, "100052": {"id": "100052", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksSignpost", "descriptor_text": "As for my conclusion slide, I hope this presents you with enough of a challenge...", "signpost": "As for my conclusion slide, I hope this presents you with enough of a challenge...", "position": "end", "x": false, "select_position": ["end"], "id": "100052"}}, "100053": {"id": "100053", "values": {"game": "JackboxTalks", "content_type": "JackboxTalksSignpost", "descriptor_text": "Boop boop I'm a scoop", "signpost": "Boop boop I'm a scoop", "position": "end", "x": false, "select_position": ["end"], "id": "100053"}}, "100054": {"id": "100054", "values": {"game": "Quiplash3", "content_type": "Quiplash3Round1Question", "descriptor_text": "The earth is flat? It's actually .", "prompt": "The earth is flat? It's actually .", "safetyQuips": ["a sphere", "not a moon", "a giant cookie"], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "./external content/Champd Up Sounds/completelyforgettable_response.ogg/", "prompt_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Champd Up Sounds/completelyforgettable_response.ogg", "response_sound": "./external content/Quiplash 3 Sounds/yeahobviously_response.ogg/", "response_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Quiplash 3 Sounds/yeahobviously_response.ogg", "response_filter": "flat", "response_transcript": "Yeah, obviously", "id": "100054"}}, "100055": {"id": "100055", "values": {"game": "Quiplash3", "content_type": "Quiplash3Round1Question", "descriptor_text": "Hi , I'm dad", "prompt": "Hi , I'm dad", "safetyQuips": ["malnourished", "idiot", "adopted"], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "", "prompt_file_browse": "", "response_sound": "", "response_file_browse": "", "response_filter": "", "response_transcript": "", "id": "100055"}}, "100056": {"id": "100056", "values": {"game": "Quiplash3", "content_type": "Quiplash3Round1Question", "descriptor_text": "The funniest thing could say right now is .", "prompt": "The funniest thing could say right now is .", "safetyQuips": ["nothing", "bazinga", "I am funny"], "includesPlayerName": true, "x": false, "us": false, "prompt_sound": "", "prompt_file_browse": "", "response_sound": "", "response_file_browse": "", "response_filter": "", "response_transcript": "", "id": "100056"}}, "100057": {"id": "100057", "values": {"game": "Quiplash3", "content_type": "Quiplash3Round1Question", "descriptor_text": "What the founding fathers would say if they were reanimated.", "prompt": "What the founding fathers would say if they were reanimated.", "safetyQuips": ["Brainssssssss", "\"Put me back, please\"", "\"WHAT KIND OF SORCERY IS THIS?\""], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "./external content/Quiplash 3 Sounds/zombie.ogg/", "prompt_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Quiplash 3 Sounds/zombie.ogg", "response_sound": "./external content/Quiplash 3 Sounds/igotnewsforya_response.ogg/", "response_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Quiplash 3 Sounds/igotnewsforya_response.ogg", "response_filter": "poop", "response_transcript": "You think that's funny? Well I got news for ya, poop.", "id": "100057"}}, "100058": {"id": "100058", "values": {"game": "Quiplash3", "content_type": "Quiplash3Round1Question", "descriptor_text": "Stop trying to make happen. It's not going to happen", "prompt": "Stop trying to make happen. It's not going to happen", "safetyQuips": ["a senate that actually does things", "r/prequelmemes", "fetch"], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "", "prompt_file_browse": "", "response_sound": "", "response_file_browse": "", "response_filter": "", "response_transcript": "", "id": "100058"}}, "100059": {"id": "100059", "values": {"game": "Quiplash3", "content_type": "Quiplash3Round2Question", "descriptor_text": "If you looked up the word in the dictionary, you would find a picture of .", "prompt": "If you looked up the word in the dictionary, you would find a picture of .", "safetyQuips": ["pulling a homer", "yep yep yep", "not funny, didn't laugh"], "includesPlayerName": true, "x": false, "us": false, "prompt_sound": "", "prompt_file_browse": "", "response_sound": "", "response_file_browse": "", "response_filter": "", "response_transcript": "", "id": "100059"}}, "100060": {"id": "100060", "values": {"game": "Quiplash3", "content_type": "Quiplash3Round2Question", "descriptor_text": "What the word \"Tyrotoxism\" means at a glance.", "prompt": "What the word \"Tyrotoxism\" means at a glance.", "safetyQuips": ["I have no idea", "Being toxic to Tyro", "The word to say when you don't know anything to say."], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "./external content/Quiplash 3 Sounds/Tyrotoxism.ogg//", "prompt_file_browse": "", "response_sound": "./external content/Quiplash 3 Sounds/yeahobviously_response.ogg//", "response_file_browse": "", "response_filter": "poisoning caused by microbes in stale cheese or milk|poisoned milk|poisoned cheese|poison milk|poison cheese", "response_transcript": "Yeah, obviously.", "id": "100060"}}, "100061": {"id": "100061", "values": {"game": "Quiplash3", "content_type": "Quiplash3Round2Question", "descriptor_text": " has the largest collection of that I have ever seen.", "prompt": " has the largest collection of that I have ever seen.", "safetyQuips": ["socks", "miscellaneous objects", "marshmallows"], "includesPlayerName": true, "x": false, "us": false, "prompt_sound": "", "prompt_file_browse": "", "response_sound": "", "response_file_browse": "", "response_filter": "", "response_transcript": "", "id": "100061"}}, "100062": {"id": "100062", "values": {"game": "Quiplash3", "content_type": "Quiplash3Round2Question", "descriptor_text": "\"I did not .\"", "prompt": "\"I did not .\"", "safetyQuips": ["do anything wrong", "hit her I did not, oh hi mark", "fill out this prompt. This is a safety quip, BAMBOOZLED.\""], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "./external content/Quiplash 3 Sounds/ididntblank.ogg/", "prompt_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Quiplash 3 Sounds/ididntblank.ogg", "response_sound": "./external content/Quiplash 3 Sounds/heallyeah_response.ogg/", "response_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Quiplash 3 Sounds/heallyeah_response.ogg", "response_filter": "like that|like this|think of anything to say", "response_transcript": "Hell Yeah!", "id": "100062"}}, "100063": {"id": "100063", "values": {"game": "Quiplash3", "content_type": "Quiplash3Round2Question", "descriptor_text": "The podcast no one has thought to think of yet: .", "prompt": "The podcast no one has thought to think of yet: .", "safetyQuips": ["The Podcast Podcast", "The Safety Quip Podcast", "Listen to Colors: 3D"], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "", "prompt_file_browse": "", "response_sound": "", "response_file_browse": "", "response_filter": "", "response_transcript": "", "id": "100063"}}, "100064": {"id": "100064", "values": {"game": "Quiplash3", "content_type": "Quiplash3FinalQuestion", "descriptor_text": "Three steps to making your own Frankenstein, guaranteed.", "prompt": "Three steps to making your own Frankenstein, guaranteed.", "safetyQuips": ["1. Get Some Lightning|2. Pull the switch|3. Actually, it's Frankenstein's Monster", "Dig|Up|Graves", "Give Up|You|Tryhard"], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "", "prompt_file_browse": "", "response_filter": "", "response_transcript": "", "id": "100064"}}, "100065": {"id": "100065", "values": {"game": "Quiplash3", "content_type": "Quiplash3FinalQuestion", "descriptor_text": "The only lyrics for one annoying song", "prompt": "The only lyrics for one annoying song", "safetyQuips": ["hit|stuff|hard", "buckle|your|pants", "beep|beep|Oh wait, that's my alarm clock."], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "", "prompt_file_browse": "", "response_filter": "", "response_transcript": "", "id": "100065"}}, "100066": {"id": "100066", "values": {"game": "Quiplash3", "content_type": "Quiplash3FinalQuestion", "descriptor_text": "Three things you can say to stop anything.", "prompt": "Three things you can say to stop anything.", "safetyQuips": ["stop|hammer|time", "you|are|adopted", "I never|loved|you"], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "", "prompt_file_browse": "", "response_filter": "", "response_transcript": "", "id": "100066"}}, "100067": {"id": "100067", "values": {"game": "Quiplash3", "content_type": "Quiplash3FinalQuestion", "descriptor_text": "Why your Smash main?", "prompt": "Why your Smash main?", "safetyQuips": ["what are|you|talking about", "kirbo|is the|besto", "I|spam|PK Fire"], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "./external content/Quiplash 3 Sounds/sexi_response.ogg/", "prompt_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Quiplash 3 Sounds/sexi_response.ogg", "response_filter": "", "response_transcript": "", "id": "100067"}}, "100068": {"id": "100068", "values": {"game": "Quiplash3", "content_type": "Quiplash3FinalQuestion", "descriptor_text": "Your Minecraft Block Tier List, in only three blocks.", "prompt": "Your Minecraft Block Tier List, in only three blocks.", "safetyQuips": ["diamond|soulstone|dirt", "what|is|minecraft", "bedrock|everything else is trash|except maybe emerald"], "includesPlayerName": false, "x": false, "us": false, "prompt_sound": "./external content/Quiplash 3 Sounds/shortodyssey.ogg/", "prompt_file_browse": "E:/Steam Games/steamapps/common/The Jackbox Party Pack 7/games/external content/Quiplash 3 Sounds/shortodyssey.ogg", "response_filter": "", "response_transcript": "", "id": "100068"}}, "100069": {"id": "100069", "values": {"game": "Quiplash3", "content_type": "Quiplash3SafetyQuips", "descriptor_text": "WAH", "value": "WAH", "id": "100069"}}, "100070": {"id": "100070", "values": {"game": "Quiplash3", "content_type": "Quiplash3SafetyQuips", "descriptor_text": "Nice.", "value": "Nice.", "id": "100070"}}, "100071": {"id": "100071", "values": {"game": "Quiplash3", "content_type": "Quiplash3SafetyQuips", "descriptor_text": "Wait, what are we doing again?", "value": "Wait, what are we doing again?", "id": "100071"}}, "100072": {"id": "100072", "values": {"game": "Quiplash3", "content_type": "Quiplash3SafetyQuips", "descriptor_text": "It wouldn't submit in time", "value": "It wouldn't submit in time", "id": "100072"}}, "100073": {"id": "100073", "values": {"game": "Quiplash3", "content_type": "Quiplash3SafetyQuips", "descriptor_text": "I am still cool", "value": "I am still cool", "id": "100073"}}} -------------------------------------------------------------------------------- /screenshots/Quiplash3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/screenshots/Quiplash3.PNG -------------------------------------------------------------------------------- /screenshots/TalkingPoints2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambiguousname/jackbox-custom-content/2ce1324f25f4ca08d0fec383d101468e3c8c19f0/screenshots/TalkingPoints2.PNG --------------------------------------------------------------------------------