Phoenix 2.0 for TI-82/83/83+/85/85 by Patrick Davidson - Level Creation

This documentation describes how to create external level files for 
Phoenix 2.0 and above.  For general information, refer to 'PHOENIX.TXT'.

_______________________________________________________ Table of Contents

1. Introduction ...................................................... 18
2. Building level files .............................................. 42
3. Level file layout ................................................. 76
4. Level commands ................................................... 125
5. Movement types ................................................... 330
6. Weapons .......................................................... 424
7. Sprite data format ............................................... 444
8. Path format ...................................................... 503
9. Limitations ...................................................... 528

____________________________________________________________ Introduction

The external level format has been completely redesigned for version 2.0
of Phoenix in order to make it more flexible.  Since there ae not a whole
lot of other levels out there, this should not be too much of a problem.

The current format is much more flexible than was used before.  In this
format, various aspects of enemies (like the movement type, firing rate,
strength, image, etc.) can be chosen independently, instead of having
only a few specific types.  Furthermore, all data relating to an enemy
can be chosen differently for each enemy, so there can be multiple
patterns, bosses of the same type with different images, and so forth.
Additionally all enemies can have sprites of any size, animated or not.

The data of a level file itself would normally be an assembly source
file with the data specified in define constant statements, and by
supplied macros.  The level data itself does not contain any assembly
code however, but only data to be used by the main Phoenix program.

Although separate level files must be made for each calculator, the
differences pertain only to addresses and headers.  Therefore the main
level data needs only be written once, and the levels can then by
compiled for all calculators.

____________________________________ Building level files

Level files have a specific format required for each calculator.  In the
case of the TI-85 and TI-86, they must contain the code to run the main
Phoenix program, which will then play the level.

The headers are the same for all level files, so you only need to copy
these.  You can get them in the published archive for the Jupiter
levels.  To adapt these to be your own levels, you only need to modify
the 'level.asm' file, which contains all data describing the level.
Since the only data needed by the main program once it is started is
the level data copied to a specific address, you can modify the startup
code for the TI-85 and TI-86 versions (e.g. to display your own title
screen and credits) or change the program title.  If you want to do
this, however, you should be very careful not to change anything that
affects the main code or the level may not work (in particular, for the
TI-86 version, don't insert or remove anything before level.asm is
included).

To build the levels, just run 'build levname' from a DOS command prompt.
The build.bat file supplied will automatically run all of the necessary 
building programs to build it for all calculators.  If you don't want to
build for a particular calculator, you can remove the code specific to
that calculator.  I have not included any provisions for using any fancy
graphical IDE programs to build levels, but if you like using one of
these, you probably can if you can get it to compile the same files
build.bat does (except the TI-85 version; you may have to try a little 
harder for it).

It is expected that the standard build tools for each calculator are
installed (the same ones needed to build Phoenix).  Also, it is expected
that the current directory is a subdirectory of the one where the Phoenix
source code (especially phoenixz.i) is located.

_______________________________________________________ Level file layout

(Note that all of this may make more sense if you look at some sample
level data, such as that of jupiter.asm.)

The actual data in level.asm follows the following simple pattern:

First, an 8-byte identifier of the level.  The first 8 bytes will be 
used for this nomatter what they are.  This should be some nice-looking
text, since it will be shown as the level name on the TI-82, TI-83, and
TI-83 Plus.  It is also used to check whether you have run the correct
level to restore a saved game, so it is important for the TI-85 and
TI-86 also.  An example of this would be

        .db     "LEVEL!!!"

No zero termination is needed.

(The following applies not only to external levels, but also to the
internal level data of Phoenix as well).

Next comes the list of levels.  This is simple a list of word pointers,
such as the following

        .dw     data_for_level_1
        .dw     data_for_level_2
        ...

and so on.  No relocation is needed since the level files is always
moved to a certain address (which is different on each calculator; this
is why it needs to be compiled separately for each).  There is no
specific type of end marker here.  Rather, at the end there should be a
final level which has only a command to end the game.

Then comes the level data itself.  The data for a level consists first
of the number of enemies that will be placed in that level (or more
specifically, the number of enemies you must destroy to complete the
level) followed by level commands.  Note of course that each level
must begin at the levels mentioned in the list.  Example:

data_for_level_1:
        .db      8
        .db      COMMAND
        .db      ANOTHER_COMMAND
        ...
        .db      L_END

which assumes these commands create 8 enemies.  

__________________________________________________________ Level commands

(You can see plenty of examples of full levels of commands in both
the 'jupiter.asm' level files as well as the built-in level data in
'levels.asm' which is in the same format.)

Level commands do the real work of setting up a level.  These have
symbolic names given in 'phoenixz.i' so you can just put these names
in db statements to create the commands.  Each command has its own
required format.  Since one byte of output is like any other, Phoenix
does not have any way of telling if you put too few arguments or too
many, so if you do that it will still try to read the number of
arguments that should be there, with bad results.  Thus you should be
sure to get it right.

At the beginning of processing, all enemy attributes will be set to
default values, and there will be no enemies actually present in the
level.  To place enemies (which will be needed, unless you want the level
to be very easy) you use insertion commands which place enemies with the 
current characteristics at specified coordinates.  To change the 
specifications, use other commands to set the various attributes of an
enemy.  These changes will then apply to all enemies subsequently 
inserted in the level, until you change it again.  Each level should end
with an L_END command, which marks the end of commands for that level.

And now the complete list of commands and there usages.  Under each
heading is the format the command should be used in (note that
things other than the L_ that is the first byte are variables which
you should put in the appropriate value for).

____________ L_END

 .db L_END

This command marks the end of the commands specifying a level.  This is
required at the end of every level (with the exceptions mentioned below).

____________ L_SHOP

 .db L_SHOP

This command enters the shop.  To include a shop in the game, put in
a level with 0 enemies and this as the only command.  L_END is not
needed at the end of such a level.

____________ L_GAMEEND

 .db L_GAMEEND

This command ends the game and shows the final score.  At the end of all
of your levels, you should put one such level (if not, whatever data is
past the end of your list of level pointers will be interpreted as
more level pointers, which is not good).

____________ L_SET_POWER

 .db L_SET_POWER,POWER

This command sets the power of subsequent enemies to the number given
for POWER.  This number should be between 1 and 255.  It actually gives
the amount of damage an enemy can take before being destroyed.  Note
that basic player weapon does 2 points of damage per hit.  The default
power is 3 (which is used for enemies in the first built-in level).

____________ L_SET_MOVETYPE

 .db L_SET_MOVETYPE,MOVETYPE

This command sets the movement type of subsequent enemies to the value
given as MOVETYPE, which must be one of the types specified in the
"Movement types" section.  The default type is EM_STANDARD, which just
moves in the basic rectangular pattern.

____________ L_SET_MOVEDATA

 .db L_SET_MOVEDATA,DATA1,DATA2

or

 .db L_SET_MOVEDATA
 .dw DATA

This command sets the first two bytes of the movement data.  Their 
meaning depends on the movement type chosen.  Both forms seem the same
to the game since they both put two bytes of data.  If there are two
separate numbers, you would normally use the first, but if there is only
one value, such as a pointer to a pointer, you normally use the second.
The default value is meaningful for enemies in a regular pattern, and
is not changed for most such enemies in the build-in levels.

____________ L_SET_MOVEDATA2

 .db L_SET_MOVEDATA2,DATA

This sets the third byte of the movement data, not part of the data
above.  Its meaning also depends on the movement type.

____________ L_IMAGE_STILL

 .db L_IMAGE_STILL
 .dw IMAGE_POINTER

This sets the image of the enemy to whatever is at the label given by
IMAGE_POINTER.  This should be used for non-animated enemies only.
Refer to the image format section for information on the format of the
sprite data.

____________ L_IMAGE_ANIM

 .db L_IMAGE_ANIM
 .dw IMAGE_POINTER

This sets the image of the enemy to whatever is at the label given by
IMAGE_POINTER, which in this case is a list of sprites to be animated
through.  This should be used for animated enemies only.  Refer to the
image format section for information on the format of the sprite data.

____________ L_SET_FIRETYPE

 .db L_SET_FIRETYPE,FT

This sets the firing type to whatever is given by FT.  Here the value
for FT indicates how the enemy decides when to fire, not which weapon
is used.  Firing types are:

FT_RANDOM: Each frame, randomly decides whether to fire (default)
FT_NONE: Never fires
FT_PERIODIC: Fires at a given interval

____________ L_SET_FIRERATE

 .db L_SET_FIRERATE,N

Sets the firing rate to N.  In FT_RANDOM mode, this gives a probability
of N/256 of firing each frame.  The default value (used for most of the
basic enemies in the built-in-levels) is 2, meaning the enemies will
fire 1/128 of the time (averaging about once every 4 seconds).  In 
FT_PERIODIC mode, this is the number of frames between shots.  Thus a 
value of 30 would be about once a second (depending on game speed).

___________ L_SET_WEAPON

 .db L_SET_WEAPON,W

Sets the weapon used to W.  See the weapons section for a list of all
values.  The default is W_NORMAL, which just drops the small blocks
down as you can see in the first levels of the built-in levels.

___________ L_SET_FIREPOWER

 .db L_SET_FIREPOWER,N

Sets the amount of damage done by enemy bullets to N.  This value should
be between 1 and 17 (at 17, a single hit will always destroy to player).
The default value is 1, which is used for most enemies in the built-in
levels.

___________ L_INSTALL_ONE

 .db L_INSTALL_ONE,X,Y

Places one enemy in play, using the attributes set at the moment, at 
coordinates (X,Y).  Note that the upper-left corner of the screen is
at (0,32) on the TI-85 and TI-86 and (16, 32) on the TI-82, TI-83, and
TI-83 Plus.  Also note that some enemy types will move down varying
amounts to enter, so enemies should generally be placed above the screen
from where they will swoop in.  Finally, for some specific types of
movement there are other issues regarding coordinates; refer to the
movement type data for details.

___________ L_INSTALL_ROW

 .db L_INSTALL_ROW,X,Y,N,SPACE

Places a row of enemies in play using the currently selected attributes.
Places a total of N enemies starting at (X,Y), with the X coordinate
increased by SPACE for each subsequent enemies.  Same notes about
coordinates as above apply here.

____________ L_GOTO

 .db L_GOTO
 .dw LABEL

This jumps to LABEL and continues processing from there.  LABEL must
point to another valid sequence of level commands, and begin right at
the first by of a command, as in the following

LABEL:
 .db IMAGE_STILL
 .dw image
 ...

LABEL must not be, say, at the ".dw image" line since then the first byte
of the image specification will be taken as a command, with bad results.

This command is very useful to make level files smaller.  If some levels
are similar, they can simply set their unique attributes, or create their
unique enemies, at the beginning of each,  and then have common commands
for setting the attributes that stay the same, as well as install the
enemies.  Only one of the levels needs to have the common commands; the
other can then jump to the common part.  This capability allowed both
the main Phoenix program (and the Jupiter levels) to be made smaller with
the new level data format, even though it requires more information.

____________________________________________________ Enemy movement types

Here is the list of enemy types.  Immediately after each type, an example
of the code needed for setting up the specific movement variables for the
enemy types is given.  Again, complete example levels can be found in the
source code of Phoenix or the released external levels. 

Please note that coordinates referred to here are for the upper-right
corner of the enemies.  Keep in mind the screen boundaires mentioned uner
L_INSTALL_ONE above.

___________ EM_STANDARD


 .db L_SET_MOVEDATA,-DOWNDISTANCE*2,0
 .db L_SET_MOVETYPE,EM_STANDARD

The standard type is the movement of enemies which swoop down and then move
in a rectangular pattern, such as the first enemies in the game.  The number
given by DOWNDISTANCE tells how far down the enemy will move before entering
the pattern.  You must put -2 times this value in the data byte.  The
default enemy movement data gives this a value of 30 (so -60 is the data
stored) which moves the enemies down into their typical positions from just
above the screen, where they are placed.

___________ EM_NONE

 .db L_SET_MOVETYPE,EM_NONE

Quite simply, an enemy that doesn't move at all.  This is used mainly for
explosions which you don't need to create by hand.  However if you want an
enemy to appear at the start of a level and never move, you can use this.
However, if you want the enemy to enter along a path and then stop, you
should use the EM_PATTERNSTART type with a pattern that will move it into
position, then enter a loop with 0 movement.

___________ EM_BOSS

 .db L_SET_MOVEDATA,0,DESCENT
 .db L_SET_MOVETYPE,EM_BOSS

This creates an enemy which moves down by DESCENT pixels, then moves back
and forth across the whole screen.  The initial position should be above
the top of the screen.  The left edge of the pattern is 83 pixels left of
the initial coordinate, and the right edge 7 pixels right of it, so you
should put the boss near the right edge of the screen.

___________ EM_BOUNCE

 .db L_SET_MOVEDATA,XV,YV
 .db L_SET_MOVETYPE,EM_BOUNCE

These enemies bounce around randomly inside the rectangle bounded by
(13, 31) and (107, 60).  The XV and YV are the velocities on the X and
Y axes and can be compted by adding the direction (0 for left or up, 2
for rght or down) and speed (0 for slow, 1 for fast) to get the value.
Note that the enemies must have an initial location and veloctiy that
will bring them into the rectangle.

__________ EM_PATTERNSTART

 .db L_SET_MOVEDATA
 .dw patterndata
 .db L_SET_MOVETYPE,EM_PATTERNSTART

This creates an enemy which follows a specified pattern.  For these enemies,
the X and Y coordinates have special meaning.  The X coordinate is taken
as the length (in frames) of the delay before entry, and the Y coordinate
becomes the intial X coordinate (with initial Y always set to 9).  This
allows you to create a sequence of enemies in the same path which will
be spaced in time using an L_INSTALL_ROW command.  See the section below
on path format for information on how to create the path data.

__________ EM_RAMPAGEINIT

 .db L_SET_MOVETYPE,EM_RAMPAGEINIT

A type of enemy moving randomly between (16,41) and (105,49).  The style
of movement is somewhat different from that of the EM_BOUNCE enemies.
Used for the O enemies in the main Phoenix game.

__________ EM_RAMPAGEWAIT

An enemy that moves like EM_STANDARD until the number of enemies 
remaining drops to 8, after which it changes to the rampaging type
described above.  Data is the same as for EM_STANDARD.

__________ EM_SWOOPWAIT

 .db L_SET_MOVETYPE, EM_SWOOPWAIT

An enemy that will randomly swoop down, across, and up the screen in one
of several paths.

_________________________________________________________________ Weapons

W_NORMAL - Drops small block straight down, as used by the first enemies
in the main Phoenix game.

W_DOUBLE - Fires two shots 14 pixels apart, partially aimed towads the
player.  Used by the earlier bosses in the main Phoenix game.

W_SEMIAIM - Fires a single shot that is partially aimed.

W_BIG - As with double, but more precise aim.

W_HUGE - As with W_BIG, but larger bullet as well.

W_ARROW - Drops a single arrow straight down.

W_SINGLEBIG - Single version of W_BIG.

W_SINGLEHUGE - Single version of W_HUGE.

____________________________________________________________ Image format

For sprites 8 pixels wide or narrower, the format is simple.  As an example,
here is a smiley face:

sprite_smiley:
 .db 8,9
 .db %00111100
 .db %01000010
 .db %10000001
 .db %10100101
 .db %10000001
 .db %10100101
 .db %10011001
 .db %01000010
 .db %00111100

The sprite begins with the width, then contains the height.  Following is
the sprite data, one byte per line (if the sprite is less than 8 pixels
wide, the rightmost part of it should be left as all zeros).  The data
has 0s for transparent pixels, 1 for drawn pixels.

If the sprite is more than 8 pixels wide, it is somewhat more complicated.
To create such a sprite, put in the full width and height at the start,
but in the first section of the data, put in the data only for the eight
leftmost pixels of the sprite.  Then follow this immediately with the
rest of the sprite, as if another sprite.  As an example, a 20-pixel-wide
sprite would look something like:

 .db 20,2
 .db %11111111   ; leftmost 8 pixels of sprite
 .db %11111111
 .db 12,2
 .db %11111111   ; next 8 pixels
 .db %11111111
 .db 4,2
 .db %11110000   ; last 4 pixels
 .db %11110000

There are plenty of examples in images.asm.

For an animated image, the sequence should be constructed as follows:

sequence:
 .db FIRST_IMAGE_TIME
 .dw FIRST_IMAGE
 .db SECOND_IMAGE_TIME
 .dw SECOND_IMAGE
 ...
 .db 0
 .dw sequence

The sequence is essentially a list of images and the duration each one is
shown.  The time is in game frames.  Each time is given as a byte, followed
by a word which is a pointer to the sprite (in the format given above).
At the end of the sequence, put a time of 0 (to indicate a restart) followed
by the position to restart the sequence at (normally the start of the
sequence).

_____________________________________________________________ Path format

Here is a general example of what a path would look like:

path_data:
 .db 32,0,12
path_loop:
 .db 32,8,0
 .db 32,-8,0
 .db 0
 .dw path_loop

This path will move down 24 pixels, then go left and right over a 16
pixel interval.  For each 3 bytes, the first byte indicates the number
of frames it will be used.  Then are the X velocity and Y velocity,
given as 16ths of a pixel per frame.  Note that the total distance
traveled (velocity * time) should be a multiple of 16 and thus an even
number of pixels.  When 0 is the time, it is treated as a goto, so
in this case the loop restarts (it can also be used to switch between
patterns with shared ends).  Note that enemies will stay in a loop
forever, so be sure it takes them back where they started or the enemies
will drift out of pattern (that is, sum of all displacements should be 0).
If you want an enemy to stop, just create a loop in which the only
movement command has 0 velocity.

_____________________________________________________________ Limitations

Please pay attention to all of the following:

1) You must be sure to get the level data correct, since the program does
not validate it.  To be sure your commands are valid, you should test your
level file extensively.  The commands are used simply as indexes into
jump tables, so having one that is invalid will have very bad
consequences.

2) Use only defined commands.  Maybe putting in random numbers may happen
not to crash now, but it may not stay that way in the next version.

3) Don't try to reference images and paths in the main program, since
their addresses can change in the next version.

4) There is a size limit to levels.  If the file comes out longer than
this, it will probably crash.

TI-82, TI-83, and TI-83+: 3.5K
TI-85: 1K
TI-86: 7.5K

5) Don't try to use movement types not mentioned above.  There are others
defined in phoenixz.i, but these are intermediate values which may change.
Also do not try to set movement data to something that is invalid by the
specifications above, as values other than those given may be used to
indicate additional features in later versions.