/* * Copyright (C) 1998 Janne Löf * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Zktor is a word that does not mean anything and is difficult to pronounce. */ /* I apologize for horrible coding. */ #include #include #include #include #include #include #include #include #include #include #ifndef M_PI #define M_PI 3.14 #endif #if 0 #define FULLSCREEN_MESA_3DFX /* uncomment this to get 3DFX acceleration */ #endif #ifdef FULLSCREEN_MESA_3DFX #include #endif typedef struct { int state; /* zero state means dead */ float timer; float pos_x,pos_y; float vel_x,vel_y; float dir; float radius; } Entity; /* game state */ GTimer *gtimer = NULL; double game_time; double game_tick; int draw_fast = 0; int wave_cnt; double wave_time; double vortex_time; int control_speed; int control_spin; int control_fire; int score = 0; int highscore = 0; Entity player; Entity enemy[10]; Entity vortex[10]; Entity p_bullet[20]; Entity e_bullet[20]; Entity particle[60]; GLuint fontbase = 0; float rnd() { return (1.0*rand()/(RAND_MAX+1.0)); } int collision(const Entity *a, const Entity *b) { if (a->state && b->state) { float dx = a->pos_x - b->pos_x; float dy = a->pos_y - b->pos_y; float r = a->radius + b->radius; if (dx*dx+dy*dy < r*r) return TRUE; } return FALSE; } void gCircle(float radius, int points) { float a,step = 360.0/points; for (a = 0; a < 360.0; a += step) { float dx = -sin(a*M_PI/180); float dy = cos(a*M_PI/180); glVertex2f(dx*radius,dy*radius); } } void game_init() { int i; if (!gtimer) gtimer = g_timer_new(); g_timer_reset(gtimer); game_time = g_timer_elapsed(gtimer, NULL); game_tick = 1.0 / 60; wave_cnt = 0; wave_time = game_time + 5; /* give 5 secs before start of waves */ vortex_time = game_time + 3; /* give 3 secs before 1st vortex */ control_speed = 0; control_spin = 0; control_fire = 0; score = 0; player.state = 1; player.timer = game_time; player.pos_x = 0; player.pos_y = 0; player.vel_x = 0; player.vel_y = 0; player.dir = 0; player.radius= 5; for (i=0; i 0.2 ) tick_now = 0.2; game_tick = (tick_now + 4*game_tick)/5; /* average */ game_time = time_now; /* is it time for next wave? */ if (player.state && wave_time <= game_time) { wave_time = game_time + 20; /* 20 second waves */ wave_cnt++; for (i=0; i 180) player.dir -= 360; while (player.dir <-180) player.dir += 360; /* unit direction vector */ dx = -sin(player.dir*(M_PI/180)); dy = cos(player.dir*(M_PI/180)); /* accelerate if speed control is pressed */ player.vel_x += 50 * control_speed * dx * game_tick; player.vel_y += 50 * control_speed * dy * game_tick; /* move */ player.pos_x += player.vel_x * game_tick; player.pos_y += player.vel_y * game_tick; /* collision to border */ if (player.pos_x < -100) { player.pos_x = -100; player.vel_x = 0; } if (player.pos_x > 100) { player.pos_x = 100; player.vel_x = 0; } if (player.pos_y < -100) { player.pos_y = -100; player.vel_y = 0; } if (player.pos_y > 100) { player.pos_y = 100; player.vel_y = 0; } /* if fire is pressed and 0.2 secs has elapsed since last bullet fired */ if (control_fire && (game_time - player.timer) > 0.2 ) { for (i=0; i 180) a-=360; while (a <-180) a+=360; /* turn towards player */ if (a < -3) { enemy[i].dir += 90 * game_tick; while (enemy[i].dir > 180) enemy[i].dir -= 360; } if (a > 3) { enemy[i].dir -= 90 * game_tick; while (enemy[i].dir <-180) enemy[i].dir += 360; } /* unit direction vector */ dx = -sin(enemy[i].dir*(M_PI/180)); dy = cos(enemy[i].dir*(M_PI/180)); /* accelerate if player is ahead */ if (fabs(a) < 10) { enemy[i].vel_x += 20*dx * game_tick; enemy[i].vel_y += 20*dy * game_tick; } /* move */ enemy[i].pos_x += enemy[i].vel_x * game_tick; enemy[i].pos_y += enemy[i].vel_y * game_tick; /* collision to border */ if (enemy[i].pos_x < -100) { enemy[i].pos_x = -100; enemy[i].vel_x = 0; } if (enemy[i].pos_x > 100) { enemy[i].pos_x = 100; enemy[i].vel_x = 0; } if (enemy[i].pos_y < -100) { enemy[i].pos_y = -100; enemy[i].vel_y = 0; } if (enemy[i].pos_y > 100) { enemy[i].pos_y = 100; enemy[i].vel_y = 0; } /* fire if player is alive and ahead */ if (player.state && (fabs(a) < 20) && (game_time - enemy[i].timer) > .6) { for (j=0; j 100) { vortex[i].pos_x = 200 - vortex[i].pos_x; vortex[i].vel_x = -vortex[i].vel_x; } if (vortex[i].pos_y < -100) { vortex[i].pos_y = -200 - vortex[i].pos_y; vortex[i].vel_y = -vortex[i].vel_y; } if (vortex[i].pos_y > 100) { vortex[i].pos_y = 200 - vortex[i].pos_y; vortex[i].vel_y = -vortex[i].vel_y; } /* shake player */ if (collision(&vortex[i], &player)) { player.vel_x += (rnd()*500 - 250) * game_tick; player.vel_y += (rnd()*500 - 250) * game_tick; player.dir += (rnd()*180 - 90) * game_tick; } /* shake enemy */ for (j=0; j 115 || p_bullet[i].pos_y < -115 || p_bullet[i].pos_y > 115) { p_bullet[i].state = 0; } /* collision to enemy. kills bullet and kills enemy */ /* and adds 100 to score */ for (j=0; j highscore) highscore = score; p_bullet[i].state = 0; enemy[j].state = 0; } } /* collision to vortex shakes bullet */ for (j=0; j 115 || e_bullet[i].pos_y < -115 || e_bullet[i].pos_y > 115) e_bullet[i].state = 0; /* collision to player kills bullet and kills player */ if (collision(&e_bullet[i], &player)) { e_bullet[i].state = 0; player.state = 0; } /* collision to vortex shakes bullet */ for (j=0; jwindow, FALSE, GDK_CURRENT_TIME) == 0) { if (gdk_pointer_grab(gl_area->window, FALSE, 0, NULL, NULL, GDK_CURRENT_TIME) == 0) { gtk_widget_grab_focus(gl_area); if (gtk_gl_area_make_current(GTK_GL_AREA(gl_area))) { if (XMesaSetFXmode((XMESA_FX_FULLSCREEN))) { fullscreenwidget = gl_area; return TRUE; } } gdk_pointer_ungrab(GDK_CURRENT_TIME); } gdk_keyboard_ungrab(GDK_CURRENT_TIME); } return FALSE; } if (fullscreenwidget == gl_area) { if (gtk_gl_area_make_current(GTK_GL_AREA(gl_area))) XMesaSetFXmode(XMESA_FX_WINDOW); gdk_keyboard_ungrab(GDK_CURRENT_TIME); gdk_pointer_ungrab(GDK_CURRENT_TIME); fullscreenwidget = NULL; return TRUE; } return FALSE; } #endif gint init(GtkWidget *widget) { /* OpenGL functions can be called only if makecurrent returns true */ if (gtk_gl_area_make_current(GTK_GL_AREA(widget))) { #if !defined(WIN32) GdkFont *font; #endif /* set viewport */ glViewport(0,0, widget->allocation.width, widget->allocation.height); #if !defined(WIN32) /* generate font display lists */ font = gdk_font_load("-adobe-helvetica-medium-r-normal--*-120-*-*-*-*-*-*"); if (font) { fontbase = glGenLists( 128 ); gdk_gl_use_gdk_font(font, 0, 128, fontbase); gdk_font_unref(font); } #endif } return TRUE; } /* When widget is exposed it's contents are redrawn. */ gint draw(GtkWidget *widget, GdkEventExpose *event) { /* Draw only last expose. */ if (event->count > 0) return TRUE; if (gtk_gl_area_make_current(GTK_GL_AREA(widget))) game_render(); /* Swap backbuffer to front */ gtk_gl_area_swapbuffers(GTK_GL_AREA(widget)); return TRUE; } /* When glarea widget size changes, viewport size is set to match the new size */ gint reshape(GtkWidget *widget, GdkEventConfigure *event) { /* OpenGL functions can be called only if make_current returns true */ if (gtk_gl_area_make_current(GTK_GL_AREA(widget))) { glViewport(0,0, widget->allocation.width, widget->allocation.height); } return TRUE; } gint key_press_event(GtkWidget *widget, GdkEventKey *event) { switch (event->keyval) { case GDK_Left: control_spin = 1; break; case GDK_Right: control_spin = -1; break; case GDK_Up: case GDK_space: control_fire = 1; break; case GDK_Down: control_speed = 1; break; case GDK_r: game_init(); break; case GDK_q: gtk_main_quit(); break; #ifdef FULLSCREEN_MESA_3DFX case GDK_f: switch_fullscreen(widget); break; #endif case GDK_d: draw_fast = (!draw_fast); break; } /* prevent the default handler from being run */ gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),"key_press_event"); return TRUE; } gint key_release_event(GtkWidget *widget, GdkEventKey *event) { switch (event->keyval) { case GDK_Left: if (control_spin == 1) control_spin = 0; break; case GDK_Right: if (control_spin == -1) control_spin = 0; break; case GDK_Up: case GDK_space: control_fire = 0; break; case GDK_Down: control_speed = 0; break; } /* prevent the default handler from being run */ gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),"key_release_event"); return TRUE; } gint animate(GtkWidget *glarea) { game_play(); gtk_widget_draw(GTK_WIDGET(glarea), NULL); return TRUE; } int main(int argc, char **argv) { GtkWidget *window,*vbox,*logo,*glarea; /* Attribute list for gtkglarea widget. Specifies a list of Boolean attributes and enum/integer attribute/value pairs. The last attribute must be GDK_GL_NONE. See glXChooseVisual manpage for further explanation. */ int attrlist[] = { GDK_GL_RGBA, GDK_GL_RED_SIZE,1, GDK_GL_GREEN_SIZE,1, GDK_GL_BLUE_SIZE,1, GDK_GL_DOUBLEBUFFER, GDK_GL_NONE }; #ifdef FULLSCREEN_MESA_3DFX setenv("MESA_GLX_FX", "", 1); setenv("FX_GLIDE_NO_SPLASH", "", 1); #endif /* initialize gtk */ gtk_init(&argc, &argv); /* Check if OpenGL (GLX extension) is supported. */ if (gdk_gl_query() == FALSE) { g_print("OpenGL not supported\n"); return 0; } /* Create new top level window. */ window = gtk_window_new( GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Zktor"); /* Quit form main if got delete event */ gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(gtk_main_quit), NULL); /* You should always delete gtk_gl_area widgets before exit or else GLX contexts are left undeleted, this may cause problems (=core dump) in some systems. Destroy method of objects is not automatically called on exit. You need to manually enable this feature. Do gtk_quit_add_destroy() for all your top level windows unless you are certain that they get destroy signal by other means. */ gtk_quit_add_destroy(1, GTK_OBJECT(window)); vbox = GTK_WIDGET(gtk_vbox_new(FALSE, 0)); gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); logo = gtk_label_new("Zktor"); /* Create new OpenGL widget. */ glarea = GTK_WIDGET(gtk_gl_area_new(attrlist)); /* Events for widget must be set before X Window is created */ gtk_widget_set_events(GTK_WIDGET(glarea), GDK_EXPOSURE_MASK| GDK_KEY_PRESS_MASK| GDK_KEY_RELEASE_MASK); /* set minimum size */ /* gtk_widget_set_usize(GTK_WIDGET(glarea), 200,200); */ /* set default size */ gtk_gl_area_size(GTK_GL_AREA(glarea), 640,400); /* Connect signal handlers */ /* Redraw image when exposed. */ gtk_signal_connect(GTK_OBJECT(glarea), "expose_event", GTK_SIGNAL_FUNC(draw), NULL); /* When window is resized viewport needs to be resized also. */ gtk_signal_connect(GTK_OBJECT(glarea), "configure_event", GTK_SIGNAL_FUNC(reshape), NULL); /* Do initialization when widget has been realized. */ gtk_signal_connect(GTK_OBJECT(glarea), "realize", GTK_SIGNAL_FUNC(init), NULL); /* Capture keypress events */ gtk_signal_connect(GTK_OBJECT(glarea), "key_press_event", GTK_SIGNAL_FUNC(key_press_event), NULL); gtk_signal_connect(GTK_OBJECT(glarea), "key_release_event", GTK_SIGNAL_FUNC(key_release_event), NULL); /* construct widget hierarchy */ gtk_container_add(GTK_CONTAINER(window),GTK_WIDGET(vbox)); gtk_box_pack_start(GTK_BOX(vbox), logo, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), glarea, TRUE, TRUE, 0); /* show all widgets */ gtk_widget_show(GTK_WIDGET(glarea)); gtk_widget_show(GTK_WIDGET(logo)); gtk_widget_show(GTK_WIDGET(vbox)); gtk_widget_show(window); /* set focus to glarea widget */ GTK_WIDGET_SET_FLAGS(glarea, GTK_CAN_FOCUS); gtk_widget_grab_focus(GTK_WIDGET(glarea)); /* animating */ gtk_idle_add((GtkFunction)animate, glarea); game_init(); gtk_main(); return 0; }