-
Masked
drawing of graphics at pixel precision.
-
Isometric
display of a map of tiles (no scroll)
-
Size
of map is 10x10 tiles of 24 (4 bytes) x 12 pixels
-
Stacked
drawing of four height levels pixels each.
-
Granularity:
each tile is 6x6 "points". Each layer is 8 pixels above.
-
More
levels (up to
-
Software
double buffer drawing (buffer of 8 bytes x 40 lines = 320 bytes)
-
Configurable
clipping region for updating parts of display without flickering.
-
Manage
coordinates in 3D and automatically translates it to 2D display.
-
Up
to 63 simultaneous different pictures for blocks. ID 0 is reserved for empty.
-
Tiles
can be of different heights, no limited to layer height (there are some
internal limitations, though).
-
On-the-fly
inversion of graphics when drawing, to save memory. This is indicated by flagging
a bit in the ID.
-
An
additional bit serves the purpose of flagging certain block as Special, but
retains the same picture.
-
"Free"
objects might be positioned anywhere in the 3D world, not tied to a tile
boundary.
-
Several
(7) predefined sizes (in 3D sense) for free objects.
-
Movement
functions for free objects.
-
Similar
Invert-on-the-fly and SPECIAL flags for free objects.
-
Collision
detection is independent of movement (another function shall be called).
-
Upon
calling collision detection, two arrays are filled with data about all the
tiles (background blocks) you collided with and all the free objects you
collided with separately.
-
Collision
with ID=0 is not detected BUT collision with ID=0 and Special bit set is
(un-drawn or invisible walls).
-
Comlpete C interface, easy to use.
(Note: This
is just an outline of the functions and some general advices, so you get an
idea about how NOISE works, not a programmer's manual.)
void
set_doublebuff(char onoff);
/* Sets double buffer mode on and off */
/* The next functions operate
in double buffer mode AND in screen mode: */
char
* pixel_address(unsigned char x_pos,unsigned
char y_pos,unsigned char *bit);
/* Gets the pointer to the scan which contains pixel
at position (x_pos,y_pos). Modifiles */
/* bit so it gets a '1' on the pixel position and
void
put_sprite(unsigned char x_pos,
unsigned char y_pos, sprite_t
* sprite, char invert);
/* Puts the masked sprite "sprite" in screen
position (x_pos,y_pos).
Perform inversion */
/* and rotations as necessary */
void
put_sprite2(unsigned char x_pos, unsigned char y_pos, sprite_t * sprite, char
invert);
/* Puts the masked sprite "sprite" in screen
position (x_pos,y_pos).
Perform inversion */
/* but no rotations (it's faster if we know there is
no need for rotating) */
void
clear_clip_rgn(); /* Clears the screen (or double
buffer) area defined by global structure clip_rgn */
void
init_room();
/* Initializes room. Shall be called whenever room
changes */
void
draw_room();
/* Renders the room */
void
clear_buff();
/* Clears the double buffer */
void
paint_buff();
/* Dumps the double buffer in the area defined by clip_rgn strucure */
char
get_tile(char i, char j,
char k);
/* Gets the current tile at 3D position (i,j,k) */
void
set_tile(char i, char j,
char k, char tilecode);
/* Sets the current tile at 3D positino
(i,j,k) as tilecode. Repainting shoud be
done */
void
recalc_clip(char who);
/* Calculates the params of the clipping region clip_rgn
around a moving free object */
int
collision_test(char who, char dir_mov);
/* Performs collisino test if character who wants to move in directino dir_mov */
void
move_sprite(char who, char dir_mov);
/* Moves a free object in the given direction */
void
ij2xy(unsigned char i, unsigned char j, unsigned char
*x, unsigned char * y);
/* Converts (i,j)
tile coordinates in screen coordinates and returns them in x and y params */
char
dodiv6(char op); /* Performs quick division by 6. Quite useful! */
Some
programming notes:
a/ The
first time the room is rendered, it should be done not in double buffer mode.
hires();
set_doublebuff(0);
draw_room();
set_doublebuff(1);
b/ The
sequence:
clear_buff();
draw_room();
paint_buff();
shall be
called each time the screen needs to be updated.
c/
Initializing blocks and map
Fixed (tied
to tiles or background blocks) are identified by an ID which is the entry one array:
Sprite
data:
bkg_graphs[id].lines=48;
bkg_graphs[id].scans=3;
bkg_graphs[id].image=wall1+143; /*wall1 is a
label where graphic data starts. Add (scans*lines)-1 */
bkg_graphs[id].mask=wall1m+143; /*wall1m is
the same but for the mask. Add (scans*lines)-1 */
Set a tile
at map position (i,j,k)
set_tile(i,j,k,id);
Use the
macros for setting bits in the ID:
SET_INVERT(x)
SET_SPECIAL(x)
UNSET_INVERT(x)
UNSET_SPECIAL(x)
example:
set_tile(i,j,k,SET_INVERT(id));
Use INVERT
to draw mirrored images (e.g. a door) so we save some memory (e.g. only one
door picture is necessary).
Use SPECIAL
to flag special objects (e.g. a door that may open or a block that disappears
when you collide with it).
d/
Initializing free objects:
The graphic
data is initialized as:
char_pics[i].lines=34;
char_pics[i].scans=3;
char_pics[i].image=Koenig+101; /*
same as before */
char_pics[i].mask=KoenigSH+101;
The
position data is initialized as (BEWARE the array index should be the same):
characters[i].fine_coord_i=11; /* These are
fine coordinates, not tile positions */
characters[i].fine_coord_j=11;
characters[i].fine_coord_k=0;
characters[i].type=SIZE_CHAR;
The
"type" field holds the INVERT and SPECIAL flags.
Add the
character to the characters in room array if necessary:
chars_in_room[index]=i;
and increment
the number of characters in room:
num_chars+;
e/
Example of collision checking: collision_test returns 0 if no collision.
if (!collision_test(hero,dir))
move_sprite(hero,dir);
For checking
objects we collided with:
if (num_bkg_collisions)
for (i=0;i<num_bkg_collisions;i++){
printf("
}
and
identically for free objects (sprites), but using num_obj_collisions and obj_collision_list
f/
move_sprite does everything for you, except updating the room, but if you want to
update your char pic without movement, you should
call:
recalc_clip(who);
where
"who" is the id of the character (array index in characters and in char_pics), so the clip region is updated before drawing.
- Code is
6.5 K plus 400 bytes for the room map.
- Ready for
being used with WHITE (World Handling and Interaction with The
Environment layer).
- Needs
tweaks to make it quicker... free objects are hard to draw.
- Needs
tweaks to save space without making it too much slower.
- Needs relocation
of data and some critical routines to ensure no page boundary is crossed,
saving cycles.
- No major
revisions will be done unless some major bug is found while developing WHITE.
-
General,
easy-to-use layer above NOISE.
-
Provides
basic animation and handling of sprites.
-
Automatic
movement for selected sprites.
-
Easy-to-use
functions for dynamically changing tiles.
-
Manages
several different characters the player may use in game.
-
Selected
objects and characters might be moved around the world map with many different
strategies and with two different speeds. (TO BE COMPLETED).
-
On collision turn CLOCKWISE
-
On collision turn ANTICLOCKWISE
-
On collision turn twice
-
On collision randomly act as any of
the above
-
Do one of the above randomly when
stepping
-
More to come...
-
Improved collision handling:
-
Filters
blocks and returns only those marked as SPECIAL with all necessary data.
-
Reports
collisions through callbacks.
-
Reporting
can be stopped by returning 0 from the callback.
-
Functions
that move away an object from the world to an internal "warehouse" or
back to a given positions (if empty) to provide get/drop functionality.
-
Loads
a room from a map with a given format (TO BE COMPLETED).
-
Automatically
manages changing of rooms when player moves or selects another character.
-
Provides
functions for showing/clearing room without affecting other screen areas and in
different ink colours.
-
Interact function that performs some sort of collision
detection so the application knows every object/character reachable by the
given character in a given direction.
-
Complete
C interface, very easy to use.
void
white_init();
// Inits the WHITE layer
void
white_clear_room_map(); // Clears the room data (sets
all tiles to 0)
void
white_clear_screen(); // Clears the current screen
(only the room) and sets paper and ink to 0
void
white_show_screen(char attrmap);
// Restore attribs so screen is visible. Sets the
attribute value to attrmap
void
white_load_room(char roomID);
// Loads the room number roomID
void
white_change_block(char i,
char j, char k, char newID); // Changes a background
block for the newID and repaints it.
void
white_add_new_char(char ID); // Adds a character to
the room... moving_chars[ID] should have been filled
first.
void
white_remove_char(char ID); // Removes a character
from the room.
void
white_step(char ID); // Makes a given char to move 1
step
void
white_turn(char ID, char dir); // Turns character:
CLOCKWISE (0) turns clockwise, ANTICLOCKWISE (1) turns anti-clockwise
void
white_loop(); //
main loop of white, for automatic actions.
void
white_setPC(char ID); // sets the current
player-controlled character and loads the room it is in.
void
white_interact(char ID); // Makes the character ID
(normally the player) explore what's nearby to interact with.
char
white_to_warehouse(char i,
char j, char k); // Moves the object at position (i,j,k)
to the warehouse.
//Returns ID if success, 0 if nothing there.
char
white_from_warehouse(char i,
char j, char k, char ID); // Moves object ID from the warehouse to current room
at position (i,j,k).
//Returns 0 if success
and an ID if there was an object already at that position.
Some
programming notes:
a/
Initializing
Normally
everything will be initialized in the world map data, but you might want to add
the character the player controls or any special obejct
at any time...
Set any
character that might move this way (note the macros that define automatic
movement type and speed).
aux=0;
SET_AUTOMOV(aux); // Will
move automatically
SET_SPEEDNORMAL(aux); // At normal
speed
SET_AMTYPE(aux,AM_TYPE4); //
Movement algorithm type 4
moving_chars[num_characters].room=0;
moving_chars[num_characters].frame=0;
moving_chars[num_characters].direction=EAST;
moving_chars[num_characters].automov=aux;
num_characters++;
Add any
object to the world this way:
objects[num_objects].room=0;
objects[num_objects].id=6;
objects[num_objects].i=5;
objects[num_objects].j=5;
objects[num_objects].k=0;
num_objects++;
Initialize
WHITE and load first room:
hires();
white_init();
white_load_room(INITIAL_ROOM);
Change the
current player-controlled character this way:
white_setPC(new_character);
The room new_character is in, loads automatically.
b/
Controlling a character
Moving a
character is easy. Suppose the index is "hero":
white_turn(hero,CLOCKWISE);
// Turns it clockwise
white_turn(hero,ANTICLOCKWISE);
// Idem anticlockwise
white_step(hero); // steps in the current
direction, performing collision detection (see later).
There is an special functon for interacting
with the environment (e.g. opening a door, reading a screen, taking an
object...)
white_interact(hero);
Collision
detection (see later) is launched as if the character was to move (step), but
it does not. Some intelligent programming and you could get a basic vocabulary
for interacting with objects or characters and using objects onto other objects
or characters.
c/
Collisions/interactions
Before
performing a step or when white_interact is called, collision detection
routines start. For each background object flagged as SPECIAL a callback is called:
char
white_collided_with_bkg(char who, char against, char i, char j, char k)
Returns the
ID of the colliding char (there's automatic movement also, remember? you might
want to know that other char collided with that bomb!) and the ID and position
of the block you collided with.
Upon treating the collision, return 1 if you want to keep the collision
detection routine performing its work and report other collisions. Return 0 to stop it.
The same stand
for other characters, but every collision is reported (no need to set the
SPECIAL flag):
char
white_collided_with_char(char who, char against)
Just both
IDs are returned. No need for further data, but it can be searched for in NOISE
internal variables.
d/
Getting/dropping objects
Only
objects flagged as SPECIAL AND in the "objects" array can be taken.
That is the preferred behaviour 99% of the time.
For getting
an object:
obj=white_to_warehouse(i,j,k);
if (obj)
{
// Take object,
manage inventory
}
else
{
// Can't take any object at that position. No object there or
it was not flagged as SPECIAL,
// or not in
the array
}
Combine
this in your collision callbacks and you get a nice
way of taking objects from the room.
For
dropping an object at a given tile:
k=white_from_warehouse(i,j,k,my_object);
if(k){
// No
success... object id k is already in that position
}
else
{
// Success! manage character's inventory (remove my_object)
}
e/
Automatic actions & others
For letting
WHITE perform automatic actions the function white_loop() should be called at every game loop
(some may prefer calling it twice or just one every too game loops!).
f/ Other
useful functions:
Change one
block for another... animate something, open a door, make something disappear...
white_change_block(i,j,k,new_object);
Everything
will be normal again if you exit the room and enter again.
Make a
character appear:
white_add_new_char(ID); // Adds a character to the
room... moving_chars[ID] should have been filled
first.
Make him
disappear (died?):
void
white_remove_char(ID); // Removes a character from
the room.
Version
alpha 0.01:
- Code is
90% assembly with a size of 1.5K.
- A format
for the world map should be defined.
- A room
editor should be programmed (argh!).
- Automatic
movement implements 5 different strategies which are very flexible:
- No plans
to add objects that can be freely placed between tiles
- No plans
to add gravity and/or jumping functions. Same stands for going up/down stairs.