Simple scene library for Ren'Py

  1. # Copyright (c) 2010 Karl Knechtel, Chris Charabaruk
  2. # Licensed under the zlib/libpng license
  3. # see http://www.opensource.org/licenses/zlib-license.php
  4.  
  5. # Documentation is in-line with the source, or visit
  6. # http://labs.coldacid.net/code/simple-scene-library-renpy
  7. # for further details.
  8.  
  9. init python:
  10.    
  11.     # show_scene_ actually plays the scene, show_scene lets us use it
  12.     # from a button.
  13.     def show_scene_(scene_id):
  14.        
  15.         # Put a halt to music, in case the scene starts without any
  16.         renpy.music.stop(fadeout=0.0)
  17.        
  18.         # Now we actually start running the scene!
  19.         renpy.call_in_new_context(scene_id)
  20.        
  21.         # Restore the main menu music; if it's the same as the scene's
  22.         # music, restart it from the beginning.
  23.         renpy.music.play(config.main_menu_music, if_changed=False)
  24.        
  25.         # This is what ui.interact() will return below.
  26.         return True
  27.    
  28.     show_scene = renpy.curry(show_scene_)
  29.    
  30.     # Call this with a button name and a scene label to define a scene button.
  31.     def scene_button(name, scene_id):
  32.        
  33.         # This lets us lock out scenes that the player hasn't read yet.
  34.         # If we're in developer mode, let us play scenes anyway.
  35.         if not (renpy.seen_label(scene_id) or config.developer):
  36.             name = '[Locked]'
  37.             clicked = None
  38.         else:
  39.             clicked = show_scene(scene_id)
  40.        
  41.         # Now make the actual button.
  42.         ui.textbutton(
  43.             name,
  44.             clicked=clicked,
  45.             xalign = 0.5,
  46.             xfill=True,
  47.             size_group='library')
  48.    
  49.     # Add the scene library to the main menu.
  50.     config.main_menu.insert(2, ('Scenes', ui.jumps('scene_library', "main_game_transition"), "True"))
  51.  
  52. # scene_library is the entry point to our scene library, and does any needed
  53. # setup before our UI loop.
  54. label scene_library:
  55.     python:
  56.         _game_menu_screen = None
  57.        
  58. label scene_library_loop:
  59.     python:
  60.         # music.play and the first two ui calls let us continue the main
  61.         # menu feel.
  62.         renpy.music.play(config.main_menu_music, if_changed=True)
  63.         ui.window(style='mm_root')
  64.         ui.null()
  65.        
  66.         # This builds our dialog containing the scene list.
  67.         ui.frame(xalign=0.5, yalign=0.15, xanchor='center', yanchor='top')
  68.         ui.side(['c', 'r'], spacing=5)
  69.         vp = ui.viewport(draggable=True, clipping=True, mousewheel=True, xmaximum=260, ymaximum=390)
  70.         ui.vbox()
  71.        
  72.         # Now we can actually list out all the playable scenes we want to
  73.         # make available to the player.  Each scene is added to the list with
  74.         # a call to the scene_button function defined earlier.
  75.        
  76.         # To add a scene, you call scene_button with the readable name
  77.         # first, and then the name on the label statement which is at
  78.         # the head of the scene.  For example, a scene titled "Meet Lucy",
  79.         # which starts at label meet_lucy, would be added to the list
  80.         # like so:
  81.         #   scene_button('Meet Lucy', 'meet_lucy')
  82.        
  83.         # If you need to do any setup before a scene, it is best to set up
  84.         # an alternate label that includes the Ren'Py code needed to
  85.         # prepare the scene for play, then jumps to the proper label for
  86.         # the scene.  For example, if "Meet Lucy" won't work right unless
  87.         # variable x is set to 1, you could do something like this:
  88.         #   label meet_lucy_init:
  89.         #       $ x = 1
  90.         #       jump meet_lucy
  91.         #
  92.         # You would then use meet_lucy_init in the scene list below.
  93.        
  94.         ## SCENE LIST STARTS HERE
  95.        
  96.         #scene_button('Scene Name', 'label_name')
  97.        
  98.         ## SCENE LIST ENDS HERE
  99.        
  100.         # This wraps up our dialog.
  101.         ui.close()
  102.         ui.bar(adjustment=vp.yadjustment, style='vscrollbar', yalign=0.9)
  103.         ui.close()
  104.        
  105.         # And now, a button to bring us back to the main menu.
  106.         ui.textbutton('Return', xalign = 0.5, ypos = 450, clicked = ui.returns(False), size_group = 'scene')
  107.    
  108.     # This pretty much says to take us back to the main menu if 'Return' is
  109.     # clicked, or rebuild the dialog if we've just come back from a scene.
  110.     if ui.interact():
  111.         jump scene_library_loop
  112.     else:
  113.         $ renpy.full_restart(transition=None)

While some visual novels offer a way to play back scenes after completion of the game, Ren'Py doesn't offer a scene player in its standard menus. However, it is possible to add such a feature, as the code here demonstrates.

We start by defining a function that does the heavy lifting for us, then use renpy.curry() to give us a handle to it for use from UI controls. Then, we define our UI loop, building a dialog box that gives us a scrolling list of scene names, which when clicked, will run an individual scene before returning to the dialog.

The code pretty much stands alone, and all that needs to be done is to add a list of scenes which can be accessed from the player. We use a function defined earlier to actually create the clickable buttons, and pass to it both the proper name of the scene, and the label at which the scene begins. From the code:

To add a scene, you call scene_button with the readable name first, and then the name on the label statement which is at the head of the scene. For example, a scene titled "Meet Lucy", which starts at label meet_lucy, would be added to the list like so:

  1. scene_button('Meet Lucy', 'meet_lucy')

If you need to do any setup before a scene, it is best to set up an alternate label that includes the Ren'Py code needed to prepare the scene for play, then jumps to the proper label for the scene. For example, if "Meet Lucy" won't work right unless variable x is set to 1, you could do something like this:

  1. label meet_lucy_init:
  2.     $ x = 1
  3.     jump meet_lucy

You would then use scene_button("Meet Lucy", 'meet_lucy_init') in the scene list.