/* * Alizarin Tetris * The main file. Library initialization and utility functions. * * Copyright 2000, Westley Weimer & Kiri Wagstaff */ #include /* go autoconf! */ #include #include #include #include #include "SDL.h" #include "SDL_main.h" #include "SDL_ttf.h" #if HAVE_SYS_SOCKET_H # include #else # if HAVE_WINSOCK_H # include # endif #endif /* header files */ #include "atris.h" #include "ai.h" #include "display.h" #include "grid.h" #include "piece.h" #include "sound.h" #include "identity.h" #include "options.h" /* function prototypes */ #include "ai.pro" #include "display.pro" #include "event.pro" #include "gamemenu.pro" #include "highscore.pro" #include "identity.pro" #include "network.pro" #include "xflame.pro" static color_style *event_cs[2]; /* pass these to event_loop */ static sound_style *event_ss[2]; static AI_Player *event_ai[2]; static char *event_name[2]; extern int Score[2]; /*************************************************************************** * Panic() * It's over. Don't even try to clean up. *********************************************************************PROTO*/ void Panic(const char *func, const char *file, char *fmt, ...) { va_list ap; va_start(ap, fmt); printf("\nPANIC in %s() of %s:\n\t",func,file); #if HAVE_VPRINTF vprintf((const char *)fmt, ap); #else #warning "Since you lack vprintf(), you will not get informative PANIC messages." #endif printf("\n\nlibc error %3d| %s\n",errno,strerror(errno)); printf( "SDL error | %s\n",SDL_GetError()); SDL_CloseAudio(); exit(1); } /*************************************************************************** * usage() * Display summary usage information. ***************************************************************************/ static void usage(void) { printf("\n\t\t\t\tatris -- Alizarin Tetris\n" "Usage: atris [options] \n" "\t-h --help\t\tThis message.\n" "\t-b --bg \t\tFlaming background (default).\n" "\t-n --noflame\t\tNo flaming background.\n" "\t-s --sound\t\tEnable sound effects (default).\n" "\t-q --quiet\t\tNo sound effects.\n" "\t-w --window\t\tWindowed display (default).\n" "\t-f --fullscreen\t\tFull-screen display.\n" "\t-d=X --depth=X\t\tSet color detph (bpp) to X.\n" "\t-r=X --repeat=X\t\tSet the keyboard repeat delay to X.\n" "\t\t\t\t(1 = Slow Repeat, 16 = Fast Repeat)\n" ); exit(1); } /*************************************************************************** * save_options() * Save current settings to a file. ***************************************************************************/ void save_options(char *filespec) { FILE *fout = fopen(filespec, "wt"); if (!fout) return; fprintf(fout, "# bpp = 15, 16, 24, 32 or 0 for auto-detect bits per pixel\n" "bpp = %d\n" "# sound_wanted = 0 or 1\n" "sound_wanted = %d\n" "# full_screen = 0 or 1\n" "full_screen = %d\n" "# flame = 0 or 1 (CPU-sucking background flame)\n" "flame = %d\n" "# key_repeat = 1 to 32 (1 = slow repeat, 16 = default)\n" "key_repeat = %d\n" "# power_pieces = 0 or 1\n" "power_pieces = %d\n" "# double_difficulty = 0 or 1\n" "double_difficulty = %d\n" "# long_settle = 0 or 1\n" "long_settle = %d\n" "# upward_rotation = 0 or 1\n" "upward_rotation = %d\n" "#\n" "color_style = %d\n" "sound_style = %d\n" "piece_style = %d\n" "game_style = %d\n" , Options.bpp_wanted, Options.sound_wanted, Options.full_screen, Options.flame_wanted, Options.key_repeat_delay, Options.special_wanted, Options.faster_levels, Options.long_settle_delay, Options.upward_rotation, Options.named_color, Options.named_sound, Options.named_piece, Options.named_game); fclose(fout); Debug("Preference file [%s] saved.\n",filespec); return; } /*************************************************************************** * load_options() * Load options from a user settings file. ***************************************************************************/ void load_options(char * filespec) { FILE *fin = fopen(filespec, "rt"); Options.full_screen = TRUE; Options.sound_wanted = TRUE; Options.flame_wanted = TRUE; Options.bpp_wanted = 0; Options.key_repeat_delay = 8; Options.special_wanted = FALSE; Options.faster_levels = FALSE; Options.upward_rotation = TRUE; Options.long_settle_delay = TRUE; Options.named_color = -1; Options.named_sound = -1; Options.named_piece = -1; Options.named_game = -1; if (!fin) return; while (!feof(fin)) { char buf[1024]; char cmd[1024]; fgets(buf,sizeof(buf),fin); if (feof(fin)) break; if (buf[0] == '#' || buf[0] == '\n') continue; sscanf(buf,"%s",cmd); if (!strcasecmp(cmd,"full_screen")) { sscanf(buf,"%s = %d",cmd,&Options.full_screen); } else if (!strcasecmp(cmd,"sound_wanted")) { sscanf(buf,"%s = %d",cmd,&Options.sound_wanted); } else if (!strcasecmp(cmd,"flame")) { sscanf(buf,"%s = %d",cmd,&Options.flame_wanted); } else if (!strcasecmp(cmd,"bpp")) { sscanf(buf,"%s = %d",cmd,&Options.bpp_wanted); } else if (!strcasecmp(cmd,"key_repeat")) { sscanf(buf,"%s = %d",cmd,&Options.key_repeat_delay); if (Options.key_repeat_delay < 1) Options.key_repeat_delay = 1; if (Options.key_repeat_delay > 32) Options.key_repeat_delay = 32; } else if (!strcasecmp(cmd,"power_pieces")) { sscanf(buf,"%s = %d",cmd,&Options.special_wanted); } else if (!strcasecmp(cmd,"double_difficulty")) { sscanf(buf,"%s = %d",cmd,&Options.faster_levels); } else if (!strcasecmp(cmd,"long_settle")) { sscanf(buf,"%s = %d",cmd,&Options.long_settle_delay); } else if (!strcasecmp(cmd,"upward_rotation")) { sscanf(buf,"%s = %d",cmd,&Options.upward_rotation); } else if (!strcasecmp(cmd,"color_style")) { sscanf(buf,"%s = %d",cmd,&Options.named_color); } else if (!strcasecmp(cmd,"sound_style")) { sscanf(buf,"%s = %d",cmd,&Options.named_sound); } else if (!strcasecmp(cmd,"piece_style")) { sscanf(buf,"%s = %d",cmd,&Options.named_piece); } else if (!strcasecmp(cmd,"game_style")) { sscanf(buf,"%s = %d",cmd,&Options.named_game); } else { Debug("Unable to parse configuration line\n%s",buf); } } fclose(fin); Debug("Preference file [%s] loaded.\n",filespec); return; } /*************************************************************************** * parse_options() * Check the command-line arguments. ***************************************************************************/ static void parse_options(int argc, char *argv[]) { int i; for (i=1; i 32) Options.key_repeat_delay = 32; } else { Debug("option not understood: [%s]\n",argv[i]); usage(); } } Debug("Command line arguments parsed.\n"); return; } /*************************************************************************** * level_adjust() * What happens with all of those thumbs-up, thumbs-down adjustments? * * Returns the new value of "match". ***************************************************************************/ static int level_adjust(int a[3], int b[3], int level[2], int match) { int i; int up[2] = {0, 0}, down[2] = {0, 0}; for (i=0;i<=match;i++) { if (a[i] == ADJUST_UP) up[0]++; if (a[i] == ADJUST_DOWN) down[0]++; if (b[i] == ADJUST_UP) up[1]++; if (b[i] == ADJUST_DOWN) down[1]++; } while (up[0] > 0 && up[1] > 0) { up[0]--; up[1]--; } while (up[0] > 0 && down[0] > 0) { up[0]--; down[0]--; } while (up[1] > 0 && down[1] > 0) { up[1]--; down[1]--; } while (down[0] > 0 && down[1] > 0) { down[0]--; down[1]--; } if (up[0] == 3 || down[0] == 3 || up[1] == 3 || down[1] == 3) { if (up[0] == 3) level[0]++; if (down[0] == 3) level[0]--; if (up[1] == 3) level[1]++; if (down[1] == 3) level[1]--; a[0] = a[1] = a[2] = b[0] = b[1] = b[2] = -1; return 0; } a[0] = a[1] = a[2] = b[0] = b[1] = b[2] = -1; match = max( max(up[0],up[1]), max(down[0], down[1]) ); /* fill out the a[], b[] arrays */ for (i=0;ilevel; level[1] = p2->level; /* start the games */ while (!done) { time(&our_time); /* make the boards */ SeedRandom(our_time); g[0] = generate_board(10,20,level[0]); SeedRandom(our_time); g[1] = generate_board(10,20,level[1]); SeedRandom(our_time); event_name[0] = p1->name; event_name[1] = p2->name; /* draw the background */ draw_background(screen, cs.style[0]->w, g, level, my_adj, their_adj, event_name); /* start the fun! */ curtimeleft = 120; event_cs[0] = event_cs[1] = cs.style[cs.choice]; event_ss[0] = event_ss[1] = ss.style[ss.choice]; if (event_loop(screen, ps.style[ps.choice], event_cs, event_ss, g, level, 0, &curtimeleft, 0, adjustment, NULL, our_time, HUMAN_PLAYER, HUMAN_PLAYER, NULL) >= 0) { draw_background(screen, cs.style[0]->w,g,level,my_adj,their_adj, event_name); SDL_Delay(1000); } my_adj[match] = adjustment[0]; their_adj[match] = adjustment[1]; match = level_adjust(my_adj, their_adj, level, match); /* update results */ p1->level = level[0]; p2->level = level[1]; /* show them what's what! */ draw_background(screen, cs.style[0]->w,g,level,my_adj,their_adj, event_name); draw_score(screen,0); draw_score(screen,1); done = give_notice(NULL,1); } /* end: while !done */ return; } /*************************************************************************** * play_AI_VS_AI() * Play the AI_VS_AI-style game. Two AI's duke it out! Winnings and scores * are collected. ***************************************************************************/ static void play_AI_VS_AI(color_styles cs, piece_styles ps, sound_styles ss, Grid g[2], AI_Player *p1, AI_Player *p2) { int curtimeleft; int match; int level[2]; /* level array: me + dummy slot */ int adjustment[2] = {-1, -1}; /* result of playing a match */ int my_adj[3]; /* my three winnings so far */ int their_adj[3]; /* their three winnings so far */ int done = 0; time_t our_time; int p1_results[3] = {0, 0, 0}; int p2_results[3] = {0, 0, 0}; my_adj[0] = -1; my_adj[1] = -1; my_adj[2] = -1; their_adj[0] = -1; their_adj[1] = -1; their_adj[2] = -1; match = 0; /* start the games */ while (!done) { time(&our_time); /* make the boards */ level[0] = level[1] = Options.faster_levels ? 1+ZEROTO(8) : 2+ZEROTO(16); SeedRandom(our_time); g[0] = generate_board(10,20,level[0]); SeedRandom(our_time); g[1] = generate_board(10,20,level[0]); SeedRandom(our_time); event_name[0] = p1->name; event_name[1] = p2->name; /* draw the background */ draw_background(screen, cs.style[0]->w, g, level, p1_results, p2_results, event_name); /* start the fun! */ curtimeleft = 120; event_cs[0] = cs.style[cs.choice]; event_cs[1] = cs.style[ZEROTO(cs.num_style)]; event_ss[0] = event_ss[1] = ss.style[ss.choice]; event_ai[0] = p1; event_ai[1] = p2; if (event_loop(screen, ps.style[ps.choice], event_cs, event_ss, g, level, 0, &curtimeleft, 0, adjustment, NULL, our_time, AI_PLAYER, AI_PLAYER, event_ai) < 0) { return; } if (adjustment[0] != -1) p1_results[ adjustment[0] ] ++; if (adjustment[1] != -1) p2_results[ adjustment[1] ] ++; /* show them what's what! */ draw_background(screen, cs.style[0]->w, g, level, p1_results, p2_results, event_name); draw_score(screen,0); draw_score(screen,1); } /* end: while !done */ return; } /*************************************************************************** * play_SINGLE_VS_AI() * Play the SINGLE_VS_AI-style game. You and someone else both have two * minutes per level, but the limit isn't deadly. Complex adjustment rules. ***************************************************************************/ static int play_SINGLE_VS_AI(color_styles cs, piece_styles ps, sound_styles ss, Grid g[2], person *p, AI_Player *aip) { int curtimeleft; int match; int level[2]; /* level array: me + dummy slot */ int adjustment[2] = {-1, -1}; /* result of playing a match */ int my_adj[3]; /* my three winnings so far */ int their_adj[3]; /* their three winnings so far */ int done = 0; time_t our_time; my_adj[0] = my_adj[1] = my_adj[2] = -1; their_adj[0] = their_adj[1] = their_adj[2] = -1; match = 0; /* start the games */ level[0] = level[1] = p->level; /* AI matches skill */ while (!done) { time(&our_time); /* make the boards */ level[1] = level[0]; SeedRandom(our_time); g[0] = generate_board(10,20,level[0]); SeedRandom(our_time); g[1] = generate_board(10,20,level[0]); SeedRandom(our_time); event_name[0] = p->name; event_name[1] = aip->name; /* draw the background */ draw_background(screen, cs.style[0]->w, g, level, my_adj, their_adj, event_name); /* start the fun! */ curtimeleft = 120; event_cs[0] = cs.style[cs.choice]; event_cs[1] = cs.style[ZEROTO(cs.num_style)]; event_ss[0] = event_ss[1] = ss.style[ss.choice]; event_ai[0] = NULL; event_ai[1] = aip; if (event_loop(screen, ps.style[ps.choice], event_cs, event_ss, g, level, 0, &curtimeleft, 0, adjustment, NULL, our_time, HUMAN_PLAYER, AI_PLAYER, event_ai) >= 0) { draw_background(screen, cs.style[0]->w,g,level,my_adj,their_adj, event_name); draw_score(screen,0); draw_score(screen,1); SDL_Delay(1000); } my_adj[match] = adjustment[0]; their_adj[match] = adjustment[1]; match = level_adjust(my_adj, their_adj, level, match); /* show them what's what! */ draw_background(screen, cs.style[0]->w,g,level,my_adj,their_adj, event_name); draw_score(screen,0); draw_score(screen,1); done = give_notice(NULL, 1); } /* end: while !done */ return level[0]; } /*************************************************************************** * play_NETWORK() * Play the NETWORK-style game. You and someone else both have two minutes * per level, but the limit isn't deadly. Complex adjustment rules. :-) ***************************************************************************/ static int play_NETWORK(color_styles cs, piece_styles ps, sound_styles ss, Grid g[2], person *p, char *hostname) { int curtimeleft; extern char *error_msg; int server; int match; int level[2]; /* level array: me + dummy slot */ int adjustment[2] = {-1, -1}; /* result of playing a match */ int my_adj[3]; /* my three winnings so far */ int their_adj[3]; /* their three winnings so far */ char message[1024]; char *their_name; int done = 0; int sock; int their_cs_choice; int their_data; time_t our_time; server = (hostname == NULL); #define SEND(msg,len) {if (send(sock,(const char *)msg,len,0) < (signed)len) goto error;} #define RECV(msg,len) {if (recv(sock,(char *)msg,len,0) < (signed)len) goto error;} #define SERVER_SYNC 0x12345678 #define CLIENT_SYNC 0x98765432 level[0] = p->level; /* establish connection */ if (server) { SDL_Event event; /* try this a lot until they press Q to give up */ clear_screen_to_flame(); draw_string("Waiting for the client ...", color_blue, screen->w/2, screen->h/2, DRAW_CENTER|DRAW_ABOVE |DRAW_UPDATE); draw_string("Press 'Q' to give up.", color_blue, screen->w/2, screen->h/2, DRAW_CENTER|DRAW_UPDATE); do { sock = Server_AwaitConnection(7741); if (sock == -1 && SDL_PollEvent(&event) && event.type == SDL_KEYDOWN) if (event.key.keysym.sym == SDLK_q) goto done; } while (sock == -1); } else { /* client */ int i; SDL_Event event; sock = -1; for (i=0; i<5 && sock == -1; i++) { sock = Client_Connect(hostname,7741); if (sock == -1 && SDL_PollEvent(&event) && event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_q) goto done; if (sock == -1) SDL_Delay(1000); } if (sock == -1) goto error; } clear_screen_to_flame(); /* consistency checks: same number of colors */ SEND(&cs.style[cs.choice]->num_color, sizeof(int)); RECV(&their_data, sizeof(int)); if (their_data != cs.style[cs.choice]->num_color) { SPRINTF(message,"The # of colors in your styles are not equal. (%d/%d)", cs.style[cs.choice]->num_color, their_data); goto known_error; } SEND(&ps.style[ps.choice]->num_piece, sizeof(int)); RECV(&their_data, sizeof(int)); if (their_data != ps.style[ps.choice]->num_piece) { SPRINTF(message,"The # of shapes in your styles are not equal. (%d/%d)", ps.style[ps.choice]->num_piece, their_data); goto known_error; } SEND(&Options.special_wanted, sizeof(int)); RECV(&their_data, sizeof(int)); if (their_data != Options.special_wanted) { SPRINTF(message,"You must both agree on whether to use Power Pieces"); goto known_error; } SEND(&Options.faster_levels, sizeof(int)); RECV(&their_data, sizeof(int)); if (their_data != Options.special_wanted) { SPRINTF(message,"You must both agree on Double Difficulty"); goto known_error; } /* initial levels */ SEND(&level[0],sizeof(level[0])); SEND(&cs.choice,sizeof(cs.choice)); match = strlen(p->name); SEND(&match, sizeof(match)); SEND(p->name,strlen(p->name)); RECV(&level[1],sizeof(level[1])); Assert(sizeof(their_cs_choice) == sizeof(cs.choice)); RECV(&their_cs_choice,sizeof(cs.choice)); if (their_cs_choice < 0 || their_cs_choice >= cs.num_style) { their_cs_choice = cs.choice; } RECV(&match,sizeof(match)); Calloc(their_name, char *, match); RECV(their_name, match); my_adj[0] = my_adj[1] = my_adj[2] = -1; their_adj[0] = their_adj[1] = their_adj[2] = -1; match = 0; /* start the games */ while (!done) { /* pass the seed */ if (server) { time(&our_time); SEND(&our_time,sizeof(our_time)); } else { RECV(&our_time,sizeof(our_time)); } /* make the boards */ SeedRandom(our_time); g[0] = generate_board(10,20,level[0]); SeedRandom(our_time); g[1] = generate_board(10,20,level[1]); SeedRandom(our_time); event_name[0] = p->name; event_name[1] = their_name; /* draw the background */ draw_background(screen, cs.style[0]->w, g, level, my_adj, their_adj, event_name); /* start the fun! */ curtimeleft = 120; event_cs[0] = cs.style[cs.choice]; event_cs[1] = cs.style[their_cs_choice]; event_ss[0] = event_ss[1] = ss.style[ss.choice]; event_loop(screen, ps.style[ps.choice], event_cs, event_ss, g, level, sock, &curtimeleft, 0, adjustment, NULL, our_time, HUMAN_PLAYER, NETWORK_PLAYER, NULL); SEND(&Score[0],sizeof(Score[0])); RECV(&Score[1],sizeof(Score[1])); draw_background(screen, cs.style[0]->w,g,level,my_adj,their_adj, event_name); draw_score(screen,0); draw_score(screen,1); SDL_Delay(1000); my_adj[match] = adjustment[0]; their_adj[match] = adjustment[1]; match = level_adjust(my_adj, their_adj, level, match); /* verify that our hearts are in the right place */ if (server) { unsigned int msg = SERVER_SYNC; SEND(&msg,sizeof(msg)); RECV(&msg,sizeof(msg)); if (msg != CLIENT_SYNC) { give_notice("Network Error: Syncronization Failed", 0); goto done; } } else { unsigned int msg; RECV(&msg,sizeof(msg)); if (msg != SERVER_SYNC) { give_notice("Network Error: Syncronization Failed", 1); goto done; } msg = CLIENT_SYNC; SEND(&msg,sizeof(msg)); } /* OK, we believe we are both dancing on the beat ... */ /* don't even talk to me about three-way handshakes ... */ /* show them what's what! */ draw_background(screen, cs.style[0]->w,g,level,my_adj,their_adj, event_name); draw_score(screen,0); draw_score(screen,1); if (server) { done = give_notice(NULL, 1); SEND(&done,sizeof(done)); } else { SDL_Event event; draw_string("Waiting for the", color_blue, 0, 0, DRAW_CENTER|DRAW_ABOVE |DRAW_UPDATE|DRAW_GRID_0); draw_string("Server to go on.", color_blue, 0, 0, DRAW_CENTER |DRAW_UPDATE|DRAW_GRID_0); RECV(&done,sizeof(done)); while (SDL_PollEvent(&event)) /* do nothing */ ; } clear_screen_to_flame(); } /* end: while !done */ close(sock); return level[0]; /* error conditions! */ error: if (error_msg) SPRINTF(message,"Network Error: %s",error_msg); else SPRINTF(message,"Network Error: %s",strerror(errno)); known_error: clear_screen_to_flame(); give_notice(message, 0); done: close(sock); return level[0]; } /*************************************************************************** * play_MARATHON() * Play the MARATHON-style game. You have five minutes per level: try to * get many points. Returns the final level. ***************************************************************************/ static int play_MARATHON(color_styles cs, piece_styles ps, sound_styles ss, Grid g[2], person *p) { int curtimeleft; int level[2]; /* level array: me + dummy slot */ int adjustment[2] = {-1, -1}; /* result of playing a match */ int my_adj[3]; /* my three winnings so far */ char message[1024]; int result; level[0] = p->level; /* starting level */ SeedRandom(0); Score[0] = 0; /* global variable! */ my_adj[0] = -1; my_adj[1] = -1; my_adj[2] = -1; while (1) { /* until they give up by pressing 'q'! */ /* 5 mintues per match */ curtimeleft = 300; /* generate the board */ g[0] = generate_board(10,20,level[0]); /* draw the background */ event_name[0] = p->name; draw_background(screen, cs.style[0]->w, g, level, my_adj, NULL, event_name); /* get the result, but don't update level yet */ event_cs[0] = event_cs[1] = cs.style[cs.choice]; event_ss[0] = event_ss[1] = ss.style[ss.choice]; result = event_loop(screen, ps.style[ps.choice], event_cs, event_ss, g, level, 0, &curtimeleft, 1, adjustment, NULL, time(NULL), HUMAN_PLAYER, NO_PLAYER, NULL); if (result < 0) { /* explicit quit */ return level[0]; } /* reset board */ my_adj[0] = adjustment[0]; /* display those mighty accomplishments winnings */ draw_background(screen, cs.style[0]->w ,g, level,my_adj,NULL, event_name); draw_score(screen,0); if (adjustment[0] != ADJUST_UP) { give_notice("Game Over!", 0); return level[0]; } my_adj[0] = -1; level[0]++; SPRINTF(message,"Up to Level %d!",level[0]); if (give_notice(message, 1)) { return level[0]; } } } /*************************************************************************** * play_SINGLE() * Play the SINGLE-style game. You have five total minutes to win three * matches. Returns the final level. ***************************************************************************/ static int play_SINGLE(color_styles cs, piece_styles ps, sound_styles ss, Grid g[], person *p) { int curtimeleft; int match; int level[2]; /* level array: me + dummy slot */ int adjustment[2] = {-1, -1}; /* result of playing a match */ int my_adj[3]; /* my three winnings so far */ char message[1024]; int result; level[0] = p->level; /* starting level */ SeedRandom(0); Score[0] = 0; /* global variable! */ while (1) { /* until they give up by pressing 'q'! */ /* 5 total minutes for three matches */ curtimeleft = 300; my_adj[0] = -1; my_adj[1] = -1; my_adj[2] = -1; for (match=0; match<3 && curtimeleft > 0; match++) { /* generate the board */ g[0] = generate_board(10,20,level[0]); event_name[0] = p->name; /* draw the background */ draw_background(screen, cs.style[0]->w, g, level, my_adj, NULL, event_name); /* get the result, but don't update level yet */ event_cs[0] = event_cs[1] = cs.style[cs.choice]; event_ss[0] = event_ss[1] = ss.style[ss.choice]; result = event_loop(screen, ps.style[ps.choice], event_cs, event_ss, g, level, 0, &curtimeleft, 1, adjustment, NULL, time(NULL), HUMAN_PLAYER, NO_PLAYER, NULL); if (result < 0) { /* explicit quit */ return level[0]; } /* reset board */ my_adj[match] = adjustment[0]; /* display those mighty accomplishments winnings */ draw_background(screen, cs.style[0]->w,g, level,my_adj,NULL, event_name); draw_score(screen,0); message[0] = 0; if (match == 2) { if (my_adj[0] == ADJUST_UP && my_adj[1] == ADJUST_UP && my_adj[2] == ADJUST_UP) /* go up! */ { level[0]++; SPRINTF(message,"Up to level %d!",level[0]); } else if (my_adj[0] == ADJUST_DOWN && my_adj[1] == ADJUST_DOWN && my_adj[2] == ADJUST_DOWN) { /* devolve! */ level[0]--; SPRINTF(message,"Down to level %d!",level[0]); } } if (give_notice(message, 1) == 1) { /* explicit quit */ return level[0]; } } } } /*************************************************************************** * main() * Start the program, check the arguments, etc. ***************************************************************************/ int main(int argc, char *argv[]) { color_styles cs; piece_styles ps; sound_styles ss; identity *id; AI_Players *ai; Grid g[2]; int renderstyle = TTF_STYLE_NORMAL; unsigned int flags; Uint32 time_now; SDL_Event event; umask(000); Debug("\tWelcome to Alizarin Tetris (version %s - %s)\n", VERSION, "Delay" ); Debug("\t~~~~~~~~~~~~~~~~~~~~~~~~~~ (%s)\n", __DATE__); #ifdef HAVE_WINSOCK_H load_options("atris.rc"); #else { char filespec[2048]; SPRINTF(filespec,"%s/.atrisrc", getenv("HOME")); load_options(filespec); } #endif parse_options(argc, argv); if (SDL_Init(SDL_INIT_VIDEO)) PANIC("SDL_Init failed!"); /* Clean up on exit */ atexit(SDL_Quit); /* Initialize the TTF library */ if ( TTF_Init() < 0 ) PANIC("TTF_Init failed!"); atexit(TTF_Quit); Debug("SDL initialized.\n"); /* Initialize the display in a 640x480 native-depth mode */ flags = // SDL_HWSURFACE | SDL_SWSURFACE | SDL_FULLSCREEN | // SDL_SRCCOLORKEY | // SDL_ANYFORMAT | 0; if (Options.full_screen) flags |= SDL_FULLSCREEN; screen = SDL_SetVideoMode(640, 480, Options.bpp_wanted, flags); if ( screen == NULL ) PANIC("Could not set 640x480 video mode"); Debug("Video Mode: %d x %d @ %d bpp\n", screen->w, screen->h, screen->format->BitsPerPixel); if (screen->format->BitsPerPixel <= 8) { PANIC("You need >256 colors to play atris"); } /* Set the window title */ SDL_WM_SetCaption("Alizarin Tetris", (char*)NULL); Network_Init(); setup_colors(screen); setup_layers(screen); if (chdir(ATRIS_LIBDIR)) { Debug("WARNING: cannot change directory to [%s]\n", ATRIS_LIBDIR); Debug("WARNING: playing in current directory instead\n"); } else Debug("Changing directory to [%s]\n",ATRIS_LIBDIR); /* Set up the font */ sfont = TTF_OpenFont("graphics/NewMediumNormal.ttf",18); font = TTF_OpenFont("graphics/NewMediumNormal.ttf",24); lfont = TTF_OpenFont("graphics/NewMediumNormal.ttf",36); hfont = TTF_OpenFont("graphics/NewMediumNormal.ttf",96); if ( font == NULL ) PANIC("Couldn't open [graphics/NewMediumNormal.ttf].", ATRIS_LIBDIR); TTF_SetFontStyle(font, renderstyle); TTF_SetFontStyle(sfont, renderstyle); /* Initialize scores */ Score[0] = Score[1] = 0; /* enable unicode so we can easily see which key they pressed */ SDL_EnableUNICODE(1); ps = load_piece_styles(); cs = load_color_styles(screen); ss = load_sound_styles(Options.sound_wanted); if (Options.named_color >= 0 && Options.named_color < cs.num_style) cs.choice = Options.named_color; if (Options.named_piece >= 0 && Options.named_piece < ps.num_style) ps.choice = Options.named_piece; if (Options.named_sound >= 0 && Options.named_sound < ss.num_style) ss.choice = Options.named_sound; if (Options.named_game >= 0 && Options.named_game <= 5) gametype = (GT) Options.named_game; else gametype = SINGLE; ai = AI_Players_Setup(); id = load_identity_file(); atris_xflame_setup(); /* our happy splash screen */ { while (SDL_PollEvent(&event)) ; draw_string(PACKAGE " - " VERSION, color_white, screen->w, 0, DRAW_LEFT | DRAW_UPDATE); draw_string("Welcome To", color_purple, screen->w/2, screen->h/2, DRAW_CENTER | DRAW_UPDATE | DRAW_ABOVE | DRAW_LARGE); draw_string("Alizarin Tetris", color_blue, screen->w/2, screen->h/2, DRAW_CENTER | DRAW_UPDATE | DRAW_HUGE); } time_now = SDL_GetTicks(); do { poll_and_flame(&event); } while (SDL_GetTicks() < time_now + 600); clear_screen_to_flame(); /* lose the "welcome to" words */ while (1) { int p1, p2; int retval; SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY*10, SDL_DEFAULT_REPEAT_INTERVAL); choose_gametype(&ps,&cs,&ss,ai); if (gametype == QUIT) break; else Options.named_game = (int)gametype; /* play our game! */ Score[0] = Score[1] = 0; SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY*10, SDL_DEFAULT_REPEAT_INTERVAL); /* what sort of game would you like? */ switch (gametype) { case SINGLE: p1 = who_are_you(screen, &id, -1, 1); clear_screen_to_flame(); if (p1 < 0) break; id->p[p1].level = play_SINGLE(cs,ps,ss,g,&id->p[p1]); clear_screen_to_flame(); break; case MARATHON: p1 = who_are_you(screen, &id, -1, 1); clear_screen_to_flame(); if (p1 < 0) break; retval = play_MARATHON(cs,ps,ss,g,&id->p[p1]); clear_screen_to_flame(); /* it's high score time */ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY*10, SDL_DEFAULT_REPEAT_INTERVAL); high_score_check(retval, Score[0]); clear_screen_to_flame(); break; case SINGLE_VS_AI: p1 = who_are_you(screen, &id, -1, 1); clear_screen_to_flame(); if (p1 < 0) break; p2 = pick_an_ai(screen, "As Your Opponent", ai); if (p2 < 0) break; ai->player[p2].delay_factor = pick_ai_factor(screen); clear_screen_to_flame(); id->p[p1].level = play_SINGLE_VS_AI(cs,ps,ss,g, &id->p[p1], &ai->player[p2]); clear_screen_to_flame(); break; case AI_VS_AI: p1 = pick_an_ai(screen, "As Player 1", ai); clear_screen_to_flame(); if (p1 < 0) break; p2 = pick_an_ai(screen, "As Player 2", ai); clear_screen_to_flame(); if (p2 < 0) break; play_AI_VS_AI(cs,ps,ss,g, &ai->player[p1], &ai->player[p2]); clear_screen_to_flame(); break; case TWO_PLAYERS: p1 = who_are_you(screen, &id, -1, 1); clear_screen_to_flame(); if (p1 < 0) break; p2 = who_are_you(screen, &id, p1, 2); clear_screen_to_flame(); if (p2 < 0) break; play_TWO_PLAYERS(cs,ps,ss,g, &id->p[p1], &id->p[p2]); clear_screen_to_flame(); break; case NETWORK: p1 = who_are_you(screen, &id, -1, 1); clear_screen_to_flame(); if (p1 < 0) break; id->p[p1].level = play_NETWORK(cs,ps,ss,g,&id->p[p1],network_choice(screen)); clear_screen_to_flame(); break; default: break; } /* * save those updates! */ save_identity_file(id, NULL, 0); } /* end: while(choose_gametype() != -1) */ SDL_CloseAudio(); TTF_CloseFont(sfont); TTF_CloseFont( font); TTF_CloseFont(lfont); TTF_CloseFont(hfont); Network_Quit(); Options.named_color = cs.choice; Options.named_piece = ps.choice; Options.named_sound = ss.choice; #ifdef HAVE_WINSOCK_H save_options("atris.rc"); #else { char filespec[2048]; SPRINTF(filespec,"%s/.atrisrc", getenv("HOME")); save_options(filespec); } #endif return 0; } /* * $Log: atris.c,v $ * Revision 1.100 2001/01/06 00:30:55 weimer * yada * * Revision 1.99 2001/01/06 00:25:23 weimer * changes so that we remember things ... * * Revision 1.98 2001/01/05 21:38:01 weimer * more key changes * * Revision 1.97 2001/01/05 21:36:28 weimer * little change ... * * Revision 1.96 2001/01/05 21:12:31 weimer * advance to atris 1.0.5, add support for ".atrisrc" and changing the * keyboard repeat rate * * Revision 1.95 2000/11/10 18:16:48 weimer * changes for Atris 1.0.4 - three new special options * * Revision 1.94 2000/11/06 04:41:47 weimer * fixed up Nirgal to Olympus * * Revision 1.93 2000/11/06 04:39:56 weimer * networking consistency check for power pieces * * Revision 1.92 2000/11/06 04:06:44 weimer * option menu * * Revision 1.91 2000/11/06 01:22:40 wkiri * Updated menu system. * * Revision 1.90 2000/11/03 03:41:35 weimer * made the light and dark "edges" of pieces global, rather than part of a * specific color style. also fixed a bug where we were updating too much * when drawing falling pieces (bad min() code on my part) * * Revision 1.89 2000/11/02 03:06:20 weimer * better interface for walk-radio menus: we are now ready for Kiri to change * them to add run-time options ... * * Revision 1.88 2000/11/01 03:53:06 weimer * modifications for version 1.0.1: you can pick your starting level, you can * pick the AI difficulty factor, the game is better about placing new pieces * when there is garbage, when things fall out of your control they now fall * at a uniform rate ... * * Revision 1.87 2000/10/30 16:25:25 weimer * display the network player score during network games. Also give the * non-server a little message when waiting for the server to go on. * * Revision 1.86 2000/10/30 03:49:09 weimer * minor changes ... * * Revision 1.85 2000/10/29 22:55:01 weimer * networking consistency checks (you must have the same number of doodads): * special hotkey 'f' in main menu toggles full screen mode * added depth specification on the command line * automatically search for the darkest non-black color ... * * Revision 1.84 2000/10/29 21:28:58 weimer * fixed a few failures to clear the screen if we didn't have a flaming * backdrop * * Revision 1.83 2000/10/29 21:23:28 weimer * One last round of header-file changes to reflect my newest and greatest * knowledge of autoconf/automake. Now if you fail to have some bizarro * function, we try to go on anyway unless it is vastly needed. * * Revision 1.82 2000/10/29 19:04:32 weimer * minor highscore handling changes: new filename, use the draw_string() and * draw_bordered_rect() and input_string() interfaces, handle the widget layer * and the flame layer, etc. Also fix a minor bug where you would be prevented * from settling if you pressed a key even if it didn't really move you. :-) * * Revision 1.81 2000/10/29 17:23:13 weimer * incorporate "xflame" flaming background for added spiffiness ... * * Revision 1.80 2000/10/29 00:17:39 weimer * added support for a system independent random number generator * * Revision 1.79 2000/10/29 00:06:27 weimer * networking fixes, change "styles/" to "styles" so that it works on Windows * * Revision 1.78 2000/10/28 23:39:24 weimer * added initialization support for Winsock 1.1 networking, ala SDL_net * * Revision 1.77 2000/10/28 16:40:17 weimer * Further changes: we can now build .tar.gz files and RPMs! * * Revision 1.76 2000/10/28 13:39:14 weimer * added a pausing feature ... * * Revision 1.75 2000/10/27 19:39:49 weimer * doubled the level for random AI deathmatches ... * * Revision 1.74 2000/10/21 01:14:42 weimer * massive autoconf/automake restructure ... * * Revision 1.73 2000/10/20 21:17:37 weimer * re-added the lemmings sound style ... * * Revision 1.72 2000/10/20 01:32:09 weimer * Minor play issue problems -- time is now truly a hard limit! * * Revision 1.71 2000/10/19 01:04:46 weimer * consistency error * * Revision 1.70 2000/10/19 00:20:27 weimer * sound directory changes, added a ticking clock ... * * Revision 1.69 2000/10/18 23:57:49 weimer * general fixup, color changes, display changes. * Notable: "Safe" Blits and Updates now perform "clipping". No more X errors, * we hope! * * Revision 1.68 2000/10/18 02:12:37 weimer * network fix * * Revision 1.67 2000/10/18 02:07:16 weimer * network play improvements * * Revision 1.66 2000/10/18 02:04:02 weimer * playability changes ... * * Revision 1.65 2000/10/14 16:17:41 weimer * level adjustment changes, added some new AIs, etc. * * Revision 1.64 2000/10/14 02:52:44 weimer * fixed a memory corruption problem in display (a use after a free) * * Revision 1.63 2000/10/14 01:42:53 weimer * better scoring of thumbs-up, thumbs-down * * Revision 1.62 2000/10/14 01:24:34 weimer * fixed error with not advancing levels when fighting AI * * Revision 1.61 2000/10/13 22:34:26 weimer * minor wessy AI changes * * Revision 1.60 2000/10/13 18:23:28 weimer * fixed a race condition in tetris_event() * * Revision 1.59 2000/10/13 16:37:39 weimer * Changed the AI so that it now passes state around via void pointers, rather * than using global variables. This allows the same AI to play itself. Also * changed the "AI vs. AI" display so that you can keep track of total wins * and losses. * * Revision 1.58 2000/10/13 15:41:53 weimer * revamped AI support, now you can pick your AI and have AI duels (such fun!) * the mighty Aliz AI still crashes a bit, though ... :-) * * Revision 1.57 2000/10/13 02:26:54 weimer * rudimentary identity functions, including adding new players * * Revision 1.56 2000/10/12 22:21:25 weimer * display changes, more multi-local-play threading (e.g., myScore -> * Score[0]), that sort of thing ... * * Revision 1.55 2000/10/12 19:17:08 weimer * Further support for AI players and multiple game types. * * Revision 1.54 2000/10/12 01:38:07 weimer * added initial support for persistent player identities * * Revision 1.53 2000/10/12 00:49:08 weimer * added "AI" support and walking radio menus for initial game configuration * * Revision 1.52 2000/09/09 19:14:34 weimer * forgot to draw the background in MULTI games .. * * Revision 1.51 2000/09/09 17:05:35 wkiri * Hideous log changes (Wes: how dare you include a comment character!) * * Revision 1.50 2000/09/09 16:58:27 weimer * Sweeping Change of Ultimate Mastery. Main loop restructuring to clean up * main(), isolate the behavior of the three game types. Move graphic files * into graphics/-, style files into styles/-, remove some unused files, * add game flow support (breaks between games, remembering your level within * this game), multiplayer support for the event loop, some global variable * cleanup. All that and a bag of chips! * * Revision 1.49 2000/09/05 20:22:12 weimer * native video mode selection, timing investigation * * Revision 1.48 2000/09/04 19:48:02 weimer * interim menu for choosing among game styles, button changes (two states) * * Revision 1.47 2000/09/03 21:17:38 wkiri * non-MULTI player games don't need to check for passing random seed around. * * Revision 1.46 2000/09/03 21:06:31 wkiri * Now handles three different game types (and does different things). * Major changes in display.c to handle this. */