mirror of
https://github.com/isometimes/rpi4-osdev
synced 2024-11-24 19:20:40 +00:00
687 lines
16 KiB
C
687 lines
16 KiB
C
#include "../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 */
|
|
}
|
|
|