/* gcompris - maze.c
*
* Copyright (C) 2002, 2008 Bastiaan Verhoef
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include "gcompris/gcompris.h"
#define SOUNDLISTFILE PACKAGE
/*-------------------*/
#define NORTH 1
#define WEST 2
#define SOUTH 4
#define EAST 8
#define SET 16
#define BAD 32
#define WON 64
#define MAX_BREEDTE 37
#define MAX_HOOGTE 20
#define BASE_X1 50
#define BASE_Y1 50
#define BASE_X2 800
#define BASE_Y2 470
#define LINE_COLOR "white"
#define WALL_COLOR 0x1a1109ffL
/* array of the board */
static int Maze[MAX_BREEDTE][MAX_HOOGTE];
static int position[MAX_BREEDTE*MAX_HOOGTE][2];
static int ind=0;
static int begin;
static int end;
static int breedte=10;
static int hoogte=20;
static int cellsize=20;
static int buffer=4;
static int board_border_x=20;
static int board_border_y=3;
static int thickness=2;
static gboolean run_fast=FALSE;
static gboolean modeIs2D=TRUE;
static gboolean modeRelative=FALSE;
static gboolean modeIsInvisible=FALSE;
/*-----------------------*/
static GcomprisBoard *gcomprisBoard = NULL;
static gboolean board_paused = TRUE;
static void start_board (GcomprisBoard *agcomprisBoard);
static void pause_board (gboolean pause);
static void end_board (void);
static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
static int gamewon;
static void process_ok(void);
static void game_won(void);
static void repeat(void);
/* ================================================================ */
static GooCanvasItem *boardRootItem = NULL;
static GooCanvasItem *mazegroup = NULL;
static GooCanvasItem *tuxgroup = NULL;
static GooCanvasItem *wallgroup = NULL;
static GooCanvasItem *warning_item = NULL;
static GooCanvasItem *tuxitem = NULL;
static GooCanvasItem *maze_create_item(GooCanvasItem *parent);
static void maze_destroy_all_items(void);
static void maze_next_level(void);
static void set_level (guint level);
static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str);
/*--------------------*/
static void draw_a_rect(GooCanvasItem *group, int x1, int y1, int x2, int y2, char *color);
static void draw_a_line(GooCanvasItem *group, int x1, int y1, int x2, int y2, guint32 color);
static GooCanvasItem *draw_image(GooCanvasItem *group, int x,int y, GdkPixbuf *pixmap);
static void move_image(GooCanvasItem *group, int x,int y, GooCanvasItem *item);
static void draw_rect(GooCanvasItem *group, int x,int y,char *color);
static void draw_combined_rect(GooCanvasItem *group, int x1, int y1, int x2,int y2, char *color);
static void initMaze(void);
static int check(int x,int y);
static int* isPossible(int x, int y);
static void generateMaze(int x, int y);
static void removeSet(void);
static void draw_background(GooCanvasItem *rootItem);
static void setlevelproperties(void);
static gboolean tux_event (GooCanvasItem *item,
GooCanvasItem *target,
GdkEventButton *event,
gpointer data);
static void update_tux(gint direction);
/*---------- 3D stuff ------------*/
static GooCanvasItem *threedgroup = NULL;
static gint viewing_direction=EAST;
static gboolean threeDactive=FALSE;
static gboolean mapActive=FALSE;
typedef float eyepos_t;
// x,y e ]-1 ... 1[
// z e ]-1 ... 1 ... oo[
static eyepos_t eye_pos_x=0, eye_pos_y=0, eye_pos_z=0;
static void threeDdisplay();
static void twoDdisplay();
static void draw3D();
static gint key_press_3D(guint keyval, gchar *commit_str, gchar *preedit_str);
static gint key_press_2D_relative(guint keyval, gchar *commit_str, gchar *preedit_str);
/*----------------------*/
/* Description of this plugin */
static BoardPlugin menu_bp =
{
NULL,
NULL,
N_("Maze"),
N_("Find your way out of the maze"),
"Bastiaan Verhoef ",
NULL,
NULL,
NULL,
NULL,
start_board,
pause_board,
end_board,
is_our_board,
key_press,
process_ok,
set_level,//set_level,
NULL,
repeat,
NULL,
NULL
};
/* =====================================================================
*
* =====================================================================*/
GET_BPLUGIN_INFO(maze)
/* =====================================================================
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
* =====================================================================*/
static void pause_board (gboolean pause)
{
if(gcomprisBoard==NULL)
return;
if(gamewon == TRUE && pause == FALSE) /* the game is won */
game_won();
board_paused = pause;
}
static void set_level (guint level) {
if(gcomprisBoard!=NULL) {
gcomprisBoard->level=level;
maze_next_level();
}
}
/* =====================================================================
*
* =====================================================================*/
static void start_board (GcomprisBoard *agcomprisBoard) {
if(agcomprisBoard!=NULL) {
gcomprisBoard=agcomprisBoard;
/* disable im_context */
gcomprisBoard->disable_im_context = TRUE;
gc_set_default_background(goo_canvas_get_root_item(gcomprisBoard->canvas));
gcomprisBoard->level=1;
gcomprisBoard->maxlevel=20;
/* The mode defines if we run 2D or 3D */
/* Default mode is 2D */
modeRelative=FALSE;
modeIsInvisible=FALSE;
if(!gcomprisBoard->mode)
modeIs2D=TRUE;
else if(g_ascii_strncasecmp(gcomprisBoard->mode, "2DR", 3)==0) {
/* 2D Relative */
modeIs2D=TRUE;
modeRelative=TRUE;
} else if(g_ascii_strncasecmp(gcomprisBoard->mode, "2DI", 3)==0) {
modeIs2D=TRUE;
modeIsInvisible=TRUE;
} else if(g_ascii_strncasecmp(gcomprisBoard->mode, "2D", 2)==0) {
modeIs2D=TRUE;
} else if(g_ascii_strncasecmp(gcomprisBoard->mode, "3D", 2)==0) {
modeIs2D=FALSE;
}
if(!modeIs2D || modeIsInvisible) {
RsvgHandle *svg_handle;
svg_handle = gc_rsvg_load("maze/maze-2d-bubble.svg");
if(svg_handle) {
gc_bar_set_repeat_icon(svg_handle);
g_object_unref(svg_handle);
gc_bar_set(GC_BAR_LEVEL|GC_BAR_REPEAT_ICON);
} else {
gc_bar_set(GC_BAR_LEVEL|GC_BAR_REPEAT);
}
} else {
/* 2D Regular mode */
gc_bar_set(GC_BAR_LEVEL);
}
gc_bar_location(-1, -1, 0.6);
gamewon = FALSE;
maze_next_level();
pause_board(FALSE);
}
}
/* =====================================================================
*
* =====================================================================*/
static void end_board () {
if(gcomprisBoard!=NULL){
pause_board(TRUE);
maze_destroy_all_items();
}
gcomprisBoard = NULL;
}
/* =====================================================================
*
* =====================================================================*/
static gboolean is_our_board (GcomprisBoard *gcomprisBoard) {
if (gcomprisBoard) {
if(g_ascii_strcasecmp(gcomprisBoard->type, "maze")==0) {
/* Set the plugin entry */
gcomprisBoard->plugin=&menu_bp;
return TRUE;
}
}
return FALSE;
}
/* =====================================================================
* set initial values for the next level
* =====================================================================*/
static void maze_next_level() {
GdkPixbuf *pixmap = NULL;
maze_destroy_all_items();
gc_bar_set_level(gcomprisBoard);
setlevelproperties();
mapActive = FALSE;
gamewon = FALSE;
initMaze();
generateMaze((g_random_int()%breedte),(g_random_int()%hoogte));
removeSet();
/* Try the next level */
maze_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
draw_background(wallgroup);
if(modeIsInvisible) {
g_object_set (wallgroup, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
}
/* make a new group for the items */
begin=g_random_int()%hoogte;
end=g_random_int()%hoogte;
/* Draw the tux */
RsvgHandle *svg_handle = gc_rsvg_load("maze/tux_top_south.svg");
tuxitem = goo_canvas_svg_new (tuxgroup, svg_handle,
NULL);
g_object_unref (svg_handle);
goo_canvas_item_translate(tuxgroup,
cellsize*(0)-breedte + board_border_x,
cellsize*(begin)-hoogte + board_border_y);
g_signal_connect(tuxitem,
"button_press_event",
(GCallback) tux_event, NULL);
/* Draw the target */
pixmap = gc_pixmap_load("maze/door.png");
if(pixmap)
{
draw_image(mazegroup,breedte-1,end,pixmap);
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(pixmap);
#else
g_object_unref(pixmap);
#endif
}
position[ind][0]=0;
position[ind][1]=begin;
Maze[0][begin]=Maze[0][begin]+SET;
viewing_direction=EAST;
threeDactive=FALSE;
if (gcomprisBoard->level==1) run_fast=FALSE;
if (gcomprisBoard->level==14) run_fast=TRUE;
update_tux(viewing_direction);
if(!modeIs2D)
threeDdisplay();
}
/* ======================================= */
static void setlevelproperties(){
if (gcomprisBoard->level==1)
{
breedte=4;
hoogte=4;
cellsize=90;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==2)
{
breedte=5;
hoogte=4;
cellsize=88;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==3)
{
breedte=5;
hoogte=5;
cellsize=85;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==4)
{
breedte=6;
hoogte=5;
cellsize=80;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==5)
{
breedte=6;
hoogte=6;
cellsize=70;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==6)
{
breedte=6;
hoogte=7;
cellsize=65;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==7)
{
breedte=7;
hoogte=7;
cellsize=65;
buffer=2;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==8)
{
breedte=8;
hoogte=7;
cellsize=65;
buffer=5;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==9)
{
breedte=8;
hoogte=8;
cellsize=55;
buffer=5;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==10)
{
breedte=9;
hoogte=8;
cellsize=55;
buffer=4;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==11)
{
breedte=9;
hoogte=9;
cellsize=50;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==12)
{
breedte=10;
hoogte=9;
cellsize=50;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==13)
{
breedte=10;
hoogte=10;
cellsize=45;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==14)
{
breedte=8;
hoogte=16;
cellsize=28;
buffer=2;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==15)
{
breedte=14;
hoogte=14;
cellsize=30;
buffer=4;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==16)
{
breedte=16;
hoogte=15;
cellsize=28;
buffer=3;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==17)
{
breedte=17;
hoogte=16;
cellsize=28;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==18)
{
breedte=18;
hoogte=17;
cellsize=25;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==19)
{
breedte=19;
hoogte=18;
cellsize=24;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
else if (gcomprisBoard->level==20)
{
breedte=19;
hoogte=19;
cellsize=24;
buffer=8;
board_border_x=(int) (BASE_X2-breedte*cellsize)/2;
board_border_y=(int) (BASE_Y2-hoogte*cellsize)/2;
}
}
/*
* Repeat let the user get a help map in 3D mode
*
*/
static void repeat () {
if(modeIsInvisible) {
if(mapActive) {
g_object_set (wallgroup, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
/* Hide the warning */
g_object_set (warning_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
mapActive = FALSE;
} else {
g_object_set (wallgroup, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
/* Display a warning that you can't move there */
g_object_set (warning_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
mapActive = TRUE;
}
}
if(modeIs2D)
return;
if(threeDactive) {
RsvgHandle *svg_handle;
svg_handle = gc_rsvg_load("maze/maze-3d-bubble.svg");
if(svg_handle) {
gc_bar_set_repeat_icon(svg_handle);
g_object_unref(svg_handle);
}
twoDdisplay();
/* Display a warning that you can't move there */
g_object_set (warning_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
} else {
RsvgHandle *svg_handle;
svg_handle = gc_rsvg_load("maze/maze-2d-bubble.svg");
if(svg_handle) {
gc_bar_set_repeat_icon(svg_handle);
g_object_unref(svg_handle);
}
g_object_set (warning_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
threeDdisplay();
}
}
/* =====================================================================
* Destroy all the items
* =====================================================================*/
static void maze_destroy_all_items() {
if(boardRootItem!=NULL)
goo_canvas_item_remove(boardRootItem);
if (threedgroup!=NULL)
goo_canvas_item_remove(threedgroup);
mazegroup = NULL;
tuxgroup = NULL;
wallgroup = NULL;
boardRootItem = NULL;
threedgroup=NULL;
}
/* =====================================================================
*
* =====================================================================*/
static GooCanvasItem *
maze_create_item(GooCanvasItem *parent)
{
gchar *message;
boardRootItem = goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
NULL);
mazegroup = goo_canvas_group_new(boardRootItem,
NULL);
goo_canvas_item_translate(mazegroup,
breedte,
hoogte);
tuxgroup = goo_canvas_group_new(mazegroup,
NULL);
wallgroup = goo_canvas_group_new(boardRootItem,
NULL);
if(modeIsInvisible) {
message = _("Look at your position, then switch back to invisible mode to continue your moves");
} else {
message = _("Look at your position, then switch back to 3D mode to continue your moves");
}
warning_item = goo_canvas_text_new (boardRootItem,
message,
(double) BOARDWIDTH/2,
(double) BOARDHEIGHT-BARHEIGHT-25,
-1,
GTK_ANCHOR_CENTER,
"font", gc_skin_font_board_tiny,
"fill_color_rgba", gc_skin_color_content,
NULL);
g_object_set (warning_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
return NULL;
}
/* =====================================================================
*
* =====================================================================*/
static void game_won() {
twoDdisplay();
/* Try the next level */
gcomprisBoard->level++;
if(gcomprisBoard->level > gcomprisBoard->maxlevel)
gcomprisBoard->level = gcomprisBoard->maxlevel;
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
maze_next_level();
}
/* =====================================================================
*
* =====================================================================*/
static void process_ok() {
}
static void
draw_a_rect(GooCanvasItem *group,
int x1, int y1, int x2, int y2,
char *color)
{
goo_canvas_rect_new(group,
x1,
y1,
x2 - x1,
y2 - y1,
"fill-color", color,
"stroke-color", color,
"line-width", 1.0,
NULL);
}
static void
draw_a_line(GooCanvasItem *group,
int x1, int y1, int x2, int y2, guint32 color)
{
goo_canvas_polyline_new(group, FALSE, 2,
(double)x1, (double)y1,
(double)x2, (double)y2,
"fill_color_rgba", color,
"line-width", (double)thickness,
NULL);
}
static void
draw_rect(GooCanvasItem *group, int x,int y,char *color)
{
int x1,y1;
y1=cellsize*(y)-hoogte + board_border_y;
x1=cellsize*(x)-breedte + board_border_x;
draw_a_rect(group,
x1+buffer,y1+buffer ,x1+cellsize-buffer,
y1+cellsize-buffer,
color);
}
/*
* Same as draw rect but for an image
* Returns the created item
*/
static GooCanvasItem *
draw_image(GooCanvasItem *group,
int x,int y, GdkPixbuf *pixmap)
{
GooCanvasItem *item = NULL;
int x1,y1;
GdkPixbuf *pixmap2;
y1=cellsize*(y)-hoogte + board_border_y;
x1=cellsize*(x)-breedte + board_border_x;
pixmap2 = gdk_pixbuf_scale_simple (pixmap,
cellsize-buffer*2,
cellsize-buffer*2,
GDK_INTERP_BILINEAR);
item = goo_canvas_image_new (group,
pixmap2,
x1+buffer,
y1+buffer,
NULL);
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(pixmap2);
#else
g_object_unref(pixmap2);
#endif
return(item);
}
/*
* Same as draw rect but for an image
*/
static void move_image(GooCanvasItem *group,
int x, int y, GooCanvasItem *item)
{
int x1,y1;
y1=cellsize*(y)-hoogte + board_border_y;
x1=cellsize*(x)-breedte + board_border_x;
goo_canvas_item_set_transform(item, NULL);
goo_canvas_item_translate(item,
x1, y1);
goo_canvas_item_raise(item, NULL);
}
static void draw_combined_rect(GooCanvasItem *group,
int x1,int y1,int x2,int y2,
char *color)
{
int xx1,yy1,xx2,yy2;
yy1=cellsize*(y1)-hoogte + board_border_y;
xx1=cellsize*(x1)-breedte + board_border_x;
yy2=cellsize*(y2)-hoogte + board_border_y;
xx2=cellsize*(x2)-breedte + board_border_x;
if (y1==y2 && x1x2)
{
draw_a_rect(group,
xx2+cellsize-buffer,
yy2+buffer,
xx1+buffer,
yy1+cellsize-buffer,
color);
}
else if (x1==x2 && y1y2)
{
draw_a_rect(group,
xx2+buffer,
yy2+cellsize-buffer,
xx1+cellsize-buffer,
yy1+buffer,
color);
}
}
static void initMaze(void)
{
int x, y;
for (x=0; x0)
{
int nr = *po;
int ran, in;
in=(g_random_int()%nr)+1;
//printf("random: %d en %d mogelijkheden\n", in, *po);
ran=*(po + in);
if (nr>=1)
switch (ran)
{
case EAST:
Maze[x][y]&=~EAST;
Maze[x+1][y]&=~WEST;
generateMaze(x+1,y);
break;
case SOUTH:
Maze[x][y]&=~SOUTH;
Maze[x][y+1]&=~NORTH;
generateMaze(x,y+1);
break;
case WEST:
Maze[x][y]&=~WEST;
Maze[x-1][y]&=~EAST;
generateMaze(x-1,y);
break;
case NORTH:
Maze[x][y]&=~NORTH;
Maze[x][y-1]&=~SOUTH;
generateMaze(x,y-1);
break;
}
po=isPossible(x,y);
}
}
static void removeSet(void)
{
int x,y;
for (x=0; x< breedte;x++)
{
for (y=0; y < hoogte; y++)
{
Maze[x][y]&=~SET;
}
}
}
/* draw the background of the playing board */
static void
draw_background(GooCanvasItem *rootItem)
{
int x,y,x1,y1;
int wall;
/*draw the lines*/
for (x1=0; x1< breedte; x1++)
{
for (y1=0; y1 < hoogte; y1++)
{
wall=Maze[x1][y1];;
y=cellsize*(y1)+board_border_y;
x=cellsize*(x1)+board_border_x;
if (x1==0)
draw_a_line(rootItem,
x, y, x, y+cellsize,
WALL_COLOR);
if (y1==0)
draw_a_line(rootItem,
x, y, x+cellsize, y,
WALL_COLOR);
if (wall&EAST)
draw_a_line(rootItem,
x+cellsize, y, x+cellsize, y+cellsize,
WALL_COLOR);
if (wall&SOUTH)
draw_a_line(rootItem,x,
y+cellsize, x+cellsize, y+cellsize,
WALL_COLOR);
}
}
}
static void movePos(int x1, int y1, int x2,int y2, int richting)
{
int ret,wall,i,bo=1;
ret=1;
wall=Maze[x1][y1];
if (wall&richting)
{
gc_sound_play_ogg ("sounds/brick.wav", NULL);
ret=0;
}
if (ret)
{
gc_sound_play_ogg ("sounds/prompt.wav", NULL);
if (Maze[x2][y2]&SET)
{
for (i=(ind); i>=0 && bo; i--)
{
if(position[i][0]==x2 && position[i][1]==y2)
{
bo=0;
move_image(mazegroup,x2,y2,tuxgroup);
// draw_rect(mazegroup,x2,y2,"blue");
}
else
{
Maze[position[i][0]][position[i][1]]&=~SET;
draw_rect(mazegroup, position[i][0], position[i][1], "red");
draw_combined_rect(mazegroup,
position[i-1][0],position[i-1][1],
position[i][0],position[i][1],
"red");
ind--;
}
}
}
else
{
ind++;
position[ind][0]=x2;
position[ind][1]=y2;
Maze[x2][y2]|=SET;
if (position[ind][0]==(breedte-1) && position[ind][1]==(end))
{
gamewon = TRUE;
twoDdisplay();
gc_bonus_display(gamewon, GC_BONUS_LION);
}
else
{
draw_combined_rect(mazegroup, x1, y1, x2, y2, "green");
draw_rect(mazegroup,x1,y1,"green");
move_image(mazegroup,x2,y2,tuxgroup);
}
}
}
}
/* return available directions, do not count direction we are coming from
returns 0 if no or more than one direction is possible */
static guint available_direction(guint last_step)
{ guint number=0,result=0;
if (last_step!=WEST && !(Maze[position[ind][0]][position[ind][1]]&EAST))
{ number++; result|=EAST; }
if (last_step!=EAST && !(Maze[position[ind][0]][position[ind][1]]&WEST))
{ number++; result|=WEST; }
if (last_step!=NORTH && !(Maze[position[ind][0]][position[ind][1]]&SOUTH))
{ number++; result|=SOUTH; }
if (last_step!=SOUTH && !(Maze[position[ind][0]][position[ind][1]]&NORTH))
{ number++; result|=NORTH; }
if (number>1) return 0;
return result;
}
static void one_step(guint richting)
{
update_tux(richting);
switch (richting)
{
case WEST: movePos(position[ind][0],position[ind][1],position[ind][0]-1,position[ind][1],richting);
return;
case EAST: movePos(position[ind][0],position[ind][1],position[ind][0]+1,position[ind][1],richting);
return;
case NORTH: movePos(position[ind][0],position[ind][1],position[ind][0],position[ind][1]-1,richting);
return;
case SOUTH: movePos(position[ind][0],position[ind][1],position[ind][0],position[ind][1]+1,richting);
return;
}
}
static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str)
{
guint richting=0,level=gcomprisBoard->level;
if(board_paused)
return FALSE;
if (threeDactive) return key_press_3D(keyval, commit_str, preedit_str);
if (modeRelative) return key_press_2D_relative(keyval, commit_str, preedit_str);
switch (keyval)
{
case GDK_Left:
/* When In 3D Mode, can't move tux in the 2D mode */
if(!modeIs2D || mapActive)
return TRUE;
richting=WEST;
break;
case GDK_Right:
/* When In 3D Mode, can't move tux in the 2D mode */
if(!modeIs2D || mapActive)
return TRUE;
richting=EAST;
break;
case GDK_Up:
/* When In 3D Mode, can't move tux in the 2D mode */
if(!modeIs2D || mapActive)
return TRUE;
richting=NORTH;
break;
case GDK_Down:
/* When In 3D Mode, can't move tux in the 2D mode */
if(!modeIs2D || mapActive)
return TRUE;
richting=SOUTH;
break;
case GDK_3:
case GDK_space:
if(modeIsInvisible) {
gc_sound_play_ogg ("sounds/flip.wav", NULL);
if(mapActive) {
g_object_set (wallgroup, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
/* Hide the warning */
g_object_set (warning_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
mapActive = FALSE;
} else {
g_object_set (wallgroup, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
/* Display a warning that you can't move there */
g_object_set (warning_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
mapActive = TRUE;
}
}
/* switch to 3D only if allowed in the mode */
if(!modeIs2D)
threeDdisplay();
return TRUE;
default: return FALSE;
}
if (Maze[position[ind][0]][position[ind][1]]&richting)
{
gc_sound_play_ogg ("sounds/brick.wav", NULL);
return TRUE;
}
one_step(richting);
viewing_direction=richting;
/* run until we come to a fork, (make sure to stop on next level!) */
while (run_fast && (richting=available_direction(richting))
&& gcomprisBoard->level==level)
{
one_step(richting);
viewing_direction=richting;
}
return TRUE;
}
static gboolean
tux_event (GooCanvasItem *item,
GooCanvasItem *target,
GdkEventButton *event,
gpointer data)
{
printf("tux_event\n");
run_fast=!run_fast;
return FALSE;
}
/*---------- 3D stuff below --------------*/
/* bit magic: rotation = bit rotation */
#define TURN_LEFT(d) ((((d)<<1)|((d)>>3))&15)
#define TURN_RIGHT(d) ((((d)>>1)|((d)<<3))&15)
#define U_TURN(d) ((((d)>>2)|((d)<<2))&15)
static gint key_press_2D_relative(guint keyval, gchar *commit_str, gchar *preedit_str)
{
guint richting=0,level=gcomprisBoard->level;
switch (keyval)
{
case GDK_Left: viewing_direction=TURN_LEFT(viewing_direction);
gc_sound_play_ogg ("sounds/grow.wav", NULL);
update_tux(viewing_direction);
return TRUE;
break;
case GDK_Right: viewing_direction=TURN_RIGHT(viewing_direction);
gc_sound_play_ogg ("sounds/grow.wav", NULL);
update_tux(viewing_direction);
return TRUE;
break;
case GDK_Up: one_step(viewing_direction);
break;
case GDK_Down:
gc_sound_play_ogg ("sounds/grow.wav", NULL);
viewing_direction=TURN_RIGHT(viewing_direction);
viewing_direction=TURN_RIGHT(viewing_direction);
update_tux(viewing_direction);
break;
default: return FALSE;
}
richting=viewing_direction;
/* run until we come to a fork, (make sure to stop on next level!) */
while (run_fast && (richting=available_direction(richting))
&& gcomprisBoard->level==level)
{
one_step(richting);
viewing_direction=richting;
}
return TRUE;
}
static gint key_press_3D(guint keyval, gchar *commit_str, gchar *preedit_str)
{
switch (keyval)
{
case GDK_Left: viewing_direction=TURN_LEFT(viewing_direction);
gc_sound_play_ogg ("sounds/grow.wav", NULL);
break;
case GDK_Right: viewing_direction=TURN_RIGHT(viewing_direction);
gc_sound_play_ogg ("sounds/grow.wav", NULL);
break;
case GDK_Up: one_step(viewing_direction);
break;
case GDK_Down:
viewing_direction=TURN_RIGHT(viewing_direction);
viewing_direction=TURN_RIGHT(viewing_direction);
gc_sound_play_ogg ("sounds/grow.wav", NULL);
break;
case GDK_2:
case GDK_space:
/* Display a warning that you can't move there */
g_object_set (warning_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
twoDdisplay();
return TRUE;
case GDK_E: case GDK_e: eye_pos_y+=0.1; if (eye_pos_y>0.9) eye_pos_y=0.9; break;
case GDK_X: case GDK_x: eye_pos_y-=0.1; if (eye_pos_y<-0.9) eye_pos_y=-0.9; break;
case GDK_D: case GDK_d: eye_pos_x+=0.1; if (eye_pos_x>0.9) eye_pos_x=0.9; break;
case GDK_S: case GDK_s: eye_pos_x-=0.1; if (eye_pos_x<-0.9) eye_pos_x=-0.9; break;
case GDK_Y: case GDK_y: case GDK_Z: case GDK_z: eye_pos_z+=0.1; break;
case GDK_R: case GDK_r: eye_pos_z-=0.1; if (eye_pos_z<-0.9) eye_pos_z=-0.9; break;
default: return FALSE;
}
update_tux(viewing_direction);
draw3D();
return TRUE;
}
struct Trapez
{ int x_left,x_right,y_left_top,y_left_bottom,y_right_top,y_right_bottom;
};
static GooCanvasItem *
draw_Trapez(GooCanvasItem *group,
struct Trapez t,const char *c1, const char *c2)
{
GooCanvasPoints *pts=goo_canvas_points_new(4);
GooCanvasItem *res=NULL;
pts->coords[0]=t.x_left;
pts->coords[1]=t.y_left_top;
pts->coords[2]=t.x_right;
pts->coords[3]=t.y_right_top;
pts->coords[4]=t.x_right;
pts->coords[5]=t.y_right_bottom;
pts->coords[6]=t.x_left;
pts->coords[7]=t.y_left_bottom;
res=goo_canvas_polyline_new(group, FALSE, 0,
"points", (GooCanvasPoints*)pts,
"fill-color", c1,
"stroke-color", c2,
"line-width", 1.0,
NULL);
return res;
}
struct vector
{ int x,y;
};
static struct vector
vector_ctor(int x, int y)
{
struct vector r;
r.x=x; r.y=y;
return r;
};
#if 0
static void print_vector(FILE *f, struct vector v)
{ fprintf(f,"(%d;%d)",v.x,v.y); }
static void print_Trapez(FILE *f, struct Trapez t)
{ fprintf(f,"(%d; %d..%d)..(%d; %d..%d)",
t.x_left,t.y_left_top,t.y_left_bottom,
t.x_right,t.y_right_top,t.y_right_bottom);
}
#endif
static gboolean
is_wall2(struct vector viewpos, int viewdir)
{
if (viewpos.x<0 || viewpos.y<0 || viewpos.x>=breedte || viewpos.y>=hoogte)
return TRUE;
return Maze[viewpos.x][viewpos.y]&viewdir;
}
/*
(facing north)
dx.dy:* * * * * *
**-2.1*-1.1**0.1**1.1**2.1**
* * * * * *
-1.1 0.1 1.1 2.1
* * * *
**-1.0**0.0**1.0**
* * * *
0.0\/1.0
* /\ *
********
*/
/* rotate a vector by specified amount */
/* this corresponds to multiplying with ( cos a, -sin a )
( sin a, cos a ) */
static struct vector
vector_turn(struct vector v,int angle) /* 1=90deg, 2=180deg */
{
switch (angle&3)
{ case 0: return v;
case 1: return vector_ctor(-v.y,v.x);
case 2: return vector_ctor(-v.x,-v.y);
case 3: return vector_ctor(v.y,-v.x);
}
return v; // quiet
}
static struct vector
vector_add(struct vector v, struct vector w)
{
return vector_ctor(v.x+w.x, v.y+w.y);
}
/* returns result in 90° steps ( 1=90° ...) */
static gint
angle(gint a, gint b)
{
if (a==b) return 0;
if (a==TURN_LEFT(b)) return 1;
if (a==U_TURN(b)) return 2;
return 3;
}
static struct vector
invert_y(struct vector v)
{
return vector_ctor(v.x, -v.y);
}
/* we have to invert the y component of our view beam because the
screen and the labyrinth are left handed systems (unlike
classical vector algebra) */
static gboolean
is_visible(struct vector viewpos, int viewdir,
struct vector distance, gboolean left_side, gboolean *is_exit)
{
struct vector where=vector_add(viewpos,invert_y(vector_turn(distance,angle(viewdir,NORTH))));
gint direction=left_side ? TURN_LEFT(viewdir) : viewdir;
if (is_wall2(where,direction))
return TRUE;
if ((where.x==breedte-2 && direction==EAST && where.y==end)
|| (where.x==breedte-1
&& (where.y==end
|| (direction==NORTH && where.y==end+1)
|| (direction==SOUTH && where.y==end-1))))
{
*is_exit=TRUE;
return TRUE;
}
return FALSE;
}
/* screen coordinate of edge (applied ray interception theorems) */
static int
transform(int s0, int w, int lx, int ly, eyepos_t ex, eyepos_t ez)
{
return s0
+ w*ex
+ (w*(1+ez)*(2*lx-(1+ex)))/(2*ly+1+ez);
}
/* inverse of transform (lx(sx)) */
/* note: it is possible to use integer arithmetic by returning dividend and divisor separately ... */
static float inverse_transform(int s0, int w, int sx, int ly, eyepos_t ex, eyepos_t ez)
{
return ((sx-s0-w*ex)*(2*ly+1+ez)
+ w*(1+ex)*(1+ez)
) / (float)(2*w*(1+ez));
}
struct screenparam
{
struct vector pos, size; /* middle position, radius */
struct vector screendist; /* position of (x,y,z)(1,1,-1) onscreen */
/* which is the eye-screen distance in screen coordinates */
};
/* these calculate the inverted function of wall_coords */
/* leftmost wall which ('s right edge) is visible right of xmin */
/* take left edge, floor (round down) */
static int
dx_left(struct screenparam sp, int xmin, int dy, gboolean left_side)
{
if (left_side)
{ if (!dy) return xmin>sp.pos.x-sp.screendist.x /* ?1:0 */ ;
if (xmin<(sp.pos.x+sp.screendist.x*eye_pos_x))
return dx_left(sp,xmin,dy,FALSE)+1;
else return dx_left(sp,xmin,dy-1,FALSE)+1;
}
return (int)(floorf(inverse_transform(sp.pos.x, sp.screendist.x, xmin,
dy, eye_pos_x, eye_pos_z)));
}
/* rightmost wall which ('s left edge) is visible */
/* take right edge, ceil (round up) */
static int
dx_right(struct screenparam sp, int xmax, int dy, gboolean left_side)
{
if (left_side)
{ if (!dy) return xmax>sp.pos.x+sp.screendist.x /* ?1:0 */ ;
if (xmax<(sp.pos.x+sp.screendist.x*eye_pos_x))
return dx_right(sp,xmax,dy-1,FALSE);
else return dx_right(sp,xmax,dy,FALSE);
}
return (int)(ceilf(inverse_transform(sp.pos.x, sp.screendist.x, xmax,
dy, eye_pos_x, eye_pos_z))) - 1;
}
static struct Trapez
wall_coords(struct screenparam sp, struct vector distance, gboolean left_side)
{
struct Trapez r;
// leftmost/rightmost wall (special handling)
if (left_side && !distance.y)
{ if (distance.x<=0)
{ r.x_left=sp.pos.x-sp.size.x;
r.y_left_top=sp.pos.y-sp.size.y;
r.y_left_bottom=sp.pos.y+sp.size.y;
r.x_right=sp.pos.x-sp.screendist.x;
r.y_right_top=sp.pos.y-sp.screendist.y;
r.y_right_bottom=sp.pos.y+sp.screendist.y;
}
else
{ r.x_right=sp.pos.x+sp.size.x;
r.y_right_top=sp.pos.y-sp.size.y;
r.y_right_bottom=sp.pos.y+sp.size.y;
r.x_left=sp.pos.x+sp.screendist.x;
r.y_left_top=sp.pos.y-sp.screendist.y;
r.y_left_bottom=sp.pos.y+sp.screendist.y;
}
goto test;
}
r.x_left=transform(sp.pos.x, sp.screendist.x, distance.x, distance.y, eye_pos_x, eye_pos_z);
// the y sign is inverted (screen coords)
r.y_left_top=transform(sp.pos.y, sp.screendist.y, 0, distance.y, eye_pos_y, eye_pos_z);
r.y_left_bottom=transform(sp.pos.y, sp.screendist.y, 1, distance.y, eye_pos_y, eye_pos_z);
if (left_side)
{ r.x_right=transform(sp.pos.x, sp.screendist.x, distance.x, distance.y-1, eye_pos_x, eye_pos_z);
r.y_right_top=transform(sp.pos.y, sp.screendist.y, 0, distance.y-1, eye_pos_y, eye_pos_z);
r.y_right_bottom=transform(sp.pos.y, sp.screendist.y, 1, distance.y-1, eye_pos_y, eye_pos_z);
if (distance.x<=0)
{ // swap coords since more distant edge becomes left
int h;
h=r.x_left; r.x_left=r.x_right; r.x_right=h;
h=r.y_left_top; r.y_left_top=r.y_right_top; r.y_right_top=h;
h=r.y_left_bottom; r.y_left_bottom=r.y_right_bottom; r.y_right_bottom=h;
}
}
else // front wall
{ r.x_right=transform(sp.pos.x, sp.screendist.x, distance.x+1, distance.y, eye_pos_x, eye_pos_z);
r.y_right_top=r.y_left_top;
r.y_right_bottom=r.y_left_bottom;
}
test:
g_assert(r.x_left<=r.x_right);
g_assert(r.y_left_top<=r.y_left_bottom);
g_assert(r.y_right_top<=r.y_right_bottom);
return r;
}
static struct Trapez
Trapez_hide(const struct Trapez t, int xmin, int xmax)
{
struct Trapez r=t;
if (xmaxt.x_left)
{ r.x_left=xmin;
r.y_left_top=t.y_left_top + (xmin-t.x_left)*(t.y_right_top-t.y_left_top)
/(t.x_right-t.x_left);
r.y_left_bottom=t.y_left_bottom + (xmin-t.x_left)*(t.y_right_bottom-t.y_left_bottom)
/(t.x_right-t.x_left);
}
if (xmax=dxl && !is_visible(position,viewdir,vector_ctor(i,dy),left_wall,&is_exit))
--i;
}
if (i>=dxl) // wall found
{ // draw it
struct Trapez t=Trapez_hide(wall_coords(sp,vector_ctor(i,dy),left_wall),xmin,xmax);
draw_Trapez(threedgroup,t,is_exit?"green":color(left_wall?TURN_LEFT(viewdir):viewdir),"black");
// draw left of it
if (t.x_left-1>=xmin) gcDisplay(position,viewdir,sp,xmin,t.x_left-1,dy,left_wall);
// right of it ...
xmin=t.x_right+1;
}
i=1;
is_exit=FALSE;
if (dxr>=1) // seek from the middle right for a wall
{ if (dxl>i) i=dxl;
while (i<=dxr && !is_visible(position,viewdir,vector_ctor(i,dy),left_wall,&is_exit))
++i;
}
if (i<=dxr) // wall found
{
struct Trapez t=Trapez_hide(wall_coords(sp,vector_ctor(i,dy),left_wall),xmin,xmax);
draw_Trapez(threedgroup,t,is_exit?"green":color(left_wall?TURN_RIGHT(viewdir):viewdir),"black");
// draw right of it
if (t.x_right+1canvas),
NULL);
Display3(vector_ctor(position[ind][0],position[ind][1]),viewing_direction,
screenparam_ctor(MAINX,MAINY,MAINSX,MAINSY,0.95*MAINSX,0.95*MAINSY));
}
static void
twoDdisplay()
{
gc_sound_play_ogg ("sounds/flip.wav", NULL);
gc_set_default_background(goo_canvas_get_root_item(gcomprisBoard->canvas));
if (threedgroup)
g_object_set (threedgroup, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
g_object_set (boardRootItem, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
threeDactive=FALSE;
}
static void
threeDdisplay()
{
gc_sound_play_ogg ("sounds/flip.wav", NULL);
gc_set_background(goo_canvas_get_root_item(gcomprisBoard->canvas),
"maze/maze-bg.svgz");
g_object_set (boardRootItem, "visibility",
GOO_CANVAS_ITEM_INVISIBLE, NULL);
threeDactive=TRUE;
draw3D();
}
static void
update_tux(gint direction)
{
gint rotation = 0;
GooCanvasBounds bounds;
gdouble scale;
/* Our svg image of tux is faced south */
switch(direction)
{
case EAST:
rotation = -90;
break;
case WEST:
rotation = 90;
break;
case NORTH:
rotation = 180;
break;
case SOUTH:
rotation = 0;
break;
}
goo_canvas_item_set_transform(tuxitem, NULL);
goo_canvas_item_get_bounds(tuxitem, &bounds);
scale = (gdouble) cellsize / (bounds.x2 - bounds.x1);
goo_canvas_item_scale(tuxitem, scale, scale);
goo_canvas_item_rotate( tuxitem, rotation,
(bounds.x2-bounds.x1)/2,
(bounds.y2-bounds.y1)/2);
}