Back to index

gcompris  8.2.2
maze.c
Go to the documentation of this file.
00001 /* gcompris - maze.c
00002  *
00003  * Copyright (C) 2002 Bastiaan Verhoef
00004  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU General Public License as published by
00007  *   the Free Software Foundation; either version 2 of the License, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details.
00014  *
00015  *   You should have received a copy of the GNU General Public License
00016  *   along with this program; if not, write to the Free Software
00017  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00020 #include "gcompris/gcompris.h"
00021 
00022 #define SOUNDLISTFILE PACKAGE
00023 /*-------------------*/
00024 #define NORTH 1
00025 #define WEST 2
00026 #define SOUTH 4
00027 #define EAST 8
00028 #define SET 16
00029 #define BAD 32
00030 #define WON 64
00031 #define MAX_BREEDTE 37
00032 #define MAX_HOOGTE 20
00033 #define BASE_X1 50
00034 #define BASE_Y1 50
00035 #define BASE_X2 800
00036 #define BASE_Y2 500
00037 
00038 #define LINE_COLOR "white"
00039 
00040 /* array of the board */
00041 static int Maze[MAX_BREEDTE][MAX_HOOGTE];
00042 static int position[MAX_BREEDTE*MAX_HOOGTE][2];
00043 
00044 static int ind=0;
00045 static int begin;
00046 static int end;
00047 static int breedte=10;
00048 static int hoogte=20;
00049 static int cellsize=20;
00050 static int buffer=4;
00051 static int board_border_x=20;
00052 static int board_border_y=3;
00053 static int thickness=2;
00054 static gboolean run_fast=FALSE;
00055 
00056 static gboolean modeIs2D=TRUE;
00057 static gboolean modeRelative=FALSE;
00058 static gboolean modeIsInvisible=FALSE;
00059 /*-----------------------*/
00060 
00061 static GcomprisBoard *gcomprisBoard = NULL;
00062 static gboolean board_paused = TRUE;
00063 
00064 static void start_board (GcomprisBoard *agcomprisBoard);
00065 static void pause_board (gboolean pause);
00066 static void end_board (void);
00067 static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
00068 static int gamewon;
00069 
00070 static void process_ok(void);
00071 static void game_won(void);
00072 static void repeat(void);
00073 
00074 /* ================================================================ */
00075 static GnomeCanvasGroup *boardRootItem = NULL;
00076 static GnomeCanvasGroup *mazegroup     = NULL;
00077 static GnomeCanvasGroup *wallgroup     = NULL;
00078 
00079 static GnomeCanvasItem *warning_item   = NULL;
00080 static GnomeCanvasItem *tuxitem        = NULL;
00081 
00082 static GnomeCanvasItem *maze_create_item(GnomeCanvasGroup *parent);
00083 static void maze_destroy_all_items(void);
00084 static void maze_next_level(void);
00085 static void set_level (guint level);
00086 static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str);
00087 /*--------------------*/
00088 static void draw_a_rect(GnomeCanvasGroup *group, int x1, int y1, int x2, int y2, char *color);
00089 static void draw_a_line(GnomeCanvasGroup *group, int x1, int y1, int x2, int y2, guint32 color);
00090 static GnomeCanvasItem *draw_image(GnomeCanvasGroup *group, int x,int y, GdkPixbuf *pixmap);
00091 static void move_image(GnomeCanvasGroup *group, int x,int y, GnomeCanvasItem *item);
00092 static void draw_rect(GnomeCanvasGroup *group, int x,int y,char *color);
00093 static void draw_combined_rect(GnomeCanvasGroup *group, int x1, int y1, int x2,int y2, char *color);
00094 static void initMaze(void);
00095 static int check(int x,int y);
00096 static int* isPossible(int x, int y);
00097 static void generateMaze(int x, int y);
00098 static void removeSet(void);
00099 static void draw_background(GnomeCanvasGroup *rootItem);
00100 static void setlevelproperties(void);
00101 static gint tux_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
00102 static gint target_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
00103 static void update_tux(gint direction);
00104 
00105 /*---------- 3D stuff ------------*/
00106 static GnomeCanvasGroup *threedgroup = NULL;
00107 static gint viewing_direction=EAST;
00108 static gboolean threeDactive=FALSE;
00109 static gboolean mapActive=FALSE;
00110 typedef float eyepos_t;
00111 
00112 // x,y e ]-1 ... 1[
00113 // z e ]-1 ... 1 ... oo[
00114 static eyepos_t eye_pos_x=0, eye_pos_y=0, eye_pos_z=0;
00115 
00116 static void threeDdisplay();
00117 static void twoDdisplay();
00118 static void draw3D();
00119 static gint key_press_3D(guint keyval, gchar *commit_str, gchar *preedit_str);
00120 static gint key_press_2D_relative(guint keyval, gchar *commit_str, gchar *preedit_str);
00121 /*----------------------*/
00122 
00123 /* Description of this plugin */
00124 static BoardPlugin menu_bp =
00125   {
00126     NULL,
00127     NULL,
00128     N_("Maze"),
00129     N_("Find your way out of the maze"),
00130     "Bastiaan Verhoef <b.f.verhoef@student.utwente.nl>",
00131     NULL,
00132     NULL,
00133     NULL,
00134     NULL,
00135     start_board,
00136     pause_board,
00137     end_board,
00138     is_our_board,
00139     key_press,
00140     process_ok,
00141     set_level,//set_level,
00142     NULL,
00143     repeat,
00144     NULL,
00145     NULL
00146   };
00147 
00148 /* =====================================================================
00149  *
00150  * =====================================================================*/
00151 GET_BPLUGIN_INFO(maze)
00152 
00153 /* =====================================================================
00154  * in : boolean TRUE = PAUSE : FALSE = CONTINUE
00155  * =====================================================================*/
00156 static void pause_board (gboolean pause)
00157 {
00158   if(gcomprisBoard==NULL)
00159     return;
00160 
00161   if(gamewon == TRUE && pause == FALSE) /* the game is won */
00162     game_won();
00163 
00164   board_paused = pause;
00165 }
00166 
00167 static void set_level (guint level) {
00168   if(gcomprisBoard!=NULL) {
00169     gcomprisBoard->level=level;
00170     maze_next_level();
00171   }
00172 }
00173 
00174 /* =====================================================================
00175  *
00176  * =====================================================================*/
00177 static void start_board (GcomprisBoard *agcomprisBoard) {
00178 
00179   GdkPixbuf *pixmap = NULL;
00180 
00181   if(agcomprisBoard!=NULL) {
00182     gchar *img;
00183 
00184     gcomprisBoard=agcomprisBoard;
00185 
00186     /* disable im_context */
00187     gcomprisBoard->disable_im_context = TRUE;
00188 
00189     img = gc_skin_image_get("gcompris-bg.jpg");
00190     gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
00191                          img);
00192     g_free(img);
00193     gcomprisBoard->level=1;
00194     gcomprisBoard->maxlevel=9;
00195 
00196     /* The mode defines if we run 2D or 3D */
00197     /* Default mode is 2D */
00198     modeRelative=FALSE;
00199     modeIsInvisible=FALSE;
00200     if(!gcomprisBoard->mode)
00201       modeIs2D=TRUE;
00202     else if(g_strncasecmp(gcomprisBoard->mode, "2DR", 3)==0) {
00203       /* 2D Relative */
00204       modeIs2D=TRUE;
00205       modeRelative=TRUE;
00206     } else if(g_strncasecmp(gcomprisBoard->mode, "2DI", 3)==0) {
00207       modeIs2D=TRUE;
00208       modeIsInvisible=TRUE;
00209     } else if(g_strncasecmp(gcomprisBoard->mode, "2D", 2)==0) {
00210       modeIs2D=TRUE;
00211     } else if(g_strncasecmp(gcomprisBoard->mode, "3D", 2)==0) {
00212       modeIs2D=FALSE;
00213     }
00214 
00215     if(!modeIs2D || modeIsInvisible) {
00216       pixmap = gc_skin_pixmap_load("maze-2d-bubble.png");
00217       if(pixmap) {
00218        gc_bar_set_repeat_icon(pixmap);
00219        gdk_pixbuf_unref(pixmap);
00220        gc_bar_set(GC_BAR_LEVEL|GC_BAR_REPEAT_ICON);
00221       } else {
00222        gc_bar_set(GC_BAR_LEVEL|GC_BAR_REPEAT);
00223       }
00224     } else {
00225       /* 2D Regular mode */
00226       gc_bar_set(GC_BAR_LEVEL);
00227     }
00228 
00229     gamewon = FALSE;
00230 
00231     maze_next_level();
00232     pause_board(FALSE);
00233   }
00234 }
00235 
00236 /* =====================================================================
00237  *
00238  * =====================================================================*/
00239 static void end_board () {
00240 
00241   if(gcomprisBoard!=NULL){
00242     pause_board(TRUE);
00243     maze_destroy_all_items();
00244   }
00245   gcomprisBoard = NULL;
00246 }
00247 
00248 /* =====================================================================
00249  *
00250  * =====================================================================*/
00251 static gboolean is_our_board (GcomprisBoard *gcomprisBoard) {
00252   if (gcomprisBoard) {
00253     if(g_strcasecmp(gcomprisBoard->type, "maze")==0) {
00254       /* Set the plugin entry */
00255       gcomprisBoard->plugin=&menu_bp;
00256       return TRUE;
00257     }
00258   }
00259   return FALSE;
00260 }
00261 /* =====================================================================
00262  * set initial values for the next level
00263  * =====================================================================*/
00264 static void maze_next_level() {
00265   GdkPixbuf *pixmap = NULL;
00266 
00267   maze_destroy_all_items();
00268   gc_bar_set_level(gcomprisBoard);
00269   setlevelproperties();
00270 
00271   mapActive = FALSE;
00272 
00273   gamewon = FALSE;
00274   initMaze();
00275   generateMaze((rand()%breedte),(rand()%hoogte));
00276   removeSet();
00277   /* Try the next level */
00278   maze_create_item(gnome_canvas_root(gcomprisBoard->canvas));
00279   draw_background(wallgroup);
00280 
00281   if(modeIsInvisible) {
00282     gnome_canvas_item_hide(GNOME_CANVAS_ITEM(wallgroup));
00283   }
00284 
00285   /* make a new group for the items */
00286   begin=rand()%hoogte;
00287   end=rand()%hoogte;
00288 
00289   /* Draw the tux */
00290   pixmap = gc_pixmap_load("gcompris/misc/tux_top_east.png");
00291   if(pixmap)
00292     {
00293       tuxitem = draw_image(mazegroup,0,begin,pixmap);
00294       gdk_pixbuf_unref(pixmap);
00295       gtk_signal_connect(GTK_OBJECT(tuxitem), "event", (GtkSignalFunc) tux_event, NULL);
00296     }
00297 
00298   /* Draw the target */
00299   pixmap = gc_pixmap_load("gcompris/misc/door.png");
00300   if(pixmap)
00301     {
00302       GnomeCanvasItem *targetitem = draw_image(mazegroup,breedte-1,end,pixmap);
00303       gdk_pixbuf_unref(pixmap);
00304       gtk_signal_connect(GTK_OBJECT(targetitem), "event", (GtkSignalFunc) target_event, NULL);
00305     }
00306 
00307   position[ind][0]=0;
00308   position[ind][1]=begin;
00309   Maze[0][begin]=Maze[0][begin]+SET;
00310   viewing_direction=EAST;
00311   threeDactive=FALSE;
00312 
00313   if (gcomprisBoard->level==1) run_fast=FALSE;
00314   if (gcomprisBoard->level==4) run_fast=TRUE;
00315 
00316   update_tux(viewing_direction);
00317 
00318   if(!modeIs2D)
00319     threeDdisplay();
00320 
00321 }
00322 /* ======================================= */
00323 static void setlevelproperties(){
00324   if (gcomprisBoard->level==1)
00325     {
00326       breedte=5;
00327       hoogte=4;
00328       cellsize=70;
00329       buffer=8;
00330 
00331       board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
00332       board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
00333     }
00334   else if (gcomprisBoard->level==2)
00335     {
00336 
00337       breedte=9;
00338       hoogte=6;
00339       cellsize=70;
00340       buffer=7;
00341       board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
00342       board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
00343     }
00344   else if (gcomprisBoard->level==3)
00345     {
00346       breedte=13;
00347       hoogte=8;
00348       cellsize=60;
00349       buffer=6;
00350       board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
00351       board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
00352     }
00353   else if (gcomprisBoard->level==4)
00354     {
00355       breedte=17;
00356       hoogte=10;
00357       cellsize=45;
00358       buffer=5;
00359       board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
00360       board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
00361     }
00362   else if (gcomprisBoard->level==5)
00363     {
00364       breedte=21;
00365       hoogte=12;
00366       cellsize=35;
00367       buffer=4;
00368       board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
00369       board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
00370     }
00371   else if (gcomprisBoard->level==6)
00372     {
00373       breedte=25;
00374       hoogte=14;
00375       cellsize=30;
00376       board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
00377       board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
00378     }
00379   else if (gcomprisBoard->level==7)
00380     {
00381       breedte=29;
00382       hoogte=16;
00383       cellsize=25;
00384       board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
00385       board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
00386     }
00387   else if (gcomprisBoard->level==8)
00388     {
00389       breedte=33;
00390       hoogte=18;
00391       cellsize=23;
00392       board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
00393       board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
00394     }
00395   else if (gcomprisBoard->level==9)
00396     {
00397       breedte=37;
00398       hoogte=20;
00399       cellsize=20;
00400       board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
00401       board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
00402     }
00403 }
00404 
00405 /*
00406  * Repeat let the user get a help map in 3D mode
00407  *
00408  */
00409 static void repeat () {
00410   GdkPixbuf *pixmap = NULL;
00411 
00412   if(modeIsInvisible) {
00413     if(mapActive) {
00414       gnome_canvas_item_hide(GNOME_CANVAS_ITEM(wallgroup));
00415       /* Hide the warning */
00416       gnome_canvas_item_hide(warning_item);
00417       mapActive = FALSE;
00418     } else {
00419       gnome_canvas_item_show(GNOME_CANVAS_ITEM(wallgroup));
00420       /* Display a warning that you can't move there */
00421       gnome_canvas_item_show(warning_item);
00422       mapActive = TRUE;
00423     }
00424   }
00425 
00426   if(modeIs2D)
00427     return;
00428 
00429   if(threeDactive) {
00430 
00431     pixmap = gc_skin_pixmap_load("maze-3d-bubble.png");
00432     if(pixmap) {
00433       gc_bar_set_repeat_icon(pixmap);
00434       gdk_pixbuf_unref(pixmap);
00435     }
00436     twoDdisplay();
00437     /* Display a warning that you can't move there */
00438     gnome_canvas_item_show(warning_item);
00439 
00440   } else {
00441 
00442     pixmap = gc_skin_pixmap_load("maze-2d-bubble.png");
00443     if(pixmap) {
00444       gc_bar_set_repeat_icon(pixmap);
00445       gdk_pixbuf_unref(pixmap);
00446     }
00447     gnome_canvas_item_hide(warning_item);
00448     threeDdisplay();
00449   }
00450 
00451 }
00452 
00453 /* =====================================================================
00454  * Destroy all the items
00455  * =====================================================================*/
00456 static void maze_destroy_all_items() {
00457   if(boardRootItem!=NULL)
00458     gtk_object_destroy (GTK_OBJECT(boardRootItem));
00459   if (threedgroup!=NULL)
00460     gtk_object_destroy(GTK_OBJECT(threedgroup));
00461   mazegroup = NULL;
00462   wallgroup = NULL;
00463   boardRootItem = NULL;
00464   threedgroup=NULL;
00465 }
00466 
00467 /* =====================================================================
00468  *
00469  * =====================================================================*/
00470 static GnomeCanvasItem *maze_create_item(GnomeCanvasGroup *parent) {
00471   gchar *message;
00472 
00473   boardRootItem = GNOME_CANVAS_GROUP(
00474                                  gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
00475                                                      gnome_canvas_group_get_type (),
00476                                                      "x", (double) 0,
00477                                                      "y", (double) 0,
00478                                                      NULL));
00479   mazegroup=GNOME_CANVAS_GROUP(gnome_canvas_item_new(boardRootItem,
00480                                                gnome_canvas_group_get_type(),
00481                                                "x",(double)breedte,
00482                                                "y",(double)hoogte,
00483                                                NULL));
00484 
00485   wallgroup=GNOME_CANVAS_GROUP(gnome_canvas_item_new(boardRootItem,
00486                                                gnome_canvas_group_get_type(),
00487                                                "x",(double) 0,
00488                                                "y",(double) 0,
00489                                                NULL));
00490 
00491   if(modeIsInvisible) {
00492     message = _("Look at your position, then switch back to invisible mode to continue your moves");
00493   } else {
00494     message = _("Look at your position, then switch back to 3D mode to continue your moves");
00495   }
00496 
00497   warning_item = gnome_canvas_item_new (boardRootItem,
00498                                    gnome_canvas_text_get_type (),
00499                                    "text", message,
00500                                    "font", gc_skin_font_board_big,
00501                                    "x", (double) BOARDWIDTH/2,
00502                                    "y", (double) BOARDHEIGHT-20,
00503                                    "anchor", GTK_ANCHOR_CENTER,
00504                                    "fill_color_rgba", gc_skin_color_content,
00505                                    NULL);
00506   gnome_canvas_item_hide(warning_item);
00507 
00508   return NULL;
00509 }
00510 /* =====================================================================
00511  *
00512  * =====================================================================*/
00513 static void game_won() {
00514   twoDdisplay();
00515   gc_sound_play_ogg ("sounds/bonus.ogg", NULL);
00516   /* Try the next level */
00517   gcomprisBoard->level++;
00518   if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
00519     gc_bonus_end_display(BOARD_FINISHED_RANDOM);
00520     return;
00521   }
00522   maze_next_level();
00523 }
00524 /* =====================================================================
00525  *
00526  * =====================================================================*/
00527 static void process_ok() {
00528 }
00529 
00530 static void
00531 draw_a_rect(GnomeCanvasGroup *group,
00532            int x1, int y1, int x2, int y2, char *color)
00533 {
00534   gnome_canvas_item_new(group,gnome_canvas_rect_get_type(),
00535                      "x1",(double)x1,
00536                      "y1",(double)y1,
00537                      "x2",(double)x2,
00538                      "y2",(double)y2,
00539                      "fill_color", color,
00540                      NULL);
00541 }
00542 
00543 static void
00544 draw_a_line(GnomeCanvasGroup *group,
00545            int x1, int y1, int x2, int y2, guint32 color)
00546 {
00547   GnomeCanvasPoints *points;
00548 
00549   points = gnome_canvas_points_new (2);
00550 
00551   points->coords[0] = x1;
00552   points->coords[1] = y1;
00553   points->coords[2] = x2;
00554   points->coords[3] = y2;
00555   gnome_canvas_item_new(group,
00556                      gnome_canvas_line_get_type(),
00557                      "points", points,
00558                      "fill_color_rgba", color,
00559                      "width_units", (double)thickness,
00560                      NULL);
00561 
00562   gnome_canvas_points_free(points);
00563 }
00564 
00565 static void draw_rect(GnomeCanvasGroup *group, int x,int y,char *color)
00566 {
00567   int x1,y1;
00568   y1=cellsize*(y)-hoogte + board_border_y;
00569   x1=cellsize*(x)-breedte + board_border_x;
00570   draw_a_rect(group,x1+buffer,y1+buffer ,x1+cellsize-buffer ,y1+cellsize-buffer ,color);
00571 }
00572 
00573 /*
00574  * Same as draw rect but for an image
00575  * Returns the created item
00576  */
00577 static GnomeCanvasItem *draw_image(GnomeCanvasGroup *group, int x,int y, GdkPixbuf *pixmap)
00578 {
00579   GnomeCanvasItem *item = NULL;
00580   int x1,y1;
00581 
00582   y1=cellsize*(y)-hoogte + board_border_y;
00583   x1=cellsize*(x)-breedte + board_border_x;
00584 
00585   item = gnome_canvas_item_new (group,
00586                             gnome_canvas_pixbuf_get_type (),
00587                             "pixbuf", pixmap,
00588                             "x",   (double)x1+buffer,
00589                             "y",   (double)y1+buffer,
00590                             "width",      (double)cellsize-buffer*2,
00591                             "height",(double)cellsize-buffer*2,
00592                             "width_set", TRUE,
00593                             "height_set", TRUE,
00594                             NULL);
00595 
00596   return(item);
00597 }
00598 
00599 /*
00600  * Same as draw rect but for an image
00601  */
00602 static void move_image(GnomeCanvasGroup *group, int x,int y, GnomeCanvasItem *item)
00603 {
00604   int x1,y1;
00605   y1=cellsize*(y)-hoogte + board_border_y;
00606   x1=cellsize*(x)-breedte + board_border_x;
00607 
00608   gnome_canvas_item_set (item,
00609                       "x",  (double)x1+buffer,
00610                       "y",  (double)y1+buffer,
00611                       NULL);
00612   gnome_canvas_item_raise_to_top(item);
00613 }
00614 
00615 static void draw_combined_rect(GnomeCanvasGroup *group, int x1,int y1,int x2,int y2,char *color)
00616 {
00617   int xx1,yy1,xx2,yy2;
00618   yy1=cellsize*(y1)-hoogte + board_border_y;
00619   xx1=cellsize*(x1)-breedte + board_border_x;
00620   yy2=cellsize*(y2)-hoogte + board_border_y;
00621   xx2=cellsize*(x2)-breedte + board_border_x;
00622   if (y1==y2 && x1<x2)
00623     {
00624       draw_a_rect(group,xx1+cellsize-buffer,yy1+buffer,xx2+buffer,yy2+cellsize-buffer,color);
00625     }
00626   else if (y1==y2 && x1>x2)
00627     {
00628       draw_a_rect(group,xx2+cellsize-buffer,yy2+buffer,xx1+buffer,yy1+cellsize-buffer,color);
00629     }
00630   else if (x1==x2 && y1<y2)
00631     {
00632       draw_a_rect(group,xx1+buffer,yy1+cellsize-buffer,xx2+cellsize-buffer,yy2+buffer,color);
00633     }
00634   else if (x1==x2 && y1>y2)
00635     {
00636       draw_a_rect(group,xx2+buffer,yy2+cellsize-buffer,xx1+cellsize-buffer,yy1+buffer,color);
00637     }
00638 
00639 }
00640 
00641 static void initMaze(void)
00642 {
00643   int x,y;
00644   for (x=0; x<breedte;x++)
00645     {
00646       for (y=0; y <hoogte; y++)
00647        {
00648          Maze[x][y]=15;
00649        }
00650     }
00651 }
00652 
00653 static int check(int x,int y)
00654 {
00655   if (Maze[x][y]&SET)
00656     return 1;
00657   else return 0;
00658 }
00659 
00660 static int* isPossible(int x, int y)
00661 {
00662   int wall=Maze[x][y];
00663   static int pos[5];
00664   wall&=~SET;
00665   pos[0]=0;
00666   if(x==0)
00667     {
00668       wall&=~WEST;
00669     }
00670   if (y==0)
00671     {
00672       wall&=~NORTH;
00673     }
00674   if(x==(breedte-1))
00675     {
00676       wall&=~EAST;
00677     }
00678   if (y==(hoogte-1))
00679     {
00680       wall&=~SOUTH;
00681     }
00682   if (wall&EAST)
00683     {
00684       if(check(x+1,y)==0)
00685        {
00686          pos[0]=pos[0]+1;
00687          pos[(pos[0])]=EAST;
00688        }
00689     }
00690   if (wall&SOUTH)
00691     {
00692       if (check(x,y+1)==0)
00693        {
00694          pos[0]=pos[0]+1;
00695          pos[(pos[0])]=SOUTH;
00696        }
00697     }
00698   if (wall&WEST)
00699     {
00700       if (check(x-1,y)==0)
00701        {
00702          pos[0]=pos[0]+1;
00703          pos[(pos[0])]=WEST;
00704        }
00705     }
00706   if (wall&NORTH)
00707     {
00708       if (check(x,y-1)==0)
00709        {
00710          pos[0]=pos[0]+1;
00711          pos[(pos[0])]=NORTH;
00712        }
00713     }
00714   return &pos[0];
00715 }
00716 
00717 static void generateMaze(int x, int y)
00718 {
00719   int *po;
00720   Maze[x][y]= Maze[x][y] + SET;
00721   po = isPossible(x,y);
00722   while (*po>0)
00723     {
00724       int nr = *po;
00725       int ran, in;
00726       in=(rand()%nr)+1;
00727       //printf("random: %d en %d mogelijkheden\n", in, *po);
00728       ran=*(po + in);
00729       if (nr>=1)
00730        switch (ran)
00731          {
00732          case EAST:
00733            Maze[x][y]&=~EAST;
00734            Maze[x+1][y]&=~WEST;
00735            generateMaze(x+1,y);
00736            break;
00737          case SOUTH:
00738            Maze[x][y]&=~SOUTH;
00739            Maze[x][y+1]&=~NORTH;
00740            generateMaze(x,y+1);
00741            break;
00742          case WEST:
00743            Maze[x][y]&=~WEST;
00744            Maze[x-1][y]&=~EAST;
00745            generateMaze(x-1,y);
00746            break;
00747          case NORTH:
00748            Maze[x][y]&=~NORTH;
00749            Maze[x][y-1]&=~SOUTH;
00750            generateMaze(x,y-1);
00751            break;
00752 
00753          }
00754       po=isPossible(x,y);
00755     }
00756 
00757 }
00758 
00759 static void removeSet(void)
00760 {
00761   int x,y;
00762   for (x=0; x< breedte;x++)
00763     {
00764       for (y=0; y < hoogte; y++)
00765        {
00766          Maze[x][y]&=~SET;
00767        }
00768     }
00769 }
00770 
00771 
00772 /* draw the background of the playing board */
00773 static void
00774 draw_background(GnomeCanvasGroup *rootItem)
00775 {
00776   int x,y,x1,y1;
00777   int wall;
00778   /*draw the lines*/
00779   for (x1=0; x1< breedte; x1++)
00780     {
00781       for (y1=0; y1 < hoogte; y1++)
00782        {
00783          wall=Maze[x1][y1];;
00784          y=cellsize*(y1)+board_border_y;
00785          x=cellsize*(x1)+board_border_x;
00786          if (x1==0)
00787            draw_a_line(rootItem,x, y, x, y+cellsize, gc_skin_get_color("maze/wall color"));
00788 
00789          if (y1==0)
00790            draw_a_line(rootItem,x, y, x+cellsize, y, gc_skin_get_color("maze/wall color"));
00791 
00792          if (wall&EAST)
00793            draw_a_line(rootItem,x+cellsize, y, x+cellsize, y+cellsize, gc_skin_get_color("maze/wall color"));
00794 
00795          if (wall&SOUTH)
00796            draw_a_line(rootItem,x, y+cellsize, x+cellsize, y+cellsize, gc_skin_get_color("maze/wall color"));
00797 
00798        }
00799     }
00800 }
00801 
00802 static void movePos(int x1, int y1, int x2,int y2, int richting)
00803 {
00804   int ret,wall,i,bo=1;
00805   ret=1;
00806   wall=Maze[x1][y1];
00807   if (wall&richting) ret=0;
00808   if (ret)
00809     {
00810       if (Maze[x2][y2]&SET)
00811        {
00812          for (i=(ind); i>=0 && bo; i--)
00813            {
00814 
00815              if(position[i][0]==x2 && position[i][1]==y2)
00816               {
00817                 bo=0;
00818                 move_image(mazegroup,x2,y2,tuxitem);
00819                 //                               draw_rect(mazegroup,x2,y2,"blue");
00820               }
00821              else
00822               {
00823                 Maze[position[i][0]][position[i][1]]&=~SET;
00824                 draw_rect(mazegroup,position[i][0],position[i][1],"red");
00825                 draw_combined_rect(mazegroup,position[i-1][0],position[i-1][1],position[i][0],position[i][1],"red");
00826                 ind--;
00827               }
00828 
00829 
00830            }
00831        }
00832       else
00833        {
00834          ind++;
00835          position[ind][0]=x2;
00836          position[ind][1]=y2;
00837          Maze[x2][y2]|=SET;
00838          if (position[ind][0]==(breedte-1) && position[ind][1]==(end))
00839            game_won();
00840          else
00841            {
00842              move_image(mazegroup,x2,y2,tuxitem);
00843              draw_combined_rect(mazegroup,x1,y1,x2,y2,"green");
00844              draw_rect(mazegroup,x1,y1,"green");
00845            }
00846        }
00847     }
00848 }
00849 
00850 /* return available directions, do not count direction we are coming from
00851    returns 0 if no or more than one direction is possible */
00852 static guint available_direction(guint last_step)
00853 {      guint number=0,result=0;
00854  if (last_step!=WEST && !(Maze[position[ind][0]][position[ind][1]]&EAST))
00855    {  number++; result|=EAST; }
00856  if (last_step!=EAST && !(Maze[position[ind][0]][position[ind][1]]&WEST))
00857    {  number++; result|=WEST; }
00858  if (last_step!=NORTH && !(Maze[position[ind][0]][position[ind][1]]&SOUTH))
00859    {  number++; result|=SOUTH; }
00860  if (last_step!=SOUTH && !(Maze[position[ind][0]][position[ind][1]]&NORTH))
00861    {  number++; result|=NORTH; }
00862  if (number>1) return 0;
00863  return result;
00864 }
00865 
00866 static void one_step(guint richting)
00867 {
00868   update_tux(richting);
00869 
00870   switch (richting)
00871     {
00872     case WEST: movePos(position[ind][0],position[ind][1],position[ind][0]-1,position[ind][1],richting);
00873       return;
00874     case EAST: movePos(position[ind][0],position[ind][1],position[ind][0]+1,position[ind][1],richting);
00875       return;
00876     case NORTH: movePos(position[ind][0],position[ind][1],position[ind][0],position[ind][1]-1,richting);
00877       return;
00878     case SOUTH: movePos(position[ind][0],position[ind][1],position[ind][0],position[ind][1]+1,richting);
00879       return;
00880     }
00881 }
00882 
00883 static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str)
00884 {
00885   guint richting=0,level=gcomprisBoard->level;
00886 
00887   if(board_paused)
00888     return FALSE;
00889 
00890   if (threeDactive) return key_press_3D(keyval, commit_str, preedit_str);
00891 
00892   if (modeRelative) return key_press_2D_relative(keyval, commit_str, preedit_str);
00893 
00894   switch (keyval)
00895     {
00896     case GDK_Left:
00897       /* When In 3D Mode, can't move tux in the 2D mode */
00898       if(!modeIs2D || mapActive)
00899        return TRUE;
00900 
00901       richting=WEST;
00902       break;
00903     case GDK_Right:
00904       /* When In 3D Mode, can't move tux in the 2D mode */
00905       if(!modeIs2D || mapActive)
00906        return TRUE;
00907 
00908       richting=EAST;
00909       break;
00910     case GDK_Up:
00911       /* When In 3D Mode, can't move tux in the 2D mode */
00912       if(!modeIs2D || mapActive)
00913        return TRUE;
00914 
00915       richting=NORTH;
00916       break;
00917     case GDK_Down:
00918       /* When In 3D Mode, can't move tux in the 2D mode */
00919       if(!modeIs2D || mapActive)
00920        return TRUE;
00921 
00922       richting=SOUTH;
00923       break;
00924     case GDK_3:
00925     case GDK_space:
00926       if(modeIsInvisible) {
00927        if(mapActive) {
00928          gnome_canvas_item_hide(GNOME_CANVAS_ITEM(wallgroup));
00929          /* Hide the warning */
00930          gnome_canvas_item_hide(warning_item);
00931          mapActive = FALSE;
00932        } else {
00933          gnome_canvas_item_show(GNOME_CANVAS_ITEM(wallgroup));
00934          /* Display a warning that you can't move there */
00935          gnome_canvas_item_show(warning_item);
00936          mapActive = TRUE;
00937        }
00938       }
00939 
00940       /* switch to 3D only if allowed in the mode */
00941       if(!modeIs2D)
00942        threeDdisplay();
00943       return TRUE;
00944     default: return FALSE;
00945     }
00946   if (Maze[position[ind][0]][position[ind][1]]&richting) return TRUE;
00947   one_step(richting);
00948   viewing_direction=richting;
00949 
00950   /* run until we come to a fork, (make sure to stop on next level!) */
00951   while (run_fast && (richting=available_direction(richting))
00952         && gcomprisBoard->level==level)
00953     {
00954       one_step(richting);
00955       viewing_direction=richting;
00956     }
00957   return TRUE;
00958 }
00959 
00960 static gint tux_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
00961 {      if (event->type!=GDK_BUTTON_PRESS) return FALSE;
00962  run_fast=!run_fast;
00963  return FALSE;
00964 }
00965 
00966 /*---------- 3D stuff below --------------*/
00967 
00968 /* bit magic: rotation = bit rotation */
00969 #define TURN_LEFT(d) ((((d)<<1)|((d)>>3))&15)
00970 #define TURN_RIGHT(d) ((((d)>>1)|((d)<<3))&15)
00971 #define U_TURN(d) ((((d)>>2)|((d)<<2))&15)
00972 
00973 static gint target_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
00974 {      if (event->type!=GDK_BUTTON_PRESS) return FALSE;
00975  threeDdisplay(position[ind][0],position[ind][1]);
00976  return FALSE;
00977 }
00978 
00979 static gint key_press_2D_relative(guint keyval, gchar *commit_str, gchar *preedit_str)
00980 {
00981   guint richting=0,level=gcomprisBoard->level;
00982 
00983   switch (keyval)
00984     {
00985     case GDK_Left: viewing_direction=TURN_LEFT(viewing_direction);
00986       update_tux(viewing_direction);
00987       return TRUE;
00988       break;
00989     case GDK_Right: viewing_direction=TURN_RIGHT(viewing_direction);
00990       update_tux(viewing_direction);
00991       return TRUE;
00992       break;
00993     case GDK_Up: one_step(viewing_direction);
00994       break;
00995     case GDK_Down:
00996       viewing_direction=TURN_RIGHT(viewing_direction);
00997       viewing_direction=TURN_RIGHT(viewing_direction);
00998       update_tux(viewing_direction);
00999       break;
01000     default: return FALSE;
01001     }
01002 
01003   richting=viewing_direction;
01004 
01005   /* run until we come to a fork, (make sure to stop on next level!) */
01006   while (run_fast && (richting=available_direction(richting))
01007         && gcomprisBoard->level==level)
01008     {
01009       one_step(richting);
01010       viewing_direction=richting;
01011     }
01012 
01013   return TRUE;
01014 }
01015 
01016 static gint key_press_3D(guint keyval, gchar *commit_str, gchar *preedit_str)
01017 {
01018   switch (keyval)
01019     {
01020     case GDK_Left: viewing_direction=TURN_LEFT(viewing_direction);
01021       break;
01022     case GDK_Right: viewing_direction=TURN_RIGHT(viewing_direction);
01023       break;
01024     case GDK_Up: one_step(viewing_direction);
01025       break;
01026     case GDK_Down:
01027       viewing_direction=TURN_RIGHT(viewing_direction);
01028       viewing_direction=TURN_RIGHT(viewing_direction);
01029       update_tux(viewing_direction);
01030       break;
01031     case GDK_2:
01032     case GDK_space:
01033       /* Display a warning that you can't move there */
01034       gnome_canvas_item_show(warning_item);
01035       twoDdisplay();
01036       return TRUE;
01037     case GDK_E: case GDK_e: eye_pos_y+=0.1; if (eye_pos_y>0.9) eye_pos_y=0.9; break;
01038     case GDK_X: case GDK_x: eye_pos_y-=0.1; if (eye_pos_y<-0.9) eye_pos_y=-0.9; break;
01039     case GDK_D: case GDK_d: eye_pos_x+=0.1; if (eye_pos_x>0.9) eye_pos_x=0.9; break;
01040     case GDK_S: case GDK_s: eye_pos_x-=0.1; if (eye_pos_x<-0.9) eye_pos_x=-0.9; break;
01041     case GDK_Y: case GDK_y: case GDK_Z: case GDK_z: eye_pos_z+=0.1; break;
01042     case GDK_R: case GDK_r: eye_pos_z-=0.1; if (eye_pos_z<-0.9) eye_pos_z=-0.9; break;
01043     default: return FALSE;
01044     }
01045   update_tux(viewing_direction);
01046   draw3D();
01047   return TRUE;
01048 }
01049 
01050 struct Trapez
01051 {  int x_left,x_right,y_left_top,y_left_bottom,y_right_top,y_right_bottom;
01052 };
01053 
01054 static GnomeCanvasItem *draw_Trapez(GnomeCanvasGroup *group, struct Trapez t,const char *c1, const char *c2)
01055 {      GnomeCanvasPoints *pts=gnome_canvas_points_new(4);
01056  GnomeCanvasItem *res=NULL;
01057  pts->coords[0]=t.x_left;
01058  pts->coords[1]=t.y_left_top;
01059  pts->coords[2]=t.x_right;
01060  pts->coords[3]=t.y_right_top;
01061  pts->coords[4]=t.x_right;
01062  pts->coords[5]=t.y_right_bottom;
01063  pts->coords[6]=t.x_left;
01064  pts->coords[7]=t.y_left_bottom;
01065  res=gnome_canvas_item_new(group,gnome_canvas_polygon_get_type(),
01066                         "points", (GnomeCanvasPoints*)pts,
01067                         "fill_color", c1,
01068                         "outline_color", c2,
01069                         "width_pixels", 1,
01070                         NULL);
01071  return res;
01072 }
01073 
01074 struct vector
01075 {  int x,y;
01076 };
01077 
01078 static struct vector vector_ctor(int x, int y)
01079 {  struct vector r;
01080  r.x=x; r.y=y;
01081  return r;
01082 };
01083 
01084 #if 0
01085 static void print_vector(FILE *f, struct vector v)
01086 {  fprintf(f,"(%d;%d)",v.x,v.y); }
01087 
01088 static void print_Trapez(FILE *f, struct Trapez t)
01089 {  fprintf(f,"(%d; %d..%d)..(%d; %d..%d)",
01090           t.x_left,t.y_left_top,t.y_left_bottom,
01091           t.x_right,t.y_right_top,t.y_right_bottom);
01092 }
01093 #endif
01094 
01095 static gboolean is_wall2(struct vector viewpos, int viewdir)
01096 {  if (viewpos.x<0 || viewpos.y<0 || viewpos.x>=breedte || viewpos.y>=hoogte)
01097   return TRUE;
01098  return Maze[viewpos.x][viewpos.y]&viewdir;
01099 }
01100 
01101 /*
01102   (facing north)
01103   dx.dy:*    *    *    *    *    *
01104   **-2.1*-1.1**0.1**1.1**2.1**
01105   *    *    *    *    *    *
01106   -1.1  0.1  1.1  2.1
01107   *    *    *    *
01108   **-1.0**0.0**1.0**
01109   *    *    *    *
01110   0.0\/1.0
01111   * /\ *
01112   ********
01113   */
01114 
01115 /* rotate a vector by specified amount */
01116 /* this corresponds to multiplying with ( cos a, -sin a )
01117    ( sin a, cos a  )  */
01118 
01119 static struct vector vector_turn(struct vector v,int angle) /* 1=90deg, 2=180deg */
01120 {  switch (angle&3)
01121   {  case 0: return v;
01122   case 1: return vector_ctor(-v.y,v.x);
01123   case 2: return vector_ctor(-v.x,-v.y);
01124   case 3: return vector_ctor(v.y,-v.x);
01125   }
01126  return v; // quiet
01127 }
01128 
01129 static struct vector vector_add(struct vector v, struct vector w)
01130 {  return vector_ctor(v.x+w.x, v.y+w.y);
01131 }
01132 
01133 /* returns result in 90 steps ( 1=90 ...) */
01134 static gint angle(gint a, gint b)
01135 {  if (a==b) return 0;
01136  if (a==TURN_LEFT(b)) return 1;
01137  if (a==U_TURN(b)) return 2;
01138  return 3;
01139 }
01140 
01141 static struct vector invert_y(struct vector v)
01142 {  return vector_ctor(v.x, -v.y);
01143 }
01144 
01145 /* we have to invert the y component of our view beam because the
01146    screen and the labyrinth are left handed systems (unlike
01147    classical vector algebra) */
01148 
01149 static gboolean is_visible(struct vector viewpos, int viewdir,
01150                         struct vector distance, gboolean left_side, gboolean *is_exit)
01151 {  struct vector where=vector_add(viewpos,invert_y(vector_turn(distance,angle(viewdir,NORTH))));
01152  gint direction=left_side ? TURN_LEFT(viewdir) : viewdir;
01153 
01154  if (is_wall2(where,direction))
01155    return TRUE;
01156  if ((where.x==breedte-2 && direction==EAST && where.y==end)
01157      || (where.x==breedte-1
01158         && (where.y==end
01159             || (direction==NORTH && where.y==end+1)
01160             || (direction==SOUTH && where.y==end-1))))
01161    {  *is_exit=TRUE;
01162    return TRUE;
01163    }
01164  return FALSE;
01165 }
01166 
01167 /* screen coordinate of edge (applied ray interception theorems) */
01168 static int transform(int s0, int w, int lx, int ly, eyepos_t ex, eyepos_t ez)
01169 {  return s0
01170      + w*ex
01171      + (w*(1+ez)*(2*lx-(1+ex)))/(2*ly+1+ez);
01172 }
01173 
01174 /* inverse of transform (lx(sx)) */
01175 /* note: it is possible to use integer arithmetic by returning dividend and divisor separately ... */
01176 static float inverse_transform(int s0, int w, int sx, int ly, eyepos_t ex, eyepos_t ez)
01177 {  return ((sx-s0-w*ex)*(2*ly+1+ez)
01178           + w*(1+ex)*(1+ez)
01179           ) / (float)(2*w*(1+ez));
01180 }
01181 
01182 struct screenparam
01183 {  struct vector pos, size; /* middle position, radius */
01184   struct vector screendist; /* position of (x,y,z)(1,1,-1) onscreen */
01185   /* which is the eye-screen distance in screen coordinates */
01186 };
01187 
01188 /* these calculate the inverted function of wall_coords */
01189 
01190 /* leftmost wall which ('s right edge) is visible right of xmin */
01191 /* take left edge, floor (round down) */
01192 static int dx_left(struct screenparam sp, int xmin, int dy, gboolean left_side)
01193 {  if (left_side)
01194   {  if (!dy) return xmin>sp.pos.x-sp.screendist.x /* ?1:0 */ ;
01195   if (xmin<(sp.pos.x+sp.screendist.x*eye_pos_x))
01196     return dx_left(sp,xmin,dy,FALSE)+1;
01197   else return dx_left(sp,xmin,dy-1,FALSE)+1;
01198   }
01199  return (int)(floorf(inverse_transform(sp.pos.x, sp.screendist.x, xmin,
01200                                    dy, eye_pos_x, eye_pos_z)));
01201 }
01202 
01203 /* rightmost wall which ('s left edge) is visible */
01204 /* take right edge, ceil (round up) */
01205 static int dx_right(struct screenparam sp, int xmax, int dy, gboolean left_side)
01206 {  if (left_side)
01207   {  if (!dy) return xmax>sp.pos.x+sp.screendist.x /* ?1:0 */ ;
01208   if (xmax<(sp.pos.x+sp.screendist.x*eye_pos_x))
01209     return dx_right(sp,xmax,dy-1,FALSE);
01210   else return dx_right(sp,xmax,dy,FALSE);
01211   }
01212  return (int)(ceilf(inverse_transform(sp.pos.x, sp.screendist.x, xmax,
01213                                   dy, eye_pos_x, eye_pos_z))) - 1;
01214 }
01215 
01216 static struct Trapez wall_coords(struct screenparam sp, struct vector distance, gboolean left_side)
01217 {  struct Trapez r;
01218 
01219 // leftmost/rightmost wall (special handling)
01220  if (left_side && !distance.y)
01221    {  if (distance.x<=0)
01222      {  r.x_left=sp.pos.x-sp.size.x;
01223      r.y_left_top=sp.pos.y-sp.size.y;
01224      r.y_left_bottom=sp.pos.y+sp.size.y;
01225      r.x_right=sp.pos.x-sp.screendist.x;
01226      r.y_right_top=sp.pos.y-sp.screendist.y;
01227      r.y_right_bottom=sp.pos.y+sp.screendist.y;
01228      }
01229    else
01230      {  r.x_right=sp.pos.x+sp.size.x;
01231      r.y_right_top=sp.pos.y-sp.size.y;
01232      r.y_right_bottom=sp.pos.y+sp.size.y;
01233      r.x_left=sp.pos.x+sp.screendist.x;
01234      r.y_left_top=sp.pos.y-sp.screendist.y;
01235      r.y_left_bottom=sp.pos.y+sp.screendist.y;
01236      }
01237    goto test;
01238    }
01239 
01240  r.x_left=transform(sp.pos.x, sp.screendist.x, distance.x, distance.y, eye_pos_x, eye_pos_z);
01241  // the y sign is inverted (screen coords)
01242  r.y_left_top=transform(sp.pos.y, sp.screendist.y, 0, distance.y, eye_pos_y, eye_pos_z);
01243  r.y_left_bottom=transform(sp.pos.y, sp.screendist.y, 1, distance.y, eye_pos_y, eye_pos_z);
01244  if (left_side)
01245    {  r.x_right=transform(sp.pos.x, sp.screendist.x, distance.x, distance.y-1, eye_pos_x, eye_pos_z);
01246    r.y_right_top=transform(sp.pos.y, sp.screendist.y, 0, distance.y-1, eye_pos_y, eye_pos_z);
01247    r.y_right_bottom=transform(sp.pos.y, sp.screendist.y, 1, distance.y-1, eye_pos_y, eye_pos_z);
01248    if (distance.x<=0)
01249      {  // swap coords since more distant edge becomes left
01250        int h;
01251        h=r.x_left; r.x_left=r.x_right; r.x_right=h;
01252        h=r.y_left_top; r.y_left_top=r.y_right_top; r.y_right_top=h;
01253        h=r.y_left_bottom; r.y_left_bottom=r.y_right_bottom; r.y_right_bottom=h;
01254      }
01255    }
01256  else // front wall
01257    {  r.x_right=transform(sp.pos.x, sp.screendist.x, distance.x+1, distance.y, eye_pos_x, eye_pos_z);
01258    r.y_right_top=r.y_left_top;
01259    r.y_right_bottom=r.y_left_bottom;
01260    }
01261  test:
01262  g_assert(r.x_left<=r.x_right);
01263  g_assert(r.y_left_top<=r.y_left_bottom);
01264  g_assert(r.y_right_top<=r.y_right_bottom);
01265  return r;
01266 }
01267 
01268 static struct Trapez Trapez_hide(const struct Trapez t, int xmin, int xmax)
01269 {  struct Trapez r=t;
01270  if (xmax<xmin) return t;
01271 
01272  if (xmin>t.x_left)
01273    {  r.x_left=xmin;
01274    r.y_left_top=t.y_left_top + (xmin-t.x_left)*(t.y_right_top-t.y_left_top)
01275      /(t.x_right-t.x_left);
01276    r.y_left_bottom=t.y_left_bottom + (xmin-t.x_left)*(t.y_right_bottom-t.y_left_bottom)
01277      /(t.x_right-t.x_left);
01278    }
01279  if (xmax<t.x_right)
01280    {  r.x_right=xmax;
01281    r.y_right_top=t.y_right_top - (t.x_right-xmax)*(t.y_right_top-t.y_left_top)
01282      /(t.x_right-t.x_left);
01283    r.y_right_bottom=t.y_right_bottom - (t.x_right-xmax)*(t.y_right_bottom-t.y_left_bottom)
01284      /(t.x_right-t.x_left);
01285    }
01286  g_assert(r.x_left<=r.x_right);
01287  g_assert(xmin<=r.x_left);
01288  g_assert(r.x_right<=xmax);
01289  g_assert(r.y_left_top<=r.y_left_bottom);
01290  g_assert(r.y_right_top<=r.y_right_bottom);
01291  return r;
01292 }
01293 
01294 static const char *color(int dir)
01295 {  if (dir==EAST) return "white";
01296  if (dir==WEST) return "grey";
01297  return "light grey";
01298 }
01299 
01300 static void gcDisplay(struct vector position, int viewdir,
01301                   struct screenparam sp, int xmin, int xmax, int dy, gboolean left_wall)
01302 {  int dxl=dx_left(sp,xmin,dy,left_wall),
01303      dxr=dx_right(sp,xmax,dy,left_wall),
01304      i=0;
01305  gboolean is_exit=FALSE;
01306 
01307  if (dxl<=0) // seek from the middle left for a wall
01308    {  if (dxr<i) i=dxr;
01309    while (i>=dxl && !is_visible(position,viewdir,vector_ctor(i,dy),left_wall,&is_exit))
01310      --i;
01311    }
01312  if (i>=dxl) // wall found
01313    {  // draw it
01314      struct Trapez t=Trapez_hide(wall_coords(sp,vector_ctor(i,dy),left_wall),xmin,xmax);
01315      draw_Trapez(threedgroup,t,is_exit?"green":color(left_wall?TURN_LEFT(viewdir):viewdir),"black");
01316      // draw left of it
01317      if (t.x_left-1>=xmin) gcDisplay(position,viewdir,sp,xmin,t.x_left-1,dy,left_wall);
01318      // right of it ...
01319      xmin=t.x_right+1;
01320    }
01321 
01322  i=1;
01323  is_exit=FALSE;
01324  if (dxr>=1) // seek from the middle right for a wall
01325    {  if (dxl>i) i=dxl;
01326    while (i<=dxr && !is_visible(position,viewdir,vector_ctor(i,dy),left_wall,&is_exit))
01327      ++i;
01328    }
01329  if (i<=dxr) // wall found
01330    {  struct Trapez t=Trapez_hide(wall_coords(sp,vector_ctor(i,dy),left_wall),xmin,xmax);
01331    draw_Trapez(threedgroup,t,is_exit?"green":color(left_wall?TURN_RIGHT(viewdir):viewdir),"black");
01332    // draw right of it
01333    if (t.x_right+1<xmax)
01334      gcDisplay(position,viewdir,sp,t.x_right+1,xmax,dy,left_wall);
01335    // draw right of it
01336    if (t.x_right+1<xmax)
01337      gcDisplay(position,viewdir,sp,t.x_right+1,xmax,dy,left_wall);
01338    // left of it ...
01339    xmax=t.x_left-1;
01340    }
01341 
01342  if (xmin<=xmax) // draw in the middle (no wall there)
01343    gcDisplay(position,viewdir,sp,xmin,xmax,dy+!left_wall,!left_wall);
01344 }
01345 
01346 static void Display3(struct vector position, int viewdir,
01347                    struct screenparam sp)
01348 {  gcDisplay(position, viewdir, sp, sp.pos.x-sp.size.x, sp.pos.x+sp.size.x,
01349           0, TRUE);
01350 }
01351 
01352 static struct screenparam screenparam_ctor(int px,int py,int sx,int sy,int sdx,int sdy)
01353 {  struct screenparam r;
01354  r.pos=vector_ctor(px,py);
01355  r.size=vector_ctor(sx,sy);
01356  r.screendist=vector_ctor(sdx,sdy);
01357  return r;
01358 }
01359 
01360 static void draw3D()
01361 {
01362 #define MAINX 400
01363 #define MAINY 240
01364 #define MAINSX 400
01365 #define MAINSY 240
01366 
01367   if (threedgroup!=NULL)
01368     gtk_object_destroy(GTK_OBJECT(threedgroup));
01369   if (!threeDactive) return;
01370   threedgroup=GNOME_CANVAS_GROUP(gnome_canvas_item_new(gnome_canvas_root(gcomprisBoard->canvas),
01371                                                  gnome_canvas_group_get_type(),
01372                                                  "x",(double)0,
01373                                                  "y",(double)0,
01374                                                  NULL));
01375   Display3(vector_ctor(position[ind][0],position[ind][1]),viewing_direction,
01376           screenparam_ctor(MAINX,MAINY,MAINSX,MAINSY,0.95*MAINSX,0.95*MAINSY));
01377 }
01378 
01379 static void twoDdisplay()
01380 {
01381   char *fileskin;
01382   fileskin = gc_skin_image_get("gcompris-bg.jpg");
01383   gc_set_background(gnome_canvas_root(gcomprisBoard->canvas), fileskin);
01384   g_free(fileskin);
01385 
01386   if (threedgroup)
01387     gnome_canvas_item_hide(GNOME_CANVAS_ITEM(threedgroup));
01388   gnome_canvas_item_show(GNOME_CANVAS_ITEM(boardRootItem));
01389   threeDactive=FALSE;
01390 }
01391 
01392 static void threeDdisplay()
01393 {
01394   gc_set_background(gnome_canvas_root(gcomprisBoard->canvas), "images/maze-bg.jpg");
01395   gnome_canvas_item_hide(GNOME_CANVAS_ITEM(boardRootItem));
01396   threeDactive=TRUE;
01397   draw3D();
01398 }
01399 
01400 static void update_tux(gint direction)
01401 {
01402   GdkPixbuf *pixmap = NULL;
01403 
01404   switch(direction)
01405     {
01406     case EAST:
01407       pixmap = gc_pixmap_load("gcompris/misc/tux_top_east.png");
01408       break;
01409     case WEST:
01410       pixmap = gc_pixmap_load("gcompris/misc/tux_top_west.png");
01411       break;
01412     case NORTH:
01413       pixmap = gc_pixmap_load("gcompris/misc/tux_top_north.png");
01414       break;
01415     case SOUTH:
01416       pixmap = gc_pixmap_load("gcompris/misc/tux_top_south.png");
01417       break;
01418     }
01419 
01420   if(pixmap)
01421     {
01422       gnome_canvas_item_set (tuxitem,
01423                           "pixbuf", pixmap,
01424                           NULL);
01425       gdk_pixbuf_unref(pixmap);
01426     }
01427 
01428 
01429 }