//#define SECRET_ROOM /* * 15924 * | * 26724--6--26723 26722--5--26721 * / | | | / | * 26700-26--26699-51--26698 26694 2 * | 5 | | | * 26720-----26719 *26715 2 26713-----26558 * | | | / | * 26201 3 26692 1 26689 26683-----26405 * | | 1 | | | * 3 26495-----26712 7 26711 4 26710 * | / | | | / | | / | * in -> 9923 1 26679--3--26657 4 26653 3 * | | | 3 | * 1 26704--2--26703 26702--1--26701 * | / / | * 26279--2--26652-----26650 26642 */ #define NSWITCH 7 #include #include #include #include #include #include #include #include #include #include #include #include #include "3darith.h" #include "findvis.h" #include "mathutils.h" #define WINX 400 #define WINY 400 #define PLAYER_SIDES 10 #define PLAYER_RADIUS .1 #define PLAYER_HEIGHT .25 #define PLAYER_PEAK .3 #define CAMERA_RADIUS .05 #define MAXMOVE 1 #define CAMERA_ADJUST .5 #define SPOT_N 64 #define ELEVATOR_TICKS 25 extern const char *__progname; typedef enum { TT_POINT = 1, TT_BOX } TRIGTYPE; typedef struct room ROOM; typedef struct roomlist ROOMLIST; typedef struct trig TRIG; typedef struct wallinit WALLINIT; typedef struct pointinit POINTINIT; typedef struct mrpriv MRPRIV; #define WHICH_PLAYER 0x00000001 #define WHICH_CAMERA 0x00000002 #define WHICH_BOTH (WHICH_PLAYER | WHICH_CAMERA) struct wallinit { double x1; double y1; double x2; double y2; } ; struct pointinit { double x; double y; double d1; double d2; } ; struct roomlist { ROOMLIST *link; ROOM *room; } ; struct trig { TRIG *link; const char *text; TRIGTYPE type; int (*active)(void *); union { struct { XYZ pt; double radius; double amin; double amax; } point; struct { XYZ x; XYZ y; double xmin; double xmax; double ymin; double ymax; } box; } ; void (*action)(void *); void *arg; } ; struct room { const char *text; TRIG *ptriggers; TRIG *ctriggers; ROOMLIST *otherdisp; GLuint list; double floorz; void (*render_variable)(ROOM *); void *private; } ; struct mrpriv { int ix; int iy; int iz; double x0; double y0; unsigned int links; int swx; XYZ sxl; XYZ sxi; XYZ sxj; int el_active; ROOM *el_to; } ; static Display *disp; static XVisualInfo *vi; static GLXContext ctx; static Screen *scr; static int scrwidth; static int scrheight; static int depth; static Window rootwin; static Colormap cmap; static Window win; static XColor bgcolour; static XColor fgcolour; static GC gc; static struct timeval nexttick; static unsigned int frameus; static struct timeval now; static unsigned int walltex; #define WALLTEXSIZE 512 #define WALLTEXSUB 8 static unsigned char walltexinit[WALLTEXSIZE*WALLTEXSIZE] = { #include "walltexture.inc" }; static unsigned char walltexture[WALLTEXSIZE+2][WALLTEXSIZE+2][4]; static unsigned int switchtex[NSWITCH][2]; #define SWITCHTEXSIZE 32 static unsigned char switchtexture[NSWITCH][2][SWITCHTEXSIZE+2][SWITCHTEXSIZE+2][4]; /* CLOSED must be zero so that unmentioned directions are closed. */ #define LINK_CLOSED 0x0 #define LINK_OPEN 0x1 #define LINK_S0_0 0x2 #define LINK_S1_0 0x3 #define LINK_S2_0 0x4 #define LINK_S3_0 0x5 #define LINK_S4_0 0x6 #define LINK_S5_0 0x7 #define LINK_S6_0 0x8 #define LINK_S0_1 0x9 #define LINK_S1_1 0xa #define LINK_S2_1 0xb #define LINK_S3_1 0xc #define LINK_S4_1 0xd #define LINK_S5_1 0xe #define LINK_S6_1 0xf #define LINK_N 0 #define LINK_S 1 #define LINK_E 2 #define LINK_W 3 #define LINK_U 4 #define LINK_D 5 #define LINK(dir,how) ((how)<<((dir)*4)) #define LINKX(val,dir) (((val)>>((dir)*4))&0xf) /* * Switch numbers are offset by 1 (eg, S4_0 -> LINK_S3_0) so that this * table's initialization can use the 1-based numbers from the diagram * above, but the rest of the references can use the 0-based numbers * used elsewhere in the code. */ #define CLOSED LINK_CLOSED #define OPEN LINK_OPEN #define S1_0 LINK_S0_0 #define S2_0 LINK_S1_0 #define S3_0 LINK_S2_0 #define S4_0 LINK_S3_0 #define S5_0 LINK_S4_0 #define S6_0 LINK_S5_0 #define S7_0 LINK_S6_0 #define S1_1 LINK_S0_1 #define S2_1 LINK_S1_1 #define S3_1 LINK_S2_1 #define S4_1 LINK_S3_1 #define S5_1 LINK_S4_1 #define S6_1 LINK_S5_1 #define S7_1 LINK_S6_1 #define N LINK_N #define S LINK_S #define E LINK_E #define W LINK_W #define U LINK_U #define D LINK_D #define L LINK /* * The indexing here is [z][y][x], which looks odd but is designed to * make it easy to map between this initialization and the diagram * above. Not much code refers to this table, after all. */ static const unsigned int mazelinks[2][4][4] = { { { L(E,S2_1) | L(N,S1_1), L(E,OPEN) | L(W,S2_1) | L(U,OPEN), L(W,OPEN) | L(U,OPEN), L(N,S3_1) }, { L(N,S3_1) | L(U,OPEN) | L(S,S1_1) | L(W,OPEN), L(E,S3_1) | L(N,S1_1), L(N,S7_1) | L(W,S3_1) | L(U,OPEN), L(N,S4_1) | L(S,S3_1) | L(U,OPEN) }, { L(S,S3_1), L(S,S1_1) | L(N,S5_1), L(S,S7_1) | L(U,OPEN), L(N,S2_1) | L(S,S4_1) | L(E,OPEN) }, { L(E,S6_1) | L(U,OPEN), L(E,S1_1) | L(S,S5_1) | L(W,S6_1) | L(N,OPEN), L(W,S1_1) | L(U,OPEN), L(S,S2_1) } }, { { L(E,S2_0) | L(N,S1_0), L(W,S2_0) | L(D,OPEN), L(E,S1_0) | L(N,S4_0) | L(D,OPEN), L(N,S3_0) | L(W,S1_0) }, { L(E,OPEN) | L(S,S1_0) | L(N,S3_0) | L(D,OPEN), L(W,OPEN) | L(N,S1_0), L(S,S4_0) | L(D,OPEN), L(S,S3_0) | L(D,OPEN) }, { L(E,OPEN) | L(S,S3_0) | L(N,S2_0), L(W,OPEN) | L(S,S1_0) | L(N,S5_0), L(D,OPEN), L(E,OPEN) | L(N,S2_0) }, { L(E,S6_0) | L(S,S2_0) | L(D,OPEN), L(S,S5_0) | L(W,S6_0), L(E,S5_0) | L(D,OPEN), L(S,S2_0) | L(W,S5_0) } } }; #undef CLOSED #undef OPEN #undef S0_0 #undef S1_0 #undef S2_0 #undef S3_0 #undef S4_0 #undef S5_0 #undef S6_0 #undef S0_1 #undef S1_1 #undef S2_1 #undef S3_1 #undef S4_1 #undef S5_1 #undef S6_1 #undef N #undef S #undef E #undef W #undef U #undef D #undef L #if ((1<screen); scrwidth = XWidthOfScreen(scr); scrheight = XHeightOfScreen(scr); depth = vi->depth; rootwin = XRootWindowOfScreen(scr); cmap = XCreateColormap(disp,rootwin,vi->visual,AllocNone); XParseColor(disp,cmap,"#646",&bgcolour); XAllocColor(disp,cmap,&bgcolour); XParseColor(disp,cmap,"#fff",&fgcolour); XAllocColor(disp,cmap,&fgcolour); p = XCreatePixmap(disp,rootwin,1,1,depth); gc = XCreateGC(disp,p,0,0); XFreePixmap(disp,p); } static void setup_context(void) { ctx = glXCreateContext(disp,vi,0,True); if (! ctx) { fprintf(stderr,"%s: can't create GL context\n",__progname); exit(1); } } #if 0 static int clip(int, int, int) __attribute__((__const__)); static int clip(int v, int min, int max) { return((vmax)?max:v); } #endif #if 0 static double blend(double, double, double) __attribute__((__const__)); static double blend(double v1, double a, double v2) { return((a*v2)+((1-a)*v1)); } #endif /* solid circle */ static int switch_texture_0(int x, int y) { return(hypot(x-15.5,y-15.5)<13); } /* hollow square */ static int switch_texture_1(int x, int y) { return( (x > 4) && (y > 4) && (x < 27) && (y < 27) && ((x < 8) || (x > 23) || (y < 8) || (y > 23)) ); } /* big X */ static int switch_texture_2(int x, int y) { return((x>2)&&(y>2)&&(x<29)&&(y<29)&&((abs(x-y)<3)||(abs(x+y-31)<3))); } /* 6x6 checkerboard */ static int switch_texture_3(int x, int y) { return((x>3)&&(y>3)&&(x<28)&&(y<28)&&((x^y)&4)); } /* upright triangle / capital delta */ static int switch_texture_4(int x, int y) { return( ((x > 7) && (x < 24) && (y > 5) && (y < 8)) || ((y > 5) && (y < 25) && (abs((2*x)-y-6) < 3)) || ((y > 5) && (y < 25) && (abs((2*x)+y-54) < 3)) ); } /* 45° solid square */ static int switch_texture_5(int x, int y) { return( (x+y >= 19) && (x+y <= 43) && (x-y >= -12) && (x-y <= 12) ); } /* plus */ static int switch_texture_6(int x, int y) { return( ((x > 3) && (x < 28) && (y > 13) && (y < 18)) || ((y > 3) && (y < 28) && (x > 13) && (x < 18)) ); } static void setup_textures(void) { int x; int y; int i; int v; static int (* const swtfnv[NSWITCH])(int, int) = { &switch_texture_0, &switch_texture_1, &switch_texture_2, &switch_texture_3, &switch_texture_4, &switch_texture_5, &switch_texture_6 }; i = 0; for (y=WALLTEXSIZE+2-1;y>=0;y--) for (x=WALLTEXSIZE+2-1;x>=0;x--) { if ((x == 0) || (x == WALLTEXSIZE+2-1) || (y == 0) || (y == WALLTEXSIZE+2-1)) { walltexture[y][x][0] = 127; walltexture[y][x][1] = 127; walltexture[y][x][2] = 127; } else { v = walltexinit[i++]; walltexture[y][x][0] = v; walltexture[y][x][1] = v; walltexture[y][x][2] = v; } walltexture[y][x][3] = 255; } if (ASZ(swtfnv) != NSWITCH) abort(); for (i=NSWITCH-1;i>=0;i--) { for (y=SWITCHTEXSIZE+2-1;y>=0;y--) for (x=SWITCHTEXSIZE+2-1;x>=0;x--) { if ( (x == 0) || (x == WALLTEXSIZE+2-1) || (y == 0) || (y == WALLTEXSIZE+2-1) || !(*swtfnv[i])(x-1,y-1) ) { switchtexture[i][0][y][x][0] = 127; switchtexture[i][0][y][x][1] = 127; switchtexture[i][0][y][x][2] = 127; switchtexture[i][1][y][x][0] = 127; switchtexture[i][1][y][x][1] = 127; switchtexture[i][1][y][x][2] = 127; } else { switchtexture[i][0][y][x][0] = 255; switchtexture[i][0][y][x][1] = 0; switchtexture[i][0][y][x][2] = 255; switchtexture[i][1][y][x][0] = 0; switchtexture[i][1][y][x][1] = 255; switchtexture[i][1][y][x][2] = 0; } switchtexture[i][0][y][x][3] = 255; switchtexture[i][1][y][x][3] = 255; } } } static void setup_switches(void) { switchstate = 0; } static void setup_elevator(void) { elevating = 0; } static void create_window(void) { XSetWindowAttributes attr; unsigned long int attrmask; attrmask = 0; attr.background_pixel = bgcolour.pixel; attrmask |= CWBackPixel; attr.event_mask = StructureNotifyMask | VisibilityChangeMask | KeyPressMask | KeyReleaseMask; attrmask |= CWEventMask; attr.colormap = cmap; attrmask |= CWColormap; win = XCreateWindow(disp,rootwin,0,0,WINX*2,WINY,0,depth,InputOutput,vi->visual,attrmask,&attr); XMapRaised(disp,win); } static void setup_texture(int *txname, int szx, int szy, unsigned char (*data)[4]) { static const double ps[4] = { .1, 0, 0, 0 }; static const double pt[4] = { 0, .1, 0, 0 }; glGenTextures(1,txname); glBindTexture(GL_TEXTURE_2D,*txname); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,szx,szy,1,GL_RGBA,GL_UNSIGNED_BYTE,&data[0][0]); glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGendv(GL_S,GL_EYE_PLANE,&ps[0]); glTexGendv(GL_T,GL_EYE_PLANE,&pt[0]); } static void setup_gl(void) { int i; if (glXMakeCurrent(disp,(GLXDrawable)win,ctx) != True) { fprintf(stderr,"%s: can't make context current\n",__progname); exit(1); } glShadeModel(GL_SMOOTH); glClearColor(0,0,0,0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-.05,.05,-.025,.025,.05,100); glMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); glEnable(GL_POLYGON_OFFSET_FILL); glPixelStorei(GL_UNPACK_ALIGNMENT,1); setup_texture(&walltex,WALLTEXSIZE+2,WALLTEXSIZE+2,&walltexture[0][0]); for (i=NSWITCH-1;i>=0;i--) { setup_texture(&switchtex[i][0],SWITCHTEXSIZE+2,SWITCHTEXSIZE+2,&switchtexture[i][0][0][0]); setup_texture(&switchtex[i][1],SWITCHTEXSIZE+2,SWITCHTEXSIZE+2,&switchtexture[i][1][0][0]); } glCullFace(GL_BACK); glFrontFace(GL_CW); glEnable(GL_CULL_FACE); kbstate = 0; } static void dump_room(ROOM *r) { TRIG *t; ROOMLIST *rl; void dump_trig(TRIG *t) { printf(" %p [%s] ",(void *)t,t->text); switch (t->type) { default: printf("type %d (?)",(int)t->type); break; case TT_POINT: printf("POINT (%g,%g)+%g, angle [%g,%g]", t->point.pt.x, t->point.pt.y, t->point.radius, t->point.amin*RADTODEG, t->point.amax*RADTODEG ); break; case TT_BOX: { XYZ p1; XYZ p2; XYZ p3; XYZ p4; p1 = xyzadd(xyzscale(t->box.x,t->box.xmin),xyzscale(t->box.y,t->box.ymin)); p2 = xyzadd(xyzscale(t->box.x,t->box.xmin),xyzscale(t->box.y,t->box.ymax)); p3 = xyzadd(xyzscale(t->box.x,t->box.xmax),xyzscale(t->box.y,t->box.ymax)); p4 = xyzadd(xyzscale(t->box.x,t->box.xmax),xyzscale(t->box.y,t->box.ymin)); printf("BOX (%g,%g)-(%g,%g)-(%g,%g)-(%g,%g)", p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y ); } break; } printf(": action %p, arg %p\n",(void *)t->action,(void *)t->arg); } printf("Room \"%s\"\n",r->text); printf(" Floor at %g\n",r->floorz); printf(" Display list %llu\n",(unsigned long long int)r->list); printf(" Other display rooms:\n"); for (rl=r->otherdisp;rl;rl=rl->link) printf(" %s\n",rl->room->text); printf(" Player triggers:\n"); for (t=r->ptriggers;t;t=t->link) dump_trig(t); printf(" Camera triggers:\n"); for (t=r->ctriggers;t;t=t->link) dump_trig(t); } /* * The way GL transformations work: * * If the current matrix is C and the coordinates presented to (eg) * glVertex3d are V, then the transformed point P is * * P = C V * * or * * [ P(0) ] = [ C( 0) C( 4) C( 8) C(12) ] [ V(0) ] * [ P(1) ] = [ C( 1) C( 5) C( 9) C(13) ] [ V(1) ] * [ P(2) ] = [ C( 2) C( 6) C(10) C(14) ] [ V(2) ] * [ P(3) ] = [ C( 3) C( 7) C(11) C(15) ] [ V(3) ] * * Calling glMultMatrix(M) does C = C M, resulting in * * [ P(0) ] = [ C( 0) C( 4) C( 8) C(12) ] [ M( 0) M( 4) M( 8) M(12) ] [ V(0) ] * [ P(1) ] = [ C( 1) C( 5) C( 9) C(13) ] [ M( 1) M( 5) M( 9) M(13) ] [ V(1) ] * [ P(2) ] = [ C( 2) C( 6) C(10) C(14) ] [ M( 2) M( 6) M(10) M(14) ] [ V(2) ] * [ P(3) ] = [ C( 3) C( 7) C(11) C(15) ] [ M( 3) M( 7) M(11) M(15) ] [ V(3) ] */ static void rebasis(XYZ xdir, XYZ ydir, XYZ zdir) { GLdouble m[16]; m[0] = xdir.x; m[1] = xdir.y; m[2] = xdir.z; m[3] = 0; m[4] = ydir.x; m[5] = ydir.y; m[6] = ydir.z; m[7] = 0; m[8] = zdir.x; m[9] = zdir.y; m[10] = zdir.z; m[11] = 0; m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1; glMultMatrixd(&m[0]); } static void render_room(ROOM *r) { glCallList(r->list); if (r->render_variable) (*r->render_variable)(r); } static void drawstuff(void) { ROOMLIST *l; if (dbg & DBG_STATE) { printf("\n"); printf("wx = (%g,%g,%g)\n",wx.x,wx.y,wx.z); printf("wy = (%g,%g,%g)\n",wy.x,wy.y,wy.z); printf("wz = (%g,%g,%g)\n",wz.x,wz.y,wz.z); printf("ploc = (%g,%g,%g)\n",ploc.x,ploc.y,ploc.z); printf("cloc = (%g,%g,%g)\n",cloc.x,cloc.y,cloc.z); printf("ex = (%g,%g,%g)\n",ex.x,ex.y,ex.z); printf("ey = (%g,%g,%g)\n",ey.x,ey.y,ey.z); printf("ez = (%g,%g,%g)\n",ez.x,ez.y,ez.z); printf("frameus = %d (%g fps), ticks %u\n",frameus,1e6/frameus,ticks); dump_room(curroom); dbg &= ~DBG_STATE; } rebasis(wx,wy,wz); glTranslated(-cloc.x,-cloc.y,-cloc.z); render_room(curroom); for (l=curroom->otherdisp;l;l=l->link) render_room(l->room); glPushMatrix(); glTranslated(ploc.x,ploc.y,ploc.z); glCallList(playerlist); glPopMatrix(); } static void render(void) { glViewport(0,0,WINX*2,WINY); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glViewport(0,0,2*WINX,WINY); drawstuff(); glXSwapBuffers(disp,win); glXWaitGL(); glXWaitX(); } static void setup_ticks(void) { gettimeofday(&nexttick,0); ticks = 0; } /* * We have to invert the matrix * * [ rx.x ry.x rz.x ] * [ rx.y ry.y rz.y ] * [ rx.z ry.z rz.z ] * * Fortunately, because it's a rotation matrix, it's about as far from * singular as can be; its determinant is theoretically exactly 1. * * The closed-form inverse of * * [ A B C ] * [ D E F ] * [ G H I ] * * is * * [ EI-FH CH-BI BF-CE ] / * [ FG-DI AI-CG CD-AF ] / AEI+BFG+CDH-AFH-BDI-CEG * [ DH-EG BG-AH AE-BD ] / * * We save some multiplications by writing the determinant as * A(EI-FH)+B(FG-DI)+C(DH-EG), for 9 multiplies and 5 adds, versus the * above, which is 12 multiplies and 5 adds - and 6 of those 9 * multiplies can be saved by noticing that three of the terms also * appear in the first column of the (matrix) dividend. * * As mentioned above, in theory, the determinant (the divisor in the * above) is exactly 1. The reason we compute it and divide by it to * help correct for numerical errors. */ static void invert_rotations(XYZ rx, XYZ ry, XYZ rz, XYZ *ix, XYZ *iy, XYZ *iz) { double det; double xx; double xy; double xz; xx = (ry.y * rz.z) - (rz.y * ry.z); xy = (rz.y * rx.z) - (rx.y * rz.z); xz = (rx.y * ry.z) - (ry.y * rx.z); det = (rx.x * xx) + (ry.x * xy) + (rz.x * xz); *ix = (XYZ) { xx / det, xy / det, xz / det }; *iy = (XYZ) { ((rz.x * ry.z) - (ry.x * rz.z)) / det, ((rx.x * rz.z) - (rz.x * rx.z)) / det, ((ry.x * rx.z) - (rx.x * ry.z)) / det }; *iz = (XYZ) { ((ry.x * rz.y) - (rz.x * ry.y)) / det, ((rz.x * rx.y) - (rx.x * rz.y)) / det, ((rx.x * ry.y) - (ry.x * rx.y)) / det }; } static int fire_triggers(TRIG *t, XYZ l, double size) { int fire; for (;t;t=t->link) { if (!t->active || (*t->active)(t->arg)) { switch (t->type) { default: abort(); break; case TT_POINT: { double h; double a; h = hypot(l.x-t->point.pt.x,l.y-t->point.pt.y); if (h <= size+t->point.radius) { a = atan2(l.y-t->point.pt.y,l.x-t->point.pt.x); fire = ((a >= t->point.amin) && (a <= t->point.amax)); } else { fire = 0; } } break; case TT_BOX: { double dx; double dy; dx = dot(l,t->box.x); if ((dx < t->box.xmin) || (dx > t->box.xmax)) { fire = 0; } else { dy = dot(l,t->box.y); fire = ((dy >= t->box.ymin) && (dy <= size+t->box.ymax)); } } break; } if (fire) { if (dbg & DBG_FIRING) printf("Trigger %p fires in %s at %u: (%g,%g)+%g: %s\n",(void *)t,curroom->text,ticks,l.x,l.y,size,t->text); (*t->action)(t->arg); return(1); } } } return(0); } static void ptriggers(void) { if (curroom) while (fire_triggers(curroom->ptriggers,ploc,PLAYER_RADIUS)) ; } static void ctriggers(void) { if (curroom) while (fire_triggers(curroom->ctriggers,cloc,CAMERA_RADIUS)) ; } static void movepby(XYZ dir, double s) { ploc = xyzadd(ploc,xyzscale(dir,s)); ptriggers(); } static void drag_camera(void) { double cz; double ed; XYZ epd; cz = cloc.z; cloc.z = ploc.z; epd = xyzsub(cloc,ploc); ez = xyzunit(epd); ey = (XYZ){0,0,1}; ex = xyzcross(ey,ez); invert_rotations(ex,ey,ez,&wx,&wy,&wz); ed = hypot(ploc.x-cloc.x,ploc.y-cloc.y); if (ed < 1.5) { ed += CAMERA_ADJUST; if (ed > 1.5) ed = 1.5; cloc = xyzadd(ploc,xyzscale(ez,ed)); ctriggers(); } else if (ed > 5) { ed = .1; cloc = xyzadd(ploc,xyzscale(ez,ed)); ctriggers(); } else if (ed > 2) { ed -= CAMERA_ADJUST; if (ed < 2) ed = 2; cloc = xyzadd(ploc,xyzscale(ez,ed)); ctriggers(); } cloc.z = cz; } static void czfloat(void) { double des; double inc; double delta; inc = .6e-6 * frameus; des = ploc.z + .75; delta = fabs(cloc.z-des); while (delta > .5) { inc *= 2; delta /= 2; } if (cloc.z < des) { cloc.z += inc; if (cloc.z > des) cloc.z = des; } else if (cloc.z > des) { cloc.z -= inc; if (cloc.z < des) cloc.z = des; } ctriggers(); } static void movecby(XYZ dir, double s) { cloc = xyzadd(cloc,xyzscale(dir,s)); ctriggers(); } static void action_gate(void *tov) { curroom = tov; ploc.z = curroom->floorz; if (dbg & DBG_GATE) dump_room(curroom); } static void motion(void) { double f; if (elevating) { ploc.z += elevator_dz; cloc.z += elevator_dz; elevating --; if (! elevating) { ((MRPRIV *)elsrc->private)->el_active = 1; action_gate(eldst); } return; } if (kbstate & KBS_SHIFT) f = .375; else if (kbstate & KBS_CTRL) f = .06; else f = .15; switch (kbstate & (KBS_ML|KBS_MR)) { case KBS_ML: movepby(ex,-f); movecby(ex,-f); break; case KBS_MR: movepby(ex,f); movecby(ex,f); break; } switch (kbstate & (KBS_MF|KBS_MB)) { case KBS_MF: movepby(ez,-f); break; case KBS_MB: movepby(ez,f); break; } if (kbstate & (KBS_ML|KBS_MR|KBS_MF|KBS_MB)) { drag_camera(); } switch (kbstate & (KBS_RL|KBS_RR)) { case KBS_RL: movecby(ex,f); drag_camera(); break; case KBS_RR: movecby(ex,-f); drag_camera(); break; } czfloat(); } static void toggle_culling(void) { switch (glIsEnabled(GL_CULL_FACE)) { case GL_FALSE: glEnable(GL_CULL_FACE); break; case GL_TRUE: glDisable(GL_CULL_FACE); break; default: fprintf(stderr,"%s: glIsEnabled returned undocumented result\n",__progname); exit(1); break; } } static void keystroke(XKeyEvent *ev, char updn) { KeySym ks; unsigned int bit; int magic; ks = XLookupKeysym(ev,0); bit = 0; magic = ((kbstate & (KBS_LSHF | KBS_RSHF)) == (KBS_LSHF | KBS_RSHF)) && (kbstate & KBS_CTRL); switch (ks) { case XK_w: case XK_W: bit = KBS_MF; break; case XK_s: case XK_S: bit = KBS_MB; break; case XK_a: case XK_A: bit = KBS_ML; break; case XK_d: case XK_D: bit = KBS_MR; break; case XK_u: case XK_U: bit = KBS_RL; break; case XK_i: case XK_I: bit = KBS_RR; break; case XK_f: case XK_F: if (magic && (updn == 'd')) dbg ^= DBG_FIRING; break; case XK_g: case XK_G: if (magic && (updn == 'd')) dbg ^= DBG_GATE; break; case XK_c: case XK_C: if (magic && (updn == 'd')) toggle_culling(); break; case XK_1: if (magic && (updn == 'd')) switchstate ^= 1; break; case XK_2: if (magic && (updn == 'd')) switchstate ^= 2; break; case XK_3: if (magic && (updn == 'd')) switchstate ^= 4; break; case XK_4: if (magic && (updn == 'd')) switchstate ^= 8; break; case XK_5: if (magic && (updn == 'd')) switchstate ^= 16; break; case XK_6: if (magic && (updn == 'd')) switchstate ^= 32; break; case XK_7: if (magic && (updn == 'd')) switchstate ^= 64; break; case XK_Shift_L: bit = KBS_LSHF; break; case XK_Shift_R: bit = KBS_RSHF; break; case XK_Control_L: bit = KBS_LCTL; break; case XK_Control_R: bit = KBS_RCTL; break; case XK_q: case XK_Q: if ((updn == 'u') && (kbstate & KBS_SHIFT)) exit(0); break; case XK_p: case XK_P: if (updn == 'd') { if (magic) dbg ^= DBG_PUSH; else dbg |= DBG_STATE; } break; } switch (updn) { case 'u': kbstate &= ~bit; break; case 'd': kbstate |= bit; break; default: abort(); break; } } static void events(void) { XEvent ev; while (XPending(disp)) { XNextEvent(disp,&ev); switch (ev.type) { default: /* XXX ignore */ break; case KeyPress: /* XKeyPressedEvent - XKeyEvent - xkey */ keystroke(&ev.xkey,'d'); break; case KeyRelease: /* XKeyReleasedEvent - XKeyEvent - xkey */ keystroke(&ev.xkey,'u'); break; case MappingNotify: /* XMappingEvent - xmapping */ XRefreshKeyboardMapping(&ev.xmapping); break; } } } static void await(void) { int ms; nexttick.tv_usec += frameus; if (nexttick.tv_usec >= 1000000) { nexttick.tv_usec -= 1000000; nexttick.tv_sec ++; } gettimeofday(&now,0); if ( (now.tv_sec > nexttick.tv_sec) || ( (now.tv_sec == nexttick.tv_sec) && (now.tv_usec >= nexttick.tv_usec) ) ) { frameus += 1000; nexttick = now; return; } ms = ((nexttick.tv_sec - now.tv_sec) * 1000) + 1 + (nexttick.tv_usec / 1000) - (now.tv_usec / 1000); if (ms < 20) { frameus += 10000; } else { frameus -= 10; } if (ms < 1) ms = 1; poll(0,0,ms); gettimeofday(&now,0); } static void tick(void) { motion(); render(); events(); await(); ticks ++; } static void setup_view(void) { ploc = (XYZ){0,0,0}; ex = (XYZ){1,0,0}; ey = (XYZ){0,1,0}; ez = xyzcross(ex,ey); invert_rotations(ex,ey,ez,&wx,&wy,&wz); cloc = (XYZ){0,-2,.75}; drag_camera(); } static void setup_input(void) { XGrabKeyboard(disp,win,False,GrabModeAsync,GrabModeAsync,CurrentTime); } static void setup_player_list(void) { int i; double s[PLAYER_SIDES+1]; double c[PLAYER_SIDES+1]; for (i=0;i<=PLAYER_SIDES;i++) { s[i] = sindeg(i*360.0/PLAYER_SIDES) * PLAYER_RADIUS; c[i] = cosdeg(i*360.0/PLAYER_SIDES) * PLAYER_RADIUS; } playerlist = glGenLists(1); glNewList(playerlist,GL_COMPILE); glColor3f(1,1,1); glBegin(GL_QUAD_STRIP); for (i=0;i<=PLAYER_SIDES;i++) { glVertex3d(c[i],s[i],0); glVertex3d(c[i],s[i],PLAYER_HEIGHT); } glEnd(); glBegin(GL_TRIANGLE_FAN); glVertex3d(0,0,PLAYER_PEAK); for (i=PLAYER_SIDES;i>=0;i--) { glVertex3d(c[i],s[i],PLAYER_HEIGHT); } glEnd(); glEndList(); } static void setup_lists(void) { setup_player_list(); } static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: extra argument `%s'\n",__progname,*av); errs = 1; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs = 1; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-seed")) { WANTARG(); seed = strtoul(av[skip],0,0); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); } static XYZ wall_push(XYZ l, TRIG *t, double size) { double dx; if (t->type != TT_BOX) abort(); dx = dot(l,t->box.x); return(xyzadd(xyzadd(xyzscale(t->box.x,dx),xyzscale(t->box.y,t->box.ymax+(size*1.01))),(XYZ){0,0,l.z})); } static XYZ corner_push(XYZ l, TRIG *t, double size) { XYZ d; double n; if (t->type != TT_POINT) abort(); d = xyzsub(l,(XYZ){t->point.pt.x,t->point.pt.y,l.z}); n = xyzlength(d); return(xyzadd(xyzadd(t->point.pt,xyzscale(d,(t->point.radius+(size*1.01))/n)),(XYZ){0,0,l.z})); } static void action_wall_push_p(void *tv) { if (dbg & DBG_PUSH) printf("wall_push_p: ploc (%g,%g,%g) -> ",ploc.x,ploc.y,ploc.z); ploc = wall_push(ploc,tv,PLAYER_RADIUS); if (dbg & DBG_PUSH) printf("(%g,%g,%g)\n",ploc.x,ploc.y,ploc.z); } static void action_wall_push_c(void *tv) { if (dbg & DBG_PUSH) printf("wall_push_c: cloc (%g,%g,%g) -> ",cloc.x,cloc.y,cloc.z); cloc = wall_push(cloc,tv,CAMERA_RADIUS); if (dbg & DBG_PUSH) printf("(%g,%g,%g)\n",cloc.x,cloc.y,cloc.z); } static void action_corner_push_p(void *tv) { if (dbg & DBG_PUSH) printf("corner_push_p: ploc (%g,%g,%g) -> ",ploc.x,ploc.y,ploc.z); ploc = corner_push(ploc,tv,PLAYER_RADIUS); if (dbg & DBG_PUSH) printf("(%g,%g,%g)\n",ploc.x,ploc.y,ploc.z); } static void action_corner_push_c(void *tv) { if (dbg & DBG_PUSH) printf("corner_push_c: cloc (%g,%g,%g) -> ",cloc.x,cloc.y,cloc.z); cloc = corner_push(cloc,tv,CAMERA_RADIUS); if (dbg & DBG_PUSH) printf("(%g,%g,%g)\n",cloc.x,cloc.y,cloc.z); } static TRIG *add_wall(ROOM *r, double x1, double y1, double x2, double y2, unsigned int which) { TRIG *t; char *s; asprintf(&s,"WALL (%g,%g)-(%g,%g)",x1,y1,x2,y2); if (which & WHICH_PLAYER) { t = malloc(sizeof(TRIG)); t->link = r->ptriggers; r->ptriggers = t; t->text = s; t->type = TT_BOX; t->active = 0; t->box.x = xyzunit((XYZ){x2-x1,y2-y1,0}); t->box.y = xyzcross((XYZ){0,0,1},t->box.x); t->box.xmin = dot(t->box.x,(XYZ){x1,y1,0}); t->box.xmax = dot(t->box.x,(XYZ){x2,y2,0}); t->box.ymax = dot(t->box.y,(XYZ){x1,y1,0}); t->box.ymin = t->box.ymax - MAXMOVE; t->action = &action_wall_push_p; t->arg = t; } if (which & WHICH_CAMERA) { t = malloc(sizeof(TRIG)); t->link = r->ctriggers; r->ctriggers = t; t->text = s; t->type = TT_BOX; t->active = 0; t->box.x = xyzunit((XYZ){x2-x1,y2-y1,0}); t->box.y = xyzcross((XYZ){0,0,1},t->box.x); t->box.xmin = dot(t->box.x,(XYZ){x1,y1,0}); t->box.xmax = dot(t->box.x,(XYZ){x2,y2,0}); t->box.ymax = dot(t->box.y,(XYZ){x1,y1,0}); t->box.ymin = t->box.ymax - MAXMOVE; t->action = &action_wall_push_c; t->arg = t; } return(t); } static TRIG *add_corner(ROOM *r, double x, double y, double rad, double degmin, double degmax, unsigned int which) { TRIG *t; char *s; asprintf(&s,"CORNER (%g,%g)+%g",x,y,rad); if (which & WHICH_PLAYER) { t = malloc(sizeof(TRIG)); t->link = r->ptriggers; r->ptriggers = t; t->text = s; t->type = TT_POINT; t->active = 0; t->point.pt = (XYZ){x,y,0}; t->point.radius = rad; t->point.amin = degmin * DEGTORAD; t->point.amax = degmax * DEGTORAD; t->action = &action_corner_push_p; t->arg = t; } if (which & WHICH_CAMERA) { t = malloc(sizeof(TRIG)); t->link = r->ctriggers; r->ctriggers = t; t->text = s; t->type = TT_POINT; t->active = 0; t->point.pt = (XYZ){x,y,0}; t->point.radius = rad; t->point.amin = degmin * DEGTORAD; t->point.amax = degmax * DEGTORAD; t->action = &action_corner_push_c; t->arg = t; } return(t); } static TRIG *add_gate(ROOM *r, double x1, double y1, double x2, double y2) { TRIG *t; char *s; asprintf(&s,"GATE (%g,%g)-(%g,%g)",x1,y1,x2,y2); t = malloc(sizeof(TRIG)); t->link = r->ptriggers; r->ptriggers = t; t->text = s; t->type = TT_BOX; t->active = 0; t->box.x = xyzunit((XYZ){x2-x1,y2-y1,0}); t->box.y = xyzcross((XYZ){0,0,1},t->box.x); t->box.xmin = dot(t->box.x,(XYZ){x1,y1,0}); t->box.xmax = dot(t->box.x,(XYZ){x2,y2,0}); t->box.ymax = dot(t->box.y,(XYZ){x1,y1,0}) - PLAYER_RADIUS; t->box.ymin = t->box.ymax - MAXMOVE; t->action = &action_gate; t->arg = 0; return(t); } static TRIG *add_switch(ROOM *r, double x, double y, double rad) { TRIG *t; char *s; asprintf(&s,"SWITCH (%g,%g)+%g",x,y,rad); t = malloc(sizeof(TRIG)); t->link = r->ptriggers; r->ptriggers = t; t->text = s; t->type = TT_POINT; t->active = 0; t->point.pt = (XYZ){x,y,0}; t->point.radius = rad; t->point.amin = - 1.01*M_PI; t->point.amax = 1.01*M_PI; t->action = 0; t->arg = 0; return(t); } static TRIG *add_zone(ROOM *r, double x0, double y0, double ix, double iy, double jx, double jy) { TRIG *t; char *s; asprintf(&s,"ZONE (%g,%g)+((%g,%g),(%g,%g))",x0,y0,ix,iy,jx,jy); t = malloc(sizeof(TRIG)); t->link = r->ptriggers; r->ptriggers = t; t->text = s; t->type = TT_BOX; t->active = 0; t->box.x = xyzunit((XYZ){ix,iy,0}); t->box.y = xyzunit((XYZ){jx,jy,0}); t->box.xmin = dot(t->box.x,(XYZ){x0,y0,0}); t->box.xmax = t->box.xmin + hypot(ix,iy); t->box.ymin = dot(t->box.y,(XYZ){x0,y0,0}); t->box.ymax = t->box.ymin + hypot(jx,jy); t->action = 0; t->arg = 0; return(t); } static void wall_rects(int n, ...) { va_list ap; XYZ p1; XYZ xdir; XYZ ydir; double x1; double x2; double y1; double y2; double x0; double y0; XYZ p2; XYZ p3; XYZ p4; va_start(ap,n); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,walltex); glBegin(GL_QUADS); for (;n>0;n--) { p1 = va_arg(ap,XYZ); xdir = va_arg(ap,XYZ); ydir = va_arg(ap,XYZ); x1 = va_arg(ap,double); x2 = va_arg(ap,double); y1 = va_arg(ap,double); y2 = va_arg(ap,double); //printf("wall_rects: n=%d p1=(%g,%g,%g) xdir=(%g,%g,%g) ydir=(%g,%g,%g) x1=%g y1=%g x2=%g y2=%g\n", // n, // p1.x, p1.y, p1.z, // xdir.x, xdir.y, xdir.z, // ydir.x, ydir.y, ydir.z, // x1, y1, x2, y2 ); p2 = xyzadd(p1,xyzscale(ydir,y2-y1)); p3 = xyzadd(p2,xyzscale(xdir,x2-x1)); p4 = xyzadd(p1,xyzscale(xdir,x2-x1)); x0 = (random() % WALLTEXSUB) / (double)WALLTEXSUB; y0 = (random() % WALLTEXSUB) / (double)WALLTEXSUB; glTexCoord2d(x0+(x1/WALLTEXSUB),y0+(y1/WALLTEXSUB)); glVertex3d(p1.x,p1.y,p1.z); glTexCoord2d(x0+(x1/WALLTEXSUB),y0+(y2/WALLTEXSUB)); glVertex3d(p2.x,p2.y,p2.z); glTexCoord2d(x0+(x2/WALLTEXSUB),y0+(y2/WALLTEXSUB)); glVertex3d(p3.x,p3.y,p3.z); glTexCoord2d(x0+(x2/WALLTEXSUB),y0+(y1/WALLTEXSUB)); glVertex3d(p4.x,p4.y,p4.z); } glEnd(); glDisable(GL_TEXTURE_2D); va_end(ap); } static void add_otherdisp(ROOM *r, ROOM *o) { ROOMLIST *l; l = malloc(sizeof(ROOMLIST)); l->link = r->otherdisp; r->otherdisp = l; l->room = o; } static ROOM *build_room(double floor, const char *text, const WALLINIT *wi, int nwi, const POINTINIT *pi, int npi) { ROOM *r; int i; r = malloc(sizeof(ROOM)); r->text = text; r->otherdisp = 0; r->ptriggers = 0; r->ctriggers = 0; r->floorz = floor; for (i=nwi-1;i>=0;i--) add_wall(r,wi[i].x1,wi[i].y1,wi[i].x2,wi[i].y2,WHICH_BOTH); for (i=npi-1;i>=0;i--) add_corner(r,pi[i].x,pi[i].y,0,pi[i].d1,pi[i].d2,WHICH_BOTH); r->render_variable = 0; return(r); } static void draw_floor(double z, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { glColor3f(.333,.333,.333); glVertex3d(x1,y1,z); glVertex3d(x2,y2,z); glVertex3d(x3,y3,z); glVertex3d(x4,y4,z); } static void draw_ceiling(double z, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { glColor3f(.667,.667,.667); glVertex3d(x1,y1,z); glVertex3d(x2,y2,z); glVertex3d(x3,y3,z); glVertex3d(x4,y4,z); } static void draw_switch_status(int sw, XYZ loc, XYZ xdir, XYZ ydir) { XYZ zdir; zdir = xyzcross(xdir,ydir); glPushMatrix(); glTranslated(loc.x,loc.y,loc.z); rebasis(xdir,ydir,zdir); glBegin(GL_QUAD_STRIP); glColor3f(.2,.2,.2); glVertex3d(-.5,0,1); glVertex3d(-.5,.25,1); glVertex3d(-.5,0,2); glVertex3d(-.5,.25,2); glVertex3d(.5,0,2); glVertex3d(.5,.25,2); glVertex3d(.5,0,1); glVertex3d(.5,.25,1); glVertex3d(-.5,0,1); glVertex3d(-.5,.25,1); glEnd(); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,switchtex[sw][(switchstate>>sw)&1]); glBegin(GL_QUADS); glTexCoord2d(0,0); glVertex3d(-.5,0,1); glTexCoord2d(0,1); glVertex3d(-.5,0,2); glTexCoord2d(1,1); glVertex3d(.5,0,2); glTexCoord2d(1,0); glVertex3d(.5,0,1); glEnd(); glDisable(GL_TEXTURE_2D); glPopMatrix(); } static void draw_switch_controls(int sw, XYZ loc, XYZ xdir, XYZ ydir) { XYZ zdir; zdir = xyzcross(xdir,ydir); glPushMatrix(); glTranslated(loc.x,loc.y,loc.z); rebasis(xdir,ydir,zdir); glEnable(GL_TEXTURE_2D); glPolygonOffset(0,-10); glBindTexture(GL_TEXTURE_2D,switchtex[sw][0]); glBegin(GL_QUADS); glTexCoord2d(0,0); glVertex3d(-.6,-.2,0); glTexCoord2d(0,1); glVertex3d(-.6,.2,0); glTexCoord2d(1,1); glVertex3d(-.2,.2,0); glTexCoord2d(1,0); glVertex3d(-.2,-.2,0); glEnd(); glBindTexture(GL_TEXTURE_2D,switchtex[sw][1]); glBegin(GL_QUADS); glTexCoord2d(0,0); glVertex3d(.2,-.2,0); glTexCoord2d(0,1); glVertex3d(.2,.2,0); glTexCoord2d(1,1); glVertex3d(.6,.2,0); glTexCoord2d(1,0); glVertex3d(.6,-.2,0); glEnd(); glPolygonOffset(0,0); glDisable(GL_TEXTURE_2D); glPopMatrix(); } static void drawspot(XYZ o, XYZ i, XYZ j) { XYZ k; int n; double s; double c; k = xyzunit(xyzcross(i,j)); glPushMatrix(); glTranslated(o.x,o.y,o.z); rebasis(i,j,k); glBegin(GL_TRIANGLE_FAN); glVertex3d(0,0,0); for (n=SPOT_N;n>=0;n--) { s = sin((n*2*M_PI)/SPOT_N); c = cos((n*2*M_PI)/SPOT_N); glVertex3d(c,s,0); } glEnd(); glPopMatrix(); } static void render_lobby(ROOM *r __attribute__((__unused__))) { draw_switch_status(0,(XYZ){4,14.75,0},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_controls(0,(XYZ){4,14.75,0},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_status(1,(XYZ){5.5,14.75,0},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_controls(1,(XYZ){5.5,14.75,0},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_status(2,(XYZ){7,14.75,0},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_controls(2,(XYZ){7,14.75,0},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_status(3,(XYZ){8.5,14.75,0},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_controls(3,(XYZ){8.5,14.75,0},(XYZ){1,0,0},(XYZ){0,1,0}); #ifdef SECRET_ROOM draw_switch_status(4,(XYZ){2,-14.75,-1},(XYZ){-1,0,0},(XYZ){0,-1,0}); draw_switch_status(5,(XYZ){0,-14.75,-1},(XYZ){-1,0,0},(XYZ){0,-1,0}); draw_switch_status(6,(XYZ){-2,-14.75,-1},(XYZ){-1,0,0},(XYZ){0,-1,0}); #endif } #define SWITCH_FUNCTIONS(n) \ static int active_sw##n##_is_1(void *arg __attribute__((__unused__))) { return((1U<<(n))&switchstate); } \ static void action_sw##n##_to_0(void *arg __attribute__((__unused__))) { switchstate &= ~(1U << (n)); } \ static int active_sw##n##_is_0(void *arg __attribute__((__unused__))) { return((1U<<(n))&~switchstate); } \ static void action_sw##n##_to_1(void *arg __attribute__((__unused__))) { switchstate |= 1U << (n); } SWITCH_FUNCTIONS(0) SWITCH_FUNCTIONS(1) SWITCH_FUNCTIONS(2) SWITCH_FUNCTIONS(3) SWITCH_FUNCTIONS(4) SWITCH_FUNCTIONS(5) SWITCH_FUNCTIONS(6) static int (*active_fns[])(void *) = { [LINK_S0_0] = &active_sw0_is_0, [LINK_S0_1] = &active_sw0_is_1, [LINK_S1_0] = &active_sw1_is_0, [LINK_S1_1] = &active_sw1_is_1, [LINK_S2_0] = &active_sw2_is_0, [LINK_S2_1] = &active_sw2_is_1, [LINK_S3_0] = &active_sw3_is_0, [LINK_S3_1] = &active_sw3_is_1, [LINK_S4_0] = &active_sw4_is_0, [LINK_S4_1] = &active_sw4_is_1, [LINK_S5_0] = &active_sw5_is_0, [LINK_S5_1] = &active_sw5_is_1, [LINK_S6_0] = &active_sw6_is_0, [LINK_S6_1] = &active_sw6_is_1 }; static void draw_elevator_arrow_d(double x0, double y0, double z) { glBegin(GL_TRIANGLES); glVertex3d(x0+1.7,y0+9.7,z+.5); glVertex3d(x0+1.5,y0+9.5,z); glVertex3d(x0+1.3,y0+9.3,z+.5); glEnd(); glBegin(GL_QUADS); glVertex3d(x0+1.4,y0+9.4,z+.5); glVertex3d(x0+1.4,y0+9.4,z+1); glVertex3d(x0+1.6,y0+9.6,z+1); glVertex3d(x0+1.6,y0+9.6,z+.5); glEnd(); } static void draw_elevator_arrow_u(double x0, double y0, double z) { glBegin(GL_TRIANGLES); glVertex3d(x0+1.3,y0+9.3,z+.5); glVertex3d(x0+1.5,y0+9.5,z+1); glVertex3d(x0+1.7,y0+9.7,z+.5); glEnd(); glBegin(GL_QUADS); glVertex3d(x0+1.4,y0+9.4,z); glVertex3d(x0+1.4,y0+9.4,z+.5); glVertex3d(x0+1.6,y0+9.6,z+.5); glVertex3d(x0+1.6,y0+9.6,z); glEnd(); } static void draw_elevator_trigger(double x0, double y0, double z) { drawspot((XYZ){x0+2,y0+9,z},(XYZ){.3,0,0},(XYZ){0,.3,0}); } static void draw_goal_award(double x0, double y0, double z) { drawspot((XYZ){x0+8,y0+3,z},(XYZ){1,0,0},(XYZ){0,1,0}); } static void draw_elevator_border(double x0, double y0, double z) { glVertex3d(x0,y0,z); glVertex3d(x0,y0+.05,z); glVertex3d(x0+4,y0+.05,z); glVertex3d(x0+4,y0,z); glVertex3d(x0,y0+3.95,z); glVertex3d(x0,y0+4,z); glVertex3d(x0+4,y0+4,z); glVertex3d(x0+4,y0+3.95,z); glVertex3d(x0,y0,z); glVertex3d(x0,y0+4,z); glVertex3d(x0+.05,y0+4,z); glVertex3d(x0+.05,y0,z); glVertex3d(x0+3.95,y0,z); glVertex3d(x0+3.95,y0+4,z); glVertex3d(x0+4,y0+4,z); glVertex3d(x0+4,y0,z); } static void render_maze(ROOM *r) { MRPRIV *p; void render_door(int dir) { double x0; double y0; double ix; double iy; double jx; double jy; int v; int stn; int stx; int (*open)(void *); v = LINKX(p->links,dir); switch (v) { case LINK_CLOSED: case LINK_OPEN: return; break; } stn = link_swno[v]; stx = link_swstate[v]; open = active_fns[v]; jx = - dirdx[dir]; jy = - dirdy[dir]; ix = jy; iy = - jx; x0 = p->x0 + 5.5 - (4.5 * ix) - (4.5 * jx); y0 = p->y0 + 5.5 - (4.5 * iy) - (4.5 * jy); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,switchtex[stn][stx]); glBegin(GL_QUADS); if ((*open)(0)) { glTexCoord2d(0,0); glVertex3d(x0+(5.5*ix)+(.1*jx),y0+(5.5*iy)+(.1*jy),r->floorz); glTexCoord2d(0,1); glVertex3d(x0+(5.5*ix)+(.1*jx),y0+(5.5*iy)+(.1*jy),r->floorz+1); glTexCoord2d(.5,1); glVertex3d(x0+(5*ix)+(.1*jx),y0+(5*iy)+(.1*jy),r->floorz+1); glTexCoord2d(.5,0); glVertex3d(x0+(5*ix)+(.1*jx),y0+(5*iy)+(.1*jy),r->floorz); glTexCoord2d(.5,0); glVertex3d(x0+(4*ix)+(.1*jx),y0+(4*iy)+(.1*jy),r->floorz); glTexCoord2d(.5,1); glVertex3d(x0+(4*ix)+(.1*jx),y0+(4*iy)+(.1*jy),r->floorz+1); glTexCoord2d(1,1); glVertex3d(x0+(3.5*ix)+(.1*jx),y0+(3.5*iy)+(.1*jy),r->floorz+1); glTexCoord2d(1,0); glVertex3d(x0+(3.5*ix)+(.1*jx),y0+(3.5*iy)+(.1*jy),r->floorz); } else { glTexCoord2d(0,0); glVertex3d(x0+(5*ix)+(.1*jx),y0+(5*iy)+(.1*jy),r->floorz); glTexCoord2d(0,1); glVertex3d(x0+(5*ix)+(.1*jx),y0+(5*iy)+(.1*jy),r->floorz+1); glTexCoord2d(1,1); glVertex3d(x0+(4*ix)+(.1*jx),y0+(4*iy)+(.1*jy),r->floorz+1); glTexCoord2d(1,0); glVertex3d(x0+(4*ix)+(.1*jx),y0+(4*iy)+(.1*jy),r->floorz); } glEnd(); glDisable(GL_TEXTURE_2D); } p = r->private; render_door(LINK_N); render_door(LINK_S); render_door(LINK_E); render_door(LINK_W); if (p->swx >= 0) { draw_switch_status(p->swx,p->sxl,p->sxi,p->sxj); draw_switch_controls(p->swx,p->sxl,p->sxi,p->sxj); } if (elevating && (r == elsrc)) { glColor3f(0,0,1); glPolygonOffset(0,-10); glBegin(GL_QUADS); draw_elevator_border(p->x0+1,p->y0+6,ploc.z); glEnd(); draw_elevator_trigger(p->x0,p->y0,ploc.z); glPolygonOffset(0,0); glBegin(GL_QUADS); draw_floor(ploc.z,p->x0+1,p->y0+6,p->x0+1,p->y0+10,p->x0+5,p->y0+10,p->x0+5,p->y0+6); glEnd(); } else if (elevating && (r == eldst)) { } else if (LINKX(p->links,LINK_D) != LINK_CLOSED) { glColor3f(0,0,1); glPolygonOffset(0,-10); if (p->el_active && (r->floorz == curroom->floorz)) draw_elevator_arrow_d(p->x0,p->y0,r->floorz); glBegin(GL_QUADS); draw_elevator_border(p->x0+1,p->y0+6,r->floorz); glEnd(); draw_elevator_trigger(p->x0,p->y0,r->floorz); glPolygonOffset(0,0); glBegin(GL_QUADS); draw_floor(r->floorz,p->x0+1,p->y0+6,p->x0+1,p->y0+10,p->x0+5,p->y0+10,p->x0+5,p->y0+6); glEnd(); } if ((LINKX(p->links,LINK_U) != LINK_CLOSED) && !(elevating && ((r == elsrc) || (r == eldst)))) { glColor3f(0,0,1); glPolygonOffset(0,-10); if (p->el_active && (r->floorz == curroom->floorz)) draw_elevator_arrow_u(p->x0,p->y0,r->floorz); glBegin(GL_QUADS); draw_elevator_border(p->x0+1,p->y0+6,r->floorz); glEnd(); draw_elevator_trigger(p->x0,p->y0,r->floorz); glPolygonOffset(0,0); glBegin(GL_QUADS); glEnd(); } } static void draw_elevator_well(double x0, double y0, double z0) { glColor3f(.2,.2,.2); glBegin(GL_QUAD_STRIP); glVertex3d(x0,y0,z0); glVertex3d(x0,y0,z0+1); glVertex3d(x0,y0+4,z0); glVertex3d(x0,y0+4,z0+1); glVertex3d(x0+4,y0+4,z0); glVertex3d(x0+4,y0+4,z0+1); glVertex3d(x0+4,y0,z0); glVertex3d(x0+4,y0,z0+1); glVertex3d(x0,y0,z0); glVertex3d(x0,y0,z0+1); glEnd(); } static void add_maze_otherdisp(ROOM *rm, ROOM *(*mr)[4][2], int dir) { ROOM *r; MRPRIV *p; int x; int y; int z; r = rm; p = r->private; x = p->ix; y = p->iy; z = p->iz; while (1) { if (LINKX(p->links,dir) == LINK_CLOSED) break; x += dirdx[dir]; y += dirdy[dir]; if ((x < 0) || (x > 3) || (y < 0) || (y > 3)) break; r = mr[x][y][z]; add_otherdisp(rm,r); p = r->private; } } static void start_elevator(ROOM *r, int dir) { elsrc = r; eldst = ((MRPRIV *)r->private)->el_to; elevating = ELEVATOR_TICKS; elevator_dz = dir * 2.0 / ELEVATOR_TICKS; ((MRPRIV *)elsrc->private)->el_active = 0; ((MRPRIV *)eldst->private)->el_active = 0; } static void action_elevator_up(void *rv) { start_elevator(rv,1); } static void action_elevator_dn(void *rv) { start_elevator(rv,-1); } static int active_elevator_active(void *rv) { return(((MRPRIV *)((ROOM *)rv)->private)->el_active); } static int active_elevator_inactive(void *rv) { return(!((MRPRIV *)((ROOM *)rv)->private)->el_active); } static void action_elevator_activate(void *rv) { ((MRPRIV *)((ROOM *)rv)->private)->el_active = 1; } static void action_take_slide(void *rv __attribute__((__unused__))) { // XXX animate something here instead of just teleporting ploc = (XYZ){8,-4,0}; cloc = (XYZ){9.75,-4,.75}; drag_camera(); action_gate(lobby); } static void action_win(void *arg __attribute__((__unused__))) { printf("You win!\n"); exit(0); } static void setup_maze(void) { static const WALLINIT wi_lobby[] = { { -16, 16, -16, -6 }, #ifdef SECRET_ROOM { -17, -5, -.5, -5 }, { -.5, -5, -.5, -8 }, { -.5, -8, -5, -8 }, { -4, -7, -4, -16 }, { -5, -15, 5, -15 }, { 4, -16, 4, -7 }, { 5, -8, .5, -8 }, { .5, -8, .5, -5 }, { .5, -5, 11, -5 }, #else { -17, -5, 11, -5 }, #endif { 10, -6, 10, 16 }, { 11, 15, -11, 15 }, { -10, 16, -10, -1 }, { -10, -1, -12, -1 }, { -12, -1, -12, 16 } }; static const POINTINIT pi_lobby[] = { { -10, -1, -90, 0 }, #ifdef SECRET_ROOM { -.5, -5, 0, 90 }, { -.5, -8, -90, 0 }, { .5, -8, -180, -90 }, { .5, -.5, 90, 180 }, #endif { -12, -1, -180, -90 } }; static const WALLINIT wi_entrance[] = { { -12, -6, -12, 11 }, { -12, 11, -10, 11 }, { -10, 11, -10, -6 }, { -11, -5, 11, -5 }, { 10, -6, 10, -.5 }, { 10, -.5, 12, -.5 }, { 12, .5, 10, .5 }, { 10, .5, 10, 16 }, { 11, 15, -17, 15 }, { -16, 16, -16, -6 } }; static const POINTINIT pi_entrance[] = { { -10, 11, 0, 90 }, { -12, 11, 90, 180 }, { 10, -.5, 90, 180 }, { 10, .5, -180, -90 } }; static const WALLINIT wi_landing_l[] = { { -16, 16, -16, -6 }, { -17, -5, 0, -5 }, { -10, -1, -12, -1 }, { -12, -1, -12, 11 } }; static const POINTINIT pi_landing_l[] = { { -10, -1, -90, 0 }, { -12, -1, -180, -90 } }; static const WALLINIT wi_landing_u[] = { { 0, 15, -17, 15 }, { -16, 16, -16, -6 }, { -12, -1, -12, 11 }, { -12, 11, -10, 11 } }; static const POINTINIT pi_landing_u[] = { { -10, 11, 0, 90 }, { -12, 11, 90, 180 } }; static const WALLINIT wi_steps[] = { { -16, 16, -16, -6 }, { -10, -1, -12, -1 }, { -12, -1, -12, 11 }, { -12, 11, -10, 11 } }; static const POINTINIT pi_steps[] = { { -12, -1, -180, 0 }, { -12, 11, 0, 180 } }; static const WALLINIT wi_sw4[] = { { 53, 10.5, 56, 10.5 }, { 56, 10.5, 56, 5.5 }, { 55, 6.5, 66, 6.5 }, { 65, 5.5, 65, 16.5 }, { 66, 15.5, 55, 15.5 }, { 56, 16.5, 56, 11.5 }, { 56, 11.5, 53, 11.5 } }; static const POINTINIT pi_sw4[] = { { 56, 10.5, 0, 90 }, { 56, 11.5, -90, 0 } }; static const WALLINIT wi_sw5[] = { { 28, 25.5, 28, 28.5 }, { 28, 28.5, 33, 28.5 }, { 32, 27.5, 32, 38.5 }, { 33, 37.5, 22, 37.5 }, { 23, 38.5, 23, 27.5 }, { 22, 28.5, 27, 28.5 }, { 27, 28.5, 27, 25.5 } }; static const POINTINIT pi_sw5[] = { { 28, 28.5, 90, 180 }, { 27, 28.5, 0, 90 } }; static const WALLINIT wi_sw6[] = { { 53, 10.5, 56, 10.5 }, { 56, 10.5, 56, 5.5 }, { 55, 6.5, 66, 6.5 }, { 65, 5.5, 65, 16.5 }, { 66, 15.5, 55, 15.5 }, { 56, 16.5, 56, 11.5 }, { 56, 11.5, 53, 11.5 } }; static const POINTINIT pi_sw6[] = { { 56, 10.5, 0, 90 }, { 56, 11.5, -90, 0 } }; ROOM *r1; TRIG *g1; ROOM *r2; TRIG *g2; ROOM *sr[21]; TRIG *gu[21]; TRIG *gd[21]; int i; int j; int k; TRIG *st; ROOM *mr[4][4][2]; TRIG *mg[4][4][2][4]; ROOM *r_sw4; ROOM *r_sw5; ROOM *r_sw6; ROOM *r; MRPRIV *rp; char *txt; WALLINIT wi[24]; int nwi; POINTINIT pi[16]; int npi; double x0; double y0; double z0; unsigned int links; unsigned int links2; #define ZFORI(i) ((i)*.15) #define YFORI(i) (((i)*.6)-.7) r1 = build_room(0,"lobby",&wi_lobby[0],ASZ(wi_lobby),&pi_lobby[0],ASZ(pi_lobby)); r1->render_variable = &render_lobby; g1 = add_gate(r1,-10,0,-10,-6); st = add_switch(r1,4-.4,14.75,.2); st->active = &active_sw0_is_1; st->action = &action_sw0_to_0; st = add_switch(r1,4+.4,14.75,.2); st->active = &active_sw0_is_0; st->action = &action_sw0_to_1; st = add_switch(r1,5.5-.4,14.75,.2); st->active = &active_sw1_is_1; st->action = &action_sw1_to_0; st = add_switch(r1,5.5+.4,14.75,.2); st->active = &active_sw1_is_0; st->action = &action_sw1_to_1; st = add_switch(r1,7-.4,14.75,.2); st->active = &active_sw2_is_1; st->action = &action_sw2_to_0; st = add_switch(r1,7+.4,14.75,.2); st->active = &active_sw2_is_0; st->action = &action_sw2_to_1; st = add_switch(r1,8.5-.4,14.75,.2); st->active = &active_sw3_is_1; st->action = &action_sw3_to_0; st = add_switch(r1,8.5+.4,14.75,.2); st->active = &active_sw3_is_0; st->action = &action_sw3_to_1; r1->list = glGenLists(1); glNewList(r1->list,GL_COMPILE); glBegin(GL_QUADS); draw_floor(0,-10,-5,-10,15,10,15,10,-5); draw_ceiling(2,-10,-5,10,-5,10,15,-10,15); #ifdef SECRET_ROOM draw_floor(0,-4,-5,4,-5,4,-15,-4,-15); draw_ceiling(1,-4,-5,-4,-15,4,-15,4,-5); #endif glEnd(); wall_rects(4, (XYZ){-10,-1,0}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 16.0, 0.0, 2.0, (XYZ){-10,15,0}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 20.0, 0.0, 2.0, (XYZ){10,15,0}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.0, 20.0, 0.0, 2.0, (XYZ){10,-5,0}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 20.0, 0.0, 2.0 ); #ifdef SECRET_ROOM wall_rects(7, (XYZ){.5,-5,0}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.0, 3.0, 0.0, 1.0, (XYZ){.5,-8,0}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.5, 4.0, 0.0, 1.0, (XYZ){4,-8,0}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.0, 7.0, 0.0, 1.0, (XYZ){4,-15,0}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 8.0, 0.0, 1.0, (XYZ){-4,-15,0}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 7.0, 0.0, 1.0, (XYZ){-4,-8,0}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 3.5, 0.0, 1.0, (XYZ){-.5,-8,0}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 3.0, 0.0, 1.0 ); #endif glEndList(); r2 = build_room(3,"entrance",&wi_entrance[0],ASZ(wi_entrance),&pi_entrance[0],ASZ(pi_entrance)); g2 = add_gate(r2,-10,16,-10,10); r2->list = glGenLists(1); glNewList(r2->list,GL_COMPILE); glBegin(GL_QUADS); draw_floor(3,-10,-5,-10,15,12,15,12,-5); draw_ceiling(5,-10,-5,12,-5,12,15,-10,15); glEnd(); wall_rects(8, (XYZ){-10,15,3}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 20.0, 0.0, 2.0, (XYZ){10,15,3}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.0, 14.5, 0.0, 2.0, (XYZ){10,.5,3}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 2.0, (XYZ){11,-.5,3}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 2.0, (XYZ){10,-.5,3}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.5, 5.0, 0.0, 2.0, (XYZ){10,-5,3}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 20.0, 0.0, 2.0, (XYZ){-10,-5,3}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 16.0, 0.0, 2.0, (XYZ){11,.5,4}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.5, 1.5, 0.0, 1.0 ); glEndList(); sr[0] = build_room(0,"lower landing",&wi_landing_l[0],ASZ(wi_landing_l),&pi_landing_l[0],ASZ(pi_landing_l)); gu[0] = add_gate(sr[0],-11,YFORI(0),-17,YFORI(0)); gd[0] = add_gate(sr[0],-10,-6,-10,0); sr[0]->list = glGenLists(1); glNewList(sr[0]->list,GL_COMPILE); glBegin(GL_QUADS); draw_floor(0,-10,-5,-16,-5,-16,YFORI(0),-10,YFORI(0)); draw_ceiling(5,-10,-5,-10,-1,-16,-1,-16,-5); draw_ceiling(5,-16,-5,-12,-5,-12,15,-16,15); glColor3f(.5,.5,.5); glVertex3d(-16,YFORI(0),0); glVertex3d(-16,YFORI(0),ZFORI(1)); glVertex3d(-12,YFORI(0),ZFORI(1)); glVertex3d(-12,YFORI(0),0); glEnd(); wall_rects(5, (XYZ){-10,-5,0}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 6.0, 0.0, 5.0, (XYZ){-16,-5,0}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 10.0, 0.0, 5.0, (XYZ){-12,11,0}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.0, 12.0, 0.0, 5.0, (XYZ){-12,-1,0}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 2.0, 0.0, 5.0, (XYZ){-10,-1,2}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.0, 4.0, 0.0, 3.0 ); glEndList(); sr[20] = build_room(3,"upper landing",&wi_landing_u[0],ASZ(wi_landing_u),&pi_landing_u[0],ASZ(pi_landing_u)); gu[20] = add_gate(sr[20],-10,10,-10,16); gd[20] = add_gate(sr[20],-17,YFORI(19),-11,YFORI(19)); sr[20]->list = glGenLists(1); glNewList(sr[20]->list,GL_COMPILE); glBegin(GL_QUADS); draw_floor(3,-10,YFORI(19),-16,YFORI(19),-16,15,-10,15); draw_ceiling(5,-10,11,-10,15,-16,15,-16,11); glEnd(); wall_rects(3, (XYZ){-16,5,0}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 10.0, 0.0, 5.0, (XYZ){-16,15,3}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 6.0, 0.0, 2.0, (XYZ){-10,11,3}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 2.0, 0.0, 2.0 ); glEndList(); for (i=21-2;i>0;i--) { asprintf(&txt,"step %d",i); sr[i] = build_room(ZFORI(i),txt,&wi_steps[0],ASZ(wi_steps),&pi_steps[0],ASZ(pi_steps)); gu[i] = add_gate(sr[i],-11,YFORI(i),-17,YFORI(i)); gd[i] = add_gate(sr[i],-17,YFORI(i-1),-11,YFORI(i-1)); sr[i]->list = glGenLists(1); glNewList(sr[i]->list,GL_COMPILE); glBegin(GL_QUADS); draw_floor(ZFORI(i),-16,YFORI(i-1),-16,YFORI(i),-12,YFORI(i),-12,YFORI(i-1)); glColor3f(.5,.5,.5); glVertex3d(-16,YFORI(i),ZFORI(i)); glVertex3d(-16,YFORI(i),ZFORI(i+1)); glVertex3d(-12,YFORI(i),ZFORI(i+1)); glVertex3d(-12,YFORI(i),ZFORI(i)); glEnd(); glEndList(); } for (i=21-1;i>0;i--) { gd[i]->arg = sr[i-1]; gu[i-1]->arg = sr[i]; } gd[0]->arg = r1; gu[20]->arg = r2; g1->arg = sr[0]; g2->arg = sr[20]; for (i=21-1;i>=0;i--) { add_otherdisp(r1,sr[i]); add_otherdisp(r2,sr[i]); add_otherdisp(sr[i],r1); add_otherdisp(sr[i],r2); for (j=21-1;j>=0;j--) if (j != i) add_otherdisp(sr[j],sr[i]); } g2 = add_gate(r2,11,-1.5,11,1.5); for (i=4-1;i>=0;i--) for (j=4-1;j>=0;j--) for (k=2-1;k>=0;k--) { void linkcheck(int dir) { links2 = mazelinks[k+dirdz[dir]][j+dirdy[dir]][i+dirdx[dir]]; if (LINKX(links,dir) != LINKX(links2,dirrev[dir])) { printf("maze data mismatch: (%d,%d,%d) vs (%d,%d,%d)\n",i,j,k,i+dirdx[dir],j+dirdy[dir],k+dirdz[dir]); exit(1); } } links = mazelinks[k][j][i]; if (i < 3) linkcheck(LINK_E); if (j < 3) linkcheck(LINK_N); if (k < 1) linkcheck(LINK_U); if (i > 0) linkcheck(LINK_W); if (j > 0) linkcheck(LINK_S); if (k > 0) linkcheck(LINK_D); } #undef YFORI #undef ZFORI #if ((1<=0;i--) for (j=4-1;j>=0;j--) for (k=2-1;k>=0;k--) { int amin(int dx, int dy) { if (dx > 0) return(0); if (dy > 0) return(90); if (dx < 0) return(-180); if (dy < 0) return(-90); abort(); } int amax(int dx, int dy) { if (dx > 0) return(0); if (dy > 0) return(90); if (dx < 0) return(180); if (dy < 0) return(-90); abort(); } void setup_wp(int dir) { double cx; double cy; int ix; int iy; int jx; int jy; jx = - dirdx[dir]; jy = - dirdy[dir]; ix = jy; iy = - jx; cx = x0 + 5.5 - (4.5 * ix) - (4.5 * jx); cy = y0 + 5.5 - (4.5 * iy) - (4.5 * jy); if (LINKX(links,dir) == LINK_CLOSED) { if (dir == LINK_N) { wi[nwi++] = (WALLINIT){cx+ix,cy+iy,cx+(ix*10),cy+(iy*10)}; } else { wi[nwi++] = (WALLINIT){cx-ix,cy-iy,cx+(ix*10),cy+(iy*10)}; } } else { if (dir == LINK_N) { wi[nwi++] = (WALLINIT){cx+ix,cy+iy,cx+(ix*4),cy+(iy*4)}; } else { wi[nwi++] = (WALLINIT){cx-ix,cy-iy,cx+(ix*4),cy+(iy*4)}; } pi[npi++] = (POINTINIT){cx+(ix*4),cy+(iy*4),amin(ix,iy),amax(jx,jy)}; wi[nwi++] = (WALLINIT){cx+(ix*4),cy+(iy*4),cx+(ix*4)-(jx*3),cy+(iy*4)-(jy*3)}; wi[nwi++] = (WALLINIT){cx+(ix*5)-(jx*3),cy+(iy*5)-(jy*3),cx+(ix*5),cy+(iy*5)}; pi[npi++] = (POINTINIT){cx+(ix*5),cy+(iy*5),amin(jx,jy),amax(-ix,-iy)}; wi[nwi++] = (WALLINIT){cx+(ix*5),cy+(iy*5),cx+(ix*10),cy+(iy*10)}; } } void setup_barriers(int dir) { double cx; double cy; int ix; int iy; int jx; int jy; int v; v = LINKX(links,dir); switch (v) { case LINK_CLOSED: case LINK_OPEN: return; break; } jx = - dirdx[dir]; jy = - dirdy[dir]; ix = jy; iy = - jx; cx = x0 + 5.5 - (4.5 * ix) - (4.5 * jx); cy = y0 + 5.5 - (4.5 * iy) - (4.5 * jy); st = add_wall(r,cx+(ix*3.5)+(jx*.1),cy+(iy*3.5)+(jy*.1),cx+(ix*4)+(jx*.1),cy+(iy*4)+(jy*.1),WHICH_PLAYER); st->active = active_fns[v]; st = add_wall(r,cx+(ix*3.5)+(jx*.1),cy+(iy*3.5)+(jy*.1),cx+(ix*4)+(jx*.1),cy+(iy*4)+(jy*.1),WHICH_CAMERA); st->active = active_fns[v]; st = add_wall(r,cx+(ix*5)+(jx*.1),cy+(iy*5)+(jy*.1),cx+(ix*5.5)+(jx*.1),cy+(iy*5.5)+(jy*.1),WHICH_PLAYER); st->active = active_fns[v]; st = add_wall(r,cx+(ix*5)+(jx*.1),cy+(iy*5)+(jy*.1),cx+(ix*5.5)+(jx*.1),cy+(iy*5.5)+(jy*.1),WHICH_CAMERA); st->active = active_fns[v]; v = link_swX_Y[link_swno[v]][!link_swstate[v]]; st = add_wall(r,cx+(ix*4)+(jx*.1),cy+(iy*4)+(jy*.1),cx+(ix*5)+(jx*.1),cy+(iy*5)+(jy*.1),WHICH_PLAYER); st->active = active_fns[v]; st = add_wall(r,cx+(ix*4)+(jx*.1),cy+(iy*4)+(jy*.1),cx+(ix*5)+(jx*.1),cy+(iy*5)+(jy*.1),WHICH_CAMERA); st->active = active_fns[v]; } TRIG *setup_g(int dir, double cx, double cy, int ix, int iy, int jx, int jy) { if (LINKX(links,dir) == LINK_CLOSED) return(0); return(add_gate(r,cx+(ix*3)-jx,cy+(iy*3)-jy,cx+(ix*6)-jx,cy+(iy*6)-jy)); } void gen_walls(int dir, double cx, double cy, int ix, int iy, int jx, int jy) { if (LINKX(links,dir) == LINK_CLOSED) { wall_rects(1, (XYZ){cx+(9*ix),cy+(9*iy),z0}, (XYZ){-ix,-iy,0}, (XYZ){0,0,1}, 0.0, (dir==LINK_N)?8.0:9.0, 0.0, 1.0 ); } else { wall_rects(4, (XYZ){cx+(9*ix),cy+(9*iy),z0}, (XYZ){-ix,-iy,0}, (XYZ){0,0,1}, 0.0, 4.0, 0.0, 1.0, (XYZ){cx+(5*ix),cy+(5*iy),z0}, (XYZ){-jx,-jy,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 1.0, (XYZ){cx+(4*ix)-jx,cy+(4*iy)-jy,z0}, (XYZ){jx,jy,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 1.0, (XYZ){cx+(4*ix),cy+(4*iy),z0}, (XYZ){-ix,-iy,0}, (XYZ){0,0,1}, 0.0, (dir==LINK_N)?3.0:4.0, 0.0, 1.0 ); } if (dir == LINK_N) { wall_rects(3, (XYZ){cx+ix,cy+iy,z0}, (XYZ){-jx,-jy,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 1.0, (XYZ){cx+ix-jx,cy+iy-jy,z0}, (XYZ){-ix,-iy,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 1.0, (XYZ){cx-jx,cy-jy,z0}, (XYZ){jx,jy,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 1.0 ); } } void build_slide_walls(double x, double y, double z) { wall_rects(4, (XYZ){x,y+1,z-1}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 4.0, 0.0, 1.0, (XYZ){x+4,y,z-1}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 4.0, 0.0, 1.0, (XYZ){x,y,z}, xyzunit((XYZ){1,0,-1}), (XYZ){0,1,0}, 0.0, sqrt(2), 0.0, 1.0, (XYZ){x+1,y,z-1}, (XYZ){1,0,0}, (XYZ){0,1,0}, 0.0, 4.0, 0.0, 1.0 ); } x0 = (i * 11) + 11; y0 = (j * 11) - 16.5; z0 = 3 + (2 * k); links = mazelinks[k][j][i]; asprintf(&txt,"maze[%d][%d][%d]",i,j,k); nwi = 0; npi = 0; setup_wp(LINK_N); setup_wp(LINK_S); setup_wp(LINK_E); setup_wp(LINK_W); r = build_room(z0,txt,&wi[0],nwi,&pi[0],npi); rp = malloc(sizeof(MRPRIV)); rp->ix = i; rp->iy = j; rp->iz = k; rp->x0 = x0; rp->y0 = y0; rp->links = links; rp->swx = -1; rp->el_active = 1; r->private = rp; mr[i][j][k] = r; setup_barriers(LINK_N); setup_barriers(LINK_S); setup_barriers(LINK_E); setup_barriers(LINK_W); mg[i][j][k][LINK_N] = setup_g(LINK_N,x0+10,y0+10,-1,0,0,-1); mg[i][j][k][LINK_S] = setup_g(LINK_S,x0+1,y0+1,1,0,0,1); mg[i][j][k][LINK_E] = setup_g(LINK_E,x0+10,y0+1,0,1,-1,0); mg[i][j][k][LINK_W] = setup_g(LINK_W,x0+1,y0+10,0,-1,1,0); r->list = glGenLists(1); glNewList(r->list,GL_COMPILE); if (LINKX(links,LINK_U) != LINK_CLOSED) { draw_elevator_well(x0+1,y0+6,z0+1); st = add_switch(r,x0+2,y0+9,.3); st->active = &active_elevator_active; st->action = &action_elevator_up; st->arg = r; st = add_zone(r,x0,y0,11,0,0,6); st->active = &active_elevator_inactive; st->action = &action_elevator_activate; st->arg = r; st = add_zone(r,x0+5,y0,6,0,0,11); st->active = &active_elevator_inactive; st->action = &action_elevator_activate; st->arg = r; } else if (LINKX(links,LINK_D) != LINK_CLOSED) { draw_elevator_well(x0+1,y0+6,z0+1); st = add_switch(r,x0+2,y0+9,.3); st->active = &active_elevator_active; st->action = &action_elevator_dn; st->arg = r; st = add_zone(r,x0,y0,11,0,0,6); st->active = &active_elevator_inactive; st->action = &action_elevator_activate; st->arg = r; st = add_zone(r,x0+5,y0,6,0,0,11); st->active = &active_elevator_inactive; st->action = &action_elevator_activate; st->arg = r; } st = add_zone(r,x0+9,y0+10+PLAYER_RADIUS,1,0,0,1); st->active = 0; st->action = &action_take_slide; st->arg = r; glBegin(GL_QUADS); if (LINKX(links,LINK_D) != LINK_CLOSED) { draw_floor(z0,x0,y0,x0,y0+6,x0+11,y0+6,x0+11,y0); draw_floor(z0,x0+5,y0+6,x0+5,y0+11,x0+9,y0+11,x0+9,y0+6); draw_floor(z0,x0+9,y0+6,x0+9,y0+10,x0+10,y0+10,x0+10,y0+6); draw_floor(z0,x0+10,y0+6,x0+10,y0+11,x0+11,y0+11,x0+11,y0+6); } else { draw_floor(z0,x0,y0,x0,y0+11,x0+9,y0+11,x0+9,y0); draw_floor(z0,x0+9,y0,x0+9,y0+10,x0+10,y0+10,x0+10,y0); draw_floor(z0,x0+10,y0,x0+10,y0+11,x0+11,y0+11,x0+11,y0); } if (LINKX(links,LINK_U) != LINK_CLOSED) { draw_ceiling(z0+1,x0,y0,x0+11,y0,x0+11,y0+6,x0,y0+6); draw_ceiling(z0+1,x0+5,y0+6,x0+11,y0+6,x0+11,y0+11,x0+5,y0+11); } else { draw_ceiling(z0+1,x0,y0,x0+11,y0,x0+11,y0+11,x0,y0+11); } glEnd(); gen_walls(LINK_N,x0+10,y0+10,-1,0,0,-1); gen_walls(LINK_S,x0+1,y0+1,1,0,0,1); gen_walls(LINK_E,x0+10,y0+1,0,1,-1,0); gen_walls(LINK_W,x0+1,y0+10,0,-1,1,0); build_slide_walls(x0+9,y0+10,z0); if ((i == 2) && (j == 2) && (k == 1)) { glColor3f(1,1,0); glPolygonOffset(0,-10); draw_goal_award(x0,y0,z0); glPolygonOffset(0,0); st = add_switch(r,x0+8,y0+3,1); st->action = &action_win; } glEndList(); r->render_variable = &render_maze; } g2->arg = mr[0][1][0]; for (i=4-1;i>=0;i--) for (j=4-1;j>=0;j--) for (k=2-1;k>=0;k--) { r = mr[i][j][k]; rp = r->private; if ((j < 4-1) && LINKX(rp->links,LINK_N)) mg[i][j][k][LINK_N]->arg = mr[i][j+1][k]; if ((j > 0) && LINKX(rp->links,LINK_S)) mg[i][j][k][LINK_S]->arg = mr[i][j-1][k]; if ((i < 4-1) && LINKX(rp->links,LINK_E)) mg[i][j][k][LINK_E]->arg = mr[i+1][j][k]; if ((i > 0) && LINKX(rp->links,LINK_W)) mg[i][j][k][LINK_W]->arg = mr[i-1][j][k]; if (LINKX(rp->links,LINK_U) || LINKX(rp->links,LINK_D)) rp->el_to = mr[i][j][!k]; } mg[0][1][0][LINK_W]->arg = r2; add_otherdisp(r2,mr[0][1][0]); add_otherdisp(mr[0][1][0],r2); add_otherdisp(mr[0][1][0],sr[20]); for (i=4-1;i>=0;i--) for (j=4-1;j>=0;j--) for (k=2-1;k>=0;k--) { r = mr[i][j][k]; rp = r->private; add_maze_otherdisp(r,&mr[0],LINK_N); add_maze_otherdisp(r,&mr[0],LINK_E); add_maze_otherdisp(r,&mr[0],LINK_S); add_maze_otherdisp(r,&mr[0],LINK_W); if (LINKX(rp->links,LINK_U) != LINK_CLOSED) { if (k) abort(); add_otherdisp(r,mr[i][j][1]); if (LINKX(((MRPRIV *)mr[i][j][1]->private)->links,LINK_N) != LINK_CLOSED) add_otherdisp(r,mr[i][j+1][1]); if (LINKX(((MRPRIV *)mr[i][j][1]->private)->links,LINK_W) != LINK_CLOSED) add_otherdisp(r,mr[i-1][j][1]); } if (LINKX(rp->links,LINK_D) != LINK_CLOSED) { if (! k) abort(); add_otherdisp(r,mr[i][j][0]); if (LINKX(((MRPRIV *)mr[i][j][0]->private)->links,LINK_N) != LINK_CLOSED) add_otherdisp(r,mr[i][j+1][0]); if (LINKX(((MRPRIV *)mr[i][j][0]->private)->links,LINK_W) != LINK_CLOSED) add_otherdisp(r,i?mr[i-1][j][0]:r2); } } r = 0; r_sw4 = build_room(3,"switch 4 room",&wi_sw4[0],ASZ(wi_sw4),&pi_sw4[0],ASZ(pi_sw4)); rp = malloc(sizeof(MRPRIV)); rp->ix = 4; rp->iy = 2; rp->iz = 0; rp->x0 = 55; rp->y0 = 5.5; rp->links = LINK(LINK_W,LINK_OPEN); rp->swx = 4; rp->sxl = (XYZ){64.75,11,3}; rp->sxi = (XYZ){0,-1,0}; rp->sxj = (XYZ){1,0,0}; r_sw4->private = rp; st = add_gate(r_sw4,55,12.5,55,9.5); st->arg = mr[3][2][0]; mg[3][2][0][LINK_E]->arg = r_sw4; st = add_switch(r_sw4,64.75,11+.4,.2); st->active = &active_sw4_is_1; st->action = &action_sw4_to_0; st = add_switch(r_sw4,64.75,11-.4,.2); st->active = &active_sw4_is_0; st->action = &action_sw4_to_1; r_sw4->list = glGenLists(1); glNewList(r_sw4->list,GL_COMPILE); glBegin(GL_QUADS); draw_floor(3,55,5.5,55,16.5,66,16.5,66,5.5); draw_ceiling(5,55,5.5,66,5.5,66,16.5,55,16.5); glEnd(); wall_rects(8, (XYZ){55,11.5,3}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 2.0, (XYZ){56,11.5,3}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 4.0, 0.0, 2.0, (XYZ){56,15.5,3}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 9.0, 0.0, 2.0, (XYZ){65,15.5,3}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.0, 9.0, 0.0, 2.0, (XYZ){65,6.5,3}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 9.0, 0.0, 2.0, (XYZ){56,6.5,3}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 4.0, 0.0, 2.0, (XYZ){56,10.5,3}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 2.0, (XYZ){55,10.5,4}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 1.0 ); glEndList(); r_sw4->render_variable = &render_maze; add_otherdisp(r_sw4,mr[3][2][0]); add_otherdisp(mr[3][2][0],r_sw4); r_sw5 = build_room(3,"switch 5 room",&wi_sw5[0],ASZ(wi_sw5),&pi_sw5[0],ASZ(pi_sw5)); rp = malloc(sizeof(MRPRIV)); rp->ix = 1; rp->iy = 4; rp->iz = 0; rp->x0 = 22; rp->y0 = 27.5; rp->links = LINK(LINK_S,LINK_OPEN); rp->swx = 5; rp->sxl = (XYZ){27.5,37.25,3}; rp->sxi = (XYZ){1,0,0}; rp->sxj = (XYZ){0,1,0}; r_sw5->private = rp; st = add_gate(r_sw5,26,27.5,29,27.5); st->arg = mr[1][3][0]; mg[1][3][0][LINK_N]->arg = r_sw5; st = add_switch(r_sw5,27.5-.4,37.25,.2); st->active = &active_sw5_is_1; st->action = &action_sw5_to_0; st = add_switch(r_sw5,27.5+.4,37.25,.2); st->active = &active_sw5_is_0; st->action = &action_sw5_to_1; r_sw5->list = glGenLists(1); glNewList(r_sw5->list,GL_COMPILE); glBegin(GL_QUADS); draw_floor(3,22,27.5,22,38.5,33,38.5,33,27.5); draw_ceiling(5,22,27.5,33,27.5,33,38.5,22,38.5); glEnd(); wall_rects(8, (XYZ){27,27.5,3}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 2.0, (XYZ){27,28.5,3}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 4.0, 0.0, 2.0, (XYZ){23,28.5,3}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 9.0, 0.0, 2.0, (XYZ){23,37.5,3}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 9.0, 0.0, 2.0, (XYZ){32,37.5,3}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.0, 9.0, 0.0, 2.0, (XYZ){32,28.5,3}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 4.0, 0.0, 2.0, (XYZ){28,28.5,3}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 2.0, (XYZ){28,27.5,4}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 1.0 ); glEndList(); r_sw5->render_variable = &render_maze; add_otherdisp(r_sw5,mr[1][3][0]); add_otherdisp(r_sw5,mr[1][2][0]); add_otherdisp(r_sw5,mr[1][1][0]); add_otherdisp(mr[1][3][0],r_sw5); add_otherdisp(mr[1][2][0],r_sw5); add_otherdisp(mr[1][1][0],r_sw5); r_sw6 = build_room(5,"switch 6 room",&wi_sw6[0],ASZ(wi_sw6),&pi_sw6[0],ASZ(pi_sw6)); rp = malloc(sizeof(MRPRIV)); rp->ix = 4; rp->iy = 2; rp->iz = 1; rp->x0 = 55; rp->y0 = 5.5; rp->links = LINK(LINK_W,LINK_OPEN); rp->swx = 6; rp->sxl = (XYZ){64.75,11,5}; rp->sxi = (XYZ){0,-1,0}; rp->sxj = (XYZ){1,0,0}; r_sw6->private = rp; st = add_gate(r_sw6,55,12.5,55,9.5); st->arg = mr[3][2][1]; mg[3][2][1][LINK_E]->arg = r_sw6; st = add_switch(r_sw6,64.75,11+.4,.2); st->active = &active_sw6_is_1; st->action = &action_sw6_to_0; st = add_switch(r_sw6,64.75,11-.4,.2); st->active = &active_sw6_is_0; st->action = &action_sw6_to_1; r_sw6->list = glGenLists(1); glNewList(r_sw6->list,GL_COMPILE); glBegin(GL_QUADS); draw_floor(5,55,5.5,55,16.5,66,16.5,66,5.5); draw_ceiling(7,55,5.5,66,5.5,66,16.5,55,16.5); glEnd(); wall_rects(8, (XYZ){55,11.5,5}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 2.0, (XYZ){56,11.5,5}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 4.0, 0.0, 2.0, (XYZ){56,15.5,5}, (XYZ){1,0,0}, (XYZ){0,0,1}, 0.0, 9.0, 0.0, 2.0, (XYZ){65,15.5,5}, (XYZ){0,-1,0}, (XYZ){0,0,1}, 0.0, 9.0, 0.0, 2.0, (XYZ){65,6.5,5}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 9.0, 0.0, 2.0, (XYZ){56,6.5,5}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 4.0, 0.0, 2.0, (XYZ){56,10.5,5}, (XYZ){-1,0,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 2.0, (XYZ){55,10.5,6}, (XYZ){0,1,0}, (XYZ){0,0,1}, 0.0, 1.0, 0.0, 1.0 ); glEndList(); r_sw6->render_variable = &render_maze; add_otherdisp(r_sw6,mr[3][2][1]); add_otherdisp(mr[3][2][1],r_sw6); lobby = r1; curroom = lobby; } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); open_display(); setup_random(); setup_visual(); setup_X(); setup_context(); setup_textures(); setup_switches(); setup_elevator(); create_window(); setup_gl(); setup_input(); setup_view(); setup_ticks(); setup_lists(); setup_maze(); while (1) tick(); }