Plig

By Chris Dewhurst

Originally published in EUG #63

General Instructions

This is the BBC/Electron conversion of the Archimedes game by the same name, exclusive to readers of EUG. It is a simple but challenging and enjoyable maze game in which you move a character around the screen that has to collect pieces of fruit. The catch is that once you start moving in a particular direction you carry on doing so until you hit a solid wall. So plan your route carefully. And hurry, there isn't much time.

There are 25 levels to complete, each of which can be accessed by typing in the a password when you select menu option 2 from the front screen. The game must be run with PAGE set to &2900. The EUG disc must be left in the drive throughout the game because, due to memory restrictions, sprites are loaded into a cache at the beginning of each level and these sprites are extracted from the sprite file on disc. Technical notes on programming are in the file PLIGTCH. Have fun!

Thanks are due to the authors of the original game, available for the Archimedes from Skullsoft at mysite.freeserve.com/skulloft/plig.htm.

Technical Notes

Notes on procedures, variables, and/or machine code subroutines are described under the notes on the program in which they are used. Machine code labels appear in lowecase in the program but are written in UPPER CASE for clarity in the text.

1. Files

S.CLEV Level generator. Needs to be run before running S.GCODE
S.GCODE Main game assembly. Assumes S.CLEV has been run
S.TITLES Machine code for title and password screens
G.PTS Sprite file accessed by G.PMC. Note these are not loaded into memory but extracted as required into a sprite 'cache' (See program description below)
G.PMC Object code created by S.GCODE
G.PMC2 Object code created by S.TITLES
G.PINTRO BASIC loader. Data for Plig banner poked into memory. *LOADs in G.PMC and G.PMC2
G.PGAME BASIC control program

2. CLEV Create Levels

0 Space 1 Tree 2 Metal Plate 3 Rock 4 Palm Tree
5 Pyramid 6 Fir Tree 7 Column 8 Plate 2 9 Tree Stump
A Solid B Bricks C Fence D Cube 1 E Piping
F Cube 2 10 Cube 3 11 Barrel 12 Grave 13 Cherries
14 Choc 15 Grapes 16 Apple 17 Pineapple 18 Drink Can
19 Acorn 1A Lolly 1B Banana 1C Flames 1D Toadstool
1E Plant 1F Mine 20 Left arrow 21 Up arrow 22 Right arrow
23 Down arrow

One character is represented by one nybble:

    0     Always a space
    1     'Fake' character Plig can pass through, e.g. false bricks
    2-7   Fruit characters
    8     Harmful character e.g. fire
    9-C   One-way characters e.g. arrows
    D-F   Solid sections e.g. trees

For example on level 1:

          Fakes-   Fruit-----   Harmful  One way---   Solid--
    Index (0)  1   3  4  6  7   8        9  A  B  C   D  E  F
    Value (0)  0   13 0  0  0   0        0  0  0  0   1  0  0

The screen is based on a 10x8 grid, equivalent to 80 characters, stored as 40 bytes - one character per nybble - specified in data items 16-55.

Data item 56: Plig's (x,y) start coordinates, &YX

Data item 57: Time allowed in seconds (BCD, e.g. 30 = 30 decimal seconds.)

3. GCODE Game Code Assembly

Declared variables, lines 50-120:

C%() Mode 2 colour codes, READ in from line 3130
NSNDS Number of sounds used in game
NSPRTS Number of sprites
NPLLTS Number of palettes used by 4-colour sprites
CACHE Sprite cache. Only 15 sprites are held in memory at any one time, extracted from G.PTS file at beginning of each level
ICND Sprite data for clock and cherries icons
FONTS Data for 2-colour digits (character data)
LEVPTR Start of pointers to level data
LEVDAT Data for first level
OSD, OSB, OSF, OSG OSWORD, OSBYTE, OSFILE, and OSARGS respectively

Zero Page Variables:

    &50   Plig x coordinate in columns (0-71)
    &51   Plig y coordinate in character rows (0-27)
    &52   Plig x coordinate in map units (0-9)
    &53   Plig y coordinate in map units (0-7)
    &54   Plig character, 16=facing forward, 17=
    &55   Level number 0-24
    &56   Number of fruit pieces left to collect
    &58   Clock value (BCD)
    &59   Clock flag, <>0 if clock needs decrementing
    &77-A Sprite Palette

Machine code subroutines in order of appearance:

CHKT Checks &59, is non-zero if screen clock to be decremented
PTIM Calculates address at which clock digits appear (5,30) and calls NUM to print digits
IEVNT Initialise the event vector EVNTV at &220/1 to call TIMER every second
TIMER Increment &59. CHKT will detect the change and adjust clock if necessary
NUM Expand 2-colour digit data into a 16-colour mode 2 sprite. Entered with X=x pos of digits, Y=y pos of digits, A=digit pair to be plotted. High and low nybbles masked off. Multiplied by 8 and added to FONTS to get pattern address. Each byte of pattern has two bits at a time rotated out. Result is an index into CCDS, a table of values for black-black, black-white, white-black, and white-white Mode 2 colour codes. These are masked or ANDed with the corresponding value from FMSKS to get different colours for different digits. This routine is a variation on the one in the OS ROM
CHKK Check keyboard for key presses. List of five INKEY codes in KEYS
PLGR Move Plig right
PLGL Move Plig left
PLGD Move Plig down
PLGU Move Plig up
PLGX Acknowledge Escape condition. Set clock flag and zeroise clock
FRUIT Collect a fruit by reprinting (EORing therefore erasing) fruit, make sounds, and decrement fruit
PLGF Show Plig facing forward briefly, used when changing direction
DELAY Delay loop affected by value in A
INTLEV Initialise level. Get correct level data address by using &55 as index into LEVPTR table of addresses stored 25 low bytes followed by 25 high bytes. Copy level data into MAPDAT. Open filename at FSP, "G.PTS", by setting X and Y registers to point to CACHE and calling OSFIND with A=&40. Store file identifier in first byte of CB, the Control Block. CB+1 and CB+2 specify where to load the data. CB+5/CB+6 is how many bytes to load, in this case the size of the sprite - &80 bytes. A sprite index of zero is a space so the corresponding position in the cache is filled with zeros. Otherwise position in PSPTS from which to extract bytes is calculated by doing ((sprite index)-1)*&80 at NOSPC. X and Y registers set to point to CB, and A=3 to signify loading of data. NEXT2 adds &80 to &70/1 to get next position in cache until all 15 sprites have been loaded. Work out Plig's starting coordinates
PPLG Print Plig
READM Read value from map, returning &FF if off map
CALCM Calculate linear map position in memory, = X*4 + Y DIV 2
WRITEM Write value to map
SHWLEV Show level, display map characters on screen. One byte represents two characters plotted one below the other. Note the 'cascading' nature of the routine where after a subroutine has been called the subroutine is executed again because it Is the next bit of code. SHW4 plots the sprite represented by a nybble. SHW3 plots sprites represented by two nybbles by calling SHW4 and leads into SHW4, thus SHW4 is executed twice. SHW2 plots a column of sprites (4 bytes=8 nybbles or sprites) by setting up a loop to call SHW3 four times. SHW1 calls SHW3 ten times to plot ten columns of sprites across the screen. Note that SHW4 tallies how many fruit characters it detects; this is the fruit counter
BUMP Make bump sound as Plig hits wall. Leads into MKSND
MKSND Make sound. A = sound number
SNDDAT Sound data
CALC Calculate screen address
EXPAND Expand four-colour sprite into mode 2 format. A=sprite number. Compacted data takes up &80 bytes, expanded to &100 bytes in page 6 of memory. Corresponding palette copied to zero page. Data is arranged in special format so that two bits at a time can be rotated out and used as an index into the palette to get the correct byte to put onto the Mode 2 screen
PLOT Plot sprite at address in &70/1 by copying data from page 6
PALETTE NPLLTS x 4 bytes giving colour codes used when expanding sprite data. Line 3170 consists of strings of 4 characters corresponding to colours used in reverse order; the colour codes are worked out in line 3080 using C%(). E.g. pallette zero specified as 3120 becomes 0,2,8,10 (black, red, green and yellow), palette four specified as 6310 -< 0,2,10,40 (black, red, yellow and cyan)
PALIND Index for each sprite into PALETTE. For example sprite 1 (Tree) uses palette 0, sprite 2 (Metal Plate) uses palette 4

4. TITLES Title Page Assembly

TTL Fills background with random combinations of blue pixels. Random number generator address is &AF51 on BBC B, &AA7B on Master. Prints menu options
EPS Password screen
GOS Game over screen
DOUB Prints double height, outlined characters at (&7B/C,&7D/E) in random colour
V25 Performs MOVE to coordinates held at (GX+1/GX,GY+1/GY). Note reverse order of VDU string for speed.
PMESS Prints each letter of message in double height
BIG Enlarges character definition to double height blanking some lines in lower half
SPECIAL Plots slanted dithered or plain bands of colour on screen
VCDS VDU codes for printing double height character
RBTAB Colour codes for dithered bands:
Index Value Colour
0/5   13/14 Green-Yellow
1/6   7/11  Red-Yellow
2/7   19/35 Red-Magenta
3/8   52/56 Blue-Cyan
4/9   31/47 Yellow-white
STRP Codes for colour bands. &0X is plain colour where X = GCOL code. &8X = dithered colour where X=index (0-4) into RBTAB
DRAWV Move and draw codes for the coloured bands
WINDAT VDU codes for graphics windows set up before bands are plotted so bands don't have to fill whole of screen

5. PINTRO Plig Intro

Defines ENVELOPEs, prints banner.

PROCplig Reads and pokes in data for banner. A pair of numbers describe a vertical line's start position and length. See article LINEIT for details. Different parts of logo printed in different colours by Boolean test in line 340.

6. PGAME Plig Game

PROCgame REPEAT initialise timer, print screen. REPEAT check keyboard UNTIL all fruit collected or out of time. If fruit collected move to next screen. UNTIL out of time
PROCintro Show password for level (not first level)
PROCgetpw Player inputs password
PROCadd Add letter to input string and call letter plotting routine to plot on screen
PROCdel Delete letter from input string
PROCkey Wait for keypress
PROCplig Plot banner - same as in PINTRO