rpi4-osdev/part12-wgt/wspr.c

687 lines
16 KiB
C

#include "wgt.h"
/* The following defines should be altered to suit your program needs */
#define MAX_SPRITES 100
#define MAX_ANIMATION 40
#define MAX_MOVE 15
typedef struct
{
unsigned char num; /* Sprite number shown */
short x, y; /* Coordinates on screen */
unsigned char on; /* On/Off, for visibility */
int ox, oy, ox2, oy2;
signed char animon; /* Animation on/off */
short animation_images[MAX_ANIMATION]; /* Animation numbers */
unsigned char animation_speeds[MAX_ANIMATION]; /* Animation speeds */
signed char current_animation; /* Current animation counter */
unsigned char animation_count; /* Delay count for animation */
signed char movex_on; /* X movement on/off */
short movex_distance[MAX_MOVE]; /* X distance per frame */
short movex_number[MAX_MOVE]; /* Number of times to move */
unsigned char movex_speed[MAX_MOVE]; /* Delay between each movement */
signed char current_movex; /* Movement index */
short current_movex_number; /* Number of times moved */
unsigned char movex_count; /* Delay count for X movement */
signed char movey_on; /* Y movement on/off */
short movey_distance[MAX_MOVE]; /* Y distance per frame */
short movey_number[MAX_MOVE]; /* Number of times to move */
unsigned char movey_speed[MAX_MOVE]; /* Delay between each movement */
signed char current_movey; /* Movement index */
short current_movey_number; /* Number of times moved */
unsigned char movey_count; /* Delay count for Y movement */
} sprite_object;
sprite_object s[MAX_SPRITES];
block backgroundscreen = NULL; /* Holds the constant background */
block spritescreen = NULL; /* Work buffer */
short maxsprite;
int tempx1, tempy1, tempx2, tempy2;
block *sprite_images;
void spriteon (short number, short x, short y, short image);
void spriteoff (short number);
void draw_sprites (int movement_multiplier);
void initialize_sprites (block *sprite_blocks);
void deinitialize_sprites (void);
void erase_sprites (void);
void animate (short spnum, char *str);
void animon (short spnum);
void animoff (short spnum);
void movex (short spnum, char *str);
void movey (short spnum, char *str);
void movexon (short spnum);
void movexoff (short spnum);
void moveyon (short spnum);
void moveyoff (short spnum);
short overlap (short s1, short s2);
void expand_dirty_rectangle (int x, int y, int x2, int y2);
void copy_sprites (void);
void spriteon (short number, short x, short y, short image)
/* Turns a sprite on at (x,y), with sprites[image] */
{
tempx1 = x;
tempy1 = y;
tempx2 = x + wgetblockwidth (sprite_images[image]);
tempy2 = y + wgetblockheight (sprite_images[image]);
/* Find the update box */
if (tempx1 < tx) /* X1 */
tempx1 = tx;
else
if (tempx1 >= WGT_SYS.xres)
tempx1 = WGT_SYS.xres - 1;
if (tempy1 < ty) /* Y1 */
tempy1 = ty;
else
if (tempy1 >= WGT_SYS.yres)
tempy1 = WGT_SYS.yres - 1;
if (tempx2 < tx) /* X2 */
tempx2 = tx;
else
if (tempx2 >= WGT_SYS.xres)
tempx2 = WGT_SYS.xres - 1;
if (tempy2 < ty) /* Y2 */
tempy2 = ty;
else
if (tempy2 >= WGT_SYS.yres)
tempy2 = WGT_SYS.yres - 1;
s[number].x = x; /* Set up the coords */
s[number].y = y;
s[number].num = image;
s[number].on = 1;
s[number].ox = tempx1; /* Set the dirty rectangle location */
s[number].oy = tempy1;
s[number].ox2 = tempx2;
s[number].oy2 = tempy2;
}
void spriteoff (short number)
/* Turns a sprite off */
{
if (s[number].on == 1)
s[number].on = 2; /* Signals sprite is to be taken off */
}
void erase_sprites (void)
/* Erases all sprites from background screen by copying the dirty
rectangles */
{
short i;
block curscreen;
short osx, osy;
sprite_object *spriteptr;
int x, y, x2, y2;
curscreen = abuf;
osx = WGT_SYS.xres;
osy = WGT_SYS.yres;
wsetscreen (spritescreen); /* Go to background screen */
spriteptr = s;
for (i = 0; i <= maxsprite; i++) /* Loop through all sprites */
{
if (spriteptr->on == 1) /* If sprite is on */
{
x = spriteptr->ox; /* Get the old dirty rectangle coordinates */
y = spriteptr->oy;
x2 = spriteptr->ox2;
y2 = spriteptr->oy2;
if (x < tx) /* Clip them, but don't change the original */
x = tx; /* values, because we need them later */
else if (x > bx)
x = bx;
if (y < ty)
y = ty;
else if (y > by)
y = by;
wcopyscreen (x, y, x2, y2, backgroundscreen, x, y, spritescreen);
}
spriteptr++; /* Next sprite */
}
abuf = curscreen;
WGT_SYS.xres = osx;
WGT_SYS.yres = osy;
}
void expand_dirty_rectangle (int x, int y, int x2, int y2)
/* Find boundaries of the old and new sprite rectangle */
{
if (x < tempx1) /* Compare with */
tempx1 = x;
if (x2 > tempx2)
tempx2 = x2;
if (y < tempy1)
tempy1 = y;
if (y2 > tempy2)
tempy2 = y2;
if (tempx1 < tx) /* Compare with clipping boundaries */
tempx1 = tx;
if (tempx2 > bx)
tempx2 = bx;
if (tempy1 < ty)
tempy1 = ty;
if (tempy2 > by)
tempy2 = by;
}
void copy_sprites (void)
{
int i;
sprite_object *spriteptr;
int x, y, x2, y2;
spriteptr = s;
for (i = 0; i <= maxsprite; i++)
{
if (spriteptr->on > 0) /* If sprite is on */
{
if (spriteptr->on == 2)
spriteptr->on = 0;
/* Store these values because they are used more than once */
x = spriteptr->x;
y = spriteptr->y;
x2 = x + wgetblockwidth (sprite_images[spriteptr->num]) - 1;
y2 = y + wgetblockheight (sprite_images[spriteptr->num]) - 1;
/* Set the dirty rectangle to the current position of the sprite */
tempx1 = x;
tempy1 = y;
tempx2 = x2;
tempy2 = y2;
expand_dirty_rectangle (spriteptr->ox, spriteptr->oy,
spriteptr->ox2, spriteptr->oy2);
wcopyscreen (tempx1, tempy1, tempx2, tempy2, spritescreen, tempx1, tempy1,
NULL);
spriteptr->ox = x;
spriteptr->oy = y;
spriteptr->ox2 = x2;
spriteptr->oy2 = y2;
}
spriteptr++;
}
}
void draw_sprites (int movement_multiplier)
/* Draw the sprites on the sprite screen */
{
short i;
block curscreen;
short osx, osy;
sprite_object *spriteptr;
int move;
curscreen = abuf;
osx = WGT_SYS.xres;
osy = WGT_SYS.yres;
wsetscreen (spritescreen); /* Go to sprite screen */
for (move = 0; move < movement_multiplier; move++)
{
spriteptr = s;
for (i = 0; i <= maxsprite; i++) /* Loop through all sprites */
{
if (spriteptr->on == 1) /* If sprite is on */
{
if (spriteptr->movex_on == 1) /* X movement on */
{
if (spriteptr->movex_count != 0)/* Delay Count not reached 0? */
spriteptr->movex_count--; /* Decrease count */
else /* Get the next move */
{
spriteptr->current_movex_number++; /* Increase # of times moved */
if (spriteptr->current_movex_number ==
spriteptr->movex_number[spriteptr->current_movex])
/* Moved the right number of times yet? */
{
spriteptr->current_movex++; /* Increase ptr in move array */
if (spriteptr->movex_number[spriteptr->current_movex] == - 1)
/* Repeat move */
spriteptr->current_movex = 0;
else if (spriteptr->movex_number[spriteptr->current_movex]
== - 2) /* End move */
{
spriteptr->current_movex--;
spriteptr->movex_on = 0; /* Turn off movement */
}
spriteptr->current_movex_number = 0;
/* Reset number of times moved */
}
spriteptr->movex_count = spriteptr->movex_speed
[spriteptr->current_movex]; /* Get delay */
spriteptr->x += spriteptr->movex_distance
[spriteptr->current_movex]; /* Update X coord */
}
}
if (spriteptr->movey_on == 1) /* Y movement on */
{
if (spriteptr->movey_count != 0)/* Delay Count not reached 0? */
spriteptr->movey_count--; /* Decrease count */
else /* Get the next move */
{
spriteptr->current_movey_number++; /* Increase # of times moved */
if (spriteptr->current_movey_number ==
spriteptr->movey_number[spriteptr->current_movey])
/* Moved the right number of times yet? */
{
spriteptr->current_movey++; /* Increase ptr in move array */
if (spriteptr->movey_number[spriteptr->current_movey] == - 1)
/* Repeat move */
spriteptr->current_movey = 0;
else if (spriteptr->movey_number[spriteptr->current_movey]
== - 2) /* End move */
{
spriteptr->current_movey--;
spriteptr->movey_on = 0; /* Turn off movement */
}
spriteptr->current_movey_number = 0;
/* Reset number of times moved */
}
spriteptr->movey_count = spriteptr->movey_speed
[spriteptr->current_movey]; /* Get delay */
spriteptr->y += spriteptr->movey_distance
[spriteptr->current_movey]; /* Update Y coord */
}
}
if (spriteptr->animon == 1) /* update animation */
{
if (spriteptr->animation_count != 0) /* Delay count at 0? */
spriteptr->animation_count--; /* No, so decrease by one. */
else /* Otherwise animate the sprite */
{
spriteptr->current_animation++; /* Increase animation ptr */
if (spriteptr->animation_images[spriteptr->current_animation]
== - 1) /* Repeat animation */
spriteptr->current_animation = 0;
if (spriteptr->animation_images[spriteptr->current_animation]
== - 2) /* End animation */
{
spriteptr->current_animation--;
spriteptr->animon = 0; /* Turn off animation */
}
spriteptr->num = spriteptr->animation_images
[spriteptr->current_animation]; /* Change sprite number */
spriteptr->animation_count = spriteptr->animation_speeds
[spriteptr->current_animation]; /* Reset delay count */
}
}
}
spriteptr++;
}
}
for (i = 0; i <= maxsprite; i++) /* Loop through all sprites */
if ((s[i].on == 1) && (sprite_images[ s[i].num ] != NULL))
wputblock (s[i].x, s[i].y, sprite_images[ s[i].num ], 1);
abuf = curscreen;
WGT_SYS.xres = osx;
WGT_SYS.yres = osy;
copy_sprites ();
}
void initialize_sprites (block *sprite_blocks)
/* Set up everything for the sprite engine */
{
short i;
maxsprite = MAX_SPRITES - 1;
sprite_images = sprite_blocks;
for (i = 0; i < MAX_SPRITES; i++) /* Turn off the sprites */
s[i].on = 0;
if (spritescreen == NULL)
spritescreen = wnewblock (0, 0, WGT_SYS.xres - 1, WGT_SYS.yres - 1);
/* Make a virtual screen for the work buffer */
if (backgroundscreen == NULL)
backgroundscreen = wnewblock (0, 0, WGT_SYS.xres - 1, WGT_SYS.yres - 1);
/* Make a virtual screen for the background screen */
}
void deinitialize_sprites (void)
{
short i;
for (i = 0; i < MAX_SPRITES; i++)
s[i].on = 0;
wfreeblock (spritescreen);
spritescreen = NULL;
wfreeblock (backgroundscreen);
backgroundscreen = NULL;
}
void animate (short spnum, char *str)
/* Parse animation string and put the data into an array */
{
short c, temp1;
short x, in, len, minus;
short change;
s[spnum].current_animation = 0; /* Reset current animation ptr */
s[spnum].animation_count = s[spnum].animation_speeds[
s[spnum].current_animation]; /* Reset delay count */
x = 0;
in = 0;
len = strlen (str);
/* Parse animation string */
do {
do {/* Find the first bracket */
c = str[x];
x++;
} while (c != '(');
change = 0;
do {
temp1 = 0;
minus = 0;
do {
c = str[x];
if ((c != ',') && (c != 'R') && (c != '-') && (c != ')'))
temp1 = (temp1 * 10) + (c - 48);
if (c == '-')
minus = 1;
x++;
} while ((x != len) && (c != ',') && (c != 'R') && (c != ')'));
if (minus == 1)
temp1 = - temp1;
if (change == 0)
s[spnum].animation_images[in] = temp1;
else
s[spnum].animation_speeds[in] = temp1;
change++;
} while (c != ')'); /* Until the last bracket */
in++;
if (in >= MAX_ANIMATION)
in = MAX_ANIMATION-1;
s[spnum].animation_images[in] = - 2;
c = str[x];
if (c == 'R')
{
x = len;
s[spnum].animation_images[in] = - 1;
}
} while (x != len);
}
void animon (short spnum)
/* Turns animation for a sprite on */
{
s[spnum].animon = 1;
}
void animoff (short spnum)
/* Turns animation for a sprite off */
{
s[spnum].animon = 0;
}
/* X movements */
void movexon (short spnum)
/* Turns a sprite's x movement on */
{
s[spnum].movex_on = 1;
}
void movexoff (short spnum)
/* Turns a sprite's x movement off */
{
s[spnum].movex_on = 0;
}
void movex (short spnum, char *str)
/* Parses a movement string and places data into movement arrays */
{
short c, temp1;
short x, in, len, minus;
short change;
s[spnum].current_movex = 0; /* First x movement */
s[spnum].movex_count = s[spnum].movex_speed[s[spnum].current_movex];
s[spnum].current_movex_number = 0; /* First time it moved */
x = 0;
in = 0;
len = strlen (str);
do {
do {
c = str[x];
x++;
} while (c != '(');
change = 0;
do {
temp1 = 0;
minus = 0;
do {
c = str[x];
if ((c != ',') && (c != 'R') && (c != '-') && (c != ')'))
temp1 = (temp1*10) + (c - 48);
if (c == '-')
minus = 1;
x++;
} while ((x != len) && (c != ',') && (c != 'R') && (c != ')'));
if (minus == 1)
temp1 = - temp1;
if (change == 0)
s[spnum].movex_distance[in] = temp1;
else if (change == 1)
s[spnum].movex_number[in] = temp1;
else
s[spnum].movex_speed[in] = temp1;
change++;
} while (c != ')');
in++;
if (in == MAX_MOVE)
x = len;
s[spnum].movex_number[in] = - 2;
c = str[x];
if (c == 'R')
{
x = len;
s[spnum].movex_number[in] = - 1;
}
} while (x != len);
}
/* Y movements */
void moveyon (short spnum)
/* Turns a sprite's y movement on */
{
s[spnum].movey_on = 1;
}
void moveyoff (short spnum)
/* Turns a sprite's y movement off */
{
s[spnum].movey_on = 0;
}
void movey (short spnum, char *str)
/* Parses a movement string and places data into movement arrays */
{
short c, temp1;
short x, in, len, minus;
short change;
s[spnum].current_movey = 0; /* First x movement */
s[spnum].movey_count = s[spnum].movey_speed[s[spnum].current_movey];
s[spnum].current_movey_number = 0; /* First time it moved */
x = 0;
in = 0;
len = strlen (str);
do {
do {
c = str[x];
x++;
} while (c != '(');
change = 0;
do {
temp1 = 0;
minus = 0;
do {
c = str[x];
if ((c != ',') && (c != 'R') && (c != '-') && (c != ')'))
temp1 = (temp1*10) + (c - 48);
if (c == '-')
minus = 1;
x++;
} while ((x != len) && (c != ',') && (c != 'R') && (c != ')'));
if (minus == 1)
temp1 = - temp1;
if (change == 0)
s[spnum].movey_distance[in] = temp1;
else if (change == 1)
s[spnum].movey_number[in] = temp1;
else
s[spnum].movey_speed[in] = temp1;
change++;
} while (c != ')');
in++;
if (in == MAX_MOVE)
x = len;
s[spnum].movey_number[in] = - 2;
c = str[x];
if (c == 'R')
{
x = len;
s[spnum].movey_number[in] = - 1;
}
} while (x != len);
}
short overlap (short s1, short s2)
/* Tests if two sprites overlap
Not pixel precise, just checks rectangles */
{
short n1, n2;
short width1, height1;
short width2, height2;
if ((s[s2].on == 1) && (s[s1].on == 1)) // Make sure both are on
{
n1 = s[s1].num; // For easier reading
n2 = s[s2].num;
width1 = wgetblockwidth (sprite_images[n1]);
width2 = wgetblockwidth (sprite_images[n2]);
height1 = wgetblockheight (sprite_images[n1]);
height2 = wgetblockheight (sprite_images[n2]);
if (( s[s2].x >= s[s1].x - width2 ) &&
( s[s2].x <= s[s1].x + width1 ) &&
( s[s2].y >= s[s1].y - height2 ) &&
( s[s2].y <= s[s1].y + height1 ))
return 1; /* Collision! */
}
return 0; /* No collision */
}