Skip to content

Write dialogue and narration for Ren'Py projects much quicker and easier, all without those pesky quotes!

License

Notifications You must be signed in to change notification settings

hasnogame/RenPy-Script-Builder

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ren'Py script builder

Introduction

The Ren'Py script builder was formed out a desire almost entirely to not have to type "" on every damn line! Most of the ideas pretty much grew from that and having to work around writing scripts without quotation marks while still maintaining the basic level of flexibility that Ren'Py offers.

The builder will take more manageable, human readable/writeable script file(s) and turn them into something Ren'Py can understand. Not that Ren'Py scripts aren't human readable, but the goal is simply to cut down on some of the tediousness of writing dialogue and narration in the format that Ren'Py requires.

Please keep in mind that this tool is only intended to help speed up the actual script part of your game (the dialogue and narration and stuff). You should still do the more programming intensive stuff, such as init and gui programming in pure rpy.

Table of Contents

Usage

To convert a .rps script file into a Ren'Py v6.99.11 compatible .rpy file(s), download the rpsb.py file to the location of your choice then start up a cmd or bash shell in that directory then run python rpsb.py input-file where input-file is the path to the file that you wish to convert.

The file(s) will be output in a location relative to the source document (by default, this will be in the same directory as the input file's path). This can be changed by setting output_path (see Configuration).

What it Does

the builder takes the input file and goes over it, reading each line and deciding what to do with it:

  • Empty lines are ignored.
  • Any line starting with a # is a comment and is ignored.
  • If the line starts with a : it is interpreted as a command.
    • If the command is a known command or matches a line replacement then it is interpreted and output.
    • Otherwise, the command (and any block it opens) is copied verbatim, sans the leading :.
  • A line starting with any of the special characters are treated accordingly.
  • Finally, if a line doesn't match any of the above requirements, it is first checked for any prefix replacements. Any portion of the line that isn't prefix replaced is wrapped in quotes (first escaping any pre-exiting quotes to preserve them) and output.

Comments

Any line beginning with a # is treated as a comment and is skipped over during parsing. Note: Unlike python, the # is not treated as a comment when it appears in the middle of a line. It must be at the beginning of the line to be treated as such.

By default, only lines starting with ## are copied to the output and all other comments are simply ignored. See Configuration

Blocks

A : at the end of a line, opens a new block. Blocks are indented in the same way as python and are closed in the same way.

Commands

Any line starting with a : is a command. The command is between the the : and the first space or trailing :

Any command not recognized by the interpreter is simply transposed as is into the output file, sans the leading :. Any block that is opened by this unknown command is also transposed as is. You can use this output pure code into the output file.

:init python:
	chars = ["Sarah", "George"]
	new_chars = []
	for char in chars:
		new_chars.append(char + "_happy")

Line and Character Replacement

:line find = replace
:character find_character = replace

:line:
	find = replace
	...
:character:
	find_character = replace
	...

These commands focus on replacing certain elements in each line.

  • :line find = replace defines a line replacement; if an entire line comprises of find it will be replaced by replace

    Example: :line tr_fade = with Fade(0.33) will look for lines that consist only of tr_fade and replace that line with with Fade(0.33) in the output.

  • :character find = replace defines a character replacement. The interpreter scans the beginning of the line, looking for find followed by a space and replaces it with replace. Use this for character prefixes on spoken script lines.

    Example: :character m: = Mick will look at the beginning of each line and replace m: with Mick before quoting the rest of the string so that m: Hello, World! becomes Mick "Hello, World!" in the output.

To define multiple replacements in a single go, you can instead place a : at the end of the command and list your replacements in a block following it. Example:

:line:
    tr_dis = with dissolve
    tr_fade = with Fade(0.75)
    park = scene bg park

:character:
    d: = David
    a: = Annie

You can specify multi-line replacements using \n

:line dorm = scene bg dorm\nwith dissolve

becomes

scene bg dorm
with dissolve

Substitutions are quite flexible and can even be used to create new commands and/or replace otherwise odd strings.

:line:
	:dance = $ dance_func()
	>> {+} = scene {}\nwith time_skip_short
	>>> {+} = scene {}\nwith time_skip_long

Wild card Matches and Regex

Wild cards can be used to match slightly varying strings.

  • ? will match 1 or 0 characters :line some?thing will match something and some_thing but not some other thing
  • * will match any number of character, including 0 :line some*thing will match each of something, some_thing and some other thing
  • + will match any number of character but will always match at least 1. :line some+thing will match some_thing and some other thing but not something

You can use {} to capture a wild card match and substitute it into the output using {} where n is the matched group. Note: This is different than in python, where () are normally used to capture matches, instead here, {} are used.

:line dis({+}) = with Dissolve({})
dis(0.75)

becomes

with Dissolve(0.75)

Substitutions will match in order, so :line foo{*}bar{*} = $ some_func({},{}) will substitute the first parameter with the match following foo and the second parameter will be substituted with the match following bar

You can control this behaviour by using numbered substitutions in the replacement string {n} where n is the match group. Using the above example :line foo{*}bar{*} = $ some_func({0},{1}) now substitutes the first parameter with the match following bar and the second with the match following foo. Note: Substitutions are zero indexed as in python, so {0} is the first match, {1] is the second and so on.

If you need to have any of ?*+{} or \ in your match string, you must prefix it with a \. The same goes for if you wish to have {} or \ in your replacement string.

Labels

::label_name
::label_name:
    ...

The label command can be used to add labels to each section of your script and is used as such: ::label

you can also specify sub labels using standard dot notation: ::.sub This will group the label under the most recent parent label.

Sub labels can be grouped under a specified parent label however by using ::parent.sub

::parent1
::.sub1
This sub1 label is grouped under parent1
::parent2.sub1
This sub1 label is instead grouped under parent2
::.sub2
This is part of parent2
::parent1.sub2
This label is listed under parent1 again

becomes

label parent1
    label .sub1
        "This sub1 label is grouped under parent1"

label parent2
    label .sub1
        "This sub1 label is instead grouped under parent2"

    label .sub2
        "This is part of parent2 too"

label parent1.sub2
    "This label is listed under parent1 again"

Note: The label command doesn't need to open a new block with a trailing : and it is optional to include it or not if you find the layout better. If you do include the trailing : then the following lines must be indented as a block. The output will be the same regardless if you use labels as block openers or not and you can mix and match.

::non-block
The label here doesn't open a block and so this line shouldn't be indented.

::block:
    The label here does open a block and so this line must be indented.
    ::.mix
    You can also mix block/non-block types just fine.
    Just as long as you maintain correct indenting.

Scene, Show and With

:sc scene_name
:s image_name
:w transition

To show a new scene, simply use :sc scene_name where scene_name is the name of the new scene to show. Likewise, use :s image_name to show an image (typically a character) on screen. You can even use at location as you would normally like :s emily annoyed at right

Transitions can easily be done also using :w transition where transition is the transition to perform

:sc town night
:w Dissolve(1.25)

Music and sound

:p channel audio [clause]
:pm music [clause]
:ps sound [clause]
:pa audio [clause]
:v voice
:stop channel [clause]

To play music and sounds use the :p channel audio where channel is the channel to play the audio on. You can optionally specify a clause to add, such as fadein or loop to the end of the command.

:pm :ps and :pa are simply syntactic sugar for playing audio on the respective channel. For example the following are equivalent:

  • :pm some_music fadein 1.2 <-> :p music some_music fadein 1.5
  • :ps a_sound <-> :p sound a_sound
  • :pa "bang.ogg" <-> :p audio "bang.ogg"

To stop a channel from playing audio, simply use :stop channel_to_stop

The :v voice command can be used to play a voice along with a line of dialogue.

Note: when specifying an audio file, you must enclose the file name in quotes as the parser won't do this for you.

Flow control

:c label_name
:j label_name
:r

Having labels is all well and good, but a large part of Ren'Py is being able to move control from one place to the next. This is typically done in two ways: the jump keyword and the call keyword.

In your script, these are replaced by :j and :c respectfully and otherwise operate the same way.

When calling another label though, it is expected to return at the end of the called label. To do this in your script, just use :r

Choices

:choice:

Choices are an important part of almost any visual novel and in Ren'Py this is accomplished through the menu: keyword.

The choice dialogue is crafted in the same way as in Ren'Py using :choice

:choice:
	d: This is a bit of dialogue accompanying the choice
	This is the first choice:
		:c choice1
	This is the second choice:
		:c choice2

becomes

menu:
	DAVID "This is a bit of dialogue accompanying the choice"
	"This is the first choice":
		call choice1
	"This is the second choice":
		call choice2

if, elif and else

:if condition:
:elif condition:
:else:

If, elif and else are used in exactly the same way as in Ren'Py but must be prefixed with a :.

NVL

:nvl:
:clear
:close

:nvl begins an nvl block. Each script line has NVL as the character and each prefixed script line has _NVL appended to the replaced prefix.

:nvl:
    This is an NVL block of text.
    As opposed to the typical ADV style that most script lines are read as.
    a: It sure is!

will become:

NVL "This is an NVL block of text."
NVL "As opposed to the typical ADV style that most script lines are read as."
Annie_NVL "It sure is!"

To clear the NVL dialogue box, simply use :clear inside the NVL block. :clear will have no effect when used outside of an NVL block.

You can change the NVL character, NVL suffix and/or prefix using the :config command

Import

:import file_name

For large games, you want to keep your source script separated into different files, perhaps to keep different arcs or paths in your story separate, or this might be useful when you have multiple writers, each working on a different portion of the script. Whatever the case, the :import file_name command (where file_name is the relative path to the file to be imported) will pull all these files together.

When called, the :import statement will stop parsing the current file at that point, open the new file, parse all of its contents (respecting any further imports) and then return to the importing file, continuing where it left off. Thus the position and order of your import commands are important.

If you wish to import multiple files in a row, you must specify an :import statement for each one.

File

:file file_name

You can specify that a new file be created at any point you like, provided you aren't inside of a block. To do this use the :file file_name command where file_name is the file name to use. If the file extension if left off then .rpy is appended before creating the file.

When you create a new file, all output from that point onward, until the end of the script, the next :file command is encountered or the next parent label is encountered (providing create_parent_files is set to True).

Logging

:log level message
:break

You can log an output to the log file and/or console window by using the :log level message command, where level is an integer from the table below and message is the message to log.

The :break command can be used to cease execution of the script builder at that point.

Log Levels

int Logging Level
0 VERBOSE
1 DEBUG
2 INFO
3 WARNING
4 ERROR

Special Characters

The interpreter respects a few special characters from Ren'Py:

  • $ placed at the beginning of a line, treats that line a python line just as Ren'Py does and simply copies the line directly to the output.
  • # begins a comment and is ignored during parsing. You can force the interpreter to copy comments to the output by setting copy_comments to True By default, a line starting with ## will be copied over, regardless of the copy_comments setting.

Configuration

:config option = value
:config:
	option = value
	...

Some things can be configured about the script interpreter itself. This is done through the use of the :config opt = val command where opt is the configuration option to change and val is the new value. You can also use :config: as a block to set multiple configuration options at once.

Although you can change configuration option at any point in the source script, and may be occasionally desirable to do so, it is often wisest to do all configuration at the very beginning of the script to avoid unexpected behaviour.

List of Config Options

The following are the available configuration options and their associated default values.

  • create_parent_files = False If set to True then the interpreter will create new files based on each parent label (labels without at least one leading .) and will place all sub labels under a parent label in that parent label's file. It will be likely that, unless you are making a kinetic novel, you will need to edit this file after the interpreter has run.
  • create_flow_control_file = True When set to True a master flow control file will be created, which will call each label in the order that they appear, respecting the flow_control_ignore list. Set this to False if you want to do this manually.
  • flow_control_ignore = ["*.choice*", "*_ignore*"] A list of label names to ignore when generating the master flow control file. Regex like matches can be used as defined in the section Wild card Matches.
  • copy_comments = False When set to True comments in the source document will be copied as is to the output.
  • copy_special_comments = "#" When set to a string, comments beginning with that string will be copied as is to the output, regardless of the copy_comments setting. By default, any line begging with ## will be copied to the output, and other comments will simply be ignored.
  • nvl_character = "NVL" Sets the character used for the NVL narrator.
  • nvl_prefix = "" Set the prefix applied to character names during NVL blocks.
  • nvl_suffix = "_NVL" Set the suffix applied to character names during NVL blocks.
  • output_path = "." Set the relative or absolute output path for the generated script files. The default "." will create the files in the same location as your script file.
  • auto_return = True When set to True, the script will automatically insert return statements at the end of each label block, mostly eliminating the need for the :r command.
  • abort_on_error = True If True, when an error is encountered, script execution will abort at that point. Setting this to False will force the script ignore the error and continue parsing. The :break command will still break processing, even if this is set to False.

Syntax Reference

Comments

key Definition
# Comment
## Comment (copied to output)

Commands

key Definition
: Start command
:line find = replace Entire line find and replace
:line: Entire line find and replace (multiple)
:character find = replace Character (line prefix) find and replace
:character: Character (line prefix) find and replace (multiple)
::label_name Label
:sc scene Show scene
:s image Show image
:w transition With transition
:p channel sound Plays sound on channel
:pm music Short cut for playing music on the music channel
:ps sound Short cut for playing sound on the sound channel
:pa audio Short cut for playing audio on the audio channel
:v voice Plays a voice
:q channel sound Queues sound up on the named channel
:stop channel Stops playing audio on the named channel
:c label Call label
:j label Jump to label
:r Return
:choice: Create menu dialogue
:if condition: if statement
:elif condition: elif statement
:else: else statement
:nvl: Opens and NVL block
:clear outputs an nvl clear statement
:import file_name Import and parse another file
:file file_name Set a new output file
:log level message Log a message at a given level to the console and/or log file
:break Stops execution of the script builder at that point
:config option = value Change the value of a config option
:config: Set multiple config options at once
:UNKNOWN Unknown commands (and any blocks they open) are output as is

Wildcards

key Definition
? Matches 1 or 0 characters
* Matches 0 or more characters
+ Matches 1 or more characters
{} Captures match for replacement in the output string
\ Escape wildcard characters

Config Options

key Default Definition
create_parent_files False If True, create files based on parent labels
create_flow_control_file True If True, create a master flow control file
flow_control_ignore ["*_choice*", "*_ignore*"] A list of label names to ignore when generating the master flow control file.
copy_comments False If True, copy all comments to the output
copy_special_comments "#" Comments starting with this string, will always be copied to the output. Disabled if set to None
nvl_character "NVL" The character used for the NVL narrator
nvl_prefix "" The prefix applied to character names during NVL blocks
nvl_suffix "_NVL" The suffix applied to character names during NVL blocks
output_path "." The output path for generated files
auto_return True If True, automatically insert return statements at the end of each label block
abort_on_error True If True, ignore any errors encountered

Log Levels

int Logging Level
0 VERBOSE
1 DEBUG
2 INFO
3 WARNING
4 ERROR

About

Write dialogue and narration for Ren'Py projects much quicker and easier, all without those pesky quotes!

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 89.2%
  • Ren'Py 10.8%