#include <stdlib.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <linux/joystick.h>
#include "TexFont.h"
#include "fbn.h"
#include "dmcgl.h"
#include "snake3d.h"
#define SOUNDSET_MUNCH_FOOD 0
#define SOUNDSET_REPLACE_FOOD 1
#define SOUNDSET_GROW_SNAKE 2
#define SOUNDSET_GAME_OVER 3
#define SOUNDSET_NEW_GAME 4
#define SOUNDSET_WINNER 5
SoundSet soundset = {6,
"./media/munch_food2.wav", NULL, 0,
"./media/replace_food.snd", NULL, 0,
"./media/grow_snake.snd", NULL, 0,
"./media/game_over.snd", NULL, 0,
"./media/new_game.snd", NULL, 0,
"./media/winner.snd", NULL, 0};
JoystickState jstate;
Game game;
GameHistory history;
Camera camera = {50.0, 50.0, 75.0f,
50.0, 50.0, 0.0,
0.0, 1.0, 0.0};
Camera txt_camera = {0.0, 0.0, 75.0f,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0};
GLuint texSnakeSkin;
GLuint texGrass;
GLuint texMarble;
TexFont *txf;
int main(int argc, char *argv[])
{
int i;
game.previous_state = OFF;
game.mainmenu = MENU_CLASSIC;
game.options.classic.init_snake_length = 9;
game.options.classic.init_snake_belly = 0;
game.options.classic.game_width = 20;
game.options.classic.game_height = 11;
game.options.classic.food_amount = 1;
game.options.classic.timeslice = 333333;
game.options.vs.init_snake_length = 13;
game.options.vs.init_snake_belly = 0;
game.options.vs.game_width = 40;
game.options.vs.game_height = 22;
game.options.vs.food_amount = 3;
game.options.vs.timeslice = 333333;
game.options.quest.timeslice = 333333;
game.options.quest.level1.length = 9;
game.options.quest.level1.food = 3;
game.options.quest.level1.width = 20;
game.options.quest.level1.height = 11;
game.options.quest.level2.length = 18;
game.options.quest.level2.food = 3;
game.options.quest.level2.width = 40;
game.options.quest.level2.height = 22;
game.options.win_width = 640;
game.options.win_height = 480;
game.options.use_sound = 0;
game.options.fullscreen = 0;
game.options.show_fps = 1;
game.options.num_snakes = 1;
game.options.snake_segment_color[0][0] = 255;
game.options.snake_segment_color[0][1] = 0;
game.options.snake_segment_color[0][2] = 0;
game.options.snake_segment_color[0][3] = 0;
game.options.snake_segment_color[1][0] = 0;
game.options.snake_segment_color[1][1] = 255;
game.options.snake_segment_color[1][2] = 0;
game.options.snake_segment_color[1][3] = 0;
for(i = 1; i < argc; ++i){
if (argv[i][0] == '-'){
switch (argv[i][1]){
case 'w':
if (++i >= argc) Usage();
game.options.classic.game_width = atoi(argv[i]);
break;
case 'h':
if (++i >= argc) Usage();
game.options.classic.game_height = atoi(argv[i]);
break;
case 'l':
if (++i >= argc) Usage();
game.options.classic.init_snake_length = atoi(argv[i]);
if(game.options.classic.init_snake_length < 2) Usage();
break;
case 't':
if (++i >= argc) Usage();
game.options.classic.timeslice = atoi(argv[i]);
break;
case 's':
game.options.use_sound = 0;
break;
case 'f':
game.options.fullscreen = 1;
break;
case 'r':
game.options.show_fps = !game.options.show_fps;
break;
default:
Usage();
break;
}
} else {
}
}
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowPosition(0, 0);
glutInitWindowSize(game.options.win_width, game.options.win_height);
glutCreateWindow("Snake3D");
if(game.options.fullscreen) glutFullScreen();
glutReshapeFunc(reshape);
glutSpecialFunc(special);
if(game.options.use_sound){
fbnSoundInit();
fbnSoundLoadData(&soundset);
}
txf = txfLoadFont("./media/font1.txf");
if(txf == NULL){
fprintf(stderr, "Problem loading %s, %s\n",
"./media/font1.txf", txfErrorString());
exit(1);
}
txfEstablishTexture(txf, 0, GL_TRUE);
JoystickInit();
ReadGameHistory();
GraphicsInit();
S3DInit();
SetState(MENU);
glutMainLoop();
return 0;
}
void GraphicsInit(void){
int i;
int texwidth, texheight;
GLubyte *texture;
GLfloat light_ambient[] = {0.2, 0.2, 0.2, 1.0};
GLfloat light_diffuse[] = {0.8, 0.8, 0.8, 1.0};
GLfloat light_specular[] = {0.8, 0.8, 0.8, 1.0};
GLfloat light_position[] = {50.0, 50.0, 50.0, 0.0};
GLfloat mat_specular[] = {0.8, 0.8, 0.8, 1.0};
GLfloat mat_shininess[] = {50.0};
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
read_ppm_to_buffer(&texture,&texwidth,&texheight, "./media/snakeskin.ppm");
glGenTextures(1, &texSnakeSkin);
glBindTexture(GL_TEXTURE_2D, texSnakeSkin);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA,texwidth,texheight,0,GL_RGBA,\
GL_UNSIGNED_BYTE,texture);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, texwidth, texheight, \
GL_RGBA, GL_UNSIGNED_BYTE, texture);
read_ppm_to_buffer(&texture,&texwidth,&texheight, "./media/grass.ppm");
glGenTextures(1, &texGrass);
glBindTexture(GL_TEXTURE_2D, texGrass);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA,texwidth,texheight,0,GL_RGBA,\
GL_UNSIGNED_BYTE,texture);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, texwidth, texheight, \
GL_RGBA, GL_UNSIGNED_BYTE, texture);
read_ppm_to_buffer(&texture,&texwidth,&texheight, "./media/marble.ppm");
glGenTextures(1, &texMarble);
glBindTexture(GL_TEXTURE_2D, texMarble);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA,texwidth,texheight,0,GL_RGBA,\
GL_UNSIGNED_BYTE,texture);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, texwidth, texheight, \
GL_RGBA, GL_UNSIGNED_BYTE, texture);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
}
void S3DInit(void){
int i;
if(!(game.fps_history = malloc(FPS_ARRAY_SIZE * sizeof(int)))){
debug("failed to malloc fps_history\n");
exit(1);
}
for(i=0; i<FPS_ARRAY_SIZE; i++) game.fps_history[i] = FPS_INIT_VALUE;
game.fps_history_total = FPS_ARRAY_SIZE * FPS_INIT_VALUE;
game.fps_history_pointer = 0;
game.fps_recent_worst_value = FPS_INIT_VALUE;
game.fps_recent_worst_index = 0;
}
void SetState(GameState new_state)
{
switch(new_state){
case INSTRUCTIONS:
glutDisplayFunc(draw_nothing);
glutIdleFunc(idle_shared);
glutKeyboardFunc(key_shared);
break;
case MENU:
glutDisplayFunc(draw_menu);
glutIdleFunc(idle_shared);
glutKeyboardFunc(key_menu);
break;
case GAME_CLASSIC :
// a new gametick timer call
glutTimerFunc((int)(game.timeslice / 1000), timer_classic, 0);
glutDisplayFunc(draw_game);
glutIdleFunc(idle_shared);
glutKeyboardFunc(key_game_shared);
SetTimeIfUnpausing();
break;
case GAME_VS:
// a new gametick timer call
glutTimerFunc((int)(game.timeslice / 1000), timer_vs, 0);
glutDisplayFunc(draw_game);
glutIdleFunc(idle_shared);
glutKeyboardFunc(key_game_shared);
SetTimeIfUnpausing();
break;
case GAME_QUEST_LEVEL1:
// a new gametick timer call
glutTimerFunc((int)(game.timeslice / 1000), timer_quest_level1, 0);
glutDisplayFunc(draw_game);
glutIdleFunc(idle_shared);
glutKeyboardFunc(key_game_shared);
SetTimeIfUnpausing();
break;
case GAME_QUEST_LEVEL2:
// record the score from the first level
game.statedata.quest.score += game.snake[0].length;
// a new gametick timer call
glutTimerFunc((int)(game.timeslice / 1000), timer_quest_level2, 0);
glutDisplayFunc(draw_game);
glutIdleFunc(idle_shared);
glutKeyboardFunc(key_game_shared);
SetTimeIfUnpausing();
break;
case GAME_PAUSED:
// ASSUMPTION: the current gamestate is GAME_*
SetTimePausing();
glutDisplayFunc(draw_game_paused);
glutIdleFunc(idle_shared);
glutKeyboardFunc(key_game_paused);
break;
case GAME_OVER:
switch(game.state){
case GAME_CLASSIC:
game.statedata.classic.score = game.snake[0].length;
if(game.statedata.classic.score >=
history.classic_hiscores[NUM_HI_SCORES - 1].score){
if(game.options.use_sound) fbnSoundPlay(SOUNDSET_WINNER, &soundset);
game.statedata.enter_initials.score = game.statedata.classic.score;
game.statedata.enter_initials.gametype = CLASSIC;
strcpy(game.statedata.enter_initials.gametype_string, "Classic");
game.statedata.enter_initials.hiscores = history.classic_hiscores;
game.statedata.enter_initials.place = InsertNewHiScore();
} else {
if(game.options.use_sound) fbnSoundPlay(SOUNDSET_GAME_OVER, &soundset);
}
break;
case GAME_VS:
if(game.options.use_sound) fbnSoundPlay(SOUNDSET_GAME_OVER, &soundset);
break;
case GAME_QUEST_LEVEL2:
game.statedata.quest.score += game.snake[0].length;
if(game.statedata.quest.score >=
history.quest_hiscores[NUM_HI_SCORES - 1].score){
if(game.options.use_sound) fbnSoundPlay(SOUNDSET_WINNER, &soundset);
game.statedata.enter_initials.score = game.statedata.quest.score;
game.statedata.enter_initials.gametype = QUEST;
strcpy(game.statedata.enter_initials.gametype_string, "Quest");
game.statedata.enter_initials.hiscores = history.quest_hiscores;
game.statedata.enter_initials.place = InsertNewHiScore();
} else {
if(game.options.use_sound) fbnSoundPlay(SOUNDSET_GAME_OVER, &soundset);
}
break;
}
glutDisplayFunc(draw_game_over);
glutIdleFunc(idle_shared);
glutKeyboardFunc(key_game_over);
break;
case ENTER_INITIALS:
game.statedata.enter_initials.current_initial = 0;
glutDisplayFunc(draw_enter_initials);
glutIdleFunc(idle_shared);
glutKeyboardFunc(key_enter_initials);
break;
case HI_SCORES:
// record starttime for animations
gettimeofday(&game.statedata.hi_scores.starttime, NULL);
// if a hi score was recently entered, start the display with the
// corresponding gametype
if(game.statedata.enter_initials.place){
switch(game.statedata.enter_initials.gametype){
case CLASSIC:
break;
case QUEST:
game.statedata.hi_scores.starttime.tv_sec -= HI_SCORE_CYCLE / 2;
break;
default:
output("assertion failed in SetState(HI_SCORES) gametype was %d\n",
game.statedata.enter_initials.gametype);
}
}
glutDisplayFunc(draw_hi_scores);
glutIdleFunc(idle_shared);
glutKeyboardFunc(key_hi_scores);
break;
case OFF:
// shut down joystick engine
JoystickFini();
// shut down font engine
txfUnloadFont(txf);
// shut down sound engine
if(game.options.use_sound){
fbnSoundUnloadData(&soundset);
fbnSoundFini();
}
// and begone...
exit(0);
break;
default:
// error
debug("Entering unknown game state %d\n", new_state);
debug("previous state was %d\n", game.state);
exit(1);
break;
}
game.previous_state = game.state;
game.state = new_state;
glutPostRedisplay();
}
void SetTimePausing(void){
struct timeval temptime;
gettimeofday(&temptime, NULL);
game.time_of_last_gametick.tv_usec =
TIMEDIFF(temptime, game.time_of_last_gametick);
}
void SetTimeIfUnpausing(void){
struct timeval temptime;
if(game.state == GAME_PAUSED){
gettimeofday(&temptime, NULL);
game.time_of_last_gametick.tv_usec =
TIMEDIFF(temptime, game.time_of_last_gametick);
}
}
void draw_nothing(void)
{
glLoadIdentity();
gluLookAt(camera.eyex, camera.eyey, camera.eyez,
camera.centerx, camera.centery, camera.centerz,
camera.upx, camera.upy, camera.upz);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glutSwapBuffers();
}
void draw_game(void)
{
struct timeval newtime;
long game_usecdiff;
long previous_render_sec, previous_render_usec;
float partial_factor;
int i,j;
++game.frames_rendered;
gettimeofday(&newtime, NULL);
if(game.state == GAME_PAUSED){
game_usecdiff = MIN(game.time_of_last_gametick.tv_usec,
game.timeslice);
// game_usecdiff = game.time_of_last_gametick.tv_usec;
} else if(game.state == GAME_OVER){
// gameover happens at the end of a cycle, because the next cycle would
// have caused an illegal move
game_usecdiff = game.timeslice;
} else {
game_usecdiff = MIN(TIMEDIFF(newtime, game.time_of_last_gametick),
game.timeslice);
// game_usecdiff = TIMEDIFF(newtime, game.time_of_last_gametick);
}
partial_factor = (float)game_usecdiff / (float)game.timeslice;
if(game.options.show_fps){
game.fps_last_frame = TIMEDIFF(newtime, game.time_of_last_render);
if(!game.fps_last_frame){
game.fps_last_frame = 666;
debug("fps_last_frame 666\n");
} else {
game.fps_last_frame = 1000000 / game.fps_last_frame;
}
game.fps_history_total -= game.fps_history[game.fps_history_pointer];
game.fps_history_total += game.fps_last_frame;
// do we need to set a new recent_worst, or expire the old one, or not
if((game.fps_last_frame < game.fps_recent_worst_value)||
(game.fps_history[game.fps_history_pointer] ==
game.fps_recent_worst_value)){
game.fps_recent_worst_value = game.fps_last_frame;
game.fps_recent_worst_index = game.fps_history_pointer;
}
game.fps_history[game.fps_history_pointer] = game.fps_last_frame;
game.fps_history_pointer = (game.fps_history_pointer + 1) % FPS_ARRAY_SIZE;
}
game.time_of_last_render = newtime;
glLoadIdentity();
gluLookAt(camera.eyex, camera.eyey, camera.eyez,
camera.centerx, camera.centery, camera.centerz,
camera.upx, camera.upy, camera.upz);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef((SCREEN_WIDTH - game.render_width) / 2.0f,
(SCREEN_HEIGHT - game.render_height) / 2.0f,
0.0f);
draw_arena();
for(i=0; i<MAX_SNAKES; i++){
if(game.snake[i].active){
draw_snake(i, partial_factor);
}
}
draw_food();
draw_text();
glutSwapBuffers();
glutPostRedisplay();
}
void draw_game_paused(void)
{
draw_game();
}
void draw_menu(void)
{
char screen_string[1024];
char temp_string[1024];
int i;
MainMenuState menustate;
float r, g, b, w, h;
float zoom;
float delta;
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(50.0f, 50.0f, 50.0f,
50.0f, 50.0f, 0.0f,
0.0f, 1.0f, 0.0f);
zoom = 5.0f;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texGrass);
glBegin(GL_POLYGON);
glColor3f(1.0f, 1.0f, 1.0f);
glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glTexCoord2f(zoom, 0.0f);
glVertex3f(100.0f, 0.0f, 0.0f);
glTexCoord2f(zoom, zoom);
glVertex3f(100.0f, 100.0f, 0.0f);
glTexCoord2f(0.0f, zoom);
glVertex3f(0.0f, 100.0f, 0.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
fbnTxfGLStateSet();
fbnTxfRenderStringExtruded("Snake3D", 0, 50.0f, 85.0f, 60.0f, 20.0f,
2.0f, 2.0f, 10,
0.2f, 0.9f, 0.2f, 1.0f,
0.2f, 0.0f, 0.2f, 1.0f, txf);
for(menustate=0; menustate<MAINMENUSTATES; menustate++){
switch(menustate){
case MENU_INSTRUCTIONS:
strcpy(temp_string, "Instructions");
break;
case MENU_CLASSIC:
strcpy(temp_string, "Classic");
break;
case MENU_VS:
strcpy(temp_string, "VS");
break;
case MENU_QUEST:
strcpy(temp_string, "Quest");
break;
case MENU_HI_SCORES:
strcpy(temp_string, "Hi Scores");
break;
case MENU_QUIT:
strcpy(temp_string, "Quit");
break;
default:
debug("Error, unknown menustate\n");
exit(1);
}
w = 4.0f * strlen(temp_string);
h = 40.0f / (MAINMENUSTATES * 1.2f);
r = 0.7f; g = 0.7f; b = 0.7f;
if(game.mainmenu == menustate){
r = 1.0f; g = 0.0f; b = 0.0f;
w = w * 1.25f;
h = h * 1.25f;
}
fbnTxfRenderString(temp_string, 0,
50.0f, 60.0f - (menustate *
(50.0f / (MAINMENUSTATES - 1))),
w, h, r, g, b, 1.0f, txf);
}
fbnTxfGLStateUnset();
glutSwapBuffers();
glutPostRedisplay();
}
void draw_game_over(void)
{
draw_game();
}
void draw_enter_initials(void)
{
float bg_tex_zoom;
float tx, ty, tw, th;
float r, g, b, a;
char screen_string[256];
char place_string[8];
char gametype_string[256];
int i;
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(txt_camera.eyex, txt_camera.eyey, txt_camera.eyez,
txt_camera.centerx, txt_camera.centery, txt_camera.centerz,
txt_camera.upx, txt_camera.upy, txt_camera.upz);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
bg_tex_zoom = 3.0f;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texGrass);
glBegin(GL_POLYGON);
glColor3f(1.0f, 1.0f, 1.0f);
glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-SCREEN_WIDTH, -SCREEN_HEIGHT, 0.0f);
glTexCoord2f(bg_tex_zoom, 0.0f);
glVertex3f(SCREEN_WIDTH, -SCREEN_HEIGHT, 0.0f);
glTexCoord2f(bg_tex_zoom, bg_tex_zoom);
glVertex3f(SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f);
glTexCoord2f(0.0f, bg_tex_zoom);
glVertex3f(-SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
fbnTxfGLStateSet();
fbnTxfRenderString("New Hi Score", 0,
50.0f, 85.0f, 90.0f, 20.0f,
0.1f, 0.9f, 0.1f, 1.0f, txf);
fbnSetPlaceString(place_string, game.statedata.enter_initials.place);
sprintf(screen_string, "Game: %s Score: %3d Rank:%5s",
game.statedata.enter_initials.gametype_string,
game.statedata.enter_initials.score, place_string);
fbnTxfRenderString(screen_string, 0,
50.0f, 65.0f, 90.0f, 8.0f,
0.9f, 0.1f, 0.1f, 1.0f, txf);
for(i=0; i<3; i++){
sprintf(screen_string, "%c",
game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[i]);
if(game.statedata.enter_initials.current_initial == i){
r = 0.9f; g = 0.1f, b = 0.1f, a = 1.0f;
} else {
r = 0.9f; g = 0.1f, b = 0.9f, a = 1.0f;
}
fbnTxfRenderString(screen_string, 0,
20.0f + i * 20.0f, 40.0f, 20.0f, 30.0f,
r, g, b, a, txf);
}
if(game.statedata.enter_initials.current_initial == 3){
r = 0.9f; g = 0.1f, b = 0.1f, a = 1.0f;
} else {
r = 0.9f; g = 0.1f, b = 0.9f, a = 1.0f;
}
fbnTxfRenderString("D", 0,
72.5f, 47.5f, 5.0f, 5.0f,
r, g, b, a, txf);
fbnTxfRenderString("o", 0,
77.5f, 42.5f, 5.0f, 5.0f,
r, g, b, a, txf);
fbnTxfRenderString("n", 0,
82.5f, 37.5f, 5.0f, 5.0f,
r, g, b, a, txf);
fbnTxfRenderString("e", 0,
87.5f, 32.5f, 5.0f, 5.0f,
r, g, b, a, txf);
fbnTxfRenderString("enter initials using snake controls", 0,
50.0f, 10.0f, 90.0f, 10.0f,
0.9f, 0.9f, 0.9f, 1.0f, txf);
fbnTxfGLStateUnset();
glutSwapBuffers();
glutPostRedisplay();
}
void draw_arena(void){
float zoom;
float x1, y1, x2, y2;
int i;
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
zoom = 30.0f;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texGrass);
glBegin(GL_POLYGON);
#define ARENA_SPREAD_WIDTH (10 * game.render_width)
#define ARENA_SPREAD_HEIGHT (10 * game.render_height)
glColor3f(1.0f, 1.0f, 1.0f);
glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(0.0f - ARENA_SPREAD_WIDTH,
0.0f - ARENA_SPREAD_HEIGHT, 0.0f);
glTexCoord2f(zoom, 0.0f);
glVertex3f(game.render_width + ARENA_SPREAD_WIDTH,
0.0f - ARENA_SPREAD_HEIGHT, 0.0f);
glTexCoord2f(zoom, zoom);
glVertex3f(game.render_width + ARENA_SPREAD_WIDTH,
game.render_height + ARENA_SPREAD_HEIGHT, 0.0f);
glTexCoord2f(0.0f, zoom);
glVertex3f(0.0f - ARENA_SPREAD_WIDTH,
game.render_height + ARENA_SPREAD_HEIGHT, 0.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
for(i=0; i<(game.game_height + 2); i++)
draw_brick(-1.0f, -1.0f + (float)i);
for(i=0; i<(game.game_width); i++)
draw_brick((float)i, (float)game.game_height);
for(i=0; i<(game.game_height + 2); i++)
draw_brick ((float)game.game_width, -1.0f + (float)i);
for(i=0; i<(game.game_width); i++)
draw_brick((float)i, -1.0f);
}
void draw_snake(int snake_id, float partial_factor){
float partial_x_offset, partial_y_offset;
Position current, previous;
Snake *snake;
snake = &(game.snake[snake_id]);
glColor3ubv(game.options.snake_segment_color[snake_id]);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texSnakeSkin);
current = snake->tail;
previous = snake->last_tail;
while(!((current.x == snake->head.x)&&(current.y == snake->head.y))){
// Calculate partial offset
if(current.y == previous.y){
partial_x_offset =
(current.x > previous.x) ? partial_factor : -partial_factor;
partial_y_offset = 0.0f;
} else {
partial_x_offset = 0.0f;
partial_y_offset =
(current.y > previous.y) ? partial_factor : -partial_factor;
}
glPushMatrix();
glTranslatef((2.5f + previous.x + partial_x_offset) *
game.screen_unit_length,
(2.5f + previous.y + partial_y_offset) *
game.screen_unit_length,
game.screen_unit_length / 2.0f);
glRotatef(45.0f, 1.0f, 0.0f, 0.0f);
glRotatef(45.0f, 0.0f, 1.0f, 0.0f);
glRotatef(45.0f, 0.0f, 0.0f, 1.0f);
// glutSolidCube(game.screen_unit_length / 2);
fbnSolidTexturedCube(game.screen_unit_length / 2);
glPopMatrix();
// setup for next iteration
previous = current;
current = game.matrix[current.x][current.y].next;
}
glDisable(GL_TEXTURE_2D);
if(current.y == previous.y){
partial_x_offset =
(current.x > previous.x) ? partial_factor : -partial_factor;
partial_y_offset = 0.0f;
} else {
partial_x_offset = 0.0f;
partial_y_offset =
(current.y > previous.y) ? partial_factor : -partial_factor;
}
glPushMatrix();
glTranslatef((2.5f + previous.x + partial_x_offset) *
game.screen_unit_length,
(2.5f + previous.y + partial_y_offset) *
game.screen_unit_length,
game.screen_unit_length / 2.0f);
glRotatef(45.0f, 1.0f, 0.0f, 0.0f);
glRotatef(45.0f, 0.0f, 1.0f, 0.0f);
glRotatef(45.0f, 0.0f, 0.0f, 1.0f);
glutSolidCube(game.screen_unit_length / 2);
glPopMatrix();
}
void draw_food(void){
glColor3ub(255, 255, 0);
glPushMatrix();
glTranslatef((2.5f + game.food.x) * game.screen_unit_length,
(2.5f + game.food.y) * game.screen_unit_length,
game.screen_unit_length / 2.0f);
glutSolidSphere(game.screen_unit_length / 2, 10, 8);
glPopMatrix();
}
void draw_text(void){
char screen_string[1024];
char temp_string[1024];
int i;
float txf_scale_factor;
int width, ascent, descent;
GameState state;
fbnTxfGLStateSet();
sprintf(screen_string, "P1:%3d ", game.snake[0].length);
fbnTxfRenderString(screen_string, "P1:012 ",
16.0f, 10.0f, 28.0f, 9.0f,
0.9f, 0.1f, 0.1f, 1.0f, txf);
if(game.options.num_snakes > 1){
sprintf(screen_string, "P2:%3d ", game.snake[1].length);
fbnTxfRenderString(screen_string, "P2:012 ",
84.0f, 10.0f, 28.0f, 9.0f,
0.1f, 0.9f, 0.1f, 1.0f, txf);
}
if(game.options.show_fps){
// sprintf(screen_string, "fps:%4d", game.fps_last_frame);
sprintf(screen_string, "fps:%4d ave:%4d worst:%4d",
game.fps_last_frame,
game.fps_history_total / FPS_ARRAY_SIZE,
game.fps_recent_worst_value);
fbnTxfRenderString(screen_string, "fps:0123 ave:0123 worst:0123",
37.5f, 92.5f, 75.0f, 5.0f,
0.9f, 0.9f, 0.9f, 1.0f, txf);
}
if((game.state == GAME_PAUSED)||(game.state == GAME_OVER)){
state = game.previous_state;
} else {
state = game.state;
}
switch(state){
case GAME_CLASSIC:
sprintf(screen_string, " Classic ");
break;
case GAME_VS:
sprintf(screen_string, " VS ");
break;
case GAME_QUEST_LEVEL1:
sprintf(screen_string, "Quest: Level 1");
break;
case GAME_QUEST_LEVEL2:
sprintf(screen_string, "Quest: Level 2");
break;
default:
debug("draw_text called from unknown gamestate: %d\n",
game.state);
}
fbnTxfRenderString(screen_string, " ",
50.0f, 93.0f, 50.0f, 10.0f,
0.0f, 0.9f, 0.0f, 1.0f, txf);
if(game.state == GAME_OVER){
switch(game.previous_state){
case GAME_CLASSIC:
case GAME_QUEST_LEVEL2:
if(game.statedata.enter_initials.place == 1){
sprintf(screen_string, "New Hi Score");
} else if(game.statedata.enter_initials.place){
sprintf(screen_string, "New Ranking Score");
} else {
sprintf(screen_string, "Game Over");
}
break;
case GAME_VS:
sprintf(screen_string, "Good Game");
break;
}
fbnTxfRenderString(screen_string, 0,
50.0f, 50.0f, 75.0f, 40.0f,
1.0f, 0.0f, 1.0f, 1.0f, txf);
fbnTxfRenderString("Press enter key", 0,
50.0f, 4.5f, 30.0f, 9.0f,
0.8f, 0.8f, 0.8f, 1.0f, txf);
} else if(game.state == GAME_PAUSED){
fbnTxfRenderString("PAUSED", 0,
50.0f, 50.0f, 60.0f, 40.0f,
1.0f, 1.0f, 1.0f, 1.0f, txf);
fbnTxfRenderString("Press enter key", 0,
50.0f, 4.5f, 30.0f, 9.0f,
0.8f, 0.8f, 0.8f, 1.0f, txf);
}
fbnTxfGLStateUnset();
}
void draw_brick(float x, float y)
{
#define INV_SQRT_TWO 0.707106f
static GLfloat n[10][3] =
{
{0.0, 0.0, 1.0},
{0.0, 0.0, -1.0},
{-1.0, 0.0, 0.0},
{1.0, 0.0, 0.0},
{0.0, 1.0, 0.0},
{0.0, -1.0, 0.0},
{-INV_SQRT_TWO, 0.0, INV_SQRT_TWO},
{INV_SQRT_TWO, 0.0, INV_SQRT_TWO},
{0.0f, INV_SQRT_TWO, INV_SQRT_TWO},
{0.0f, -INV_SQRT_TWO, INV_SQRT_TWO},
};
static GLint faces[10][4] =
{
// top, bot, lt, rt, fr, bk,
// lt_up, rt_up, fr_up, bk_up
{8, 9, 10, 11},
{0, 2, 1, 3},
{4, 6, 0, 2},
{7, 5, 3, 1},
{5, 4, 1, 0},
{6, 7, 2, 3},
{8, 10, 4, 6},
{11, 9, 7, 5},
{9, 8, 5, 4},
{10, 11, 6, 7}
};
#define BRICK_BOTTOM_Z 0.0f
#define BRICK_MIDDLE_Z 0.5f
#define BRICK_TOP_Z 0.625f
static GLfloat v[12][3] =
{
{0.0f, 1.0f, BRICK_BOTTOM_Z},
{1.0f, 1.0f, BRICK_BOTTOM_Z},
{0.0f, 0.0f, BRICK_BOTTOM_Z},
{1.0f, 0.0f, BRICK_BOTTOM_Z},
{0.0f, 1.0f, BRICK_MIDDLE_Z},
{1.0f, 1.0f, BRICK_MIDDLE_Z},
{0.0f, 0.0f, BRICK_MIDDLE_Z},
{1.0f, 0.0f, BRICK_MIDDLE_Z},
{0.125f, 0.875f, BRICK_TOP_Z},
{0.875f, 0.875f, BRICK_TOP_Z},
{0.125f, 0.125f, BRICK_TOP_Z},
{0.875f, 0.125f, BRICK_TOP_Z},
};
GLint i;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texMarble);
glColor3f(1.0f, 1.0f, 1.0f);
glPushMatrix();
glTranslatef((x + 2) * game.screen_unit_length,
(y + 2) * game.screen_unit_length,
0.0f);
glScalef(game.screen_unit_length, game.screen_unit_length,
game.screen_unit_length);
for (i = 0; i < 10; i++) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3fv(&n[i][0]);
glTexCoord2f(0.0, 1.0);
glVertex3fv(&v[faces[i][0]][0]);
glTexCoord2f(1.0, 1.0);
glVertex3fv(&v[faces[i][1]][0]);
glTexCoord2f(0.0, 0.0);
glVertex3fv(&v[faces[i][2]][0]);
glTexCoord2f(1.0, 0.0);
glVertex3fv(&v[faces[i][3]][0]);
glEnd();
}
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
void draw_hi_scores(void)
{
float zoom;
float r,g,b,a;
int i;
char placestring[8];
char tempstring[32];
float tx, ty, tw, th;
char gametypestring[32];
HiScoreEntry *hiscores;
struct timeval current_time;
int timediff;
float alpha;
int special_place;
gettimeofday(¤t_time, NULL);
timediff = TIMEDIFF(current_time, game.statedata.hi_scores.starttime);
if(timediff > HI_SCORE_CYCLE_USEC){
game.statedata.hi_scores.starttime.tv_sec += HI_SCORE_CYCLE;
timediff = timediff % HI_SCORE_CYCLE_USEC;
}
if((timediff > (HI_SCORE_DISPLAY_TIME_USEC + HI_SCORE_FADE_TIME_USEC))
&& (timediff < (HI_SCORE_CYCLE_USEC - HI_SCORE_FADE_TIME_USEC))){
hiscores = history.quest_hiscores;
strcpy(gametypestring, "Quest");
if((game.statedata.enter_initials.place) &&
(game.statedata.enter_initials.gametype == QUEST)){
special_place = game.statedata.enter_initials.place;
} else {
special_place = 0;
}
} else {
if((game.statedata.enter_initials.place) &&
(game.statedata.enter_initials.gametype == CLASSIC)){
special_place = game.statedata.enter_initials.place;
} else {
special_place = 0;
}
hiscores = history.classic_hiscores;
strcpy(gametypestring, "Classic");
}
if(timediff < HI_SCORE_DISPLAY_TIME_USEC){
alpha = 1.0f;
} else if(timediff <
(HI_SCORE_DISPLAY_TIME_USEC + HI_SCORE_FADE_TIME_USEC)){
alpha = 1.0f - ((timediff - HI_SCORE_DISPLAY_TIME_USEC) / 1000000.0f);
} else if(timediff <
(HI_SCORE_DISPLAY_TIME_USEC + 2 * HI_SCORE_FADE_TIME_USEC)){
alpha = (timediff -
(HI_SCORE_DISPLAY_TIME_USEC + HI_SCORE_FADE_TIME_USEC)) /
1000000.0f;
} else if(timediff <
(2 * HI_SCORE_DISPLAY_TIME_USEC + 2 * HI_SCORE_FADE_TIME_USEC)){
alpha = 1.0f;
} else if(timediff <
(2 * HI_SCORE_DISPLAY_TIME_USEC + 3 * HI_SCORE_FADE_TIME_USEC)){
alpha = 1.0f - ((timediff -
(2 * HI_SCORE_DISPLAY_TIME_USEC +
2 * HI_SCORE_FADE_TIME_USEC)) / 1000000.0f);
} else if(timediff <
(2 * HI_SCORE_DISPLAY_TIME_USEC + 4 * HI_SCORE_FADE_TIME_USEC)){
alpha = (timediff -
(2 * HI_SCORE_DISPLAY_TIME_USEC +
3 * HI_SCORE_FADE_TIME_USEC)) / 1000000.0f;
} else {
debug("assertion failed in draw_hi_scores timediff was %d\n", timediff);
exit(1);
}
glLoadIdentity();
gluLookAt(50.0f, 50.0f, 50.0f,
50.0f, 50.0f, 0.0f,
0.0f, 1.0f, 0.0f);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
zoom = 5.0f;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texGrass);
glBegin(GL_POLYGON);
glColor3f(1.0f, 1.0f, 1.0f);
glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glTexCoord2f(zoom, 0.0f);
glVertex3f(100.0f, 0.0f, 0.0f);
glTexCoord2f(zoom, zoom);
glVertex3f(100.0f, 100.0f, 0.0f);
glTexCoord2f(0.0f, zoom);
glVertex3f(0.0f, 100.0f, 0.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
fbnTxfGLStateSet();
#define HI_SCORE_WIDTH 55.0f
#define HI_SCORE_LOWER_BOUNDRY 15.0f
#define HI_SCORE_UPPER_BOUNDRY 75.0f
#define HI_SCORE_LEFT_BOUNDRY 35.0f
#define HI_SCORE_RIGHT_BOUNDRY 65.0f
sprintf(tempstring, "%s Hi Scores", gametypestring);
fbnTxfRenderString(tempstring, 0,
50.0f, (100.0f + HI_SCORE_UPPER_BOUNDRY) / 2.0f,
80.0f, 100.0f - HI_SCORE_UPPER_BOUNDRY,
0.0f, 1.0f, 0.0f, alpha, txf);
for(i=0; i<NUM_HI_SCORES; i++){
fbnSetPlaceString(placestring, i + 1);
sprintf(tempstring, "%s %c%c%c %d", placestring,
hiscores[i].initials[0],
hiscores[i].initials[1],
hiscores[i].initials[2],
hiscores[i].score);
tx = HI_SCORE_LEFT_BOUNDRY + i *
((HI_SCORE_RIGHT_BOUNDRY - HI_SCORE_LEFT_BOUNDRY) /
((float)NUM_HI_SCORES - 1.0f));
ty = HI_SCORE_LOWER_BOUNDRY + ((float)NUM_HI_SCORES - (float)i - 0.5f) *
((HI_SCORE_UPPER_BOUNDRY - HI_SCORE_LOWER_BOUNDRY) /
(float)NUM_HI_SCORES);
tw = HI_SCORE_WIDTH;
th = (HI_SCORE_UPPER_BOUNDRY - HI_SCORE_LOWER_BOUNDRY) /
(float)NUM_HI_SCORES;
if((i + 1) == (special_place)){
fbnTxfRenderString(tempstring, "10th fbn 999",
tx, ty, tw, th,
0.9f, 0.2f, 0.2f, alpha, txf);
} else {
fbnTxfRenderString(tempstring, "10th fbn 999",
tx, ty, tw, th,
0.8f, 0.0f, 0.8f, alpha, txf);
}
}
fbnTxfRenderString("press enter key to return to menu", 0,
50.0f, (HI_SCORE_LOWER_BOUNDRY - 0.0f) / 2.0f,
80.0f, HI_SCORE_LOWER_BOUNDRY,
0.8f, 0.8f, 0.8f, 1.0f, txf);
fbnTxfGLStateUnset();
glutSwapBuffers();
glutPostRedisplay();
}
void reshape(int w, int h){
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (float)w / (float)h, 0.01, 999.9);
glMatrixMode(GL_MODELVIEW);
}
void idle_shared(void){
joy();
}
void timer_classic(int value){
int i;
if(game.state == GAME_CLASSIC){
movesnake(0);
glutTimerFunc((int)(game.timeslice / 1000), timer_classic, 0);
}
}
void timer_vs(int value){
int i;
if(game.state == GAME_VS){
movesnake(0);
movesnake(1);
glutTimerFunc((int)(game.timeslice / 1000), timer_vs, 0);
}
}
void timer_quest_level1(int value){
int i;
if(game.state == GAME_QUEST_LEVEL1){
movesnake(0);
glutTimerFunc((int)(game.timeslice / 1000), timer_quest_level1, 0);
}
}
void timer_quest_level2(int value){
int i;
if(game.state == GAME_QUEST_LEVEL2){
movesnake(0);
glutTimerFunc((int)(game.timeslice / 1000), timer_quest_level2, 0);
}
}
void key_shared(unsigned char k, int x, int y){
switch (k) {
case ESCAPEKEY:
SetState(OFF);
break;
case TRANSLATE_CAMERA_FORWARD:
camera.eyez -= 0.1f * SCREEN_DEPTH;
txt_camera.eyez -= 0.1f * SCREEN_DEPTH;
break;
case TRANSLATE_CAMERA_BACK:
camera.eyez += 0.1f * SCREEN_DEPTH;
txt_camera.eyez += 0.1f * SCREEN_DEPTH;
break;
case TRANSLATE_CAMERA_LEFT:
camera.eyex -= 0.1f * SCREEN_WIDTH;
txt_camera.eyex -= 0.1f * SCREEN_WIDTH;
break;
case TRANSLATE_CAMERA_RIGHT:
camera.eyex += 0.1f * SCREEN_WIDTH;
txt_camera.eyex += 0.1f * SCREEN_WIDTH;
break;
case TRANSLATE_CAMERA_DOWN:
camera.eyey -= 0.1f * SCREEN_HEIGHT;
txt_camera.eyey -= 0.1f * SCREEN_HEIGHT;
break;
case TRANSLATE_CAMERA_UP:
camera.eyey += 0.1f * SCREEN_HEIGHT;
txt_camera.eyey += 0.1f * SCREEN_HEIGHT;
break;
case DEBUG_DUMP_MATRIX_KEY:
debug_dump_matrix();
break;
default:
break;
}
return;
}
void key_menu(unsigned char k, int x, int y){
key_shared(k, x, y);
switch (k) {
case P1_UPKEY:
game.mainmenu--;
if(game.mainmenu == -1)
game.mainmenu = MAINMENUSTATES - 1;
break;
case P1_DOWNKEY:
game.mainmenu++;
if(game.mainmenu == MAINMENUSTATES)
game.mainmenu = 0;
break;
case ENTERKEY:
switch(game.mainmenu){
case MENU_INSTRUCTIONS:
SetState(INSTRUCTIONS);
break;
case MENU_CLASSIC:
game.options.num_snakes = 1;
StartNewGameClassic();
break;
case MENU_VS:
game.options.num_snakes = 2;
StartNewGameVS();
break;
case MENU_QUEST:
game.options.num_snakes = 1;
StartNewGameQuest();
break;
case MENU_HI_SCORES:
SetState(HI_SCORES);
break;
case MENU_QUIT:
SetState(OFF);
break;
default:
debug("Error unknown game.mainmenu state\n");
break;
}
}
}
void key_game_shared(unsigned char k, int x, int y){
struct timeval temptime;
long tempusecdiff;
key_shared(k, x, y);
switch (k) {
case ENTERKEY:
SetState(GAME_PAUSED);
break;
case P1_UPKEY:
if(game.snake[0].current!=DOWN) game.snake[0].next_dir = UP;
break;
case P1_RIGHTKEY:
if(game.snake[0].current!=LEFT) game.snake[0].next_dir = RIGHT;
break;
case P1_LEFTKEY:
if(game.snake[0].current!=RIGHT) game.snake[0].next_dir = LEFT;
break;
case P1_DOWNKEY:
if(game.snake[0].current!=UP) game.snake[0].next_dir = DOWN;
break;
case P2_UPKEY:
if(game.snake[1].current!=DOWN) game.snake[1].next_dir = UP;
break;
case P2_RIGHTKEY:
if(game.snake[1].current!=LEFT) game.snake[1].next_dir = RIGHT;
break;
case P2_LEFTKEY:
if(game.snake[1].current!=RIGHT) game.snake[1].next_dir = LEFT;
break;
case P2_DOWNKEY:
if(game.snake[1].current!=UP) game.snake[1].next_dir = DOWN;
break;
default:
break;
}
return;
}
void key_game_paused(unsigned char k, int x, int y){
key_shared(k, x, y);
switch (k) {
case ENTERKEY:
SetState(game.previous_state);
break;
default:
break;
}
return;
}
void key_game_over(unsigned char k, int x, int y){
key_shared(k, x, y);
switch (k) {
case ENTERKEY:
// before going to next state wait for all sounds to finish playing
fbnSoundWait();
if(game.statedata.enter_initials.place){
SetState(ENTER_INITIALS);
} else {
SetState(MENU);
}
break;
default:
break;
}
}
void key_enter_initials(unsigned char k, int x, int y){
key_shared(k, x, y);
switch (k) {
case P1_UPKEY:
if(game.statedata.enter_initials.current_initial != 3)
game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] += 1;
if(game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] == 91) game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] = 48;
if(game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] == 58) game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] = 32;
if(game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] == 33) game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] = 65;
break;
case P1_RIGHTKEY:
game.statedata.enter_initials.current_initial = (game.statedata.enter_initials.current_initial + 1) % 4;
break;
case P1_LEFTKEY:
game.statedata.enter_initials.current_initial = (game.statedata.enter_initials.current_initial + 3) % 4;
break;
case P1_DOWNKEY:
if(game.statedata.enter_initials.current_initial != 3){
game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] -= 1;
if(game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] == 64) game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] = 32;
if(game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] == 31) game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] = 57;
if(game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] == 47) game.statedata.enter_initials.hiscores[game.statedata.enter_initials.place - 1].initials[game.statedata.enter_initials.current_initial] = 90;
}
break;
case ENTERKEY:
if(game.statedata.enter_initials.current_initial != 3){
game.statedata.enter_initials.current_initial += 1;
} else {
WriteGameHistory();
fbnSoundWait();
SetState(HI_SCORES);
}
break;
default:
break;
}
}
void key_hi_scores(unsigned char k, int x, int y){
key_shared(k, x, y);
switch (k) {
case ENTERKEY:
SetState(MENU);
break;
default:
break;
}
}
void special(int k, int x, int y){
switch (k) {
case GLUT_KEY_UP:
key(P1_UPKEY, x, y);
break;
case GLUT_KEY_RIGHT:
key(P1_RIGHTKEY, x, y);
break;
case GLUT_KEY_LEFT:
key(P1_LEFTKEY, x, y);
break;
case GLUT_KEY_DOWN:
key(P1_DOWNKEY, x, y);
break;
default:
return;
}
}
void key(unsigned char k, int x, int y){
switch (game.state) {
case MENU:
key_menu(k, x, y);
break;
case GAME_CLASSIC:
case GAME_VS:
case GAME_QUEST_LEVEL1:
case GAME_QUEST_LEVEL2:
key_game_shared(k, x, y);
break;
case GAME_PAUSED:
key_game_paused(k, x, y);
break;
case GAME_OVER:
key_game_over(k, x, y);
break;
case ENTER_INITIALS:
key_enter_initials(k, x, y);
break;
case HI_SCORES:
key_hi_scores(k, x, y);
break;
default:
return;
}
}
void movesnake(int snake_id){
int i,j,k;
int num_free;
SNAKE.current = SNAKE.next_dir;
if(SNAKE.belly){
SNAKE.belly--;
SNAKE.length++;
} else {
HideTail(snake_id);
}
switch(SNAKE.current){
case LEFT:
if(SNAKE.head.x <= 0)
{movesnake_gameover(snake_id); return;}
if(game.matrix[SNAKE.head.x - 1][SNAKE.head.y].snake != 0)
{movesnake_gameover(snake_id); return;}
game.matrix[SNAKE.head.x][SNAKE.head.y].next.x = SNAKE.head.x - 1;
game.matrix[SNAKE.head.x][SNAKE.head.y].next.y = SNAKE.head.y;
SNAKE.head.x = SNAKE.head.x - 1;
break;
case RIGHT:
if(SNAKE.head.x >= (game.game_width - 1))
{movesnake_gameover(snake_id); return;}
if(game.matrix[SNAKE.head.x + 1][SNAKE.head.y].snake != 0)
{movesnake_gameover(snake_id); return;}
game.matrix[SNAKE.head.x][SNAKE.head.y].next.x = SNAKE.head.x + 1;
game.matrix[SNAKE.head.x][SNAKE.head.y].next.y = SNAKE.head.y;
SNAKE.head.x = SNAKE.head.x + 1;
break;
case UP:
if(SNAKE.head.y >= (game.game_height - 1))
{movesnake_gameover(snake_id); return;}
if(game.matrix[SNAKE.head.x][SNAKE.head.y + 1].snake != 0)
{movesnake_gameover(snake_id); return;}
game.matrix[SNAKE.head.x][SNAKE.head.y].next.x = SNAKE.head.x;
game.matrix[SNAKE.head.x][SNAKE.head.y].next.y = SNAKE.head.y + 1;
SNAKE.head.y = SNAKE.head.y + 1;
break;
case DOWN:
if(SNAKE.head.y <= 0) {movesnake_gameover(snake_id); return;}
if(game.matrix[SNAKE.head.x][SNAKE.head.y - 1].snake != 0)
{movesnake_gameover(snake_id); return;}
game.matrix[SNAKE.head.x][SNAKE.head.y].next.x = SNAKE.head.x;
game.matrix[SNAKE.head.x][SNAKE.head.y].next.y = SNAKE.head.y - 1;
SNAKE.head.y = SNAKE.head.y - 1;
break;
default:
exit(1);
}
ShrinkTail(snake_id);
game.matrix[SNAKE.head.x][SNAKE.head.y].snake = snake_id + 1;
if((SNAKE.head.x == game.food.x)&&(SNAKE.head.y == game.food.y)){
fbnSoundPlay(SOUNDSET_MUNCH_FOOD, &soundset);
SNAKE.belly = SNAKE.belly + game.food_amount;
num_free =
(game.game_width * game.game_height) - SNAKE.length;
if(!num_free){SetState(GAME_OVER); return;}
i = (int)(((float)rand()/(float)RAND_MAX) * num_free);
j = 0;
k = 0;
while(j<=i){
if(game.matrix[k % game.game_width][k / game.game_width].snake == 0) j++;
if(j<=i) k++;
}
game.food.x = k % game.game_width;
game.food.y = k / game.game_width;
}
gettimeofday(&(game.time_of_last_gametick), NULL);
}
void movesnake_gameover(int snake_id){
UnhideTail(snake_id);
switch(game.state){
case GAME_CLASSIC:
case GAME_VS:
case GAME_QUEST_LEVEL2:
SetState(GAME_OVER);
break;
case GAME_QUEST_LEVEL1:
StartQuestLevel2();
break;
default:
debug("movesnake_gameover called from unhandled gamestate %d.\n",
game.state);
break;
}
}
void HideTail(int snake_id){
SNAKE.tail_hidden = 1;
game.matrix[SNAKE.tail.x][SNAKE.tail.y].snake = 0;
}
void ShrinkTail(int snake_id){
Position temptail;
temptail.x = SNAKE.tail.x;
temptail.y = SNAKE.tail.y;
if(game.matrix[temptail.x][temptail.y].snake != 0){
return;
} else {
game.matrix[temptail.x][temptail.y].snake = 0;
}
SNAKE.last_tail.x = temptail.x;
SNAKE.last_tail.y = temptail.y;
SNAKE.tail.x = game.matrix[temptail.x][temptail.y].next.x;
SNAKE.tail.y = game.matrix[temptail.x][temptail.y].next.y;
game.matrix[temptail.x][temptail.y].next.x = -1;
game.matrix[temptail.x][temptail.y].next.y = -1;
}
void UnhideTail(int snake_id){
SNAKE.tail_hidden = 0;
game.matrix[SNAKE.tail.x][SNAKE.tail.y].snake = snake_id;
}
int InsertNewHiScore(void){
int newplace = 1;
int done = 0;
int i;
while(!done){
if(game.statedata.enter_initials.score >=
game.statedata.enter_initials.hiscores[newplace - 1].score){
done = 1;
} else {
newplace++;
}
}
for(i=(NUM_HI_SCORES - 1);i>=newplace;i--)
game.statedata.enter_initials.hiscores[i] =
game.statedata.enter_initials.hiscores[i - 1];
game.statedata.enter_initials.hiscores[newplace - 1].score =
game.statedata.enter_initials.score;
game.statedata.enter_initials.hiscores[newplace - 1].initials[0] = 'A';
game.statedata.enter_initials.hiscores[newplace - 1].initials[1] = 'A';
game.statedata.enter_initials.hiscores[newplace - 1].initials[2] = 'A';
return(newplace);
}
void StartNewGameClassic(void){
int i,j;
game.game_width = game.options.classic.game_width;
game.game_height = game.options.classic.game_height;
InitLevel();
game.statedata.enter_initials.place = 0;
game.timeslice = game.options.classic.timeslice;
game.food_amount = game.options.classic.food_amount;
game.food.x = game.game_width / 2;
game.food.y = game.game_height / 2;
game.snake[0].current = RIGHT;
game.snake[0].next_dir = RIGHT;
game.snake[0].head.x = 1;
game.snake[0].head.y = 0;
game.snake[0].last_tail.x = 0;
game.snake[0].last_tail.y = 0;
game.snake[0].tail.x = game.snake[0].head.x;
game.snake[0].tail.y = game.snake[0].head.y;
game.snake[0].length = 1;
game.snake[0].belly = 0;
game.snake[0].active = 1;
game.snake[0].tail_hidden = 0;
game.matrix[game.snake[0].head.x][game.snake[0].head.y].snake = 1;
for(j=1;j<game.options.classic.init_snake_length;j++){
game.snake[0].belly = 1;
movesnake(0);
}
if(game.options.use_sound) fbnSoundPlay(SOUNDSET_NEW_GAME, &soundset);
SetState(GAME_CLASSIC);
}
void StartNewGameVS(void){
int i,j;
game.game_width = game.options.vs.game_width;
game.game_height = game.options.vs.game_height;
InitLevel();
game.statedata.enter_initials.place = 0;
game.timeslice = game.options.vs.timeslice;
game.food_amount = game.options.vs.food_amount;
game.food.x = game.game_width / 2;
game.food.y = game.game_height / 2;
game.snake[0].current = RIGHT;
game.snake[0].next_dir = RIGHT;
game.snake[0].head.x = 1;
game.snake[0].head.y = 0;
game.snake[0].last_tail.x = 0;
game.snake[0].last_tail.y = 0;
game.snake[1].current = LEFT;
game.snake[1].next_dir = LEFT;
game.snake[1].head.x = game.game_width - 2;
game.snake[1].head.y = game.game_height - 1;
game.snake[1].last_tail.x = game.game_width - 1;
game.snake[1].last_tail.y = game.game_height - 1;
game.snake[0].tail.x = game.snake[0].head.x;
game.snake[0].tail.y = game.snake[0].head.y;
game.snake[0].length = 1;
game.snake[0].belly = 0;
game.snake[0].active = 1;
game.snake[0].tail_hidden = 0;
game.matrix[game.snake[0].head.x][game.snake[0].head.y].snake = 1;
game.snake[1].tail.x = game.snake[1].head.x;
game.snake[1].tail.y = game.snake[1].head.y;
game.snake[1].length = 1;
game.snake[1].belly = 0;
game.snake[1].active = 1;
game.snake[1].tail_hidden = 0;
game.matrix[game.snake[1].head.x][game.snake[1].head.y].snake = 2;
for(j=1;j<game.options.vs.init_snake_length;j++){
game.snake[0].belly = 1;
movesnake(0);
game.snake[1].belly = 1;
movesnake(1);
}
if(game.options.use_sound) fbnSoundPlay(SOUNDSET_NEW_GAME, &soundset);
SetState(GAME_VS);
}
void StartNewGameQuest(void){
StartQuestLevel1();
}
void StartQuestLevel1(void){
int i,j;
game.food_amount = game.options.quest.level2.food;
game.game_width = game.options.quest.level1.width;
game.game_height = game.options.quest.level1.height;
InitLevel();
game.statedata.quest.score = 0;
game.statedata.enter_initials.place = 0;
game.timeslice = game.options.quest.timeslice / 2;
game.food.x = game.game_width / 2;
game.food.y = game.game_height / 2;
game.snake[0].current = RIGHT;
game.snake[0].next_dir = RIGHT;
game.snake[0].head.x = 1;
game.snake[0].head.y = 0;
game.snake[0].last_tail.x = 0;
game.snake[0].last_tail.y = 0;
game.snake[0].tail.x = game.snake[0].head.x;
game.snake[0].tail.y = game.snake[0].head.y;
game.snake[0].length = 1;
game.snake[0].belly = 0;
game.snake[0].active = 1;
game.snake[0].tail_hidden = 0;
game.matrix[game.snake[0].head.x][game.snake[0].head.y].snake = 1;
for(j=1;j<game.options.quest.level1.length;j++){
game.snake[0].belly = 1;
movesnake(0);
}
if(game.options.use_sound) fbnSoundPlay(SOUNDSET_NEW_GAME, &soundset);
SetState(GAME_QUEST_LEVEL1);
}
void StartQuestLevel2(void){
int i,j;
game.food_amount = game.options.quest.level2.food;
game.game_width = game.options.quest.level2.width;
game.game_height = game.options.quest.level2.height;
InitLevel();
game.statedata.quest.score = 0;
game.statedata.enter_initials.place = 0;
game.timeslice = game.options.quest.timeslice / 2;
game.food.x = game.game_width / 2;
game.food.y = game.game_height / 2;
game.snake[0].current = RIGHT;
game.snake[0].next_dir = RIGHT;
game.snake[0].head.x = 1;
game.snake[0].head.y = 0;
game.snake[0].last_tail.x = 0;
game.snake[0].last_tail.y = 0;
game.snake[0].tail.x = game.snake[0].head.x;
game.snake[0].tail.y = game.snake[0].head.y;
game.snake[0].length = 1;
game.snake[0].belly = 0;
game.snake[0].active = 1;
game.snake[0].tail_hidden = 0;
game.matrix[game.snake[0].head.x][game.snake[0].head.y].snake = 1;
for(j=1;j<game.options.quest.level2.length;j++){
game.snake[0].belly = 1;
movesnake(0);
}
SetState(GAME_QUEST_LEVEL2);
}
void InitLevel(void){
int i,j;
for(i=0;i<MAX_SNAKES;i++){
game.snake[i].active = 0;
}
game.frames_rendered = 0;
if(game.game_width > game.game_height){
game.screen_unit_length = SCREEN_WIDTH / (game.game_width + 4);
} else {
game.screen_unit_length = SCREEN_HEIGHT / (game.game_height + 4);
}
if(game.game_width > game.game_height){
game.render_width = SCREEN_WIDTH;
game.render_height = SCREEN_HEIGHT *
(((float)game.game_height + 4.0f) /
((float)game.game_width + 4.0f));
} else {
game.render_height = SCREEN_HEIGHT;
game.render_width = SCREEN_WIDTH *
(((float)game.game_width + 4.0f) /
((float)game.game_height + 4.0f));
}
game.time_of_last_gametick.tv_sec = 0;
game.time_of_last_gametick.tv_usec = 0;
game.time_of_last_render.tv_sec = 0;
game.time_of_last_render.tv_usec = 0;
for(i=0;i<game.game_width;i++){
for(j=0;j<game.game_height;j++){
game.matrix[i][j].snake = 0;
game.matrix[i][j].next.x = -1;
game.matrix[i][j].next.y = -1;
}
}
}
void Usage(void){
fprintf(stderr,
"Usage: snake3d [OPTION]\n"
"-w NUM = set initial classic game width to NUM\n"
"-h NUM = set initial classic game height to NUM\n"
"-l NUM = set initial classic snake length to NUM (must be >= 2)\n"
"-t NUM = set classic timeslice to NUM (in usec)\n"
"-s = disable sound\n"
"-f = fullscreen\n"
"-r = show framerate and other statistics\n"
);
exit(1);
}
void ReadGameHistory(void){
FILE *f;
int i;
f = fopen( "/tmp/snake3d_data", "r" );
if(f){
fread((void*)&history, sizeof(GameHistory), 1, f);
// HACK HACK HACK, need to check basic integrity of datafile
// CheckGameHistory();
fclose(f);
} else {
// initialize history
for(i=0; i<NUM_HI_SCORES; i++){
history.classic_hiscores[i].score = DEFAULT_CLASSIC_HI_SCORE - i;
history.classic_hiscores[i].initials[0] = 'f';
history.classic_hiscores[i].initials[1] = 'b';
history.classic_hiscores[i].initials[2] = 'n';
history.quest_hiscores[i].score = DEFAULT_QUEST_HI_SCORE - i;
history.quest_hiscores[i].initials[0] = 'f';
history.quest_hiscores[i].initials[1] = 'b';
history.quest_hiscores[i].initials[2] = 'n';
}
// write history to a file
WriteGameHistory();
}
}
void WriteGameHistory(void){
FILE *f;
f = fopen( "/tmp/snake3d_data", "w" );
if(f){
fwrite((void*)&history, sizeof(GameHistory), 1, f);
fclose(f);
} else {
debug("failed to write game data, please check the permissions of /tmp/snake3d_data\n");
}
}
void debug_dump_matrix(void){
int i, j;
for(i=0;i<game.game_height;i++){
for(j=0;j<game.game_width;j++){
debug("%d ", game.matrix[j][game.game_height - 1 - i].snake);
}
debug("\n");
}
}
void JoystickInit(void){
int version = 0x000800;
char name[128] = "Unknown";
unsigned char axes = 2;
unsigned char buttons = 2;
int i;
for(i=0;i<10;i++) jstate.j1_button_buffer[i] = 0;
for(i=0;i<10;i++) jstate.j2_button_buffer[i] = 0;
if ((jstate.j1_fd = open("/dev/js0", O_RDONLY)) < 0)
output("Snake3D: 1st joystick not found (/dev/js0), Player 1 joystick not being used\n");
if ((jstate.j2_fd = open("/dev/js1", O_RDONLY)) < 0)
output("Snake3D: 2nd joystick not found (/dev/js1), Player 2 joystick not being used\n");
ioctl(jstate.j1_fd, JSIOCGVERSION, &version);
ioctl(jstate.j1_fd, JSIOCGAXES, &axes);
ioctl(jstate.j1_fd, JSIOCGBUTTONS, &buttons);
ioctl(jstate.j1_fd, JSIOCGNAME(128), name);
ioctl(jstate.j2_fd, JSIOCGVERSION, &version);
ioctl(jstate.j2_fd, JSIOCGAXES, &axes);
ioctl(jstate.j2_fd, JSIOCGBUTTONS, &buttons);
ioctl(jstate.j2_fd, JSIOCGNAME(128), name);
}
void JoystickFini(void){
if(jstate.j1_fd >= 0){
if (close(jstate.j1_fd) < 0) {
perror("snake3d");
exit(1);
}
}
if(jstate.j2_fd >= 0){
if (close(jstate.j2_fd) < 0) {
perror("snake3d");
exit(1);
}
}
}
void joy(void){
struct js_event js;
if(jstate.j1_fd >= 0){
fcntl(jstate.j1_fd, F_SETFL, O_NONBLOCK);
while (read(jstate.j1_fd, &js, sizeof(struct js_event)) ==
sizeof(struct js_event)) {
switch(js.type & ~JS_EVENT_INIT) {
case JS_EVENT_BUTTON:
jstate.j1_button[js.number] = js.value;
if(js.value){
switch(js.number){
default:
key(ENTERKEY, 0, 0);
break;
}
} else {
}
break;
case JS_EVENT_AXIS:
jstate.j1_axis[js.number] = js.value;
switch(js.number){
case 0:
if(js.value < 0) key(P1_LEFTKEY, 0, 0);
if(js.value > 0) key(P1_RIGHTKEY, 0, 0);
break;
case 1:
if(js.value < 0) key(P1_UPKEY, 0, 0);
if(js.value > 0) key(P1_DOWNKEY, 0, 0);
break;
default:
break;
}
break;
}
}
if (errno != EAGAIN) {
perror("snake3d: error reading");
exit (1);
}
}
if(jstate.j2_fd >= 0){
fcntl(jstate.j2_fd, F_SETFL, O_NONBLOCK);
while (read(jstate.j2_fd, &js, sizeof(struct js_event)) ==
sizeof(struct js_event)) {
switch(js.type & ~JS_EVENT_INIT) {
case JS_EVENT_BUTTON:
jstate.j2_button[js.number] = js.value;
if(js.value){
switch(js.number){
default:
key(ENTERKEY, 0, 0);
break;
}
} else {
}
break;
case JS_EVENT_AXIS:
jstate.j2_axis[js.number] = js.value;
switch(js.number){
case 0:
if(js.value < 0) key(P2_LEFTKEY, 0, 0);
if(js.value > 0) key(P2_RIGHTKEY, 0, 0);
break;
case 1:
if(js.value < 0) key(P2_UPKEY, 0, 0);
if(js.value > 0) key(P2_DOWNKEY, 0, 0);
break;
default:
break;
}
break;
}
}
if (errno != EAGAIN) {
perror("snake3d: error reading");
exit (1);
}
}
}