If you successfully followed the previous lessons, this is the ultimate in Indyjava
coding, that will make your script really stand from the crowd.
a) Inheritance
Now a classic programming feature, single inheritance is also available within the
Indyjava scripting language. It allows for lean, clever scripting.
Rooms and Items can derive from other ones, to inherit attributes (like images and sounds)
functions (like Command and Event blocks) and variables. The keyword that does the magic
is EXTENDS.
ROOM some_definitions { ITEM MaleActor { INK { 5 } // Men's speech will be cyan :) } } ROOM main { ITEM Indy EXTENDS MaleActor { ANIMATION { // --- standby images --- 0 0 "gr/indy_d.gif" 1 // Looking South 0 1 "gr/indy_r.gif" 1 // Looking West 0 2 "gr/indy_u.gif" 1 // Looking North 0 3 "gr/indy_l.gif" 1 // Looking East // --- walking animation --- 1 0 "gr/indy_wd.gif" 4 // Looking South 1 1 "gr/indy_wr.gif" 6 // Looking West 1 2 "gr/indy_wu.gif" 4 // Looking North 1 3 "gr/indy_wl.gif" 6 // Looking East // --- talking animation --- 2 0 "gr/indy_td.gif" 7 // Looking South 2 1 "gr/indy_tr.gif" 4 // Looking West 2 2 "gr/indy_tu.gif" 1 // Looking North 2 3 "gr/indy_tl.gif" 4 // Looking East } POSITION { 140 140 0 } } }
Indy will talk in Cyan because it inherits the attributes defined by its base class.
However, derived classes can also override base class' properties by redefining attributes
and functions with the same name.
Purist will notice that this system makes no different between 'instance' and 'class
definition'. I apologise for that, and will try to change that later on.
The file STDLIB.ADV contains useful base classes for Rooms and Items,
that you can use by just typing IMPORT "stdlib.adv" at the
beginning of your script.
b) Color Lighting
To make character walking more realistic, 2D adventure games used to change color palettes
when stepping on dark or poorly illuminated parts of a room. Here, a 24 bit color filter
is used to recreate such effects.
ROOM main { ITEM Indy { ANIMATION { // --- standby images --- 0 0 "gr/indy_d.gif" 1 // Looking South 0 1 "gr/indy_r.gif" 1 // Looking West 0 2 "gr/indy_u.gif" 1 // Looking North 0 3 "gr/indy_l.gif" 1 // Looking East // --- walking animation --- 1 0 "gr/indy_wd.gif" 4 // Looking South 1 1 "gr/indy_wr.gif" 6 // Looking West 1 2 "gr/indy_wu.gif" 4 // Looking North 1 3 "gr/indy_wl.gif" 6 // Looking East // --- talking animation --- 2 0 "gr/indy_td.gif" 7 // Looking South 2 1 "gr/indy_tr.gif" 4 // Looking West 2 2 "gr/indy_tu.gif" 1 // Looking North 2 3 "gr/indy_tl.gif" 4 // Looking East } POSITION { 140 140 0 } } ITEM darkzone { IMAGE { 0 0 "gr/blank.gif" } POSITION { 50 140 0 } EVENT { IF { INSIDE darkzone indy } { SETLIGHTING indy 0 0 0 50 // 50% darker than normal } ELSE { SETLIGHTING indy 0 0 0 0 // 0% dark (back to normal) } } } }
The first 3 parameters of SETLIGHTING are the RGB values of the desired
"target" color (black in the example). The last parameter indicates the degree
of proximity to the target color.
It is also possible to specify a LIGHTING block for a room or an item. This would give an
initial color lighting, that could be redefined with SETLIGHTING during gameplay.
c) Procedures
The language currently supports simple procedures, without parameter passing.
Such subroutines can be useful when repetitive tasks are needed in different parts of the
script.
PROC hello_world { SAY indy "Hello procedural world!" } ROOM main { ITEM Indy { < ... etc ... > } COMMAND use whip { CALL hello_world } }
Procedures can have local variables. Also, procedures can be defined into rooms or
items, making the procedure a "local" name.
d) System functions
Some variable names and procedure names are reserved for system use. The most useful ones
are OnEntry and OnExit.
A room's OnEntry and OnExit procedures (if defined) are automatically called by the system
when appropiate.
ROOM main { ITEM Indy { ANIMATION { // --- standby images --- 0 0 "gr/indy_d.gif" 1 // Looking South 0 1 "gr/indy_r.gif" 1 // Looking West 0 2 "gr/indy_u.gif" 1 // Looking North 0 3 "gr/indy_l.gif" 1 // Looking East // --- walking animation --- 1 0 "gr/indy_wd.gif" 4 // Looking South 1 1 "gr/indy_wr.gif" 6 // Looking West 1 2 "gr/indy_wu.gif" 4 // Looking North 1 3 "gr/indy_wl.gif" 6 // Looking East // --- talking animation --- 2 0 "gr/indy_td.gif" 7 // Looking South 2 1 "gr/indy_tr.gif" 4 // Looking West 2 2 "gr/indy_tu.gif" 1 // Looking North 2 3 "gr/indy_tl.gif" 4 // Looking East } POSITION { 140 140 0 } } PROC OnEntry { SAY indy "You just entered this room" } PROC OnExit { SAY indy "You are leaving this room" } }
The example makes little sense, but you will find that OnEntry and OnExit blocks are
heavily used in adventure scripting.
e) Imageless items, and scaling
Several times during the tutorial, "blank" completely transparent GIF images
were used to work as invisible areas. It is possible to create items without images that
still have a rectangular shape, using the SIZE keyword.
ROOM main { SCALE { 0 30 143 50 } ITEM Indy { ANIMATION { < ... etc ... > } POSITION { 140 140 0 } } ITEM area { SIZE { 50 30 // Force this pixel size, despite the lack of an image } POSITION { 50 140 0 } } }
Further more, such SIZE statements can be used with items which do have images. That
would override the room's scaling scheme for that item.
The default scaling scheme for rooms is to show items at 100% size at the bottom of the
screen and slowly decrease to 0% size at the top, to simulate a 3D perspective. The SCALE
block for a room allows to customise these 2 parameters. In this example, characters will
appear at 30% size at the top of the screen (y=0) and 50% size at the bottom (y=143).
f) Splitting an adventure into several
parts
It is often preferable to split a script into independent parts, that are played by the
user sequentially.
ROOM temple { < ... etc ... > COMMAND use gun monster { LOAD "002.GAG" // After successfully completing Part I, start Part II } }
The method is to compile every ADV and save the resulting adventure under a different
name (like "001.GAG", "002.GAG" , etc...). At the right time in the
script, use the LOAD command to start the following part. Please note that all variables
and inventory items are lost in the loading process.
g) Cutscenes
An important part of a finished product is the quality of intermediate, explanatory,
non-interactive, cinematic sequences. This is achieved with a subtle use of WAIT and SLEEP
instructions.
ROOM main { ITEM Indy { < ... etc ... > } ITEM big_title { IMAGE { 0 0 "title.gif" } POSITION { 150 50 0 } } COMMAND use whip { // Begin non-interactive sequence MOVE big_title 150 130 0 WAIT big_title // Wait for the title to stop moving. SLEEP 1500 // Pause 1.5 seconds. MOVE indy 210 142 0 WAIT indy // Wait for Indy to stop moving. ORIENTATE indy 0 // Look at the user. SAY indy "I like cutscenes!" WAIT indy // Wait for his sentence to finish. // End of sequence (player will take control again automatically) } }
Cutscenes can obviously appear anywhere in the script. Any language instruction can be
used in a cutscene, including switching to another room with GOTO.
As soon as there are no more WAIT or SLEEP instructions in the program flow, control is
automatically given back to the player : the cursor changes and the interface panel
reappears. You don't have to care about these details.
h) Map view
Another feature of memorable games was the interface change to a verb-less mode, typically
used in map overviews.
ROOM island_map { IMAGE { "island_overview.gif" } SCALE { 0 30 143 30 } // Large scale (small characters) ITEM house { < ... etc ... > } PROC OnEntry { PLACE indy island_map // Be sure to be here. SETMAPMODE 1 // Change to special "map" interface } PROC OnExit { SETMAPMODE 0 // Back to standard interface } EVENT { COLLISION indy house // Player reached an interesting point in the map PLACE indy another_room GOTO another_room } }
When in "map" mode, the name of items pops up when the mouse is over them,
but no sentence can be formed by the player. The player can just walk across the map.
i) Changing item labels, and default verbs
The label and the default verb for an item appear, and form a sentence, when the mouse is
over the item in the standard interface.
ROOM main { IMAGE { "gr/idol.jpg" } ITEM Indy { < ... etc ... > } /* And now, a bit of foreground vegetation */ ITEM plant { IMAGE { 0 0 "gr/plant_1.gif" } POSITION { 0 182 0 } DEFAULTV { Pull } // custom default-verb } ITEM plant_2 { IMAGE { 0 0 "gr/plant_2.gif" } POSITION { 256 182 38 } LABEL { "strange thing" } // custom label } ITEM plant_3 { IMAGE { 0 0 "gr/plant_3.gif" } POSITION { 190 182 0 } LABEL { "" } // non-selectable scenary item } COMMAND "Look at" plant_2 // "look at strange thing" :-) { SAY indy "Nothing strange here" SETLABEL plant_2 "plant" // Use the same apparent label as the first item } }
If no LABEL is specified for an item, it's name will be used. Labels can be altered at
any time.
Items with an empty label like Plant_3 above, will be unselectable for the player.
The default verb for items is "look" but this can be specified on a per-item
basis. Typically, the default verb for human characters should be set to "talk
to".