[Tutorial] Making a Separate Content Patch

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Post Reply
Message
Author
User avatar
Belgerum
Regular
Posts: 74
Joined: Thu Nov 06, 2014 12:24 am
Projects: Tsun Rising, Eternal Hour
Organization: Seventh Heart Studios
Skype: belgerum09
Soundcloud: Belgerum
Contact:

[Tutorial] Making a Separate Content Patch

#1 Post by Belgerum » Tue Aug 22, 2017 6:04 pm

Not sure if this will be of use to anyone really important, but since I programmed this myself for the sake of a project, I thought I'd share a simple, easy way to make a game with a seperate content patch that can be added to change a Renpy game. This is useful for if you want to remove or change content from a game unless a patch file is present.

This might be used to make DLC patches that add additional routes, characters, or other content to a base game.

It also might be used to make "mature" game patches that uncensor images and add sex or gore scenes to the game script, while leaving that content out of the base game.


Step 1: Set up the directory for the patch

Image

First, you should create a folder in your game directory for the content that will be included in the patch. This is where we will be putting all of the content we want to include in the patch. Make sure that your game can run smoothly even without anything you put in there, since the patch will be detachable from the game later.

Image

Inside the new folder, make an RPY file. We will use this file to control how the patch functions. Also, put any images, music, or files that will be patched into the game. By putting them here, we can make it so these files are included with the patch, and don't take up extra file space in the core game.

Step 2: Make the patch change a persistent variable on game startup, and define patch assets.

Now, we're going to set a persistent variable. First, we put this code in the regular script.rpy, above the start label:

Code: Select all

init -3 python:
    persistent.patch_installed = False
init -1 python:
    if persistent.patch_installed and not persistent.patch_first_time:
        persistent.patch_enabled = True
        persistent.patch_first_time = True
    elif not persistent.patch_installed:
        persistent.patch_first_time = False
        persistent.patch_enabled = False
Then, in the patch RPY file in the new folder you just created, put this:

Code: Select all

init -2:
    $ persistent.patch_installed = True
By placing this code, the game will, when starting the game, set "persistent.patch_installed" to be False. Then, if the patch RPY file is present, it will set it to True. Thus, the variable will always be True if the patch is present in the game directory, and False if it is not.

To go a step further, the game will then set a variable "persistent.patch_enabled," which will determine if the patch has been turned on/off by the player if you put an option in the settings menu to turn the patch off without leaving the game.

Next, in the patch RPY file, define all of the new assets that will only be used in the patch, and are contained in the folder. Do this the exact same way as you would at the top of script.rpy.

For example, if you want to make a content patch that adds Lucy to the game, the patch RPY file might look something like this:

Code: Select all

### This file can be used to define content in the patch, and enable the persistent variable if the patch exists.

### Initial Variable Setup:
init -2:
    $ persistent.patch_installed = True

### Declare characters used in the patched content.
define l = Character("Lucy", who_color="#ffcccc")

### Declare images used in the patched content.
image lucy happy = "patch/images/lucy_happy.png"
image lucy mad = "patch/images/lucy_mad.png"

### Declare music and sounds used in the patched content.
define audio.track2 = "patch/audio/track2.ogg"
define audio.punch = "patch/audio/punch.wav"
Step 3: Add prompts to your script which change the game based on the persistent variable.

Now for the fun part. Throughout your game, you can test the "persistent.patch_enabled" variable to see if the patch is enabled or not. For example:

Code: Select all

if persistent.patch_enabled:
    jump patch_scene
If the patch is enabled, the game will jump to the new label here. You can put the label and new scene in the patch RPY file, if you don't want to include it in the base game.

Code: Select all

if persistent.patch_enabled:
    show uncensored image
else:
    show censored image

Code: Select all

if persistent.patch_enabled:
    "I film her in the party."
else:
    "I f*** her in the p****."
You can use this to have images and dialogue alternate depending on whether the patch is enabled or not like above.

Code: Select all

menu:
    "I love Eileen":
        jump eileen_romance
    "I love Lucy":
        jump lucy_romance
    "I love both girls" if persistent.patch_enabled:
        jump harem_route
You can also add additional choices to menus if the patch is enabled like the example above. The persistent variable switch can be used to alternate content and add new parts to the game in a large number of ways.

You can also place ConditionSwitch on images, if you want the patch to change how certain characters look without altering tons of code. This might be useful for patches that just add cat ears to characters, or swap some images for others. Here's an example:

Code: Select all

image eileen pose = ConditionSwitch(
    "persistent.patch_enabled", "patch/images/eileen_pose_cat_ears.png",
    "not persistent.patch_enabled", "images/eileen_pose.png"
    )
Optional Step: Make changes to the GUI

For those who want some added functionality out of the patch, and have some added knowledge of screens and customization, you can do the same thing above with Repy's GUI as you can with the script.

For starters, you can add some text to the main menu in screens.rpy if you like, so the player can rest assured that they installed the patch correctly.

Code: Select all

        textbutton _("Start Game") action Start()
        textbutton _("Load Game") action ShowMenu("load")
        textbutton _("Preferences") action ShowMenu("preferences")
        textbutton _("Help") action Help()
        textbutton _("Quit") action Quit(confirm=False)
        
    if persistent.patch_enabled:
        text "Patch applied!" xalign 0.0 yalign 1.0
You can also toggle the patch in the preferences menu by defining a function, and adding a binary switch to the screen:

Code: Select all

init 1 python:
    def enable_patch():
        persistent.patch_enabled = True
    def disable_patch():
        persistent.patch_enabled = False

Code: Select all

if persistent.patch_installed:
    frame:
        style_group "pref"
        has vbox

        label _("Patch")
        textbutton _("On") action [Function(enable_patch), SelectedIf(persistent.patch_enabled)]
        textbutton _("Off") action [Function(disable_patch), SelectedIf(not persistent.patch_enabled)]
Step 4: Build a distribution with Renpy, and separate the files.

Now that you've programmed your game , and told it what to do if the patch is enabled or disabled, it's time to build the game distribution and share it with the world! Hit that button on the Renpy Launcher that says "Build Distributions." If you are prompted to add build information to the end of options.rpy, say "yes."

Now that you're sure you have the build information on the bottom of options.rpy, open it up and take a look. You can modify the archive settings if you like, but the most important part is that you archive the patch folder to a seperate file.

Code: Select all

    build.archive("Patch", "all")
    build.classify('game/patch/**.**', 'Patch')
This makes Renpy label all files and subfolders in the "patch" folder we created earlier to be compressed into a single RPA file when you build a game distribution.

After you've finished modifying the code, go back to the Renpy Launcher and finish building the distributions. Extract the ZIP file that is created, and you should find that the patch folder is gone, replaced by a Patch.rpy file.

Image

This is your completed game version, already patched! The distribution will run as if the patch was installed as long as the Patch.rpa stays in the game folder. To make the game run without the patched material, simply move or delete the Patch.rpa file. You can also feel free to rename the Patch.rpa file to whatever you like, as long as it remains a RPA file, and is in the correct directory.

A sample game is attached below, to show an example of how this works. When the patch is installed and enabled, a different ending will occur. Let me know if you have questions.
Attachments
Unpatched Build.zip
The distribution build from Renpy, with the patch file removed.
(30.61 MiB) Downloaded 20 times
Patch.zip
The patch, taken from the distribution build's game folder.
(1.07 MiB) Downloaded 21 times
Source Files.zip
Source files for the project before building the distribution.
(6.45 MiB) Downloaded 20 times

Hesyisytehray
Newbie
Posts: 6
Joined: Tue Dec 12, 2017 6:53 am
Projects: Your Life Is A Failure Simulator
Organization: S.H.Y.
IRC Nick: Hesyisytehray
Tumblr: hesyisytehray
Deviantart: Hesyisytewithhray
Github: hesyisytehray
Skype: Hesyisytehray
Soundcloud: Hesyisytehray
itch: hesyisytehray
Location: SEA
Contact:

Re: [Tutorial] Making a Separate Content Patch

#2 Post by Hesyisytehray » Tue Dec 12, 2017 9:53 am

Nice, actually it will come in handy as long as people finds no better way to replace patches.

Good job!

User avatar
Milkymalk
Miko-Class Veteran
Posts: 521
Joined: Wed Nov 23, 2011 5:30 pm
Completed: Don't Look (AGS game)
Projects: KANPEKI! ★Perfect Play★
Organization: Crappy White Wings
Location: Germany
Contact:

Re: [Tutorial] Making a Separate Content Patch

#3 Post by Milkymalk » Fri Dec 15, 2017 12:13 pm

Good to see this in action, nice tutorial. However, I still wonder how I can actually CHANGE stuff with a patch - I mean stuff that was present before I even thought of the patch. Like, overwrite screens, functions and labels.
Crappy White Wings
Working on: KANPEKI!
(On Hold: New Eden, Imperial Sea, Pure Light)

User avatar
Belgerum
Regular
Posts: 74
Joined: Thu Nov 06, 2014 12:24 am
Projects: Tsun Rising, Eternal Hour
Organization: Seventh Heart Studios
Skype: belgerum09
Soundcloud: Belgerum
Contact:

Re: [Tutorial] Making a Separate Content Patch

#4 Post by Belgerum » Fri Dec 15, 2017 3:19 pm

Milkymalk wrote:
Fri Dec 15, 2017 12:13 pm
Good to see this in action, nice tutorial. However, I still wonder how I can actually CHANGE stuff with a patch - I mean stuff that was present before I even thought of the patch. Like, overwrite screens, functions and labels.
Using this method, you won't be able to directly overwrite anything in a base game. This only works from the perspective of a developer who wants to make a patch for their game, and who can update the base game to allow for the patch's functions.

User avatar
Milkymalk
Miko-Class Veteran
Posts: 521
Joined: Wed Nov 23, 2011 5:30 pm
Completed: Don't Look (AGS game)
Projects: KANPEKI! ★Perfect Play★
Organization: Crappy White Wings
Location: Germany
Contact:

Re: [Tutorial] Making a Separate Content Patch

#5 Post by Milkymalk » Sun Dec 17, 2017 12:45 pm

Yes, but sometimes a developer also wants to change something in order to make the patch work: Update a game menu, add an option in a choice menu for additional branching etc.
Crappy White Wings
Working on: KANPEKI!
(On Hold: New Eden, Imperial Sea, Pure Light)

User avatar
Belgerum
Regular
Posts: 74
Joined: Thu Nov 06, 2014 12:24 am
Projects: Tsun Rising, Eternal Hour
Organization: Seventh Heart Studios
Skype: belgerum09
Soundcloud: Belgerum
Contact:

Re: [Tutorial] Making a Separate Content Patch

#6 Post by Belgerum » Sun Dec 17, 2017 1:20 pm

A developer that wants to patch updates or fixes to a single base game would best find a solution that's simpler and more direct, like replacing the script or archive file(s) in the game directory with a newer version, or just uploading a new build entirely.

I can't predict what situations every developer might face, but in this case, I'm assuming that the developer has control over the base game build, whether it be by patching updates, or because the game is still in development and unreleased.

Either way, that's a problem that would be very situational.

Post Reply

Who is online

Users browsing this forum: No registered users