NOISE (Novel Oric ISometric Engine)

Main Features:

-          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 7 in theory) can be added by changing a define constant

-          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.

 

Current version’s C interface:

(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 0 in the rest */

 

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("Col: Obj %d @ %d,%d,%d\n", bkg_collision_list[i].id, bkg_collision_list[i].i, bkg_collision_list[i].j, bkg_collision_list[i].k);

 }

 

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.

 

 

 

Version alpha 0.1

 

- 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.

 

 

WHITE (World Handling and Interaction with The Environment)

 

Main Features:

-          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.

 

Current C interface:

 

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.

 

Some pics: