Difference between revisions of "AGI Specifications: Chapter 8 - View Resources"
(21 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
[[Adventure Game Interpreter Specifications|Table of Contents]] | [[Adventure Game Interpreter Specifications|Table of Contents]] | ||
− | <div align="center"><div id="view"></div><div id="s8"></div><br /><span style="font-size: 22pt"> | + | <div align="center"><div id="view"></div><div id="s8"></div><br /><span style="font-size: 22pt">View Resources</span> |
''Written by [[Peter Kelly]]''<br /> | ''Written by [[Peter Kelly]]''<br /> | ||
− | ''Modifications by [[Claudio Matsuoka]''<br /> | + | ''Modifications by [[Claudio Matsuoka]]''<br /> |
''(Last updated: 22 May 1999)''</div> | ''(Last updated: 22 May 1999)''</div> | ||
Line 10: | Line 10: | ||
== <div id="ss8.1"></div><br />8.1 Introduction == | == <div id="ss8.1"></div><br />8.1 Introduction == | ||
− | + | View resources contain some of the graphics for the game. Unlike the picture resources which are full-screen background images, View resources are smaller ''sprites'' used in the game, such as animations and objects. They are also stored as bitmaps, whereas pictures are stored in vector format. | |
− | Each | + | Each View resource consists of one or more ''loops''. Each loop in the resource consists of one or more ''cels'' (frames). Thus several animations can be stored in one view, or a View can just be used for a single image. The maximum number of loops supported by the interpreter is 255 (0-254) and the maximum number of cels in each is 255 (0-254). |
Note: Except when noted otherwise, 16 bit data is stored in little endian format (i.e. the value has the least significant byte stored first, and the most significant byte stored second). Most word values in AGI are stored like this, but not all. | Note: Except when noted otherwise, 16 bit data is stored in little endian format (i.e. the value has the least significant byte stored first, and the most significant byte stored second). Most word values in AGI are stored like this, but not all. | ||
− | == <div id="ss8.2"></div><br />8.2 | + | == <div id="ss8.2"></div><br />8.2 View Resource Format == |
| | ||
Line 23: | Line 23: | ||
<blockquote> | <blockquote> | ||
− | < | + | {| class="wikitable" <!-- width="450" --> |
− | Byte | + | |- |
− | + | !Byte!!Meaning | |
− | + | |- | |
− | + | |align="center"|<code>0</code>||<code>Unknown (always seems to be either 1 or 2)</code> | |
− | + | |- | |
− | + | |align="center"|<code>1</code>||<code>Unknown (always seems to be 1)</code> | |
− | + | |- | |
− | + | |align="center"|<code>2</code>||<code>Number of loops</code> | |
− | + | |- | |
− | + | |align="center"|<code>3-4</code>||<code>Position of description (more on this later) Both bytes are 0 if there is no description</code> | |
− | + | |- | |
− | + | |align="center"|<code>5-6</code>||<code>Position of first loop</code> | |
− | + | |- | |
+ | |align="center"|<code>7-8</code>||<code>Position of second loop (if any)</code> | ||
+ | |- | ||
+ | |align="center"|<code>9-10</code>||<code>Position of third loop (if any)</code> | ||
+ | |} | ||
</blockquote> | </blockquote> | ||
Line 46: | Line 50: | ||
<blockquote> | <blockquote> | ||
− | < | + | {| class="wikitable" <!-- width="450" --> |
− | Byte | + | |- |
− | + | !Byte!!Meaning | |
− | + | |- | |
− | + | |align="center"|<code>0</code>||<code>Number of cels in this loop</code> | |
− | + | |- | |
− | + | |align="center"|<code>1-2</code>||<code>Position of first cel, relative to start of loop</code> | |
− | + | |- | |
− | + | |align="center"|<code>3-4</code>||<code>Position of second cel (if any), relative to start of loop</code> | |
+ | |- | ||
+ | |align="center"|<code>5-6</code>||<code>Position of third cel (if any), relative to start of loop</code> | ||
+ | |} | ||
</blockquote> | </blockquote> | ||
Line 62: | Line 69: | ||
<blockquote> | <blockquote> | ||
− | < | + | {| class="wikitable" <!-- width="450" --> |
− | Byte | + | |- |
− | + | !Byte!!Meaning | |
− | + | |- | |
− | + | |align="center" valign="top"|<code>0</code>||<code>Width of cel (remember that AGI pixels are 2 normal EGA pixels <br /> | |
− | + | wide so a cel of width 12 is actually 24 pixels wide on screen)</code> | |
− | + | |- | |
− | + | |align="center"|<code>1</code>||<code>Height of cel</code> | |
− | + | |- | |
− | + | |align="center"|<code>2</code>||<code>Transparency and cel mirroring</code> | |
+ | |} | ||
</blockquote> | </blockquote> | ||
Line 86: | Line 94: | ||
The actual image data for each cel is stored using RLE (run length encoding) compression. This means that instead of having one byte for each single pixel (or 1/2 byte as you would use for 16 colors), each byte specifies how many pixels there are to be in a row and what color they are. I will refer to these groups of pixels as "chunks". | The actual image data for each cel is stored using RLE (run length encoding) compression. This means that instead of having one byte for each single pixel (or 1/2 byte as you would use for 16 colors), each byte specifies how many pixels there are to be in a row and what color they are. I will refer to these groups of pixels as "chunks". | ||
− | This method of compression is not very efficient if there is a lot of single pixels in the image (e.g. a | + | This method of compression is not very efficient if there is a lot of single pixels in the image (e.g. a View showing static on a TV screen), but in most cases it does save a fair amount of space. |
Each line (not to be confused with a chunk) in the cel consists of several bytes of pixel data, then a 0 to end the line. Each byte of pixel data represents one chunk. The first four bits determine the color, and the last four bits determine the number of pixels in the chunk. | Each line (not to be confused with a chunk) in the cel consists of several bytes of pixel data, then a 0 to end the line. Each byte of pixel data represents one chunk. The first four bits determine the color, and the last four bits determine the number of pixels in the chunk. | ||
Line 107: | Line 115: | ||
Mirroring is when you have one loop where all the cels are a mirror image of the corresponding cels in another loop. Although you could do this manually by drawing one loop and then copying and pasting all the cels to another loop and flipping them horizontally, AGI views provide the ability to have this done automatically -- you can draw one loop, and have another loop which is set as a mirror of this loop. Thus, when you change one loop you change the other. This is useful if you have an animation of a character walking left and right -- you just draw the right-walking animation and have another loop a mirror of this which will have the left-walking animation. Another advantage of cel mirroring is to save space -- it doesn't make much difference these days, but back when AGI was written the games were designed to run on 256K systems which meant that memory had to be used as efficiently as possible. | Mirroring is when you have one loop where all the cels are a mirror image of the corresponding cels in another loop. Although you could do this manually by drawing one loop and then copying and pasting all the cels to another loop and flipping them horizontally, AGI views provide the ability to have this done automatically -- you can draw one loop, and have another loop which is set as a mirror of this loop. Thus, when you change one loop you change the other. This is useful if you have an animation of a character walking left and right -- you just draw the right-walking animation and have another loop a mirror of this which will have the left-walking animation. Another advantage of cel mirroring is to save space -- it doesn't make much difference these days, but back when AGI was written the games were designed to run on 256K systems which meant that memory had to be used as efficiently as possible. | ||
− | Mirroring is done by having both loops share the same cel data -- you saw above that you can have two loop references pointing to the same place. The first four bits of the 3rd byte in the header of each cel tell the interpreter what is mirrored: | + | Mirroring is done by having both loops share the same cel data -- you saw above that you can have two loop references pointing to the same place. The first four bits of the 3rd byte in the header of each cel tell the interpreter what is mirrored:<br /> |
− | + | | |
<blockquote> | <blockquote> | ||
− | < | + | {| class="wikitable" <!-- width="450" --> |
− | + | |- | |
− | + | !Byte!!Meaning | |
− | + | |- | |
− | + | |align="center"|<code>0</code>||<code>Specify whether or not this cel is mirrored</code> | |
− | + | |- | |
− | + | |align="center"|<code>1-3</code>||<code>Specify the number of the loop (from 0-7) which is NOT mirrored</code> | |
− | </ | + | |} |
− | </ | + | </blockquote><br /> |
When the interpreter goes to display a loop, it looks at the bit 0 and sees if it is mirrored or not. If it is, then it checks the loop number -- if this is NOT the same as the current loop, then it flips the cel before displaying it. | When the interpreter goes to display a loop, it looks at the bit 0 and sees if it is mirrored or not. If it is, then it checks the loop number -- if this is NOT the same as the current loop, then it flips the cel before displaying it. | ||
− | If you have a cel that is mirrored, you need to ensure that the number of bytes the cel takes up in the resource is greater than or equal to the number of bytes that the flipped version of the cel would take up. The reason for this is that the interpreter loads the | + | If you have a cel that is mirrored, you need to ensure that the number of bytes the cel takes up in the resource is greater than or equal to the number of bytes that the flipped version of the cel would take up. The reason for this is that the interpreter loads the View resource into memory and works with that for displaying cels, rather than decoding it and storing it in memory as a non-compressed bitmap. I assume that it doesn't even bother "decoding" it as such -- it probably just draws the chunks of color on the screen as they are. When it has to display the flipped version of a cel, instead of storing the flipped cel somewhere else in memory, it flips the version that is there. So in memory you have the View resource that was read from the file, except that some of the cels have been changed. This is why there is mirroring information stored in each cel -- the interpreter has to know what cels have been changed. When it flips a cel, it changes the loop number in the 3rd byte of the cel header to the number of the loop it is currently displaying the cel for. So when it looks at this number the next time for a different loop, it will see that the cel is not the right way round for that loop and mirror it again. |
This process seems very inefficient to me. I don't know why it doesn't just draw the chunks of color on the screen back to front. But since it does it this way we have to make sure that there is enough room for the flipped cel. | This process seems very inefficient to me. I don't know why it doesn't just draw the chunks of color on the screen back to front. But since it does it this way we have to make sure that there is enough room for the flipped cel. | ||
Line 132: | Line 140: | ||
==== Description ==== | ==== Description ==== | ||
− | All the views in the game that are used as close-ups of inventory items have a description. When a player "examines" these (in some games you can select "see object" from the menu), they will see the first cel of the first loop of this | + | All the views in the game that are used as close-ups of inventory items have a description. When a player "examines" these (in some games you can select "see object" from the menu), they will see the first cel of the first loop of this View and the description of the object they are examining. This is brought up using the <code>show.obj</code> command. |
The description is stored in plain text, and terminated by a null character. Lines are separated by an 0x0A. | The description is stored in plain text, and terminated by a null character. Lines are separated by an 0x0A. | ||
− | == <div id="ss8.3"></div><br />8.3 | + | == <div id="ss8.3"></div><br />8.3 View Table == |
− | ''Written by [[Lance Ewing]]''<br /> | + | <div align="center">''Written by [[Lance Ewing]]''<br /> |
− | ''(Last updated: 31 August 1997).'' | + | ''(Last updated: 31 August 1997).''</div> |
| | ||
− | Firstly we should note that there is a difference between a | + | Firstly we should note that there is a difference between a View and a View Object. A View is a collection of animated sequences that are stored in a VOL file. When a View is loaded into memory, it is then connected to one or more View table entries (see below) that store information on what the interpreter calls objects (don't confuse this with inventory items). An object is an animated sprite that is currently being controlled by the interpreter. With each interpretation cycle, the state of each object in the View table is updated and, if required, updated on the screen. It is therefore possible to have five or more hungry crocodiles swimming in a moat each which have there own View table entries, all of which point to the same View data. |
View objects appear to have the following properties: | View objects appear to have the following properties: | ||
Line 162: | Line 170: | ||
* number of cels | * number of cels | ||
− | There are probably other properties that aren't listed here which they also possess. In an object oriented environment such as SCI, these properties are stored as instance variables (or selectors) as part of the object. Since AGI isn't object oriented, we would expect to find some sort of | + | There are probably other properties that aren't listed here which they also possess. In an object oriented environment such as SCI, these properties are stored as instance variables (or selectors) as part of the object. Since AGI isn't object oriented, we would expect to find some sort of View table stored in memory which holds theses properties within it. In SQ2 this View table consisted of 43 byte entries. Most commands that deal with View objects will make adjustments to the data in the entry for the relevant object. |
| | ||
Line 169: | Line 177: | ||
<blockquote> | <blockquote> | ||
− | < | + | {| class="wikitable" <!-- width="450" --> |
− | Byte | + | |- |
− | + | !Byte!!Meaning | |
− | 00-01 step.time (stored twice) | + | |- |
− | + | |align="center"|<code>00-01</code>||<code>step.time (stored twice)</code> | |
− | 03-04 x position | + | |- |
− | 05-06 y position | + | |align="center"|<code>02</code>||<code>??</code> |
− | + | |- | |
− | 08-09 pointer to start of | + | |align="center"|<code>03-04</code>||<code>x position</code> |
− | + | |- | |
− | + | |align="center"|<code>05-06</code>||<code>y position</code> | |
− | 0C-0D pointer to start of loop data | + | |- |
− | + | |align="center"|<code>07</code>||<code>current view</code> | |
− | + | |- | |
− | 10-11 pointer to start of cel data | + | |align="center"|<code>08-09</code>||<code>pointer to start of View data</code> |
− | 12-13 pointer to start of cel data (same as above) | + | |- |
− | 14-15 ?? | + | |align="center"|<code>0A</code>||<code>current loop</code> |
− | 16-17 copy of x position | + | |- |
− | 18-19 copy of y position | + | |align="center"|<code>0B</code>||<code>number of loops</code> |
− | 1A-1B x size | + | |- |
− | 1C-1D y size | + | |align="center"|<code>0C-0D</code>||<code>pointer to start of loop data</code> |
− | + | |- | |
− | 1F-20 cycle time (stored twice) | + | |align="center"|<code>0E</code>||<code>current cel</code> |
− | + | |- | |
− | + | |align="center"|<code>0F</code>||<code>number of cels</code> | |
− | + | |- | |
− | + | |align="center"|<code>10-11</code>||<code>pointer to start of cel data</code> | |
− | + | |- | |
− | + | |align="center"|<code>12-13</code>||<code>pointer to start of cel data (same as above)</code> | |
− | + | |- | |
− | + | |align="center"|<code>14-15</code>||<code>??</code> | |
− | 25-26 View flags (see below) | + | |- |
− | + | |align="center"|<code>16-17</code>||<code>copy of x position</code> | |
− | + | |- | |
+ | |align="center"|<code>18-19</code>||<code>copy of y position</code> | ||
+ | |- | ||
+ | |align="center"|<code>1A-1B</code>||<code>x size</code> | ||
+ | |- | ||
+ | |align="center"|<code>1C-1D</code>||<code>y size</code> | ||
+ | |- | ||
+ | |align="center"|<code>1E</code>||<code>step size</code> | ||
+ | |- | ||
+ | |align="center"|<code>1F-20</code>||<code>cycle time (stored twice)</code> | ||
+ | |- | ||
+ | |align="center" valign="top"|<code>21</code>||<code>direction (heading) 0 = stationary, 1 = north, 2 = north/east, 3 = east, 4 = south/east, 5 = south, 6 = south/west, 7 = west, 8 = north/west</code> | ||
+ | |- | ||
+ | |align="center" valign="top"|<code>22</code>||<code>0 = normal.motion, 1 = wander, 2 = follow.ego, 3 = move.obj</code> | ||
+ | |- | ||
+ | |align="center" valign="top"|<code>23</code>||<code>0 = normal.cycle, 1 = end.of.loop, 2 = reverse.loop, 3 = reverse.cycle</code> | ||
+ | |- | ||
+ | |align="center"|<code>24</code>||<code>priority</code> | ||
+ | |- | ||
+ | |align="center"|<code>25-26</code>||<code>View flags (see below)</code> | ||
+ | |} | ||
</blockquote> | </blockquote> | ||
Line 208: | Line 236: | ||
<blockquote> | <blockquote> | ||
− | < | + | {| class="wikitable" <!-- width="450" --> |
− | + | |- | |
− | + | !Byte!!Meaning | |
− | + | |- | |
− | + | |align="center"|<code>0</code>||<code>??</code> | |
− | + | |- | |
− | + | |align="center"|<code>1</code>||<code>0 = observe blocks, 1 = ignore blocks</code> | |
− | + | |- | |
− | + | |align="center"|<code>2</code>||<code>0 = priority released, 1 = fixed priority</code> | |
− | + | |- | |
− | + | |align="center"|<code>3</code>||<code>0 = observe horizon, 1 = ignore horizon</code> | |
− | + | |- | |
− | + | |align="center"|<code>4</code>||<code>??</code> | |
− | + | |- | |
− | + | |align="center"|<code>5</code>||<code>0 = stop cycling, 1 = cycling</code> | |
− | + | |- | |
− | + | |align="center"|<code>6</code>||<code>??</code> | |
− | + | |- | |
− | + | |align="center"|<code>7</code>||<code>??</code> | |
− | + | |- | |
− | + | |align="center"|<code>8</code>||<code>1 = View on water</code> | |
− | + | |- | |
− | + | |align="center"|<code>9</code>||<code>0 = observe objects, 1 = ignore objects</code> | |
− | + | |- | |
− | + | |align="center"|<code>10</code>||<code>??</code> | |
+ | |- | ||
+ | |align="center"|<code>11</code>||<code>1 = View on land</code> | ||
+ | |- | ||
+ | |align="center"|<code>12</code>||<code>??</code> | ||
+ | |- | ||
+ | |align="center"|<code>13</code>||<code>0 = loop released, 1 = loop fixed</code> | ||
+ | |- | ||
+ | |align="center"|<code>14</code>||<code>??</code> | ||
+ | |- | ||
+ | |align="center"|<code>15</code>||<code>??</code> | ||
+ | |- | ||
+ | |align="center"|<code>27</code>||<code>?? (storage for some View related command parameters)</code> | ||
+ | |- | ||
+ | |align="center"|<code>28</code>||<code>?? (storage for some View related command parameters)</code> | ||
+ | |- | ||
+ | |align="center"|<code>29</code>||<code>?? (storage for some View related command parameters)</code> | ||
+ | |- | ||
+ | |align="center"|<code>2A</code>||<code>?? (storage for some View related command parameters)</code> | ||
+ | |} | ||
</blockquote> | </blockquote> | ||
− | Note: The above format structure is simply the way in which Sierra's AGI interpreter stores information about | + | Note: The above format structure is simply the way in which Sierra's AGI interpreter stores information about View objects. In attempting to write an AGI interpreter, you would not have to restrict yourself to this format, just as long as you store this information in some manner that the interpreter can have access to. |
− | == <div id="ss8.4"></div><br />8.4 | + | == <div id="ss8.4"></div><br />8.4 View test commands == |
− | ''Written by [[Lance Ewing]]''<br /> | + | <div align="center">''Written by [[Lance Ewing]]''<br /> |
− | ''(Last updated: 31 August 1997).'' | + | ''(Last updated: 31 August 1997).''</div> |
| | ||
− | There are four | + | There are four Logic test commands that are to do with ViewS. These are: |
<blockquote> | <blockquote> | ||
− | < | + | <div class="CodeBlockHeader">Code:</div> |
+ | <syntaxhighlight lang="agi"> | ||
obj.in.box() | obj.in.box() | ||
posn() | posn() | ||
right.posn() | right.posn() | ||
centre.posn() | centre.posn() | ||
− | </ | + | </syntaxhighlight> |
</blockquote> | </blockquote> | ||
− | All of these commands are for testing whether a | + | All of these commands are for testing whether a View object is within a given rectangle on the screen. All of them take the same parameters and apart from a slight change in each case, they do exactly the same thing and even share about 95% of their code. The general form is the following: |
<blockquote> | <blockquote> | ||
− | <code>command( | + | <code>command(View object num, left, top, right, bottom)</code> |
</blockquote> | </blockquote> | ||
− | A | + | A View has a position stored in its View table entry that says where about on the screen the View object is at the present time. The problem with this position is saying which pixel is the position pixel for an object that takes up usually over a hundred pixels. Okay, you might say that most views are actors or props that sit on the ground and therefore the bottom row of pixels will give you a y position. This is a good argument, but now you need to say which of these pixels in the bottom row is the actual position. Sierra must have faced this problem or they wouldn't have provided four commands for achieving essentially the same thing. |
− | By default the position hot spot in a | + | By default the position hot spot in a View is the bottom left pixel. |
<blockquote> | <blockquote> | ||
− | <code><pre> | + | {| border="1px" cellspacing="0" cellpadding="0" bordercolor="#F2F2F2" bgcolor="#F9F9F9" |
+ | |<code><pre> | ||
......... | ......... | ||
......... | ......... | ||
Line 275: | Line 324: | ||
X........ | X........ | ||
</pre></code> | </pre></code> | ||
+ | |} | ||
</blockquote> | </blockquote> | ||
− | This is the location that gets stored in the | + | This is the location that gets stored in the View object table. The difference between the test commands given above is how they adjust the x position before testing it against the rectangle border lines. <code>posn</code> Leaves the x position as it is (left side). <code>right.posn</code> adds (xsize-1) to the x position giving the right side. <code>center.posn</code> adds (xsize/2) to the x position giving the center. <code>obj.in.box</code> tests both the left and right sides which essentially tests whether the whole bottom row of pixels is in the "box". |
The test is TRUE if | The test is TRUE if | ||
Line 289: | Line 339: | ||
The following examples are available in the [http://AGI.helllabs.org/AGIspecs/ distribution package]: | The following examples are available in the [http://AGI.helllabs.org/AGIspecs/ distribution package]: | ||
− | * <code>[[AGI:Examples/view/viewview.pas|viewview.pas]]</code> by [[Peter Kelly]]: unit from AGIhack that displays | + | * <code>[[AGI:Examples/view/viewview.pas|viewview.pas]]</code> by [[Peter Kelly]]: unit from AGIhack that displays Views |
| | ||
Line 295: | Line 345: | ||
[[Adventure Game Interpreter Specifications|Table of Contents]] | [[Adventure Game Interpreter Specifications|Table of Contents]] | ||
− | <span style="float: left">[[AGI Specifications: Chapter 7 - | + | <span style="float: left">[[AGI Specifications: Chapter 7 - Picture Resources|< Previous: Chapter 7 - Picture Resources]]</span><span style="float: right">[[AGI Specifications: Chapter 9 - Sound Resources|Next: Chapter 9 - Sound Resources >]]</span> |
| | ||
+ | [[Category:AGI Documentation]] | ||
[[Category:Adventure Game Interpreter Specifications]] | [[Category:Adventure Game Interpreter Specifications]] |
Latest revision as of 19:37, 2 March 2018
8.1 Introduction
View resources contain some of the graphics for the game. Unlike the picture resources which are full-screen background images, View resources are smaller sprites used in the game, such as animations and objects. They are also stored as bitmaps, whereas pictures are stored in vector format.
Each View resource consists of one or more loops. Each loop in the resource consists of one or more cels (frames). Thus several animations can be stored in one view, or a View can just be used for a single image. The maximum number of loops supported by the interpreter is 255 (0-254) and the maximum number of cels in each is 255 (0-254).
Note: Except when noted otherwise, 16 bit data is stored in little endian format (i.e. the value has the least significant byte stored first, and the most significant byte stored second). Most word values in AGI are stored like this, but not all.
8.2 View Resource Format
View header (7+ bytes)
Byte Meaning 0
Unknown (always seems to be either 1 or 2)
1
Unknown (always seems to be 1)
2
Number of loops
3-4
Position of description (more on this later) Both bytes are 0 if there is no description
5-6
Position of first loop
7-8
Position of second loop (if any)
9-10
Position of third loop (if any)
Note: Two of these loop references CAN point to the same place. This is done when you want to use mirroring (more on this later).
Loop header (3+ bytes)
Byte Meaning 0
Number of cels in this loop
1-2
Position of first cel, relative to start of loop
3-4
Position of second cel (if any), relative to start of loop
5-6
Position of third cel (if any), relative to start of loop
Cel header (3 bytes)
Byte Meaning 0
Width of cel (remember that AGI pixels are 2 normal EGA pixels
wide so a cel of width 12 is actually 24 pixels wide on screen)
1
Height of cel
2
Transparency and cel mirroring
The first four bits of this byte tell the interpreter how to handle the mirroring of this cel (explained later).
The last four bits represent the transparent color. When the cel is drawn on the screen, any pixels of this color will show through to the background.
All cels have a transparent color, so if you want an opaque cel then you must set the transparent color to one that is not used in the cel.
Cel data
The actual image data for each cel is stored using RLE (run length encoding) compression. This means that instead of having one byte for each single pixel (or 1/2 byte as you would use for 16 colors), each byte specifies how many pixels there are to be in a row and what color they are. I will refer to these groups of pixels as "chunks".
This method of compression is not very efficient if there is a lot of single pixels in the image (e.g. a View showing static on a TV screen), but in most cases it does save a fair amount of space.
Each line (not to be confused with a chunk) in the cel consists of several bytes of pixel data, then a 0 to end the line. Each byte of pixel data represents one chunk. The first four bits determine the color, and the last four bits determine the number of pixels in the chunk.
Example: AX BY CZ 00
This line will have:
- X pixels of color A (
AX
) - Y pixels of color B (
BY
) - Z pixels of color C (
CZ
) - (then that will be the end of the line) (
00
)
If the color of the last chunk on the line is the transparent color, there is no need to store this. For example, if C was the transparent color in the above example, you could just write AX BY 00
. This also saves some space.
Mirroring
Mirroring is when you have one loop where all the cels are a mirror image of the corresponding cels in another loop. Although you could do this manually by drawing one loop and then copying and pasting all the cels to another loop and flipping them horizontally, AGI views provide the ability to have this done automatically -- you can draw one loop, and have another loop which is set as a mirror of this loop. Thus, when you change one loop you change the other. This is useful if you have an animation of a character walking left and right -- you just draw the right-walking animation and have another loop a mirror of this which will have the left-walking animation. Another advantage of cel mirroring is to save space -- it doesn't make much difference these days, but back when AGI was written the games were designed to run on 256K systems which meant that memory had to be used as efficiently as possible.
Mirroring is done by having both loops share the same cel data -- you saw above that you can have two loop references pointing to the same place. The first four bits of the 3rd byte in the header of each cel tell the interpreter what is mirrored:
Byte Meaning 0
Specify whether or not this cel is mirrored
1-3
Specify the number of the loop (from 0-7) which is NOT mirrored
When the interpreter goes to display a loop, it looks at the bit 0 and sees if it is mirrored or not. If it is, then it checks the loop number -- if this is NOT the same as the current loop, then it flips the cel before displaying it.
If you have a cel that is mirrored, you need to ensure that the number of bytes the cel takes up in the resource is greater than or equal to the number of bytes that the flipped version of the cel would take up. The reason for this is that the interpreter loads the View resource into memory and works with that for displaying cels, rather than decoding it and storing it in memory as a non-compressed bitmap. I assume that it doesn't even bother "decoding" it as such -- it probably just draws the chunks of color on the screen as they are. When it has to display the flipped version of a cel, instead of storing the flipped cel somewhere else in memory, it flips the version that is there. So in memory you have the View resource that was read from the file, except that some of the cels have been changed. This is why there is mirroring information stored in each cel -- the interpreter has to know what cels have been changed. When it flips a cel, it changes the loop number in the 3rd byte of the cel header to the number of the loop it is currently displaying the cel for. So when it looks at this number the next time for a different loop, it will see that the cel is not the right way round for that loop and mirror it again.
This process seems very inefficient to me. I don't know why it doesn't just draw the chunks of color on the screen back to front. But since it does it this way we have to make sure that there is enough room for the flipped cel.
It seems that not all versions of the interpreter require this, however -- I was working with version 2.917 when I was testing this out, but I noticed that version 2.440 did not require this. I will attempt to try this with all different interpreters and provide a list of which ones use this in a future version of this document. But it is best to put these bytes in just in case, as the views will still work regardless.
Description
All the views in the game that are used as close-ups of inventory items have a description. When a player "examines" these (in some games you can select "see object" from the menu), they will see the first cel of the first loop of this View and the description of the object they are examining. This is brought up using the show.obj
command.
The description is stored in plain text, and terminated by a null character. Lines are separated by an 0x0A.
8.3 View Table
(Last updated: 31 August 1997).
Firstly we should note that there is a difference between a View and a View Object. A View is a collection of animated sequences that are stored in a VOL file. When a View is loaded into memory, it is then connected to one or more View table entries (see below) that store information on what the interpreter calls objects (don't confuse this with inventory items). An object is an animated sprite that is currently being controlled by the interpreter. With each interpretation cycle, the state of each object in the View table is updated and, if required, updated on the screen. It is therefore possible to have five or more hungry crocodiles swimming in a moat each which have there own View table entries, all of which point to the same View data.
View objects appear to have the following properties:
- x position
- y position
- current view
- current loop
- current cel
- priority
- cycle time (1/n gives the fraction of the maximum speed)
- step time (1/n gives the fraction of the maximum speed)
- x size (in pixels)
- y size (in pixels)
- step size
- direction
- number of loops
- number of cels
There are probably other properties that aren't listed here which they also possess. In an object oriented environment such as SCI, these properties are stored as instance variables (or selectors) as part of the object. Since AGI isn't object oriented, we would expect to find some sort of View table stored in memory which holds theses properties within it. In SQ2 this View table consisted of 43 byte entries. Most commands that deal with View objects will make adjustments to the data in the entry for the relevant object.
View table entry
Byte Meaning 00-01
step.time (stored twice)
02
??
03-04
x position
05-06
y position
07
current view
08-09
pointer to start of View data
0A
current loop
0B
number of loops
0C-0D
pointer to start of loop data
0E
current cel
0F
number of cels
10-11
pointer to start of cel data
12-13
pointer to start of cel data (same as above)
14-15
??
16-17
copy of x position
18-19
copy of y position
1A-1B
x size
1C-1D
y size
1E
step size
1F-20
cycle time (stored twice)
21
direction (heading) 0 = stationary, 1 = north, 2 = north/east, 3 = east, 4 = south/east, 5 = south, 6 = south/west, 7 = west, 8 = north/west
22
0 = normal.motion, 1 = wander, 2 = follow.ego, 3 = move.obj
23
0 = normal.cycle, 1 = end.of.loop, 2 = reverse.loop, 3 = reverse.cycle
24
priority
25-26
View flags (see below)
View flags:
Byte Meaning 0
??
1
0 = observe blocks, 1 = ignore blocks
2
0 = priority released, 1 = fixed priority
3
0 = observe horizon, 1 = ignore horizon
4
??
5
0 = stop cycling, 1 = cycling
6
??
7
??
8
1 = View on water
9
0 = observe objects, 1 = ignore objects
10
??
11
1 = View on land
12
??
13
0 = loop released, 1 = loop fixed
14
??
15
??
27
?? (storage for some View related command parameters)
28
?? (storage for some View related command parameters)
29
?? (storage for some View related command parameters)
2A
?? (storage for some View related command parameters)
Note: The above format structure is simply the way in which Sierra's AGI interpreter stores information about View objects. In attempting to write an AGI interpreter, you would not have to restrict yourself to this format, just as long as you store this information in some manner that the interpreter can have access to.
8.4 View test commands
(Last updated: 31 August 1997).
There are four Logic test commands that are to do with ViewS. These are:
Code:<syntaxhighlight lang="agi"> obj.in.box() posn() right.posn() centre.posn() </syntaxhighlight>
All of these commands are for testing whether a View object is within a given rectangle on the screen. All of them take the same parameters and apart from a slight change in each case, they do exactly the same thing and even share about 95% of their code. The general form is the following:
command(View object num, left, top, right, bottom)
A View has a position stored in its View table entry that says where about on the screen the View object is at the present time. The problem with this position is saying which pixel is the position pixel for an object that takes up usually over a hundred pixels. Okay, you might say that most views are actors or props that sit on the ground and therefore the bottom row of pixels will give you a y position. This is a good argument, but now you need to say which of these pixels in the bottom row is the actual position. Sierra must have faced this problem or they wouldn't have provided four commands for achieving essentially the same thing.
By default the position hot spot in a View is the bottom left pixel.
......... ......... ......... ......... X = position hot spot. ......... ......... X........
This is the location that gets stored in the View object table. The difference between the test commands given above is how they adjust the x position before testing it against the rectangle border lines. posn
Leaves the x position as it is (left side). right.posn
adds (xsize-1) to the x position giving the right side. center.posn
adds (xsize/2) to the x position giving the center. obj.in.box
tests both the left and right sides which essentially tests whether the whole bottom row of pixels is in the "box".
The test is TRUE if
(X1 >= left) && (y >= top) && (X2 <= right) && (y <= bottom).
8.5 Sample code
The following examples are available in the distribution package:
viewview.pas
by Peter Kelly: unit from AGIhack that displays Views
< Previous: Chapter 7 - Picture ResourcesNext: Chapter 9 - Sound Resources >