#include #include #include #include #include #include #include #include #include #include #include #include #include #include #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]; int playedbell; int clocktime; int sprayalpha; // for spray effect SDL_Surface *pausedtext, *pausedshadow; int paused; 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_NUMNETS, P_BELL, P_BIGNET, P_GEMBOOST, P_NUMNETS, P_GEMBOOST, P_NUMNETS, P_HELMET, -1 }; int curfruittype = 0; int curpoweruptype = 0; int gtime = 0; int fpsticks = 0; int fpsstart = 0; int curworld = 1; int curlevelnum; int skipto = -1; // which level to skip to 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 blue = {0, 0, 255, 0}; SDL_Color cyan = {0, 255, 255, 0}; SDL_Color white = {255, 255, 255, 0}; SDL_Color grey = {210, 210, 210, 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; cheat = B_FALSE; levelbg = NULL; /* 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], "-c")) { printf("Cheat mode.\n"); cheat = B_TRUE; } else if (!strcmp(argv[i], "-l")) { if (++i >= argc) { printf("Missing level number.\n"); usage(); exit(1); } skipto = atoi(argv[i]); printf("Skipping to level %d.\n",skipto); } 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_RED_SIZE, 5 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 ); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); //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); screen = SDL_SetVideoMode(640,480,16,SDL_SWSURFACE|vidargs); #endif if (!screen) { printf("Failed to open window: %s\n", SDL_GetError()); exit(1); } srand(time(NULL)); if (loadimagesets()) { printf("Error loading images.\n"); return 1; } if (loadlevellist()) { printf("Error loading level list from levels.dat.\n"); return 1; } // is we're skipping to a level, do so now if (skipto >= 0) { for (i = 0; i < numlevels; i++) { if (levelentry[i].id == skipto) { curlevelnum = i; } } } 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("newtiles/land.png"); fakeblock.numframes = 1; fakeblock.next = NULL; fakeblock.prev = NULL; /* load fonts */ TTF_Init(); sprintf(filename, "gamefont.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; } /* init tiles */ if (loadtiletypes("green.tiles")) { printf("Cannot initialise tiles\n"); exit(1); } playmusic(normalmusic); if (loadlevel(curworld,curlevelnum)) { return 1; } //levelcomplete = LV_FINAL; curlevelnum-- ; // since nexlevel() will increment it nextlevel(); //drawlevel(); flip(); timer = 0; player->invuln = INVULNTIME; player->score = 0; player->lives = 3; /* generate images for "PAUSED" text */ pausedtext = TTF_RenderText_Solid(font[TEXTSIZE_PAUSED], "PAUSED", yellow); pausedshadow = TTF_RenderText_Solid(font[TEXTSIZE_PAUSED], "PAUSED", black); paused = B_FALSE; // main loop while (1) { removeall(); // check for sprite death and update moevment counters checksprites(); /* check for end of level */ if (levelcomplete == LV_CLEAR) { addoutlinetext(320,240,TEXTSIZE_LEVEL,"Level Complete!",&green,&black,LEVELWINDELAY); levelcomplete = LV_WAIT; playfx(FX_WINLEVEL); // turn off clock powerup if (player->powerup == PW_CLOCK) { Mix_ResumeMusic(); player->powerup = B_FALSE; } } 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_BLACKCLOUD) { mcount++; break; } } } if (mcount == 0) { levelcompletetime = gtime; levelcomplete = LV_FINAL; } } /* check for keys */ SDL_PumpEvents(); keys = SDL_GetKeyState(NULL); if (keys[SDLK_d]) { if (boss) { boss->angry = BOSSFLASHTIME; printf("hit!\n"); } } if (keys[SDLK_c]) { // cheat if (toggletimer == 0) { // all powerups playfx(FX_POWERUP); player->netmax = 4; // all nets player->netbig = B_TRUE; // big net player->speed = 2; // fast sprintf(tempm, "Full power!"); addoutlinetext(player->x,player->y - player->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); toggletimer = 80; } } if (keys[SDLK_p]) { if (toggletimer == 0) { if (paused) { paused = B_FALSE; } else { paused = B_TRUE; } toggletimer = 80; } } if (keys[SDLK_ESCAPE]) { return 1; } if (!paused) { if (keys[SDLK_q]) { gtime = nexthurryup-1; } if (keys[SDLK_l]) { if (toggletimer == 0) { addscore(player, 100001); toggletimer = 80; } } if (keys[SDLK_n]) { if (toggletimer == 0) { // nextlevel(); sprite_t *s2, *nexts; // make the screen shake player->powerup = PW_BOMB; player->timer1 = BOMBSHAKETIME; // kill all monsters playfx(FX_BOOM); sprintf(tempm, "KABOOM!!"); addoutlinetext(player->x,player->y - player->img->h/2, TEXTSIZE_BOMB, tempm,&red,&yellow,POINTSDELAY); for (s2 = sprite; s2 ; s2 = nexts) { nexts = s2->next; if (isbullet(s2->id)) { s2->dead = D_FINAL; } else if (ismonster(s2->id)) { s2->willbecome = P_DIAMOND; if (s2->caughtby) { s2->caughtby = NULL; player->netcaught--; } die(s2); } } toggletimer = 50; } } if (keys[SDLK_d]) { if (toggletimer == 0) { // dump sprites dumpsprites(); toggletimer = 50; } } if (keys[SDLK_RETURN]) { if (toggletimer == 0) { SDL_WM_ToggleFullScreen(screen); toggletimer = 50; } } 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)) { if (player->climbing || isladderabove(player)) { if (!player->netting && !player->slamming) { player->y -= getspeed(player); player->jumping = 0; player->falling = 0; player->climbing = B_TRUE; player->moved = MV_WALK; } } } if (keys[SDLK_DOWN]) { if (isonladder(player)) { if (!player->netting && !player->slamming) { player->y += getspeed(player); player->jumping = 0; player->falling = 0; player->climbing = B_TRUE; player->moved = MV_WALK; } } } if (keys[SDLK_x]) { if (isinwater(player)) { if (!player->jumping) { //player->jumping = B_FALSE; player->falling = B_FALSE; if (!isroofabove(player)) { // is there water above us too? if (isinwaterpoint(player->x, player->y-TILEH)) { // if so, swim up player->y -= (getspeed(player)*3); } else { int whichway; // if not, jump up player->climbing = B_FALSE; if (keys[SDLK_RIGHT]) { whichway = 1; } else if (keys[SDLK_LEFT]) { whichway = -1; } else { whichway = 0; } playfx(FX_SPLASH); jump(player, whichway); } } } } else { // not in water 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 { // jumping int whichway; if (keys[SDLK_RIGHT]) { whichway = 1; } else if (keys[SDLK_LEFT]) { whichway = -1; } else { whichway = 0; } 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; /* handle mace */ if (player->powerup == PW_MACE) { sprite_t *s; int found; // use existing mace if it is there found = B_FALSE; for (s = sprite; s ; s = s->next) { if (s->id == P_MACE) { s->x = player->x; s->y = player->y - (imageset[player->id].img[F_SHOOT]->h / 2) + 5; found = B_TRUE; } } if (!found) { addsprite(P_MACE, player->x, player->y - (imageset[player->id].img[F_SHOOT]->h / 2) + 5, "mace"); } } } } else { if ((player->netcaught < player->netmax) && (player->climbing == B_FALSE)) { // handle cannon if (player->powerup == PW_CANNON) { playfx(FX_CANNON); player->powerup = PW_CANNONFIRE; player->timer3 = CANNONSIZE+5; // use this for size } else { /* 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[player->id].img[F_SHOOT]->h / 2) + 5; found = B_TRUE; } } if (!found) { addsprite(P_GLOVE, player->x, player->y - (imageset[player->id].img[F_SHOOT]->h / 2) + 5, "glove"); } } } } } } } } if (!paused) { if (player->powerup != PW_CLOCK) { // 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); } } // ice effect if (curlevel->iced == ICE_INPROGRESS) { doice(); } } // move sprites moveallsprites(); // animate text movetext(); if (levelcomplete == LV_INIT) { // only player dogravity(player); dotileeffects(player); } else { sprite_t *s; switch (levelcomplete) { case LV_INPROGRESS: case LV_CLEAR: case LV_WAIT: case LV_FINAL: case LV_CLOUD: /* gravity */ for (s = sprite ; s ; s = s->next) { dogravity(s); } /* tile effects */ for (s = sprite ; s ; s = s->next) { dotileeffects(s); } break; } } // bell flash effect if (player->hasbell) { /* check for bell sound */ /* play a bell sound if the powerup will be a permenant one */ if (ispermenant(level->poweruptype)) { // play sound once if (!playedbell) { playfx(FX_BELL); playedbell = BELL_DONESOUND; } if (playedbell != BELL_DONEFLASH) { if (timer >= BELLTIME) { playedbell = BELL_DONEFLASH; } else if (timer % 10 == 0) { SDL_Rect area; area.x = 0; area.y = 0; area.w = 640; area.h = 480; SDL_FillRect(screen, &area, SDL_MapRGB(screen->format,white.r,white.g,white.b)); flip(); } } } } checkcollideall(); } drawnetting(player); if (player->powerup == PW_CANNONFIRE) { // cannon firing if (timer % 2 == 0) { int found; sprite_t *s; // find cannon found = B_FALSE; for (s = sprite; s ; s = s->next) { if (s->id == P_CANNON) { found = B_TRUE; break; } } if (!found) { printf("weird error - no cannon!!\n"); player->powerup = B_FALSE; } else { int initx,inity; int i; int initsize; int xx,yy; sprite_t *s3; initsize = player->timer3; if (initsize > CANNONSIZE) initsize = CANNONSIZE; // calc init positions inity = s->y - (s->img->h/2) - (initsize/2); initx = s->x - (initsize/2); yy = inity; xx = initx; // horiz lines for (i = 0; i < initsize ; i++) { drawline16(screen,0,yy,640,yy,white); for (s3 = sprite; s3 ; s3 = s3->next) { if (ismonster(s3->id) && !s3->dead) { if ((yy >= s3->y - s3->img->h) && (yy <= s3->y)) { s3->willbecome = P_DIAMOND; die(s3); } } } yy++; } // vert lines for (i = 0; i < initsize ; i++) { drawline16(screen,xx,0,xx,480,white); for (s3 = sprite; s3 ; s3 = s3->next) { if (ismonster(s3->id) && !s3->dead) { if ((xx >= s3->x - (s3->img->w/2)) && (xx <= s3->x + (s3->img->w/2))) { s3->willbecome = P_DIAMOND; die(s3); } } } xx++; } // dec size if (timer % 8 == 0) { player->timer3--; if (player->timer3 <= 0) { player->powerup = B_FALSE; s->dead = D_FINAL; } } } } } // draw sprites drawallsprites(); /* draw text */ drawtext(); drawscore(); if (boss) { drawbosshealth(); } if (paused) { SDL_Rect area; // show that we are paused area.x = (640/2) - (pausedshadow->w/2) - 2; area.y = (480/2) - (pausedshadow->h/2) + 2; area.w = 0; area.h = 0; SDL_BlitSurface(pausedshadow, NULL, screen, &area); area.x = (640/2) - (pausedtext->w/2); area.y = (480/2) - (pausedtext->h/2); area.w = 0; area.h = 0; SDL_BlitSurface(pausedtext, NULL, screen, &area); } if (!paused) { /* is screen shaking? */ if (player->powerup == PW_BOMB) { if (timer % 5 == 0) { int amt; SDL_Rect area; amt = (rand() % 20)+1; area.x = 0; area.y = amt; area.w = 640; area.h = 480-amt; SDL_BlitSurface(screen, &area, screen, NULL); area.x = 0; area.y = 480-amt; area.w = 640; area.h = amt; SDL_FillRect(screen, &area, SDL_MapRGB(screen->format,black.r,black.g,black.b)); } if (--player->timer1 == 0) { player->powerup = B_FALSE; } } else if (player->powerup == PW_SPRAYUP) { // green overlay for fly spray SDL_SetAlpha(greenbox, SDL_SRCALPHA, sprayalpha); sprayalpha += 5; if (sprayalpha >= 100) { player->powerup = PW_SPRAYDOWN; } SDL_BlitSurface(greenbox,NULL,screen,NULL); } else if (player->powerup == PW_SPRAYDOWN) { // green overlay for fly spray SDL_SetAlpha(greenbox, SDL_SRCALPHA, sprayalpha); sprayalpha -= 5; if (sprayalpha <= 0) { player->powerup = PW_SPRAYUP; } SDL_BlitSurface(greenbox,NULL,screen,NULL); } else if (player->powerup == PW_RATSHAKE) { // horizontal shake due to rat // shake screen if (timer % 5 == 0) { int amt; SDL_Rect area; amt = (rand() % 20)+1; area.x = amt; area.y = 0; area.w = 640-amt; area.h = 480; SDL_BlitSurface(screen, &area, screen, NULL); area.x = 640-amt; area.y = 0; area.w = amt; area.h = 480; SDL_FillRect(screen, &area, SDL_MapRGB(screen->format,black.r,black.g,black.b)); } // create rat if (timer % 10 == 0) { if (countmonsters() < 8) { // max 7 rats + king rat puffin(P_RAT, rand() % (640-(TILEW*8))+(TILEW*4), TILEH, "gen_rat",0); } } if (--player->timer1 == 0) { player->powerup = B_FALSE; } } } // Update screen flip(); if (!paused) { if (++timer == 100) timer = 0; } if (toggletimer > 0) toggletimer--; if (!paused) { 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++; // handle clock effect if (player->powerup == PW_CLOCK) { char tempm[SMALLBUFLEN]; // text if (clocktime > 0) { sprintf(tempm, "%d",clocktime); addoutlinetext(320,120,TEXTSIZE_LEVEL, tempm, &yellow,&black,15); // sound effect playfx(FX_CLOCK); } // never reach hurryup time nexthurryup++; // decrement counter clocktime--; if (clocktime < 0) { // finished! Mix_ResumeMusic(); player->powerup = B_FALSE; } } /* check for hurryup*/ if (!isbosslevel(curlevelnum)) { if (gtime == nexthurryup) { if (!levelcomplete) { for (s = sprite; s; s = s->next) { if ((s != player) && (ismonster(s->id))) { s->angry = B_TRUE; s->iced = B_FALSE; } } addoutlinetext(320,240,TEXTSIZE_HURRY, "Hurry up!", &yellow,&black,HURRYDELAY); stopmusic(); Mix_PlayChannel(CH_HURRYUP, sfx[FX_HURRYUP], 0); } } else if (gtime == nexthurryup + 15) { // 15 secs after hurryup if (!levelcomplete) { addsprite(P_BLACKCLOUD, 320,240,"cloud"); addoutlinetext(320,240,TEXTSIZE_HURRY, "Too slow!", &red,&black,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 */ /* the type of powerup is randomly determined when the level is loaded */ /* IMPORTANT: the name 'random_up' is important as it tells addsprite() to give it a higher doomcount */ puffin(level->poweruptype, 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) { int x,y; levelcomplete = LV_CLOUD; switch (curlevel->exitdir) { case D_RIGHT: default: x = TILEW/2; y = rand() % 480; break; case D_LEFT: x = 640-(TILEW/2); y = rand() % 480; break; case D_UP: x = rand() % 640; y = 480-(TILEW/2); break; case D_DOWN: x = rand() % 640; y = TILEW/2; break; } puffin(P_PINKCLOUD, x, y, "pinkcloud", 0); } } } fpsstart = fpsticks; } } } void nextlevel(void) { char msg[SMALLBUFLEN]; sprite_t *cloudp; // 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); // do the moving to next level animation drawlevel(); // now the player gets invincibility player->invuln = INVULNTIME; /* reset game stats */ levelcomplete = LV_INIT; levelcompletetime = -1; /* reset level stats */ level->gotpowerup = B_FALSE; if (player->hasbell) { playedbell = B_FALSE; } else { playedbell = BELL_DONEFLASH; } level->iced = ICE_NONE; level->icey = -1; sprintf(msg, "Level %d-%d",curworld, curlevelnum); addoutlinetext(320,240-18,TEXTSIZE_LEVEL,msg,&white,&black,LEVELDELAY); sprintf(msg, "\"%s\"", curlevel->name); addoutlinetext(320,240+18,TEXTSIZE_LEVEL2,msg,&cyan,&black,LEVELDELAY); /* reset player stats */ player->netting = B_FALSE; player->slamming = B_FALSE; player->jumping = B_FALSE; player->powerup = B_FALSE; player->netcaught = 0; // add initial fading cloud cloudp = addsprite(P_PINKCLOUD, player->x,player->y + (imageset[P_PINKCLOUD].img[F_WALK1]->h / 2),"initial_pinkcloud"); cloudp->size = 1.0; // TODO: add boss every 20 levles //if (curlevel % 20 == 0) { //} if (cheat) { player->speed = 2; } // reset timer timer = 0; } void jump(sprite_t *s, int dir) { // if we've just been hit with armour, bypass all these checks) if (!s->recoiling) { // can't jump if already jumping if (s->jumping) return; if (s->jumptimer) return; // can only jump up when climbing if (s->climbing && (dir == 0)) { return; } } if (s->recoiling || isonground(s) || isinwater(s) || isonladder(s)) { if (ismonster(s->id)) { s->jumpdir = dir; if (s->jumpdir != 0) { s->dir = s->jumpdir; } // special case if ((s->id == P_KINGRAT) && (s->timer1 == KRS_CHARGEWAIT)) { s->jumpspeed = MONJUMPSPEED; // will be changed later s->jumping = 1; } else { s->jumptimer = 60; s->willjumpspeed = MONJUMPSPEED; } } else { s->jumpdir = dir; if (s->jumpdir != 0) { s->dir = s->jumpdir; } s->jumping = 1; if (s->climbing) { s->jumpspeed = 3; } else if (s->ontramp) { s->jumpspeed = 7; } else { s->jumpspeed = 5; } } // stop climbing s->climbing = B_FALSE; // stop recoiling s->recoiling = B_FALSE; // play a sound effect if this is the player if (s == player) { if (player->ontramp) { playfx(FX_SPRING); } else { playfx(FX_JUMP); } } adjustx(s, F_JUMP); } } void die(sprite_t *s) { sprite_t *s2; /* clouds can't die like this */ if (s->id == P_BLACKCLOUD) return; if (s->id == P_PINKCLOUD) 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) { // if we have armour, we lose it instead of dying if (player->armour) { // lose armour player->armour = B_FALSE; player->id = P_PLAYER; // become invulnerable temporarily player->invuln = INVULNTIME; // bounce back player->recoiling = B_TRUE; jump(player, -player->dir); // play sound playfx(FX_OW); // don't process rest of the death code return; } else { int tnum; if (player->powerup == PW_CLOCK) { Mix_ResumeMusic(); } player->powerup = B_FALSE; nexthurryup = gtime + level->hurryuptime; if (curmusic == fastmusic) { playmusic(normalmusic); } // reset powerup types curpoweruptype = 0; /* lose a life */ player->lives--; /* play sound */ playfx(FX_DIE); // draw text tnum = rand() % MAXDEATHTEXT; addoutlinetext(player->x,player->y,TEXTSIZE_DEATH,deathtext[tnum],&red,&black,DIEDELAY); } } /* release anything we've caught */ for (s2 = sprite->next ; s2 ; s2 = s2->next) { if (s2->caughtby == s) { s2->caughtby = NULL; s2->angry = B_TRUE; } } // un-ice everything if (s == player) { for (s2 = sprite->next ; s2 ; s2 = s2->next) { if (s2->iced) { s2->iced = B_FALSE; } } } // boss effects if (s == boss) { // stop screen shaking if (player->powerup == PW_RATSHAKE) { player->powerup = B_FALSE; } // reset timers s->timer1 = 0; s->timer2 = 0; // death timer s->timer3 = BOSSDIETIME; } /* set death attribute */ s->dead = D_INITIAL; s->netting = 0; s->slamming = 0; s->iced = 0; s->angry = 0; // check for level clear checklevelend(); } void checklevelend(void) { sprite_t *s2; int mcount; if (!levelcomplete) { mcount = 0; /* any monsters left? */ for (s2 = sprite->next ; s2 ; s2 = s2->next) { if (ismonster(s2->id) && !s2->dead) { if (s2->id != P_BLACKCLOUD) { mcount++; } } } if (mcount == 0) { levelcomplete = LV_CLEAR; } } } // count monsters on level int countmonsters(void) { sprite_t *s2; int mcount; mcount = 0; /* any monsters left? */ for (s2 = sprite->next ; s2 ; s2 = s2->next) { if (ismonster(s2->id) && !s2->dead) { if (s2->id != P_BLACKCLOUD) { mcount++; } } } return mcount; } void cleanup(void) { int i; SDL_FreeSurface(pausedtext); SDL_FreeSurface(pausedshadow); 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 keepchecking; int xdiff,ydiff; int ythresh; int netsleft; // for all other sprites for (s2 = sprite ; s2 ; s2 = s2->next) { if (s2 == s) continue; else if (s->dead) continue; else if (s2->dead) continue; else if (s->caughtby) continue; else if (s2->caughtby) continue; else if (s2->teleporting) continue; keepchecking = B_TRUE; /* check for colission with our net */ if (s->netting ) { if (isnettable(s2->id)) { 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; // take numnets into account for y check ythresh = s2->img->h; netsleft = s->netmax - s->netcaught; if (netsleft > 1) { ythresh += ((int)s->img->h / (int)(netsleft+1) ); } //if ((xdiff <= s2->img->w/2) && (ydiff <= s2->img->h)) { if ((xdiff <= s2->img->w/2) && (ydiff <= ythresh)) { // 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); sprintf(tempm, bifftext[rand() % MAXBIFFTEXT]); addoutlinetext(s2->x,s2->y - s->img->h/2, TEXTSIZE_BIFF, tempm,&red,&yellow,POINTSDELAY); keepchecking = B_FALSE; } else if (s2->iced) { // it dies playfx(FX_ICEBREAK); die(s2); } else { // otherwise we caught it if we have enough nets if (s->netcaught < s->netmax) { s2->caughtby = s; s2->jumping = B_FALSE; s2->falling = 0; s2->caughtstate = C_NETTING; s->netcaught++; keepchecking = B_FALSE; } } } } } // end if s->netting // don't keep going if this sprite has now been killed/caught if (!keepchecking) continue; /* now 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! */ // are we the player? if (s == player) { //if (isfruit(s2->id) && (s2->teleporting == 0)) { if (isfruit(s2->id)) { int gotscore = s2->score; /* kill the fruit */ s2->dead = D_FINAL; /* give points to the player */ addscore(player, gotscore); /* handle fruit effects */ if (!dofruiteffect(s2)) { playfx(FX_FRUIT); sprintf(tempm, "%d", gotscore); addoutlinetext(s2->x,s2->y - s2->img->h/2, TEXTSIZE_POINTS, tempm, &white,&black,POINTSDELAY); } } else if (ismonster(s2->id) || isbullet(s2->id)) { if (s2->iced) { // monster dies playfx(FX_ICEBREAK); die(s2); } else if (!s->invuln) { // player dies die(s); } } } else if (s->id == P_SMASH) { // smash from mace slam if (ismonster(s2->id)) { // monster dies die(s2); // become something special s2->willbecome = P_DIAMOND; } } } } // end for each sprite } // 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++; } } // returns TRUE if we finished the level int 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 != player)) { if (!iseffect(s->id) && (s != player)) { // caught or dead sprites can move, in case // the player catches something before level start time if ((!s->caughtby) && (!s->dead)) return B_FALSE; } } // only player can move if you have a clock if (player->powerup == PW_CLOCK) { if (!iseffect(s->id) && (s != player) && !s->caughtby && !s->dead) { return B_FALSE; } } // iced monsters can't move if (ismonster(s->id) && s->iced) { return B_FALSE; } /* timer */ if (s->doomcount) { if (s->id == P_PUFF) printf("PUFF WITH DOOMCOUNT!\n"); 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 B_FALSE; } else if (s->dead == D_INITIAL) { /* just set to dead */ if (s == boss) { // flash s->timer3--; if (s->timer3 == 0) { s->dead = D_FINAL; } } else { // 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 B_FALSE; } 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 B_FALSE; } 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 B_FALSE; } 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 B_FALSE; } if (!iseffect(s->id)) { if (isonground(s) || ((s->id == P_KINGRAT) && (s->timer1 == KRS_WALK))) { if ((!s->falling) && (!s->jumping)) { if (!s->climbing && !s->dropping) { if (!isonladder(s)) { adjustheight(s); } } } } } /* sprite is about to jump */ if (s->jumptimer) { s->jumptimer--; if (s->jumptimer == 0) { s->jumping = 1; s->jumpspeed = s->willjumpspeed; if (s->jumpdir != 0) s->dir = s->jumpdir; return B_FALSE; } else if (s->jumptimer % 20 == 0) { s->dir = -s->dir; } } else if ((s->id == P_PUFF) || (s->id == P_SMASH) || (s->id == P_SPARKLE)) { /* 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 if (s->id == P_PUFF) { if (s->timer3 >= 0) { sprite_t *newsp; newsp = addsprite(s->timer3, s->x,s->y,s->name ); // is it a boss? if so update boss pointer switch (s->timer3) { case P_KINGRAT: boss = newsp; } } } } else if (s->timer1 >= PUFFFRAMES) { s->timer1 = 999; // TODO: REMOVE THIS - debugging! s->dead = D_FINAL; } } } } else if (s->id == P_CANNON) { // cannon effect // die if player dies if (player->dead) { s->dead = D_FINAL; } else if (player->powerup == PW_CANNON) { int targx,targy; // try to stay behind player targx = player->x - (player->dir * 32); targy = player->y; if (s->x < targx) { s->x += 2; if (s->x > targx) s->x = targx; } if (s->y < targy) { s->y += 2; if (s->y > targy) s->y = targy; } if (s->x > targx) { s->x -= 2; if (s->x < targx) s->x = targx; } if (s->y > targy) { s->y -= 2; if (s->y < targy) s->y = targy; } } } 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_MACE) { // mace slam effect // dies when the player finishes slamming if (!player->slamming) { s->dead = D_FINAL; } else { double dist; // keep it at the end of the slam dist = (player->slamangle * (180/M_PI))/2; s->x = player->x + cos(player->slamangle-(180*(M_PI/180)))*dist*player->dir; s->y = player->y + sin(player->slamangle-(180*(M_PI/180)))*dist + imageset[P_MACEPOWERUP].img[F_WALK1]->h/2; 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->dir * (s->img->w/2)),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 || boss)) { /* if player is below, fall off */ if (xdiff <= (TILEW*8)) { move = B_TRUE; } } else if (player->y == s->y) { if ((s->angry) || boss) { /* 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) || (boss)) { 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_KINGRAT) { /* timer1 is state 0 == walk back and forth timer2 counts down, at zero we change state */ if (s->timer1 == KRS_WALK) { int canseeplayer = B_TRUE; int ydis; // move rv = movex(s, s->dir*getspeed(s)); if (rv) { /* if we couldn't move (hit a wall), turn */ s->dir = -s->dir; } // if player is in front, charge! if (player->dead) { canseeplayer = B_FALSE; } else { ydis = abs(s->y - player->y); if (ydis > 2) { canseeplayer = B_FALSE; } else if ((s->dir < 0) && (player->x > s->x)) { canseeplayer = B_FALSE; } else if ((s->dir > 0) && (player->x < s->x)) { canseeplayer = B_FALSE; } } if (canseeplayer) { // CHARGE! playfx(FX_BOSSWINDUP); s->timer1 = KRS_CHARGEWAIT; s->timer2 = KR_NUMJUMPS; } else { // dec timer s->timer2--; if (s->timer2 == 0) { if (player->dead) { // reset timer s->timer2 = KR_WALKTIME; } else { // jump to player's height s->timer1 = KRS_JUMP; } } } } else if (s->timer1 == KRS_CHARGEWAIT) { // jump timer2 times. if (timer % 2 == 0) { // add puffs puffin(-1, s->x + (rand() % (s->img->w)) - (s->img->w/2), s->y, "nothing", rand() % 5); } if (!s->jumping && !s->falling) { s->timer2--; if ((s->timer2 <= 0) && (!s->jumping)){ //CHARGE s->timer1 = KRS_CHARGE; s->flies = B_TRUE; playfx(FX_BOSSCHARGE); } else { // bounce jump(s,0); s->jumpspeed = 1; } } } else if (s->timer1 == KRS_CHARGE) { int rv; // move fast forwards rv = movex(s, s->dir*KR_CHARGESPEED); if (rv) { // hit a wall - get stunned s->timer1 = KRS_STUN; s->timer2 = KR_STUNTIME; s->flies = B_FALSE; playfx(FX_BOSSWALL); // shake the screen via a fake player powerup player->powerup = PW_RATSHAKE; player->timer1 = RATSHAKETIME; } } else if (s->timer1 == KRS_STUN) { s->timer2--; if (s->timer2 == 0) { s->timer1 = KRS_WALK; s->timer2 = KR_WALKTIME; } } else if (s->timer1 == KRS_JUMP) { // jump to player height if (!s->jumping) { int ydis; int try; int tot; int n; if ((player->dead) || (player->y > s->y)) { // player below or dead try = 2; } else { // get distance ydis = abs(s->y - player->y); // figure out how much we need to jump for (try = 2; try < (KR_MAXJUMP-1); try++) { tot = 0; for (n = try; n >= 1; n--) { tot = tot + (5*n); } if (tot >= ydis) { break; } } } jump(s, 0); // manually adjust speed try++; // one more just to be sure s->willjumpspeed = try; // next state s->timer1 = KRS_WAITFORTOP; } } else if (s->timer1 == KRS_WAITFORTOP) { if (!s->jumping) { s->timer1 = KRS_FALL; } } else if (s->timer1 == KRS_FALL) { // fall to player height if (!s->jumping && (s->y >= player->y)) { // back to normal state but lower timer s->timer1 = KRS_WALK; s->timer2 = KR_WALKTIME; // low delay } } } 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) { } } 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 = MV_WALK; } else if (s->id == P_SPIDER) { /* timer1 loopsfrom 0 - 45 if timer2 is 0, we can shoot. if it is 1, we can't. */ if (s->timer1) { s->timer1--; } /* 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->timer1 == 0)) { s->timer1 = 200; s->flies = B_FALSE; s->falling = B_TRUE; s->fallspeed = 8; } else { int tx,ty; 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 - 4,&tx,&ty); 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_PINKCLOUD) { if (levelcomplete == LV_CLOUD) { SDL_Surface *ts; double targx,targy; double amtleft; double xdis,ydis,dis; int turnsleft; if (!player->dead) { /* If small, grow and move towards player's feet (at a speed which will get us there when we are fully grown. */ targx = player->x; targy = player->y + (imageset[P_PINKCLOUD].img[F_WALK1]->h / 2); if (s->img->w < imageset[P_PINKCLOUD].img[F_WALK1]->w) { // grow s->size += ((double)PCGROWSPEED); ts = rotozoomSurfaceXY(imageset[P_PINKCLOUD].img[F_WALK1],0, s->size, s->size,0); SDL_FreeSurface(s->img); s->img = ts; } // calculate number of turns left amtleft = 1.0 - s->size; // amount left turnsleft = (int) (amtleft / (double)PCGROWSPEED); if (turnsleft < 1) { // make sure we're in the right place s->x = targx; s->y = targy; s->rotated = 0; if (levelcomplete == LV_CLOUD) { levelcomplete = LV_CLOUDLOOP; } printf("init angle = %0.2f\n", s->angle / (M_PI/180)); } else { // calculate distance to player xdis = abs(targx - s->x); ydis = abs(targy - s->y); dis = sqrt((xdis*xdis) + (ydis*ydis)); // calculate speed needed to reach player x/y in time s->speed = dis / turnsleft; // figure out angle to player s->angle = atan2(targy - s->y, targx - s->x); // keep between 0 and 360 if (s->angle > (360*(M_PI/180))) { s->angle -= (360*(M_PI/180)); } if (s->angle < 0) { s->angle += (360*(M_PI/180)); } // figure out x/y speed s->xs = (cos(s->angle) * s->speed); s->ys = (sin(s->angle) * s->speed); //printf("cloud: amtleft = %0.2f (1.0 - ssize=%0.2f)",amtleft,s->size); // printf("cloud: speed = %0.2f, xdis=%0.2f,ydis=%0.2f,dis=%0.2f, turnsleft=%d,xs=%0.2f,ys=%0.2f\n",speed,xdis,ydis,dis,turnsleft,s->xs,s->ys); // move s->x += s->xs; s->y += s->ys; } } } else if (levelcomplete == LV_CLOUDLOOP) { // stick player to us player->x = s->x; player->y = s->y - (imageset[P_PINKCLOUD].img[F_WALK1]->h / 2); // turn off payer attributes player->climbing = B_FALSE; player->netting = B_FALSE; player->slamming = B_FALSE; // keep turning towards exit (2 degrees at a time) s->angle += (PCTURN * (M_PI/180)); // keep between 0 and 360 if (s->angle > (360*(M_PI/180))) { s->angle -= (360*(M_PI/180)); } if (s->angle < 0) { s->angle += (360*(M_PI/180)); } // track total rotation s->rotated += (PCTURN * (M_PI/180)); if (s->angle < (270 * (M_PI/180))) { // decellerate if (s->speed > 5) { s->speed -= 1; } } else { // accellerate if (s->speed < 10) { s->speed += 0.5; } } // figure out x/y speed s->xs = (cos(s->angle) * s->speed); s->ys = (sin(s->angle) * s->speed); // move s->x += s->xs; s->y += s->ys; // keep on screen if (s->x > 640-(s->img->w/2)) { s->x = 640 - (s->img->w/2); } if (s->y > 480-(s->img->h/2)) { s->y = 480 - (s->img->h/2); } if (s->x < s->img->w/2) { s->x = s->img->w/2; } if (s->y < player->img->h/2) { s->y = player->img->h/2; } // if we've done at least one revolution and moving right... if (s->rotated >= 360 * (M_PI/180)) { if (s->xs > abs(s->ys)) { // level clear! levelcomplete = LV_NEXTLEV; return B_TRUE; } } } else if ((levelcomplete == LV_INIT) || (levelcomplete == LV_INPROGRESS)) { SDL_Surface *ts; // shrink s->size -= ((double)PCSHRINKSPEED); if (s->size <= 0.1) { s->dead = D_FINAL; } else { ts = rotozoomSurfaceXY(imageset[P_PINKCLOUD].img[F_WALK1],0, s->size, s->size,0); SDL_FreeSurface(s->img); s->img = ts; } // move up slightly s->y -= PCSHRINKSPEED; } } else if (s->id == P_BLACKCLOUD) { if ((player->dead) || (levelcomplete)) { if ((s->img->h <= 3) || (s->img->w <= 3)) { s->dead = D_FINAL; /* reset hurryup timer */ nexthurryup = gtime + level->hurryuptime; } 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; } } } return B_FALSE; } void dotileeffects(sprite_t *s) { tiletype_t *tt; int finished = B_FALSE; int state = 0; int tilex,tiley; if (iseffect(s->id) || isbullet(s->id)) { return; } if (s->jumping || s->dead || s->caughtby) { return; } /* check where we are */ tt = gettileat(s->x,s->y-2,&tilex,&tiley); // teleporters if ((tt->id == T_TELEPORT) || (tt->id == T_TELEPORT2)) { if (s == player || ismonster(s->id)) { if (s->id != P_BLACKCLOUD) { if (!s->teleporting) { playfx(FX_TELEPORT); s->teleporting = 1; } } } } /* check under us */ tt = gettileat(s->x,s->y+3,&tilex,&tiley); // CHECKS WHICH COUlD APPLY TO TILES UNDER AND SLIGHTLY LEFT/RIGHT OF US while (!finished) { if (tt->id == T_RIGHT) { if (!ismonster(s->id) && !isfruit(s->id)) { if (player->powerup != PW_CLOCK) { movex(s, 1.5); } } finished = B_TRUE; } else if (tt->id == T_LEFT) { if (!ismonster(s->id) && !isfruit(s->id)) { if (player->powerup != PW_CLOCK) { movex(s, -1.5); } } finished = B_TRUE; } else if ((tt->id == T_ICE) || (tt->id == T_ICETOP)) { if (s == player) { if (player->powerup != PW_CLOCK) { if (!s->moved) { movex(s, s->dir*s->speed); s->moved = MV_ICE; } } } finished = B_TRUE; } else if (tt->spikes) { if (!isfruit(s->id) ) { if (!s->invuln) { if (s->id != P_BLACKCLOUD) { die(s); } } } finished = B_TRUE; } else if (tt->id == T_TRAMPUP) { /* tile changes to trampoline down */ if (!isfruit(s->id) && !iseffect(s->id) && !isbullet(s->id)) { // are we on a trampoline already? if (s->ontramp) { // a different one? if ((s->trampx != tilex) || (s->trampy != tiley)) { // if a different one, release it curlevel->map[s->trampy * LEVELW + s->trampx] = getuniq(T_TRAMPUP); drawtile(temps, s->trampx, s->trampy); } } else { // remember we were on it so it can release s->ontramp = B_TRUE; s->trampx = tilex; s->trampy = tiley; // move it down then draw it curlevel->map[tiley*LEVELW+tilex] = getuniq(T_TRAMPDOWN); drawtile(temps, tilex, tiley); } } finished = B_TRUE; } else if (tt->id == T_TRAMPDOWN) { // don't keep checking tiles left/right finished = B_TRUE; } else { if (state == 0) { /* check tile to our right */ tt = gettileat(s->x + s->img->w/2,s->y+3,&tilex,&tiley); state = 1; } else if (state == 1) { /* check tile to our left */ tt = gettileat(s->x - s->img->w/2,s->y+3,&tilex,&tiley); state = 2; } else { finished = B_TRUE; } } } } // initial transition to a new level void drawlevel(void) { int x,y; int dstx,dsty,xdis,ydis; int turns; double pxspeed,pyspeed; SDL_Rect area,dst; int speed = 16; SDL_Surface *playerbg; SDL_Surface *cloudbg; sprite_t tempcloud; sprite_t *cloud; /* 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); SDL_DisplayFormat(temps); // init cloud cloud = &tempcloud; cloud->id = P_PINKCLOUD; cloud->iceimg = NULL; setdefaults(cloud); // redo image cloud->img =rotozoomSurfaceXY(imageset[P_PINKCLOUD].img[F_WALK1],0,1,1,0); // change player to floating image player->img = imageset[player->id].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); SDL_DisplayFormat(playerbg); // create buffer for cloud background cloudbg = SDL_CreateRGBSurface(SDL_SWSURFACE, cloud->img->w, cloud->img->h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask,screen->format->Bmask, screen->format->Amask); SDL_DisplayFormat(cloudbg); // draw the full level onto the 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; // figure out how many loops it will take to scroll to the next level switch (oldexitdir) { case D_LEFT: case D_RIGHT: speed = 16; turns = 640 / speed; break; case D_UP: case D_DOWN: default: speed = 12; turns = 480 / speed; break; } turns -= 2; // just to be safe // figure out how fast player needs to move to get there in time pxspeed = ceil((double)xdis / (double)turns); if (pxspeed < 1) pxspeed = 1; pyspeed = ceil((double)ydis / (double)turns); if (pyspeed < 1) pyspeed = 1; 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,pxspeed,pyspeed); // set cloud location cloud->x = player->x; cloud->y = player->y + (cloud->img->h / 2); // shuffle real screen area.x = 0; area.y = 0; area.w = 640-speed; area.h = 480; dst.x = speed; 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 = speed; 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); grabbehind(cloud, cloudbg); // draw player drawsprite(player); drawsprite(cloud); // update screen SDL_GL_SwapBuffers(); SDL_UpdateRect(screen, 0,0,640,480); SDL_framerateDelay(&manager); // 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 ); area.x = cloud->x - cloud->img->w/2; area.y = cloud->y - cloud->img->h; area.w = 0; area.h = 0; SDL_BlitSurface(cloudbg, 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,pxspeed,pyspeed); // set cloud location cloud->x = player->x; cloud->y = player->y + (cloud->img->h / 2); // shuffle real screen area.x = 0; area.y = 0; area.w = 640; area.h = 480-speed; dst.x = 0; dst.y = speed; 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 = speed; 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); grabbehind(cloud, cloudbg); // draw player drawsprite(player); drawsprite(cloud); // update screen SDL_GL_SwapBuffers(); SDL_UpdateRect(screen, 0,0,640,480); SDL_framerateDelay(&manager); // 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 ); area.x = cloud->x - cloud->img->w/2; area.y = cloud->y - cloud->img->h; area.w = 0; area.h = 0; SDL_BlitSurface(cloudbg, 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,pxspeed,pyspeed); // set cloud location cloud->x = player->x; cloud->y = player->y + (cloud->img->h / 2); // shuffle real screen area.x = 0; area.y = speed; area.w = 640; area.h = 480-speed; 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 = speed; dst.x = 0; dst.y = 480-speed; dst.w = 0; dst.h = 0; SDL_BlitSurface(temps, &area, screen, &dst); // remember area behind player grabbehind(player, playerbg); grabbehind(cloud, cloudbg); // draw player drawsprite(player); drawsprite(cloud); // update screen SDL_GL_SwapBuffers(); SDL_UpdateRect(screen, 0,0,640,480); SDL_framerateDelay(&manager); // 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 ); area.x = cloud->x - cloud->img->w/2; area.y = cloud->y - cloud->img->h; area.w = 0; area.h = 0; SDL_BlitSurface(cloudbg, NULL, screen, &area ); } } else { // RIGHT 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,pxspeed,pyspeed); // set cloud location cloud->x = player->x; cloud->y = player->y + (cloud->img->h / 2); // shuffle real screen area.x = speed; area.y = 0; area.w = 640-speed; 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 = speed; area.h = 480; dst.x = 640-speed; dst.y = 0; dst.w = 0; dst.h = 0; SDL_BlitSurface(temps, &area, screen, &dst); // remember area behind player grabbehind(player, playerbg); grabbehind(cloud, cloudbg); // draw player drawsprite(player); drawsprite(cloud); // update screen SDL_GL_SwapBuffers(); SDL_UpdateRect(screen, 0,0,640,480); SDL_framerateDelay(&manager); // 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 ); area.x = cloud->x - cloud->img->w/2; area.y = cloud->y - cloud->img->h; area.w = 0; area.h = 0; SDL_BlitSurface(cloudbg, NULL, screen, &area ); } } SDL_FreeSurface(playerbg); SDL_FreeSurface(cloudbg); SDL_FreeSurface(cloud->img); levelcomplete = B_FALSE; } double getspeed(sprite_t *s ) { int id = s->id; double speed = 1; if (s == 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 = 2; else speed = 1.5; } else if (id == P_KINGRAT) { speed = 1.5; } if (isinwater(s)) speed /= 2; if ((player->powerup == PW_SPRAYUP) || (player->powerup == PW_SPRAYDOWN)) { if (s != player) { 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 drawbosshealth(void) { int i; SDL_Rect area; int healthframe; int maxhealth; maxhealth = getbosshealth(boss->id); area.x = (640/2) - maxhealth*(healthbar[0]->w+HEALTHBARGAP); area.y = HEALTHBARY; area.w = 0; area.h = 0; // for each health point, draw two bars for (i = 0; i < boss->lives; i++) { if (i < (maxhealth/8)) { healthframe = HF_RED; } else if (i <= (maxhealth/3)) { healthframe = HF_YELLOW; } else { healthframe = HF_GREEN; } SDL_BlitSurface(healthbar[healthframe], NULL, screen, &area); area.x += (healthbar[healthframe]->w + HEALTHBARGAP); SDL_BlitSurface(healthbar[healthframe], NULL, screen, &area); area.x += (healthbar[healthframe]->w + HEALTHBARGAP); } } void drawscore(void) { SDL_Surface *score; SDL_Rect area; int i; addcommas(tempm, player->score); /* shadow */ score = TTF_RenderText_Solid(font[TEXTSIZE_SCORE], 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[TEXTSIZE_SCORE], tempm, red); area.x = 20; area.y = 5; area.w = 0; area.h = 0; SDL_BlitSurface(score, NULL, screen, &area); SDL_FreeSurface(score); // lives // lives - show 1 less than lives area.x = 20; area.y = 25; area.w = 0; area.h = 0; for (i = 1; i < player->lives; i++) { SDL_BlitSurface(head, NULL, screen, &area); area.x += (head->w + 3); } // level # sprintf(tempm, "Level %d-%d",curworld, curlevelnum); /* shadow */ score = TTF_RenderText_Solid(font[TEXTSIZE_SCORE], tempm, black); area.x = 320-(score->w/2)-2; area.y = 7; area.w = 0; area.h = 0; SDL_BlitSurface(score, NULL, screen, &area); SDL_FreeSurface(score); /* score */ score = TTF_RenderText_Solid(font[TEXTSIZE_SCORE], tempm, white); area.x = 320-(score->w/2); 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); // make sure it's on the screen (leave space for border) if (t->x - (t->img->w / 2) < 2) { // left t->x = 2 + (t->img->w/2); } if (t->x + (t->img->w / 2) > 640-2) { // right t->x = 640-2 - (t->img->w/2); } if (t->y - (t->img->h / 2) < 2) { // top t->y = 2 + (t->img->h/2); } if (t->y + (t->img->h / 2) > 480-2) { // bottom t->y = 480-2 - (t->img->h/2); } /* 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; int xx; 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; xx = s->x + s->netdir*s->netlen; drawline16(screen,sx,s->nety,xx,yy,white); // add sparkle xx = s->x + s->netdir*s->netlen; addsprite(P_SPARKLE, xx + (rand() % 14) - 7, yy + (rand() % 8) - 4, "sparkle"); } //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; int ii; 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); // add sparkles for (ii = 0 ; ii < player->netmax; ii++) { addsprite(P_SPARKLE, s->netxstart + (rand() % 8) - 4, s->netystart + (rand() % 8) - 4, "sparkle"); } } } 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 isladder(int tid) { switch (tid) { case T_LADDER: case T_LADDERTOP: return B_TRUE; } return B_FALSE; } int isonladder(sprite_t *s) { tiletype_t *tthere; tthere = gettileat(s->x,s->y, NULL,NULL); if (isladder(tthere->id)) { 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) { return isinwaterpoint(s->x, s->y - s->img->h/2); } int isinwaterpoint(int x, int y) { tiletype_t *tt; tt = gettileat(x, y, 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/3, s->y - s->img->h,NULL,NULL); if (tt->solid) return B_TRUE; tt = gettileat(s->x - s->img->w/3, 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 (!s->swimming) { 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; } } } //if (s->falling && s->id == P_KINGRAT) { if (s->id == P_KINGRAT) { 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)) { if (s->dropping && (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; tiletype_t *tt; int tilex,tiley; if (s->id == P_PINKCLOUD) return; // only player can move if you have a clock if (player->powerup == PW_CLOCK) { if (!iseffect(s->id) && (s != player) && !s->caughtby && !s->dead) { return; } } // if we were on a trampoline and are now not, it releases */ tt = gettileat(s->x,s->y,&tilex,&tiley); if (s->ontramp) { if (s->trampy != tiley) { // change tile type curlevel->map[s->trampy * LEVELW + s->trampx] = getuniq(T_TRAMPUP); drawtile(temps, s->trampx, s->trampy); // update sprite settings s->ontramp = B_FALSE; s->trampx = -1; s->trampy = -1; } } if (s->dead) return; if (s->flies && !s->iced) return; // no gravity if you fly, but ice cancels flying if (iseffect(s->id)) return; if (isbullet(s->id)) return; //if (isonladder(s) && !s->falling && !s->jumping) { /* if (isonladder(s) ) { s->falling = B_FALSE; return; } */ // update flashing bosses if ((s == boss) && (s->angry)) { s->angry--; } // iced sprites can't jump if (s->iced) { s->jumping = B_FALSE; } // update water stats if (s == player || ismonster(s->id)) { if (isinwater(s)) { if (!s->swimming) { s->swimming = B_TRUE; playfx(FX_SPLASH); if (s == player) adjustx(s, F_SWIM1); else adjustx(s, F_WALK1); } } else { if (s->swimming) { s->swimming = B_FALSE; } } } if (s->climbing) { int attop = B_FALSE; // check if we are at the top of our ladder //tt = gettileat(s->x, s->y - s->img->h-1,NULL,NULL); if (!isonladder(s)) { attop = B_TRUE; } //if (isonground(s)) { if (isongroundpoint(s, s->x, s->y)) { if (!isongroundpoint(s, s->x, s->y-1)) { attop = B_TRUE; attop = B_TRUE; } } if (attop) { s->climbing = B_FALSE; } else { // don't do any more checking 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 = B_FALSE; s->falling = B_TRUE; s->fallspeed = 0; } } // handle ring if (s == player) { if (player->powerup == PW_RINGJUMP) { if (timer % 2 == 0) { int xx,yy; // add sparkle xx = player->x + (rand() % player->img->w) - (player->img->w/2); yy = player->y - (rand() % (player->img->h/2)); addsprite(P_SPARKLE, xx, yy, "sparkle"); // gain points addscore(player, 20); } } } /* have we hit a roof ? */ /* can jump through one tile, but not two or more */ if (isroofabove(s) && isroofnabove(s,2)) { if (s->id != P_KINGRAT) { // king rat can't hit roof /* stop jumping */ s->jumping = B_FALSE; s->falling = B_TRUE; s->fallspeed = 0; } } } else { // not jumping int ontheground; ontheground = isonground(s); if (ontheground) { if (s->falling && s->iced) { // when an iced monster hits the ground, it smashes s->willbecome = P_DIAMOND; playfx(FX_ICEBREAK); die(s); } if ((s->id == P_KINGRAT) && (s->timer1 == KRS_FALL)) { // special case // king rat drops until he is at player height if ((player->dead) || (s->y < player->y)) { // above player ontheground = B_FALSE; } else { // above player s->dropping = B_FALSE; s->falling = B_FALSE; s->climbing = B_FALSE; } } else { // everyone else s->dropping = B_FALSE; s->falling = B_FALSE; s->climbing = B_FALSE; } } // don't use an ELSE here because king rat needs to fall through if (!ontheground) { //if ((s->id == P_KINGRAT) && ((s->timer1 == KRS_CHARGE) || (s->timer1 == KRS_WALK))) { // do nothing //} else { if (s->falling == B_FALSE) { s->fallspeed = 1; } s->falling = B_TRUE; if (isinwater(s) && !s->iced) { 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 { if (s->netlen <= 0) { s->netting = 0; for (s2 = sprite ; s2 ; s2 = s2->next) { if ((s2->caughtby == s) && (s2->caughtstate == C_NETTING)) { s2->caughtstate = C_NETTED; } } } } } } else if (s->slamming) { int netx,nety; double dist; int hitwall = B_FALSE; s->slamangle += (10 * (M_PI/180)); dist = (s->slamangle * (180/M_PI))/2; netx = s->x + cos(s->slamangle-(180*(M_PI/180)))*dist*s->dir; nety = s->y + sin(s->slamangle-(180*(M_PI/180)))*dist; if ((netx >= (640-TILEW)) || (netx <= TILEW)) { hitwall = B_TRUE; } // hit a boss? if (boss) { if ( (netx >= boss->x-(boss->img->w/2)) && (netx <= boss->x+(boss->img->w/2))) { if ((nety >= boss->y - boss->img->h) && (nety <= boss->y)) { hitwall = B_TRUE; } } } if (s->slamangle >= (180 * (M_PI/180)) || hitwall) { /* finished slamming */ int xdiff,ydiff,xnet = 0,ynet = 0; int pointsinc = 250; int psize = 6; int gotsomething = B_FALSE; int hitboss = B_FALSE; int macex,macey; int numcaught = 0; s->slamming = 0; /* reset fruit type counter */ curfruittype = 0; // if we have a mace, add an explosion and play a thump sound if (player->powerup == PW_MACE) { // play sound playfx(FX_MACE); // find location of mace for (s2 = sprite; s2 ; s2 = s2->next) { if (s2->id == P_MACE) { break; } } if (!s2) { // should never happen macex = 0; macey = 0; } else { int xx,yy; macex = s2->x; macey = s2->y - s2->img->h/2; // add explosion //puffin(-1, macex, macey, "nothing", 0); addsprite(P_SMASH, macex, macey, "smash"); addsprite(P_SMASH, macex+TILEW, macey, "smash"); addsprite(P_SMASH, macex-TILEW, macey, "smash"); addsprite(P_SMASH, macex, macey+TILEH, "smash"); addsprite(P_SMASH, macex, macey-TILEH, "smash"); for (yy = 1; yy <= MACEEXPY; yy++) { for (xx = 1; xx <= MACEEXPX; xx++) { addsprite(P_SMASH, macex+xx*TILEW, macey-yy*TILEH, "smash"); addsprite(P_SMASH, macex-xx*TILEW, macey-yy*TILEH, "smash"); addsprite(P_SMASH, macex+xx*TILEW, macey+yy*TILEH, "smash"); addsprite(P_SMASH, macex-xx*TILEW, macey+yy*TILEH, "smash"); //puffin(-1, macex+xx*TILEW, macey-yy*TILEH, "nothing", 0); //puffin(-1, macex-xx*TILEW, macey-yy*TILEH, "nothing", 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; numcaught++; // used later in slam code tt = gettileat(s2->x,s2->y+2,NULL,NULL); /* if on ground or hitting a wall, monster dies */ if ((hitwall) || (tt == NULL) || (tt->solid)) { /* will become a fruit when it finishes dying */ if (boss) { s2->willbecome = -1; } else { 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; } } } // if we have a powerup, centre of net is the mace position if (player->powerup == PW_MACE) { xnet = macex; ynet = macey; } // only check for hitting something if we already had a monster caught, // or we have a mace if (gotsomething || (player->powerup == PW_MACE)) { /* kill anything we hit */ for (s2 = sprite; s2 ; s2 = s2->next) { if ((s2->caughtby != s) && (!s2->dead) && (ismonster(s2->id))) { int xthresh,ythresh; xdiff = s2->x - xnet; if (xdiff < 0) xdiff =-xdiff; ydiff = (s2->y - s2->img->h/2) - ynet; if (ydiff < 0) ydiff =-ydiff; /* if (player->powerup == PW_MACE) { xthresh = TILEW*MACEEXPX*2; ythresh = TILEW*MACEEXPY*2; } else { */ xthresh = s2->img->w; ythresh = s2->img->h; //} if ((xdiff <= xthresh) && (ydiff <= ythresh)) { if (s2 == boss) { int n; // flash white, longer for each monster we were // holding s2->angry = BOSSFLASHTIME*numcaught; // lose health s2->lives -= numcaught; // if no health left < 0, it dies if (s2->lives <= 0) { sprite_t *s3; // todo: change to big fruit! s2->willbecome = P_DIAMOND; // add puffs for (n = 0; n < 30; n++) { puffin(-1, s2->x - (s2->img->w/2) + (rand() % s2->img->w), s2->y - (rand() % s2->img->h), "nothing", rand() % 80); } playfx(FX_BOSSDIE); die(s2); pointsinc *= 2; pointsinc = 64000; // hardcode for a boss psize += 10; gotsomething++; // kill everything else too for (s3 = sprite; s3 ; s3=s3->next) { if (ismonster(s3->id) && !s3->dead) { s3->willbecome = P_DIAMOND; if (s3->caughtby) { s3->caughtby = NULL; player->netcaught--; } die(s3); } } hitboss = B_TRUE; } else { // remaining health playfx(FX_BOSSHIT); hitboss = B_TRUE; } } else if (s2->id != P_BLACKCLOUD) { // non bosses /* dies and becomes a powerup */ // if we were holding something, we can get a powerup // if we used a mace, it becomes a diamon. if (gotsomething) { if (boss) { // no fruits on boss levels s2->willbecome = -1; } else { // if player isn't fast, give a speed. if (player->speed != PLAYERFAST) { s2->willbecome = P_SPEED; } else { // otherwise use normal powerup counter s2->willbecome = poweruptypes[curpoweruptype]; if (poweruptypes[++curpoweruptype] == -1) { curpoweruptype = 0; } } } } else if (player->powerup == PW_MACE) { s2->willbecome = P_DIAMOND; } else { // should never happen /* 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; gotsomething++; } } } } } if (!hitboss) { 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); addoutlinetext(xnet,ynet-TILEH, psize, tempm, &white,&black,POINTSDELAY); /* give points to player */ //s->score += pointsinc; addscore(s, pointsinc); } } // end if slamangle > 180degrees } // end if slamming } 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 to E/W */ 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; } // if falling, check the tile directly to our SW/SEtoo */ if (s->falling) { newx = s->x + (amtdir*TILEW/2); newy = cury; 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 = MV_WALK; // rings if (s == player) { if (player->powerup == PW_RINGWALK) { if (isonground(player)) { int xx,yy; // add sparkle xx = player->x + (rand() % player->img->w) - (player->img->w/2); yy = player->y - (rand() % (player->img->h/2)); addsprite(P_SPARKLE, xx, yy, "sparkle"); // gain points addscore(player, 3); } } } 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 = PLAYERFAST; addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, "Speed up!", &white,&black,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); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); return B_TRUE; } else if (s->id == P_BIGNET) { playfx(FX_POWERUP); player->netbig = B_TRUE; sprintf(tempm, "Big net!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); return B_TRUE; } else if (s->id == P_TROPHY) { // all powerups playfx(FX_POWERUP); player->netmax = 4; // all nets player->netbig = B_TRUE; // big net player->speed = 2; // fast sprintf(tempm, "Full power!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); return B_TRUE; } else if (s->id == P_BELL) { // all powerups playfx(FX_BELL); // different sound effect player->hasbell = B_TRUE; sprintf(tempm, "Powerup Detector!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); return B_TRUE; } else if (s->id == P_RINGGOLD) { // points for walking playfx(FX_POWERUP); player->powerup = PW_RINGWALK; sprintf(tempm, "Walk Ring!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); return B_TRUE; } else if (s->id == P_RINGSILVER) { // points for walking playfx(FX_POWERUP); player->powerup = PW_RINGJUMP; sprintf(tempm, "Jump Ring!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); return B_TRUE; } else if (s->id == P_MACEPOWERUP) { playfx(FX_POWERUP); player->powerup = PW_MACE; sprintf(tempm, "Mace Slam!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); return B_TRUE; } else if (s->id == P_BOXING) { playfx(FX_POWERUP); player->powerup = PW_BOXING; sprintf(tempm, "Boxing Glove!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); return B_TRUE; } else if (s->id == P_HELMET) { int xx,yy; playfx(FX_ARMOR); player->id = P_ARMOUR; // change how the player looks player->armour = B_TRUE; sprintf(tempm, "Armour!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); // add puffs for (xx = s->x - TILEW; xx <= s->x + TILEW; xx += TILEW) { for (yy = s->y - TILEW*2; yy <= s->y; yy += TILEH) { puffin(-1, xx, yy, "nothing", 0); } } return B_TRUE; } else if (s->id == P_GEMBOOST) { playfx(FX_POWERUP); if (player->gemboost <= 1) { player->gemboost = 2; } else { player->gemboost = 3; } sprintf(tempm, "Bonus x%d!",player->gemboost); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); return B_TRUE; } else if (s->id == P_FTODIAMOND) { sprite_t *s2, *nexts; // convert all flowers to diamonds playfx(FX_MORPH); sprintf(tempm, "Make diamonds!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); for (s2 = sprite; s2 ; s2 = nexts) { nexts = s2->next; if (isflower(s2->id)) { puffin(-1, s2->x, s2->y, "nothing", 0); // replace with a diamond s2->id = P_DIAMOND; s2->score = getpoints(P_DIAMOND); sprintf(s2->name, "made_diamond"); } } playfx(FX_MORPH); return B_TRUE; } else if (s->id == P_FTOGEM) { sprite_t *s2, *nexts; int howmany; int puffdelay; int gemtype = P_GEMYELLOW; int xx; tiletype_t *tt; // convert all flowers to gems playfx(FX_MORPH); sprintf(tempm, "Make gems!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); for (s2 = sprite; s2 ; s2 = nexts) { nexts = s2->next; if (isflower(s2->id)) { puffin(-1, s2->x, s2->y, "nothing", 0); // replace with a diamond s2->id = flowertogem(s2->id); s2->score = getpoints(s2->id); sprintf(s2->name, "made_gem"); } } // and also make a stream of gems underneath us howmany = (STREAMWID+1)*3; // 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; } switch (puffdelay % 3) { case 0: gemtype = P_GEMYELLOW; break; case 1: gemtype = P_GEMRED; break; case 2: gemtype = P_GEMPURPLE; break; } /* create a gem */ puffin(gemtype, xx, s->y, "gem", puffdelay); 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; } switch (puffdelay % 3) { case 0: gemtype = P_GEMYELLOW; break; case 1: gemtype = P_GEMRED; break; case 2: gemtype = P_GEMPURPLE; break; } /* create a gem */ puffin(gemtype, xx, s->y, "gem", puffdelay); puffdelay += 1; } return B_TRUE; } else if (s->id == P_CLOCK) { // Freeze monsters playfx(FX_POWERUP); player->powerup = PW_CLOCK; clocktime = CLOCKTIME; sprintf(tempm, "Freeze time!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); // pause music Mix_PauseMusic(); return B_TRUE; } else if (s->id == P_SNOWMAN) { // ice playfx(FX_FREEZE); sprintf(tempm, "Blizzard!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); if (!curlevel->iced) { curlevel->iced = ICE_INPROGRESS; curlevel->icey = 0; } return B_TRUE; } else if (s->id == P_SPRAY) { // flyspray playfx(FX_SPRAY); sprintf(tempm, "Fly Spray!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); player->powerup = PW_SPRAYUP; sprayalpha = 0; return B_TRUE; } else if (s->id == P_CANNONPOWERUP) { // flyspray playfx(FX_POWERUP); sprintf(tempm, "Fusion Cannon"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); player->powerup = PW_CANNON; puffin(P_CANNON, player->x, player->y,"cannon", 0); return B_TRUE; } else if (s->id == P_BOMB) { sprite_t *s2, *nexts; // make the screen shake player->powerup = PW_BOMB; player->timer1 = BOMBSHAKETIME; // kill all monsters playfx(FX_BOOM); sprintf(tempm, "KABOOM!!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_BOMB, tempm,&red,&yellow,POINTSDELAY); for (s2 = sprite; s2 ; s2 = nexts) { nexts = s2->next; if (isbullet(s2->id)) { s2->dead = D_FINAL; } else if (ismonster(s2->id)) { s2->willbecome = P_DIAMOND; if (s2->caughtby) { s2->caughtby = NULL; player->netcaught--; } die(s2); } } return B_TRUE; } else if (s->id == P_SHIELD) { playfx(FX_POWERUP); sprintf(tempm, "Shield!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY); // temp invincibility player->invuln = SHIELDTIME; return B_TRUE; } else if (s->id == P_HELP) { playfx(FX_POWERUP); // TODO: move other HELP text around if need be! addoutlinetext(320,240,TEXTSIZE_HELP, s->name, &white,&black,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) * player->gemboost; 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); 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); puffdelay += 1; } playfx(FX_BONUS); sprintf(tempm, "BONUS!"); addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_BONUS, tempm,&white,&black,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)) { if ( pl->climbing || 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"); loadfx(FX_MORPH, "morph.wav"); loadfx(FX_BOOM, "boom.wav"); loadfx(FX_SPRING, "spring.wav"); loadfx(FX_TELEPORT, "teleport.wav"); loadfx(FX_SPLASH, "splash.wav"); loadfx(FX_MACE, "mace.wav"); loadfx(FX_LIFE, "life.wav"); loadfx(FX_GAMEOVER, "gameover.wav"); loadfx(FX_OW, "ow.wav"); loadfx(FX_BELL, "bell.wav"); loadfx(FX_CLOCK, "clock.wav"); loadfx(FX_ARMOR, "armor.wav"); loadfx(FX_FREEZE, "freeze.wav"); loadfx(FX_ICEBREAK, "icebreak.wav"); loadfx(FX_BOSSWINDUP, "chargewait.wav"); loadfx(FX_BOSSCHARGE, "bosscharge.wav"); loadfx(FX_BOSSDIE, "bossdie.wav"); loadfx(FX_BOSSHIT, "bosshit.wav"); loadfx(FX_BOSSWALL, "bosswall.wav"); loadfx(FX_SPRAY, "spray.wav"); loadfx(FX_CANNON, "fusion.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, double xspeed, double yspeed) { if (p->x < dstx) { p->x += xspeed; if (p->x > dstx) p->x = dstx; } if (p->x > dstx) { p->x -= xspeed; if (p->x < dstx) p->x = dstx; } if (p->y < dsty) { p->y += yspeed; if (p->y > dsty) p->y = dsty; } if (p->y > dsty) { p->y -= yspeed; 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; } 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"); i++; } printf("--------\n"); printf("Total monsters: %d\n",mcount); printf("\n\n"); } void addoutlinetext(int x, int y, int size, char *msg, SDL_Color *col, SDL_Color *bgcol, int delay) { addtext(x-1,y,size,msg,bgcol,delay); // outline addtext(x-1,y-1,size,msg,bgcol,delay); // outline addtext(x,y-1,size,msg,bgcol,delay); // outline addtext(x+1,y-1,size,msg,bgcol,delay); // outline addtext(x+1,y,size,msg,bgcol,delay); // outline addtext(x+1,y+1,size,msg,bgcol,delay); // outline addtext(x,y+1,size,msg,bgcol,delay); // outline addtext(x-1,y+1,size,msg,bgcol,delay); // outline addtext(x,y,size,msg,col,delay); // main text } char *addcommas(char *buffer, int num) { char tempbuf[MIDBUFLEN]; char *p; char *p2; int count; sprintf(tempbuf, "%d",num); p2 = buffer; p = tempbuf; if (strlen(tempbuf) <= 3) { strcpy(buffer,tempbuf); return buffer; } else { count = ((strlen(tempbuf)-1) % 3)+1; } for (p = tempbuf; *p; p++) { if (count == 0) { count = 3; *p2 = ','; p2++; } *p2 = *p; p2++; count--; } *p2 = '\0'; return buffer; } void addscore(sprite_t *s, int amt) { int oldscore; oldscore = s->score; s->score += amt; // each multiple of 100,000 if (s == player) { if ((s->score / 100000) > (oldscore / 100000)) { playfx(FX_LIFE); s->lives++; addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_LIFE, "Extra life!",&green,&black,LIFEDELAY); } } } void doice(void) { int yy,xx,changed; sprite_t *s; int l2id; // just in case if (!curlevel->iced) { curlevel->iced = ICE_INPROGRESS; } // slowly change a level to ice //if (timer % ICESPEED == 0) { xx = 0; for (yy = curlevel->icey; yy >= 0; yy--) { // finished? if ((xx == LEVELW-1) && (yy == LEVELH-1)) { curlevel->iced = ICE_COMPLETE; break; } // make sure tile is valid if ((yy >= 0) && (yy < LEVELH) && (xx >= 0) && (xx < LEVELW)) { tiletype_t *tt; changed = B_FALSE; // if not already a second layer here... //l2id = curlevel->map2[yy*LEVELW+xx]; tt = gettile(curlevel->map2[yy*LEVELW+xx]); l2id = tt->id; if (l2id == T_BLANK) { // add ice layer tt = gettile(curlevel->map[yy*LEVELW+xx]); switch (tt->id) { case T_FULL: curlevel->map2[yy*LEVELW+xx] = getuniq(T_ICE); changed = B_TRUE; break; case T_LAND: curlevel->map2[yy*LEVELW+xx] = getuniq(T_ICETOP); changed = B_TRUE; break; } } else { // certain l2 ids can still be replaced switch (l2id) { case T_FULL: curlevel->map2[yy*LEVELW+xx] = getuniq(T_ICE); changed = B_TRUE; break; case T_LAND: printf("bb\n"); curlevel->map2[yy*LEVELW+xx] = getuniq(T_ICETOP); changed = B_TRUE; break; } } if (changed) { drawtile(temps, xx, yy); } } xx++; if (xx >= LEVELW) break; } // ice any monsters for (s = sprite; s ; s = s->next) { if (ismonster(s->id)) { if ((s->x <= (xx*TILEW)) && (s->y <= (curlevel->icey*TILEH))) { if (!s->iced && !s->dead && !s->caughtby) { // ice it! s->iced = B_TRUE; s->jumping = B_FALSE; } } } } // increase icey for next time curlevel->icey++; } void adjustx(sprite_t *s,int framenum) { int newx,newy,diff; tiletype_t *tt; diff = (imageset[s->id].img[framenum]->w - s->img->w) +1 ; newy = s->y-TILEH; // check RIGHT newx = s->x + (s->img->w/2); tt = gettileat(newx,newy,NULL,NULL); if (tt->solid == S_SOLID) { s->x -= diff; } // check LEFT newx = s->x - (s->img->w/2); tt = gettileat(newx,newy,NULL,NULL); if (tt->solid == S_SOLID) { s->x += diff; } } /* check for death & update movement status*/ void checksprites(void) { sprite_t *s, *nextsprite; for (s = sprite ; s ; s = nextsprite) { s->moved = MV_NONE; nextsprite = s->next; if (s->dead == D_FINAL) { if (s == player) { if (player->lives > 0) { /* 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 { if (levelcomplete == LV_GAMEOVER) { // TODO: Wait until "game over" text is gone, then slowly fade out the screen // then show hiscores // then back to title screen } else { addoutlinetext(320,240,TEXTSIZE_GAMEOVER,"Game Over",&red,&black,GAMEOVERDELAY); levelcomplete = LV_GAMEOVER; stopmusic(); playfx(FX_GAMEOVER); } } } else { if ((s->id == P_PUFF) && (s->timer1 != 999)) { printf("puff deid without changing!!!\n"); } killsprite(s); // check for level completion checklevelend(); } } } // check if we've hit the cloud if (levelcomplete == LV_NEXTLEV) { nextlevel(); } } void moveallsprites(void) { sprite_t *s; for (s = sprite; s ; s = s->next) { movesprite(s); } } void checkcollideall(void) { sprite_t *s; /* check collisions for player and effects */ for (s = sprite ; s ; s = s->next) { if ((s == player) || needscollisions(s->id)) { checkcollide(s); } } } void drawallsprites(void) { sprite_t *s; /* draw non-puff sprites */ for (s = sprite ; s ; s = s->next) { if (s->id != P_PUFF) drawsprite(s); } /* draw puff sprites */ for (s = sprite ; s ; s = s->next) { if (s->id == P_PUFF) drawsprite(s); } }