IMPORTANT!!! Please scroll down and read at least the first paragraph before diving into game files. /*----------------------------------*\ | _____ _ _ | | / ____| | | | | | | | __| |__ ___ ___| |_ ___ | | | | |_ | '_ \ / _ \/ __| __/ __| | | | |__| | | | | (_) \__ \ |_\__ \ | | \_____|_| |_|\___/|___/\__|___/ | | _ | | | | | | __ _ _ __ __| | | | / _` | '_ \ / _` | | | | (_| | | | | (_| | | | \__,_|_| |_|\__,_| | | _ | | | | | | | | __ _ _ __ ___ ___ | | _ | |/ _` | '_ \ / _ \/ __| | | | |__| | (_| | |_) | __/\__ \ | | \____/ \__,_| .__/ \___||___/ | | | | | | |_| | \*__________________________________*/ Hello! If you are reading this, then you might be interested in modifying this game. It is actually possible to add your own things or change the game in ways you want. But keep in mind, if you modify the game, it is your responsibility to figure out why the game doesn't work with your additions or modifications. It is recommended you make an entire backup of the game installation any time you plan on editing game files, that way you can revert back to the original version in case your modifications broke the game... If you are still interested in modding the game, keep scrolling and read below! Now, past this README file, if you look in the /resources/ folder, you will see there are a few different folders. Before I explain in detail what each one does, I will explain the general point of them. * enemy - Where you can mod existing enemies or add your own enemies. * house - Where certain (minor) game flags are set, such as if you have already seen the intro dialog once. * lang - Where every bit of text is loaded from. Keep in mind, the game font (probably) only supports A-Z. * sound - Where some sound effects are loaded from. You can add your own, so long as they are .ogg files! * sprites - Where majority of in-game sprites are loaded from. The largest and most particular part of these folders is the sprites folder. Before I go into depth, this is something important you should note. Due to the way this game functions, contents can be loaded from both the game's /resources/ folder (where you are seeing this text file from) and from its AppData folder. Contents in the /AppData/Local/ folder are prioritized over your installation directory's /resources/ folder. To find the /AppData/Local/ folder, go into Windows Explorer. Then, type %appdata% into the directory bar up top. Once you go there, you may be in a folder named Roaming. Go up one level out of that directory, and go to the Local folder instead. If you have played to at least the 2nd floor in-game, the main.sav file will also be here. It is encoded in Base64, if you so wish to edit your save as well. When permanent flags are set, they will go into this folder, in /resources/house/. Now, to explain the /sprites/ folder: === /_progen/ === In here, every type of room is stored. Their exact naming schemes are important, as are most other sprites with how they are labelled. If you want to add your own rooms, you must use specific colors and name them in certain ways. Next to this README.txt file is also a KEY.png file. In this file, you can color pick each and every color it has to make specific placements in your custom rooms, however you want! If you are going to use a tool, make sure you don't have any anti-aliasing enabled. Use EXACT colorations as per the KEY.png file, and it is recommended that you draw with a brush size of 1 or the pencil tool, to make it easiest. When naming your custom rooms, you must name it with the prefix "spr_progen_". If it has this, it will be recognized by the game's room compiler. After that prefix, you must specify which kind of room it is. Here is a list of room types you can choose from, and a brief explanation of what they are: * branchb - Room with doors that go up, right, and left. * branchl - Room with doors that go up, right, and down. * branchr - Room with doors that go up, left, and down. * brancht - Room with doors that go left, right, and down. * cornerbl - Room with doors that go up and right. * cornerbr - Room with doors that go up and left. * cornertl - Room with doors that go right and down. * cornertr - Room with doors that go left and down. * cross - Room with doors that go up, down, left, and right. * hallh - Room with doors that go left and right. * hallv - Room with doors that go up and down. * roomb - Room with a door that goes up. * rooml - Room with a door that goes right. * roomr - Room with a door that goes left. * roomt - Room with a door that goes down. * start - The starting room of a floor. It must have a door that goes left, right, and upstairs. Aside the room sprite files, you should see ".progen" files. These are pre-compiled versions of the room sprites that are stored into a file. If they are deleted, or the corresponding ".png" sprite file is modified in any way, the game will automatically regenerate the files. Even room sprites that you add! It is recommended you structure your custom rooms in the exact way that you see some of the existing rooms. If a room type requires certain doors, you should include those, or else the game will probably break, crash, or become unplayable. Your room sprites also do not need to be even dimensions - they can be 16x16, 16x29, 31x8, whatever you want. Keep in mind that the larger your room is, the longer it will take the game to compile it and the game may perform worse inside of them. When the game compiles a new .progen file, it will not go into the installation directory's resource folder. Instead, it goes into its /AppData/Local/Ghosts_and_Japes/ directory, as explained previously. <<>> Also, if you delete a .png file, the game will not add it to the rooms that will generate in-game list, even if the .progen file exists. Now, if you want a name example of how to name your room sprites, here is an example: "spr_progen_hallv_my_cool_hall.png" To recap, you need the "spr_progen_" prefix followed by the type of room it is, such as "hallv". Then you can put any name afterwards to make it distinctly named from other rooms in the folder. Once the game is ran once, it should generate the ".progen" file for it and place it in /AppData/Local/Ghosts_and_Japes/ === /(insert enemy name here)/ === You'll see every enemy's sprites are stored in folders named after themselves. Them being named this way actually doesn't matter, it is for organization purposes. You could, for example, have an enemy named "cool_ghost" but it loads its sprites from the "uncool_ghost" folder just fine. Each and every enemy is required to at least have these sprites: eat, gameover, grabbed, idle, MASK, and painting. Another important requirement is that the color used for transparency is the top-left most pixel. If your enemy, or any other sprites, is rendering weirdly in-game, check the color you used for transparency. Also, animated sprites need to have square-shaped frames. So, if you wanted an animated sprite with 4 frames, and your enemy is 16x16, the sprite sheet should end up being 64x16. Dialog portraits are completely optional, if you want your enemy to even speak at all. The names are generally self-explanatory; here is what they mean and when they are used: * eat - When the enemy is currently attacking the player. * gameover - Displays after the enemy has defeated the player. * grabbed - Frame 1 displays when the enemy is stunned by the player's flash. The whole sprite animation plays while the enemy is being siphoned by the player's wand. * idle - The general animation that the enemy uses. * MASK - The enemy's interaction range. If the player's MASK collides with the enemy's MASK, interactions can happen; such as the enemy beginning to attack the player or the player being allowed to talk to the enemy. * painting - When an enemy spawns in a room, they are able to replace pre-existing paintings with their own. * portraits - Optional. Can be used in the strings_en.txt file to make a textbox sequence render that portrait. === /painting/ === In this folder, random paintings that are placed around the household are loaded here. These MUST be named with the prefix "spr_obj_painting_". Anything after that is fine. Once again, make sure the top-left most pixel is the color you want to use for transparency, or it will render wrongly in-game. So, for example, you can have a painting sprite file named "spr_obj_painting_my_coolpainting.png" === /player/ === The sprite names for the player are hard-coded. The portraits are optional, as they are only used for textbox sequences seen in the strings_en.txt file. If you are going to edit the player sprites, you must name them in the exact same way as you found them. The player must have at least these sprites: * spr_player_down_idle * spr_player_down_move * spr_player_generic_gameover * spr_player_ko * spr_player_left_idle * spr_player_left_move * spr_player_MASK * spr_player_right_idle * spr_player_right_move * spr_player_up_idle * spr_player_up_move The player MASK sprite is used for the player's interaction collision range. If it is overlapping with an enemy's interaction mask, interactions such as attacking or chatting can occur. The generic gameover sprite is used for if the player is somehow defeated by something other than an enemy. === /projectile/ === All projectile animations are loaded from here, you can add your own projectiles to your custom enemies. Now, the /sound/ folder. This folder is simple; if you want to add your own custom sounds for your enemies, their projectiles, or dialog, you place them here. All they have to be is in .ogg format. If they sound wrong in-game, make sure your sound file does not have a strange sample rate. The /lang/ folder is where nearly all text, aside from a few hard-coded in-game ones, are stored. You can also add new dialog in here with certain special effects. Per-enemy dialog should be placed in their own respective JSON files, unless there's something specific you need a dialog they speak to do. I will explain how our text formatting system works: Here, I will list an example text: "txt.cool_string_name.0": "[speaker.spr_portrait_player_neutral.snd_vox_player][color.rgb.0,255,0][motion.wavy.20]Hello,[wait.amount.5] [reset][motion.rando.1]world!" The first value, ' "txt.cool_string_name.0": ' is the text's identifier. This is to be used in the enemy json files to make it actually load and be used in-game! The second value is where the actual text contents are. There are a few text formatting options to make your dialog more fun to read. I will explain them in order of how they appear in the example text above, in order, then list all of the text formatting options below that: * [speaker.spr_portrait_player_neutral.snd_vox_player] - Here, you can set who is speaking aloud the dialog. If you remove this entirely, it will be read generically by the game. After the first ".", the name of the portrait sprite must be specific. Then, after the second ".", you specify the voice sound they use for reading their dialog. * [color.rgb.0,255,0] - You can color your text to any color by supplying RGB values. In the example text, the text "Hello," would show up in green. * [wait.amount.5] - How long to wait before reading the letters after this tag. In this case, it's 5, which means it waits 5 frames. This game runs in 30 FPS. * [motion.wavy.20] - This moves every individual letter it affects in an orbit around its origin point. The 20 means its orbit rotates 20 degrees every frame. * [reset] - This is used to reset and remove all previous text formatting that were applied to the text. So, after the green "Hello," that had wavy letters as well, the text after the "[reset]" would no longer have wavy motion or green applied. * [motion.rando.1] - Moves each letter in a random direction around its origin point, which has the effect of looking jittery. The number specified, 1, means each letter moves between -1 to 1 around the letter's origin point on its x- and y- axis. IMPORTANT!!! With these text formatters, you must not ever put it at the very end of a string. If you want to do that, you must add a blank space directly after it. If the string ends with the formatting tag and no space afterwards, the game will crash once it reads that text formatter. This is important to note, for things such as enemies talking to you automatically while attacking you. For those, you will want to place, for example, a [wait.amount.30] afterwards or else their dialog disappears immediately as soon as they're done talking. Next, here is a list of every single one of your text formatting options: * [speaker.sprite_name_here.audio_name_here] - With this tag, you place it at the start of a text, and that will tell the textbox to render "sprite_name_here" on the left inside of the textbox, and play "audio_name_here" for every time a letter (a-z) is read in the textbox. This is used to make text be spoken by a character, for instance. * [color.rgb.255,255,255] - With this tag, you can color text after its position in the text using RGB values separated by commas. With a value of 255,255,255 this results in the text after it being rendered as white. Note that by default, in textboxes with no color tag, the text is rendered in white. This applies to after a [reset] tag is read. (see below for more info on [reset]) * [motion.motion_name_here.value] - There are a few different options with this tag. For the first argument, "motion_name_here", you have these options: - "rando" makes the text have a jittery appearance. "value" dictates how much the text jitters away from its origin point, in pixels. Values MUST be whole numbers (integers). - "wavy" makes the text orbit around is origin point. "value" dictates how many degrees to rotate around its origin per frame. Note that the game runs in 30 FPS. Value must be whole numbers (integers). - "singsongy" is the exact same as "wavy", but it will not move left or right. Value must be whole numbers (integers). - "wobbly" is the exact same as "wavy", but it will not move up or down. Value must be whole numbers (integers). * [wait.amount.value] - Use this tag to indicate when the text should wait a certain amount of frames before continuing. Replace "value" with the number of frames to wait before continuing to read the text. So, for example, [wait.amount.30] would make the textbox wait 30 frames (or 1 second, since the game runs in 30 FPS) before continuing to read. Finally, depending on the contents of the text identifier key, the textbox can behave differently. * Text identifier ends with ".choicec" - You can specify choices the player can input, at the end of the string, separating them with | * Text identifier ends with ".grab_player" - After this text finishes, the initiator of the text (if an enemy) will grab the player and begin attacking them. The /house/ folder is where certain specific permanent game flags are set. At the time of writing this, there are currently 4 "house" files, as I will continue to refer to them by. These files are files that have no file extension, and continue 1 line of very basic info. If you can't find a specific house file, check /AppData/Local/Ghosts_and_Japes/resources/house/ and the game installation's directory's /resources/house/ folder. Here is what each one does currently: * size - Here, you can specify how large every floor of the game should be. It is a default of 16, has a minimum of 8, and a maximum of 3072. It is highly recommended you leave this alone, as 16 feels best to play. 8 means every floor is very short, and 3072 means every floor is gigantic. Your computer may crash or be unhappy if you do anything especially in the thousands. The game is probably also impossible to beat with floor sizes that large. * grave - This file keeps track of how many enemies you have defeated across all playthroughs. * intro1 - A simple file that keeps track of whether or not you saw the player's intro dialog from playing for the first time. * item_swap_explanation - Similar to "intro1", keeps track of whether or not you had alternate item hand swapping explained to you yet. * opt_music - Options menu setting; where the music toggle is stored. * opt_window_fullscreen - Options menu setting; where the fullscreen toggle is stored. * opt_window_scale - Options menu setting; where the window scale is stored. Now this is where the fun begins, with the /enemy/ folder. Here, you can add your own enemies just like how you've seen all the others in-game. You'll see that there may be .inactive files. These are example files to help you, as some things might not even be used in the game currently. Similarly, if you ever want to disable an enemy, just delete them or change their file extension from .json to anything else that doesn't start with .json. It would be most easiest to explain how you make your own enemy by looking over existing ones, copying their file, then modifying it to how you see fit. Open up angello.json and look over its contents, for example: There are 3 main structs (things that go between {}) in enemy json files, with 1 of them being entirely optional. === "Anim" === The 1st required main struct. Here, you specify the way your character looks in-game. Your enemy is required to have these animations, or it will not be imported at all: * idle - The general sprite the enemy uses. * grabbed - The 1st frame is shown when the enemy is stunned by a flash of light. While it is being attacked by a player, the entire animation plays. * eat - Plays while the enemy attacks the player. * painting - The enemy's room painting replacement. If you didn't know, when enemies spawn, if there's a painting in a room, there's a chance they can possess a painting when spawning. * game_over - The enemy's game over art that is displayed if it defeats the player. * MASK - The enemy's interaction range collision mask. Some of these have sub-values of "sprite", "speed", "glow" and "offset" as you can see. * With "sprite", you specify the name of the sprite file. * With "speed", you specify the frames per second of the sprite's animation speed. If you set it to 0, it will not animate. * With "glow", you specify the sprite's visibility. If it's set to true, the enemy will render above darkness and fog layer. NOTE that this only applies to anims of "idle", "grabbed", "eat", and "projectile". * With "offset", you specify how far on the x- and y- axis it should be rendered from its origin point. This is only used by the anim "game_over", to adjust the sprite's positioning. Depending on the enemy's behavior, specified in the main struct "Specs", there will also be an anim section labelled "projectile", which is an array of structs. See magi.json, for example. Here, this enemy has a list of structs for anim "projectile". Enemies do not have a shooting animation, but here is where you specify what projectile they should randomly shoot. If you only list one projectile, that one will be chosen every time, of course. I will now explain the "projectile" struct and what each value means. * sprite - The sprite that the projectile uses. It can be animated or not. * speed - The animation speed (in frames per second) that the projectile sprite animates. * move_speed - How fast the projectile should move, in units per frame. * shoot_speed - How fast the enemy should shoot when it is time to shoot, in seconds. Note that this doesn't have to be a whole number. So, for example, 0.5 seconds is okay. With "shooter" behavior, this means it will shoot "shoot_speed" seconds after it re-appears from disappearing. With the "polluter" behavior, it means it will shoot this projectile every "shoot_speed" seconds while it's around. * behavior - The projectile's behavior. Currently, there are 3: - "homing" - Will follow the player - "normal" - Will travel in a line in the direction it was shot out from - "normal_rotate" - Same as "normal", but it rotates towards where it's going * lifetime - How long, in seconds, the projectile should (at most) be allowed to live for before disappearing. Again, non-whole numbers are okay. * glow - Similar to how enemy glow value behaves. If it's set to true, it will render above the darkness and fog layers, and vice versa for false. * hit - Here, you specify what the projectile should do upon it hitting the player. For "effect", you can set it to... - "entrance" - Causes the player to forcefully walk towards whoever hit it. - "shrink" - Causes the player to shrink, rendering them unable to fight back. - "damage" - Deals flat damage to the player. For "amount", what you specify here depends on the "effect". - "entrance" - How long the player should be entranced. - "shrink" - How long the player should stay shrunken. - "damage" - How much damage to deal to the player. * sound - The sound effect to play when the projectile is shot. === "Specs" === The 2nd required main struct. Here is an explanation of each sub-value of "Specs": * behavior - How the enemy should behave. You can set it to: - "normal": Enemy chases the player as usual. - "shooter": Enemy chases the player, but also shoots a projectile towards the player after it re-appears. - "polluter": Enemy chases the player, but also shoots a projectile towards the player in a certain interval. - "friendly": Enemy roams around randomly, and can be spoken to if the player is near. * appearances - An array of where this enemy should spawn. Currently, there are 6 unique values you can enter: - "normal" - "dungeon" - "overgrowth" - "water" - "boiler" - "dark" If your enemies in the enemy folder lack an enemy for a specific floor of the house, that floor will use completely random enemies. So for instance, if there's no enemies in your folder that spawn on the boiler room floor, then it will just choose from every enemy at random. * health - Has 2 sub-values: - "amount": The amount of health the enemy has. - "loss": How fast the enemy loses health from being siphoned by the player. * damage - How much damage the enemy does to the player every time it attacks it. * struggle - How many times a player must press left or right to escape from it. * speedup - How many "eat" "Anim" loops must play before the enemy begins to attack faster. * sound - The sound to play every time the enemy attacks a player. * speed - How fast the enemy should move, in units per frame. Can be a non-whole number. * panic - Has 2 sub-values: - "panic": The movement speed multiplier for how fast the enemy should move while being siphoned by a player. - "randomness": A random range of angles for movement direction that the enemy should move in away from the player that is siphoning it. Larger numbers make it behave more erratically, while lower numbers make it move in a more straight-like line. Can be non-whole numbers. * pull - Has 2 sub-values: - "amount": How long a player can pull on the enemy while siphoning them, in seconds. Can be a non-whole number. - "cooldown": How long a player has to wait, after pulling for "pull.amount" seconds, before a player can pull on it again, in seconds. Can be a non-whole number. === "Dialog" === The 3rd, and final, OPTIONAL main struct. Here, you can make your enemy talk to the player as it interacts with them. It is best that you look over the enemy json files to understand how you structure your own. Basically, every dialog is an array of arrays (lists of lists), meaning it's lists of text sequences to display. The sequences are chosen at random, so if you only specify one sequence in the list, it will be that one every time. Every dialog entry is optional, so if one is missing, the enemy just won't talk for that specific trigger. See kaishi.json for an example of using both lang file-loaded strings and embedded strings. With enemies, you can directly write their dialog into their JSON. Alternatively, you can write their dialog entries as lang file keys, for example, "txt.my_enemy.0", and so on. You must use the lang file if you want certain special effects from dialog triggering; this is most useful for ghosts such as friendly ghosts. You must have a lang file entry with ".choicec" in its key if you want to be able to make a dialog with choices. You can also put ".grab_player" in a lang file entry's key to make it so once that dialog entry ends, the enemy speaking immediately grabs the player. Again, it is best to look over existing files and see how it works. Copy over existing json code and change it to your needs. Now, instead of providing exhaustive examples, I will explain what every trigger is: * initial_grab - When the enemy initially grabs the player. * hide_and_seek - When the enemy initially grabs a player that was hiding in a box. * entrancement - When the enemy initially grabs a player under the effects of entrancement. * shrunk - When the enemy initially grabs a player under the effects of being shrunk. This dialog takes priority over the entrancement dialog. * taking_too_long - When the player has not resisted against the enemy for a long time. * power_outage - When the power goes out and the enemy exists. * health_milestone - Sub-values: - sixtypercent - When the player being attacked by the enemy has reached 60% health remaining, as long as the enemy can deal more than 0 damage. - thirtypercent - When the player being attacked by the enemy has reached 30% health remaining, as long as the enemy can deal more than 0 damage. - love_two - First dialog that plays after a while of the enemy healing the player, as long as the enemy deals less than 0 damage (is a healer). - love_three - Second dialog that plays after a while of the enemy healing the player, as long as the enemy deals less than 0 damage (is a healer). - love_four - Third and final dialog that plays after a while of the enemy healing the player, as long as the enemy deals less than 0 damage (is a healer). * gameover - Sub-values: - attempted - When the enemy has defeated the player, and the player has resisted at least once. - gaveup - When the enemy has defeated the player, and the player didn't resist at any point. * absorbed - After the game over sequence, if the player waits until the enemy has stolen all of the player's max health, this dialog will replace the game over sequence. * talk_to - Only applicable for enemies with a behavior value of "friendly". Sub-values: - main - The main dialog to display upon talking to the enemy for the first time. - response1 - The dialog to display upon selecting choice 1 in the main dialog. - response2 - Same as above, but with 2nd choice from main dialog. - response3 - Same as above, but with 3rd choice from main dialog. - response4 - Same as above, but with 4th choice from main dialog. - response5 - Same as above, but with 5th choice from main dialog. - response6 - Same as above, but with 6th choice from main dialog. - response7 - Same as above, but with 7th choice from main dialog. - response8 - Same as above, but with 8th choice from main dialog. - response9 - Same as above, but with 9th choice from main dialog. If you read everything above, you are awesome. Happy modding! <3