OPL2 manual
Introduction
This is the OPL2 manual, which explains most, but not all, variables, functions and classes of OPL2 for modders and people who want to read/modify the sourcecode and gives example uses. OPL2 API reference lists all the OPL2 variables, functions and classes with their descriptions, but without any specific order and without any example plugin uses. The reference is included in html format with every OPL2 distribution. It can also be downloaded separately here: link OPL2 uses the Panda3D game engine. Panda3D manual can be found here: link Panda3D API reference (list of classes/functions) can be found here: link If you see a variable, function or class and you don't see it in the OPL2 API reference, it's either part of the Python Standard Library and documented at python.org or part of the Panda3D game engine API. First section of the manual is about the different methods of modding OPL2. Then we'll learn all the OPL2 modules, what each does and the most useful variables and functions of each module with example plugins using them. In the final section we'll be presented with actually useful plugins.
Modding methods
OPL2 code mods are called plugins. There are two kind of plugins. 1) Startup plugins are imported on startup of the game and must be placed in opl2_data/plugins/startup (can have their own subfolder). 2) Another less popular type of plugins are placed in opl2_data/plugins and are either imported through the PL2 scripting language (%PY command) or at some point by other Python plugins). OPL2 code can be modded by completely overwriting existing code (variables/functions/classes), hooking functions to OPL2 functions or writing independent non-interfering code, or a combination of these. Overwriting existing variables/functions/classes is a general Python knowledge. This is generally not recommended as same can be usually done with hooking your functions to OPL2 functions which allows different plugins to work together instead of overwriting each other's changes to the OPL2 code. We will also not learn how to write independent non-interfering code because it is a general Python knowledge and you're good to go as long as you've read this manual, the API reference and so are sure your code will run in harmony with OPL2 code. Hookable functions is a powerful feature of OPL2 added in R7. How this works? Most OPL2 functions are converted to callable classes which you probably haven't heard about. In short, they can be used (called) as ordinary functions, but have methods like Python classes do. These methods are used to assign (hook) your own functions to them. To hook your mod's function to OPL2's hookable functions, do something like this: originalOPL2Function.addPre(myFunction) # for a function to be called before the OPL2 func originalOPL2Function.addPost(myFunction) # for a function to be called after the OPL2 func As you see, if you want your function to run before the OPL2 function/method, you use the method addPre() and if you want it to run after, you use addPost(). To make many plugins which hook the same function work better together an optional sorting argument can be used to tell in what order the hooked functions will be called (higher numbers called first). Just making your own function to run before or after an OPL2 function might not be enough in most cases. Functions take arguments and might behave quite differently and produce different results because of that. Taking this into account, OPL2 API allows you to get the arguments of the hookable function and even prevent the hookable from being called itself from inside your hook function. In such case the first argument of the hooked function will be a dict of arguments of the hookable function ({name:value,name:value,etc}). Hooked functions can even "kill" (prevent from being called) the hookable function itself, by having the string "KILL" as the return value. This is true only for hooked functions meant to be called *before* the hookable function (preFunc), of course. Functions meant to be called *after* the hookable function (postFunc) can find out if the hookable
has been "killed" by checking the boolean variable "hookableFunction.hasBeenKilled". OPL2 even allows you to make your mod's functions/methods hookable. def originalFunction(): pass modding.makeFunctionExtendable('originalFunction') Arguments of addPre() and addPost() functions: addPre(func, extraArgs, sort, getFuncArgs) addPost(self, func, extraArgs, sort, getFuncArgs) func your function which you want to hook extraArgs the arguments of your function, optional sort the sorting of your function, optional, default is 0 getFuncArgs get the argument names and their values of the hookable function as a Python dictionary, default is False To "unhook" your mod's function, use the methods removePre() or removePost(). Here's an example OPL2 plugin which uses the above functions. def makeGUITransparent(hookableArgs): if hookableArgs['arg'] == 1: # if going to the CharacterCreationScreen, not leaving pl2functions.Globals.characterCreationScreen.guiParent.setAlphaScale(0.6) pl2functions.Globals.characterCreationScreen.itemIconsParent.setColorScaleOff(1) pl2functions.Globals.characterCreationScreen.windowText1.setColorScaleOff(1) pl2functions.Globals.characterCreationScreen.windowText1.setColorScale(1,0,0,1) pl2functions.Globals.characterCreationScreen.windowText2.setColorScaleOff(1) pl2functions.Globals.characterCreationScreen.roomIcon.setColorScaleOff(1) pl2functions.openCharacterCreationScreen.addPost(makeGUITransparent, getFuncArgs=True) Name it something like TranslusentCharacterCreationScreen.py and place it in opl2_data/plugins/startup As you see, all this plugin does is define a new function and assign/hook it to an existing OPL2 function and is just over 10 lines of code, but it has a very visible effect in the game if you go to the Character Creation Screen: the GUI is now semitransparent.
It's kinda hard to notice on a small image, so I've highlighted parts which clearly show the transluency. You'll learn about pl2functions.openCharacterCreationScreen and pl2functions.Globals.characterCreationScreen.x in this manual and the OPL2 API reference. Of course we can make a lot more useful and interesting plugins, this is just a short example. For more info on the internals of the hookable functions, check out the Modding module in the OPL2 API reference and the Modding.py module of the sourcecode.
OPL2 modules list
OPL2's sourcecode is pretty small: it consists of 13 Python modules and 2 faster version C++ modules. main.py Main script. Runs the GUI Launcher or directly starts the game (by importing PL2Start.py) depending if the launcher is enabled or not in the configuration file (opl2_data/config.txt). Generally useless for modders. GUILauncher.py A launcher which shows up at startup and allows you to change some game settings before starting the actual game. Can be disabled. If you'll want to reenable, either edit the config file yourself or delete the config file and let OPL2 generate a default one next time. Generally useless for modders. PL2Start.py Similar to Panda3D's DirectStart module, which simply creates an instance of ShowBase (which itself initializes many default Panda3D stuff). PL2Start imports DirectStart, creates an instance of PL2Base, starts the PL2 interpreter by telling it to run the script "script.txt" and also starts Panda3D's game loop. Generally useless for modders. cRand.py This class is meant to mimic C's random number generator. Is used by the game to encrypt and decrypt *.dat files, which are used for saving characters and game progress. Generally useless for modders. pyparsing.py A 3rd party open source parser library-module, used for parsing PL2's *.txt scripts. Generally useless for modders. AddonScanner.py Provides functions for scanning the game's add-ons folder for add-ons, extracting the entries from pl2 archives, and attribute files from the add-ons, parsing them and generating Python dictionaries which are used in the game by PL2 scripts and the CharacterCreationScreen class. Used by the PL2Base class. PL2Loader.py A class for loading PL2's native file formats (PL2, TMB, TSB, TCM, BIN, PSD). Similar to Panda3D's Loader class. The functions accept file paths or data strings. An instance is created in the PL2Base class (like an instance of Loader is created when you make an instance of Panda3D's Loader class, for example by importing DirectStart). PL2Interpreter.py A parser/interpreter for PL2's TXT scripts. Parser uses the pyparsing.py library-module. Interpreter implemented as a Panda3D Task object.
PL2Functions.py A module containing all the Python functions which correspond to PL2 TXT commands. Can be called directly from Python code or assigned to the PL2 *.txt script commands by using the commandsdict dictionary of the PL2Interpreter class. Very interesting for modders. PL2Base.py Similar to Panda3D's ShowBase class (which is instantiated automatically when you import DirectStart module). Sets up some default objects and functions used by other OPL2 modules. Very interesting for modders. CharacterCreationScreen.py The Clothing Selection Screen from PL2 which is usually accessed with the %Z command. Very interesting for modders. CameraManager.py Provides camera controls very similar to the original game. An instance is created in the PL2Base class. Modding.pyd Functions/classes for making modding the game simpler and more powerful. cDecompressPL2.pyd Contains faster C++ version of the function _decompressPL2 from the PL2loader class. Used for decompressing LZSS compressed *.pl2 files. Generally useless for modders. cPsd.pyd Contains faster C++ versions of the functions _mold and _decompressRLE from the PL2Loader class. Used for decompressing RLE compressed *.psd image layers and rearranging pixel data. Generally useless for modders. In the next sections we will learn about each module separately, their main variables, function and classes and examples of using (accessing/modifying) those in plugins. For a full list of functions, with info such as what arguments they take, check the OPL2 API reference. In this manual the modules will be listed in order of relevance and useless modules will be skipped, unlike in the automatically generated API reference.
PL2Base
PL2Base module contains the PL2Base class. Some variables of PL2Base class. (access them like this: pl2base.x) vfs crand pl2loader camMgr pl2variables unlockedAddons attributes filesDict undiesTypeDict font titleImage fader1 fader2 characterParent charParents A dictionary which allows you to find out in which *.pl2 archive the file you want is. Tells what type an underwear mod is. More on that later. Default font of the game. Title image, a Panda3D NodePath A fader (curtain) on top of the 3d scene, used for fading from one screen to the next. A fader (curtain) on top of everything. Parent node of all bodies and parts. If you want to create an effect on all characters and their clothes, use this NodePath. Each body and its parts are attached to a single parent node, which is stored in this list. If you want to create an effect on one character and his/her clothing, use a NodePath from this list. A list where the bodies (and only) of characters are stored and accessed by index by the PL2 Scripting Language. A list of lists where all clothing (parts) of characters are stored. First index is the character index, second is his clothing (part) index (up to 32 each). Part Index: 0: Body # (not used and stored in charList instead, blame PL2 devs) 1. Eyes 2. Upper Underwear 3. Lower Underwear 4. Socks 5. Upper Clothes 6. Lower Clothes 7. Head Item A Panda3D VirtualFileSystem object, used to create a virtual folder (named '/ramdisk') where ogg files are stored and played in-game. A cRand object. Generally useless for modders. An OPL2 PL2loader object. Loader for PL2's native file formats. An OPL2 CameraManager object. Handles camera controls. Integer list used by the PL2 Scripting Language to store and retrieve data. Names of addons which are unlocked. Useless if pl2variables[132] is equal to 5963 (which is usually the case) which unlocks everything.
charList partList
8. Face Item 9. Neck Item 10. Arm\Hand Item 11. Shoes 12. Hair Underwear indexes/slots actually are lists which hold the models of underwear states and the type of underwear in the end (more on that later). undiesStates partNameList roomsParent roomList lightsParent lightList stores the 'state' of upper and lower underwear, this is needed for one PL2 Scripting Language command stores tmb (part) names, used by save files Parent node of all rooms. If you want to create an effect on all rooms, use this NodePath. A list where the rooms are stored and accessed by index by the PL2 Scripting Language. parent node for all light nodes 8 lights exist which are controlled by scripts, all are disabled by default. Panda3D doesn't have a concept of light ambient color, instead a separate 'AmbientLight' light type exists. So we have a list of [PointLight, AmbientLight] for each 'PL2 light' instead. This light causes everything to be black by default. Game's textbox (its frame) An button on top of textbox which you can click on to skip/advance text. The cursor of the textbox which appears to signify the end of the current page. The actual Text node (Panda3D OnScreenText object). A (optionally colored) text on the top of the textbox mainly used for displaying character names. If title image is shown, buttons (created by PL2 scripts) move lower on screen. Instead of moving the buttons one-by-one, they are attached to this NodePath which moves itself instead. buttons created by TXT scripts are added to this parent of all buttons created by PL2 scripts default GUI button geometry (Panda3D NodePath) default GUI button geometry, when mouse over it (Panda3D NodePath) default GUI button geometry, when disabled (Panda3D NodePath) Parent node for all (psd) image nodes created by PL2 TXT scripts. Parent node of character gauges ('excitement meters'). Girl's gauge background Girl's gauge. Scale this node to scale girl's gauge along the left side (to change its value)
defaultLight textbox textboxWindow textboxCursor textboxText textboxColorText isTitleShown
buttons buttonsParent buttonGeom buttonRolloverGeom buttonDisabledGeom psdsParent characterGauges girlGaugeBack girlGauge girlGaugeLeft
boyGaugeBack boyGauge boyGaugeLeft girlName boyName girlTextColor boyTextColor censorFrame scriptAudio clickSound1 clickSound2 clickSound3 prtScImagePrefix prtScImageIndex
Boys's gauge background Boys's gauge. Scale this node to scale boy's gauge along the left side (to change its value) Name of the girl character. Name of the boy character. Color of the girl's name (used for textboxColorText). Color of the boy's name (used for textboxColorText). Censor billboard (use NodePath.copyTo() to dublicate). Sounds accessed and played by PL2 TXT scripts. Click sound 1 when clicking on GUI elements. Click sound 2 when clicking on GUI elements. Click sound 3 when clicking on GUI elements. Used by pl2base.printScreen(). The prefix of the image files created when pressing the prt sc button. A second image name prefix which grows higher.
Some methods of PL2Base class. (access them like this: pl2base.x()) printScreen Make a screenshot of the screen. OPL2 calls this function when you press Ctrl + Prt Sc. If you use it yourself, you are suggested to use the "name" argument. Add a text watermark to the top-left corner of the screen. Used to specify the version of OPL2 in screenshots. Load image as 3d plane. Names in PL2 scripts are case insensitive. This function takes a file name you need to extract, finds the first case insensitive match in the list (which you'll usually get from pl2base.filesDict.keys() and returns it. Fullwidth latin characters are used in some asian encodings to make the text more readable by having the latin and native characters the same width. The original font file of PL2 doesn't support ordinary ASCII characters, only fullwidth. Pass your ASCII string to this function to get the fullwidth version of the string usable with the original font file. Warning: only latin letters and some signs are supported. Adds newlines to a string to make it fit the textbox Get the specified AnimBundle (animation) NodePath from the specified Actor, which you can assign and play on another Actor. Used in the game to assign body animations to their clothing. Panda's Actor class doesn't have such function itself as usually you load animations from Panda's EGG/BAM files, which get cached, so you can "reload" them without performance loss. Get the current (module's) directory. Pass "__file__" as argument.
addWatermark loadImage caseInsensitiveMatch
asciiToFullwidth
textWrap getAnimBundle
getCurrentDir
Now we're gonna write an example plugin which will make all characters and their clothes green. pl2base.charactersParent.setColorScale(0,1,0,1) # R,G,B,A Result:
Boring, but gives an idea how an effect can be applied to all characters at once. A custom shader would be more interesting. Let's try to do something else now. Let's change the title image. from panda3d.core import * # for TransparencyAttrib pl2base.titleImage = pl2base.loadImage(pl2base.getCurrentDir(__file__)+'awesome.png') # need this for current dir, blame Python pl2base.titleImage.reparentTo(aspect2d) pl2base.titleImage.setTransparency(TransparencyAttrib.MAlpha) pl2base.titleImage.setBin('gui-popup', 1) pl2base.titleImage.setZ(0.42) pl2base.titleImage.hide()
Again, we will see actually fun and useful plugins in the final section of this manual.
PL2Interpreter
PL2Interpreter module contains the PL2Interpreter class. Things modders can do: get the currently executing command name, execute a single command yourself, among other things. Some variables of PL2Base class. (access them like this: pl2interpreter.x) waiting index code script Is the PL2 Scripting Language Interpreter in waiting (pause) mode or not? The index of the currently executing command in the list of the commands of the current script. List of PL2 TXT command+args sublists, can be accessed for debugging, for example. The Pyparsing Group object telling the syntax of PL2 Scripting Language. Can be overwritten to modify the supported scripting language or available commands. Not very fun thing to do.
Some methods of PL2Interpreter class. (access them like this: pl2interpreter.x()) stopExecution Stop executing any currently loaded and running script. By default OPL2 runs script.txt, so if you want to, say, make a full transformation mod by running your own PL2 TXT or Python scripts, this is the first thing to run. Load your own TXT script and start execution. Go to the specified label in the current TXT script. You can, say, write code to run this when you push a button in the game or something. Execute a single command by passing it as a string to this function. Can be used for example for interactive consoles. The Panda3D task that runs the commands when needed. Hook to this function to know what is being executed.
loadTXT gotoLabel executeCommand _executorTask
Here's a mod that stops the default scripts on startup and runs your own: pl2interpreter.stopExecution() pl2interpreter.loadTXT('myfile')
CameraManager
You can stop OPL2 camera controls and create your own or use Panda3D default orbiting control, change mouse sensitivity, disable/enable rotation, panning, zooming, get mouse position, change orbit target, camera's x offset and play a camera animation. Some variables of CameraManager class. (access them like this: camMgr.x) rotationOrigin _islmbdown _isrmbdown _previousX _previousY Camera orbit origin NodePath. Is left mouse button pressed. Is right mouse button pressed. Mouse X position on screen. Mouse Y position on screen.
Some methods of CameraManager class. (access them like this: camMgr.x()) <just read the OPL2 API reference for a list of CameraManager methods, avoiding pointless copypaste> Example plugin: camMgr.setFov(90)
PL2Functions
There aren't really any variables to post here, most functions are self-contained. And you can get a list of all the functions in the OPL2 API reference.
CharacterCreationScreen
CharacterCreationScreen is usually instantiated in PL2Functions.py, though you can create an instance yourself. If it's the former case, you can access the instace as pl2functions.Globals.characterCreationScreen . Some variables of CharacterCreationScreen class. (access them like this: pl2functions.Globals.characterCreationScreen.x) sectionCameraValues A dictionary with camera origin location, camera rotation and camera distance from camera origin values list. Modify it to change camera animations in CameraCreationScreen. A dictionary with Panda3D AudioSound values. Change these to change what sound gets played when going to specified section. Parent node for all CharacterCreationScreen GUI nodes. Modify this and all GUI nodes get modified. parent of itemsWindows The icon that hides/removes a clothing. The icon used when no icon set in the PL2 model addon. A dictionary with the texts which get displayed when you go to the specified section. Values are lists with [sectionName, sectionDescription] strings. If you want to modify these, don't forget to pass your ASCII string through pl2base.asciiToFullwidth() to turn it to shift-jis and make it work with the default PL2 font. The info window geometry. The text node of the info window used for section names. The text node of the info window used for section descriptions. Buttons list containing the buttons which take you to the specified section. The standard size of the clickable item icons. The x lenght of the item buttons grid. The Panda3D DirectScrolledFrame which holds the clickable buttons. Parent of all clcikable item icons which load the items when clicked. List holding the above icon buttons. The map chooser GUI frame. The map chooser GUI left button. The map chooser GUI right button.
sectionSoundsDict guiParent itemsParent disableIcon noIcon sectionDescriptions
windowImage windowText1 windowText2 sectionButtons itemButtonSize itemsGridXLenght itemsScrolledFrame itemIconsParent itemIconButtons chooseMapFrame chooseMapLeft chooseMapRight
characterCreationScreenLogo The logo which is usually on the top-right of the screen.
loadButton saveButton startButton gotoClothingSetsButton
Button which sends you to Loading section. Button which sends you to Saving section. Button which exits the CharacterCreationScreen. Button which goes to Clothing Sets section
Some methods of CharacterCreationScreen class. (access them like this: pl2functions.Globals.characterCreationScreen.x()) _chooseMap() You can use this to, say, load rooms from your own generated menu, instead of pressing the right button or left button to reach your map.
Real (useful) plugins
There are many example plugins you can download from here: link As you see, unlike the example plugins here, you can add all sorts of useful and interesting stuff with modding, such as beautiful smooth shadows, cel shading (cartoon shading), graphics effects like bloom and outline, add GUI to make choosing maps in the Character Creation Screen or even make a complete transformation mod and make GTA running on the OPL2 engine. OPL2 is powered by the Panda3D game engine which supports all sorts of things: link The limit is your GPU, your level of experience, your available free time and motivation. I'll strongly suggest reading the Panda3D manual if you want to make more advanced mods, oterwise you will be guessing stuff most of the time. -ingen58 OPL2 without plugins (left) and with plugins (right)