This is an alternate version of OPL2’s wiki page.

Overview

DatatypeDescription
Bool0 for false, else for true
Decimal32-bit integral number
Float32-bit floating-point number
PathFilename without extension, case-insensitive
StringASCII text
TextShift-JIS text

Scripts are in Shift-JIS encoding, with player-facing text in the non-ASCII range. Newlines should use CRLF and indentation use tabs—not spaces.

Single-line comments extend from an octothorpe (#) to an end-of-line and may appear after commands. Multi-line comments begin and end with a dollar sign ($).

Commands begin with a percent sign (%) and are case-sensitive. Arguments are colon (,) delimited and end with a semicolon (;); argumentless commands don’t end with a semicolon.

Text to be shown via the text field is simply placed at its desired location; no command needed. Lines implicitly wrap at 25 characters (even in the middle of a word) or explicitly with %r. The text field never automatically pauses; %K or %k must be used to allow the player to read it. Note that player-facing English and other ASCII characters must use fullwidth characters.

Tips:

Statements

StatementFunctionExample
:<S:label>;Label location:next;
&<D:name_idx>Runtime character name&0

Commands

CommandFunctionExample
Audio
%M{<P:.ogg>},<D:ignored>;Plays music (loops); stops if no filename specified%MBGM01,0; / %M,0;
%MA{<P:.ogg>},<D:ignored>;Plays environment sound (loops); stops if no filename specified%MAse09,0; / %MA,0;
%MS{<P:.ogg>},<D:ignored>;Plays sound effect (doesn’t loop); stops if no filename specified%MSse30,0; / %MS,0;
%o{<P:.ogg>};Plays voice (doesn’t loop); stops if no filename specified%o0002; / %o;
%oR<D:char_idx>,<D:minFile>,<D:maxFile>,<F:minTime_secs>,<F:maxTime_secs>;Plays random voices (%04d.ogg) with random delays; stops if minFile is less than 0%oR0,0453,0458,0.5,1; / %oR0,-1,0,0,0;
Camera
%c{<P:.tcm>},<B:repeat>;Runs camera track%ccam2_3,0; / %c,0;
%cl<B:lock>;Toggles player control%cl0;
%cp<S:point>;Sets location in the room (only when locked)%cploc_pos00;
%cu<D:char_idx>,<F:x>,<F:y>,<F:z>;Sets first-person “up vector”%cu0,0.0,1.0,0.0;
%cv<D:char_idx>,<D:point>;Sets first-person look-at point (to loc_sight%02d)%cv0,1;
Character Model
%ma<D:char_idx>,<D:part_idx>,<P:.tmb>;Loads item%ma0,1,imo_eye_01;
%mc<D:char_idx>,<D:0=front,1=behind,2=mouth>,<B:censor>;Shows or hides area’s censor%mc0,0,1;
%md<D:char_idx>,<D:part_idx>;Removes item%md0,5;
%mh<D:char_idx>,<D:0=upper,1=lower>,<D:0=on,1=half,2=off>;Toggles underclothes’ state%mh0,1,1;
%ml<D:char_idx>,<P:.tmb>;Loads body and removes other items%ml0,imo_bodyA_00;
%mm<D:char_idx>,<P:.tsb>;Loads animation%mm0,event_01;
%mp<D:char_idx>,<S:point>;Sets location in the room%mp0,loc_pos00;
%mrRestarts character animations and clears the text field%mr
%mv<D:char_idx>,<B:visible>;Toggles visibility%mv1,1;
Character Lighting
%la<D:light_idx>,<F:r>,<F:g>,<F:b>;Sets ambient color%la1,0.2,0.2,0.2;
%ld<D:light_idx>,<F:r>,<F:g>,<F:blue>;Sets diffuse color%ld1,0.2,0.2,0.2;
%le<D:light_idx>,<B:visible>;Toggles visibility%le1,1;
%ls<D:light_idx>,<F:r>,<F:g>,<F:b>;Sets specular color%ls1,0.8,0.8,0.8;
%lv<D:light_idx>,<F:x>,<F:y>,<F:z>;Sets rotation%lv1,1,1,1;
Flow Control
%G<P:.txt>;Runs script%Gtitle;
%IShows defined buttons and waits for player selection%I
%i{*}<T:text>,<S:label>;Defines button (max 8) that jumps to label when clicked or is disabled if text begins with an asterisk%iTalk to Her,zen01; / %i*Talk to Her,zen01;
%J<S:label>;Jumps to label%Jnext;
%L<S:label>;Calls label as subroutine (max depth is 16)%Linit_char;
%lExits subroutine%l
%QQuits game%Q
%w<D:duration_frames>;Waits%w100;
Room Model
%mb<D:room_idx>,{<P:.tmb>};Loads model; removes if no filename specified%mb1,room_03A; / %mb1,;
%mn<D:room_idx>,<P:.tsb>;Loads animation%mn1,room_03A;
%mV<D:room_idx>,<B:visible>;Toggles visibility%mV0,0;
Text Field
%KPauses text field then clears it%K
%kPauses text field%k
%n{<S:color_byte>}{<T:text>};Sets header field, which is pinned as the first line; clears if no options specified; ignores any text preceding the color code%nText; / %n&0; / %n;
%RClears text field%R
%rInserts newline%r
User Interface
%f<D:0=3D,1=all>,<D:opacity>,<D:duration_frames>;Fades screen; opacity 0 is black, 255 is transparent, and 512 is colored (uses fade.psd)%f1,255,120;
%g{<P:.psd>};Shows image (first layer only); hides if no filename specified%gtitle2; / %g;
%q<B:enable>;Toggles quit hotkey; when enabled, Escape jumps to SystemExit (if it exists) or runs script.txt; delayed by text field pauses%q1;
%T<B:visible>;Toggles title image%T1;
%W<B:visible>;Toggles text field%W1;
%WG<B:visible>;Toggles character gauges, controlled by variable 128 for female and 129 for male%WG1;
%XCloses customization screen; only useful in label SystemExit since the CS pauses execution%X
%Z<B:0=room,1=char>;Opens customization screen; not implemented for rooms%Z1;
Variable
%E<D:var_idx>,<S:comparison>,<D:value>,<S:label>;Evaluates condition and jumps to label if true%E18,=,1,h_mode;
%S<D:var_idx>,<S:operation>,<D:value>;Sets variable%S18,=,1;

Bugs:

Comparisons

OperatorTruth State
=Equal to
!=Not equal to
>Greater than
>=Greater than or equal to
<Less than

Operations

OperatorAction
=Assignment
+Addition
-Subtraction

Indexes

Character

Range is [0, 7]; other indexes cause corruption. Indexes not in [0, 1] cause corruption in %cu, %cv; and are ignored in %oR.

  1. Main female
  2. Main male

Color

Range is [1, 2]; other indexes cause corruption. These indexes are raw character codes! E.g., %nblue;; note the usually-invisible character between “n” and “b”.

  1. Blue →←
  2. Pink →←

Light

Range is [0, 7]; other indexes are ignored.

Name

Range is [0, 3]; other indexes cause corruption.

  1. Male (colored if in %n)
  2. Female (colored if in %n)
  3. Male
  4. Female

Part

Range is [0, 31]; other indexes cause corruption.

  1. Body (Use %ml)
  2. Eyes
  3. Upper underclothes
  4. Lower underclothes
  5. Socks
  6. Upper outer clothes
  7. Lower outer clothes
  8. Head item
  9. Face item
  10. Neck item
  11. Arm item
  12. Shoes
  13. Hair

Room

Range is [0, 7]; other indexes cause corruption.

  1. Main

Variable

Range is [0, 255]; other indexes cause corruption. All are initialized to zero and indexes in [128, 255] persist between runs as pl2state.dat.