diff --git a/data/sounds/alarm.wav b/data/sounds/alarm.wav new file mode 100644 index 0000000..d6abc61 Binary files /dev/null and b/data/sounds/alarm.wav differ diff --git a/data/sounds/gun.wav b/data/sounds/gun.wav new file mode 100644 index 0000000..6e0fd6f Binary files /dev/null and b/data/sounds/gun.wav differ diff --git a/data/sprites/gunner.png b/data/sprites/gunner.png new file mode 100644 index 0000000..fe9e24b Binary files /dev/null and b/data/sprites/gunner.png differ diff --git a/defs.h b/defs.h index 299eb42..71076a0 100644 --- a/defs.h +++ b/defs.h @@ -227,7 +227,7 @@ /* enums */ /* sounds */ -#define MAXFX 45 +#define MAXFX 47 #define FX_SHOOT 0 #define FX_SLAM 1 #define FX_KILL 2 @@ -273,6 +273,8 @@ #define FX_SKULL 42 #define FX_1UP 43 #define FX_CATCH 44 +#define FX_GUN 45 +#define FX_ALARM 46 // card suits #define CS_HEART 1 @@ -298,7 +300,7 @@ #define S_SLOPE 2 // Sprite types -#define MAXPTYPES 125 +#define MAXPTYPES 126 #define P_PLAYER 0 #define P_RAT 1 #define P_CHEESE 2 @@ -428,7 +430,8 @@ #define P_SKULL 121 #define P_WINGLEFT 122 #define P_WINGRIGHT 123 -#define P_PLANT 124 +#define P_PLANT 124 +#define P_GUN 125 // cards #define CARDFONTX 4 @@ -462,9 +465,13 @@ #define PW_CANNONFIRE 10 // fusion cannon firing #define PW_PHONE 11 // skip level #define PW_ACCORDION 12 // super long net +#define PW_GUNNER 13 // machine gunner // "virtual" powerup for bosses #define PW_RATSHAKE 20 // shake screen horizontally +#define GUNNERSPEED 2.5 // speed crosshair moves in gunner mode +#define GUNNERDELAY 10 // how fast gunner powerup shoots + // Frame names #define F_WALK1 0 #define F_JUMP 1 @@ -793,7 +800,7 @@ imageset_t imageset[MAXPTYPES]; extern SDL_Color black; extern SDL_Surface *screen, *temps, *levelbg, *head, *head5, *icecube; extern SDL_Surface *healthbar[]; -extern SDL_Surface *greenbox; +extern SDL_Surface *greenbox, *redbox; extern sprite_t *sprite, *lastsprite, *player, *boss, *mask; extern level_t *curlevel; extern tiletype_t fakeblock; diff --git a/globals.h b/globals.h index 32592c5..bdce852 100644 --- a/globals.h +++ b/globals.h @@ -27,6 +27,7 @@ SDL_Surface *screen; // the actual video screen SDL_Surface *head,*head5; // imgs in corner showing number of lives SDL_Surface *icecube; // overlaid on frozen monsters SDL_Surface *greenbox; // for fly spray effect +SDL_Surface *redbox; // for gunner SDL_Surface *healthbar[HEALTHFRAMES]; // for boss health sprite_t *sprite; // head of sprite linked list sprite_t *lastsprite; // tail of sprite linked list diff --git a/rc.c b/rc.c index f5a3d7e..dc667ff 100644 --- a/rc.c +++ b/rc.c @@ -56,6 +56,11 @@ char tempm[BUFLEN]; int savemap[LEVELW*LEVELH]; int watertime; +double gunorigx; +double gunorigy; +int gundelay; +int guntime; + int playedbell; int clocktime; @@ -354,13 +359,15 @@ int main (int argc, char **argv) { addoutlinetext(320,240,TEXTSIZE_LEVEL,"Level Complete!",&green,&black,LEVELWINDELAY, TT_NORM); levelcomplete = LV_WAIT; playfx(FX_WINLEVEL); - // turn off clock and water powerups + // turn off certain powerups if (player->powerup == PW_CLOCK) { Mix_ResumeMusic(); losepowerup(player); } else if ((curlevel->iced == WATER_INPROGRESS) || (curlevel->iced == WATER_COMPLETE)) { curlevel->iced = B_FALSE; undoflood(); + } else if (player->powerup == PW_GUNNER) { + losepowerup(player); } // kill all cards, so we don't have a pokereffect during endoflevel @@ -442,6 +449,11 @@ int main (int argc, char **argv) { } } } + if (player->powerup == PW_GUNNER) { // red overlay for machine gunner + if (levelcomplete != LV_DOPOKER) { + SDL_BlitSurface(redbox,NULL,screen,NULL); + } + } /********************************************** * Move sprites @@ -733,11 +745,22 @@ int main (int argc, char **argv) { */ flip(); + + /********************************************** + * Timers + */ if (!paused) { if (++timer == 100) timer = 0; } if (toggletimer > 0) toggletimer--; + + if (player->powerup == PW_GUNNER) { + // delay between shots + if (gundelay > 0) { + gundelay--; + } + } fpscount++; tick(); @@ -796,8 +819,9 @@ void tick(void) { } } - // handle clock effect + // handle visual countdown timers if (levelcomplete == LV_INPROGRESS) { + // clock if (player->powerup == PW_CLOCK) { char tempm[SMALLBUFLEN]; // text @@ -837,6 +861,24 @@ void tick(void) { undoflood(); } } + // handle gunner effect + if (player->powerup == PW_GUNNER) { + char tempm[SMALLBUFLEN]; + // text + if (guntime > 0) { + sprintf(tempm, "%d",guntime); + addoutlinetext(320,120,TEXTSIZE_LEVEL, tempm, &red,&black,15, TT_NORM); + } + // never reach hurryup time + resethurryup(curlevel); + + // decrement counter + guntime--; + if (guntime < 0) { + // finished! + losepowerup(player); + } + } } @@ -1595,6 +1637,10 @@ int movesprite(sprite_t *s) { int rv; tiletype_t *tt; + if ((s == player) && (player->powerup == PW_GUNNER)) { + return B_FALSE; + } + if (levelcomplete == LV_INIT) { // most things can't move in this state @@ -1928,9 +1974,11 @@ if (s->id == P_PUFF) printf("PUFF WITH DOOMCOUNT!\n"); // appeared on top of us if ((xdiff <= player->img->w/2 + newsp->img->w/2) && (ydiff <= player->img->h/2 + newsp->img->h/2)) { - // bonus! - getfruit(player, newsp, 4); - addoutlinetext(player->x,player->y - (player->img->h*1.5), TEXTSIZE_MULTI, "Nice catch!", &green,&black,MULTIDELAY, TT_NORM); + if ((!player->dead) && (player->powerup != PW_GUNNER)) { + // bonus! + getfruit(player, newsp, 4); + addoutlinetext(player->x,player->y - (player->img->h*1.5), TEXTSIZE_MULTI, "Nice catch!", &green,&black,MULTIDELAY, TT_NORM); + } } } @@ -3104,6 +3152,13 @@ void dotileeffects(sprite_t *s) { return; } + // no tile efffects for machine gun + if (player->powerup == PW_GUNNER) { + if (s == player) { + return; + } + } + /* check where we are */ tt = gettileat(s->x,s->y-2,&tilex,&tiley); @@ -4396,6 +4451,7 @@ void dogravity(sprite_t *s) { tiletype_t *tt; int tilex,tiley; + if (s->id == P_PINKCLOUD) return; // only player can move if you have a clock @@ -4405,6 +4461,13 @@ void dogravity(sprite_t *s) { } } + // no gravity for player if you have the machine gun + if (player->powerup == PW_GUNNER) { + if (s == player) { + return; + } + } + // if we were on a trampoline and are now not, it releases */ tt = gettileat(s->x,s->y,&tilex,&tiley); if (s->ontramp) { @@ -5467,6 +5530,15 @@ int dofruiteffect(sprite_t *s) { addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY, TT_NORM); player->doublejump = B_TRUE; return B_TRUE; + } else if (s->id == P_GUN) { + playfx(FX_ALARM); + sprintf(tempm, "Machine gunner!"); + gunorigx = player->x; + gunorigy = player->y; + guntime = 10; + gundelay = 0; // used to control shooting speed + player->powerup = PW_GUNNER; + return B_TRUE; } else if (s->id == P_SKULL) { playfx(FX_SKULL); sprintf(tempm, "Power Down!"); @@ -5661,6 +5733,8 @@ int initsound(void) { loadfx(FX_SKULL, "skull.wav"); loadfx(FX_1UP, "1up.wav"); loadfx(FX_CATCH, "catch.wav"); + loadfx(FX_GUN, "gun.wav"); + loadfx(FX_ALARM, "alarm.wav"); // load sound effects for (i = 0; i < MAXFX; i++) { @@ -6126,7 +6200,11 @@ void checkcollideall(void) { /* check collisions for player and effects */ for (s = sprite ; s ; s = s->next) { - if ((s == player) || needscollisions(s->id)) { + if (s == player) { + if (player->powerup != PW_GUNNER) { + checkcollide(s); + } + } else if (needscollisions(s->id)) { checkcollide(s); } } @@ -7220,106 +7298,138 @@ if (cheat) { Player movement ************************************************************/ if ((!paused) && (!levelcomplete != LV_DOPOKER) && (levelcomplete != LV_CLOUDLOOP)) { - if ((!player->dead) && (!player->teleporting)) { - int moveok = B_FALSE; - if (player->climbing) { - tiletype_t *tt; - // can only move left/right if we're at the bottom of a ladder - tt = gettileat(player->x,player->y, NULL,NULL); - if (tt->solid) { - moveok = B_TRUE; + if (player->powerup == PW_GUNNER) { + // move crosshairs + if (keydown(SDLK_RIGHT)) { + if (player->x < 640-(TILEW/2)) { + player->x += GUNNERSPEED; + } + } + if (keydown(SDLK_LEFT)) { + if (player->x > (TILEW/2)) { + player->x -= GUNNERSPEED; + } + } + if (keydown(SDLK_DOWN)) { + if (player->y < 480-(TILEH/2)) { + player->y += GUNNERSPEED; } - tt = gettileat(player->x,player->y+TILEH, NULL,NULL); - if (tt->solid ) { - moveok = B_TRUE; - } - } else moveok = B_TRUE; - - if (moveok) { - if (keydown(SDLK_RIGHT)) { - if (canmove(player)) { - movex(player, getspeed(player)); - } - if (canturn(player)) { - player->dir = D_RIGHT; - } - } else if (keydown(SDLK_LEFT)) { - if (canmove(player)) { - movex(player, -getspeed(player)); - } - if (canturn(player)) { - player->dir = D_LEFT; - } - } } - if (keydown(SDLK_UP)) { - if (!player->netting && !player->slamming && !player->jumping) { - if (player->climbing) { - int ladderx = isladderabove(player); - // if tile above is non-solid, or a ladder - if (ladderx || !isroofabove(player)) { - // lock to ladder + if (player->y > (TILEH/2)) { + player->y -= GUNNERSPEED; + } + } + if (keydown(SDLK_z)) { + // shoot - add explosion + if (gundelay == 0) { + playfx(FX_GUN); + addsprite(P_SMASH, player->x, player->y+(TILEH/2), "gunexplosion"); + gundelay = GUNNERDELAY; + } + } + } else { + if ((!player->dead) && (!player->teleporting)) { + int moveok = B_FALSE; + if (player->climbing) { + tiletype_t *tt; + // can only move left/right if we're at the bottom of a ladder + tt = gettileat(player->x,player->y, NULL,NULL); + if (tt->solid) { + moveok = B_TRUE; + } + tt = gettileat(player->x,player->y+TILEH, NULL,NULL); + if (tt->solid ) { + moveok = B_TRUE; + } + } else moveok = B_TRUE; + + if (moveok) { + if (keydown(SDLK_RIGHT)) { + if (canmove(player)) { + movex(player, getspeed(player)); + } + if (canturn(player)) { + player->dir = D_RIGHT; + } + } else if (keydown(SDLK_LEFT)) { + if (canmove(player)) { + movex(player, -getspeed(player)); + } + if (canturn(player)) { + player->dir = D_LEFT; + } + } + } + + if (keydown(SDLK_UP)) { + if (!player->netting && !player->slamming && !player->jumping) { + if (player->climbing) { + int ladderx = isladderabove(player); + // if tile above is non-solid, or a ladder + if (ladderx || !isroofabove(player)) { + // lock to ladder + if (ladderx) { + player->x = ladderx; // lock to ladder + } + // continue climbing + player->y -= getspeed(player); + player->jumping = 0; + player->falling = 0; + player->climbing = B_TRUE; + player->moved = MV_WALK; + } + } else {// not climbing + int ladderx = isladderabove(player); if (ladderx) { player->x = ladderx; // lock to ladder + // start climbing + player->y -= getspeed(player); + player->jumping = 0; + player->falling = 0; + player->climbing = B_TRUE; + player->moved = MV_WALK; } - // continue climbing - player->y -= getspeed(player); - player->jumping = 0; - player->falling = 0; - player->climbing = B_TRUE; - player->moved = MV_WALK; + } - } else {// not climbing - int ladderx = isladderabove(player); - if (ladderx) { - player->x = ladderx; // lock to ladder - // start climbing - player->y -= getspeed(player); - player->jumping = 0; - player->falling = 0; - player->climbing = B_TRUE; - player->moved = MV_WALK; - } - } - } - } - if (keydown(SDLK_DOWN)) { - if (!player->netting && !player->slamming && !player->jumping) { - int ladderx = isonladder(player); - if (ladderx) { - player->y += getspeed(player); - player->jumping = 0; - player->falling = 0; - player->climbing = B_TRUE; - player->moved = MV_WALK; - // lock player to centre of ladder - player->x = ladderx; - } - } - } - // Jump - if (keydown(SDLK_x)) { - trytojump(player); - } else if (player->jumping && player->doublejump) { - // have to let go of jump button to double jump - if (!player->doublejumpready) { - player->doublejumpready = B_TRUE; - } - } - // Shoot - if (keydown(SDLK_z)) { + } if (keydown(SDLK_DOWN)) { - trytoslam(player); - } else { - trytoshoot(player); + if (!player->netting && !player->slamming && !player->jumping) { + int ladderx = isonladder(player); + if (ladderx) { + player->y += getspeed(player); + player->jumping = 0; + player->falling = 0; + player->climbing = B_TRUE; + player->moved = MV_WALK; + // lock player to centre of ladder + player->x = ladderx; + } + } + } + // Jump + if (keydown(SDLK_x)) { + trytojump(player); + } else if (player->jumping && player->doublejump) { + // have to let go of jump button to double jump + if (!player->doublejumpready) { + player->doublejumpready = B_TRUE; + } + } + // Shoot + if (keydown(SDLK_z)) { + if (keydown(SDLK_DOWN)) { + trytoslam(player); + } else { + trytoshoot(player); + } } - } - // Slam - if (keydown(SDLK_c)) { - trytoslam(player); + // Slam + if (keydown(SDLK_c)) { + trytoslam(player); + } } } } @@ -8635,5 +8745,13 @@ void stopteleporting(sprite_t *s) { } void losepowerup(sprite_t *s) { + if (s->powerup == PW_GUNNER) { + // go back to original position + player->x = gunorigx; + player->y = gunorigy; + // invulnerable for a little while + player->invuln = INVULNTIME/2; + } s->powerup = B_FALSE; + } diff --git a/shared.c b/shared.c index 88e1e8c..cfd37ca 100644 --- a/shared.c +++ b/shared.c @@ -1007,7 +1007,6 @@ int loadimagesets(void) { healthbar[HF_RED] = IMG_Load(tempfile); // green square for flyspray effect - greenbox = SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h, @@ -1016,6 +1015,15 @@ int loadimagesets(void) { SDL_FillRect(greenbox, NULL, SDL_MapRGB(greenbox->format, 0, 150, 0)); SDL_SetAlpha(greenbox, SDL_SRCALPHA,80); + // red square for gunner effect + redbox = SDL_CreateRGBSurface(SDL_SWSURFACE, + screen->w, + screen->h, + screen->format->BitsPerPixel, screen->format->Rmask, + screen->format->Gmask,screen->format->Bmask, 0); + SDL_FillRect(redbox, NULL, SDL_MapRGB(greenbox->format, 150, 0, 0)); + SDL_SetAlpha(redbox, SDL_SRCALPHA,80); + loadspriteimage(P_PLAYER,F_WALK1, "sprites/pdwarf.png"); loadspriteimage(P_PLAYER,F_JUMP, "sprites/pdwarfjump.png"); @@ -1299,6 +1307,9 @@ int loadimagesets(void) { loadspriteimage(P_WINGBOOTS,F_WALK1, "sprites/wingboots.png"); imageset[P_WINGBOOTS].numimages = 1; + loadspriteimage(P_GUN,F_WALK1, "sprites/gunner.png"); + imageset[P_GUN].numimages = 1; + // wings loadspriteimage(P_WINGLEFT,0, "sprites/wingleft0.png"); // standing loadspriteimage(P_WINGLEFT,1, "sprites/wingleft1.png"); // jumping @@ -1532,6 +1543,11 @@ void drawsprite(sprite_t *s) { SDL_Rect area; int frame = 0; + // don't show caught mosnters in gunner mode + if ((s != player) && (s->caughtby == player) && (player->powerup == PW_GUNNER)) { + return; + } + if ((s == player) && (levelcomplete == LV_NEXTLEV)) { frame = F_SHOOT; if (curlevel->exitdir == D_RIGHT) { @@ -1957,6 +1973,7 @@ int isfruit(int id) { case P_CANNONPOWERUP: case P_CLOVER: case P_ACCORDION: + case P_GUN: case P_SKULL: return FT_TEMP; /* flowers */ @@ -2571,7 +2588,7 @@ int loadlevellist(void) { int randompowerup(void) { int num; - num = rand() % 31; + num = rand() % 32; switch (num) { case 0: @@ -2637,6 +2654,8 @@ int randompowerup(void) { return P_WINGBOOTS; case 30: return P_SKULL; + case 31: + return P_GUN; } } @@ -2768,6 +2787,7 @@ void setfruitinfo(void) { setinfo(P_UFO, "UFO", "Calls in a powerful meteor strike!", "ufo.png"); setinfo(P_TAP, "Tap", "The leaky tap will flood the level with water for 20 seconds, allowing you to access hard to reach areas.", "tap.png"); setinfo(P_ACCORDION, "Accordion", "Makes your nets enormous", "accordion.png"); + setinfo(P_GUN, "Gunner", "Temporarily equips you with a super powerful machine gun!", "gunner.png"); setinfo(P_SKULL, "Skull", "Avoid these at all costs! The skull will cause you to lose all net powerups.", "skull.png"); setinfo(P_CLOVER, "4-Leaf Clover", "Increases your luck...", "clover.png"); @@ -3173,7 +3193,21 @@ void drawplayer(sprite_t *s, SDL_Rect *where) { #ifndef __EDITOR - // only raw wings in certain states + if (player->powerup == PW_GUNNER) { + // just draw crosshairs + // box + drawbox16(screen, s->x-(TILEW/2),s->y-(TILEH/2),s->x+(TILEW/2),s->y+(TILEH/2), &green, NULL); + // littlebox + drawbox16(screen, s->x-1,s->y-1,s->x+1,s->y+1, &green, NULL); + // lines + drawline16(screen, s->x, 0, s->x, s->y-(TILEH/2), green); // top + drawline16(screen, s->x, s->y+(TILEH/2), s->x, 480-1, green); // bottom + drawline16(screen, 0, s->y, s->x-(TILEW/2), s->y, green); // left + drawline16(screen, s->x+(TILEW/2), s->y, 640-1, s->y, green); // right + return; + } + + // only draw wings in certain states switch (levelcomplete) { case LV_NEXTLEV: case LV_CLOUDLOOP: