ratcatcher/rc.c

9348 lines
218 KiB
C

#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef WINDOWS
#include <winsock.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#endif
#include <math.h>
#include <SDL.h>
#include <SDL_keysym.h>
#include <SDL_rotozoom.h>
#include <SDL_ttf.h>
#include <SDL_framerate.h>
#include <SDL_mixer.h>
#include "shared.h"
#include "rc.h"
FPSmanager manager;
SDL_Surface *temps;
SDL_Surface *screen;
SDL_Surface *credittext;
TTF_Font *font[MAXLETTERHEIGHT];
char hiscoreserver[BUFLEN];
int hiscoreport;
hiscore_t hiscore[MAXHISCORES];
int numhiscores;
int wanthiscores;
int gothiscore = -1;
int havejoysticks;
int joybuttons;
SDL_Joystick *joy;
Uint8 *keys;
int joyx,joyy,joybut[MAXJOYBUTTONS], joymap[MAXJOYBUTTONS];
Mix_Music *curmusic = NULL; // pointer to currently playing music
char tempm[BUFLEN];
// used to remember old level if we flood it
int savemap[LEVELW*LEVELH];
int watertime;
double gunorigx;
double gunorigy;
int gundelay;
int guntime;
int playedbell;
int clocktime;
int gameovertime;
int sprayalpha; // for spray effect
int gameover;
int firstlevel;
SDL_Surface *pausedtext, *pausedshadow;
int paused;
int wantframerate = B_FALSE;
int fps;
int fpscount;
int titlemode;
int blinkspeed;
int credits = 0;
int titledone;
int skiplevels;
/* 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_BIGNET,
P_BELL,
P_NUMNETS,
P_GEMBOOST,
P_MASKPOWERUP,
P_HONEY,
P_NUMNETS,
P_GEMBOOST,
P_HELMET,
P_WINGBOOTS,
-1
};
int curfruittype = 0;
int curpoweruptype = 0;
int gtime = 0;
int fpsticks = 0;
int fpsstart = 0;
int curworld;
int curlevelnum;
int skipto = -1; // which level to skip to
level_t *curlevel;
int levelcompletetime;
int oldlevelcomplete;
sprite_t *sprite = NULL; /* main sprite list */
sprite_t *player = NULL;
sprite_t *lastsprite;
sprite_t *lastmet;
int pokereffect;
int pokerpoints;
SDL_Color red = {255, 0, 0, 0};
SDL_Color red2 = {150, 0, 0, 0};
SDL_Color red3 = {90, 0, 0, 0};
SDL_Color orange = {255, 167, 88, 1};
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 grey2 = {90, 90, 90, 0};
SDL_Color green = {0, 255, 0, 0};
SDL_Color green2 = {0, 150, 0, 0};
SDL_Color yellow = {255, 255, 0, 0};
SDL_Color greenish = {82, 125, 74, 0};
int vidargs = 0;
int fullscreen = B_FALSE;
int timer = 0;
int toggletimer = 0;
int main (int argc, char **argv) {
//sprite_t *s,*nextsprite;
char filename[BUFLEN];
int i;
int *animtile;
levelbg = NULL;
datadir = NULL;
// default
sprintf(hiscoreserver, "ratcatcher.nethack.net");
hiscoreport = 80;
// set program name (including version string)
sprintf(progname, "Rat Catcher v0.%d", REV);
/* handle arguments */
if (argc >= 2) {
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-fs")) {
printf("Fullscreen mode enabled.\n");
fullscreen = B_TRUE;
} else if (!strcmp(argv[i], "-v")) {
printf("%s\n",progname);
exit(1);
} else if (!strcmp(argv[i], "-d")) {
if (++i >= argc) {
printf("Missing data dir.\n");
usage();
exit(1);
}
datadir = strdup(argv[i]);
} 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 if (!strcmp(argv[i], "-hs")) {
if (++i >= argc) {
printf("Missing hiscore server name.\n");
usage();
exit(1);
}
if (strstr(argv[i], ":")) {
char *p,*p2;
// use port as well
p2 = hiscoreserver;
for (p = argv[i];p; p++) {
if (*p == ':') {
p++;
break;
} else {
*p2 = *p;
}
p2++;
}
*p2 = '\0';
hiscoreport = atoi(p);
printf("Hiscore server set to '%s'\n",hiscoreserver);
printf("Hiscore port set to '%d'\n",hiscoreport);
} else {
sprintf(hiscoreserver, "%s",argv[i]);
printf("Hiscore server set to '%s'\n",hiscoreserver);
}
} else if (!strcmp(argv[i], "-hp")) {
if (++i >= argc) {
printf("Missing hiscore port.\n");
usage();
exit(1);
}
hiscoreport = atoi(argv[i]);
printf("Hiscore port set to '%d'\n",hiscoreport);
} else {
usage();
exit(1);
}
}
}
if (datadir == NULL) {
datadir = strdup("data");
}
initsdl();
if (TTF_Init()) {
printf("TTF_Init: %s\n", TTF_GetError());
}
atexit(cleanup);
if (loadimagesets()) {
printf("Error loading images.\n");
return 1;
}
if (loadlevellist()) {
printf("Error loading level list from levels.dat.\n");
return 1;
}
/*
fakeblock.id = T_LAND;
strcpy(fakeblock.name,"Fake");
for (i = 0; i < TILEW; i++) {
fakeblock.lowness[i] = 0;
}
fakeblock.solid = S_SOLID;
fakeblock.img[0] = IMG_Load("newtiles/land.png");
fakeblock.numframes = 1;
fakeblock.next = NULL;
fakeblock.prev = NULL;
*/
/* load fonts */
sprintf(filename, "%s/gamefont.ttf",datadir);
for (i = 1; i < MAXLETTERHEIGHT; i++) {
SDL_Surface *surf;
font[i] = TTF_OpenFont(filename,i);
if (!font[i]) {
printf("Error opening font: %s\n", TTF_GetError());
return 1;
}
surf = TTF_RenderText_Solid(font[i], "test", red);
if (!surf) {
printf("Error during font test, size %d\n",i);
return 1;
}
SDL_FreeSurface(surf);
}
/* 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);
}
/* 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;
gamemode = GM_NORM; // default
showhelp = B_TRUE; // default
// try to get hiscores
printf("Attepting to download hiscores from %s:%d...\n",hiscoreserver,hiscoreport); fflush(stdout);
if (gethiscores(B_FALSE)) {
printf("Couldn't download hiscores. Disabling hiscore code.\n");
wanthiscores = B_FALSE;
} else {
printf("Hiscores successfully downloaded.\n");
wanthiscores = B_TRUE;
}
// outside loop
while (1) {
// title screen
if (skipto <= 0) {
dotitlescreen();
srand(time(NULL));
}
// shuffle cards
shufflecards();
startgame();
// main loop
while (!gameover) {
removeall();
// check for sprite death and update moevment counters
checksprites();
/**********************************************
* check for end of level
*/
if (levelcomplete == LV_CLEAR) {
sprite_t *s2, *nexts;
addoutlinetext(320,240,TEXTSIZE_LEVEL,"Level Complete!",&green,&black,LEVELWINDELAY, TT_NORM);
levelcomplete = LV_WAIT;
playfx(FX_WINLEVEL);
// 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
for (s2 = sprite; s2 ; s2 = nexts) {
nexts = s2->next;
if (iscard(s2->id) || s2->id == P_MOVINGCARD) {
s2->dead = D_FINAL;
}
}
// stop teleporting
if (player->teleporting) {
stopteleporting(player);
}
} 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 keypress or other input (joystick etc)
*/
handleinput();
if (!paused) {
/**********************************************
* Special effects #1 - ones which change the level by
* modifiying tiles, etc.
*/
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();
}
// water effect
if (timer % FLOODSPEED == 0) {
if (curlevel->iced == WATER_INPROGRESS) {
doflood();
}
}
}
// gunner effect - red overlay
if (player->powerup == PW_GUNNER) {
if (levelcomplete != LV_DOPOKER) {
SDL_BlitSurface(redbox,NULL,screen,NULL);
}
if (uncaughtmonsters() <= 0) {
// finish if no monsters are left alive & uncaught
losepowerup(player);
}
}
/**********************************************
* Move sprites
*/
if (levelcomplete == LV_DOPOKER) {
// only fivecards sprite can move
sprite_t *this;
for (this = sprite; this ; this = this->next) {
if (this->id == P_FIVECARDS) {
movesprite(this);
}
}
} else { // all other states...
if (levelcomplete != LV_HELPFREEZE) {
// move sprites
moveallsprites();
}
}
/**********************************************
* Move onscreen text
*/
if (levelcomplete != LV_HELPFREEZE) {
movetext();
}
/**********************************************
* Gravity
*/
if (levelcomplete == LV_INIT) {
// only player
dogravity(player);
checkwrap(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);
checkwrap(s);
}
/* tile effects */
for (s = sprite ; s ; s = s->next) {
dotileeffects(s);
}
break;
case LV_GAMEOVER: // no collision detection or player movement
/* gravity */
for (s = sprite->next ; s ; s = s->next) {
dogravity(s);
checkwrap(s);
}
break;
case LV_DOPOKER: // do nothing
case LV_HELPFREEZE: // do nothing
break;
}
}
/**********************************************
* Special effects #2 - ones which need to happen
* after sprite movement
*/
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();
}
}
}
}
/**********************************************
* Collision detection
*/
if ((levelcomplete != LV_DOPOKER) && (levelcomplete != LV_HELPFREEZE)) {
checkcollideall();
}
}
/**********************************************
* Drawing routines
*/
// player netting
if (levelcomplete != LV_DOPOKER) {
drawnetting(player);
}
// cannon firing
if ((levelcomplete != LV_DOPOKER) && (levelcomplete != LV_HELPFREEZE) && (player->powerup == PW_CANNONFIRE)) { // cannon firing
docannoneffect();
}
// draw sprites
if (levelcomplete != LV_DOPOKER) {
drawallsprites();
}
// warning for cloud
if (!isbosslevel(curlevelnum)) {
if ((gtime >= nexthurryup + 10) && (gtime < nexthurryup+15)) { // 15 secs after hurryup
if (levelcomplete == LV_INPROGRESS) {
if (timer % 4 == 0) {
// add puffs
puffin(-1, 320 + (rand() % (TILEW*4)) - (TILEW*2),
240 + (rand() % (TILEH*2)) - TILEH, "cloudwarn", rand() % 5);
}
}
}
}
// poker effect
// this must come last as it clears the screen and blanks out other sprites
if (levelcomplete == LV_DOPOKER) {
SDL_Rect area;
sprite_t *this;
area.x = 0;
area.y = 0;
area.w = 640;
area.h = 480;
// clear screen
SDL_FillRect(screen, &area, SDL_MapRGB(screen->format,black.r,black.g,black.b));
// only fivecards sprite is drawn
for (this = sprite; this ; this = this->next) {
if (this->id == P_FIVECARDS) {
drawsprite(this);
}
}
} else if (levelcomplete == LV_HELPFREEZE) {
SDL_Rect area;
SDL_Surface *blackbox;
blackbox = SDL_CreateRGBSurface(SDL_SWSURFACE,
screen->w,
screen->h,
screen->format->BitsPerPixel, screen->format->Rmask,
screen->format->Gmask,screen->format->Bmask, 0);
SDL_FillRect(blackbox, &area, SDL_MapRGB(screen->format,black.r,black.g,black.b));
SDL_SetAlpha(blackbox, SDL_SRCALPHA, 175);
// clear screen
SDL_BlitSurface(blackbox,NULL,screen,NULL);
SDL_FreeSurface(blackbox);
}
// draw text
drawtext();
// draw score
drawscore();
// draw boss health
if (boss) {
drawbosshealth();
}
// draw 'PAUSED' text
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);
}
/**********************************************
* Special effects #3 - ones which need to happen
* after graphics drawing
*/
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) {
losepowerup(player);
}
} else if (player->powerup == PW_SPRAYUP) { // green overlay for fly spray
if (levelcomplete != LV_DOPOKER) {
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
if (levelcomplete != LV_DOPOKER) {
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(-1) < 8) { // max 7 rats + king rat
puffin(P_RAT, rand() % (640-(TILEW*8))+(TILEW*4), TILEH, "gen_rat",0);
}
}
if (--player->timer1 == 0) {
losepowerup(player);
}
} else if (player->powerup == PW_SNAILSHAKE) { // vertical shake due to king snail
// shake screen
if (timer % 5 == 0) {
int amt;
SDL_Rect area;
amt = (rand() % 20)+1;
// offset screen upwards
area.x = 0;
area.y = amt;
area.w = 640;
area.h = 480-amt;
SDL_BlitSurface(screen, &area, screen, NULL);
// fill in empty bit
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) {
losepowerup(player);
}
}
}
/**********************************************
* Update the screen
*/
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();
} // end main loop
// TODO: fade
// clear screen
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format,black.r,black.g,black.b));
if (wanthiscores) {
// check for a hiscore, if so submit it
checkhiscores();
// clear screen
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format,black.r,black.g,black.b));
// show high scores
showhiscores();
// clear screen ready to start again...
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format,black.r,black.g,black.b));
}
} // end outside loop
return 0;
}
void tick(void) {
sprite_t *s;
SDL_framerateDelay(&manager);
if (paused) return;
if (levelcomplete == LV_HELPFREEZE) return;
fpsticks = SDL_GetTicks();
if (fpsstart == 0) {
fpsstart = fpsticks;
} else {
/* once per second */
if (fpsticks - fpsstart >= 1000) {
// calculate frames per second
fps = fpscount;
fpscount = 0;
// incremenet game time
gtime++;
// check for game over - 5 seocnds after game over text disappears
if (gameovertime > 0) {
if (gtime >= (gameovertime + GAMEOVERWAIT)) {
gameover = B_TRUE;
}
}
// handle visual countdown timers
if (levelcomplete == LV_INPROGRESS) {
// clock
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, TT_NORM);
// sound effect
playfx(FX_CLOCK);
}
// never reach hurryup time
nexthurryup++;
// decrement counter
clocktime--;
if (clocktime < 0) {
// finished!
Mix_ResumeMusic();
losepowerup(player);
}
}
// handle flood effect
if (curlevel->iced == WATER_COMPLETE) {
char tempm[SMALLBUFLEN];
// text
if (watertime > 0) {
sprintf(tempm, "%d",watertime);
addoutlinetext(320,120,TEXTSIZE_LEVEL, tempm, &cyan,&black,15, TT_NORM);
}
// never reach hurryup time
resethurryup(curlevel);
// decrement counter
watertime--;
if (watertime < 0) {
// finished!
curlevel->iced = B_FALSE;
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);
}
}
}
/* check for hurryup*/
if (!isbosslevel(curlevelnum)) {
if ((gtime == nexthurryup) && (curlevel->iced != WATER_COMPLETE)) {
if (!levelcomplete) {
// all sprites get angry and un-iced
for (s = sprite; s; s = s->next) {
if ((s != player) && (ismonster(s->id))) {
s->angry = B_TRUE;
s->iced = B_FALSE;
}
}
// rings are disabled
if ((player->powerup == PW_RINGWALK) || (player->powerup == PW_RINGJUMP)) {
losepowerup(player);
}
addoutlinetext(320,240,TEXTSIZE_HURRY, "Hurry up!", &yellow,&black,HURRYDELAY, TT_NORM);
stopmusic();
Mix_PlayChannel(CH_HURRYUP, sfx[FX_HURRYUP], 0);
}
} else if (gtime == nexthurryup + 15) { // 15 secs after hurryup
if (!levelcomplete) {
sprite_t *bc;
bc = addsprite(P_BLACKCLOUD, 320,240,"cloud");
makeinvuln(bc);
addoutlinetext(320,240,TEXTSIZE_HURRY, "Too slow!", &red,&black,HURRYDELAY, TT_NORM);
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;
int x,y;
// remove the player
if (!firstlevel) {
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;
// not on a trampoline!
player->ontramp = B_FALSE;
player->trampx = -1;
player->trampy = -1;
// load next level data
if ((player->powerup == PW_PHONE) && (!isbosslevel(curlevelnum))) {
// don't add monsters
loadlevel(curworld,curlevelnum, B_FALSE);
} else {
loadlevel(curworld,curlevelnum, B_TRUE);
}
// remmeber layer 2 in case we replace it with water
for (y = 0 ; y < LEVELH; y++) {
for (x = 0 ; x < LEVELW; x++) {
savemap[y*LEVELW+x] = curlevel->map2[y*LEVELW+x];
}
}
watertime = -1;
// do the moving to next level transition (ie. scroll the screen)
drawlevel();
// now the player gets invincibility
makeinvuln(player);
// phone is cancelled on boss levels
if (player->powerup == PW_PHONE) {
if (isbosslevel(curlevelnum)) {
losepowerup(player);
skiplevels = 0;
}
}
/* reset game stats */
if (player->powerup == PW_PHONE) {
levelcomplete = LV_CLOUD;
} else {
levelcomplete = LV_INIT;
levelcompletetime = -1;
}
/* reset level stats */
level->gotpowerup = B_FALSE;
// this is based on the max number of monsters killed
// simulatneously on the previous level...
forcegoodcard = nextforcegoodcard;
nextforcegoodcard = B_FALSE;
if (isbosslevel(curlevelnum)) {
// don't play bell on boss levels
playedbell = BELL_DONEFLASH;
} else {
if (player->hasbell) {
playedbell = B_FALSE;
} else {
playedbell = BELL_DONEFLASH;
}
}
// if this is boss level, play music
if (isbosslevel(curlevelnum)) {
stopmusic();
playmusic(bossmusic);
} else {
if (curmusic != normalmusic) {
stopmusic();
playmusic(normalmusic);
}
}
level->iced = ICE_NONE;
level->icey = -1;
sprintf(msg, "Level %d-%d",getcurworld(), getcurlevel());
addoutlinetext(320,240-18,TEXTSIZE_LEVEL,msg,&white,&black,LEVELDELAY, TT_NORM);
sprintf(msg, "\"%s\"", curlevel->name);
addoutlinetext(320,240+18,TEXTSIZE_LEVEL2,msg,&cyan,&black,LEVELDELAY, TT_NORM);
/* reset player stats */
player->netting = B_FALSE;
player->slamming = B_FALSE;
player->jumping = B_FALSE;
player->netcaught = 0;
// make sure player is at the right position
player->x = (curlevel->p1x * TILEW) + (TILEW/2);
player->y = (curlevel->p1y * TILEH) + TILEH-2;
// remove powerup, phone is a special case
if (player->powerup == PW_PHONE) {
skiplevels--;
if (skiplevels <= 0) {
losepowerup(player);
}
} else {
losepowerup(player);
}
// 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;
if (cheat) {
player->speed = 2;
}
// haven't got a card yet
gotcard = B_FALSE;
// reset timer
timer = 0;
}
void jump(sprite_t *s, int dir) {
// if we've ust been hit with armour, bypass all these checks)
if (!s->recoiling) {
// can't jump if already jumping
if (s->jumping) {
// unless we have the doublejump powerup
if (!s->doublejump || s->useddoublejump || !s->doublejumpready) {
return;
}
}
if (s->jumptimer) return;
if (s->doublejump && (s->falling || s->jumping ) && (s->useddoublejump || !s->doublejumpready)) {
return;
}
// can only jump sideways when climbing
if (s->climbing && (dir == 0)) {
return;
}
}
// check for recoiling here, because we always need to be able to
// "jump" backwards, even if already jumping
if (s->recoiling || isonground(s) || isinwater(s) || isonladder(s) || s->doublejump) {
if (ismonster(s->id) || (s->id == P_KSSHELL)) {
if (s->recoiling) { // recoiling monsters don't pause before jumping
if (s->id == P_KSSHELL) {
s->jumpspeed = 3;
s->jumpdir = dir*3;
} else {
s->jumpspeed = MONJUMPSPEED;
s->jumpdir = dir;
}
s->jumping = 1;
s->dir = -s->jumpdir; // face backwards
} else {
s->jumpdir = dir;
if (s->jumpdir != 0) {
s->dir = s->jumpdir;
}
// special case
if ((s->id == P_KINGRAT) && (s->timer1 == KRS_CHARGEWAIT)) {
// jump right away
s->jumpspeed = MONJUMPSPEED; // will be changed later
s->jumping = 1;
} else {
// delay then jump
s->jumptimer = getjumpdelay(s->id);
s->willjumpspeed = getmonjumpspeed(s);
}
}
} else {
tiletype_t *tt;
s->jumpdir = dir;
if (s->jumpdir != 0) {
s->dir = s->jumpdir;
}
if (s->doublejump) {
if (s->falling || s->jumping) {
// we just used up the doublejump
s->useddoublejump = B_TRUE;
puffin(-1, s->x, s->y, "nothing", 0);
} else {
// we're jumping off the ground
// need to let go of jump before double jumping
s->doublejumpready = B_FALSE;
}
}
// if on rollers, they add to your inertia
// if you are facing their direction
tt= gettileat(s->x, s->y, NULL, NULL);
if ((tt->id == T_RIGHT) && (s->jumpdir > 0)) {
s->jumpdir++;
} else if ((tt->id == T_LEFT) && (s->jumpdir < 0)) {
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;
if (s == player) {
// stop recoiling
if (s->recoiling) {
s->recoiling = B_FALSE;
} else {
// play sound effect
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
makeinvuln(player);
// 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();
}
losepowerup(player);
resethurryup(curlevel);
if (curmusic == fastmusic) {
playmusic(normalmusic);
}
player->climbing = B_FALSE;
// kill mask sprite
if (mask) {
mask->dead = D_FINAL;
}
// 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, TT_NORM);
/* release anything we've caught */
for (s2 = sprite->next ; s2 ; s2 = s2->next) {
if (s2->caughtby == s) {
uncatch(s2);
s2->angry = B_TRUE;
if (s2->id == P_SPIDER) {
// climb up!
s2->flies = B_TRUE;
s2->falling = B_FALSE;
s2->ys = -getspeed(s2);
}
}
}
// un-ice everything if player dies
for (s2 = sprite->next ; s2 ; s2 = s2->next) {
if (s2->iced) {
s2->iced = B_FALSE;
}
}
// undo water effect
if (curlevel->iced == WATER_COMPLETE) {
undoflood();
curlevel->iced = B_FALSE;
}
}
} else if (s == boss) { // boss effects
sprite_t *ss;
// stop screen shaking and rats falling
if (player->powerup == PW_RATSHAKE) {
losepowerup(player);
} else if (player->powerup == PW_SNAILSHAKE) {
losepowerup(player);
}
// reset timers
s->timer1 = 0;
s->timer2 = 0;
// death timer
s->timer3 = BOSSDIETIME;
if (s->id == P_KINGRAT) {
// stop rats from coming in
for (ss = sprite ; ss ; ss = ss->next) {
if ((s->id == P_PUFF) && (ss->timer3 == P_RAT)) {
// cancel the puffin
ss->timer3 = -1;
}
}
}
// give a permenant powerup!
switch (s->id) {
case P_KINGRAT:
addsprite(P_BIGSPEED, (640/2), 0, "bigspeed");
break;
case P_KINGSNAIL:
addsprite(P_BIGNUMNETS, (640/2), 0, "bignumnets");
break;
}
} else if ((s->id == P_SNAIL) && (s->lives > 0)) { // snails can't die but turn into slugs instead
sprite_t *newsp;
// create a slug here
newsp = addsprite(P_SLUG, s->x,s->y, "slug" );
puffin(-1, s->x, s->y, "nothing", 0); // create the illusion of a puff
newsp->dir = player->dir; // facing away from player
// make sure it can't be netted right away!
if (player->powerup != PW_CLOCK) {
newsp->invuln = SLUGINVULNTIME;
}
// the shell recoils (other code will kill us off when this is done)
// -become invulnerable temporarily
s->invuln = INVULNTIME*2; // make sure this lasts until we die
// -bounce back
s->recoiling = B_TRUE;
jump(s,player->dir); // ie. away from the player
playfx(FX_CRACK);
// when we hit the ground, later code will check for this.
// if a snail with lives=0 hits the ground, they die (it actually
// looks like a shell)
s->lives = 0;
// attributes
s->angry = 0;
s->iced = 0;
// don't do rest of code
return;
}
/* set death attribute */
s->dead = D_INITIAL;
s->netcaught = B_FALSE;
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 of a given type (-1 for all) on level
int countmonsters(int montype) {
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) {
if ((montype == -1) || (s2->id == montype)) {
mcount++;
}
}
}
}
return mcount;
}
// count of monsters left alive and uncaught
int uncaughtmonsters(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 && !s2->caughtby) {
if (s2->id != P_BLACKCLOUD) {
mcount++;
}
}
}
return mcount;
}
void cleanup(void) {
int i;
printf("commencing cleanup routine....\n");
SDL_FreeSurface(pausedtext);
SDL_FreeSurface(pausedshadow);
Mix_HaltMusic();
Mix_CloseAudio();
for (i = 1; i < MAXLETTERHEIGHT; i++) {
TTF_CloseFont(font[i]);
}
TTF_Quit();
SDL_Quit();
printf("complete.\n");
}
void checkcollide(sprite_t *s) {
sprite_t *s2;
int keepchecking;
double xdiff,ydiff;
double xthresh, 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;
else if (iseffect(s2->id)) continue;
keepchecking = B_TRUE;
/* check for colission with our net */
if (s->netting ) {
if ((isnettable(s2) && !s2->invuln) || (s->netsticky && isfruit(s2->id))){
double nety;
if ((s->x + s->netlen*s->netdir) > s2->x) {
xdiff = (s->x + s->netlen*s->netdir) - s2->x;
} else {
xdiff = s2->x - (s->x + s->netlen*s->netdir);
}
if (xdiff < 0) xdiff = -xdiff;
nety = (s->y - (s->img->h/2) - 1);
if (nety > (s2->y - s2->img->h/2)) {
ydiff = nety - (s2->y - s2->img->h/2);
} else {
ydiff = (s2->y - s2->img->h/2) - nety;
}
if (ydiff < 0) ydiff = -ydiff;
xthresh = s2->img->w;
ythresh = s2->img->h;
// check...
if (ythresh > TILEH*2) {
printf("*DEBUG* ythresh too high: %0.0f\n",ythresh);
ythresh = TILEH*2;
}
// take numnets into account for y check
netsleft = s->netmax - s->netcaught;
if (netsleft > 1) {
ythresh += ((int)s->img->h / (int)(netsleft+1) );
if (ythresh > TILEH*3) {
printf("*DEBUG2* ythresh too high: %0.0f\n",ythresh);
ythresh = TILEH*3;
}
}
//if ((xdiff <= s2->img->w/2) && (ydiff <= s2->img->h)) {
if ((xdiff <= xthresh) && (ydiff <= ythresh)) {
// we hit something!
if (s->netsticky && isfruit(s2->id)) {
getfruit(s, s2, 1);
} else {
// Otherwise, it must be a monster
// 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, TT_NORM);
keepchecking = B_FALSE;
} else if (s2->iced) {
// it dies
s2->willbecome = P_DIAMOND;
playfx(FX_ICEBREAK);
if (s2->id == P_SNAIL) s2->id = P_SLUG;
die(s2);
} else {
// otherwise we caught it if we have enough nets
if (s->netcaught < s->netmax) {
printf("caught: ydiff=%0.0f,ythresh=%0.0f,s='%s',%0.0f,%0.0f s2='%s',%0.0f,%0.0f\n",
ydiff,ythresh,
s->name, s->x, s->y, s2->name,s2->x,s2->y);
s2->caughtby = s;
s2->jumping = B_FALSE;
s2->falling = 0;
s2->caughtstate = C_NETTING;
s->netcaught++;
// special case for spider, so that it will
// recover if we release it
if (s2->id == P_SPIDER) {
s2->flies = B_FALSE;
}
playfx(FX_CATCH);
keepchecking = B_FALSE;
}
}
} // end if isfruit or ismonster
}
}
} // 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;
// adjust for zapper
xthresh = s->img->w/2 + s2->img->w/2;
ythresh = s->img->h/2 + s2->img->h/2;
if (s->id == P_ZAPPER) {
xthresh += (TILEW*2.5);
ythresh += (TILEH*2.5);
}
if ((xdiff <= xthresh) && (ydiff <= ythresh)) {
/* COLLISION! */
// are we the player?
if (s == player) {
//if (isfruit(s2->id) && (s2->teleporting == 0)) {
if (isfruit(s2->id)) {
getfruit(player, s2, 1);
} else if (ismonster(s2->id) || isbullet(s2->id)) {
if (s2->iced) {
// monster dies
playfx(FX_ICEBREAK);
if (s2->id == P_SNAIL) s2->id = P_SLUG;
s2->willbecome = P_DIAMOND;
die(s2);
} else if (!s->invuln) {
// player dies
die(s);
}
}
} else if ((s->id == P_SMASH) || (s->id == P_STAR) || (s->id == P_METEOR) ) {
if (ismonster(s2->id)) {
switch (s->id) {
case P_STAR: playfx(FX_STARHIT); break;
}
// monster dies
if (s2->id == P_SNAIL) s2->id = P_SLUG;
die(s2);
// become something special
s2->willbecome = P_DIAMOND;
}
} else if (s->id == P_ZAPPER) {
if (ismonster(s2->id)) {
if (s->timer4 == 0) {
// set timer for zap effect
s->timer4 = ZAPPERTIME;
s->zapping = s2;
playfx(FX_ZAP);
// monster dies
if (s2->id == P_SNAIL) s2->id = P_SLUG;
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 ((s == player) && (player->powerup == PW_GUNNER)) {
return B_FALSE;
}
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;
}
// for random powerup, pick a random image
if ((timer % 5) == 0) {
if (s->id == P_RANDOM) {
s->timer1 = randompowerup();
while (s->timer1 == P_RANDOM) {
s->timer1 = randompowerup();
}
// set image
s->img = imageset[s->timer1].img[F_WALK1];
}
}
/* timer */
if (s->doomcount) {
if (player->powerup != PW_GUNNER) {
s->doomcount--;
}
if (s->doomcount == 0) {
s->dead = D_FINAL;
}
}
/* avoid edges of screen */
if (s->y < s->img->h) {
// if (!s->flies) {
if (s->dead) {
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+s->img->h)) {
/* 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)) {
int x,y,ty;
// get position for puff/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;
if (s->quickdie || (isonground(s) && !isongroundpoint(s, s->x, s->y-TILEH))) {
/* change into a fruit */
s->dead = D_FINAL;
//addsprite(s->willbecome, x, y, "Fruit", B_FALSE);
if (boss) {
puffin(-1, x, y, "nothing", 0);
} else {
puffin(s->willbecome, x, y, "fruit", 0);
}
//ss = addsprite(P_PUFF, x, y, "Fruit", B_FALSE);
//ss->timer3 = s->willbecome;
} else if (s->bounces >= 6) {
// just die
s->dead = D_FINAL;
puffin(-1, x, y, "nothing_sprd", 0);
}
}
}
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;
}
// mark that we've allocated the player's teleport image
s->allocimg = B_TRUE;
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;
break;
} else { // search map2
tt = gettile(curlevel->map2[y*LEVELW+x]);
if (tt->id == T_TELEPORTDEST) {
/* teleport there */
s->x = (x * TILEW) + (TILEW/2);
s->y = (y * TILEH) + TILEH-2;
break;
}
}
}
}
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->allocimg = B_FALSE;
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) {
if (s->id != P_SLUG) { // slugs don't turn back and forth before jumping
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;
// if player already has this, and no cards already on screen,
// we get a card instead (and also haven't got a card yet htis
// level)
if (!gotcard) {
if ((isfruit(s->timer3) == FT_PERM) && haspowerup(player, s->timer3)) {
sprite_t *search;
int found;
found = B_FALSE;
// is there already a card here or about to appear?
for (search = sprite; search ; search = search->next) {
if (iscard(search->id)) {
found = B_TRUE;
break;
}
// if it's a puff that will become a card...
if ((search->id == P_PUFF) && (iscard(search->timer3))) {
found = B_TRUE;
break;
}
}
if (!found) {
s->timer3 = P_FIRSTCARD;
}
}
}
if (s->timer3 == P_FIRSTCARD) {
s->timer3 = getrandomcard();
gotcard = B_TRUE;
}
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:
case P_KINGSNAIL:
boss = newsp;
}
// is it in the water?
if (isinwater(newsp)) {
newsp->swimming = B_TRUE;
newsp->watertimer = rand() % BUBBLETIME;
}
// is it a points fruit appearing on top of us?
if (isfruit(newsp->id) == FT_FRUIT) {
int xdiff,ydiff;
/* now check for collision with us */
xdiff = player->x - newsp->x;
if (xdiff < 0) xdiff = -xdiff;
ydiff = (player->y-(player->img->h/2)) - (newsp->y-(newsp->img->h/2));
if (ydiff < 0) ydiff = -ydiff;
// appeared on top of us
if ((xdiff <= player->img->w/2 + newsp->img->w/2) &&
(ydiff <= player->img->h/2 + newsp->img->h/2)) {
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);
}
}
}
}
}
} else if (s->timer1 >= PUFFFRAMES) {
s->timer1 = 999; // for debugging
s->dead = D_FINAL;
}
}
}
} else if (s->id == P_MOVINGCARD) {
// timer1 is the actual cardid
// timer2 is the target x position
// timer3 is the target y position
//
// move towards end position
movetostart(s, s->timer2, s->timer3, CARDSPEED, CARDSPEED);
// are we there yet?
if ((s->x == s->timer2) && (s->y == s->timer3)) {
gaincard(player, s->timer1);
s->dead = D_FINAL;
}
} else if (s->id == P_FIVECARDS) {
// are we there yet?
if ((s->x == 320) && (s->y == 240 )) {
if (s->timer2 == 0) {
int i;
SDL_Surface *blackness, *cardimg;
SDL_Rect area;
s->timer2 = -1; //this means we are counting down
s->timer1 = POKERWAITTIME;
// hide non-used cards
cardimg = imageset[P_FIRSTCARD].img[F_WALK1];
area.x = 0;
area.y = 0;
area.w = cardimg->w;
area.h = cardimg->h;
// create semi-transparent black surface the size of a card
blackness = SDL_CreateRGBSurface(SDL_SWSURFACE,
cardimg->w, cardimg->h,
screen->format->BitsPerPixel, screen->format->Rmask,
screen->format->Gmask,screen->format->Bmask,
screen->format->Amask);
SDL_FillRect(blackness, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
SDL_SetAlpha(blackness, SDL_SRCALPHA, 200);
for (i = 0; i < player->numcards; i++) {
if (player->usedcard[i] == B_FALSE) {
// hide it
//SDL_FillRect(s->img, &area, SDL_MapRGB(screen->format, 0, 0, 0));
SDL_BlitSurface(blackness, NULL, s->img, &area);
}
area.x += (cardimg->w + 2);
}
SDL_FreeSurface(blackness);
} else {
s->timer1--;
if (s->timer1 == 0) {
sprite_t *s2, *nexts;
int i;
// die
s->dead = D_FINAL;
// restore state
levelcomplete = oldlevelcomplete;
Mix_ResumeMusic();
// do effect!
dopokereffect(player,pokereffect);
// clear player cards
for (i = 0; i < MAXCARDS; i++) {
player->card[i] = -1;
}
player->numcards = 0;
// shuffle cards
shufflecards();
// kill all monsters (if not skipping levels)
if (player->powerup != PW_PHONE) {
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;
s2->lives = 0; // for snails
if (s2->caughtby) {
uncatch(s2);
player->netcaught--;
}
die(s2);
}
}
}
}
}
} else {
// move towards centre of the screen
movetostart(s, 320, 240, FIVECARDSPEED, FIVECARDSPEED);
}
} else if (s->id == P_MASK) { // mask effect
// stay on top of player
if (player->slamming) {
s->y = player->y + MASKOFFSETSLAMY;
s->x = player->x + (MASKOFFSETSLAMX*player->dir);
} else {
s->y = player->y + MASKOFFSETY;
s->x = player->x + (MASKOFFSETX*player->dir);
}
// die if player doesn't have a mask
if (!player->hasmask) {
s->dead = D_FINAL;
}
} else if (s->id == P_STAR) { // shuriken effect
// animate
s->timer1++;
if (s->timer1 >= STARFRAMES) {
s->timer1 = 0;
}
// move until we hit the edge of the screen
s->x = s->x + s->xs;
s->y = s->y + s->ys;
// if off screen
if ((s->x >= 640 - s->img->w/2) || (s->x <= s->img->w/2) || (s->y <= 0) || (s->y >= 480+s->img->h)) {
// die
s->dead = D_FINAL;
}
} else if (s->id == P_METEOR) { // meteor
double oldy;
oldy = s->y;
// fall to bottom of screen then die
s->y = s->y + s->ys;
// if just appeared onscreen
if ((oldy < 0) && (s->y >= 0)) {
playfx(FX_METEOR);
}
// if off screen
if (s->y >= (480 + s->img->h)) {
// die
s->dead = D_FINAL;
}
} else if (s->id == P_BUBBLE) { // bubble effect
tiletype_t *tt;
// float up, die when we leave the water
tt = gettileat(s->x,s->y, NULL,NULL);
if (tt->water) {
s->y -= 0.5;
// move left/right
s->x += (sin(s->y/5)/5);
} else {
// die
s->dead = D_FINAL;
}
} else if (s->id == P_ZAPPER) { // zapper effect
double degs;
/* timer1 is the frame
timer2 controls when to change frame
doomcount ?
*/
// die if level finished
if ((levelcomplete == LV_CLEAR) || (levelcomplete == LV_WAIT)) {
s->dead = D_FINAL;
}
// move in figure 8 around player
s->x = player->x;
s->y = player->y;
degs = (double)s->timer3 * ((double)M_PI/(double)180);
s->x += (sin(degs)*(double)48);
s->y += (sin(degs*2)*(double)16);
// adjust position counter
s->timer3 += 3;
if (s->timer3 >= (360*2)) {
s->timer3 = 0;
}
// adjust frame
s->timer2--;
if (s->timer2 <= 0) {
s->timer1++;
if (s->timer1 >= imageset[P_ZAPPER].numimages) {
s->timer1 = 0;
}
s->timer2 = ZAPPERDELAY;
}
// adjust zap counter
if (s->timer4 > 0) {
s->timer4--;
}
} 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;
tiletype_t *tunder;
/* distance to player */
xdiff = player->x - s->x;
if (xdiff < 0) absxdiff = -xdiff;
else absxdiff = xdiff;
// tile in front and below
tt = gettileat(s->x + s->dir*getspeed(s) + (s->dir * (s->img->w/2)),s->y + (TILEH/2),NULL,NULL);
// tile below
tunder = gettileat(s->x ,s->y + 1,NULL,NULL);
/* if there's a hole in front of us and below*/
if (tt->solid == S_NOTSOLID) {
// we're on a slope
if (tunder->solid == S_SLOPE) {
move = B_TRUE;
} else {
if (s->angry || boss) {
if (player->y > s->y) {
/* if player is below, fall off */
if (xdiff <= (TILEW*8)) {
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;
}
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 { // falling
tiletype_t *tunder, *t2under;
// tile below
tunder = gettileat(s->x ,s->y,NULL,NULL);
t2under = gettileat(s->x ,s->y+s->img->h,NULL,NULL);
if ((tunder->solid == S_SLOPE) || (t2under->solid == S_SLOPE)) {
movex(s, s->dir*getspeed(s));
}
}
} else if (s->id == P_SNAIL) {
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 {
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;
}
// jump?
if (boss) {
if ((player->dead == 0) && (!s->jumping) && (!s->jumptimer)) {
/* if player is above us or at same level...*/
if (player->y <= (s->y-TILEH)) {
int ydiff = s->y - player->y;
if ((ydiff >= (TILEH*4)) && (ydiff <= (TILEH*8))) { // player between 4 and 8 tiles above
if (xdiff <= (TILEW*16)) { // if closeish horizontally
/* jump up */
jump(s, 0);
}
} else if ((xdiff >= (TILEW*1)) && (xdiff <= (TILEW*9))) { // if 1-9 tiles right
if (s->dir == D_RIGHT) {
/* jump right */
jump(s, D_RIGHT);
}
} else if ((xdiff <= -(TILEW*1)) && (xdiff >= -(TILEW*9))) { // if 1-9 tiles left
if (s->dir == D_LEFT) {
/* jump left */
jump(s, D_LEFT);
}
}
}
}
}
} else { // falling
if (s->recoiling) {
// fall backwards
rv = movex(s, -s->dir*getspeed(s));
}
}
} else if (s->id == P_SLUG) {
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, try to jump over the gap */
if ((s->dir == D_RIGHT) && (xdiff > 0) && (xdiff <= (TILEW*9))) {
jump(s,D_RIGHT);
} else if ((s->dir == D_LEFT) && (xdiff < 0) && (xdiff >= -(TILEW*9))) {
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 or at same level...*/
if (player->y <= s->y) {
int ydiff = s->y - player->y;
if ((ydiff >= (TILEH*4)) && (ydiff <= (TILEH*8))) { // player between 4 and 8 tiles above
if (xdiff <= (TILEW*16)) { // if closeish horizontally
/* jump up */
jump(s, 0);
}
} else if ((xdiff >= (TILEW*1)) && (xdiff <= (TILEW*9))) { // if 1-9 tiles right
if (s->dir == D_RIGHT) {
/* jump right */
jump(s, D_RIGHT);
}
} else if ((xdiff <= -(TILEW*1)) && (xdiff >= -(TILEW*9))) { // if 1-9 tiles left
if (s->dir == D_LEFT) {
/* jump left */
jump(s, D_LEFT);
}
}
}
}
} else { // falling
int rv;
// move forwards
rv = movex(s, s->dir*getspeed(s));
}
} else if (s->id == P_PLANT) {
// don't move!
s->moved = MV_WALK;
} 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_KINGSNAIL) {
/* timer1 is state
0 == walk back and forth
timer2 counts down, at zero we change state
*/
if ((s->timer1 == KSS_WALK1) || (s->timer1 == KSS_WALK2)) {
// walk back and forth
rv = movex(s, s->dir*getspeed(s));
if (rv) {
/* if we couldn't move (hit a wall), turn */
s->dir = -s->dir;
}
// dec timer
s->timer2--;
if (s->timer2 == 0) {
if (player->dead) {
// reset timer
s->timer2 = KR_WALKTIME;
} else {
// release snails or jump, depending on state
if ((countmonsters(P_SNAIL) == 0) || (s->timer1 == KSS_WALK1)) { // next state is shooting
s->timer1 = KSS_PAUSE1;
s->timer2 = KS_SHOOTWAIT;
playfx(FX_SNAILPREPARE);
} else { // next state is jump
s->timer1 = KSS_PAUSE2;
s->timer2 = KS_JUMPWAIT;
}
}
}
} else if (s->timer1 == KSS_PAUSE1) {
// add puffs on shell
if (timer % 6 == 0) {
puffin(-1, s->x + -s->dir*((s->img->w/4) + (rand() % 7)-3),
s->y-(s->img->h/2) - (rand() % 7) - 3, "nothing", rand() % 5);
}
s->timer2--;
if (s->timer2 == 0) {
sprite_t *newsp;
// shoot!
s->timer1 = KSS_SHOOT;
// shell cracks
playfx(FX_CRACK);
newsp = addsprite(P_KSSHELL, s->x + -s->dir*((s->img->w/4) + (rand() % 7)-3),
s->y-(s->img->h/2) - (rand() % 7) - 3, "ksshell");
// the shell recoils (other code will kill us off when this is done)
// -become invulnerable temporarily
newsp->invuln = INVULNTIME*2; // make sure this lasts until we die
// -bounce back
newsp->recoiling = B_TRUE;
jump(newsp,-s->dir); // ie. away from king snail
}
} else if (s->timer1 == KSS_SHOOT) {
// shoot out snails
if (timer % 20 == 0) {
if (countmonsters(-1) < 8) { // max 7 snails + king snail
sprite_t *newsp;
newsp = addsprite(P_SNAIL, s->x + -s->dir*(s->img->w/4), s->y-(s->img->h/2),"babsnail");
// make it shoot upwards
newsp->jumping = 1;
newsp->jumpspeed = (rand() % 7) + 7; // 7 - 13
newsp->jumpdir = (((double)(rand() % 110) - 50) / 10); // -5 to 5
if (rand() % 2) newsp->dir = 1;
else newsp->dir = -1;
playfx(FX_WHOOSH);
} else {
// enough, regenerate shell
s->timer1 = KSS_REGEN;
s->timer3 = 1; // percentage of shell size
}
}
} else if (s->timer1 == KSS_REGEN) {
// shell gets bigger
s->timer3++;
// wait for shell to finish
if (s->timer3 >= 120) {
// walk again
s->timer1 = KSS_WALK2;
s->timer2 = KS_WALKTIME;
}
} else if (s->timer1 == KSS_PAUSE2) {
if (!s->jumptimer) {
int ydis;
int try;
int tot;
int n;
// remember our height
s->timer3 = s->y;
// delay then jump
s->jumptimer = getjumpdelay(s->id);
s->jumpdir = 0;
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 = KSS_JUMPING;
}
} else if (s->timer1 == KSS_JUMPING) {
// wait until we land...
if (!s->jumptimer && !s->jumping && !s->falling) {
sprite_t *ss, *nexts;
// snails turn to slugs!!
for (ss = sprite ; ss ; ss = nexts) {
nexts = ss->next;
if ((ss->id == P_SNAIL) && !ss->caughtby && !ss->dead) {
die(ss);
}
}
// now pause a while before moving again
s->timer1 = KSS_PAUSE3;
s->timer2 = KS_RELOADWAIT;
// shake the screen via a fake player powerup
playfx(FX_BOSSWALL);
player->powerup = PW_SNAILSHAKE;
player->timer1 = SNAILSHAKETIME;
}
} else if (s->timer1 == KSS_PAUSE3) {
s->timer2--;
if (s->timer2 == 0) {
// go back to start!
s->timer1 = KSS_WALK1;
s->timer2 = KS_WALKTIME;
}
}
} else if (s->id == P_TICK) {
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+TILEH/2) ) {
/* 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;
}
/* move */
s->x += s->xs;
s->y += s->ys;
s->dir = absxs;
s->moved = MV_WALK;
} else if (s->id == P_FISH) { // very similar to bee
double absxs,absys;
if ((s->xs == -99) || (s->ys == -99)) {
s->xs = getspeed(s)*2;
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)*2;
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) || !tt->water) {
/* turn */
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 (!s->flies && (isonground(s) || (s->y >= (480-TILEH) ) )) {
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;
// stop the player teleporting if required
if (player->teleporting) {
stopteleporting(player);
}
}
} 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;
// keep on screen!!
keeponscreen(s);
}
}
} else if (levelcomplete == LV_CLOUDLOOP) {
// make sure we are on the screen!!
// should never happen, but...
if (s->x < 0) s->x = 320;
if (s->y < 0) s->y = 240;
if (s->x >= 640) s->x = 320;
if (s->y >= 480) s->y = 240;
// stick player to us
player->x = s->x;
player->y = s->y - (imageset[P_PINKCLOUD].img[F_WALK1]->h / 2);
// turn off player attributes
player->climbing = B_FALSE;
player->netting = B_FALSE;
player->slamming = B_FALSE;
player->jumping = 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 (but use player height
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->size <= 0.2) {
s->dead = D_FINAL;
/* reset hurryup timer */
resethurryup(curlevel);
} else {
SDL_Surface *ts, *cloudim;
//printf("shrink\n");
cloudim = imageset[P_BLACKCLOUD].img[0];
/* get smaller */
s->size -= 0.1;
ts = rotozoomSurfaceXY(cloudim,0,s->size, s->size, 0);
//ts = rotozoomSurfaceXY(s->img,0, 0.9 , 0.9 ,0);
SDL_FreeSurface(s->img); // free old image
s->img = ts;
s->y--;
}
} else {
if (!s->invuln) {
if ((s->xs == -99) || (s->ys == -99)) {
if (player->y > s->y) {
s->ys = 0.5;
} else {
s->ys = -0.5;
}
if (player->x > s->x) {
s->xs = 1;
} else {
s->xs = -1;
}
}
s->x += s->xs;
s->y += s->ys;
if (s->x >= (640 - s->img->w/2)) {
s->xs = -s->xs;
s->x = 640 - s->img->w/2;
}
if (s->x <= (s->img->w/2)) {
s->xs = -s->xs;
s->x = s->img->w/2;
}
//if (s->y >= (480 - s->img->h)) {
if (s->y >= 480-1) {
s->ys = -s->ys;
//s->y = 480 - s->img->h;
s->y = 480-1;
}
if (s->y <= (s->img->h)) {
s->ys = -s->ys;
s->y = s->img->h;
}
//if (timer % CLOUDGROWSPEED == 0) {
if (1) {
double growamt;
//int w,h;
SDL_Surface *ts, *cloudim;
cloudim = imageset[P_BLACKCLOUD].img[F_WALK1];
//w = s->img->w;
//h = s->img->h;
//ts = rotozoomSurfaceXY(s->img,0, 1.1 , 1.1 ,0);
growamt = ((double)CLOUDGROWAMT / (double)CLOUDGROWSPEED);
s->size += growamt;
//printf("grow, now %0.2f\n",s->size);
// TODO: free old first? depends if we ever use a preset one
ts = rotozoomSurfaceXY(cloudim,0,s->size, s->size, 0);
SDL_FreeSurface(s->img);
s->img = ts;
s->y += growamt;
}
} // end if !s->invuln
}
}
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;
}
// 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);
// teleporters
if ((tt->id == T_TELEPORT) || (tt->id == T_TELEPORT2)) {
//if (!isendoflev()) { // can't enter teleporters after level end to avoid cloud moving too far
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 (player->powerup != PW_CLOCK) {
if (!ismonster(s->id) && !isfruit(s->id)) {
movex(s, 1.5);
} else if (s->id == P_PLANT) {
movex(s, 1.5);
}
finished = B_TRUE;
}
} else if (tt->id == T_LEFT) {
if (player->powerup != PW_CLOCK) {
if (!ismonster(s->id) && !isfruit(s->id)) {
movex(s, -1.5);
} else if (s->id == P_PLANT) {
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) && (s->id != P_KINGSNAIL)) {
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
if (s->tramplayer == 1) {
curlevel->map[s->trampy * LEVELW + s->trampx] = getuniq(T_TRAMPUP);
} else {
curlevel->map2[s->trampy * LEVELW + s->trampx] = getuniq(T_TRAMPUP);
}
drawtile(temps, s->trampx, s->trampy);
}
} else {
tiletype_t *temptile;
// remember we were on it so it can release
s->ontramp = B_TRUE;
s->trampx = tilex;
s->trampy = tiley;
// which layer was it on?
temptile = gettile(curlevel->map2[s->trampy*LEVELW+s->trampx]);
if (temptile->id == T_TRAMPUP) {
s->tramplayer = 2;
} else {
s->tramplayer = 1;
}
// move it down then draw it
if (s->tramplayer == 1) {
curlevel->map[tiley*LEVELW+tilex] = getuniq(T_TRAMPDOWN);
} else {
curlevel->map2[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;
double turns;
double pspeed;
double dist;
SDL_Rect area,dst;
int speed = 16;
SDL_Surface *playerbg;
SDL_Surface *cloudbg;
sprite_t tempcloud;
sprite_t *cloud;
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);
// set player image
player->img = imageset[player->id].img[F_SHOOT];
// create buffer for player background
playerbg = SDL_CreateRGBSurface(SDL_SWSURFACE,
player->img->w+1, player->img->h+1,
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+1, cloud->img->h+1,
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
if (turns < 1) turns = 1;
// 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 (pxspeed > pyspeed) pspeed = pxspeed;
// else pspeed = pyspeed;
dist = sqrt((xdis*xdis) + (ydis*ydis));
pspeed = dist / turns;
// just to be sure
//pspeed += 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,pspeed,pspeed);
// 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
player->img = imageset[player->id].img[F_SHOOT];
grabbehind(player, playerbg);
grabbehind(cloud, cloudbg);
// draw player
if (curlevelnum != 1) {
drawsprite(player);
drawsprite(cloud);
}
// update screen
//SDL_GL_SwapBuffers();
SDL_UpdateRect(screen, 0,0,640,480);
SDL_framerateDelay(&manager);
// remove player
if (curlevelnum != 1) {
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,pspeed,pspeed);
// 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
player->img = imageset[player->id].img[F_SHOOT];
grabbehind(player, playerbg);
grabbehind(cloud, cloudbg);
// draw player
if (curlevelnum != 1) {
drawsprite(player);
drawsprite(cloud);
}
// update screen
//SDL_GL_SwapBuffers();
SDL_UpdateRect(screen, 0,0,640,480);
SDL_framerateDelay(&manager);
// remove player
if (curlevelnum != 1) {
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,pspeed,pspeed);
// 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
player->img = imageset[player->id].img[F_SHOOT];
grabbehind(player, playerbg);
grabbehind(cloud, cloudbg);
// draw player
if (curlevelnum != 1) {
drawsprite(player);
drawsprite(cloud);
}
// update screen
//SDL_GL_SwapBuffers();
SDL_UpdateRect(screen, 0,0,640,480);
SDL_framerateDelay(&manager);
// remove player
if (curlevelnum != 1) {
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,pspeed,pspeed);
// 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
player->img = imageset[player->id].img[F_SHOOT];
grabbehind(player, playerbg);
grabbehind(cloud, cloudbg);
// draw player
if (curlevelnum != 1) {
drawsprite(player);
drawsprite(cloud);
}
// update screen
//SDL_GL_SwapBuffers();
SDL_UpdateRect(screen, 0,0,640,480);
SDL_framerateDelay(&manager);
// remove player
if (curlevelnum != 1) {
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_SNAIL) {
if (s->recoiling) {
speed = 2;
} else {
if (s->angry) speed = 1;
else speed = 0.5;
}
} else if (id == P_SLUG) {
if (s->angry) speed = 1;
else speed = 0.5;
if (s->jumping) {
speed = 8; // very fast jumping
}
} 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) && (s->id != P_FISH)) {
if (!s->hasmask) {
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, int ttype) {
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->type = ttype;
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);
if (t->img == NULL) {
printf("rendertext failed for '%s'\n", t->txt);
fflush(stdout);
}
t->next = NULL;
lasttext = t;
return B_FALSE;
}
void movetext(void) {
text_t *t,*nextt;
int newhelpfreeze = B_FALSE;
for (t = text ; t ; t = nextt) {
nextt = t->next;
if (t->state == 0) {
t->size += TEXTSPEED;
if (t->size >= t->maxsize) {
if (t->type == TT_HELP) {
// freeze
oldlevelcomplete = levelcomplete;
levelcomplete = LV_HELPFREEZE;
t->state = t->delay;
newhelpfreeze = B_TRUE;
} else {
t->state = 1;
}
}
} else if (t->state == t->delay) {
t->size -= TEXTSPEED;
if (t->size <= 3) {
if ((t->type == TT_GAMEOVER) && (levelcomplete == LV_GAMEOVER)) {
// start game over timer
gameovertime = gtime;
}
killtext(t);
}
} else {
t->state++;
}
}
if (newhelpfreeze) {
// expire delay on all help text
for (t = text ; t ; t = t->next) {
if (t->type == TT_HELPSHADOW) {
t->state = t->delay;
}
}
}
}
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;
SDL_Color scorecol;
int i;
int numtoshow;
int scoreval;
if (forcegoodcard) {
if ((timer / LUCKYFLASH) % 2 == 0) {
scorecol = green;
} else {
scorecol = red;
}
} else {
scorecol = red;
}
if (wantframerate) {
scoreval = fps;
sprintf(tempm, "%d fps",scoreval);
} else {
scoreval = player->score;
addcommas(tempm, scoreval);
}
/* 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, scorecol);
area.x = 20;
area.y = 5;
area.w = 0;
area.h = 0;
SDL_BlitSurface(score, NULL, screen, &area);
SDL_FreeSurface(score);
// lives
// show 1 less than lives
numtoshow = player->lives-1;
area.x = 20;
area.y = 25;
area.w = 0;
area.h = 0;
// show "x5" for lots of lives
while (numtoshow >= 5) {
SDL_BlitSurface(head5, NULL, screen, &area);
area.x += (head->w + 3);
numtoshow -= 5;
}
for (i = 0; i < numtoshow; i++) {
SDL_BlitSurface(head, NULL, screen, &area);
area.x += (head->w + 3);
}
// level #
sprintf(tempm, "Level %d-%d",getcurworld(), getcurlevel());
/* 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);
/* text */
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);
// cards
if (levelcomplete != LV_DOPOKER) {
area.x = 20;
area.y = 55;
area.w = 0;
area.h = 0;
for (i = 0; i < player->numcards; i++) {
SDL_Surface *cardimg;
cardimg = imageset[player->card[i]].img[F_WALK1];
SDL_BlitSurface(cardimg, NULL, screen, &area);
area.x += (cardimg->w + 2);
}
}
drawcredits();
}
void drawtext(void) {
text_t *t;
SDL_Rect area;
for (t = text ; t ; t = t->next) {
if ((levelcomplete != LV_HELPFREEZE) || (t->type == TT_HELP)) {
/* 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) {
sprite_t *s;
text_t *t;
SDL_Rect area;
int clearfull = B_FALSE;
int *animtile;
/*
switch (player->powerup) {
case PW_CANNONFIRE:
case PW_SPRAYUP:
case PW_SPRAYDOWN:
case PW_BOMB:
case PW_RATSHAKE:
clearfull = B_TRUE;
break;
}
switch (levelcomplete) {
case LV_DOPOKER:
case LV_HELPFREEZE:
case LV_CLOUDLOOP:
case LV_CLOUD:
case LV_NEXTLEV:
clearfull = B_TRUE;
break;
}
if (curlevel->iced) clearfull = B_TRUE;
if (paused) clearfull = B_TRUE;
if (playedbell == BELL_DONESOUND) {
if (timer < BELLTIME) {
clearfull = B_TRUE;
}
}
if (isbosslevel(curlevelnum)) {
clearfull = B_TRUE;
}
*/
clearfull = B_TRUE;
if (clearfull) {
// clear the entire screen
SDL_BlitSurface(temps, NULL, screen, NULL);
} else {
for (s = sprite; s ; s = s->next) {
/*
// blit every tile coverting this sprite
startx = s->x - (s->img->w/2);
starty = s->y - s->img->h;
endx = s->x + (s->img->w/2);
endy = s->y;
for (y = starty ; y <= endy; y++) {
for (x = startx ; x <= endx; x++) {
drawtile(screen,x,y);
}
}*/
removesprite(s);
}
// animated tiles
for (animtile = curlevel->animtiles; animtile && *animtile != -1; animtile++) {
int offset,tx,ty;
offset = *animtile;
tx = offset%LEVELW; ty = offset / LEVELW;
area.x = tx*TILEW;
area.y = ty*TILEH;
area.w = TILEW;
area.h = TILEH;
SDL_BlitSurface(temps, &area, screen, &area);
}
// remove all text
for (t = text ; t ; t = t->next) {
/* 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(temps, &area, screen, &area);
}
// clear top left (with score +cards)
area.x = 0;
area.y = 0;
area.w = 200;
area.h = 80;
SDL_BlitSurface(temps, &area, screen, &area);
// clear top middle (level)
area.x = 250;
area.y = 0;
area.w = 100;
area.h = 25;
SDL_BlitSurface(temps, &area, screen, &area);
// clear bottom middle (credits)
area.x = 270;
area.y = 455;
area.w = 130;
area.h = 24;
SDL_BlitSurface(temps, &area, screen, &area);
}
}
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;
if (s->netsticky) {
drawdotline16(screen,sx,s->nety,xx,yy,orange,yellow);
} else {
drawline16(screen,sx,s->nety,xx,yy,white);
}
// add sparkle
xx = s->x + s->netdir*s->netlen;
if (levelcomplete != LV_HELPFREEZE) {
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;
SDL_Color *col1,*col2;
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;
// select colours
if (s->netsticky) {
col1 = &orange;
col2 = &yellow;
} else {
col1 = &white;
col2 = &white;
}
/* middle dotline */
drawdotline16(screen,s->x,s->y - s->img->h/2,
s->netxstart,s->netystart,*col1,*col2);
/* left dotline */
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;
drawdotline16(screen,s->x,s->y - s->img->h/2,x, y, *col1,*col2);
/* right dotline */
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;
drawdotline16(screen,s->x,s->y - s->img->h/2,x, y, *col1,*col2);
// 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;
}
if (s->netting) {
if (s->dir == D_LEFT) {
if (s->powerup == PW_ACCORDION) {
startx = 0;
} else {
startx -= 5;
}
} else {
if (s->powerup == PW_ACCORDION) {
endx = LEVELW-1;
} else {
endx += 5;
}
}
starty -= 1;
endy += 1;
}
if (s->doublejump) {
if (s->dir == D_LEFT) {
endx += 2;
} else {
startx -= 2;
}
}
// handle spider's web
if ((s->id == P_SPIDER) && ((s->ys != -99) || s->falling) && !s->dead && !s->caughtby && !s->iced) {
// do entire column
starty = 0;
}
// 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;
}
// return the x position of the middle of the ladder if so
int isonladder(sprite_t *s) {
int tx;
tiletype_t *tthere;
tthere = gettileat(s->x,s->y, &tx,NULL);
if (isladder(tthere->id)) {
return (tx*TILEW)+(TILEW/2);
}
// if ladder bove and climbing
if (isladderabove(s) && s->climbing) {
return isladderabove(s);
}
return B_FALSE;
}
// return the x position of the middle of the ladder if so
int isladderabove(sprite_t *s) {
tiletype_t *tthere;
int tx;
//tthere = gettileat(s->x,s->y-TILEH, NULL,NULL);
tthere = gettileat(s->x,s->y-s->img->h, &tx,NULL);
if (isladder(tthere->id)) {
return (tx*TILEW)+(TILEW/2);
}
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;
}
}
// 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) {
if (s->trampy != tiley) {
// change tile type
if (s->tramplayer == 1) {
curlevel->map[s->trampy * LEVELW + s->trampx] = getuniq(T_TRAMPUP);
} else {
curlevel->map2[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 (s->id != P_KSSHELL) {
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) {
// we just entered the water
s->swimming = B_TRUE;
s->watertimer = rand() % BUBBLETIME;
// play a splash sound
if (curlevel->iced != WATER_INPROGRESS) {
if (!s->caughtby && !s->dead) {
playfx(FX_SPLASH);
}
}
// give player a mask if they don't have one
if ((s == player) && (s->hasmask)) {
if (mask == NULL) {
mask = addsprite(P_MASK, player->x + MASKOFFSETX*player->dir, player->y + MASKOFFSETY, "mask");
}
}
// adjust x pos so we don't get stuck in a wall
if (s == player) adjustx(s, F_SWIM1);
else adjustx(s, F_WALK1);
if (s == player) {
// dim the music
Mix_VolumeMusic(MIX_MAX_VOLUME/3);
}
}
// generate bubbles
// just use s->x and s->y to add randomness, so that
// all sprites don't make bubbles at the same time
s->watertimer++;
if (s->watertimer >= BUBBLETIME) {
addsprite(P_BUBBLE,s->x + (s->dir*((s->img->w/4)+(rand() % 12 - 6))),s->y-(s->img->h/2),"bubble" );
s->watertimer = 0;
}
} else {
if (s->swimming) {
// exitted the water
s->swimming = B_FALSE;
if (s == player) {
Mix_VolumeMusic(MIX_MAX_VOLUME);
}
// get rid of the mask
if (mask) {
mask->dead = D_FINAL;
}
}
}
}
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;
}
}
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, 65);
}
}
}
/* 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;
if ((s->id == P_BIGSPEED) && (s->y < 480/2)) {
ontheground = B_FALSE;
} else if ((s->id == P_BIGNUMNETS) && (s->y < 480/2)) {
ontheground = B_FALSE;
} else {
ontheground = isonground(s);
}
if (ontheground) {
// reset doublejump
if (s->doublejump) {
s->useddoublejump = B_FALSE;
}
if (s->falling && s->iced) {
// when an iced monster hits the ground, it smashes
s->willbecome = P_DIAMOND;
if (s->id == P_SNAIL) s->id = P_SLUG;
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 if ((s->id == P_KINGSNAIL) && (s->timer1 == KSS_JUMPING)) { // special case
// king snail drops until he is at original height
if (s->y < s->timer3) { // above orig height
ontheground = B_FALSE;
} else {
s->dropping = B_FALSE;
s->falling = B_FALSE;
s->climbing = B_FALSE;
}
} else if ((s->id == P_SNAIL) && (s->lives == 0) && s->falling) {
// snail dies - this is actuall the snail's shell
s->dead = D_FINAL;
} else if ((s->id == P_KSSHELL) && s->falling) {
// king snail's shell dies when it hits the ground
s->dead = D_FINAL;
} else { // everyone else
s->dropping = B_FALSE;
s->falling = B_FALSE;
s->climbing = B_FALSE;
}
}
// snail also dies if it's been alive too long
if ((s->id == P_SNAIL) && (s->lives == 0) && (!s->invuln)) {
s->dead = D_FINAL;
}
// 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->climbing) {
if (s->falling == B_FALSE) {
s->fallspeed = 1;
}
s->falling = B_TRUE;
if (isinwater(s) && !s->iced) {
if (s->hasmask) {
// bob around
s->y += (sin(timer/5)/3);
} else {
// sink
s->y += (s->fallspeed/2);
}
} else {
s->y += s->fallspeed;
}
if ((timer % 10 == 0) && (s->fallspeed < FALLSPEED)) {
s->fallspeed++;
}
//}
//}
}
}
if (s->netting) {
if (s->powerup == PW_ACCORDION) {
if (s->netspeed > 0) {
s->netlen += s->netspeed;
} else {
s->netlen += (s->netspeed*2);
}
} else {
s->netlen += s->netspeed;
}
s->netting++;
if ((s->powerup == PW_ACCORDION) || (s->netting % 2 == 0)) {
if (s->netspeed > -NETSPEED) {
s->netspeed--;
} else {
if (s->netlen <= 0) {
s->netting = 0;
adjustx(s, F_WALK1);
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 && (s->slamangle >= (90 * (M_PI/180))))) {
/* finished slamming */
int xdiff,ydiff,xnet = 0,ynet = 0;
int pointsinc = 250;
int psize = 6;
int gotsomething = B_FALSE;
int gotclover = B_FALSE;
int hitboss = B_FALSE;
int macex,macey;
int numcaught = 0;
// initialise just in case
macex = s->x ; macey = s->y;
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 = s->x ; macey = s->y;
} else {
int xx,yy;
macex = s2->x;
macey = s2->y - s2->img->h/2;
// add explosion
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");
}
}
}
}
/* 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 */
curfruittype++;
if (fruittypes[curfruittype] == -1) {
curfruittype = 0;
}
}
if ((player->powerup == PW_MACE) && (s2->id == P_SNAIL)) {
// turn into a slug so that it really dies
s2->id = P_SLUG;
}
die(s2);
if ((player->powerup == PW_MACE) || (s2->id != P_SNAIL)) {
pointsinc *= 2;
psize += 10;
gotsomething++;
// killing 5 at a time gives us a clover for good luck
if ((!gotclover) && (gotsomething >= 5)) {
puffin(P_CLOVER, s2->x, s2->y, "clover", 0);
gotclover = B_TRUE;
}
}
xnet = s2->x;
ynet = s2->y - s2->img->h/2;
} 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 && !s2->invuln && (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) {
uncatch(s3);
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 diamond.
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 if (!gotcard & (gotsomething >= 4)) { // card for killing 5 at once
s2->willbecome = getrandomcard();
gotcard = B_TRUE;
} else if (!gotcard && (rand() % 5 == 0)) { // random chance of a card
s2->willbecome = getrandomcard();
gotcard = B_TRUE;
} 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;
}
}
if (s2->id == P_SNAIL) {
// turn into a slug so that it really dies
s2->id = P_SLUG;
}
/*
if (s2->id == P_SNAIL) {
// avoid triggering death code twice for caught snails
// since for them s->dead won't be set after we
// slam them.
if (s2->lives != 0) { // ie if we didn't just catch+slam it
die(s2);
}
} else {
*/
die(s2);
pointsinc *= 2;
psize += 10;
gotsomething++;
// killing 5 at a time gives us a clover for good luck
if ((!gotclover) && (gotsomething >= 5)) {
puffin(P_CLOVER, s2->x, s2->y, "clover", 0);
gotclover = B_TRUE;
}
//}
}
}
}
}
} // end if gotsomething || PW_MACE
// play sound effect
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 */
uncatch(s2);
}
}
s->netcaught = 0;
/* show points */
if (psize >= MAXLETTERHEIGHT) {
psize = MAXLETTERHEIGHT-1;
}
if (pointsinc > 250) {
/* give points to player */
//s->score += pointsinc;
sprintf(tempm, "%d",addscore(s, pointsinc));
addoutlinetext(xnet,ynet-TILEH, psize, tempm, &white,&black,POINTSDELAY, TT_NORM);
}
} // 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 */
if ((s->id == P_SNAIL) && (s->jumping) && (boss)) {
// unless we're a snail on kingsnail level
} else {
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) && (s->id != P_KINGSNAIL)) {
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) && !player->swimming) {
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, 15);
}
}
}
return B_FALSE;
}
void adjustheight(sprite_t *s) {
tiletype_t *tt;
int xoff,groundy;
int tilex,tiley;
double origy;
int totmoved;
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) {
origy = s->y;
totmoved = 0;
// keep moving up
while (tt->solid == S_SOLID) {
s->y--;
totmoved++;
// don't move more than 1 tile worth!
// don't go off top of screen
if ((totmoved >= TILEH) || (s->y <= TILEH)) {
break;
}
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, TT_NORM);
return B_TRUE;
} else if (s->id == P_BIGSPEED) {
playfx(FX_POWERUP);
player->permspeed = B_TRUE;
player->speed = PLAYERFAST;
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_LIFE, "SUPER SPEED UP!", &cyan,&black,POINTSDELAY, TT_NORM);
return B_TRUE;
} else if (s->id == P_BIGNUMNETS) {
playfx(FX_POWERUP);
player->permnumnets = 2;
player->netmax = 2;
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_LIFE, "PERMENANT 2X NETS!", &cyan,&black,POINTSDELAY, TT_NORM);
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, TT_NORM);
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, TT_NORM);
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, TT_NORM);
return B_TRUE;
} else if (s->id == P_MASKPOWERUP) {
playfx(FX_POWERUP);
player->hasmask = B_TRUE;
sprintf(tempm, "Scuba Mask!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY, TT_NORM);
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, TT_NORM);
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, TT_NORM);
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, TT_NORM);
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, TT_NORM);
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, TT_NORM);
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, TT_NORM);
// add puffs
for (xx = player->x - TILEW; xx <= player->x + TILEW; xx += TILEW) {
for (yy = player->y - TILEW*2; yy <= player->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, TT_NORM);
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, TT_NORM);
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, TT_NORM);
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, TT_NORM);
// pause music
Mix_PauseMusic();
return B_TRUE;
} else if (s->id == P_TAP) {
// flood level
playfx(FX_FLOOD);
sprintf(tempm, "Flood!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&blue,&black,POINTSDELAY, TT_NORM);
if (!curlevel->iced) {
curlevel->iced = WATER_INPROGRESS;
curlevel->icey = LEVELH-1;
// water for 20 seconds
watertime = 20;
}
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,&cyan,&black,POINTSDELAY, TT_NORM);
if (!curlevel->iced) {
curlevel->iced = ICE_INPROGRESS;
curlevel->icey = 0;
}
// postpone hurryup
nexthurryup += 10;
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, TT_NORM);
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, TT_NORM);
player->powerup = PW_CANNON;
puffin(P_CANNON, player->x, player->y,"cannon", 0);
return B_TRUE;
} else if (s->id == P_HONEY) {
playfx(FX_POWERUP);
player->netsticky = B_TRUE;
sprintf(tempm, "Sticky net!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY, TT_NORM);
return B_TRUE;
} else if (s->id == P_LIFE) {
extralife(player);
return B_TRUE;
} else if (s->id == P_PHONE) {
sprite_t *s2, *nexts;
// can't skip levels if the nxet one is a boss level
if (isbosslevel(curlevelnum + 1)) {
playfx(FX_ENGAGED);
sprintf(tempm, "Engaged");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY, TT_NORM);
} else {
playfx(FX_PHONE);
sprintf(tempm, "Telephone!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY, TT_NORM);
// set powerup
player->powerup = PW_PHONE;
skiplevels = 1;
// kill all enemies
for (s2 = sprite; s2 ; s2 = nexts) {
nexts = s2->next;
if (isbullet(s2->id) || ismonster(s2->id)) {
s2->dead = D_FINAL;
if (s2->caughtby) {
uncatch(s2);
player->netcaught--;
}
}
}
// call in cloud immediately
levelcomplete = LV_FINAL;
}
return B_TRUE;
} else if (s->id == P_UFO) {
int n;
int wid,hei;
sprite_t *sp;
playfx(FX_POWERUP);
sprintf(tempm, "Meteor Shower!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY, TT_NORM);
wid = imageset[P_METEOR].img[0]->w;
hei = imageset[P_METEOR].img[0]->h;
for (n = 0; n < 7; n++) {
sp = addsprite(P_METEOR, rand() % (640 - (wid*2)) + (wid/2), - (rand() % (hei*7)), "meteor");
sp->ys = (rand() % METEORMAXSPEED)+2;
}
lastmet = sp;
return B_TRUE;
} else if (s->id == P_STARPOWERUP) {
int n;
double ang;
sprite_t *sp;
// add a starburst
ang = (rand() % 360) * (M_PI/180);
for (n = 0; n < 8; n++) {
sp = addsprite(P_STAR, player->x, player->y, "star");
/*
switch (n) {
case 0: sp->xs = 0; sp->ys = -STARSPEED; break;
case 1: sp->xs = STARSPEED; sp->ys = -STARSPEED; break;
case 2: sp->xs = STARSPEED; sp->ys = 0; break;
case 3: sp->xs = STARSPEED; sp->ys = STARSPEED; break;
case 4: sp->xs = 0; sp->ys = STARSPEED; break;
case 5: sp->xs = -STARSPEED; sp->ys = STARSPEED; break;
case 6: sp->xs = -STARSPEED; sp->ys = 0; break;
case 7: sp->xs = -STARSPEED; sp->ys = -STARSPEED; break;
}
*/
sp->xs = cos(ang)*STARSPEED;
sp->ys = sin(ang)*STARSPEED;
ang = ang + (45 * (M_PI/180));
// use timer1 as the frame counter
sp->timer1 = rand() % STARFRAMES;
}
playfx(FX_STAR);
sprintf(tempm, "Shuriken!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY, TT_NORM);
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, TT_NORM);
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;
s2->lives = 0; // for snails
if (s2->caughtby) {
uncatch(s2);
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, TT_NORM);
// temp invincibility
player->invuln = SHIELDTIME;
return B_TRUE;
} else if (s->id == P_CLOVER) {
playfx(FX_POWERUP);
sprintf(tempm, "Lucky!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_CLOVER, tempm,&green2,&black,CLOVERDELAY, TT_NORM);
nextforcegoodcard = B_TRUE;
forcegoodcard = B_TRUE;
return B_TRUE;
} else if (s->id == P_ACCORDION) {
playfx(FX_POWERUP);
sprintf(tempm, "Extra Long Net!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY, TT_NORM);
player->powerup = PW_ACCORDION;
return B_TRUE;
} else if (s->id == P_WINGBOOTS) {
playfx(FX_POWERUP);
sprintf(tempm, "Double jump!");
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!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY, TT_NORM);
gunorigx = player->x;
gunorigy = player->y;
guntime = 10;
gundelay = 0; // used to control shooting speed
// turn off netting etc
player->netting = B_FALSE;
player->slamming = B_FALSE;
player->powerup = PW_GUNNER;
return B_TRUE;
} else if (s->id == P_ZAPPOWERUP) {
sprite_t *newsp;
playfx(FX_POWERUP);
sprintf(tempm, "Bug Zapper!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY, TT_NORM);
puffin(-1, player->x, player->y, "bzpuff", 0);
newsp = addsprite(P_ZAPPER, player->x,player->y, "bugzapper" );
newsp->timer1 = 0;
newsp->timer2 = ZAPPERDELAY;
newsp->timer3 = 0;
newsp->doomcount = 800;
return B_TRUE;
} else if (s->id == P_SKULL) {
playfx(FX_SKULL);
sprintf(tempm, "Power Down!");
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&black,&grey,POINTSDELAY, TT_NORM);
player->netmax = 1;
player->netsticky = B_FALSE;
player->netbig = B_FALSE;
return B_TRUE;
} else if (s->id == P_HELP) {
playfx(FX_POWERUP);
addoutlinetext(320,240,TEXTSIZE_HELP, s->name, &white,&black,HELPDELAY,TT_HELP);
return B_TRUE;
} else if (iscard(s->id)) {
if (player->numcards < MAXCARDS) {
sprite_t *newc;
int cardwidth,cardheight;
playfx(FX_CARD);
// show text
sprintf(tempm, getcardname(s->id));
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY,TT_NORM);
// add a "card" effect. it will move slowly towards the corner.
newc = addsprite(P_MOVINGCARD,s->x, s->y, "moving_card");
// timer1 is the actual cardid
// timer2 is the target x position
// timer3 is the target y position
cardwidth = imageset[P_FIRSTCARD].img[F_WALK1]->w;
cardheight = imageset[P_FIRSTCARD].img[F_WALK1]->h;
newc->timer1 = s->id;
newc->timer2 = CARDX + (player->numcards * (cardwidth+2)) + (cardwidth/2);
newc->timer3 = CARDY + cardheight;
// set image
newc->img = imageset[s->timer1].img[F_WALK1];
} else {
// is this possible?!
addoutlinetext(s->x,s->y - s->img->h/2, TEXTSIZE_POINTS, "Full cards!",&red,&black,POINTSDELAY,TT_NORM);
}
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,TT_NORM);
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(" -hs xx Set hiscore_server to http://xx.\n");
printf(" -hp xx Connect to hiscore_server on port 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 ( 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, "%s/sounds/",datadir);
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;
char filename[BUFLEN];
/* 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_COIN, "coin.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");
loadfx(FX_CRACK, "crack.wav");
loadfx(FX_PHONE, "phone.wav");
loadfx(FX_STAR, "star.wav");
loadfx(FX_STARHIT, "starhit.wav");
loadfx(FX_METEOR, "meteor.wav");
loadfx(FX_ENGAGED, "engaged.wav");
loadfx(FX_FLOOD, "flood.wav");
loadfx(FX_CARD, "card.wav");
loadfx(FX_POKER, "poker.wav");
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");
loadfx(FX_ZAP, "zap.wav");
loadfx(FX_SNAILPREPARE, "longdrum.wav");
loadfx(FX_WHOOSH, "whoosh.wav");
// load sound effects
for (i = 0; i < MAXFX; i++) {
if (!sfx[i]) {
return B_TRUE;
}
}
// set up callback
Mix_ChannelFinished(channeldone);
// load music
sprintf(filename, "%s/music/main.mod",datadir);
normalmusic = Mix_LoadMUS(filename);
if (!normalmusic) {
printf("can't load music\n");
return B_TRUE;
}
sprintf(filename, "%s/music/mainfast.mod",datadir);
fastmusic = Mix_LoadMUS(filename);
if (!fastmusic) {
printf("can't load fast music\n");
return B_TRUE;
}
sprintf(filename, "%s/music/boss.mod",datadir);
bossmusic = Mix_LoadMUS(filename);
if (!bossmusic) {
printf("can't load music\n");
return B_TRUE;
}
sprintf(filename, "%s/music/hiscore.mod",datadir);
hiscoremusic = Mix_LoadMUS(filename);
if (!hiscoremusic) {
printf("can't load 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);
if (player) {
if (player->swimming) {
Mix_VolumeMusic(MIX_MAX_VOLUME/3);
} else {
Mix_VolumeMusic(MIX_MAX_VOLUME);
}
} else {
Mix_VolumeMusic(MIX_MAX_VOLUME);
}
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) {
double ang,xs,ys;
// figure out angle to player
ang = atan2(dsty - p->y, dstx - p->x);
xs = (cos(ang) * xspeed);
ys = (sin(ang) * yspeed);
if (p->x < dstx) {
p->x += xs;
if (p->x > dstx) p->x = dstx;
}
if (p->x > dstx) {
p->x += xs;
if (p->x < dstx) p->x = dstx;
}
if (p->y < dsty) {
p->y += ys;
if (p->y > dsty) p->y = dsty;
}
if (p->y > dsty) {
p->y += ys;
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;
// create buffer for player background
/*
if (surf) {
SDL_FreeSurface(surf);
surf = NULL;
}
surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
s->img->w, s->img->h,
screen->format->BitsPerPixel, screen->format->Rmask,
screen->format->Gmask,screen->format->Bmask,
screen->format->Amask);
SDL_DisplayFormat(surf);
*/
// remember area behind player
area.x = s->x - s->img->w/2;
area.y = s->y - s->img->h;
area.w = surf->w;
area.h = surf->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");
}
// returns width
int drawoutlinetext(SDL_Surface *where,int x, int y, int size, char *msg, SDL_Color *col, SDL_Color *bgcol) {
SDL_Surface *surf;
SDL_Rect area;
int wid;
area.w=0;area.h=0;
//shadow
surf = TTF_RenderText_Solid(font[size], msg, *bgcol);
area.x = x-1; area.y = y; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x-1; area.y = y-1; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x; area.y = y-1; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x+1; area.y = y-1; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x+1; area.y = y; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x+1; area.y = y+1; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x; area.y = y+1; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x-1; area.y = y+1; SDL_BlitSurface(surf, NULL, where, &area);
// text
SDL_SetColors(surf, col, 1, 1);
area.x = x; area.y = y; SDL_BlitSurface(surf, NULL, where, &area);
wid = surf->w;
SDL_FreeSurface(surf);
return wid;
}
void drawoutlinecentretext(SDL_Surface *where, int y, int size, char *msg, SDL_Color *col, SDL_Color *bgcol) {
SDL_Surface *surf;
SDL_Rect area;
int x;
area.w=0;area.h=0;
//shadow
surf = TTF_RenderText_Solid(font[size], msg, *bgcol);
x = 320 - (surf->w/2);
area.x = x-1; area.y = y; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x-1; area.y = y-1; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x; area.y = y-1; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x+1; area.y = y-1; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x+1; area.y = y; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x+1; area.y = y+1; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x; area.y = y+1; SDL_BlitSurface(surf, NULL, where, &area);
area.x = x-1; area.y = y+1; SDL_BlitSurface(surf, NULL, where, &area);
// text
SDL_SetColors(surf, col, 1, 1);
area.x = x; area.y = y; SDL_BlitSurface(surf, NULL, where, &area);
SDL_FreeSurface(surf);
}
void addoutlinetext(int x, int y, int size, char *msg, SDL_Color *col, SDL_Color *bgcol, int delay, int ttype) {
int shadowtype;
if (ttype == TT_HELP) {
shadowtype = TT_HELPSHADOW;
} else {
shadowtype = TT_NORM;
}
addtext(x-1,y,size,msg,bgcol,delay,shadowtype); // outline
addtext(x-1,y-1,size,msg,bgcol,delay,shadowtype); // outline
addtext(x,y-1,size,msg,bgcol,delay,shadowtype); // outline
addtext(x+1,y-1,size,msg,bgcol,delay,shadowtype); // outline
addtext(x+1,y,size,msg,bgcol,delay,shadowtype); // outline
addtext(x+1,y+1,size,msg,bgcol,delay,shadowtype); // outline
addtext(x,y+1,size,msg,bgcol,delay,shadowtype); // outline
addtext(x-1,y+1,size,msg,bgcol,delay,shadowtype); // outline
addtext(x,y,size,msg,col,delay,ttype); // 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;
}
int addscore(sprite_t *s, int amt) {
int oldscore;
oldscore = s->score;
/*
// less points in easy mode
if (gamemode == GM_EASY) {
amt /= 2;
}
*/
s->score += amt;
// each multiple of 100,000
if (s == player) {
if ((s->score / 100000) > (oldscore / 100000)) {
extralife(s);
}
}
return amt;
}
void extralife(sprite_t *which) {
playfx(FX_COIN);
which->lives += 1;
addoutlinetext(which->x,which->y - which->img->h/2, TEXTSIZE_LIFE, "Extra life!",&green,&black,LIFEDELAY,TT_NORM);
}
// slowly change level to ice
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--) {
// 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:
curlevel->map2[yy*LEVELW+xx] = getuniq(T_ICETOP);
changed = B_TRUE;
break;
}
}
if (changed) {
drawtile(temps, xx, yy);
}
}
// finished?
if ((xx == LEVELW-1) && (yy == LEVELH-1)) {
curlevel->iced = ICE_COMPLETE;
break;
}
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;
if (imageset[s->id].img[framenum] == NULL) {
return;
}
if ((levelcomplete == LV_CLOUD) || (levelcomplete == LV_CLOUDLOOP)) {
return;
}
if (imageset[s->id].img[framenum]->w > s->img->w) {
diff = (imageset[s->id].img[framenum]->w - s->img->w) +1 ;
} else {
diff = (s->img->w - imageset[s->id].img[framenum]->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);
makeinvuln(player);
} else {
if (levelcomplete != LV_GAMEOVER) {
// special type - when it expires, gameover timer will start
addoutlinetext(320,240,TEXTSIZE_GAMEOVER,"Game Over",&red,&black,GAMEOVERDELAY,TT_GAMEOVER);
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) {
if (player->powerup != PW_GUNNER) {
checkcollide(s);
}
} else if (needscollisions(s->id)) {
checkcollide(s);
}
}
}
void drawallsprites(void) {
sprite_t *s;
int lastframe;
/* draw non-puff sprites */
for (s = sprite ; s ; s = s->next) {
if (s->id != P_PUFF) {
lastframe = s->frame;
drawsprite(s);
if (s->frame != lastframe) {
adjustx(s, s->frame);
}
}
}
/* draw puff sprites */
for (s = sprite ; s ; s = s->next) {
if (s->id == P_PUFF) drawsprite(s);
}
}
// check if sprite has passed off bottom/top of screen
void checkwrap(sprite_t *s) {
if (!s->dead) {
if (!iseffect(s->id)) {
/* if we've fallen off the bottom... */
if (s->y > (480+s->img->h)) {
// move to top
s->y = -s->img->h;
}
/* if we've gone off the top */
if (s->y < -s->img->h) {
// move to bottom
s->y = (480+s->img->h);
}
}
}
}
int getcurworld(void) {
return getworld(curlevelnum);
}
int getcurlevel(void) {
return getlevel(curlevelnum);
}
// returns how high a given monster will jump
int getmonjumpspeed(sprite_t *s ) {
switch (s->id) {
case P_SLUG:
if (s->jumpdir == 0) { // jumping straight up
return MONJUMPSPEED;
} else { // jumping horizontally
return 3;
}
case P_KINGSNAIL:
return 3;
default:
return MONJUMPSPEED;
}
}
// returns how long to pause before jumping
int getjumpdelay(int mid) {
switch (mid) {
case P_SLUG:
return 30;
case P_KINGSNAIL:
return 60;
default:
return 60;
}
}
void togglepause(void) {
if (paused) {
Mix_ResumeMusic();
paused = B_FALSE;
} else {
Mix_PauseMusic();
paused = B_TRUE;
}
}
void togglefullscreen(void) {
// close window
//SDL_Quit();
// set fullscreen variable
if (fullscreen) {
fullscreen = B_FALSE;
} else {
fullscreen = B_TRUE;
}
screen=SDL_SetVideoMode(screen->w,screen->h,screen->format->BitsPerPixel,SDL_SWSURFACE|(screen->flags&SDL_FULLSCREEN?0:SDL_FULLSCREEN));
// set title bar if required
if (!fullscreen) {
SDL_WM_SetCaption(progname, progname);
}
// redraw background
SDL_BlitSurface(temps, NULL, screen, NULL);
/* redo framerate manager */
SDL_setFramerate(&manager, WANTFPS);
}
void initsdl(void) {
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE)==-1) {
printf("SDL_Init: %s\n", SDL_GetError());
exit(1);
}
vidargs = 0;
if (fullscreen) {
vidargs |= SDL_FULLSCREEN;
}
#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
// set title bar
SDL_WM_SetCaption(progname, progname);
if (!screen) {
printf("Failed to open window: %s\n", SDL_GetError());
exit(1);
}
SDL_ShowCursor(SDL_DISABLE);
// find joysticks
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
joy = SDL_JoystickOpen(0);
if (joy) {
printf("%d joystick(s) detected and enabled.\n",SDL_NumJoysticks());
SDL_JoystickEventState(SDL_ENABLE);
havejoysticks = B_TRUE;
joybuttons = SDL_JoystickNumButtons(joy);
printf("Joy 1 has %d buttons\n",joybuttons);
setjoymappings();
} else {
havejoysticks = B_FALSE;
printf("No joysticks found.\n");
}
SDL_EnableUNICODE(1);
}
// player collects the given fruit
void getfruit(sprite_t *giveto, sprite_t *fruit, int multiplier) {
char tempm[MIDBUFLEN];
int gotscore = fruit->score;
int modscore = gotscore; // default
int i;
/* kill the fruit */
fruit->dead = D_FINAL;
/* give points to the player */
for (i = 0; i < multiplier; i++) {
modscore = addscore(giveto, gotscore);
}
if (fruit->id == P_RANDOM) {
// change id to whatever it currently looks like
fruit->id = fruit->timer1;
}
/* handle fruit effects */
if (!dofruiteffect(fruit)) {
playfx(FX_FRUIT);
if (multiplier <= 1) {
sprintf(tempm, "%d", modscore);
addoutlinetext(fruit->x,fruit->y - fruit->img->h/2, TEXTSIZE_POINTS, tempm, &white,&black,POINTSDELAY,TT_NORM);
} else {
sprintf(tempm, "%d x %d" , modscore,multiplier);
addoutlinetext(fruit->x,fruit->y - fruit->img->h/2, TEXTSIZE_POINTS + 2*multiplier, tempm, &white,&black,POINTSDELAY,TT_NORM);
}
}
}
// slowly change level to water
void doflood(void) {
int yy,xx,changed;
int l2id;
// just in case
if (!curlevel->iced) {
curlevel->iced = WATER_INPROGRESS;
}
// slowly change a level to water
//if (timer % ICESPEED == 0) {
for (yy = curlevel->icey ; (yy <= curlevel->icey+1) && (yy < LEVELH); yy++) {
for (xx = 0; xx < LEVELW; xx++) {
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) {
// maybe change to water ice layer
tt = gettile(curlevel->map[yy*LEVELW+xx]);
switch (tt->id) {
case T_BLANK:
case T_SKY:
case T_SPIKES:
case T_WATER:
case T_WATERTOP:
if (yy == curlevel->icey) {
curlevel->map2[yy*LEVELW+xx] = getuniq(T_WATERTOP);
} else {
curlevel->map2[yy*LEVELW+xx] = getuniq(T_WATER);
}
changed = B_TRUE;
break;
}
} else { // certain l2 ids can still be replaced
switch (l2id) {
case T_SKY:
case T_WATER:
case T_WATERTOP:
if (yy == curlevel->icey) {
curlevel->map2[yy*LEVELW+xx] = getuniq(T_WATERTOP);
} else {
curlevel->map2[yy*LEVELW+xx] = getuniq(T_WATER);
}
changed = B_TRUE;
break;
}
}
if (changed) {
drawtile(temps, xx, yy);
}
}
}
// decrease icey for next time
curlevel->icey--;
if (curlevel->icey < 0) {
curlevel->iced = WATER_COMPLETE;
}
}
// put flood effect back to normal
void undoflood(void) {
int x,y;
for (y = 0 ; y < LEVELH; y++) {
for (x = 0 ; x < LEVELW; x++) {
curlevel->map2[y*LEVELW+x] = savemap[y*LEVELW+x];
drawtile(temps, x, y);
}
}
SDL_BlitSurface(temps, NULL, screen, NULL);
}
int haspowerup(sprite_t *s, int pid) {
switch (pid) {
case P_SPEED:
if (s->speed != 1) {
return B_TRUE;
}
break;
case P_NUMNETS:
if (s->netmax >= 4 ) {
return B_TRUE;
}
break;
case P_BIGNET:
if (s->netbig) {
return B_TRUE;
}
break;
case P_MASKPOWERUP:
if (s->hasmask) {
return B_TRUE;
}
break;
case P_TROPHY:
if ((s->netmax >= 4) && (s->netbig) && (s->speed != 1)) {
return B_TRUE;
}
break;
case P_HELMET:
if (s->armour) {
return B_TRUE;
}
break;
case P_BELL:
if (s->hasbell) {
return B_TRUE;
}
break;
case P_GEMBOOST:
if (s->gemboost >= 3) {
return B_TRUE;
}
break;
case P_HONEY:
if (s->netsticky) {
return B_TRUE;
}
break;
case P_WINGBOOTS:
if (s->doublejump) {
return B_TRUE;
}
break;
}
return B_FALSE;
}
void gaincard(sprite_t *s, int cardid) {
int i;
char msg[BUFLEN];
// just in case
if (s->numcards >= MAXCARDS) return;
// gain the card
s->card[s->numcards] = cardid;
s->numcards++;
// check for a full hand of cards
if (s->numcards == MAXCARDS) {
int cardwidth,cardheight;
SDL_Rect area;
sprite_t *newsp;
cardwidth = imageset[P_FIRSTCARD].img[F_WALK1]->w;
cardheight = imageset[P_FIRSTCARD].img[F_WALK1]->h;
// remember old level state
oldlevelcomplete = levelcomplete;
// change state to LV_CARDCOMPLETE
// (in this state, everything is frozen )
levelcomplete = LV_DOPOKER;
// generate FIVECARDS sprite
newsp = addsprite(P_FIVECARDS, CARDX, CARDY, "fivecards");
// create blank image
newsp->img = SDL_CreateRGBSurface(SDL_SWSURFACE,
(cardwidth+2)*MAXCARDS, cardheight,
screen->format->BitsPerPixel, screen->format->Rmask,
screen->format->Gmask,screen->format->Bmask,
screen->format->Amask);
SDL_FillRect(newsp->img, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
// fill in the cards
area.x = 0;
area.y = 0;
area.w = 0;
area.h = 0;
for (i = 0; i < player->numcards; i++) {
SDL_Surface *cardimg;
cardimg = imageset[player->card[i]].img[F_WALK1];
if (cardimg == NULL){
printf("ERROR! Card image is null!!\n");
fflush(stdout);
}
SDL_BlitSurface(cardimg, NULL, newsp->img, &area);
area.x += (cardimg->w + 2);
}
// adjust x value so we can use drawsprite()
newsp->x += (newsp->img->w / 2);
newsp->y += (newsp->img->h);
// figure out what happened
pokereffect = getpokereffect(player);
getpokermsg2(pokereffect, msg);
// display BIG message
addoutlinetext(320,180,TEXTSIZE_POKER,getpokermsg(pokereffect),&green, &black, POKERDELAY,TT_NORM);
addoutlinetext(320,290,TEXTSIZE_POKER,msg,&green, &black, POKERDELAY,TT_NORM);
// pause music
Mix_PauseMusic();
// sound effect
playfx(FX_POKER);
}
}
// figure out what poker effect to apply and set variables
// also record which cards were used in pl->usedcards
int getpokereffect(sprite_t *pl) {
int i, c, count, curval, val, suit, found;
// default
pokerpoints = 0;
/// mark all player cards as unused
for (c = 0; c < MAXCARDS; c++) {
pl->usedcard[c] = B_FALSE;
}
// * check for royal straight flush (perm trophy)
// for each card, count up from here
for (i = 0; i < MAXCARDS; i++) {
count = 1;
val = getcardvalue(pl->card[i]);
suit = getcardsuit(pl->card[i]);
curval = val;
found = B_TRUE;
// start at this card
while (found && count < 5) {
found = B_FALSE;
for (c = 0; c < MAXCARDS; c++) {
if (c != i) {
if (getcardsuit(pl->card[c]) == suit) { // same suit?
if (getcardvalue(pl->card[c]) == curval + 1) {
curval++;
count++;
found = B_TRUE;
}
}
}
}
}
// got it?
if (count >= 5) {
if (curval == 13) { // last card was a king
// GOT IT!
/// mark all player cards as USED
for (c = 0; c < MAXCARDS; c++) {
pl->usedcard[c] = B_TRUE;
}
return PE_ROYALFLUSH;
}
}
}
// * check for straight flush (skip 7 levels)
for (i = 0; i < MAXCARDS; i++) {
count = 1;
val = getcardvalue(pl->card[i]);
suit = getcardsuit(pl->card[i]);
curval = val;
found = B_TRUE;
// start at this card
while (found && count < 5) {
found = B_FALSE;
for (c = 0; c < MAXCARDS; c++) {
if (c != i) {
if (getcardsuit(pl->card[c]) == suit) { // same suit?
if (getcardvalue(pl->card[c]) == curval + 1) {
curval++;
count++;
found = B_TRUE;
}
}
}
}
}
// got it?
if (count >= 5) {
// GOT IT!
/// mark all player cards as USED
for (c = 0; c < MAXCARDS; c++) {
pl->usedcard[c] = B_TRUE;
}
return PE_STRAIGHTFLUSH;
}
}
// * check for four of a kind (perm 4 nets )
for (i = 0; i < MAXCARDS; i++) {
val = getcardvalue(pl->card[i]);
count = 1;
for (c = 0; c < MAXCARDS; c++) {
if (c != i) {
if (getcardvalue(pl->card[c]) == val ) {
count++;
}
}
}
// got it?
if (count >= 4) {
// GOT IT!
/// mark all player cards with correct value as USED
for (c = 0; c < MAXCARDS; c++) {
if (getcardvalue(pl->card[c]) == val) {
pl->usedcard[c] = B_TRUE;
}
}
return PE_FOUR;
}
}
// * check for full house (perm bignet )
for (i = 0; i < MAXCARDS; i++) {
val = getcardvalue(pl->card[i]);
count = 1;
for (c = 0; c < MAXCARDS; c++) {
if (c != i) {
if (getcardvalue(pl->card[c]) == val ) {
count++;
}
}
}
// got it?
if (count >= 3) {
int ii, val2,cc,count2;
// got a triple with this card - now look for doubles
for (ii = 0; ii < MAXCARDS; ii++) {
val2 = getcardvalue(pl->card[ii]);
if (val2 != val) { // must be a DIFFERENT value!
count2 = 1;
for (cc = 0; cc < MAXCARDS; cc++) {
if (cc != ii) {
if (getcardvalue(pl->card[cc]) == val2 ) {
count2++;
}
}
}
// got it?
if (count2 >= 2) {
// GOT IT! - double with this one
/// mark all player cards as USED
for (c = 0; c < MAXCARDS; c++) {
pl->usedcard[c] = B_TRUE;
}
return PE_FULLHOUSE;
}
}
}
}
}
// * check for flush (skip 5 levels )
for (i = 0; i < MAXCARDS; i++) {
suit = getcardsuit(pl->card[i]);
count = 1;
for (c = 0; c < MAXCARDS; c++) {
if (c != i) {
if (getcardsuit(pl->card[c]) == suit ) {
count++;
}
}
}
// got it?
if (count >= 5) {
// GOT IT!
/// mark all player cards as USED
for (c = 0; c < MAXCARDS; c++) {
pl->usedcard[c] = B_TRUE;
}
return PE_FLUSH;
}
}
// * check for straight (perm honey)
for (i = 0; i < MAXCARDS; i++) {
count = 1;
val = getcardvalue(pl->card[i]);
suit = getcardsuit(pl->card[i]);
curval = val;
found = B_TRUE;
// start at this card
while (found && count < 5) {
found = B_FALSE;
for (c = 0; c < MAXCARDS; c++) {
if (c != i) {
if (getcardvalue(pl->card[c]) == curval + 1) {
curval++;
count++;
found = B_TRUE;
}
}
}
}
// got it?
if (count >= 5) {
// GOT IT!
/// mark all player cards as USED
for (c = 0; c < MAXCARDS; c++) {
pl->usedcard[c] = B_TRUE;
}
return PE_STRAIGHT;
}
}
// * check for triple (skip 3 levels )
for (i = 0; i < MAXCARDS; i++) {
val = getcardvalue(pl->card[i]);
count = 1;
for (c = 0; c < MAXCARDS; c++) {
if (c != i) {
if (getcardvalue(pl->card[c]) == val ) {
count++;
}
}
}
// got it?
if (count >= 3) {
// GOT IT!
/// mark all player cards with correct value as USED
for (c = 0; c < MAXCARDS; c++) {
if (getcardvalue(pl->card[c]) == val) {
pl->usedcard[c] = B_TRUE;
}
}
return PE_TRIPLE;
}
}
// * check for two pair (value * 2000 for each card in pair )
for (i = 0; i < MAXCARDS; i++) {
val = getcardvalue(pl->card[i]);
count = 1;
for (c = 0; c < MAXCARDS; c++) {
if (c != i) {
if (getcardvalue(pl->card[c]) == val ) {
count++;
}
}
}
// got it?
if (count >= 2) {
int ii, val2,cc,count2;
// got a pair with this card - now look for another one
for (ii = 0; ii < MAXCARDS; ii++) {
val2 = getcardvalue(pl->card[ii]);
if (val2 != val) { // must be a DIFFERENT value!
count2 = 1;
for (cc = 0; cc < MAXCARDS; cc++) {
if (cc != ii) {
if (getcardvalue(pl->card[cc]) == val2 ) {
count2++;
}
}
}
// got it?
if (count2 >= 2) {
// GOT IT! - double with this one
/// mark all player cards as USED and assign points
pokerpoints = 0;
for (c = 0; c < MAXCARDS; c++) {
int thisval;
thisval = getcardvalue(pl->card[c]);
if ((thisval == val) || (thisval == val2)) {
pokerpoints += (thisval * 4000);
pl->usedcard[c] = B_TRUE;
}
}
return PE_TWOPAIR;
}
}
}
}
}
// * check for pair (value * 2000 for each card in pair )
for (i = 0; i < MAXCARDS; i++) {
val = getcardvalue(pl->card[i]);
count = 1;
for (c = 0; c < MAXCARDS; c++) {
if (c != i) {
if (getcardvalue(pl->card[c]) == val ) {
count++;
}
}
}
// got it?
if (count >= 2) {
// GOT IT!
// assign points and
// mark all player cards with correct value as USED
pokerpoints = 0;
for (c = 0; c < MAXCARDS; c++) {
int thisval;
thisval = getcardvalue(pl->card[c]);
if (thisval == val) {
pokerpoints += (thisval * 4000);
pl->usedcard[c] = B_TRUE;
}
}
return PE_PAIR;
}
}
// * default is high card (value * 1000)
// find highest card
count = -1;// use this to track highest card index
curval = -1;
for (i = 0; i < MAXCARDS; i++) {
val = getcardvalue(pl->card[i]);
if (val > curval) {
curval = val;
count = i;
}
}
// assign points
pokerpoints = curval * 1000;
// mark used card
for (c = 0; c < MAXCARDS; c++) {
if (c == count) {
pl->usedcard[c] = B_TRUE;
} else {
pl->usedcard[c] = B_FALSE;
}
}
return PE_HIGHCARD;
}
// actually apply the current poker effect (in global "pokereffect")
void dopokereffect(sprite_t *pl, int effect) {
char tempmsg[BUFLEN];
sprite_t *s2, *nexts;
switch (effect) {
case PE_HIGHCARD:
case PE_TWOPAIR:
case PE_PAIR:
// points
sprintf(tempmsg, "%d",addscore(pl, pokerpoints));
addoutlinetext(pl->x,pl->y - pl->img->h/2, TEXTSIZE_BONUS, tempmsg,&cyan,&black,POKERMSGDELAY,TT_NORM);
break;
case PE_TRIPLE:
// skip 3 levels
sprintf(tempmsg, "Skip 3 levels!");
addoutlinetext(pl->x,pl->y - pl->img->h/2, TEXTSIZE_BONUS, tempmsg,&cyan,&black,POKERMSGDELAY,TT_NORM);
// set powerup
player->powerup = PW_PHONE;
skiplevels = 2;
// kill all enemies
for (s2 = sprite; s2 ; s2 = nexts) {
nexts = s2->next;
if (isbullet(s2->id) || ismonster(s2->id)) {
s2->dead = D_FINAL;
if (s2->caughtby) {
uncatch(s2);
player->netcaught--;
}
}
}
// call in cloud immediately
levelcomplete = LV_FINAL;
break;
case PE_STRAIGHT:
// permenant big net
pl->permbignet = B_TRUE;
pl->netbig = B_TRUE;
sprintf(tempmsg, "Permenant Big Nets!");
addoutlinetext(pl->x,pl->y - pl->img->h/2, TEXTSIZE_BONUS, tempmsg,&cyan,&black,POKERMSGDELAY,TT_NORM);
break;
case PE_FLUSH:
// skip 5 levels
sprintf(tempmsg, "Skip 5 levels!");
addoutlinetext(pl->x,pl->y - pl->img->h/2, TEXTSIZE_BONUS, tempmsg,&cyan,&black,POKERMSGDELAY,TT_NORM);
// set powerup
player->powerup = PW_PHONE;
skiplevels = 4;
// kill all enemies
for (s2 = sprite; s2 ; s2 = nexts) {
nexts = s2->next;
if (isbullet(s2->id) || ismonster(s2->id)) {
s2->dead = D_FINAL;
if (s2->caughtby) {
uncatch(s2);
player->netcaught--;
}
}
}
// call in cloud immediately
levelcomplete = LV_FINAL;
break;
case PE_FULLHOUSE:
// permenant double jump
pl->permdoublejump = B_TRUE;
pl->doublejump = B_TRUE;
sprintf(tempmsg, "Permenant Double Jump!");
addoutlinetext(pl->x,pl->y - pl->img->h/2, TEXTSIZE_BONUS, tempmsg,&cyan,&black,POKERMSGDELAY,TT_NORM);
break;
case PE_FOUR:
// permenant max nets
pl->permnumnets = 4;
pl->netmax = 4;
sprintf(tempmsg, "Permenant Max Nets!");
addoutlinetext(pl->x,pl->y - pl->img->h/2, TEXTSIZE_BONUS, tempmsg,&cyan,&black,POKERMSGDELAY,TT_NORM);
break;
case PE_STRAIGHTFLUSH:
// skip 7 levels
sprintf(tempmsg, "Skip 7 levels!");
addoutlinetext(pl->x,pl->y - pl->img->h/2, TEXTSIZE_BONUS, tempmsg,&cyan,&black,POKERMSGDELAY,TT_NORM);
// set powerup
player->powerup = PW_PHONE;
skiplevels = 6;
// kill all enemies
for (s2 = sprite; s2 ; s2 = nexts) {
nexts = s2->next;
if (isbullet(s2->id) || ismonster(s2->id)) {
s2->dead = D_FINAL;
if (s2->caughtby) {
uncatch(s2);
player->netcaught--;
}
}
}
// call in cloud immediately
levelcomplete = LV_FINAL;
break;
case PE_ROYALFLUSH:
// permenant trophy
pl->permspeed = B_TRUE;
pl->permbignet = B_TRUE;
pl->permnumnets = 4;
pl->speed = 2;
pl->netbig = B_TRUE;
pl->netmax = 4;
sprintf(tempmsg, "Permenant full power!");
addoutlinetext(pl->x,pl->y - pl->img->h/2, TEXTSIZE_BONUS, tempmsg,&cyan,&black,POKERMSGDELAY,TT_NORM);
break;
}
}
char *getpokermsg(int effect) {
switch (effect) {
case PE_HIGHCARD:
return "High Card";
case PE_PAIR:
return "Pair!";
case PE_TWOPAIR:
return "Two Pairs!";
case PE_TRIPLE:
return "Triple!";
case PE_STRAIGHT:
return "Straight!";
case PE_FLUSH:
return "Flush!";
case PE_FULLHOUSE:
return "Full House!";
case PE_FOUR:
return "Four of a Kind!";
case PE_STRAIGHTFLUSH:
return "Straight Flush!";
case PE_ROYALFLUSH:
return "ROYAL FLUSH!";
}
return "unknown";
}
char *getpokermsg2(int effect, char *buf) {
sprintf(buf, "Unknown");
switch (effect) {
case PE_HIGHCARD:
sprintf(buf, "%d points",pokerpoints);
break;
case PE_PAIR:
sprintf(buf, "%d points",pokerpoints);
break;
case PE_TWOPAIR:
sprintf(buf, "%d points",pokerpoints);
break;
case PE_TRIPLE:
sprintf(buf, "Skip 3 levels");
break;
case PE_STRAIGHT:
sprintf(buf, "Permenant Big Nets");
break;
case PE_FLUSH:
sprintf(buf, "Skip 5 levels");
break;
case PE_FULLHOUSE:
sprintf(buf, "Permenant Double Jump");
break;
case PE_FOUR:
sprintf(buf, "Permenant 4x Nets");
break;
case PE_STRAIGHTFLUSH:
sprintf(buf, "Skip 7 levels");
break;
case PE_ROYALFLUSH:
sprintf(buf, "Permenant Full Power");
break;
}
return buf;
}
void handleinput(void) {
SDL_Event event;
/* check for key releases */
SDL_PumpEvents();
while (SDL_PollEvent(&event)) {
if (event.type == SDL_KEYUP) {
if ((event.key.keysym.sym == SDLK_5) || (event.key.keysym.sym == SDLK_6)) {
credits++;
playfx(FX_COIN);
}
if (levelcomplete == LV_HELPFREEZE) {
if (event.key.keysym.sym == SDLK_z) {
levelcomplete = oldlevelcomplete;
}
}
} else if (havejoysticks && (event.type == SDL_JOYBUTTONUP)) {
if (levelcomplete == LV_HELPFREEZE) {
if (joybuttontokey(event.jbutton.button) == SDLK_z) {
levelcomplete = oldlevelcomplete;
}
}
}
}
// don't accept other input in helpfreeze state
if (levelcomplete == LV_HELPFREEZE) return;
// now handle key preses /joystick
getinput();
/* ************************************************************
These keys can always be pressed
************************************************************/
// toggle fullscreen
if (keydown(SDLK_f)) {
if (toggletimer == 0) {
// always pause first
paused = B_TRUE;
togglefullscreen();
toggletimer = 40;
}
}
// pause
if (keydown(SDLK_p)) {
if (toggletimer == 0) {
togglepause();
toggletimer = 80;
}
}
// quit
if (keydown(SDLK_ESCAPE)) {
exit(1);
}
if (keydown(SDLK_F2)) { // show framerate
if (toggletimer == 0) {
if (wantframerate) {
wantframerate = B_FALSE;
} else {
wantframerate = B_TRUE;
}
toggletimer = 80;
}
}
if (cheat) {
if (keydown(SDLK_q)) {
gtime = nexthurryup-1;
//gtime = nexthurryup+14;
}
if (keydown(SDLK_b)) {
player->lives = 1;
//gtime = nexthurryup+14;
}
if (keydown(SDLK_i)) {
if (player->numcards == 0) {
/*
gaincard(player, getrandomcard());
gaincard(player, getrandomcard());
gaincard(player, getrandomcard());
gaincard(player, getrandomcard());
*/
gaincard(player, P_CARDD4);
gaincard(player, P_CARDD5);
gaincard(player, P_CARDD1);
gaincard(player, P_CARDDJ);
gaincard(player, P_CARDD10);
}
}
if (keydown(SDLK_l)) {
if (toggletimer == 0) {
addscore(player, 100001);
toggletimer = 30;
}
}
if (keydown(SDLK_v)) { // cheat
if (toggletimer == 0) {
// all powerups
playfx(FX_POWERUP);
player->netmax = 4; // all nets
player->netbig = B_TRUE; // big net
player->speed = 2; // fast
player->netsticky = B_TRUE;
player->doublejump = B_TRUE;
player->hasbell = B_TRUE;
player->hasmask = B_TRUE;
sprintf(tempm, "Cheat!");
addoutlinetext(player->x,player->y - player->img->h/2, TEXTSIZE_POINTS, tempm,&white,&black,POINTSDELAY,TT_NORM);
toggletimer = 80;
}
}
if (keydown(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,TT_NORM);
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) {
uncatch(s2);
player->netcaught--;
}
die(s2);
}
}
toggletimer = 50;
}
}
if (keydown(SDLK_d)) {
if (toggletimer == 0) {
// dump sprites
dumpsprites();
toggletimer = 50;
}
}
if (keydown(SDLK_d)) {
sprite_t *s2,*nexts;
// set powerup
player->powerup = PW_PHONE;
// kill all enemies
for (s2 = sprite; s2 ; s2 = nexts) {
nexts = s2->next;
if (isbullet(s2->id) || ismonster(s2->id)) {
s2->dead = D_FINAL;
if (s2->caughtby) {
uncatch(s2);
player->netcaught--;
}
}
}
}
}
/* ************************************************************
Player movement
************************************************************/
if ((!paused) && (!levelcomplete != LV_DOPOKER) && (levelcomplete != LV_CLOUDLOOP)) {
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;
}
}
if (keydown(SDLK_UP)) {
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->swimming) && (player->hasmask)) {
// swimming
swimup(player);
} else if (!player->netting && !player->slamming && !player->jumping) {
// climbing
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;
}
}
}
}
if (keydown(SDLK_DOWN)) {
if ((player->swimming) && (player->hasmask)) {
// swimming
swimdown(player);
} else 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);
}
}
}
}
// ignore other events
while (SDL_PollEvent(&event)) {
}
}
void swimup(sprite_t *pl) {
pl->falling = B_FALSE;
if (!isroofabove(pl)) {
// is there water above us too?
if (isinwaterpoint(pl->x, pl->y-TILEH)) {
double swimspeed;
swimspeed = getspeed(pl);
if (pl->hasmask) {
swimspeed *= 1.5;
} else {
swimspeed *= 2;
}
// if so, swim up
pl->y -= swimspeed;
} else {
int whichway;
// if not, jump up
pl->climbing = B_FALSE;
if (keydown(SDLK_RIGHT)) {
whichway = 1;
} else if (keydown(SDLK_LEFT)) {
whichway = -1;
} else {
whichway = 0;
}
playfx(FX_SPLASH);
jump(pl, whichway);
}
}
}
void swimdown(sprite_t *pl) {
pl->falling = B_FALSE;
if (!isonground(pl)) {
double swimspeed;
// swim down
swimspeed = getspeed(pl);
if (pl->hasmask) {
swimspeed *= 1.5;
} else {
swimspeed *= 2;
}
pl->y += swimspeed;
}
}
void trytojump(sprite_t *pl) {
// can't jump while slamming
if (pl->slamming) return;
if (isinwater(pl)) { // in water?
// swim up
if (!pl->jumping) {
swimup(pl);
}
} else { // not in water
// jump
if (!pl->jumping || (pl->doublejump && pl->doublejumpready && !pl->useddoublejump)) {
if (!pl->falling || (pl->doublejump && pl->doublejumpready && !pl->useddoublejump)) {
if (isonground(pl) || isonladder(pl) || pl->doublejump ) {
/* dropping through a bridge */
if (keydown(SDLK_DOWN)) {
if (isonbridge(pl) && !pl->falling) {
/* drop down */
pl->dropping = B_TRUE;
pl->dropx = pl->x / TILEW;
pl->dropy = pl->y / TILEH;
}
} else { // jumping
int whichway;
if (keydown(SDLK_RIGHT)) {
whichway = 1;
} else if (keydown(SDLK_LEFT)) {
whichway = -1;
} else {
whichway = 0;
}
jump(pl, whichway);
}
}
}
}
}
}
void trytoslam(sprite_t *pl) {
if ((!pl->netting) && (!pl->slamming)) {
/* slam */
if ((!pl->slamming) && (isonground(pl) && !pl->climbing)) {
playfx(FX_SLAM);
pl->slamming = B_TRUE;
pl->slamangle = 0;
pl->netxstart = pl->x - (pl->img->w/2)*pl->dir;
adjustx(pl, F_SLAM1);
pl->netystart = pl->y;
/* handle mace */
if (pl->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 = pl->x;
s->y = pl->y - (imageset[pl->id].img[F_SHOOT]->h / 2) + 5;
found = B_TRUE;
}
}
if (!found) {
addsprite(P_MACE, pl->x,
pl->y - (imageset[pl->id].img[F_SHOOT]->h / 2) + 5,
"mace");
}
}
}
}
}
void trytoshoot(sprite_t *pl) {
if ((!pl->netting) && (!pl->slamming)) {
if ((pl->netcaught < pl->netmax) && (pl->climbing == B_FALSE)) {
// handle cannon
if (pl->powerup == PW_CANNON) {
playfx(FX_CANNON);
pl->powerup = PW_CANNONFIRE;
pl->timer3 = CANNONSIZE+5; // use this for size
} else {
/* shoot net */
playfx(FX_SHOOT);
pl->netting = 1;
adjustx(pl, F_SHOOT);
if (pl->powerup == PW_ACCORDION) {
pl->netspeed = ACCNETSPEED;
} else if (pl->netbig) {
pl->netspeed = BIGNETSPEED;
} else {
pl->netspeed = NETSPEED;
}
pl->netlen = 0;
pl->netdir = pl->dir;
// set up initial...
pl->nety = pl->y - (pl->img->h/2) + 2;
}
/* handle boxing glove */
if (pl->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 = pl->x;
s->y = pl->y - (imageset[pl->id].img[F_SHOOT]->h / 2) + 5;
found = B_TRUE;
}
}
if (!found) {
addsprite(P_GLOVE, pl->x,
pl->y - (imageset[pl->id].img[F_SHOOT]->h / 2) + 5,
"glove");
}
}
}
}
}
int keydown(int checkfor) {
// check for keypress
if (keys[checkfor]) {
return B_TRUE;
}
// check for joystick
if (havejoysticks) {
//if (joybut[keytojoybutton(checkfor)]) return B_TRUE;
if (joybuttondown(checkfor)) return B_TRUE;
if ((checkfor == SDLK_UP) && (joyy <= -6000)) return B_TRUE;
if ((checkfor == SDLK_DOWN) && (joyy >= 6000)) return B_TRUE;
if ((checkfor == SDLK_LEFT) && (joyx <= -6000)) return B_TRUE;
if ((checkfor == SDLK_RIGHT) && (joyx >= 6000)) return B_TRUE;
}
return B_FALSE;
}
// draw cannon lasers and check for collision from them
void docannoneffect(void) {
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");
losepowerup(player);
} 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 (!s3->caughtby) {
if ((yy >= s3->y - s3->img->h) && (yy <= s3->y)) {
s3->willbecome = P_DIAMOND;
if (s3->id == P_SNAIL) s3->id = P_SLUG;
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 (!s3->caughtby) {
if ((xx >= s3->x - (s3->img->w/2)) && (xx <= s3->x + (s3->img->w/2))) {
s3->willbecome = P_DIAMOND;
if (s3->id == P_SNAIL) s3->id = P_SLUG;
die(s3);
}
}
}
}
xx++;
}
// dec size
if (timer % 8 == 0) {
player->timer3--;
if (player->timer3 <= 0) {
losepowerup(player);
s->dead = D_FINAL;
SDL_BlitSurface(temps, NULL, screen, NULL);
}
}
}
}
}
void dotitlescreen(void) {
char tempst[BUFLEN];
char svnver[MIDBUFLEN];
SDL_Surface *titlebg;
SDL_Surface *cointext, *text, *text2, *easy, *norm, *ver;
SDL_Surface *help, *helpon, *helpoff;
SDL_Event event;
SDL_Rect area;
int timer = 0;
//int i;
int texton = B_TRUE;
int htstart = 0, htime = 0,hticks;
// reset gothiscore setting
gothiscore = -1;
// load title screen
sprintf(tempst, "%s/backgrounds/title.png",datadir);
titlebg = IMG_Load(tempst);
if (!titlebg) {
printf("cannot load title screen (%s)\n",tempst);
exit(1);
}
// set up text
cointext = TTF_RenderText_Solid(font[TEXTSIZE_TITLE], "Insert Coin", white);
text = TTF_RenderText_Solid(font[TEXTSIZE_TITLE], "Press 1UP to start", red);
text2 = TTF_RenderText_Solid(font[TEXTSIZE_TITLE], "Select game mode:", red);
easy = TTF_RenderText_Solid(font[TEXTSIZE_TITLE], "Easy Mode", red2);
norm = TTF_RenderText_Solid(font[TEXTSIZE_TITLE], "Normal Mode", red2);
help = TTF_RenderText_Solid(font[TEXTSIZE_TITLE], "Hints:", green);
helpoff = TTF_RenderText_Solid(font[TEXTSIZE_TITLE], "Off", green2);
helpon = TTF_RenderText_Solid(font[TEXTSIZE_TITLE], "On", green2);
sprintf(svnver, "v0.%d",REV);
ver = TTF_RenderText_Solid(font[TEXTSIZE_VER], svnver, greenish);
if (credits > 0) {
titlemode = TS_WAIT1UP;
} else {
titlemode = TS_INSERTCOIN;
}
gamemode = GM_NORM; // default
blinkspeed = 20;
// wait for keypress
titledone = B_FALSE;
while (!titledone) {
if (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_JOYAXISMOTION:
joyx = SDL_JoystickGetAxis(joy,0);
joyy = SDL_JoystickGetAxis(joy,1);
if (joyy <= -6000) handletitleinput(SDLK_UP);
if (joyy >= 6000) handletitleinput(SDLK_DOWN);
if (joyx <= -6000) handletitleinput(SDLK_LEFT);
if (joyx >= 6000) handletitleinput(SDLK_RIGHT);
break;
case SDL_KEYUP:
handletitleinput(event.key.keysym.sym);
break;
case SDL_JOYBUTTONUP:
handletitleinput(joybuttontokey(event.jbutton.button));
break;
}
}
if (++timer >= blinkspeed) {
// reset timer
timer = 0;
// blink text
if (texton) {
// disappear
texton = B_FALSE;
} else {
// appear
texton = B_TRUE;
}
}
// draw screen
if (titlemode != TS_HISCORES) {
SDL_BlitSurface(titlebg, NULL, screen, NULL);
} else {
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
}
if (titlemode != TS_HISCORES) {
// version number
area.x = 640 - (ver->w) - 10;
area.y = 150;
SDL_SetColors(ver, &black, 1, 1);
SDL_BlitSurface(ver, NULL, screen, &area);
area.x -= 2;
area.y -= 2;
SDL_SetColors(ver, &greenish, 1, 1);
SDL_BlitSurface(ver, NULL, screen, &area);
}
// mode-specific text
if ((titlemode == TS_INSERTCOIN) || (titlemode == TS_HISCORES)) {
if (texton) {
area.x = 320 - (cointext->w/2)+2;
area.y = 10;
SDL_SetColors(cointext, &black, 1, 1);
SDL_BlitSurface(cointext, NULL, screen, &area);
area.x -= 2;
area.y -= 2;
SDL_SetColors(cointext, &white, 1, 1);
SDL_BlitSurface(cointext, NULL, screen, &area);
}
}
if (titlemode == TS_WAIT1UP) {
//if (texton) {
area.x = 320 - (text->w/2)+2;
area.y = 240 - (text->h/2)+2;
SDL_SetColors(text, &black, 1, 1);
SDL_BlitSurface(text, NULL, screen, &area);
area.x -= 2;
area.y -= 2;
SDL_SetColors(text, &red, 1, 1);
SDL_BlitSurface(text, NULL, screen, &area);
//}
} else if (titlemode == TS_SELECTMODE) {
SDL_Surface *desc;
// "select mode"
area.x = 320 - (text2->w/2)+2;
area.y = 240 - (text2->h*3)+2;
SDL_SetColors(text2, &black, 1, 1);
SDL_BlitSurface(text2, NULL, screen, &area);
area.x -=2 ; area.y -= 2;
SDL_SetColors(text2, &red, 1, 1);
SDL_BlitSurface(text2, NULL, screen, &area);
// "easy"
area.x = 320 - (easy->w/2)+2;
area.y = 240 - (easy->h)+2;
SDL_SetColors(easy, &black, 1, 1);
SDL_BlitSurface(easy, NULL, screen, &area);
area.x -= 2; area.y -= 2;
if ((gamemode == GM_NORM) || (texton)) { // easy not blinking
// normal
SDL_SetColors(easy, &red2, 1, 1);
} else {
// white
SDL_SetColors(easy, &white, 1, 1);
}
SDL_BlitSurface(easy, NULL, screen, &area);
// "normal"
area.x = 320 - (norm->w/2)+2;
area.y = 240 +2;
SDL_SetColors(norm, &black, 1, 1);
SDL_BlitSurface(norm, NULL, screen, &area);
area.x -= 2; area.y -= 2;
if ((gamemode == GM_EASY) || (texton)) { // easy not blinking
// normal
SDL_SetColors(norm, &red2, 1, 1);
} else {
// white
SDL_SetColors(norm, &white, 1, 1);
}
SDL_BlitSurface(norm, NULL, screen, &area);
// help on/off
area.x = 320 - 100 +2;
area.y = 480 - 50 +2;
SDL_SetColors(help, &black, 1, 1);
SDL_BlitSurface(help, NULL, screen, &area);
area.x -= 2; area.y -= 2;
SDL_SetColors(help, &red, 1, 1);
SDL_BlitSurface(help, NULL, screen, &area);
// "on"
area.x += (help->w+10)+2;
area.y = 480 - 50 +2;
SDL_SetColors(helpon, &black, 1, 1);
SDL_BlitSurface(helpon, NULL, screen, &area);
area.x -= 2; area.y -= 2;
if ((showhelp == B_FALSE) || (texton)) { // on not blinking
// normal
SDL_SetColors(helpon, &red2, 1, 1);
} else {
// white
SDL_SetColors(helpon, &white, 1, 1);
}
SDL_BlitSurface(helpon, NULL, screen, &area);
// "off"
area.x += (helpon->w+10)+2;
area.y = 480 - 50 +2;
SDL_SetColors(helpoff, &black, 1, 1);
SDL_BlitSurface(helpoff, NULL, screen, &area);
area.x -= 2; area.y -= 2;
if ((showhelp == B_TRUE) || (texton)) { // off not blinking
// normal
SDL_SetColors(helpoff, &red2, 1, 1);
} else {
// white
SDL_SetColors(helpoff, &white, 1, 1);
}
SDL_BlitSurface(helpoff, NULL, screen, &area);
// level description
if (gamemode == GM_EASY) {
int x,y;
int startx,starty;
desc = TTF_RenderText_Solid(font[TEXTSIZE_TITLE2], "Permenant armour and extra time", black);
startx = 320 - (desc->w/2);
starty = 240 + (desc->h)*2;
for (x = startx-1 ; x <= startx+1; x++) {
for (y = starty-1 ; y <= starty+1; y++) {
area.x = x; area.y = y;
SDL_BlitSurface(desc, NULL, screen, &area);
}
}
area.x = startx;
area.y = starty;
SDL_SetColors(desc, &green, 1, 1);
SDL_BlitSurface(desc, NULL, screen, &area);
SDL_FreeSurface(desc);
}
} else if (titlemode == TS_HISCORES) {
drawhiscores();
}
// draw text
drawcredits();
SDL_UpdateRect(screen, 0,0,640,480);
hticks = SDL_GetTicks();
if (htstart == 0) {
htstart = hticks;
} else {
/* once per second */
if (hticks - htstart >= 1000) {
htime++;
htstart = hticks;
if ((htime > 0) && (htime % TTIME == 0)) {
if (titlemode == TS_INSERTCOIN) {
// INSERTCOIN -> NORMAL HISCORES
gethiscores(easymode());
titlemode = TS_HISCORES;
} else if (titlemode == TS_HISCORES) {
// next time, get other hiscores
if (gamemode == GM_EASY) gamemode = GM_NORM;
else gamemode = GM_EASY;
// NORMAL HISCORES -> INSERTCOIN
titlemode = TS_INSERTCOIN;
}
}
}
}
}
// clear screen to black
//SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
// free temp surfaces
SDL_FreeSurface(text );
SDL_FreeSurface(text2 );
SDL_FreeSurface(easy );
SDL_FreeSurface(norm );
}
void startgame(void) {
int i;
// initial variables
curlevelnum = 1;
curworld = 1; // TODO: REMOVE THIS
musicplaying = B_FALSE;
levelcompletetime = -1;
oldlevelcomplete = -1;
pokereffect = -1;
pokerpoints = 0;
skiplevels = 0;
gothiscore = -1;
curfruittype = 0;
curpoweruptype = 0;
fpsticks = 0;
fpsstart = 0;
// init player variables - if player
// hasn't been allocated yet then this will
// be done so in loadlevel(), and the below
// settings done in addsprite()
if (player) {
int c;
setdefaults(player);
player->permspeed = B_FALSE;
player->permbignet = B_FALSE;
player->permnumnets = B_FALSE;
player->permsticky = B_FALSE;
player->numcards = 0;
for (c = 0; c < MAXCARDS; c++) {
player->card[c] = -1;
player->usedcard[c] = 0;
}
}
// 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;
}
}
}
// allocate player
if (player == NULL) {
addsprite(P_PLAYER, 32, 450, "Player" );
player = lastsprite;
}
// pre-load initial level
/*
if (loadlevel(curworld,curlevelnum, B_TRUE)) {
exit(1);
}
*/
// more initial variables
makeinvuln(player);
player->score = 0;
if (gamemode == GM_EASY) {
player->permarmour = B_TRUE;
player->armour = B_TRUE;
player->id = P_ARMOUR;
} else {
player->permarmour = B_FALSE;
player->armour = B_FALSE;
player->id = P_PLAYER;
}
player->lives = 3;
forcegoodcard = B_FALSE;
nextforcegoodcard = B_FALSE;
gameover = B_FALSE;
gameovertime = -1;
gtime = 0;
curlevelnum-- ; // since nextlevel() will increment it
firstlevel = B_TRUE;
// start the first level
nextlevel();
firstlevel = B_FALSE;
flip();
// start timer
timer = 0;
}
void uncatch(sprite_t *s) {
s->caughtby = NULL;
s->caughtstate = B_FALSE;
}
void makeinvuln(sprite_t *s) {
s->invuln = INVULNTIME;
}
void setjoymappings(void) {
if (joy && havejoysticks) {
// ps3
joymap[4] = SDLK_UP;
joymap[5] = SDLK_RIGHT;
joymap[6] = SDLK_DOWN;
joymap[7] = SDLK_LEFT;
joymap[14] = SDLK_z;
joymap[13] = SDLK_x;
// mame (directions are joystick)
joymap[0] = SDLK_z;
joymap[1] = SDLK_x;
joymap[2] = SDLK_c;
joymap[3] = SDLK_c;
}
}
int joybuttontokey(int buttonnum) {
return joymap[buttonnum];
}
int joybuttondown(int key) {
int i;
for (i = 0; i < joybuttons; i++) {
if ((joymap[i] == key) && (joybut[i])) return B_TRUE;
}
return B_FALSE;
}
/*
int keytojoybutton(int key) {
int i;
for (i = 0; i < joybuttons; i++) {
if (joymap[i] == key) return i;
}
return -1;
}
*/
void handletitleinput(int key) {
if (key == SDLK_ESCAPE) {
// quit
exit(0);
}
if ((titlemode == TS_INSERTCOIN) || (titlemode == TS_HISCORES)) {
if ((key == SDLK_5) || (key == SDLK_6) || (key == SDLK_RETURN)) {
credits++;
playfx(FX_COIN);
titlemode = TS_WAIT1UP;
}
} else if (titlemode == TS_WAIT1UP) {
if ((key == SDLK_5) || (key == SDLK_6)) {
credits++;
playfx(FX_COIN);
// if this was the first credit, default to normal mode
if (credits == 1) {
gamemode = GM_NORM;
}
} else if ((key == SDLK_1) || (key == SDLK_2) || (key == SDLK_RETURN)) {
if (credits > 0) {
playfx(FX_1UP);
credits--;
titlemode = TS_SELECTMODE;
blinkspeed = 5;
}
}
} else if (titlemode == TS_SELECTMODE) {
// pick current mode
if (key == SDLK_UP) {
gamemode = GM_EASY;
} else if (key == SDLK_DOWN) {
gamemode = GM_NORM;
} else if (key == SDLK_LEFT) {
showhelp = B_TRUE;
} else if (key == SDLK_RIGHT) {
showhelp = B_FALSE;
} else if ((key == SDLK_z) || (key == SDLK_RETURN)) {
playfx(FX_1UP);
titledone = B_TRUE;
}
}
}
void drawcredits(void) {
char tempst[BUFLEN];
SDL_Rect area;
// credits
sprintf(tempst, "Credits: %d",credits);
credittext = TTF_RenderText_Solid(font[TEXTSIZE_CREDIT], tempst, black);
area.x = 320 - (credittext->w / 2) + 2;
area.y = 480 - 2 - (credittext->h)+2;
SDL_SetColors(credittext, &black, 1, 1);
SDL_BlitSurface(credittext, NULL, screen, &area);
area.x -= 2; area.y -= 2;
SDL_SetColors(credittext, &white, 1, 1);
SDL_BlitSurface(credittext, NULL, screen, &area);
SDL_FreeSurface(credittext);
}
int submithiscore(int score,int level, char *name) {
struct sockaddr_in serveraddress;
struct hostent *server;
int rv;
int sock;
char buf[1500];
char newtext[BUFLEN];
char buf2[512];
char *p,*p2;
server = gethostbyname(hiscoreserver);
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
printf("Error getting socket.\n");
return B_TRUE;
}
//bzero((char *) &serveraddress, sizeof(serveraddress));
memset(&serveraddress, 0, sizeof(serveraddress));
serveraddress.sin_family = AF_INET;
memcpy(&serveraddress.sin_addr.s_addr, server->h_addr,
server->h_length);
serveraddress.sin_port = htons(hiscoreport);
// replace spaces in name with %20s
p2 = newtext;
for (p = name ; *p != '\0'; p++) {
if (*p == ' ') {
*p2 = '%'; p2++;
*p2 = '2'; p2++;
*p2 = '0';
} else {
*p2 = *p;
}
p2++;
} *p2 = '\0';
// connect
if (connect(sock,(struct sockaddr *)&serveraddress,sizeof(serveraddress)) < 0) {
printf("Error connecting to hiscore server.\n");
return B_TRUE;
}
// send request
sprintf(buf2, "GET HTTP://ratcatcher.nethack.net/hiscores/submitscore.php?score=%d&name=%s&level=%d&easymode=%d HTTP/1.0\n\n", score,newtext,level,easymode());
write(sock,buf2,strlen(buf2));
// wait for data
rv = read(sock, buf, 1500);
while (rv > 0) {
rv = read(sock, buf, 1500);
}
close(sock);
return B_FALSE;
}
int gethiscores(int easyscores) {
struct sockaddr_in serveraddress;
struct hostent *server;
int rv;
int sock;
char buf[BUFLEN];
char buf2[512];
int state = 0;
int pos;
int finished;
server = gethostbyname(hiscoreserver);
if (!server) {
printf("can't resolve hiscore server name\n");
return B_TRUE;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
printf("Error getting socket.\n");
return B_TRUE;
}
//bzero((char *) &serveraddress, sizeof(serveraddress));
memset(&serveraddress, 0, sizeof(serveraddress));
serveraddress.sin_family = AF_INET;
memcpy(&serveraddress.sin_addr.s_addr, server->h_addr,
server->h_length);
serveraddress.sin_port = htons(hiscoreport);
// connect
if (connect(sock,(struct sockaddr *)&serveraddress,sizeof(serveraddress)) < 0) {
perror("connect");
printf("Error connecting to hiscore server.\n");
return B_TRUE;
}
// send request
if (easyscores) {
sprintf(buf2, "GET HTTP://ratcatcher.nethack.net/hiscores/hiscores_plain.php?easymode=1 HTTP/1.0\n\n");
} else {
sprintf(buf2, "GET HTTP://ratcatcher.nethack.net/hiscores/hiscores_plain.php HTTP/1.0\n\n");
}
write(sock,buf2,strlen(buf2));
pos = 0;
finished = B_FALSE;
// wait for data
//rv = read(sock, buf, 1500);
rv = socket_readline(sock, buf);
while ((rv > 0) && !finished) {
buf[rv] = '\0';
if (state == 0) { // header
char *p;
p = strstr(buf, "text/plain");
if (p) {
state = 1;
}
} else if (state == 1) { // waiting for start of data
if (strstr(buf, "start")) {
state = 2;
}
} else {
char *p;
// validate line
if (strstr(buf, "endofscores")) {
finished = B_TRUE;
} else if (strstr(buf, "^")) {
// process line
p = strtok(buf, "^");
hiscore[pos].score = atoi(p);
p = strtok(NULL, "^");
hiscore[pos].level = atoi(p);
p = strtok(NULL, "^");
sprintf(hiscore[pos].name,"%s", p);
pos++;
if (pos >= MAXHISCORES) {
// too many - cut it off here
finished = B_TRUE;
}
}
}
//rv = read(sock, buf, 1500);
rv = socket_readline(sock, buf);
}
numhiscores = pos;
close(sock);
return B_FALSE;
}
void checkhiscores(void){
int finished = B_FALSE;
int i;
char srank[BUFLEN],sscore[BUFLEN],slevel[BUFLEN],sname[BUFLEN];
//char line[BUFLEN];
char commascore[BUFLEN];
int x,y,sx,sy;
char thisname[BUFLEN];
int timer = 0,keytimer = 0;
int capital = B_TRUE;
int curlet;
// no hiscores if we are cheating!
if (cheat) {
return;
}
// contact server and read appropriate list
if (gethiscores(easymode())) {
printf("Cannot contact hiscore server!\n");
return;
}
// check if we are higher than any
gothiscore = -1;
for (i = 0; i < numhiscores; i++) {
if (player->score > hiscore[i].score) {
// we got a hi score
gothiscore = i;
break;
}
}
if (gothiscore == -1) {
// didn't get a hiscore
return;
}
// play hiscore music
stopmusic();
playmusic(hiscoremusic);
// prompt user for a name
curlet = 'a';
strcpy(thisname, "");
while (!finished) {
SDL_Color *fg, *bg;
SDL_Event event;
char let[2];
// clear
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format,black.r,black.g,black.b));
//draw hiscore heading
sx = 110;
sy = 100;
x = sx;
y = sy;
// headings
drawoutlinecentretext(screen, 50, TEXTSIZE_HISCORE, "New high score!", &white, &grey2);
// print rank
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, "Rank", &cyan, &blue);
x += 70;
// print score
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, "Score", &cyan, &blue);
x += 150;
// print level
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, "Level", &cyan, &blue);
x += 90;
// print name
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, "Name", &cyan, &blue);
y += (TEXTSIZE_HISCORE*2);
x = sx;
// player's score...
// generate hiscore lines
sprintf(srank, "%-2d.",gothiscore+1);
addcommas(commascore, player->score );
sprintf(sscore, "%-14s",commascore);
sprintf(slevel, "%1d-%-2d",getworld(curlevelnum),getlevel(curlevelnum));
if (strlen(thisname) > 0) {
sprintf(sname, "%-32s",thisname);
}
fg = &white;
bg = &grey2;
// print rank
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, srank, fg, bg);
x += 70;
// print score
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, sscore, fg, bg);
x += 150;
// print level
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, slevel, fg, bg);
x += 90;
// print name
if (strlen(thisname) > 0) {
int wid;
wid = drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, thisname, fg, bg);
x += (wid + 2);
}
// print cursor
if (curlet == '*') {
if (((timer / 5) % 2) == 0) {
drawoutlinetext(screen, x, y+3, TEXTSIZE_HISCORE/2, "END", &white, &grey2);
} else {
drawoutlinetext(screen, x, y+3, TEXTSIZE_HISCORE/2, "END", &red, &red2);
}
} else {
sprintf(let, "%c",capital ? toupper(curlet) : curlet );
if (((timer / 5) % 2) == 0) {
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, let, &white, &grey2);
} else {
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, let, &red, &red2);
}
}
if (++timer == 100) {
timer = 0;
}
getinput();
if (keytimer == 0) {
// check for scrolling
if (keydown(SDLK_UP)) { // scroll through letters
if (curlet == 'a') curlet = '*';
else if (curlet == '*') curlet = ' ';
else if (curlet == ' ') curlet = 'z';
else curlet--;
keytimer = 5;
} else if (keydown(SDLK_DOWN)) { // scroll through letters
if (curlet == 'z') curlet = ' ';
else if (curlet == ' ') curlet = '*';
else if (curlet == '*') curlet = 'a';
else curlet++;
keytimer = 5;
}
} else {
keytimer--;
}
if (strlen(thisname) >= MAXHISCORENAME) {
// have to END now
curlet='*';
}
// wait for a key...
if (SDL_PollEvent(&event)) {
int key = -1;
switch (event.type) {
case SDL_KEYUP:
key = event.key.keysym.sym;
break;
case SDL_JOYBUTTONUP:
key = joybuttontokey(event.jbutton.button);
break;
}
if ((event.type == SDL_KEYUP) || (event.type == SDL_JOYBUTTONUP)) {
if (key != -1) {
if (key == SDLK_ESCAPE) {
// quit
exit(0);
} else if ((key == SDLK_RETURN) || (key == SDLK_5)) {
if (strlen(thisname) > 0) {
playfx(FX_1UP);
finished = B_TRUE;
}
} else if ((key == SDLK_BACKSPACE) || (key == SDLK_x)) {
thisname[strlen(thisname)-1] = '\0';
if (strlen(thisname) == 0) {
capital = B_TRUE;
} else if (thisname[strlen(thisname)-1] == ' ') {
capital = B_TRUE;
}
} else if (key == SDLK_z) {
// add current letter or finish if it is on "end"
if (curlet == '*') {
if (strlen(thisname) > 0) {
playfx(FX_1UP);
finished = B_TRUE;
}
} else {
// capital
let[0] = capital ? toupper(curlet) : curlet;
let[1] = '\0';
strcat(thisname, let);
// don't let name get too long
playfx(FX_POWERUP);
if (curlet == ' ') {
// next letter is a capital
capital = B_TRUE;
} else {
capital = B_FALSE;
}
}
}
}
}
}
SDL_UpdateRect(screen, 0,0,640,480);
}
// submit the hiscore
if (submithiscore(player->score, curlevelnum, thisname)) {
printf("failed to submit hiscore.\n");
} else {
// downlaod the hiscores again
gethiscores(easymode());
}
}
void showhiscores(void){
int finished = B_FALSE;
//char line[BUFLEN];
int htstart = 0, htime = 0,hticks;
// contact server and read list
/*
if (gethiscores()) {
printf("Cannot contact hiscore server!\n");
return;
}
*/
/*
hiscore[0].score = 55000;
hiscore[0].level = 24;
sprintf(hiscore[0].name, "King Rat");
hiscore[1].score = 1000;
hiscore[1].level = 5;
sprintf(hiscore[1].name, "Queen Rat");
numscores = 2;
*/
// display list on screen and wait for key
while (!finished) {
SDL_Event event;
drawhiscores();
// check for a key...
if (SDL_PollEvent(&event)) {
int key = -1;
switch (event.type) {
case SDL_KEYUP:
key = event.key.keysym.sym;
break;
case SDL_JOYBUTTONUP:
key = joybuttontokey(event.jbutton.button);
break;
}
if (key != -1) {
if (key == SDLK_ESCAPE) {
// quit
exit(0);
} else if ((key == SDLK_1) || (key == SDLK_2)) {
// 1up buttons
finished = B_TRUE;
}
}
}
SDL_UpdateRect(screen, 0,0,640,480);
hticks = SDL_GetTicks();
if (htstart == 0) {
htstart = hticks;
} else {
/* once per second */
if (hticks - htstart >= 1000) {
htime++;
htstart = hticks;
}
if (htime >= HISCORE_DISPLAYTIME) {
finished = B_TRUE;
}
}
}
// stop music ready for title screen again (it will only be playing if
// we got a hiscore)
stopmusic();
}
int socket_readline(int sock, char* out) {
/* current location in buffer */
int i = 0;
/* whether the previous char was a \r */
int cr = 0;
char ch;
/* result, grows as we need it to */
//out = malloc(sizeof(char) * size);
for ( ; ; ) {
if (recv(sock, &ch, 1, 0) == -1) {
return -1;
}
/*
if (i >= size) {
// grow buffer
size *= 2;
out = realloc(out, size);
}
*/
if (ch == '\n') {
/* if preceded by a \r, we overwrite it */
if (cr) {
i--;
}
out[i] = '\0';
break;
} else {
cr = (ch == '\r' ? 1 : 0);
out[i] = ch;
}
i++;
}
return i + 1;
}
void drawhiscores(void) {
char srank[BUFLEN],sscore[BUFLEN],slevel[BUFLEN],sname[BUFLEN];
char commascore[BUFLEN];
int x,y,sx,sy;
int i;
//draw hiscore list
sx = 110;
sy = 100;
x = sx;
y = sy;
// headings
if (easymode()) {
drawoutlinecentretext(screen, 50, TEXTSIZE_HISCORE, "High Scores - Easy", &cyan, &blue);
} else {
drawoutlinecentretext(screen, 50, TEXTSIZE_HISCORE, "High Scores - Normal", &cyan, &blue);
}
if (numhiscores <= 0) {
// print rank
y += (TEXTSIZE_HISCORE*4);
drawoutlinecentretext(screen, y, TEXTSIZE_HISCORE, "High score server unavailable", &red, &red3);
} else {
// print rank
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, "Rank", &cyan, &blue);
x += 70;
// print score
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, "Score", &cyan, &blue);
x += 150;
// print level
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, "Level", &cyan, &blue);
x += 90;
// print name
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, "Name", &cyan, &blue);
y += (TEXTSIZE_HISCORE*2);
x = sx;
for (i = 0; i < numhiscores; i++) {
SDL_Color *fg, *bg;
// generate hiscore lines
sprintf(srank, "%-2d.",i+1);
addcommas(commascore, hiscore[i].score);
sprintf(sscore, "%-14s",commascore);
sprintf(slevel, "%1d-%-2d",getworld(hiscore[i].level),getlevel(hiscore[i].level));
sprintf(sname, "%-32s",hiscore[i].name);
if ((gothiscore != -1) && (i == gothiscore)) {
fg = &white;
bg = &grey2;
} else {
fg = &red;
bg = &red3;
}
// print rank
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, srank, fg, bg);
x += 70;
// print score
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, sscore, fg, bg);
x += 150;
// print level
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, slevel, fg, bg);
x += 90;
// print name
drawoutlinetext(screen, x, y, TEXTSIZE_HISCORE, sname, fg, bg);
// go to next line
x = sx;
y += (TEXTSIZE_HISCORE+2);
}
}
}
// read keyboard info
void getinput(void) {
int i;
keys = SDL_GetKeyState(NULL);
if (havejoysticks) {
joyx = SDL_JoystickGetAxis(joy,0);
joyy = SDL_JoystickGetAxis(joy,1);
for (i = 0; i < joybuttons; i++) {
joybut[i] = SDL_JoystickGetButton(joy,i);
}
}
}
int isendoflev(void) {
if (levelcomplete == LV_CLEAR) return B_TRUE;
if (levelcomplete == LV_WAIT) return B_TRUE;
if (levelcomplete == LV_FINAL) return B_TRUE;
if (levelcomplete == LV_CLOUD) return B_TRUE;
if (levelcomplete == LV_CLOUDLOOP) return B_TRUE;
if (levelcomplete == LV_NEXTLEV) return B_TRUE;
return B_FALSE;
}
void keeponscreen(sprite_t *s) {
if (s->x >= (640-(s->img->w/2))) {
s->x = 640 - (s->img->w/2)-1;
}
if (s->x <= s->img->w/2) {
s->x = (s->img->w/2)+1;
}
if (s->y <= s->img->h) {
s->y = s->img->h+1;
}
if (s->y >= 480) {
s->y = 480 - 1;
}
}
void stopteleporting(sprite_t *s) {
if (s->teleporting) {
if (s->allocimg) {
SDL_FreeSurface(s->img);
s->img = NULL;
}
s->allocimg = B_FALSE;
s->teleporting = 0;
s->img = imageset[s->id].img[F_WALK1];
}
}
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;
}
int easymode(void) {
if (gamemode == GM_EASY) return B_TRUE;
return B_FALSE;
}