indy_logo.gif (3362 bytes)

Step 9: advanced issues

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".

Next >>

Back to the main page