home *** CD-ROM | disk | FTP | other *** search
- /**********************************************************************
- * spr.c
- *
- * The sprite support system main functions
- **********************************************************************
- This file is part of
-
- STK -- The sprite toolkit -- version 1.0
-
- Copyright (C) Jari Karjala 1990
-
- The sprite toolkit (STK) is a FreeWare toolkit for creating high
- resolution sprite graphics with PCompatible hardware. This toolkit
- is provided as is without any warranty or such thing. See the file
- COPYING for further information.
-
- **********************************************************************/
-
- #include <stdio.h>
- #include <stdlib.h>
-
- #include <alloc.h> /* farmalloc(), farfree() */
- #include <dos.h> /* delay() */
- #include <graphics.h> /* setactivepage(), setvisualpage(), mode defines */
- #include <mem.h> /* memcpy(), movedata() */
- #include <time.h> /* clock(), clock_t */
-
- #include "sprP.h"
- #include "spr.h"
- #include "spr_low.h"
- #include "spr_misc.h"
-
- /** display lists for each display page **/
- SPRITE spr_sprites[2] = { NULL, NULL };
-
- /** the sprites to be destroyed after next_pass **/
- static SPRITE destroy_list = { NULL };
-
- /** the current drawing page **/
- WORD spr_drawingpage = 0;
-
- /**********************************************************************
- * The delay after setvisualpage() before removing old objects.
- * Time is given in milliseconds. Hercules cards do not need this,
- * but many EGA/VGA cards do need some milliseconds to switch pages.
- **********************************************************************/
- int spr_pass_delay = 10;
-
- /**********************************************************************
- * Called from low level funtions if not sprite system initialized,
- * or if initialization fails.
- **********************************************************************/
- void static fatal_error()
- {
- closegraph();
- puts("FATAL: Sprite system initialization error.");
- exit(255);
- }
-
-
- /** hardware dependent putter **/
- void static (*spr_low_putter)
- (BYTE far *shape, BYTE far *dest, BYTE far *save,
- WORD w, WORD h) = fatal_error;
-
- /** hardware dependent eraser **/
- void static (*spr_low_eraser)(BYTE far *dest, BYTE far *save,
- WORD w, WORD h) = fatal_error;
-
- /** hardware dependent address calculator (TCC gives warning here) **/
- BYTE static far *(*spr_low_addr)(WORD x, WORD y, BYTE page) = fatal_error;
-
-
- /**********************************************************************
- * Initialize the sprite system to the given display hardware.
- * Supported graphicsdrivers: EGAMONO and HERCMONO
- * The visual page is set to 0.
- * NOTE: This function must be called before any other sprite funtions
- * and the graphics mode must have been set before this call.
- *
- * graphicsdriver The BGI identifier for the graphics driver used.
- **********************************************************************/
- void spr_initialize(int graphicsdriver)
- {
- switch (graphicsdriver) {
- case HERCMONO:
- spr_low_putter = spr_low_herc_put;
- spr_low_eraser = spr_low_herc_erase;
- spr_low_addr = spr_low_herc_addr;
- spr_pass_delay = 1; /** hercules cards do not need this **/
- break;
-
- case EGAMONO:
- spr_low_putter = spr_low_ega_mono_put;
- spr_low_eraser = spr_low_ega_mono_erase;
- spr_low_addr = spr_low_ega_mono_addr;
- break;
-
- default:
- fatal_error(); /** (TCC gives warning here) **/
- }
-
- setvisualpage(spr_drawingpage);
- }
-
-
-
- /**********************************************************************
- * Create a sprite.
- *
- * w,h Width (must be < 8*255) and height (<256) of sprite in pixels
- * pic The picture bitmap for the sprite
- * mask The mask bitmap for the sprite
- * res The number of steps wanted per 8 bit interval in horizontal
- * direction (1,2,4,8). For example, the value 8 gives one
- * pixel resolution in X-direction.
- * ID The user supplied ID for the sprite (not obligatory)
- *
- * Return: the newly created sprite or NULL if parameter error
- * or out-of-memory
- **********************************************************************/
- SPRITE spr_create(WORD w, WORD h,
- BITMAP shape, BITMAP mask,
- BYTE res, WORD ID)
- {
- SPRITE spr;
- WORD size;
-
- if (w<8 || h<1 || w >= 8*255 || h > 255 || shape==NULL || mask==NULL)
- return NULL;
-
- if (res!=1 && res!=2 && res!=4 && res!=8)
- return NULL;
-
- spr = (SPRITE)malloc(sizeof(struct _sprite));
- if (spr==NULL)
- return NULL;
-
- if (w&7)
- spr->w = (w>>3) + 1; /* not exact byte boundary, round up */
- else
- spr->w = w>>3;
-
- if (res>1) /** make space for right shifts **/
- spr->w++;
-
- spr->wp = w;
- spr->hp = h;
- spr->res = res;
- spr->flags = FLAG_NONE;
- spr->id = ID;
- spr->x = spr->y = (WORD)-1; /* Place it out of screen initially */
-
- size = spr->w * spr->hp; /* size of one bitmap */
- spr->size = size * 2; /* shape + mask size */
-
- /** malloc space for usage count, bitmaps + save buffer for old images **/
- spr->data = (FARMAP)farmalloc(spr->size*(long)res + size*2L);
- if (spr->data==NULL) {
- free(spr);
- return NULL;
- }
-
- /** create shifted/combined shape/mask maps **/
- spr_misc_create_data(spr, shape, mask);
-
- /** set save buffer address for both display pages **/
- spr->saved[0] = spr->data + (spr->size * res);
- spr->saved[1] = spr->data + (spr->size * res + size);
-
- spr->next[0] = spr->next[1] = NULL;
- spr->addr[0] = spr->addr[1] = NULL;
-
- spr->share = NULL;
- spr->share_index = 0;
-
- return spr;
- }
-
- /**********************************************************************
- * Create a shared version of the given sprite. This allows
- * n spr_copies which all share the shape data thus saving
- * quite much memory.
- *
- * spr The sprite to share
- * n The maximum number of shared copies
- *
- * Return: New SPRITE or NULL if error (out of memory, spr==NULL, etc)
- **********************************************************************/
- SPRITE spr_share(SPRITE spr, BYTE n)
- {
- WORD size, i;
-
- if (spr==NULL)
- return NULL;
-
- size = spr->w * spr->hp; /* size of one bitmap */
- n++;
-
- /** Reallocate space for shapes, save space for copies and share index **/
- spr->data = (FARMAP)farrealloc(spr->data,
- spr->size*spr->res + n*size*2L + n);
-
- /** Reset old save buffer address for both display pages **/
- /** (Realloc might move the block) **/
- spr->saved[0] = spr->data + (spr->size * spr->res);
- spr->saved[1] = spr->data + (spr->size * spr->res + size);
-
- /** Set share index pointer and reset its contents **/
- spr->share = spr->data + (spr->size*spr->res + n*size*2L);
- spr->share_index = 0;
- spr->max_share = n-1;
- spr->share[spr->share_index] = 1;
- for (i=1; i<spr->max_share; i++)
- spr->share[i] = 0;
-
- return spr;
- }
-
-
- /**********************************************************************
- * Return a copy of the given sprite.
- *
- * spr The sprite to copy
- * id The ID for the new sprite
- *
- * Return: New SPRITE of NULL if error (out of memory, spr==NULL, etc)
- **********************************************************************/
- SPRITE spr_copy(SPRITE spr_old, WORD id)
- {
- SPRITE spr;
- WORD size, i;
-
- if (spr_old==NULL)
- return NULL;
-
- spr = (SPRITE)malloc(sizeof(struct _sprite));
- if (spr==NULL)
- return NULL;
- memcpy(spr, spr_old, sizeof(struct _sprite));
-
- spr->id = id;
- size = spr->w * spr->hp; /* size of one bitmap */
-
- if (spr->share!=NULL) { /** we have a shared sprite **/
- for (i=0; i<spr->max_share; i++)
- if (spr->share[i]==0)
- break;
- if (i==spr->max_share) { /** no free slots **/
- free(spr);
- return NULL;
- }
- spr->saved[0] = spr->data + (spr->size * spr->res + (size*2)*i);
- spr->saved[1] = spr->data + (spr->size * spr->res + (size*2)*i+size);
- spr->share[i] = 1;
- spr->share_index = i;
- }
- else { /** not shared, must make a full copy **/
- /** space for usage count, bitmaps + save buffer for old images **/
- spr->data = (FARMAP)farmalloc(spr->size*spr->res + size*2L);
- if (spr->data==NULL) {
- free(spr);
- return NULL;
- }
-
- movedata(FP_SEG(spr_old->data), FP_OFF(spr_old->data),
- FP_SEG(spr->data), FP_OFF(spr->data),
- spr->size*spr->res);
-
- /** set save buffer address for both display pages **/
- spr->saved[0] = spr->data + (spr->size * spr->res);
- spr->saved[1] = spr->data + (spr->size * spr->res + size);
- }
-
- return spr;
- }
-
- /**********************************************************************
- * Put the sprite into the given position into the display list.
- * NOTE: the call has no effect on screen until a call to
- * spr_next_pass() is made.
- *
- * spr The sprite to put
- * x The X coordinate
- * y The Y coordinate
- **********************************************************************/
- void spr_put(SPRITE spr, WORD x, WORD y)
- {
- /** add sprite to the display list **/
- spr->next[spr_drawingpage] = spr_sprites[spr_drawingpage];
- spr_sprites[spr_drawingpage] = spr;
-
- /** save coordinates for collision checking & SHAPE_OFS macro **/
- spr->x = x;
- spr->y = y;
-
- spr->addr[spr_drawingpage] = spr_low_addr(x,y,spr_drawingpage);
- }
-
- /**********************************************************************
- * Remove the sprite from the display list
- * NOTE: the call has no effect on screen until a call to
- * spr_next_pass() is made.
- *
- * spr The sprite to hide
- **********************************************************************/
- void spr_hide(SPRITE spr)
- {
- spr_misc_delete(spr, spr_drawingpage);
- }
-
- /**********************************************************************
- * Delete the given sprite and release associated memory buffers.
- * Also delete it from the display lists. (Actually the sprite is
- * only hidden and moved into the deletion list, the memory is freed
- * after the next_pass().)
- *
- * spr The sprite to delete
- **********************************************************************/
- void spr_delete(SPRITE spr)
- {
- spr_hide(spr);
- spr->next[spr_drawingpage] = destroy_list;
- destroy_list = spr;
- }
-
- /**********************************************************************
- * Display all sprites in the chain in reversed order.
- * NOTE: this is recursive, so it needs 4-8 bytes (depending on memory
- * model) of stack space for each sprite.
- **********************************************************************/
- void static show_all_reversed(SPRITE s)
- {
- if (s==NULL)
- return;
-
- /** First descent recursively to the end of list **/
- if (s->next[spr_drawingpage]!=NULL)
- show_all_reversed(s->next[spr_drawingpage]);
-
- spr_low_putter(s->data+SHAPE_OFS(s),
- s->addr[spr_drawingpage], s->saved[spr_drawingpage],
- s->w, s->hp);
- }
-
- /**********************************************************************
- * This function actually shows the sprites.
- * The following functions are performed (the order is important):
- * - put all sprite images into the hidden page (in reverse order).
- * - set hidden page to visual page (delay a moment for EGA cards).
- * - delete the old sprite images from the (now hidden) page.
- * - now it is safe to actually free the spr_deleted sprites.
- *
- * Return: The current visual page.
- **********************************************************************/
- WORD spr_next_pass(void)
- {
- SPRITE s;
- int i;
-
- /** Display all sprites at once in reverse order **/
- show_all_reversed(spr_sprites[spr_drawingpage]);
-
- setvisualpage(spr_drawingpage); /** show 'em **/
-
- delay(spr_pass_delay); /** EGA/VGA cards are SLOW! **/
-
- /** Then delete old ones **/
- spr_drawingpage ^= 1;
- s = spr_sprites[spr_drawingpage];
- while (s!=NULL) {
- spr_low_eraser(s->addr[spr_drawingpage], s->saved[spr_drawingpage],
- s->w,s->hp);
- s = s->next[spr_drawingpage];
- }
- spr_sprites[spr_drawingpage] = NULL;
-
- /** Destroy the sprites which have been spr_deleted **/
- s = destroy_list;
- while (s!=NULL) {
- if (s->share!=NULL) { /** if shared must check for usage **/
- s->share[s->share_index] = 0;
- for (i=0; i<s->max_share; i++)
- if (s->share[i])
- break;
- if (i==s->max_share)
- farfree(s->data);
- }
- else
- farfree(s->data);
-
- free(s);
- s = s->next[spr_drawingpage^1];
- }
- destroy_list = NULL;
-
- return spr_drawingpage^1;
- }
-
- /**********************************************************************
- * This function tries to regulate the frame speed by delaying if
- * we are doing more frames per second than we should. The target
- * speed is 1 frame per one clock tick (about 18 frames per second).
- * (This is about the highest reasonable rate for 15-20 32x24 pixel
- * sprites on a Hercules screen with a 10 MHz 286.)
- **********************************************************************/
- void spr_regulate_speed(void)
- {
- static int d=0;
- static int j=0;
- static clock_t old_clock=0;
- int i;
-
- delay(d);
- if ((j++ & 7)==7) {
- if ((i=(int)(clock()-old_clock)) < 8)
- d += 8-i; /** running too fast (more than frame per tick) **/
- else
- if (d>0) /** running too slow (less than frame per tick) **/
- d--;
- old_clock = clock();
- }
- }
-
- /**********************************************************************
- * Return the user supplied identifier of the sprite
- **********************************************************************/
- WORD spr_get_id(SPRITE spr)
- {
- return spr->id;
- }
-
- /**********************************************************************
- * Return the X coordinate of the sprite
- **********************************************************************/
- WORD spr_get_x(SPRITE spr)
- {
- return spr->x;
- }
-
- /**********************************************************************
- * Return the Y coordinate of the sprite
- **********************************************************************/
- WORD spr_get_y(SPRITE spr)
- {
- return spr->y;
- }
-
- /**********************************************************************
- * Return the width of the sprite
- **********************************************************************/
- WORD spr_get_width(SPRITE spr)
- {
- return spr->w*8;
- }
-
- /**********************************************************************
- * Return the height of the sprite
- **********************************************************************/
- WORD spr_get_height(SPRITE spr)
- {
- return spr->hp;
- }
-