ratcatcher/rc.c

2786 lines
60 KiB
C

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <math.h>
#include <SDL.h>
#include <SDL_keysym.h>
#include <SDL_rotozoom.h>
#include <SDL_ttf.h>
#include <SDL_framerate.h>
#include <SDL_mixer.h>
#include "shared.h"
#include "rc.h"
FPSmanager manager;
SDL_Surface *temps;
SDL_Surface *screen;
TTF_Font *font[MAXLETTERHEIGHT];
Mix_Music *curmusic = NULL; // pointer to currently playing music
char tempm[BUFLEN];
tiletype_t fakeblock;
/* the order in which fruit will appear */
int fruittypes[] = {
P_CHEESE,
P_ICECREAM,
P_CHIPS,
P_BURGER,
-1
};
/* the order in which powerups will appear */
int poweruptypes[] = {
P_SPEED,
P_NUMNETS,
P_BIGNET,
P_NUMNETS,
-1
};
int curfruittype = 0;
int curpoweruptype = 0;
int gtime = 0;
int fpsticks = 0;
int fpsstart = 0;
int curworld = 1;
int curlevelnum;
level_t *curlevel;
int levelcompletetime = -1;
sprite_t *sprite = NULL; /* main sprite list */
sprite_t *player;
sprite_t *lastsprite;
SDL_Color red = {255, 0, 0, 0};
SDL_Color black = {0, 0, 0, 0};
SDL_Color white = {255, 255, 255, 0};
SDL_Color green = {0, 255, 0, 0};
SDL_Color yellow = {255, 255, 0, 0};
int vidargs = 0;
int timer = 0;
int toggletimer = 0;
int main (int argc, char **argv) {
Uint8 *keys;
sprite_t *s,*nextsprite;
char filename[BUFLEN];
int i;
int *animtile;
curlevelnum = 1;
musicplaying = B_FALSE;
/* handle arguments */
if (argc >= 2) {
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-fs")) {
printf("Fullscreen mode enabled.\n");
vidargs |= SDL_FULLSCREEN;
} else if (!strcmp(argv[i], "-l")) {
if (++i >= argc) {
printf("Missing level number.\n");
usage();
exit(1);
}
curlevelnum = atoi(argv[i]);
printf("Skipping to level %d.\n",curlevelnum);
} else {
usage();
exit(1);
}
}
}
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE)==-1) {
printf("SDL_Init: %s\n", SDL_GetError());
exit(1);
}
atexit(cleanup);
#ifdef OPENGL
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
screen = SDL_SetVideoMode(640,480,16,SDL_OPENGLBLIT|vidargs);
#else
screen = SDL_SetVideoMode(640,480,16,SDL_SWSURFACE|SDL_DOUBLEBUF|vidargs);
#endif
if (loadimagesets()) {
return 1;
}
fakeblock.id = T_LAND;
strcpy(fakeblock.name,"Fake");
for (i = 0; i < TILEW; i++) {
fakeblock.lowness[i] = 0;
}
fakeblock.solid = S_SOLID;
fakeblock.img[0] = IMG_Load("land.bmp");
fakeblock.numframes = 1;
fakeblock.next = NULL;
fakeblock.prev = NULL;
/* load fonts */
TTF_Init();
sprintf(filename, "verdana.ttf");
for (i = 1; i < MAXLETTERHEIGHT; i++) {
font[i] = TTF_OpenFont(filename,i);
if (!font[i]) {
printf("Error opening font: %s\n", TTF_GetError());
return 1;
}
}
/* calculate fps delay */
SDL_initFramerate(&manager);
SDL_setFramerate(&manager, WANTFPS);
/* init sound */
if (initsound()) {
printf("sound error.\n");
return 1;
}
playmusic(normalmusic);
if (loadlevel(curworld,curlevelnum)) {
return 1;
}
//levelcomplete = LV_FINAL;
curlevelnum-- ; // since nexlevel() will increment it
nextlevel();
//drawlevel();
/* TODO: place player */
flip();
timer = 0;
player->invuln = INVULNTIME;
player->score = 0;
while (1) {
removeall();
/* check for death & update movement status*/
for (s = sprite ; s ; s = nextsprite) {
s->moved = B_FALSE;
nextsprite = s->next;
if (s->dead == D_FINAL) {
if (s == player) {
/* TODO: lose a life */
/* if we have lives left, go back to start position */
setdefaults(s);
s->x = (curlevel->p1x * TILEW) + (TILEW/2);
s->y = (curlevel->p1y * TILEH) + (TILEH/2);
s->invuln = INVULNTIME;
} else {
killsprite(s);
// check for level completion
countmonsters();
}
}
}
/* check for end of level */
if (levelcomplete == LV_CLEAR) {
addtext(318,242,32,"Level Complete!",&black,LEVELDELAY);
addtext(320,240,32,"Level Complete!",&yellow,LEVELDELAY);
levelcomplete = LV_WAIT;
playfx(FX_WINLEVEL);
} else if (levelcomplete == LV_WAIT) {
int mcount = 0;
sprite_t *s2;
/* when all monsters have become fruits */
for (s2 = sprite->next ; s2 ; s2 = s2->next) {
if (ismonster(s2->id)) {
if (s2->id != P_CLOUD) {
mcount++;
break;
}
}
}
if (mcount == 0) {
levelcompletetime = gtime;
levelcomplete = LV_FINAL;
}
}
/* check for keys */
SDL_PumpEvents();
keys = SDL_GetKeyState(NULL);
if (keys[SDLK_q]) {
gtime = curlevel->hurryuptime -1;
}
if (keys[SDLK_n]) {
if (toggletimer == 0) {
nextlevel();
toggletimer = 50;
}
}
// dump sprites
if (keys[SDLK_d]) {
if (toggletimer == 0) {
dumpsprites();
toggletimer = 50;
}
}
if (keys[SDLK_RETURN]) {
if (toggletimer == 0) {
SDL_WM_ToggleFullScreen(screen);
toggletimer = 50;
}
}
if (keys[SDLK_ESCAPE]) {
return 1;
}
if ((!player->dead) && (!player->teleporting)) {
if (keys[SDLK_RIGHT]) {
if (canmove(player)) {
movex(player, getspeed(player));
}
if (canturn(player)) {
player->dir = D_RIGHT;
}
}
if (keys[SDLK_LEFT]) {
if (canmove(player)) {
movex(player, -getspeed(player));
}
if (canturn(player)) {
player->dir = D_LEFT;
}
}
if (keys[SDLK_UP]) {
if (isonladder(player) || isladderabove(player)) {
player->y -= getspeed(player);
player->jumping = 0;
player->falling = 0;
player->climbing = B_TRUE;
}
}
if (keys[SDLK_DOWN]) {
if (isonladder(player)) {
player->y += getspeed(player);
player->jumping = 0;
player->falling = 0;
player->climbing = B_TRUE;
}
}
if (keys[SDLK_x]) {
if (isinwater(player)) {
player->jumping = B_FALSE;
player->falling = B_FALSE;
if (!isroofabove(player)) {
player->y -= (getspeed(player)*3);
}
} else {
if (!player->jumping) {
if (!player->falling) {
if (isonground(player) || isonladder(player)) {
/* dropping through a bridge */
if (keys[SDLK_DOWN]) {
if (isonbridge(player) && !player->falling) {
/* drop down */
player->dropping = B_TRUE;
player->dropx = player->x / TILEW;
player->dropy = player->y / TILEH;
}
} else {
int whichway;
player->climbing = B_FALSE;
if (keys[SDLK_RIGHT]) {
whichway = 1;
} else if (keys[SDLK_LEFT]) {
whichway = -1;
} else {
whichway = 0;
}
playfx(FX_JUMP);
jump(player, whichway);
}
}
}
}
}
}
if (keys[SDLK_z]) {
if ((!player->netting) && (!player->slamming)) {
if (keys[SDLK_DOWN]) {
/* slam */
if ((!player->slamming) && (isonground(player))) {
playfx(FX_SLAM);
player->slamming = B_TRUE;
player->slamangle = 0;
player->netxstart = player->x - (player->img->w/2)*player->dir;
player->netystart = player->y;
}
} else {
if (player->netcaught < player->netmax) {
/* shoot net */
playfx(FX_SHOOT);
player->netting = 1;
if (player->netbig) {
player->netspeed = NETSPEED+3;
} else {
player->netspeed = NETSPEED;
}
player->netlen = 0;
player->netdir = player->dir;
/* handle boxing glove */
if (player->powerup == PW_BOXING) {
sprite_t *s;
int found;
// use existing glove if it is there
found = B_FALSE;
for (s = sprite; s ; s = s->next) {
if (s->id == P_GLOVE) {
s->x = player->x;
s->y = player->y - (imageset[P_PLAYER].img[F_SHOOT]->h / 2) + 5;
found = B_TRUE;
}
}
if (!found) {
addsprite(P_GLOVE, player->x,
player->y - (imageset[P_PLAYER].img[F_SHOOT]->h / 2) + 5,
"glove");
}
}
}
}
}
}
}
// for each animated tile on the level...
for (animtile = curlevel->animtiles; animtile && *animtile != -1; animtile++) {
int offset,numframes;
tiletype_t *tt;
offset = *animtile;
tt = gettile(curlevel->map[offset]);
numframes = tt->numframes;
// is it time to change frames?
if ((timer % tt->animspeed) == 0) {
// change its frame
curlevel->tileframe[offset]++;
if (curlevel->tileframe[offset] >= numframes) {
curlevel->tileframe[offset] = 0;
}
// redraw it
drawtile(temps, offset%LEVELW,offset/LEVELW);
}
}
// move sprites
for (s = sprite; s ; s = s->next) {
movesprite(s);
}
movetext();
if (levelcomplete == LV_INIT) {
// only player
dogravity(player);
dotileeffects(player);
} else {
/* gravity */
for (s = sprite ; s ; s = s->next) {
dogravity(s);
}
/* tile effects */
for (s = sprite ; s ; s = s->next) {
dotileeffects(s);
}
}
/* check collisions */
checkcollide(player);
drawscore();
drawnetting(player);
/* draw player */
for (s = sprite ; s ; s = s->next) {
drawsprite(s);
}
/* draw text */
drawtext();
flip();
if (++timer == 100) timer = 0;
if (toggletimer > 0) toggletimer--;
tick();
}
return 0;
}
void tick(void) {
sprite_t *s;
SDL_framerateDelay(&manager);
fpsticks = SDL_GetTicks();
if (fpsstart == 0) {
fpsstart = fpsticks;
} else {
/* once per second */
if (fpsticks - fpsstart >= 1000) {
gtime++;
/* check for hurryup*/
if (gtime == curlevel->hurryuptime) {
if (!levelcomplete) {
for (s = sprite; s; s = s->next) {
if ((s != player) && (!isfruit(s->id))) {
s->angry = B_TRUE;
}
}
addtext(320,240,TEXTSIZE_HURRY, "Hurry up!", &yellow,HURRYDELAY);
stopmusic();
Mix_PlayChannel(CH_HURRYUP, sfx[FX_HURRYUP], 0);
}
} else if (gtime == curlevel->hurryuptime + 10) {
if (!levelcomplete) {
addsprite(P_CLOUD, 320,240,"cloud");
addtext(320,240,TEXTSIZE_HURRY, "Too slow!", &red,HURRYDELAY);
playfx(FX_TOOSLOW);
}
}
/* check for random power up */
if ((curlevel->powerupx != -1) && (curlevel->powerupy != -1)) {
if (!curlevel->gotpowerup) {
if (gtime == curlevel->poweruptime) {
if (!levelcomplete) {
/* add a random powerup at the right position */
/* IMPORTANT: the name 'random_up' is important as it tells addsprite() to
give it a higher doomcount */
puffin(randompowerup(),
curlevel->powerupx*TILEW + (TILEW/2),
curlevel->powerupy*TILEH + TILEH - 1,
"random_up",0);
curlevel->gotpowerup = B_TRUE;
}
}
}
}
/* 5 seconds after level completion */
if (levelcomplete == LV_FINAL) {
if (gtime - levelcompletetime >= 5) {
if (!player->dead) {
levelcomplete = LV_NEXTLEV;
nextlevel();
}
}
}
fpsstart = fpsticks;
}
}
}
void nextlevel(void) {
char msg[SMALLBUFLEN];
// remove the player
removesprite(player);
/* in case we skipped the level due to a powerup etc */
levelcomplete = LV_NEXTLEV;
/* go to next level */
curlevelnum++;
if (!musicplaying || (curmusic == fastmusic)) {
playmusic(normalmusic);
}
// don't want the player flashing while scrolling
player->invuln = 0;
// these two handle the scroll effect to the next level
loadlevel(curworld,curlevelnum);
drawlevel();
// now the player gets invincibility
player->invuln = INVULNTIME;
/* reset game stats */
levelcomplete = LV_INIT;
levelcompletetime = -1;
/* reset level stats */
level->gotpowerup = B_FALSE;
sprintf(msg, " Level %02d-%02d ",curworld, curlevelnum);
addtext(318,242,32,msg,&black,LEVELDELAY);
addtext(320,240,32,msg,&green,LEVELDELAY);
/* reset player stats */
player->netting = B_FALSE;
player->slamming = B_FALSE;
player->jumping = B_FALSE;
player->powerup = B_FALSE;
}
void jump(sprite_t *s, int dir) {
if (s->jumping) return;
if (s->jumptimer) return;
if (isonground(s)) {
if (ismonster(s->id)) {
s->jumpdir = dir;
if (s->jumpdir != 0) {
s->dir = s->jumpdir;
}
s->jumptimer = 60;
} else {
s->jumpdir = dir;
if (s->jumpdir != 0) {
s->dir = s->jumpdir;
}
s->jumping = 1;
s->jumpspeed = 5;
}
}
}
void die(sprite_t *s) {
sprite_t *s2;
/* clouds can't die like this */
if (s->id == P_CLOUD) return;
/* if this is the player, hurryup time counter resets */
// We already do this in the cloud movement routine, but
// this handles cases where the cloud hasn't appeared yet.
if (s == player) {
gtime = 0;
if (curmusic == fastmusic) {
playmusic(normalmusic);
}
// reset powerup types
curpoweruptype = 0;
}
if (s == player) {
/* play sound */
playfx(FX_DIE);
}
/* release anything we've caught */
for (s2 = sprite->next ; s2 ; s2 = s2->next) {
if (s2->caughtby == s) {
s2->caughtby = NULL;
s2->angry = B_TRUE;
}
}
/* set death attribute */
s->dead = D_INITIAL;
s->netting = 0;
s->slamming = 0;
// check for level clear
countmonsters();
}
// check whether the level has been won
void countmonsters(void) {
sprite_t *s2;
int mcount;
mcount = 0;
/* any monsters left? */
if (!levelcomplete) {
for (s2 = sprite->next ; s2 ; s2 = s2->next) {
if (ismonster(s2->id) && !s2->dead) {
if (s2->id != P_CLOUD) {
mcount++;
break;
}
}
}
if (mcount == 0) {
levelcomplete = LV_CLEAR;
}
}
}
void cleanup(void) {
int i;
Mix_HaltMusic();
Mix_CloseAudio();
for (i = 1; i < MAXLETTERHEIGHT; i++) {
TTF_CloseFont(font[i]);
}
TTF_Quit();
SDL_Quit();
}
void checkcollide(sprite_t *s) {
sprite_t *s2;
int collide;
int xdiff,ydiff;
for (s2 = sprite ; s2 ; s2 = s2->next) {
collide = B_TRUE;
if (s2 == s) collide = B_FALSE;
else if (s->dead) collide = B_FALSE;
else if (s2->dead) collide = B_FALSE;
else if (s->caughtby) collide = B_FALSE;
else if (s2->caughtby) collide = B_FALSE;
if (collide) {
/* check for colission with our net */
if ((s->netting) && (!s2->caughtby)) {
if (ismonster(s2->id) && s2->id != P_CLOUD) {
xdiff = (s->x + s->netlen*s->netdir) - s2->x;
if (xdiff < 0) xdiff = -xdiff;
ydiff = s->netystart - (s2->y - s2->img->h/2);
if (ydiff < 0) ydiff = -ydiff;
if ((xdiff <= s2->img->w/2) && (ydiff <= s2->img->h)) {
// we hit something!
// if we have a boxing glove, it dies
if (s->powerup == PW_BOXING) {
s2->dead = D_BOUNCING;// die as soon as it hits a wall
s2->bounces = 1;
s2->quickdie = B_TRUE;
/* go FAST in the direction player is facing */
s2->xs = s->dir * 5;
s2->ys = 0;
/* slightly raise the sprite to avoid isonground() being true */
s2->y -= 3;
/* make sure we're not too high since we'll never get lower now */
if (s2->y <= TILEH) s2->y = TILEH+1;
// become something special
s2->willbecome = P_DIAMOND;
playfx(FX_KILL);
} else {
// otherwise we caught it if we have enough nets
if (s->netcaught < s->netmax) {
s2->caughtby = s;
s2->jumping = 0;
s2->falling = 0;
s2->caughtstate = C_NETTING;
s->netcaught++;
}
}
}
}
} else {
/* check for collision with us */
xdiff = s->x - s2->x;
if (xdiff < 0) xdiff = -xdiff;
ydiff = (s->y-(s->img->h/2)) - (s2->y-(s2->img->h/2));
if (ydiff < 0) ydiff = -ydiff;
if ((xdiff <= s->img->w/2 + s2->img->w/2) &&
(ydiff <= s->img->h/2 + s2->img->h/2)) {
/* COLLISION! */
if (isfruit(s2->id) && (s2->teleporting == 0)) {
if (s == player) {
int gotscore = s2->score;
/* kill the fruit */
s2->dead = D_FINAL;
/* give points to the player */
player->score = player->score + gotscore;
/* handle fruit effects */
if (!dofruiteffect(s2)) {
playfx(FX_FRUIT);
sprintf(tempm, "%d", gotscore);
addtext(s2->x,s2->y - s2->img->h/2, TEXTSIZE_POINTS, tempm, &white,POINTSDELAY);
}
}
}
if (ismonster(s2->id) || isbullet(s2->id)) {
if ((s == player) && (!s->invuln)) {
die(s);
}
}
}
}
}
}
}
// do death bouncing effect
void bouncesprite(sprite_t *s) {
s->x += s->xs;
s->y += s->ys;
if (s->x >= (640-TILEW-(s->img->w/2))) {
// move back onto screen
while (s->x >= (640-TILEW-(s->img->w/2))) {
s->x--;
}
if (s->xs > 0) s->xs = -s->xs; // reverse direction
s->bounces++;
} else if (s->x <= TILEW+s->img->w/2) {
// move back onto screen
while (s->x <= TILEW+s->img->w/2) {
s->x++;
}
if (s->xs < 0) s->xs = -s->xs; // reverse direction
s->bounces++;
}
if (s->y >= (480-(s->img->h/2))) {
while (s->y >= (480-(s->img->h/2))) {
s->y--;
}
if (s->ys > 0) s->ys = -s->ys; // reverse direction
s->bounces++;
} else if (s->y <= s->img->h) {
while (s->y <= s->img->h) {
s->y++;
}
if (s->ys < 0) s->ys = -s->ys; // reverse direction
s->bounces++;
}
}
void movesprite(sprite_t *s) {
int rv;
tiletype_t *tt;
if (levelcomplete == LV_INIT) {
// most things can't move in this state
if ((s->id != P_PUFF) && (s->id != P_PLAYER)) {
// caught or dead sprites can move, in case
// the player catches something before level start time
if ((!s->caughtby) && (!s->dead)) return;
}
}
/* timer */
if (s->doomcount) {
s->doomcount--;
if (s->doomcount == 0) {
s->dead = D_FINAL;
}
}
/* avoid edges of screen */
if (s->y < s->img->h) {
if (!s->flies) {
s->y = s->img->h;
}
}
if (s->x > (640 - s->img->w/2)) {
s->x = 640 - s->img->w/2;
}
if (s->x < (s->img->w/2)) {
s->x = s->img->w/2;
}
if (s->invuln) {
s->invuln--;
}
if (s->caughtby) {
if (s->caughtby->slamming) {
/* */
s->x = s->caughtby->netxstart;
s->y = s->caughtby->netystart;
} else {
/* stay at position of net */
s->y = s->caughtby->y;
if (s->caughtstate == C_NETTING) {
s->x = s->caughtby->x + (s->caughtby->netlen*s->caughtby->netdir);
} else {
s->x = s->caughtby->x + (s->caughtby->img->w/2) * -(s->caughtby->dir);
}
}
return;
} else if (s->dead == D_INITIAL) { /* just set to dead */
// 0 through 5
s->xs = 0.5 + ((rand() % 50) / 10); // 0.5 through 5.5
if (rand() % 2) { // 50% change of going left
s->xs = s->xs * -1;
}
s->ys = -1 * (4 + ((rand() % 30) / 10)); // -4 through -7
s->dead = D_BOUNCING;
s->bounces = 0;
if (s == player) {
s->jumpspeed = 8;
s->jumping = 1;
}
return;
} else if (s->dead == D_BOUNCING) { /* dying */
if (s == player) {
/* shoot up in the air, then fall */
s->y -= s->jumpspeed;
s->jumping++;
if (s->jumping % 5 == 0) {
s->jumpspeed--;
}
/* if we've fallen off the bottom... */
if (s->y >= 480) {
/* pause before respawning */
s->jumpspeed = 0; /* this is now a timer */
s->dead = D_LASTBOUNCE;
}
} else {
/* bounch around the screen 3 times */
bouncesprite(s);
if ((s->bounces >= 2) && (s->ys >= 0)) {
// make sure we're not on the ground
if (!isonground(s)) {
//int tx,ty;
//tiletype_t *tt;
//tt = gettileat(s->x,s->y,&tx,&ty);
s->dead = D_LASTBOUNCE;
}
}
}
return;
} else if (s->dead == D_LASTBOUNCE) { /* final fall */
if (s == player) {
/* just delay... */
s->jumpspeed++;
if (s->jumpspeed == 50) {
/* really die */
s->dead = D_FINAL;
}
} else { /* bounce around, stop when we hit the ground */
bouncesprite(s);
if ((s->ys >= 0) && (s->y >= TILEH)) {
if (s->quickdie || isonground(s)) {
int x,y,ty;
s->dead = D_FINAL;
/* change into a fruit */
x = s->x;
gettileat(s->x,s->y-1,NULL,&ty);
//y = ty*TILEH + TILEH - 2;
y = ty*TILEH - 2;
/* make sure it's within the screen */
if (x > (640-TILEW)) x = 640-TILEW;
if (x < (TILEW)) x = TILEW;
//addsprite(s->willbecome, x, y, "Fruit", B_FALSE);
puffin(s->willbecome, x, y, "fruit", 0);
//ss = addsprite(P_PUFF, x, y, "Fruit", B_FALSE);
//ss->timer3 = s->willbecome;
}
}
}
return;
} else if (s->teleporting > 0) {
SDL_Surface *ts;
if (timer % 2 == 0) {
/* shrink */
if (s->teleporting == TP_SHRINKING) {
ts = rotozoomSurfaceXY(s->img,0, 0.9 , 0.9 ,0);
s->img = ts;
} else {
ts = rotozoomSurfaceXY(s->img,0, 0.9 , 0.9 ,0);
SDL_FreeSurface(s->img);
s->img = ts;
}
if ((s->img->w <= 2) || (s->img->h <= 2)) {
/* go to tele dest */
int x,y;
/* find destination */
for (y = 0; y < LEVELH; y++) {
for (x = 0; x < LEVELW; x++) {
tt = gettile(curlevel->map[y*LEVELW+x]);
if (tt->id == T_TELEPORTDEST) {
/* teleport there */
s->x = (x * TILEW) + (TILEW/2);
s->y = (y * TILEH) + TILEH-2;
}
}
}
s->teleporting = -1;
} else s->teleporting++;
}
} else if (s->teleporting < 0) {
double size;
if (timer % 2 == 0) {
/* grow */
size = (double)-s->teleporting / 10;
SDL_FreeSurface(s->img);
if (size >= 1) {
s->teleporting = 0;
s->img = imageset[s->id].img[F_WALK1];
} else {
s->img = rotozoomSurfaceXY(imageset[s->id].img[F_WALK1],0,size,size,0);
s->teleporting--;
}
}
} else if (s->jumping) {
movex(s, s->jumpdir*getspeed(s));
return;
}
if (!iseffect(s->id)) {
if (isonground(s)) {
if ((!s->falling) && (!s->jumping)) {
if (!s->climbing && !s->dropping) {
adjustheight(s);
}
}
}
}
/* sprite is about to jump */
if (s->jumptimer) {
s->jumptimer--;
if (s->jumptimer == 0) {
s->jumping = 1;
s->jumpspeed = 5;
if (s->jumpdir != 0) s->dir = s->jumpdir;
return;
} else if (s->jumptimer % 20 == 0) {
s->dir = -s->dir;
}
} else if (s->id == P_PUFF) {
/* SUMMARY:
timer1:
indicates current frame. if < 0, don't draw.
if >= PUFFFRAMES, use last frame.
when timer1 gets to PUFFAPPEAR, create a gem
of type s->
timer2:
loops from 0 to 9.
at intervals of PUFFSPEED, increment timer1.
*/
// still delaying
if (s->timer1 < 0) {
// increment "frame"
s->timer1++;
} else {
// increment frame at a slower pace
if (++s->timer2 >= 10) {
s->timer2 = 0;
}
if (timer % PUFFSPEED == 0) {
s->timer1++;
// finished animating
if (s->timer1 == PUFFAPPEAR) {
// create a gem/fruit/etc
addsprite(s->timer3, s->x,s->y,s->name );
} else if (s->timer1 >= PUFFFRAMES) {
s->dead = D_FINAL;
}
}
}
} else if (s->id == P_GLOVE) { // boxing glove effect
// dies when the player finshes netting
if (!player->netting) {
s->dead = D_FINAL;
} else {
// keep it at the end of the net
s->x = player->x + (player->netdir * player->netlen);
s->y = player->y - (player->img->h/2) + 5;
s->dir = player->dir;
}
} else if (s->id == P_RAT) {
if (!s->falling) {
int move = B_FALSE;
int xdiff, absxdiff;
/* distance to player */
xdiff = player->x - s->x;
if (xdiff < 0) absxdiff = -xdiff;
else absxdiff = xdiff;
tt = gettileat(s->x + s->dir+getspeed(s),s->y,NULL,NULL);
/* if there's a hole in front of us */
if (tt->solid == S_NOTSOLID) {
if ((player->y > s->y) && (s->angry)) {
/* if player is below, fall off */
if (xdiff <= (TILEW*8)) {
move = B_TRUE;
}
} else if (player->y == s->y) {
if (s->angry) {
/* if player is at same level and close, jump */
if ((s->dir == D_RIGHT) && (xdiff > 0) && (xdiff <= (TILEW*7))) {
jump(s,D_RIGHT);
} else if ((s->dir == D_LEFT) && (xdiff < 0) && (xdiff >= -(TILEW*7))) {
jump(s,D_LEFT);
}
}
}
} else {
move = B_TRUE;
}
/* either move or turn around */
if (move) {
rv = movex(s, s->dir*getspeed(s));
if (rv) {
/* if we couldn't move (hit a wall), turn */
s->dir = -s->dir;
}
} else {
s->dir = -s->dir;
}
if (s->angry) {
if ((player->dead == 0) && (!s->jumping) && (!s->jumptimer)) {
/* if player is above us, jump */
if (player->y < s->y) {
if ((xdiff >= (TILEW*2)) && (xdiff <= (TILEW*3))) {
/* jump right */
jump(s, 1);
} else if ((xdiff <= -(TILEW*2)) && (xdiff >= -(TILEW*3))) {
/* jump left */
jump(s, -1);
} else if (s->y - player->y <= (TILEH*6)) {
if ((xdiff >= 0) && (xdiff < (TILEW*2))) {
/* jump up */
jump(s, 0);
} else if ((xdiff <= 0) && (xdiff > -(TILEW*2))) {
/* jump up */
jump(s, 0);
}
} else {
/* jump whichever way we're facing */
/*
s->jumpdir = s->dir;
s->jumping = 1;
s->jumpspeed = 5;
*/
}
}
}
}
}
} else if (s->id == P_COKE) {
if (!s->falling) {
int move = B_FALSE;
int xdiff, absxdiff;
/* distance to player */
xdiff = player->x - s->x;
if (xdiff < 0) absxdiff = -xdiff;
else absxdiff = xdiff;
tt = gettileat(s->x + s->dir+getspeed(s),s->y,NULL,NULL);
/* if there's a hole in front of us */
if (tt->solid == S_NOTSOLID) {
if (player->y > s->y ) {
/* if player is below and nearby, fall off */
if (xdiff <= (TILEW*16)) {
move = B_TRUE;
}
} else if (player->y == s->y) {
/* if player is at same level and close, jump */
if ((s->dir == D_RIGHT) && (xdiff > 0) && (xdiff <= (TILEW*7))) {
jump(s,D_RIGHT);
} else if ((s->dir == D_LEFT) && (xdiff < 0) && (xdiff >= -(TILEW*7))) {
jump(s,D_LEFT);
}
}
} else {
move = B_TRUE;
}
/* either move or turn around */
if (move) {
rv = movex(s, s->dir*getspeed(s));
if (rv) {
/* if we couldn't move (hit a wall), turn */
s->dir = -s->dir;
}
} else {
s->dir = -s->dir;
}
/* moves like an angry rat all the time */
if ((player->dead == 0) && (!s->jumping) && (!s->jumptimer)) {
/* if player is above us...*/
if (player->y < s->y) {
if ((xdiff >= (TILEW*2)) && (xdiff <= (TILEW*3))) { // if 2-3 tiles right
/* jump right */
jump(s, D_RIGHT);
} else if ((xdiff <= -(TILEW*2)) && (xdiff >= -(TILEW*3))) { // if 2-3 tiles left
/* jump left */
jump(s, D_LEFT);
} else if (s->y - player->y <= (TILEH*6)) { // player less than 6 tiles above
if ((xdiff >= 0) && (xdiff < (TILEW*4))) { // ... and within 4 tiles
/* jump up */
jump(s, 0);
} else if ((xdiff <= 0) && (xdiff > -(TILEW*4))) { // ... and within 4 tiles
/* jump up */
jump(s, 0);
}
}
}
}
}
} else if (s->id == P_SNAKE) {
/* timer1 loopsfrom 0 - 19
if timer2 is 0, we can shoot. if it is 1, we can't.
*/
// inc shooting timer
if (s->timer1) {
s->timer1--;
if (s->timer1 == 0) {
printf("can shoot again\n");
}
}
if (!s->falling) {
int move = B_FALSE;
int xdiff, absxdiff,ydiff;
/* distance to player */
xdiff = player->x - s->x;
if (xdiff < 0) absxdiff = -xdiff;
else absxdiff = xdiff;
tt = gettileat(s->x + s->dir+getspeed(s),s->y,NULL,NULL);
/* if there's a hole in front of us */
if (tt->solid == S_NOTSOLID) {
if ((player->y > s->y) && (s->angry)) {
/* if player is below, fall off */
if (xdiff <= (TILEW*8)) {
move = B_TRUE;
}
} else if (player->y == s->y) {
if (s->angry) {
/* if player is at same level and close, jump */
if ((s->dir == D_RIGHT) && (xdiff > 0) && (xdiff <= (TILEW*7))) {
jump(s,D_RIGHT);
} else if ((s->dir == D_LEFT) && (xdiff < 0) && (xdiff >= -(TILEW*7))) {
jump(s,D_LEFT);
}
}
}
} else {
move = B_TRUE;
}
/* shoot */
ydiff = player->y - s->y;
if (ydiff < 0) ydiff =-ydiff;
if (ydiff <= (TILEH*4)) {
sprite_t *ss;
int shoot = B_FALSE;
if (s->bullet == NULL) { // if we don't already have a bullet
// if we are facing the player
if ( (player->x < s->x) && (s->dir == D_LEFT) ) {
shoot = B_TRUE;
} else if ( (player->x > s->x) && (s->dir == D_RIGHT) ) {
shoot = B_TRUE;
}
}
if (shoot) {
// if our shooting timer is okay
if (s->timer1 == 0) {
ss = addsprite(P_SPIT,s->x,s->y - s->img->h/2,"spit" );
ss->ys = 0;
ss->xs = s->dir * (getspeed(s)*2);
ss->dir = s->dir;
ss->owner = s;
s->bullet = ss;
// start timer for shooting again
s->timer1 = 200;
}
}
}
/* either move or turn around */
if (move) {
rv = movex(s, s->dir*getspeed(s));
if (rv) {
/* if we couldn't move (hit a wall), turn */
s->dir = -s->dir;
}
} else {
s->dir = -s->dir;
}
if (s->angry) {
if ((player->dead == 0) && (!s->jumping) && (!s->jumptimer)) {
/* if player is above us, jump */
if (player->y < s->y) {
if ((xdiff >= (TILEW*2)) && (xdiff <= (TILEW*3))) {
/* jump right */
jump(s, 1);
} else if ((xdiff <= -(TILEW*2)) && (xdiff >= -(TILEW*3))) {
/* jump left */
jump(s, -1);
} else if (s->y - player->y <= (TILEH*6)) {
if ((xdiff >= 0) && (xdiff < (TILEW*2))) {
/* jump up */
jump(s, 0);
} else if ((xdiff <= 0) && (xdiff > -(TILEW*2))) {
/* jump up */
jump(s, 0);
}
} else {
/* jump whichever way we're facing */
/*
s->jumpdir = s->dir;
s->jumping = 1;
s->jumpspeed = 5;
*/
}
}
}
}
}
} else if (s->id == P_BEE) {
double absxs,absys;
if ((s->xs == -99) || (s->ys == -99)) {
s->xs = getspeed(s);
s->ys = getspeed(s);
}
if (s->xs > 0) absxs = 1;
else absxs = -1;
if (s->ys > 0) absys = 1;
else absys = -1;
/* this will fix the speed if ANGRY is set */
s->xs = absxs*getspeed(s);
s->ys = absys*getspeed(s);
/* can we move? */
tt = gettileat(s->x + absxs*((s->img->w/2)+8), s->y-(s->img->h/2),NULL,NULL);
if ((tt->solid) || (tt->spikes )) {
/* turn */
s->xs = -s->xs;
}
tt = gettileat(s->x, s->y-(s->img->h/2) + absys*((s->img->h/2)+8),NULL,NULL);
if ((tt->solid) || (tt->spikes)) {
/* turn */
s->ys = -s->ys;
}
/* check for top of screen */
// TODO: later, remove this when we can wrap around
if ((s->y <= s->img->h) && (s->ys < 0)) {
s->ys = -s->ys;
}
/* move */
s->x += s->xs;
s->y += s->ys;
s->dir = absxs;
s->moved = B_TRUE;
} else if (s->id == P_SPIDER) {
/* if on ground, go up */
if (isonground(s) && !s->flies) {
s->flies = B_TRUE;
s->falling = B_FALSE;
s->ys = -getspeed(s);
} else if (s->falling) {
/* if we are about to hit spikes, go back up */
tt = gettileat(s->x,s->y + 8,NULL,NULL);
if (tt->spikes) {
/* go back up */
s->flies = B_TRUE;
s->falling = B_FALSE;
s->ys = -getspeed(s);
}
} else {
if (s->ys != -99) {
/* if roof above us */
tt = gettileat(s->x,s->y - s->img->h,NULL,NULL);
if (tt->solid) {
int tiley;
/* start walking again */
tiley = (int) (s->y / TILEH);
s->y = tiley*TILEH;
s->ys = -99;
s->flies = B_TRUE;
} else {
s->y += s->ys;
s->flies = B_TRUE;
}
} else {
int move = B_FALSE;
int xdiff;
/* walk back and forwards */
/* drop if player is close */
xdiff = player->x - s->x;
if (xdiff < 0) xdiff =-xdiff;
if ((player->y > s->y) && (xdiff <= (TILEW*2))) {
s->flies = B_FALSE;
s->falling = B_TRUE;
s->fallspeed = 8;
} else {
s->flies = B_TRUE;
/* if there's a hole in front of us */
tt = gettileat(s->x + s->dir*((s->img->w/2)+2),s->y - s->img->h - 2,NULL,NULL);
if (tt->solid == S_NOTSOLID) {
move = B_FALSE;
} else {
move = B_TRUE;
}
/* either move or turn around */
if (move) {
rv = movex(s, s->dir*getspeed(s));
if (rv) {
/* if we couldn't move (hit a wall), turn */
s->dir = -s->dir;
}
} else {
s->dir = -s->dir;
}
}
}
}
} else if (s->id == P_SPIT) {
if (movex(s, s->xs)) {
s->dead = D_FINAL;
}
} else if (s->id == P_CLOUD) {
if ((player->dead) || (levelcomplete)) {
if ((s->img->h <= 3) || (s->img->w <= 3)) {
s->dead = D_FINAL;
/* reset hurryup timer */
gtime = 0;
} else {
SDL_Surface *ts;
/* get smaller */
ts = rotozoomSurfaceXY(s->img,0, 0.9 , 0.9 ,0);
SDL_FreeSurface(s->img);
s->img = ts;
s->y--;
}
} else {
if ((s->xs == -99) || (s->ys == -99)) {
s->ys = 0.5;
s->xs = 1;
}
s->x += s->xs;
s->y += s->ys;
if (s->x >= (640 - s->img->w/2 - 5)) {
s->xs = -s->xs;
s->x = 640 - s->img->w/2 - 6;
}
if (s->x <= (s->img->w/2 + 5)) {
s->xs = -s->xs;
s->x = s->img->w/2 + 6;
}
if (s->y >= (480 - s->img->h/2 - 5)) {
s->ys = -s->ys;
s->y = 480 - s->img->h/2 - 6;
}
if (s->y <= (s->img->h/2 + 5)) {
s->ys = -s->ys;
s->y = s->img->h/2 + 6;
}
if (timer % 50 == 0) {
int w,h;
SDL_Surface *ts;
/* get bigger */
w = s->img->w;
h = s->img->h;
ts = rotozoomSurfaceXY(s->img,0, 1.1 , 1.1 ,0);
SDL_FreeSurface(s->img);
s->img = ts;
s->y += 2;
}
}
}
}
void dotileeffects(sprite_t *s) {
tiletype_t *tt;
int finished = B_FALSE;
int state = 0;
if (s->jumping || s->dead || s->caughtby) {
return;
}
/* check where we are */
tt = gettileat(s->x,s->y-2,NULL,NULL);
if ((tt->id == T_TELEPORT) || (tt->id == T_TELEPORT2)) {
if (s->id == P_PLAYER || ismonster(s->id)) {
if (s->id != P_CLOUD) {
s->teleporting = 1;
}
}
}
/* check under us */
tt = gettileat(s->x,s->y+3,NULL,NULL);
while (!finished) {
if (tt->id == T_RIGHT) {
if (!ismonster(s->id) && !isfruit(s->id)) {
movex(s, 1.5);
}
finished = B_TRUE;
} else if (tt->id == T_LEFT) {
if (!ismonster(s->id) && !isfruit(s->id)) {
movex(s, -1.5);
}
finished = B_TRUE;
} else if (tt->spikes) {
if (!isfruit(s->id)) {
if (!s->invuln) {
if (s->id != P_CLOUD) {
die(s);
}
}
}
finished = B_TRUE;
} else {
if (state == 0) {
/* check tile to our right */
tt = gettileat(s->x + s->img->w/2,s->y+3,NULL,NULL);
state = 1;
} else if (state == 1) {
/* check tile to our left */
tt = gettileat(s->x - s->img->w/2,s->y+3,NULL,NULL);
state = 2;
} else {
finished = B_TRUE;
}
}
}
}
// initial transition to a new level
void drawlevel(void) {
int x,y;
int dstx,dsty,xdis,ydis,dis;
int turns;
int pspeed;
SDL_Rect area,dst;
int speed = TILEW;
SDL_Surface *playerbg;
/* TODO: chekc for memory leak here with temps -
need to free it at the end? */
if (temps) {
SDL_FreeSurface(temps);
temps = NULL;
}
temps = SDL_CreateRGBSurface(SDL_SWSURFACE,
640, 480,
screen->format->BitsPerPixel, screen->format->Rmask,
screen->format->Gmask,screen->format->Bmask,
screen->format->Amask);
// change player to floating image
player->img = imageset[P_PLAYER].img[F_SHOOT];
// create buffer for player background
playerbg = SDL_CreateRGBSurface(SDL_SWSURFACE,
player->img->w, player->img->h,
screen->format->BitsPerPixel, screen->format->Rmask,
screen->format->Gmask,screen->format->Bmask,
screen->format->Amask);
// draw the full level onto a temporary surface
for (x = 0; x < LEVELW; x++) {
for (y = 0; y < LEVELH; y++) {
drawtile(temps,x,y);
}
}
// figure out where the player's new start position is
dstx = (curlevel->p1x * TILEW) + (TILEW/2);
dsty = (curlevel->p1y * TILEH) + TILEH-2;
// figure out distance to newposition
xdis = player->x - dstx; if (xdis < 0) xdis = -xdis;
ydis = player->y - dsty; if (ydis < 0) ydis = -ydis;
if (xdis > ydis) dis = xdis;
else dis = ydis;
// figure out how many loops it will take to scroll to the next level
switch (oldexitdir) {
case D_LEFT:
case D_RIGHT:
turns = 640 / TILEW;
break;
case D_UP:
case D_DOWN:
default:
turns = 480 / TILEH;
break;
}
turns -= 4; // just to be safe
// figure out how fast player needs to move to get there in time
pspeed = dis / turns;
if (oldexitdir == D_LEFT) {
// blit a column at a time to the real screen, shuffling
// the real one along.
for (x = 0; x < 640; x += speed) {
// move player
movetostart(player,dstx,dsty,pspeed);
// shuffle real screen
area.x = 0;
area.y = 0;
area.w = 640-TILEW;
area.h = 480;
dst.x = TILEW;
dst.y = 0;
dst.w = 0;
dst.h = 0;
SDL_BlitSurface(screen, &area, screen, &dst);
// blit next column from temp surface (take last column first)
area.x = 640-x;
area.y = 0;
area.w = TILEW;
area.h = 480;
dst.x = 0;
dst.y = 0;
dst.w = 0;
dst.h = 0;
SDL_BlitSurface(temps, &area, screen, &dst);
// remember area behind player
grabbehind(player, playerbg);
// draw player
drawsprite(player);
// update screen
SDL_UpdateRect(screen, 0,0,640,480);
// remove player
area.x = player->x - player->img->w/2;
area.y = player->y - player->img->h;
area.w = 0;
area.h = 0;
SDL_BlitSurface(playerbg, NULL, screen, &area );
}
} else if (oldexitdir == D_UP) {
// blit a row at a time to the real screen, shuffling
// the real one along.
for (y = 0; y < 480; y += speed) {
// move player
movetostart(player,dstx,dsty,pspeed);
// shuffle real screen
area.x = 0;
area.y = 0;
area.w = 640;
area.h = 480-TILEH;
dst.x = 0;
dst.y = TILEH;
dst.w = 0;
dst.h = 0;
SDL_BlitSurface(screen, &area, screen, &dst);
// blit next row from temp surface (take last column first)
area.x = 0;
area.y = 480-y;
area.w = 640;
area.h = TILEH;
dst.x = 0;
dst.y = 0;
dst.w = 0;
dst.h = 0;
SDL_BlitSurface(temps, &area, screen, &dst);
// remember area behind player
grabbehind(player, playerbg);
// draw player
drawsprite(player);
// update screen
SDL_UpdateRect(screen, 0,0,640,480);
// remove player
area.x = player->x - player->img->w/2;
area.y = player->y - player->img->h;
area.w = 0;
area.h = 0;
SDL_BlitSurface(playerbg, NULL, screen, &area );
}
} else if (oldexitdir == D_DOWN) {
// blit a row at a time to the real screen, shuffling
// the real one along.
for (y = 0; y < 480; y += speed) {
// move player
movetostart(player,dstx,dsty,pspeed);
// shuffle real screen
area.x = 0;
area.y = TILEH;
area.w = 640;
area.h = 480-TILEH;
dst.x = 0;
dst.y = 0;
dst.w = 0;
dst.h = 0;
SDL_BlitSurface(screen, &area, screen, &dst);
// blit next row from temp surface (take last column first)
area.x = 0;
area.y = y;
area.w = 640;
area.h = TILEH;
dst.x = 0;
dst.y = 480-TILEH;
dst.w = 0;
dst.h = 0;
SDL_BlitSurface(temps, &area, screen, &dst);
// remember area behind player
grabbehind(player, playerbg);
// draw player
drawsprite(player);
// update screen
SDL_UpdateRect(screen, 0,0,640,480);
// remove player
area.x = player->x - player->img->w/2;
area.y = player->y - player->img->h;
area.w = 0;
area.h = 0;
SDL_BlitSurface(playerbg, NULL, screen, &area );
}
} else { // right, or default
// blit a column at a time to the real screen, shuffling
// the real one along.
for (x = 0; x < 640; x += speed) {
// move player
movetostart(player,dstx,dsty,pspeed);
// shuffle real screen
area.x = TILEW;
area.y = 0;
area.w = 640-TILEW;
area.h = 480;
dst.x = 0;
dst.y = 0;
dst.w = 0;
dst.h = 0;
SDL_BlitSurface(screen, &area, screen, &dst);
// blit next column from temp surface (take last column first)
area.x = x;
area.y = 0;
area.w = TILEW;
area.h = 480;
dst.x = 640-TILEW;
dst.y = 0;
dst.w = 0;
dst.h = 0;
SDL_BlitSurface(temps, &area, screen, &dst);
// remember area behind player
grabbehind(player, playerbg);
// draw player
drawsprite(player);
// update screen
SDL_UpdateRect(screen, 0,0,640,480);
// remove player
area.x = player->x - player->img->w/2;
area.y = player->y - player->img->h;
area.w = 0;
area.h = 0;
SDL_BlitSurface(playerbg, NULL, screen, &area );
}
}
SDL_FreeSurface(playerbg);
levelcomplete = B_FALSE;
}
double getspeed(sprite_t *s ) {
int id = s->id;
double speed = 1;
if (id == P_PLAYER) {
speed = s->speed;
} else if (id == P_RAT) {
if (s->angry) speed = 1.5;
else speed = 1;
} else if (id == P_SNAKE) {
if (s->angry) speed = 1.5;
else speed = 1;
} else if (id == P_BEE) {
if (s->angry) speed = 2;
else speed = 1;
} else if (id == P_SPIDER) {
if (s->angry) speed = 1.5;
else speed = 2;
} else if (id == P_CHEESE) {
speed = 0;
}
if (isinwater(s)) speed /= 2;
return speed;
}
int addtext(int x, int y, int size, char *string, SDL_Color *c, int delay) {
text_t *t;
if (text == NULL) {
text = malloc(sizeof(text_t));
t = text;
t->prev = NULL;
} else {
t = lasttext;
t->next = malloc(sizeof(text_t));
t->next->prev = t;
t = t->next;
}
t->bg = SDL_CreateRGBSurface(SDL_SWSURFACE,
300, 110,
screen->format->BitsPerPixel, screen->format->Rmask,
screen->format->Gmask,screen->format->Bmask,
screen->format->Amask);
t->c = c;
t->x = x;
t->y = y;
t->maxsize = size;
t->size = 3;
strcpy(t->txt, string);
t->state = 0;
t->delay = delay;
t->img = TTF_RenderText_Solid(font[t->size], t->txt, *t->c);
t->next = NULL;
lasttext = t;
return B_FALSE;
}
/* copy background buffer (ie. tiles) to screen, erasing sprites */
void fruit(void) {
SDL_BlitSurface(temps, NULL, screen, NULL);
}
void movetext(void) {
text_t *t,*nextt;
for (t = text ; t ; t = nextt) {
nextt = t->next;
if (t->state == 0) {
t->size += TEXTSPEED;
if (t->size >= t->maxsize) {
t->state = 1;
}
} else if (t->state == t->delay) {
t->size -= TEXTSPEED;
if (t->size <= 3) {
killtext(t);
}
} else {
t->state++;
}
}
}
void drawscore(void) {
SDL_Surface *score;
SDL_Rect area;
sprintf(tempm, "%d",player->score);
/* shadow */
score = TTF_RenderText_Solid(font[14], tempm, black);
area.x = 18;
area.y = 7;
area.w = 0;
area.h = 0;
SDL_BlitSurface(score, NULL, screen, &area);
SDL_FreeSurface(score);
/* score */
score = TTF_RenderText_Solid(font[14], tempm, green);
area.x = 20;
area.y = 5;
area.w = 0;
area.h = 0;
SDL_BlitSurface(score, NULL, screen, &area);
SDL_FreeSurface(score);
}
void drawtext(void) {
text_t *t;
SDL_Rect area;
for (t = text ; t ; t = t->next) {
/* create text */
if (t->img) {
SDL_FreeSurface(t->img);
t->img = NULL;
}
t->img = TTF_RenderText_Solid(font[t->size], t->txt, *t->c);
/* get bg */
t->bgarea.x = t->x - t->img->w/2;
t->bgarea.y = t->y - t->img->h/2;
t->bgarea.w = t->img->w;
t->bgarea.h = t->img->h;
SDL_BlitSurface(screen, &t->bgarea, t->bg, NULL);
/* draw text */
area.x = t->x - t->img->w/2;
area.y = t->y - t->img->h/2;
area.w = t->img->w;
area.h = t->img->h;
SDL_BlitSurface(t->img,NULL, screen, &area);
}
}
/* copy background buffer (ie. tiles) to screen, erasing sprites */
void removeall(void) {
SDL_BlitSurface(temps, NULL, screen, NULL);
}
void drawnetting(sprite_t *s) {
int sx;
SDL_Rect area;
if (s->netting) {
int y,yy;
int dis;
int netsleft;
sx = s->x;
s->nety = s->y - (s->img->h/2) + 2;
s->netxstart = s->x;
s->netystart = s->nety - 3;
if (s->netdir == 1) {
area.x = s->netxstart + TILEW/2;
} else {
area.x = s->netxstart - TILEW/2 - s->netlen;
}
//area.y = s->netystart;
area.y = s->y - s->img->h-2 + 2;
area.w = s->netlen;
area.h = s->img->h+2;
SDL_BlitSurface(screen, &area,s->netbg, NULL);
netsleft = s->netmax - s->netcaught;
if (netsleft < 1) netsleft = 1;
dis = (int)s->img->h / (int)(netsleft+1) + 1;
for (y = dis; y < s->img->h; y += dis) {
yy = s->y - s->img->h;
yy += y;
drawline16(screen,sx,s->nety,s->x + s->netdir*s->netlen,yy,white);
}
//drawline16(screen,sx,s->nety,s->x + s->netdir*s->netlen,s->nety-3,white);
//drawline16(screen,sx,s->nety,s->x + s->netdir*s->netlen,s->nety,white);
//drawline16(screen,sx,s->nety,s->x + s->netdir*s->netlen,s->nety+3,white);
} else if (s->slamming) {
double dist;
int x,y;
dist = (s->slamangle * (180/M_PI))/2;
s->netxstart = s->x + cos(s->slamangle-(180*(M_PI/180)))*dist*s->dir;
s->netystart = s->y + sin(s->slamangle-(180*(M_PI/180)))*dist;
/* middle line */
drawline16(screen,s->x,s->y - s->img->h/2,
s->netxstart,s->netystart,white);
/* left line */
x = s->x + cos(s->slamangle-(5*(M_PI/180))-(180*(M_PI/180)))*dist*s->dir;
y = s->y + sin(s->slamangle-(5*(M_PI/180))-(180*(M_PI/180)))*dist;
drawline16(screen,s->x,s->y - s->img->h/2,x, y, white);
/* right line */
x = s->x + cos(s->slamangle+(5*(M_PI/180))-(180*(M_PI/180)))*dist*s->dir;
y = s->y + sin(s->slamangle+(5*(M_PI/180))-(180*(M_PI/180)))*dist;
drawline16(screen,s->x,s->y - s->img->h/2,x, y, white);
}
}
void removenetting(sprite_t *s) {
SDL_Rect area,sarea;
if (s->netting) {
sarea.x = 0;
sarea.y = 0;
sarea.w = s->netlen;
sarea.h = s->img->h+2;
if (s->netdir == 1) {
area.x = s->netxstart + TILEW/2;
} else {
area.x = s->netxstart - TILEW/2 - s->netlen;
}
//area.y = s->netystart;
area.y = s->y - s->img->h-2;
area.w = s->netlen;
area.h = s->img->h+2;
if (s->netbg != NULL) {
SDL_BlitSurface(s->netbg, &sarea, screen, &area);
}
}
}
void removesprite(sprite_t *s) {
int startx,starty,endx,endy;
int x,y;
/* find topleft-most tile */
gettileat(s->x - s->img->w, s->y - s->img->h,&startx,&starty);
/* find bottomright-most tile */
gettileat(s->x + s->img->w, s->y + s->img->h,&endx,&endy);
if (s->slamming) {
if (s->dir == D_LEFT) {
startx -= 5;
endx += 1;
} else {
startx -= 1;
endx += 5;
}
starty -= 2;
endy += 1;
}
/* draw the tiles */
for (y = starty; y <= endy; y++) {
for (x = startx; x <= endx; x++) {
drawtile(screen,x,y);
}
}
}
int isonbridge(sprite_t *s) {
tiletype_t *tthere;
tthere = gettileat(s->x,s->y, NULL,NULL);
if (tthere->id == T_BRIDGE) {
return B_TRUE;
}
return B_FALSE;
}
int isonladder(sprite_t *s) {
tiletype_t *tthere;
tthere = gettileat(s->x,s->y, NULL,NULL);
if ((tthere->id == T_LADDER) || (tthere->id == T_LADDERTOP)) {
return B_TRUE;
}
return B_FALSE;
}
int isladderabove(sprite_t *s) {
tiletype_t *tthere;
tthere = gettileat(s->x,s->y-TILEH, NULL,NULL);
if (tthere->id == T_LADDER) {
return B_TRUE;
}
return B_FALSE;
}
int isinwater(sprite_t *s) {
tiletype_t *tt;
tt = gettileat(s->x, s->y - s->img->h/2, NULL, NULL);
if (tt->water) {
return B_TRUE;
}
return B_FALSE;
}
int isroofabove(sprite_t *s) {
tiletype_t *tt;
/* get tile above sprite's head */
tt = gettileat(s->x, s->y - s->img->h,NULL,NULL);
if (tt->solid) return B_TRUE;
tt = gettileat(s->x + s->img->w/2, s->y - s->img->h,NULL,NULL);
if (tt->solid) return B_TRUE;
tt = gettileat(s->x - s->img->w/2, s->y - s->img->h,NULL,NULL);
if (tt->solid) return B_TRUE;
return B_FALSE;
}
/* is there a roof n tiles above us. If howfar is 1, this is the same
as the regular isroofabove() */
int isroofnabove(sprite_t *s, int howfar) {
tiletype_t *tt;
int ypos;
ypos = s->y - s->img->h - TILEH*(howfar-1);
/* get tile above sprite's head */
tt = gettileat(s->x, ypos,NULL,NULL);
if (tt->solid) return B_TRUE;
tt = gettileat(s->x + (s->img->w/2 - 2), ypos,NULL,NULL);
if (tt->solid) return B_TRUE;
tt = gettileat(s->x - (s->img->w/2 - 2), ypos,NULL,NULL);
if (tt->solid) return B_TRUE;
return B_FALSE;
}
int isonground(sprite_t *s) {
/* get tile below sprite's feet */
if (isongroundpoint(s, s->x, s->y)) {
return B_TRUE;
}
if ((s->dead) || (!s->falling && !s->dropping)) {
if (isongroundpoint(s, s->x + s->img->w/2, s->y)) {
return B_TRUE;
}
if (isongroundpoint(s, s->x - s->img->w/2, s->y)) {
return B_TRUE;
}
}
return B_FALSE;
}
int isongroundpoint(sprite_t *s, int x,int y) {
tiletype_t *tt;
int tilex,tiley;
int xoff;
int groundy;
tt = gettileat(x,y, &tilex, &tiley);
// slope etc doesn't matter if you're dead
if (s->dead && tt->solid) {
return B_TRUE;
}
// when dropping, the tile you dropped from doesn't count
// as "ground".
if (s->dropping && (tilex == s->dropx) && (tiley == s->dropy)) {
return B_FALSE;
}
/* get offset */
xoff = x - (tilex*TILEW);
/* if it's not solid... */
if (tt->solid == 0) {
return B_FALSE;
} else {
/* check height of tile at that position */
groundy = tiley*TILEH + tt->lowness[xoff];
/* above ground level */
if (y < groundy) {
return B_FALSE;
} else if (y > groundy + 3) {
/* below ground level */
if (s->jumping) {
return B_FALSE;
}
} else if (s->falling) {
tiletype_t *abovetile;
/* falling, on a tile, but with tiles above you */
// ie. you've jumped up through a single tile (can't
// jump through two or more tiles
abovetile = gettileat(x,y-TILEH, NULL, NULL);
if (abovetile->solid) {
return B_FALSE;
}
// } else if (s->dropping) {
// tiletype_t *abovetile;
/* if the tile we're on is solid but not the one
above, then we've "dropped" and are still
travelling through the tile we dropped from. */
// abovetile = gettileat(x,y-TILEH, NULL, NULL);
// if (!abovetile->solid) {
// return B_FALSE;
// }
}
}
return B_TRUE;
}
void dogravity(sprite_t *s) {
sprite_t *s2;
if (s->dead) return;
if (s->flies) return;
if (iseffect(s->id)) return;
if (isbullet(s->id)) return;
if (isonladder(s) && !s->falling && !s->jumping) {
s->falling = B_FALSE;
return;
}
// handle jumps
if (s->jumping) {
s->falling = B_FALSE;
s->y -= s->jumpspeed;
s->jumping++;
if (s->jumping % 5 == 0) {
if (s->jumpspeed > 0) s->jumpspeed--;
else {
s->jumping = 0;
s->falling = B_TRUE;
s->fallspeed = 0;
}
}
/* have we hit a roof ? */
/* can jump through one tile, but not two or more */
if (isroofabove(s) && isroofnabove(s,2)) {
/* stop jumping */
s->jumping = 0;
s->falling = B_TRUE;
s->fallspeed = 0;
}
} else {
if (isonground(s) ) {
s->dropping = B_FALSE;
s->falling = B_FALSE;
s->climbing = B_FALSE;
} else {
if (s->falling == B_FALSE) {
s->fallspeed = 1;
}
s->falling = B_TRUE;
if (isinwater(s)) {
s->y += (s->fallspeed/2);
} else {
s->y += s->fallspeed;
}
if ((timer % 10 == 0) && (s->fallspeed < FALLSPEED)) {
s->fallspeed++;
}
}
}
if (s->netting) {
s->netlen += s->netspeed;
s->netting++;
if (s->netting % 2 == 0) {
if (s->netspeed > -NETSPEED) s->netspeed--;
else {
s->netting = 0;
for (s2 = sprite ; s2 ; s2 = s2->next) {
if ((s2->caughtby == s) && (s2->caughtstate == C_NETTING)) {
s2->caughtstate = C_NETTED;
}
}
}
}
}
if (s->slamming) {
s->slamangle += (10 * (M_PI/180));
if (s->slamangle >= (180 * (M_PI/180))) {
/* finished slamming */
int xdiff,ydiff,xnet = 0,ynet = 0;
int pointsinc = 250;
int psize = 6;
int gotsomething = B_FALSE;
s->slamming = 0;
/* reset fruit type counter */
curfruittype = 0;
/* kill anything we've caught */
for (s2 = sprite; s2 ; s2 = s2->next) {
/* kill anything we have caught */
if (s2->caughtby == s) {
tiletype_t *tt;
tt = gettileat(s2->x,s2->y+2,NULL,NULL);
/* if on ground, monster dies */
if ((tt == NULL) || (tt->solid)) {
/* will become a fruit when it finishes dying */
s2->willbecome = fruittypes[curfruittype];
/* increment fruit type */
if (fruittypes[++curfruittype] == -1) {
curfruittype = 0;
}
die(s2);
pointsinc *= 2;
psize += 10;
xnet = s2->x;
ynet = s2->y - s2->img->h/2;
gotsomething++;
} else {
/* otherwise it gets angry */
s2->angry = B_TRUE;
}
}
}
/* kill anything we hit */
for (s2 = sprite; s2 ; s2 = s2->next) {
if ((s2->caughtby != s) && (!s2->dead) && (ismonster(s2->id))) {
xdiff = s2->x - xnet;
if (xdiff < 0) xdiff =-xdiff;
ydiff = (s2->y - s2->img->h/2) - ynet;
if (ydiff < 0) ydiff =-ydiff;
if ((xdiff <= s2->img->w) && (ydiff <= s2->img->h)) {
if (s2->id != P_CLOUD) {
/* dies and becomes a powerup */
s2->willbecome = poweruptypes[curpoweruptype];
if (poweruptypes[++curpoweruptype] == -1) {
curpoweruptype = 0;
}
die(s2);
pointsinc *= 2;
psize += 10;
gotsomething++;
}
}
}
}
if (gotsomething >= 1) {
playfx(FX_KILL);
}
if (gotsomething > 1) {
playfx(FX_MULTIKILL);
}
gotsomething = B_FALSE;
/* release anything we've caught */
for (s2 = sprite; s2 ; s2 = s2->next) {
if (s2->caughtby == s) {
/* release it */
s2->caughtby = NULL;
}
}
s->netcaught = 0;
/* show points */
if (psize >= MAXLETTERHEIGHT) {
psize = MAXLETTERHEIGHT-1;
}
if (pointsinc > 250) {
sprintf(tempm, "%d",pointsinc);
addtext(xnet,ynet-TILEH, psize, tempm, &white,POINTSDELAY);
/* give points to player */
s->score += pointsinc;
}
}
}
}
int movex(sprite_t *s,double amt) {
double newx,newy;
double curx,cury;
int tilex,tiley;
tiletype_t *tt,*tt2;
int newxoff,newgroundy;
int newtilex,newtiley;
double amtdir;
tt = gettileat(s->x, s->y, &tilex,&tiley);
if (amt > 0) amtdir = 1;
else (amtdir = -1);
curx = s->x;
cury = s->y;
/* check for blockage in front of us */
//newx = s->x + (amtdir*(s->img->w/2));
newx = s->x + (amtdir*TILEW/2);
newy = cury-TILEH;
tt2 = gettileat(newx,newy,&newtilex,&newtiley);
if (tt2->solid == S_SOLID) {
return B_TRUE;
}
if (tt2->solid == S_SLOPE && (!candoslopes(s->id))) {
return B_TRUE;
}
/* get new position */
newx = curx + amt;
newy = cury-2;
tt2 = gettileat(newx,newy,&newtilex,&newtiley);
newxoff = newx - (newtilex*TILEW);
newgroundy = newtiley*TILEH + tt2->lowness[newxoff];
/* new block is at least partially solid */
if (tt2->solid == S_SOLID) {
return B_TRUE;
} else if ((tt2->solid == S_SLOPE) && candoslopes(s->id)) {
/* we can move, but need to adjust our height */
s->x += amt;
} else {
/* new block is empty */
s->x += amt;
}
s->moved = B_TRUE;
return B_FALSE;
}
void adjustheight(sprite_t *s) {
tiletype_t *tt;
int xoff,groundy;
int tilex,tiley;
if ((s->flies) || isbullet(s->id)) {
return;
}
tt = gettileat(s->x,s->y-1,&tilex,&tiley);
if (!tt) return;
if (tt->solid == S_SLOPE) {
xoff = s->x - (tilex*TILEW);
groundy = tiley*TILEH + tt->lowness[xoff];
s->y = groundy;
} else if (tt->solid == S_SOLID) {
while (tt->solid == S_SOLID) {
s->y--;
tt = gettileat(s->x,s->y-1,&tilex,&tiley);
}
}
}
int dofruiteffect(sprite_t *s) {
if (s->id == P_SPEED) {
playfx(FX_POWERUP);
player->speed = 2;
addtext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, "Speed up!", &white,POINTSDELAY);
return B_TRUE;
} else if (s->id == P_NUMNETS) {
playfx(FX_POWERUP);
if (player->netmax < 4) {
player->netmax++;
}
sprintf(tempm, "%d nets!",player->netmax);
addtext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,POINTSDELAY);
return B_TRUE;
} else if (s->id == P_BIGNET) {
playfx(FX_POWERUP);
player->netbig = B_TRUE;
sprintf(tempm, "Big net!");
addtext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,POINTSDELAY);
return B_TRUE;
} else if (s->id == P_BOXING) {
playfx(FX_POWERUP);
//player->netbig = B_TRUE;
player->powerup = PW_BOXING;
sprintf(tempm, "Boxing Glove!");
addtext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,POINTSDELAY);
return B_TRUE;
} else if (s->id == P_HELP) {
playfx(FX_POWERUP);
addtext(320,240,TEXTSIZE_HELP, s->name, &white,HELPDELAY);
return B_TRUE;
} else if (isflower(s->id)) {
int xx;
sprite_t *ss;
tiletype_t *tt;
int found = B_FALSE;
/* was this the last fruit of its kind on the level? */
for (ss = sprite; ss; ss = ss->next) {
if ((ss != s) && (ss->id == s->id)) {
found = B_TRUE;
}
}
/* if so, create a left/right stream of flowers */
if (!found) {
int howmany = STREAMWID + 1;
int puffdelay;
// RIGHT
puffdelay = 0;
for (xx = s->x+TILEW; xx < s->x + (TILEW*howmany); xx += TILEW) {
// if on a wall, exit
tt = gettileat(xx,s->y-TILEH,NULL,NULL);
if (tt->solid) {
break;
}
/* create a flower */
puffin(flowertogem(s->id), xx, s->y, "flower", puffdelay);
//ss = addsprite(P_PUFF, xx,s->y,"puff", B_FALSE);
//ss->timer1 = -puffdelay;
//ss->timer3 = flowertogem(s->id);
puffdelay += 1;
}
// LEFT
puffdelay = 0;
for (xx = s->x+TILEW; xx > s->x - (TILEW*howmany); xx -= TILEW) {
// if on a wall, exit
tt = gettileat(xx,s->y-TILEH,NULL,NULL);
if (tt->solid) {
break;
}
puffin(flowertogem(s->id), xx, s->y, "flower", puffdelay);
//ss = addsprite(P_PUFF, xx,s->y,"puff", B_FALSE);
//ss->timer1 = -puffdelay;
//ss->timer3 = flowertogem(s->id);
puffdelay += 1;
}
playfx(FX_BONUS);
sprintf(tempm, "BONUS!");
addtext(s->x,s->y - s->img->h/2, TEXTSIZE_BONUS, tempm,&white,BONUSDELAY);
return B_TRUE;
}
}
return B_FALSE;
}
void usage(void) {
printf("usage: rc [-fs] [-l xx]\n");
printf(" -fs Start in full-screen mode.\n");
printf(" -l xx Skip to level xx.\n");
printf("\n");
}
// returns B_TRUE if the given player is able to walk/fall left/right
int canmove(sprite_t *pl) {
if (!pl->jumping && !pl->slamming ) {
if (!pl->netting) {
if (!isonladder(pl) || pl->falling || isonground(pl)) {
return B_TRUE;
}
} else if (pl->netting && pl->falling) { // netting and falling
return B_TRUE;
}
}
return B_FALSE;
}
// returns B_TRUE if the given player is able to change which direction they're facing
int canturn(sprite_t *pl) {
if (!pl->slamming && !pl->netting) {
return B_TRUE;
}
return B_FALSE;
}
int loadfx(int sid , char *filename) {
char tempname[BUFLEN];
sprintf(tempname, "sounds/");
strncat(tempname, filename, BUFLEN);
sfx[sid] = Mix_LoadWAV(tempname);
if (!sfx[sid]) {
printf("error loading %s\n",tempname);
return B_TRUE;
}
return B_FALSE;
}
int initsound(void) {
int i;
/* init */
if (Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 1024) < 0) {
printf("Error initialising sound: %s.\n",Mix_GetError());
return B_TRUE;
}
Mix_AllocateChannels(CH_LASTCHANNEL);
loadfx(FX_SHOOT, "shoot.wav");
loadfx(FX_SLAM, "slam.wav");
loadfx(FX_KILL, "kill.wav");
loadfx(FX_MULTIKILL, "multikill.wav");
loadfx(FX_JUMP, "jump.wav");
loadfx(FX_FRUIT, "fruit.wav");
loadfx(FX_POWERUP, "powerup.wav");
loadfx(FX_DIE, "die.wav");
loadfx(FX_WINLEVEL, "winlevel.wav");
loadfx(FX_HURRYUP, "hurryup.wav");
loadfx(FX_TOOSLOW, "tooslow.wav");
loadfx(FX_BONUS, "bonus.wav");
// load sound effects
for (i = 0; i < MAXFX; i++) {
if (!sfx[i]) {
return B_TRUE;
}
}
// set up callback
Mix_ChannelFinished(channeldone);
// load music
normalmusic = Mix_LoadMUS("music/main.mod");
if (!normalmusic) {
printf("can't load music\n");
return B_TRUE;
}
fastmusic = Mix_LoadMUS("music/mainfast.mod");
if (!fastmusic) {
printf("can't load fast music\n");
return B_TRUE;
}
return B_FALSE;
}
void playfx(int num) {
Mix_PlayChannel(-1, sfx[num], 0);
}
void playmusic(Mix_Music *toplay) {
/* stop music */
if (musicplaying) {
Mix_HaltMusic();
}
/* start music */
//music = toplay;
Mix_PlayMusic(toplay, -1);
curmusic = toplay;
musicplaying = B_TRUE;
}
void stopmusic(void) {
/* stop music */
if (musicplaying) {
Mix_HaltMusic();
}
musicplaying = B_FALSE;
curmusic = NULL;
}
// callback for sound effects finishing
void channeldone(int channel) {
if (channel == CH_HURRYUP) {
// start fast music
playmusic(fastmusic);
}
}
// move player towards new position
void movetostart(sprite_t *p, int dstx, int dsty, int speed) {
if (p->x < dstx) {
p->x += speed;
if (p->x > dstx) p->x = dstx;
}
if (p->x > dstx) {
p->x -= speed;
if (p->x < dstx) p->x = dstx;
}
if (p->y < dsty) {
p->y += speed;
if (p->y > dsty) p->y = dsty;
}
if (p->y > dsty) {
p->y -= speed;
if (p->y < dsty) p->y = dsty;
}
}
// grabs area behind a sprite into a temp buffer
SDL_Surface *grabbehind(sprite_t *s, SDL_Surface *surf) {
SDL_Rect area;
// remember area behind player
area.x = s->x - s->img->w/2;
area.y = s->y - s->img->h;
area.w = s->img->w;
area.h = s->img->h;
SDL_BlitSurface(screen, &area, surf, NULL);
return surf;
}
// returns a random powerup
int randompowerup(void) {
int num;
num = rand() % 4;
return P_BOXING;
switch (num) {
case 0:
default:
return P_SPEED;
case 1:
return P_BIGNET;
case 2:
return P_NUMNETS;
case 3:
return P_BOXING;
}
}
void dumpsprites(void) {
sprite_t *s;
int i = 0;
int mcount = 0;
for (s = sprite; s ; s = s->next) {
printf("Sprite #%d: %s at %1.0f,%1.0f id=%d (",i,s->name,s->x,s->y, s->id);
if (ismonster(s->id)) { printf("MONSTER, "); mcount++; }
if (isfruit(s->id)) printf("FRUIT, ");
if (isflower(s->id)) printf("FLOWER, ");
if (isbullet(s->id)) printf("BULLET, ");
if (iseffect(s->id)) printf("EFFECT, ");
printf(")\n");
}
printf("--------\n");
printf("Total monsters: %d\n",mcount);
printf("\n\n");
}