Some of this first section has been taken from "The Official Book of King's Quest" written by Donald B. Trivette.
The PICTURE is usually used for background pictures and other full screen images. Pictures in AGI and early SCI games are not stored as a complete picture. Instead they're constructed and stored as coordinates and vectors. Vectors give the instructions for drawing a picture and they have the advantage of taking less space than would a bit image of a complete picture.
To maintain and enhance the three dimensional quality of the rooms, every object is constructed with a priority in relation to other objects. There are 16 bands or areas in which things may be placed. Although the priority bands are invisible in the finished product, the artist must use them like a horizontal grid as he draws the room. Considerable effort and time is spent placing houses, bushes, and trees so the player remains unaware of the rooms mathematical rigidity. It wouldn't so for things to look like they were lined up on a checker board.
In AGI games, the priority bands lie roughly in the following Y ranges:
Priority Band | Y range |
---|---|
4 | - |
5 | 48 - 59 |
6 | 60 - 71 |
7 | 72 - 83 |
8 | 84 - 95 |
9 | 96 - 107 |
10 | 108 - 119 |
11 | 120 - 131 |
12 | 132 - 143 |
13 | 144 - 155 |
14 | 156 - 167 |
15 | 168. |
The reason that there is no priority 0 to 3 shown above is because they don't exist. Priority four has the least priority. It is given to the background components. Nothing is ever given a priority of four except for the background parts of the picture itself. The colours black, blue, green, and cyan are infact used for control lines because the control lines and the priority bands are drawn on the same screen for AGI games, and this means that there would be a clash if we had priority 0 to 3.
I've checked all the AGI games including those using version 3 and it looks as if nothing is ever drawn below 168. Some objects in the picture might have a priority of 15. These objects will start from a position with a Y component of 168. Ego can never walk below 167 and thus never has a priority of 15. SCI games are obviously a bit different because they extend the pictures down to the full 200 pixels.
As the room nears completion, the artist adds control lines that determine where Ego can walk. Ego shouldn't walk through a wall or tree, for example, but sometimes Ego does. The Sierra staff spend a lot of time running Ego all over the rooms looking for places where Ego falls off or walks through something Ego shouldn't. Fixing an error may involve shifting things around or adding new features to a room to cover up a bug. I remember finding a bug in Space Quest 2 which allowed Roger to walk off into space in the starting room.
Black = unconditional barrier.
Blue = conditional barrier.
Green = alarm barrier.
Cyan = water (or any other surface).
Some of the above control lines set flags that can be tested when EGO walks over them (see flag descriptions in another document).
In most games, black and blue appear to be used for obstacle use which means that Ego can't walk past that line. This is the main use for control lines. Other uses include triggers which active an event. For example, control lines are used at the edge of water, or the edge of a cliff to tell the interpreter at what point it should execute its "drowning" or "falling" sequence of events. We can all remember the nasty trap that the hunter beast on Labion set for us in Space Quest II. This is activated when Ego walks past a green control line.
In some games like KQ1, KQ2, KQ3, and SQ1, you can press ALT-D to enter a Debug mode. In this mode you can switch to the screen which holds the priority bands and control lines. In KQ1 you push F6, in KQ2 F8, and in all the others you usually type "show priority".
In the AGI interpreter, the priority bands and control lines are drawn on the same screen. This can lead to problems when the interpreter wants to know the priority of a pixel that has a control line drawn over it. The way that the interpreter deals with this is to search downwards until it finds a pixel that isn't a control line and it assumes that the pixel it is looking at is the same priority as the one it found by searching downwards (increasing Y). Usually this will be the pixel immediately below but for some pixels this can be as far away as twenty pixels or more!! In some games this can cause a noticeable visual error if you are aware of it. For example, you could walk behind a strip of grass that thinks it is part of the tree below it (KQ1, room 20, blue control line beside left hand tree). There are also a number of other one pixel visual errors that are virtually unnoticeable if you don't know they are there.
_ |_| <- priority required for this pixel. Search downwards | |_| \ for next pixel | |_| \___ These pixels have all got a control with a priority \|/ |_| / line covering their priority info. |_| / |_| <- use this pixels priority.
It may be possible to draw the control lines on a separate screen altogether which would conserve the priority information.
Pictures are drawn using nine different drawing actions. These actions are given values from 0xF0 to 0xFA and are defined as follows:
0xF0 : Change picture colour and enable picture draw.
0xF1 : Disable picture draw.
0xF2 : Change priority colour and enable priority draw.
0xF3 : Disable priority draw.
0xF4 : Draw a Y corner.
0xF5 : Draw an X corner.
0xF6 : Absolute line (long lines).
0xF7 : Relative line (short lines).
0xF8 : Fill.
0xF9 : Change pen size and style.
0xFA : Plot with pen.
0xFB - 0xFE : Unused in most AGI games.
Note: SQ2 appears to be the only AGI version 2 game that uses 0xF9 and 0xFA. The AGI interpreters before this game will most likely not support these two drawing actions.
The special code 0xFF says that the end of the picture data has been reached. All other values are used by the various drawing actions to tell them what to draw and will always be less than 0xF0. The picture data can be processed byte by byte. Whenever a value of 0xF0 or greater is encountered, the action is changed to the one given and then all the bytes between this code and the next action code are arguments to this action. Half of the actions have a set number of arguments, the other half can have an unlimited number of arguments.
In the following detailed descriptions of each action, the word "picture" refers to the screen that is seen by the player when they play the AGI screen. The word "priority" refers to the screen that is held in memory and contains control information invisible to the player. As a picture is drawn, both screens are updated depending on whether drawing is enabled from each screen.
Function: Changes the current drawing colour for the picture screen to that given by the one and only argument, and enables subsequent actions to draw to the picture screen.
Example: F0 0D Changes picture screen drawing colour to light magenta and enables drawing to the picture screen.
Function: Disables drawing to the picture screen. This is done whenever there is something which only needs to be drawn on the priority screen such as the control lines. There are no arguments for this action.
Function: Changes the current drawing colour for the priority screen to that given by the one and only argument, and enables subsequent actions to draw to the priority screen.
Example: F0 04 Changes priority screen drawing colour to red and enables drawing to the priority screen.
Function: Disables drawing to the priority screen. This is done whenever there is something which only needs to be drawn on the picture screen such as the finer details of the picture. There are no arguments for this action.
I call the following two actions the corner actions because they do not draw diagonal lines at all but instead alternate from horizontal line to vertical line (or vice versa) giving rise to a series of right angled corners.
_________ | | B__ | |_____ | | |_| A
The above diagram shows the type of pattern created. If A were the starting coordinate, then it would be called a Y corner. This is because the Y or vertical component is changed first. If B were the starting coordinate, then it would be called an X corner. This is because the X or horizontal component is changed first.
Function: The first two arguments for this action are the coordinates of the starting position on the screen in the order x and then y. The remaining arguments are in the order y1, x1, y2, x2, ...
Note that the y component is the first to be changed and also note that this action does not necessarily end on either component, it just ends when the next byte of 0xF0 or above is encountered. A line is drawn after each byte is processed.
Example: F4 16 16 18 12 16 F?
($12, $16) ($16, $16) E S S = Start X X E = End XXXXX X = normal piXel ($12, $18) ($16, $18)
Function: The first two arguments for this action are the coordinates of the starting position on the screen in the order x and then y. The remaining arguments are in the order x1, y1, x2, y2, ...
Note that the x component is the first to be changed and also note that this action does not necessarily end on either component, it just ends when the next byte of 0xF0 or above is encountered. A line is drawn after each byte is processed.
Example: F5 16 16 18 12 16 F?
($16, $12) ($18, $12) EXX X S = Start X E = End X X = normal piXel SXX ($16, $16) ($18, $16)
Function: Draws lines between points. The first two arguments are the starting coordinates. The remaining arguments are in groups of two which give the coordinates of the next location to draw a line to. There can be any number of arguments but there should always be an even number.
Example: F6 30 50 34 51 38 53 F?
This will draw a line from (48, 80) to (52, 81), and a line from (52, 81) to (56, 83).
Function: Draw short relative lines. By relative we mean that the data gives displacements which are relative from the current location. The first argument gives the standard starting coordinates. All the arguments which follow these first two are of the following format:
S | X disp | S | Y disp | ||||
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
This gives a displacement range of between -7 and +7 for both the X and the Y direction.
Example: F7 10 10 22 40 06 CC F?
S + S = Start X+++X X = End of each line + + = pixels in each line E + E = End + + + + Remember that CC = (x-4, y-4). ++ X
S + S = Start X+++X X = End of each line + + = pixels in each line E + E = End + + + + Remember that CC = (x-4, y-4). ++ X
Function: Flood fill from the locations given. Arguments are given in groups of two bytes which give the coordinates of the location to start the fill at. If picture drawing is enabled then it flood fills from that location on the picture screen to all pixels locations that it can reach which are white in colour. The boundary is given by any pixels which are not white.
If priority drawing is enabled, and picture drawing is not enabled, then it flood fills from that location on the priority screen to all pixels that it can reach which are red in colour. The boundary in this case is given by any pixels which are not red.
If both picture drawing and priority drawing are enabled, then a flood fill naturally enough takes place on both screens. In this case there is a difference in the way the fill takes place in the priority screen. The difference is that it not only looks for its own boundary, but also stops if it reaches a boundary that exists in the picture screen but does not necessarily exist in the priority screen.
Drawing actions 0xF9 and 0xFA deal with plotting patterns. Most drawing programs have options to change the size, and style of the pen or brush. The style covers different shapes and textures. AGI PICTURES provide these tools as well.
Function: Change the characteristics of the pattern plotted by drawing action 0xFA. If bit 5 is not set, then the pattern is a solid shape. If bit 5 is set, then the pattern is like a splatter. Bit 4 selects whether the brush is a circle or a rectangle. Bits 0-2 give the size of the shape which will be a value from 0 to 7. These characteristics appear to only affect drawing action 0xFA.
The default brush is a solid circle or rectangle of size 0, which should be used until an 0xF9 action is encountered.
___ ___ ___ ___ ___ ___ ___ ___ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |___|___|___|___|___|___|___|___| | | |___|___| 0 = Solid _________| | | 1 = Splatter | |______ Pen size | 0 = Circle ____________| 1 = Rectangle
RECTANGLE SIZES
X XX XXX XXXX XXXXX XXXXXX XXXXXXX XXXXXXXX size+1 0 X* XXX XXXX XXXXX XXXXXX XXXXXXX XXXXXXXX _____________ XX X*X XXXX XXXXX XXXXXX XXXXXXX XXXXXXXX | | 1 XXX XX*X XXXXX XXXXXX XXXXXXX XXXXXXXX | | XXX XXXX XX*XX XXXXXX XXXXXXX XXXXXXXX | | 2 XXXX XXXXX XXX*XX XXXXXXX XXXXXXXX | | (size*2)+1 XXXX XXXXX XXXXXX XXX*XXX XXXXXXXX | | 3 XXXXX XXXXXX XXXXXXX XXXX*XXX | | XXXXX XXXXXX XXXXXXX XXXXXXXX | | 4 XXXXXX XXXXXXX XXXXXXXX | | XXXXXX XXXXXXX XXXXXXXX | | WHERE 5 XXXXXXX XXXXXXXX |_____________| XXXXXXX XXXXXXXX X = agi pixels 6 XXXXXXXX IN GENERAL * = coordinates given XXXXXXXX for plot 7
CIRCLE SIZES
X XX X XX X XX XXX XX size+1 0 X* XXX XX XXX XXXX XXXXX XXXX _____________ XX X*X XXXX XXXXX XXXX XXXXX XXXXXX | | 1 XXX XX*X XXXXX XXXX XXXXX XXXXXX | | X XXXX XX*XX XXXXXX XXXXXXX XXXXXX | | 2 XX XXXXX XXX*XX XXXXXXX XXXXXXXX | | (size*2)+1 XX XXXXX XXXXXX XXX*XXX XXXXXXXX | | 3 XXX XXXX XXXXXXX XXXX*XXX | | X XXXX XXXXXXX XXXXXXXX | | 4 XXXX XXXXX XXXXXXXX | | XX XXXXX XXXXXX | | WHERE 5 XXXXX XXXXXX |_____________| XXX XXXXXX X = agi pixels 6 XXXX IN GENERAL * = coordinates given XX for plot 7
To implement this you will need to store bitmaps for each of these of these circles.
Function: Plots points with the pen defined with drawing action 0xF9. If the pen style is set to solid, then the arguments are just a list of coordinates to be plotted. If the pen style is set to splatter brush (texture), then the arguments are in groups of three with the first argument giving the texture number and the other two giving the coordinates. The texture number determines in what way the pixels will splatter within the defined shape. Bits 1-7 seem to give the actual texture number. Bit 0 does not do anything. This means that there are 120 different pixel splatter bitmaps (values 0xF0 and above can not be used as they are treated as drawing actions). There is actually only 32 bytes of texture data which means that most of the splatter bitmaps overlap.
All of the data needed for the 128 texture patterns is included in the following 32 bytes (256 bits):
0x20, 0x94, 0x02, 0x24, 0x90, 0x82, 0xa4, 0xa2, 0x82, 0x09, 0x0a, 0x22, 0x12, 0x10, 0x42, 0x14, 0x91, 0x4a, 0x91, 0x11, 0x08, 0x12, 0x25, 0x10, 0x22, 0xa8, 0x14, 0x24, 0x00, 0x50, 0x24, 0x04
The only difference between each texture pattern is its starting position within this table. The following table gives the starting bit position in the above table for each texture pattern number given as the first argument of each pen plot:
0x00, 0x18, 0x30, 0xc4, 0xdc, 0x65, 0xeb, 0x48, 0x60, 0xbd, 0x89, 0x04, 0x0a, 0xf4, 0x7d, 0x6d, 0x85, 0xb0, 0x8e, 0x95, 0x1f, 0x22, 0x0d, 0xdf, 0x2a, 0x78, 0xd5, 0x73, 0x1c, 0xb4, 0x40, 0xa1, 0xb9, 0x3c, 0xca, 0x58, 0x92, 0x34, 0xcc, 0xce, 0xd7, 0x42, 0x90, 0x0f, 0x8b, 0x7f, 0x32, 0xed, 0x5c, 0x9d, 0xc8, 0x99, 0xad, 0x4e, 0x56, 0xa6, 0xf7, 0x68, 0xb7, 0x25, 0x82, 0x37, 0x3a, 0x51, 0x69, 0x26, 0x38, 0x52, 0x9e, 0x9a, 0x4f, 0xa7, 0x43, 0x10, 0x80, 0xee, 0x3d, 0x59, 0x35, 0xcf, 0x79, 0x74, 0xb5, 0xa2, 0xb1, 0x96, 0x23, 0xe0, 0xbe, 0x05, 0xf5, 0x6e, 0x19, 0xc5, 0x66, 0x49, 0xf0, 0xd1, 0x54, 0xa9, 0x70, 0x4b, 0xa4, 0xe2, 0xe6, 0xe5, 0xab, 0xe4, 0xd2, 0xaa, 0x4c, 0xe3, 0x06, 0x6f, 0xc6, 0x4a, 0x75, 0xa3, 0x97, 0xe1
Important note: When drawing the brush, if the bit position in the texture data (first table above) reaches 255, it should loop round to 0, instead of looping at 256 as you would normally expect. This may be because of a bug in the picture drawing code in the interpreter. If you loop at 256 then some of the patterns will not be correct.
When a texture pattern is drawn in the shape of a circle, the texture pattern 'fills' the shape of the circle. This diagram will explain what I mean:
X.XX X. X.X. XX .... X.X. .X.X .... X... .X.X ..X. X. XXXX .. Rectangle Circle |
The corner pixels of the circle which aren't part of the circle are totally ignored. The circle isn't just a cut out of the equivalent rectangle. A bit hard to explain. Look at the code for showpic.exe for more info. |
Writing code to interpret the picture data in order to draw the picture on the screen is easier said than done. It turns out that you have to have a line drawing algorithm which exactly matches the one that Sierra uses. A pixel out of place can mean that a fill overflows or doesn't work at all.
You will also have to write your own fill routine because not many of the standard fill routines can stop at a multicoloured boundary. You are also dealing with two screens both of which will probably be stored in memory somewhere rather than the screen.
The picture screen has a starting state of being completely white. The priority screen has starting state of being completely red. It is important that you set all pixels in each screen to the relevant background colour else you won't get the right result.
The screen mode used by the AGI games is the 320x200x16 standard EGA mode. However, all graphics is designed to be shown on a 160x200x16 mode. This was apparently the resolution that the original PCjr interpreter used. They stuck with it when they started supporting EGA and thus have a situation where each AGI pixel has a width of two normal 320x200 pixels.
This routine is relatively straight forward and I suggest that you look at it and try to understand it or you'll be having headaches trying to get you're routines acting like the Sierra ones. Basically it draws a line from (x1, y1) to (x2, y2) using a function called pset to draw a single pixel. The function "round" is what makes it act like the Sierra. Essentially when it comes down to a 50:50 decision about where to put a pixel, the direction in which the line is being drawn is taken into account. I've only noticed one pixel out of place in all the screens I've tried SHOWPIC on which makes me believe its probably not a fault in this algorithm, but somewhere else in the code.
int round(float aNumber, float dirn) { if (dirn < 0) return ((aNumber - floor(aNumber) <= 0.501)? floor(aNumber) : ceil(aNumber)); return ((aNumber - floor(aNumber) < 0.499)? floor(aNumber) : ceil(aNumber)); } void drawline(word x1, word y1, word x2, word y2) { int height, width; float x, y, addX, addY; height = (y2 - y1); width = (x2 - x1); addX = (height==0? height:(float)width/abs(height)); addY = (width==0? width:(float)height/abs(width)); if (abs(width) > abs(height)) { y = y1; addX = (width == 0? 0 : (width/abs(width))); for (x=x1; x!=x2; x+=addX) { pset(round(x, addX), round(y, addY)); y+=addY; } pset(x2,y2); } else { x = x1; addY = (height == 0? 0 : (height/abs(height))); for (y=y1; y!=y2; y+=addY) { pset(round(x, addX), round(y, addY)); x+=addX; } pset(x2,y2); } }
The functions floor, ceil, and abs are all standard C functions, but for all you none C programmers, here's what they do:
floor: rounds a floating point number down.
ceil: rounds a floating point number up. abs: returns the absolute value of a number |x|.
I have discovered that using a queue in a flood fill routine works quite well. It is also the easiest method to understand as far as I'm concerned. I just thought about what needed to be done and this method took shape.
Basically you start at a particular location. If its the desired background colour (white or red depending on the screen), then set that pixel. You then check the pixels immediately up, left, down, and right to see if they are of the desired background colour. If they are, store them in the queue. You then retrieve the first pixel position from the queue and repeat the above steps.
I've often wondered if it would be possible to show PICTUREs in a higher resolution, for example, 640x400. Since the data is stored as vectors, it should be possible to multiply all the x components by four and all the y components by two and then draw the lines. This would give less blocky pictures. There would be a number of problems to overcome. Firstly, the fill action (or tool) may cause problems because pixels could be in the wrong places. There will also be a need to draw end pixels of a line with a width of four so that there are no holes for the flood fill to flow out of.
The picture editor that Sierra used back in the vector picture days was much like a CAD program. I've seen a few photos of it in "The Official Book of King's Quest". It has a status bar at the top which gives the current tool being used (Line, Fill, etc), the current X and Y locations, and four others which I explain below.
Status bar examples:
Tool:Line V:8 P:A C:o X=249 Y=89 Pri:5 Tool:Fill V:B P:0 C:B X=96 Y=99 Pri:6 Tool:Line V:A P:o C:o X=199 Y=55 Pri:2
o=off (or disabled)
Pri looks like it could be giving the current priority band that the cursor location is in. The above status lines are for the SCI Picture Editor. I ran these values past SQ3 and the values given for Pri are indeed the values of the priority band at the locations given.
I think that V, P, and C refer to the colours being used on the three different screens (the SCI games have a separate screen for the control lines rather than having both the priority bands and control lines on the same screen. This is why there were three screens and not the two that we are used to in AGI games). This would mean that V=Visual, P=Priority and C=Control.
In an AGI Picture Editor, there would only be the Visual screen and the Priority screen. The picture editor would obviously be able to switch between the two screens. I've also
noticed that the early vector based SCI picture editor supports a feature which removes solid colours (Fills) with a single keystroke and I presume another keystroke puts them
back. When the fills have been removed, they are represented as a tiny cross. Apparently removing the solid colours makes it easier to add small details like flowers.
Top
You can help keep The Sierra Help Pages and its affiliates alive by helping to defray some of the costs of hosting this site. If it has been of help to you, please consider contributing to help keep it online.Thank you.
The Sierra Help Pages | Sierra Game Help | Walkthroughs | Hints, Tips & Spoilers | Utilities | Links | SHP Forums | Search
© 2013 to present The Sierra Help Pages. All rights reserved. All Sierra games, artwork and music © Sierra.