#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 "2darith.h" #include "3darith.h" #include "findvis.h" #include "mathutils.h" #include "rectregion.h" #define WINX 400 #define WINY 400 #define PLAYER_SIDES 20 #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 #define RETURN_FRAGS 1024 #define RETURN_FRAG_NOISE .1 #define RETURN_TICKS 25 #define WALL_EPSILON 1e-4 #define FLOAT_SPEED (.5e-6) #define SWITCH_SPEED (.1e-6) extern const char *__progname; typedef enum { DISP_NORMAL = 1, DISP_OVERHEAD } DISPTYPE; typedef struct room ROOM; typedef struct roomlist ROOMLIST; typedef struct trig TRIG; typedef struct trigops TRIGOPS; typedef struct wallinit WALLINIT; typedef struct mrpriv MRPRIV; typedef struct crpriv CRPRIV; typedef struct frag FRAG; typedef struct tp_wall TP_WALL; typedef struct tp_gate TP_GATE; typedef struct tp_switch TP_SWITCH; typedef struct tp_zone TP_ZONE; #define WHICH_PLAYER 0x00000001 #define WHICH_CAMERA 0x00000002 #define WHICH_BOTH (WHICH_PLAYER | WHICH_CAMERA) struct frag { unsigned int colour; XYZ p; XYZ d; } ; struct wallinit { double x1; double y1; double x2; double y2; } ; struct roomlist { ROOMLIST *link; ROOM *room; } ; struct trigops { int (*pfire)(TRIG *); int (*cfire)(TRIG *); void (*dump)(TRIG *); } ; #define TRIG_OPS(name) { \ &trig_##name##_pfire, \ &trig_##name##_cfire, \ &trig_##name##_dump } struct trig { TRIG *link; const char *text; TRIGOPS *ops; void *priv; } ; /* * Represents a wall. The values here are preprocessed, with * substantial redundancy, to make hit processing fast. i points * parallel to the wall, j perpendicular to it; j points towards * accessible space. imin and imax are the minimum and maximum values * of p·i for p on the wall; jdot is the value of p·j for the wall * (which is constant along the wall, since j is normal to the wall). * emin is the endpoint corresponding to imin, emax to imax (thus, * emin = imin i + jdot j and emax = imax i + jdot j). * * This can trip on either player or camera; when it trips, it just * pushes the tripping thing back into accessible space. * * This handles exclusion due to both the bulk of the wall and its * endpoints. */ struct tp_wall { XY emin; XY emax; XY i; XY j; double imin; double imax; double jdot; int (*live)(void *); void *arg; } ; /* * Represents a gate. This is a trigger which activates when passed * unidirectionally and moves the player to a new room. Data * structures are a simplified form of those for walls (because gates * don't care about endpoints). * * These trip whenever the player moves from one side to the other of * the activation line, and at least one of the starting position and * ending position is within the length of the gate within * WALL_EPSILON of slop. */ struct tp_gate { int (*live)(void *); XY i; XY j; double imin; double imax; double jdot; ROOM *to; void *arg; } ; /* * A step-on-it-to-activate switch. at says where it is, with size * being its size. It trips as soon as any part of the player touches * it (so, measured by the player centre, the effective size is * PLAYER_RADIUS more than size). */ struct tp_switch { int (*live)(void *); XY at; double size; void (*trip)(void *); void *arg; } ; /* * A "do something on entering this zone". This trips as soon as the * player centre, dotted with x, is between min.x and max.x, and * similarly for y. */ struct tp_zone { int (*live)(void *); XY x; XY y; XY min; XY max; void (*trip)(void *); void *arg; } ; struct room { const char *text; TRIG *triggers; ROOMLIST *otherdisp; GLuint list; double floorz; void (*render_variable)(ROOM *); void *private; void (*fire)(void *); void *firearg; } ; 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; } ; struct crpriv { ROOM *r1; ROOM *r2; } ; 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 void (*eatkey)(KeySym, char); 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]; #define SWITCHTEXSIZE 32 static unsigned char switchtexture[NSWITCH][SWITCHTEXSIZE+2][SWITCHTEXSIZE+2][4]; /* CLOSED must be zero so that unmentioned directions are closed. */ #if NSWITCH != 7 #error "LINK_* definitions assume 7 switches." #endif #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_E 1 #define LINK_S 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); } } /* 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_ui(void) { eatkey = 0; elevating = 0; return_stage = 0; culling = 1; clipping = 1; kbstate = 0; display = DISP_NORMAL; } 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][y][x][0] = 127; switchtexture[i][y][x][1] = 127; switchtexture[i][y][x][2] = 127; } else { switchtexture[i][y][x][0] = 255; switchtexture[i][y][x][1] = 0; switchtexture[i][y][x][2] = 255; } switchtexture[i][y][x][3] = 255; } } } static void setup_switches(void) { switchstate = 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); 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],SWITCHTEXSIZE+2,SWITCHTEXSIZE+2,&switchtexture[i][0][0]); } glCullFace(GL_BACK); glFrontFace(GL_CW); } static void dump_room(ROOM *r) { TRIG *t; ROOMLIST *rl; const char *sep; void dump_trig(TRIG *t) { printf(" %p [%s] ",(void *)t,t->text); (*t->ops->dump)(t); printf("\n"); } 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:"); sep = ""; for (rl=r->otherdisp;rl;rl=rl->link) { printf("%s%s",sep,rl->room->text); sep = ", "; } printf("\n"); printf(" Triggers:\n"); for (t=r->triggers;t;t=t->link) dump_trig(t); } static void action_gate(ROOM *to) { curroom = to; ploc.z = to->floorz; if (dbg & DBG_GATE) dump_room(to); } static XY push_away_by(XY from, XY loc, double distance) { return(xyadd(from,xyscale(xyunit(xysub(loc,from)),distance))); } static int trig_wall_fire(TRIG *t, XYZ *locp, XYZ prev, double size) { TP_WALL *p; XY loc; XY prevloc; double jv; double pjv; double iv; double piv; double imin; double imax; double d; p = t->priv; if (p->live && !(*p->live)(p->arg)) return(0); loc = (XY){locp->x,locp->y}; jv = dot2(loc,p->j) - p->jdot; /* Too far on the accessible side to touch? No hit. */ if (jv >= size) return(0); prevloc = (XY){prev.x,prev.y}; pjv = dot2(prevloc,p->j) - p->jdot; /* Previous location on the other side? No crossing -> no push. */ if (pjv < 0) return(0); iv = dot2(loc,p->i); piv = dot2(prevloc,p->i); imin = p->imin - size; imax = p->imax + size; /* Both prev and cur are out of range on the same end? No hit. */ if ( ((iv <= imin) && (piv <= imin)) || ((iv >= imax) && (piv >= imax)) ) return(0); /* To avoid deeply-nested ifs, provide a braekable context. */ do { if ((iv >= imin) && (iv <= imax)) { /* * Cur within length of wall. Just push away. This can be * slightly wrong, but I think not wrong enough to worry about. */ loc = xyadd(loc,xyscale(p->j,(size-jv)+WALL_EPSILON)); break; } if ( (fabs(loc.x-prevloc.x) < WALL_EPSILON) && (fabs(loc.y-prevloc.y) < WALL_EPSILON) ) { /* * Still (or almost still) within range. (This can happen if we * were teleported into range, or if the wall itself is moving. * At this writing, neither one can happen, but I'm not quite * certain there's no other way for this condition to arise.) * * We know we're not within imin<=iv<=imax, or the previous test * would have tripped. */ d = hypot(loc.x-p->emin.x,loc.y-p->emin.y); if (d < size) { loc = push_away_by(p->emin,loc,size+WALL_EPSILON); break; } d = hypot(loc.x-p->emax.x,loc.y-p->emax.y); if (d < size) { loc = push_away_by(p->emax,loc,size+WALL_EPSILON); break; } /* * Must be in one of the small areas between bounding rectangle * corner and endpoint circles. No hit. */ return(0); } /* * All the easy tests failed. All these cases can, I think, be * ignored. This is not ideal, but I think the sorts of errors * introduced occur only when moving fast and are ignorable (such * as just brushing a wall corner and going through it rather than * bumping into it). * * Note that these errors are ignorable only because all the * interior corners in the map have walls that project past the * corner. For example, we would do (0,10)-(0,-1) and * (-1,0)-(10,0) rather than (0,10)-(0,0) and (0,0)-(10,0); if we * did the latter, the player could go right through the corner, * because the player can move substantially more than one * player-radius per tick. (This isn't entirely true of the * secret room; there, we include two short walls which have no * purpose other than to deal with this issue.) */ return(0); } while (0); if (dbg & DBG_FIRING) printf("Trigger %p fires in %s at %u: (%g,%g) pushed to (%g,%g)\n", (void *)t,curroom->text,ticks,locp->x,locp->y,loc.x,loc.y); locp->x = loc.x; locp->y = loc.y; return(1); } static int trig_wall_pfire(TRIG *t) { return(trig_wall_fire(t,&ploc,plocprev,PLAYER_RADIUS)); } static int trig_wall_cfire(TRIG *t) { return(trig_wall_fire(t,&cloc,clocprev,CAMERA_RADIUS)); } static void trig_wall_dump(TRIG *t) { TP_WALL *p; p = t->priv; printf("WALL (%g,%g)-(%g,%g) ((%g,%g),(%g,%g),%g,%g,%g)", p->emin.x, p->emin.y, p->emax.x, p->emax.y, p->i.x, p->i.y, p->j.x, p->j.y, p->imin, p->imax, p->jdot); } static TRIGOPS trigops_wall = TRIG_OPS(wall); static int trig_gate_pfire(TRIG *t) { TP_GATE *p; XY loc; XY prevloc; double jv; double pjv; double iv; double piv; p = t->priv; if (p->live && !(*p->live)(p->arg)) return(0); loc = (XY){ploc.x,ploc.y}; jv = dot2(loc,p->j) - p->jdot; if (jv >= 0) return(0); prevloc = (XY){plocprev.x,plocprev.y}; pjv = dot2(prevloc,p->j) - p->jdot; if (pjv < 0) return(0); iv = dot2(loc,p->i); piv = dot2(prevloc,p->i); if (! ( ((iv >= p->imin-WALL_EPSILON) && (iv <= p->imax+WALL_EPSILON)) || ((piv >= p->imin-WALL_EPSILON) && (piv <= p->imax+WALL_EPSILON)) ) ) return(0); if (dbg & DBG_FIRING) printf("Trigger %p fires in %s at %u: (%g,%g) gated to %s\n", (void *)t,curroom->text,ticks,ploc.x,ploc.y,p->to->text); action_gate(p->to); return(1); } static int trig_gate_cfire(TRIG *t __attribute__((__unused__))) { return(0); } static void trig_gate_dump(TRIG *t) { TP_GATE *p; p = t->priv; printf("GATE (%g,%g),(%g,%g),%g,%g,%g ?%p(%p)", p->i.x, p->i.y, p->j.x, p->j.y, p->imin, p->imax, p->jdot, (void *)p->live, p->arg); } static TRIGOPS trigops_gate = TRIG_OPS(gate); static int trig_switch_pfire(TRIG *t) { TP_SWITCH *p; double h; p = t->priv; if (p->live && !(*p->live)(p->arg)) return(0); h = hypot(ploc.x-p->at.x,ploc.y-p->at.y); if (h < p->size+PLAYER_RADIUS) { if (dbg & DBG_FIRING) printf("Trigger %p fires in %s at %u: (%g,%g) near (%g,%g)+%g: %s\n", (void *)t,curroom->text,ticks,ploc.x,ploc.y,p->at.x,p->at.y,p->size,t->text); (*p->trip)(p->arg); return(1); } return(0); } static int trig_switch_cfire(TRIG *t __attribute__((__unused__))) { return(0); } static void trig_switch_dump(TRIG *t) { TP_SWITCH *p; p = t->priv; printf("SWITCH (%g,%g)+%g ?%p %p(%p)",p->at.x,p->at.y,p->size,(void *)p->live,(void *)p->trip,p->arg); } static TRIGOPS trigops_switch = TRIG_OPS(switch); static int trig_zone_pfire(TRIG *t) { TP_ZONE *p; double d; p = t->priv; if (p->live && !(*p->live)(p->arg)) return(0); d = dot2((XY){ploc.x,ploc.y},p->x); if ((d < p->min.x) || (d > p->max.x)) return(0); d = dot2((XY){ploc.x,ploc.y},p->y); if ((d < p->min.y) || (d > p->max.y)) return(0); if (dbg & DBG_FIRING) printf("Trigger %p fires in %s at %u: (%g,%g) in (%g,%g)[%g,%g] (%g,%g)[%g,%g]: %s\n", (void *)t,curroom->text,ticks,ploc.x,ploc.y,p->x.x,p->x.y,p->min.x,p->max.x,p->y.x,p->y.y,p->min.y,p->max.y,t->text); (*p->trip)(p->arg); return(1); } static int trig_zone_cfire(TRIG *t __attribute__((__unused__))) { return(0); } static void trig_zone_dump(TRIG *t) { TP_ZONE *p; XY p1; XY p2; XY p3; XY p4; p = t->priv; p1 = xyadd(xyscale(p->x,p->min.x),xyscale(p->y,p->min.y)); p2 = xyadd(xyscale(p->x,p->min.x),xyscale(p->y,p->max.y)); p3 = xyadd(xyscale(p->x,p->max.x),xyscale(p->y,p->max.y)); p4 = xyadd(xyscale(p->x,p->max.x),xyscale(p->y,p->min.y)); printf("ZONE (%g,%g)-(%g,%g)-(%g,%g)-(%g,%g) %p(%p)", p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, (void *)p->trip, p->arg ); } static TRIGOPS trigops_zone = TRIG_OPS(zone); /* * 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) { if (r->list) glCallList(r->list); if (r->render_variable) (*r->render_variable)(r); } static void set_culling(void) { if (culling) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); } static void draw_return_frags(void) { int i; FRAG *f; double x; double y; double z; glDisable(GL_CULL_FACE); glColor3f(1,1,1); glBegin(GL_QUADS); for (i=RETURN_FRAGS-1;i>=0;i--) { f = &return_frags[i]; glColor3f(f->colour&1,(f->colour>>1)&1,(f->colour>>2)&1); // XXX random orientations? x = ploc.x + f->p.x; y = ploc.y + f->p.y; z = ploc.z + f->p.z; glVertex3f(x-.01,y-.01,z); glVertex3f(x-.01,y+.01,z); glVertex3f(x+.01,y+.01,z); glVertex3f(x+.01,y-.01,z); glVertex3f(x-.01,y,z-.01); glVertex3f(x-.01,y,z+.01); glVertex3f(x+.01,y,z+.01); glVertex3f(x+.01,y,z-.01); glVertex3f(x,y-.01,z-.01); glVertex3f(x,y-.01,z+.01); glVertex3f(x,y+.01,z+.01); glVertex3f(x,y+.01,z-.01); } glEnd(); set_culling(); } static void drawstuff_normal(void) { ROOMLIST *l; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-.02,.02,-.01,.01,.02,100); glMatrixMode(GL_MODELVIEW); set_culling(); switch (return_stage) { case RETURN_FADEOUT: case RETURN_FADEIN: glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glPushMatrix(); break; default: glDisable(GL_BLEND); break; } 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); switch (return_stage) { default: abort(); break; case RETURN_NONE: glPushMatrix(); glTranslated(ploc.x,ploc.y,ploc.z); glCallList(playerlist); glPopMatrix(); break; case RETURN_EXPLODE: case RETURN_IMPLODE: draw_return_frags(); break; case RETURN_FADEOUT: draw_return_frags(); glPopMatrix(); glColor4f(0,0,0,(ticks-return_tick0)/(double)RETURN_TICKS); if (0) { case RETURN_FADEIN: draw_return_frags(); glPopMatrix(); glColor4f(0,0,0,(RETURN_TICKS-(ticks-return_tick0))/(double)RETURN_TICKS); } glBegin(GL_QUADS); glVertex3f(-.05,-.05,-.05); glVertex3f(-.05,.05,-.05); glVertex3f(.05,.05,-.05); glVertex3f(.05,-.05,-.05); glEnd(); break; } } static void drawstuff_overhead(void) { TRIG *t; int first; XY min; XY max; XY centre; double scale; int i; int j; first = 1; for (t=curroom->triggers;t;t=t->link) { TP_WALL *p; if (t->ops != &trigops_wall) continue; p = t->priv; if (first) { min.x = p->emin.x; max.x = p->emin.x; min.y = p->emin.y; max.y = p->emin.y; first = 0; } if (p->emin.x < min.x) min.x = p->emin.x; if (p->emin.x > max.x) max.x = p->emin.x; if (p->emin.y < min.y) min.y = p->emin.y; if (p->emin.y > max.y) max.y = p->emin.y; if (p->emax.x < min.x) min.x = p->emax.x; if (p->emax.x > max.x) max.x = p->emax.x; if (p->emax.y < min.y) min.y = p->emax.y; if (p->emax.y > max.y) max.y = p->emax.y; } if (max.x == min.x) { max.x += 1; min.x -= 1; } if (max.y == min.y) { max.y += 1; min.y -= 1; } centre = xyscale(xyadd(min,max),.5); scale = .9 / ((max.x-centre.x > max.y-centre.y) ? max.x-centre.x : max.y-centre.y); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-2,2,-1,1,0,10); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); if (first) { glBegin(GL_QUADS); glColor3f(.5,.5,.5); glVertex3f(-1,-1,-9); glVertex3f(-1,1,-9); glVertex3f(1,1,-9); glVertex3f(1,-1,-9); glEnd(); } else { double pri[4]; double pt; void circle_at(double x, double y, double r) { glBegin(GL_LINE_STRIP); for (i=0;i<=PLAYER_SIDES;i++) { glVertex2f( x+(r*cosdeg((360.0*i)/PLAYER_SIDES)), y+(r*sindeg((360.0*i)/PLAYER_SIDES)) ); } glEnd(); } glBegin(GL_QUADS); glColor3f(.2,.2,.2); glVertex3f(-1,-1,-9); glVertex3f(-1,1,-9); glVertex3f(1,1,-9); glVertex3f(1,-1,-9); glEnd(); pri[0] = -8; pri[1] = -7; pri[2] = -6; pri[3] = -5; for (i=4-1;i>=0;i--) { j = random() % 4; if (j != i) { pt = pri[i]; pri[i] = pri[j]; pri[j] = pt; } } glScaled(scale,scale,1); glTranslated(-centre.x,-centre.y,0); glPushMatrix(); glTranslated(0,0,pri[0]); for (t=curroom->triggers;t;t=t->link) { if (t->ops == &trigops_wall) { TP_WALL *p; p = t->priv; glBegin(GL_LINES); if (!p->live || (*p->live)(p->arg)) { glColor3f(1,0,0); } else { glColor3f(.5,0,0); } glVertex2f(p->emin.x,p->emin.y); glVertex2f(p->emax.x,p->emax.y); glEnd(); } } glPopMatrix(); glPushMatrix(); glTranslated(0,0,pri[1]); for (t=curroom->triggers;t;t=t->link) { if (t->ops == &trigops_gate) { TP_GATE *p; XY e1; XY e2; p = t->priv; e1 = xyadd(xyscale(p->j,p->jdot),xyscale(p->i,p->imin)); e2 = xyadd(xyscale(p->j,p->jdot),xyscale(p->i,p->imax)); glBegin(GL_LINES); if (!p->live || (*p->live)(p->arg)) { glColor3f(0,1,0); } else { glColor3f(0,.5,0); } glVertex2f(e1.x,e1.y); glVertex2f(e2.x,e2.y); glEnd(); } } glPopMatrix(); glPushMatrix(); glTranslated(0,0,pri[2]); for (t=curroom->triggers;t;t=t->link) { if (t->ops == &trigops_switch) { TP_SWITCH *p; p = t->priv; if (!p->live || (*p->live)(p->arg)) { glColor3f(0,1,1); } else { glColor3f(0,.5,.5); } circle_at(p->at.x,p->at.y,p->size); } } glPopMatrix(); glPushMatrix(); glTranslated(0,0,pri[3]); for (t=curroom->triggers;t;t=t->link) { if (t->ops == &trigops_zone) { TP_ZONE *p; XY p1; XY p2; XY p3; XY p4; p = t->priv; p1 = xyadd(xyscale(p->x,p->min.x),xyscale(p->y,p->min.y)); p2 = xyadd(xyscale(p->x,p->min.x),xyscale(p->y,p->max.y)); p3 = xyadd(xyscale(p->x,p->max.x),xyscale(p->y,p->max.y)); p4 = xyadd(xyscale(p->x,p->max.x),xyscale(p->y,p->min.y)); if (!p->live || (*p->live)(p->arg)) { glColor3f(1,0,1); } else { glColor3f(.5,0,.5); } glBegin(GL_LINE_STRIP); glVertex2f(p1.x,p1.y); glVertex2f(p2.x,p2.y); glVertex2f(p3.x,p3.y); glVertex2f(p4.x,p4.y); glVertex2f(p1.x,p1.y); glEnd(); } } glPopMatrix(); glPushMatrix(); glTranslated(0,0,-1); glColor3f(1,1,1); circle_at(ploc.x,ploc.y,PLAYER_RADIUS); glColor3f(.5,.5,.5); circle_at(cloc.x,cloc.y,CAMERA_RADIUS); glPopMatrix(); } glPopMatrix(); glDisable(GL_BLEND); } static void drawstuff(void) { 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); dbg &= ~DBG_STATE; } if (dbg & DBG_ROOM) { dump_room(curroom); dbg &= ~DBG_ROOM; } switch (display) { default: abort(); break; case DISP_NORMAL: drawstuff_normal(); break; case DISP_OVERHEAD: drawstuff_overhead(); break; } } 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 ] / * * Since the arguments are a rotation matrix, the divisor would be 1 * except for numerical errors, so we don't compute it. We want to * keep numerical errors from accumulating, though, so we compute the * output X vector, xyzunit() it, then compute the Y vector, remove * any X component and xyzunit() that, then compute the Z vector as * X × Y. * * This is a bit more expensive than the straight-up inversion, but * this is not a heavily-used routine - a few times each tick. */ static void invert_rotations(XYZ rx, XYZ ry, XYZ rz, XYZ *ix, XYZ *iy, XYZ *iz) { XYZ ox; XYZ oy; ox.x = (ry.y * rz.z) - (rz.y * ry.z); ox.y = (rz.y * rx.z) - (rx.y * rz.z); ox.z = (rx.y * ry.z) - (ry.y * rx.z); ox = xyzunit(ox); oy.x = (rz.x * ry.z) - (ry.x * rz.z); oy.y = (rx.x * rz.z) - (rz.x * rx.z); oy.z = (ry.x * rx.z) - (rx.x * ry.z); oy = xyzunit(xyz_subtract_component(oy,ox)); *ix = ox; *iy = oy; *iz = xyzcross(ox,oy); } static void fire_triggers_p(void) { TRIG *t; if (! clipping) return; while (1) { for (t=curroom->triggers;t;t=t->link) { if ((*t->ops->pfire)(t)) { break; } } if (! t) return; } } static void fire_triggers_c(void) { TRIG *t; while (1) { for (t=curroom->triggers;t;t=t->link) { if ((*t->ops->cfire)(t)) { break; } } if (! t) return; } } static XYZ movepby(XYZ amt) { XYZ pl0; pl0 = ploc; ploc = xyzadd(ploc,amt); fire_triggers_p(); return(xyzsub(ploc,pl0)); } 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)); fire_triggers_c(); } else if (ed > 5) { ed = .01; cloc = xyzadd(ploc,xyzscale(ez,ed)); clocprev = cloc; } else if (ed > 2) { ed -= CAMERA_ADJUST; if (ed < 2) ed = 2; cloc = xyzadd(ploc,xyzscale(ez,ed)); fire_triggers_c(); } cloc.z = cz; } static void czfloat(void) { double des; double inc; double delta; inc = FLOAT_SPEED * 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; } } static void movecby(XYZ amt) { cloc = xyzadd(cloc,amt); fire_triggers_c(); } static void set_return_stage(int s) { return_stage = s; return_tick0 = ticks; } static void reset_camera(void) { ex = xyzunit(xyzsub(ploc,cloc)); ez = (XYZ){0,0,1}; ey = xyzcross(ez,ex); invert_rotations(ex,ey,ez,&wx,&wy,&wz); drag_camera(); } static void teleport(double px, double py, double pz, double cx, double cy, double cz) { ploc = (XYZ){px,py,pz}; cloc = (XYZ){cx,cy,cz}; plocprev = ploc; clocprev = cloc; reset_camera(); } static void place_in_lobby(void) { teleport(0,0,0,0,-2,.75); } static void animate_frags(int sign) { int i; FRAG *f; if (sign > 0) { for (i=RETURN_FRAGS-1;i>=0;i--) { f = &return_frags[i]; f->p = xyzadd(f->p,f->d); } } else { for (i=RETURN_FRAGS-1;i>=0;i--) { f = &return_frags[i]; f->p = xyzsub(f->p,f->d); } } } static void motion(void) { double f; int i; double d; f = SWITCH_SPEED * frameus; for (i=NSWITCH-1;i>=0;i--) { d = (switchstate & (1U << i)) ? 1 : 0; if (switchz[i] > d) { switchz[i] -= f; if (switchz[i] < d) switchz[i] = d; } if (switchz[i] < d) { switchz[i] += f; if (switchz[i] > d) switchz[i] = d; } } switch (return_stage) { default: abort(); case RETURN_NONE: break; case RETURN_EXPLODE: animate_frags(1); if (ticks-return_tick0 > RETURN_TICKS) { set_return_stage(RETURN_FADEOUT); case RETURN_FADEOUT: animate_frags(1); if (ticks-return_tick0 > RETURN_TICKS) { action_gate(lobby_room); place_in_lobby(); set_return_stage(RETURN_FADEIN); case RETURN_FADEIN: animate_frags(-1); if (ticks-return_tick0 > RETURN_TICKS) { set_return_stage(RETURN_IMPLODE); case RETURN_IMPLODE: animate_frags(-1); if (ticks-return_tick0 > RETURN_TICKS) { set_return_stage(RETURN_NONE); } } } } return; break; } 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: movecby(movepby(xyzscale(ex,-f))); break; case KBS_MR: movecby(movepby(xyzscale(ex,f))); break; } switch (kbstate & (KBS_MF|KBS_MB)) { case KBS_MF: movepby(xyzscale(ez,-f)); break; case KBS_MB: movepby(xyzscale(ez,f)); break; } if (! clipping) { switch (kbstate & (KBS_MU|KBS_MD)) { case KBS_MU: movecby(movepby(xyzscale(ey,f))); break; case KBS_MD: movecby(movepby(xyzscale(ey,-f))); break; } } if (kbstate & (KBS_ML|KBS_MR|KBS_MF|KBS_MB)) { drag_camera(); } switch (kbstate & (KBS_RL|KBS_RR)) { case KBS_RL: movecby(xyzscale(ex,f)); drag_camera(); break; case KBS_RR: movecby(xyzscale(ex,-f)); drag_camera(); break; } czfloat(); } static void key_warp(KeySym ks, char updn) { if (updn == 'u') return; eatkey = 0; switch (ks) { case XK_l: case XK_L: action_gate(lobby_room); place_in_lobby(); cloc.z = ploc.z; return; break; case XK_e: case XK_E: action_gate(entrance_room); teleport(5,0,3,3,0,3); return; break; case XK_g: case XK_G: action_gate(goal_room); teleport(40,9.5,5,38.75,10.75,5); return; break; case XK_1: action_gate(first_room); teleport(16.5,0,3,16.5,-2,3); return; break; case XK_5: action_gate(sw5_room); teleport(60.5,11,3,58.5,11,3); return; break; case XK_6: action_gate(sw6_room); teleport(27.5,33,3,27.5,31,3); return; break; case XK_7: action_gate(sw7_room); teleport(60.5,11,5,58.5,11,5); return; break; #ifdef SECRET_ROOM case XK_s: case XK_S: action_gate(lobby_room); teleport(0,-10,0,0,-8,0); return; break; #endif } } static void key_disp(KeySym ks, char updn) { if (updn == 'u') return; eatkey = 0; switch (ks) { case XK_n: case XK_N: display = DISP_NORMAL; return; break; case XK_o: case XK_O: display = DISP_OVERHEAD; return; 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_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; default: if (eatkey) { (*eatkey)(ks,updn); return; } switch (ks) { case XK_w: case XK_W: if (magic) { if (updn == 'd') eatkey = &key_warp; } else { 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: if (magic) { if (updn == 'd') eatkey = &key_disp; } else { 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_y: case XK_Y: bit = KBS_MU; break; case XK_h: case XK_H: bit = KBS_MD; 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')) culling = ! culling; break; case XK_l: case XK_L: if (magic && (updn == 'd')) clipping = ! clipping; break; case XK_1: if (magic && (updn == 'd')) switchstate ^= 1<<0; break; case XK_2: if (magic && (updn == 'd')) switchstate ^= 1<<1; break; case XK_3: if (magic && (updn == 'd')) switchstate ^= 1<<2; break; case XK_4: if (magic && (updn == 'd')) switchstate ^= 1<<3; break; case XK_5: if (magic && (updn == 'd')) switchstate ^= 1<<4; break; case XK_6: if (magic && (updn == 'd')) switchstate ^= 1<<5; break; case XK_7: if (magic && (updn == 'd')) switchstate ^= 1<<6; break; case XK_p: case XK_P: if (updn == 'd') { if (magic) dbg ^= DBG_PUSH; else dbg |= DBG_STATE; } break; case XK_r: case XK_R: if (updn == 'd') dbg |= DBG_ROOM; break; } 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 ++; plocprev = ploc; clocprev = cloc; } static void setup_view(void) { place_in_lobby(); } 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=PLAYER_SIDES;i>=0;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=PLAYER_SIDES;i>=0;i--) { glVertex3d(c[i],s[i],PLAYER_HEIGHT); glVertex3d(c[i],s[i],0); } 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 TRIG *add_wall(ROOM *r, double x1, double y1, double x2, double y2) { TRIG *t; char *s; TP_WALL *p; asprintf(&s,"WALL (%g,%g)-(%g,%g)",x1,y1,x2,y2); p = malloc(sizeof(TP_WALL)); t = malloc(sizeof(TRIG)); t->link = r->triggers; r->triggers = t; t->text = s; t->ops = &trigops_wall; t->priv = p; p->emin = (XY){x1,y1}; p->emax = (XY){x2,y2}; p->i = xyunit(xysub(p->emax,p->emin)); p->j = xyrotateccw(p->i); p->imin = dot2(p->i,p->emin); p->imax = dot2(p->i,p->emax); if (p->imin > p->imax) abort(); p->jdot = dot2(p->j,p->emin); if (fabs(dot2(p->j,p->emax)-p->jdot) > WALL_EPSILON) abort(); p->live = 0; return(t); } static void wall_set_live(TRIG *t, int (*live)(void *), void *arg) { if (t->ops != &trigops_wall) abort(); ((TP_WALL *)t->priv)->live = live; ((TP_WALL *)t->priv)->arg = arg; } static TRIG *add_gate(ROOM *r, double x1, double y1, double x2, double y2) { TRIG *t; char *s; TP_GATE *p; asprintf(&s,"GATE (%g,%g)-(%g,%g)",x1,y1,x2,y2); p = malloc(sizeof(TP_GATE)); t = malloc(sizeof(TRIG)); t->link = r->triggers; r->triggers = t; t->text = s; t->ops = &trigops_gate; t->priv = p; p->i = xyunit(xysub((XY){x2,y2},(XY){x1,y1})); p->j = xyrotateccw(p->i); p->imin = dot2(p->i,(XY){x1,y1}); p->imax = dot2(p->i,(XY){x2,y2}); if (p->imin >= p->imax) abort(); p->jdot = dot2(p->j,(XY){x1,y1}); if (fabs(dot2(p->j,(XY){x2,y2})-p->jdot) > WALL_EPSILON) abort(); p->to = 0; p->live = 0; return(t); } static void gate_set_to(TRIG *t, ROOM *r) { if (t->ops != &trigops_gate) abort(); ((TP_GATE *)t->priv)->to = r; } static void gate_set_live(TRIG *t, int (*live)(void *), void *arg) { if (t->ops != &trigops_gate) abort(); ((TP_GATE *)t->priv)->live = live; ((TP_GATE *)t->priv)->arg = arg; } static TRIG *add_switch(ROOM *r, double x, double y, double rad, int (*live)(void *), void (*trip)(void *), void *arg) { TRIG *t; char *s; TP_SWITCH *p; asprintf(&s,"SWITCH (%g,%g)+%g",x,y,rad); p = malloc(sizeof(TP_SWITCH)); t = malloc(sizeof(TRIG)); t->link = r->triggers; r->triggers = t; t->text = s; t->ops = &trigops_switch; t->priv = p; p->at = (XY){x,y}; p->size = rad; p->live = live; p->trip = trip; p->arg = arg; return(t); } static TRIG *add_zone( ROOM *r, double x0, double y0, double ix, double iy, double jx, double jy, int (*live)(void *), void (*trip)(void *), void *arg ) { TRIG *t; char *s; TP_ZONE *p; asprintf(&s,"ZONE (%g,%g)+((%g,%g),(%g,%g))",x0,y0,ix,iy,jx,jy); p = malloc(sizeof(TP_ZONE)); t = malloc(sizeof(TRIG)); t->link = r->triggers; r->triggers = t; t->text = s; t->ops = &trigops_zone; t->priv = p; p->x = xyunit((XY){ix,iy}); p->y = xyunit((XY){jx,jy}); p->min.x = dot2(p->x,(XY){x0,y0}); p->max.x = p->min.x + hypot(ix,iy); p->min.y = dot2(p->y,(XY){x0,y0}); p->max.y = p->min.y + hypot(jx,jy); p->live = live; p->trip = trip; p->arg = arg; 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); xdir = xyzunit(xdir); ydir = xyzunit(ydir); 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) { ROOM *r; int i; r = malloc(sizeof(ROOM)); r->text = text; r->otherdisp = 0; r->triggers = 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); 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_arrow(int solid) { if (solid) { glBegin(GL_TRIANGLES); glVertex3d(-.3,0,0); glVertex3d(0,0,.5); glVertex3d(.3,0,0); glEnd(); glBegin(GL_QUADS); glVertex3d(-.15,0,-.5); glVertex3d(-.15,0,0); glVertex3d(.15,0,0); glVertex3d(.15,0,-.5); glEnd(); } else { glBegin(GL_QUADS); glVertex3d(-.15,0,-.5); glVertex3d(-.15,0,.025); glVertex3d(-.1,0,.05); glVertex3d(-.1,0,-.5); glVertex3d(.1,0,-.5); glVertex3d(.1,0,.05); glVertex3d(.15,0,.025); glVertex3d(.15,0,-.5); glVertex3d(-.15,0,-.5); glVertex3d(-.15,0,-.45); glVertex3d(.15,0,-.45); glVertex3d(.15,0,-.5); glVertex3d(-.1,0,.05); glVertex3d(-.125,0,0); glVertex3d(-.3,0,0); glVertex3d(-.25,0,.05); glVertex3d(.1,0,.05); glVertex3d(.25,0,.05); glVertex3d(.3,0,0); glVertex3d(.125,0,0); glVertex3d(0,0,.5); glVertex3d(.3,0,0); glVertex3d(.2356904810515469952,0,.01); glVertex3d(-.01,0,.4194841350859116588); glVertex3d(0,0,.5); glVertex3d(.01,0,.4194841350859116588); glVertex3d(-.2356904810515469952,0,.01); glVertex3d(-.3,0,0); glEnd(); } } 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+(switchz[sw]*.5)); rebasis(xdir,ydir,zdir); glBegin(GL_QUAD_STRIP); glColor3f(.2,.2,.2); glVertex3d(-.125,0,0); glVertex3d(-.125,-.5,0); glVertex3d(.125,0,0); glVertex3d(.125,-.5,0); glVertex3d(.125,0,.5); glVertex3d(.125,-.5,.5); glVertex3d(-.125,0,.5); glVertex3d(-.125,-.5,.5); glVertex3d(-.125,0,0); glVertex3d(-.125,-.5,0); glEnd(); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,switchtex[sw]); glBegin(GL_QUADS); glTexCoord2d(0,0); glVertex3d(-.125,-.5,0); glTexCoord2d(0,2); glVertex3d(-.125,-.5,.5); glTexCoord2d(1,2); glVertex3d(.125,-.5,.5); glTexCoord2d(1,0); glVertex3d(.125,-.5,0); 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); glPolygonOffset(0,-10); glPushMatrix(); glColor3f(1,1,1); glScaled(.25,.25,.25); glTranslated(1.2,0,.5); draw_arrow(1); glTranslated(-2.4,0,0); glRotated(180,0,-1,0); draw_arrow(1); glPopMatrix(); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,switchtex[sw]); glBegin(GL_QUADS); glTexCoord2d(0,0); glVertex3d(-.5,-.4,0); glTexCoord2d(0,1); glVertex3d(-.5,0,0); glTexCoord2d(1,1); glVertex3d(-.1,0,0); glTexCoord2d(1,0); glVertex3d(-.1,-.4,0); glTexCoord2d(0,0); glVertex3d(.1,-.4,0); glTexCoord2d(0,1); glVertex3d(.1,0,0); glTexCoord2d(1,1); glVertex3d(.5,0,0); glTexCoord2d(1,0); glVertex3d(.5,-.4,0); glEnd(); glDisable(GL_TEXTURE_2D); glPolygonOffset(0,0); 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,15,1},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_controls(0,(XYZ){4,15,0},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_status(1,(XYZ){5.5,15,1},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_controls(1,(XYZ){5.5,15,0},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_status(2,(XYZ){7,15,1},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_controls(2,(XYZ){7,15,0},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_status(3,(XYZ){8.5,15,1},(XYZ){1,0,0},(XYZ){0,1,0}); draw_switch_controls(3,(XYZ){8.5,15,0},(XYZ){1,0,0},(XYZ){0,1,0}); #ifdef SECRET_ROOM draw_switch_status(4,(XYZ){2,-15,0},(XYZ){-1,0,0},(XYZ){0,-1,0}); draw_switch_status(5,(XYZ){0,-15,0},(XYZ){-1,0,0},(XYZ){0,-1,0}); draw_switch_status(6,(XYZ){-2,-15,0},(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, int active) { if (active && (now.tv_usec < 500000)) return; glPushMatrix(); glTranslated(x0+1.5,y0+9.5,z+.5); glRotated(-45,0,0,-1); glRotated(180,0,1,0); draw_arrow(active); glPopMatrix(); } static void draw_elevator_arrow_u(double x0, double y0, double z, int active) { if (active && (now.tv_usec < 500000)) return; glPushMatrix(); glTranslated(x0+1.5,y0+9.5,z+.5); glRotated(-45,0,0,-1); draw_arrow(active); glPopMatrix(); } 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); if ((*open)(0)) { glBegin(GL_QUADS); if (r->floorz < 4) { glColor3f(.5,.5,.5); glVertex3d(x0+(4*ix)-(.15*jx),y0+(4*iy)-(.15*jy),r->floorz+.9); glVertex3d(x0+(5*ix)-(.15*jx),y0+(5*iy)-(.15*jy),r->floorz+.9); glVertex3d(x0+(5*ix)-(.15*jx),y0+(5*iy)-(.15*jy),r->floorz+1); glVertex3d(x0+(4*ix)-(.15*jx),y0+(4*iy)-(.15*jy),r->floorz+1); glColor3f(.2,.2,.2); glVertex3d(x0+(4*ix)-(.15*jx),y0+(4*iy)-(.15*jy),r->floorz+.9); glVertex3d(x0+(4*ix)-(1.85*jx),y0+(4*iy)-(1.85*jy),r->floorz+.9); glVertex3d(x0+(5*ix)-(1.85*jx),y0+(5*iy)-(1.85*jy),r->floorz+.9); glVertex3d(x0+(5*ix)-(.15*jx),y0+(5*iy)-(.15*jy),r->floorz+.9); } else { glColor3f(.5,.5,.5); glVertex3d(x0+(4*ix)-(.15*jx),y0+(4*iy)-(.15*jy),r->floorz+0); glVertex3d(x0+(5*ix)-(.15*jx),y0+(5*iy)-(.15*jy),r->floorz+0); glVertex3d(x0+(5*ix)-(.15*jx),y0+(5*iy)-(.15*jy),r->floorz+.1); glVertex3d(x0+(4*ix)-(.15*jx),y0+(4*iy)-(.15*jy),r->floorz+.1); glColor3f(.2,.2,.2); glVertex3d(x0+(4*ix)-(.15*jx),y0+(4*iy)-(.15*jy),r->floorz+.1); glVertex3d(x0+(5*ix)-(.15*jx),y0+(5*iy)-(.15*jy),r->floorz+.1); glVertex3d(x0+(5*ix)-(1.85*jx),y0+(5*iy)-(1.85*jy),r->floorz+.1); glVertex3d(x0+(4*ix)-(1.85*jx),y0+(4*iy)-(1.85*jy),r->floorz+.1); } glEnd(); } else { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,switchtex[stn]); glBegin(GL_QUADS); glTexCoord2d(0,0); glVertex3d(x0+(5*ix)-(.15*jx),y0+(5*iy)-(.15*jy),r->floorz); glTexCoord2d(0,1); glVertex3d(x0+(5*ix)-(.15*jx),y0+(5*iy)-(.15*jy),r->floorz+1); glTexCoord2d(1,1); glVertex3d(x0+(4*ix)-(.15*jx),y0+(4*iy)-(.15*jy),r->floorz+1); glTexCoord2d(1,0); glVertex3d(x0+(4*ix)-(.15*jx),y0+(4*iy)-(.15*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,xyzadd(p->sxl,(XYZ){0,0,1}),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); if (r->floorz == curroom->floorz) { draw_elevator_arrow_d(p->x0,p->y0,r->floorz,p->el_active); glPolygonOffset(0,-10); 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 (r->floorz == curroom->floorz) draw_elevator_arrow_u(p->x0,p->y0,r->floorz,p->el_active); 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); } } 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 append_otherdisps(ROOM *t, ROOM *f) { ROOMLIST *l; for (l=f->otherdisp;l;l=l->link) { add_otherdisp(t,l->room); } } 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 return_setup_frags(void) { int i; FRAG *f; double r; double h; for (i=RETURN_FRAGS-1;i>=0;i--) { f = &return_frags[i]; f->colour = (random()|random()) & 7; do { f->p.x = (((random() & 0xffff) * PLAYER_RADIUS*2) / 0xffff) - PLAYER_RADIUS; f->p.y = (((random() & 0xffff) * PLAYER_RADIUS*2) / 0xffff) - PLAYER_RADIUS; r = hypot(f->p.x,f->p.y); } while (r > PLAYER_RADIUS); h = PLAYER_PEAK - (r * ((PLAYER_PEAK - PLAYER_HEIGHT) / PLAYER_RADIUS)); f->p.z = ((random() & 0xffff) * h) / 0xffff; f->d = xyzunit(xyzsub(f->p,(XYZ){0,0,(PLAYER_PEAK+PLAYER_HEIGHT)/4})); f->d = xyzscale(f->d,(((random()&0xffff)*.9)/0xffff)+.1); f->d.x += (((random() & 0xffff) * RETURN_FRAG_NOISE) / 0xffff) - (RETURN_FRAG_NOISE/2); f->d.y += (((random() & 0xffff) * RETURN_FRAG_NOISE) / 0xffff) - (RETURN_FRAG_NOISE/2); f->d.z += (((random() & 0xffff) * RETURN_FRAG_NOISE) / 0xffff) - (RETURN_FRAG_NOISE/2); f->d = xyzscale(f->d,.075); } } 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 int active_not_returning(void *rv __attribute__((__unused__))) { return(return_stage == RETURN_NONE); } static void action_return_to_lobby(void *rv __attribute__((__unused__))) { set_return_stage(RETURN_EXPLODE); return_setup_frags(); } static void action_win(void *arg __attribute__((__unused__))) { printf("You win!\n"); exit(0); } #if ((1 << LINK_N) | (1 << LINK_E)) != 3 #error "Code assumes LINK_N and LINK_E are 0 and 1." #endif 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, -4, -15 }, { -5, -14, -3, -16 }, { -5, -15, 5, -15 }, { 3, -16, 5, -14 }, { 4, -15, .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 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, 12, -6 }, { 12, 16, 12, .5 }, { 12, .5, 10, .5 }, { 10, .5, 10, 16 }, { 11, 15, -17, 15 }, { -16, 16, -16, -6 } }; static const WALLINIT wi_landing_l[] = { { -16, 16, -16, -6 }, { -17, -5, 0, -5 }, { -10, -1, -12, -1 }, { -12, -1, -12, 11 } }; static const WALLINIT wi_landing_u[] = { { 0, 15, -17, 15 }, { -16, 16, -16, -6 }, { -12, -1, -12, 11 }, { -12, 11, -10, 11 } }; static const WALLINIT wi_steps[] = { { -16, 16, -16, -6 }, { -10, -1, -12, -1 }, { -12, -1, -12, 11 }, { -12, 11, -10, 11 } }; static const WALLINIT wi_sw4[] = { { 54, 5.5, 54, 10.5 }, { 54, 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, 54, 11.5 }, { 54, 11.5, 54, 16.5 } }; static const WALLINIT wi_sw5[] = { { 33, 26.5, 28, 26.5 }, { 28, 26.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, 26.5 }, { 27, 26.5, 22, 26.5 } }; static const WALLINIT wi_sw6[] = { { 54, 5.5, 54, 10.5 }, { 54, 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, 54, 11.5 }, { 54, 11.5, 54, 16.5 } }; 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 *mrr[4][4][2]; TRIG *mrg[4][4][2][4]; ROOM *mcr[4][4][2][2]; TRIG *mcg[4][4][2][2][2]; ROOM *r_sw4; ROOM *r_sw5; ROOM *r_sw6; ROOM *r; MRPRIV *rp; CRPRIV *cp; char *txt; WALLINIT wi[27]; int nwi; double x0; double y0; double z0; unsigned int links; unsigned int links2; RECTREGION *rgn; #define ZFORI(i) ((i)*.15) #define YFORI(i) (((i)*.6)-.7) r1 = build_room(0,"lobby",&wi_lobby[0],ASZ(wi_lobby)); r1->render_variable = &render_lobby; g1 = add_gate(r1,-10,-1,-10,-6); add_switch(r1,4-.4,14.75,.2,&active_sw0_is_1,&action_sw0_to_0,0); add_switch(r1,4+.4,14.75,.2,&active_sw0_is_0,&action_sw0_to_1,0); add_switch(r1,5.5-.4,14.75,.2,&active_sw1_is_1,&action_sw1_to_0,0); add_switch(r1,5.5+.4,14.75,.2,&active_sw1_is_0,&action_sw1_to_1,0); add_switch(r1,7-.4,14.75,.2,&active_sw2_is_1,&action_sw2_to_0,0); add_switch(r1,7+.4,14.75,.2,&active_sw2_is_0,&action_sw2_to_1,0); add_switch(r1,8.5-.4,14.75,.2,&active_sw3_is_1,&action_sw3_to_0,0); add_switch(r1,8.5+.4,14.75,.2,&active_sw3_is_0,&action_sw3_to_1,0); 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(5, (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){3.5,-7,0}, (XYZ){0,0,1}, 0.0, 8.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){3.5,7,0}, (XYZ){0,0,1}, 0.173762, 8.0, 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)); g2 = add_gate(r2,-10,16,-10,11); 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)); gu[0] = add_gate(sr[0],-11,YFORI(0),-17,YFORI(0)); gd[0] = add_gate(sr[0],-10,-6,-10,-1); 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)); gu[20] = add_gate(sr[20],-10,11,-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)); 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--) { gate_set_to(gd[i],sr[i-1]); gate_set_to(gu[i-1],sr[i]); } gate_set_to(gd[0],r1); gate_set_to(gu[20],r2); gate_set_to(g1,sr[0]); gate_set_to(g2,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_room_wi(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+jx,cy+jy,cx-(jx*2),cy-(jy*2)}; wi[nwi++] = (WALLINIT){cx-ix-jx,cy-iy-jy,cx+(ix*2)-jx,cy+(iy*2)-jy}; wi[nwi++] = (WALLINIT){cx+ix-(jx*2),cy+iy-(jy*2),cx+ix,cy+iy}; 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+jx,cy+jy,cx-(jx*2),cy-(jy*2)}; wi[nwi++] = (WALLINIT){cx-ix-jx,cy-iy-jy,cx+(ix*2)-jx,cy+(iy*2)-jy}; wi[nwi++] = (WALLINIT){cx+ix-(jx*2),cy+iy-(jy*2),cx+ix,cy+iy}; 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)}; } wi[nwi++] = (WALLINIT){cx+(ix*4),cy+(iy*4),cx+(ix*4)-(jx*2),cy+(iy*4)-(jy*2)}; wi[nwi++] = (WALLINIT){cx+(ix*4)-(jx*2),cy+(iy*4)-(jy*2),cx-ix-(jx*2),cy-iy-(jy*2)}; wi[nwi++] = (WALLINIT){cx+(ix*10)-(jx*2),cy+(iy*10)-(jy*2),cx+(ix*5)-(jx*2),cy+(iy*5)-(jy*2)}; wi[nwi++] = (WALLINIT){cx+(ix*5)-(jx*2),cy+(iy*5)-(jy*2),cx+(ix*5),cy+(iy*5)}; 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); v = link_swX_Y[link_swno[v]][!link_swstate[v]]; st = add_wall(r,cx+(ix*4)-(jx*.15),cy+(iy*4)-(jy*.15),cx+(ix*5)-(jx*.15),cy+(iy*5)-(jy*.15)); wall_set_live(st,active_fns[v],0); } TRIG *setup_g(int dir, double cx, double cy, int ix, int iy, int jx, int jy) { int l; TRIG *g; l = LINKX(links,dir); switch (l) { case LINK_CLOSED: return(0); break; case LINK_OPEN: return(add_gate(r,cx+(ix*3)-jx,cy+(iy*3)-jy,cx+(ix*6)-jx,cy+(iy*6)-jy)); break; default: g = add_gate(r,cx+(ix*3)-(jx*.15),cy+(iy*3)-(jy*.15),cx+(ix*6)-(jx*.15),cy+(iy*6)-(jy*.15)); gate_set_live(g,active_fns[l],0); return(g); break; } } 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) { glBegin(GL_QUADS); glColor3f(1,1,1); glVertex3d(cx+ix,cy+iy,z0); glVertex3d(cx+ix-jx,cy+iy-jy,z0); glVertex3d(cx-jx,cy-jy,z0); glVertex3d(cx,cy,z0); glVertex3d(cx+ix,cy+iy,z0+1); glVertex3d(cx,cy,z0+1); glVertex3d(cx-jx,cy-jy,z0+1); glVertex3d(cx+ix-jx,cy+iy-jy,z0+1); glColor3f(1,0,0); glVertex3d(cx+ix,cy+iy,z0); glVertex3d(cx+ix,cy+iy,z0+1); glVertex3d(cx+ix-jx,cy+iy-jy,z0+1); glVertex3d(cx+ix-jx,cy+iy-jy,z0); glColor3f(0,1,0); glVertex3d(cx+ix-jx,cy+iy-jy,z0); glVertex3d(cx+ix-jx,cy+iy-jy,z0+1); glVertex3d(cx-jx,cy-jy,z0+1); glVertex3d(cx-jx,cy-jy,z0); glColor3f(0,0,1); glVertex3d(cx-jx,cy-jy,z0); glVertex3d(cx-jx,cy-jy,z0+1); glVertex3d(cx,cy,z0+1); glVertex3d(cx,cy,z0); glEnd(); } } void setup_corridor(int dir, double ox, double oy, int ix, int iy, int jx, int jy, double fz) { switch (LINKX(links,dir)) { case LINK_CLOSED: case LINK_OPEN: return; break; } wi[0] = (WALLINIT){ox-ix,oy-iy,ox+(ix*4),oy+(iy*4)}; wi[1] = (WALLINIT){ox+(ix*4),oy+(iy*4),ox+(ix*4)-(jx*2),oy+(iy*4)-(jy*2)}; wi[2] = (WALLINIT){ox+(ix*4)-(jx*2),oy+(iy*4)-(jy*2),ox-ix-(jx*2),oy-iy-(jy*2)}; wi[3] = (WALLINIT){ox+(ix*10)-(jx*2),oy+(iy*10)-(jy*2),ox+(ix*5)-(jx*2),oy+(iy*5)-(jy*2)}; wi[4] = (WALLINIT){ox+(ix*5)-(jx*2),oy+(iy*5)-(jy*2),ox+(ix*5),oy+(iy*5)}; wi[5] = (WALLINIT){ox+(ix*5),oy+(iy*5),ox+(ix*10),oy+(iy*10)}; if (jx == 0) { asprintf(&txt,"corridor (%d,%d-%d,%d)",i,j,j-jy,k); } else { asprintf(&txt,"corridor (%d-%d,%d,%d)",i,i-iy,j,k); } r = build_room(fz,txt,&wi[0],6); cp = malloc(sizeof(CRPRIV)); r->private = cp; mcg[i][j][k][dir][0] = add_gate(r,ox+(ix*5)-(jx*.15),oy+(iy*5)-(jy*.15),ox+(ix*4)-(jx*.15),oy+(iy*4)-(jy*.15)); mcg[i][j][k][dir][1] = add_gate(r,ox+(ix*4)-(jx*1.85),oy+(iy*4)-(jy*1.85),ox+(ix*5)-(jx*1.85),oy+(iy*5)-(jy*1.85)); r->list = glGenLists(1); glNewList(r->list,GL_COMPILE); glEndList(); mcr[i][j][k][dir] = r; } 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; setup_room_wi(LINK_N); setup_room_wi(LINK_S); setup_room_wi(LINK_E); setup_room_wi(LINK_W); r = build_room(z0,txt,&wi[0],nwi); 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; mrr[i][j][k] = r; setup_barriers(LINK_N); setup_barriers(LINK_S); setup_barriers(LINK_E); setup_barriers(LINK_W); mrg[i][j][k][LINK_N] = setup_g(LINK_N,x0+10,y0+10,-1,0,0,-1); mrg[i][j][k][LINK_S] = setup_g(LINK_S,x0+1,y0+1,1,0,0,1); mrg[i][j][k][LINK_E] = setup_g(LINK_E,x0+10,y0+1,0,1,-1,0); mrg[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); add_switch(r,x0+2,y0+9,.3,&active_elevator_active,&action_elevator_up,r); add_zone(r,x0,y0,11,0,0,6,&active_elevator_inactive,&action_elevator_activate,r); add_zone(r,x0+5,y0,6,0,0,11,&active_elevator_inactive,&action_elevator_activate,r); } else if (LINKX(links,LINK_D) != LINK_CLOSED) { draw_elevator_well(x0+1,y0+6,z0-1); add_switch(r,x0+2,y0+9,.3,&active_elevator_active,&action_elevator_dn,r); add_zone(r,x0,y0,11,0,0,6,&active_elevator_inactive,&action_elevator_activate,r); add_zone(r,x0+5,y0,6,0,0,11,&active_elevator_inactive,&action_elevator_activate,r); } st = add_zone(r,x0+8,y0+10+PLAYER_RADIUS,2,0,0,1,&active_not_returning,&action_return_to_lobby,0); glBegin(GL_QUADS); rgn = rrgn_init(); rrgn_add1(rgn,x0,y0,x0+11,y0+11); if (LINKX(links,LINK_D) != LINK_CLOSED) rrgn_sub1(rgn,x0+1,y0+6,x0+5,y0+10); rrgn_sub1(rgn,x0+9,y0+10,x0+10,y0+11); rrgn_scan(rgn,({ void d(double x1, double y1, double x2, double y2) { draw_floor(z0,x1,y1,x1,y2,x2,y2,x2,y1); } &d; })); rrgn_done(rgn); rgn = rrgn_init(); rrgn_add1(rgn,x0,y0,x0+11,y0+11); if (LINKX(links,LINK_U) != LINK_CLOSED) rrgn_sub1(rgn,x0+1,y0+6,x0+5,y0+10); rrgn_sub1(rgn,x0+9,y0+10,x0+10,y0+11); glBegin(GL_QUADS); rrgn_scan(rgn,({ void d(double x1, double y1, double x2, double y2) { draw_ceiling(z0+1,x1,y1,x2,y1,x2,y2,x1,y2); } &d; })); rrgn_done(rgn); 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); if ((i == 2) && (j == 2) && (k == 1)) { glColor3f(1,1,0); glPolygonOffset(0,-10); draw_goal_award(x0,y0,z0); glPolygonOffset(0,0); add_switch(r,x0+8,y0+3,1,0,&action_win,0); goal_room = r; } glEndList(); r->render_variable = &render_maze; setup_corridor(LINK_N,x0+10,y0+10,-1,0,0,-1,k?5.1:3); setup_corridor(LINK_E,x0+10,y0+1,0,1,-1,0,k?5.1:3); } gate_set_to(g2,mrr[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 = mrr[i][j][k]; rp = r->private; if (j < 4-1) { switch (LINKX(rp->links,LINK_N)) { case LINK_CLOSED: break; case LINK_OPEN: gate_set_to(mrg[i][j][k][LINK_N],mrr[i][j+1][k]); break; default: gate_set_to(mrg[i][j][k][LINK_N],mcr[i][j][k][LINK_N]); gate_set_to(mcg[i][j][k][LINK_N][0],r); break; } } if (j > 0) { switch (LINKX(rp->links,LINK_S)) { case LINK_CLOSED: break; case LINK_OPEN: gate_set_to(mrg[i][j][k][LINK_S],mrr[i][j-1][k]); break; default: gate_set_to(mrg[i][j][k][LINK_S],mcr[i][j-1][k][LINK_N]); gate_set_to(mcg[i][j-1][k][LINK_N][1],r); break; } } if (i < 4-1) { switch (LINKX(rp->links,LINK_E)) { case LINK_CLOSED: break; case LINK_OPEN: gate_set_to(mrg[i][j][k][LINK_E],mrr[i+1][j][k]); break; default: gate_set_to(mrg[i][j][k][LINK_E],mcr[i][j][k][LINK_E]); gate_set_to(mcg[i][j][k][LINK_E][0],r); break; } } if (i > 0) { switch (LINKX(rp->links,LINK_W)) { case LINK_CLOSED: break; case LINK_OPEN: gate_set_to(mrg[i][j][k][LINK_W],mrr[i-1][j][k]); break; default: gate_set_to(mrg[i][j][k][LINK_W],mcr[i-1][j][k][LINK_E]); gate_set_to(mcg[i-1][j][k][LINK_E][1],r); break; } } if (LINKX(rp->links,LINK_U) || LINKX(rp->links,LINK_D)) rp->el_to = mrr[i][j][!k]; } gate_set_to(mrg[0][1][0][LINK_W],r2); add_otherdisp(r2,mrr[0][1][0]); add_otherdisp(sr[20],mrr[0][1][0]); add_otherdisp(mrr[0][1][0],r2); add_otherdisp(mrr[0][1][0],sr[20]); add_otherdisp(mrr[0][1][1],r2); for (i=4-1;i>=0;i--) for (j=4-1;j>=0;j--) for (k=2-1;k>=0;k--) { r = mrr[i][j][k]; rp = r->private; add_maze_otherdisp(r,&mrr[0],LINK_N); add_maze_otherdisp(r,&mrr[0],LINK_E); add_maze_otherdisp(r,&mrr[0],LINK_S); add_maze_otherdisp(r,&mrr[0],LINK_W); if (LINKX(rp->links,LINK_U) != LINK_CLOSED) { if (k) abort(); add_otherdisp(r,mrr[i][j][1]); if (LINKX(((MRPRIV *)mrr[i][j][1]->private)->links,LINK_N) != LINK_CLOSED) add_otherdisp(r,mrr[i][j+1][1]); if (LINKX(((MRPRIV *)mrr[i][j][1]->private)->links,LINK_W) != LINK_CLOSED) add_otherdisp(r,mrr[i-1][j][1]); } if (LINKX(rp->links,LINK_D) != LINK_CLOSED) { if (! k) abort(); add_otherdisp(r,mrr[i][j][0]); if (LINKX(((MRPRIV *)mrr[i][j][0]->private)->links,LINK_N) != LINK_CLOSED) add_otherdisp(r,mrr[i][j+1][0]); if (LINKX(((MRPRIV *)mrr[i][j][0]->private)->links,LINK_W) != LINK_CLOSED) add_otherdisp(r,i?mrr[i-1][j][0]:r2); } } for (i=4-1-1;i>=0;i--) for (j=4-1-1;j>=0;j--) for (k=2-1;k>=0;k--) { r = mrr[i][j][k]; rp = r->private; switch (LINKX(rp->links,LINK_N)) { case LINK_CLOSED: case LINK_OPEN: break; default: append_otherdisps(mcr[i][j][k][LINK_N],r); append_otherdisps(mcr[i][j][k][LINK_N],mrr[i][j+1][k]); break; } switch (LINKX(rp->links,LINK_E)) { case LINK_CLOSED: case LINK_OPEN: break; default: append_otherdisps(mcr[i][j][k][LINK_E],r); append_otherdisps(mcr[i][j][k][LINK_E],mrr[i+1][j][k]); break; } } r = 0; r_sw4 = build_room(3,"switch 4 room",&wi_sw4[0],ASZ(wi_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){65,11,3}; rp->sxi = (XYZ){0,-1,0}; rp->sxj = (XYZ){1,0,0}; r_sw4->private = rp; gate_set_to(add_gate(r_sw4,55,12.5,55,9.5),mrr[3][2][0]); gate_set_to(mrg[3][2][0][LINK_E],r_sw4); add_switch(r_sw4,64.75,11+.4,.2,&active_sw4_is_1,&action_sw4_to_0,0); add_switch(r_sw4,64.75,11-.4,.2,&active_sw4_is_0,&action_sw4_to_1,0); 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,mrr[3][2][0]); add_otherdisp(mrr[3][2][0],r_sw4); r_sw5 = build_room(3,"switch 5 room",&wi_sw5[0],ASZ(wi_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.5,3}; rp->sxi = (XYZ){1,0,0}; rp->sxj = (XYZ){0,1,0}; r_sw5->private = rp; gate_set_to(add_gate(r_sw5,26,27.5,29,27.5),mrr[1][3][0]); gate_set_to(mrg[1][3][0][LINK_N],r_sw5); add_switch(r_sw5,27.5-.4,37.25,.2,&active_sw5_is_1,&action_sw5_to_0,0); add_switch(r_sw5,27.5+.4,37.25,.2,&active_sw5_is_0,&action_sw5_to_1,0); 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,mrr[1][3][0]); add_otherdisp(r_sw5,mrr[1][2][0]); add_otherdisp(r_sw5,mrr[1][1][0]); add_otherdisp(mrr[1][3][0],r_sw5); add_otherdisp(mrr[1][2][0],r_sw5); add_otherdisp(mrr[1][1][0],r_sw5); r_sw6 = build_room(5,"switch 6 room",&wi_sw6[0],ASZ(wi_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){65,11,5}; rp->sxi = (XYZ){0,-1,0}; rp->sxj = (XYZ){1,0,0}; r_sw6->private = rp; gate_set_to(add_gate(r_sw6,55,12.5,55,9.5),mrr[3][2][1]); gate_set_to(mrg[3][2][1][LINK_E],r_sw6); add_switch(r_sw6,64.75,11+.4,.2,&active_sw6_is_1,&action_sw6_to_0,0); add_switch(r_sw6,64.75,11-.4,.2,&active_sw6_is_0,&action_sw6_to_1,0); 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,mrr[3][2][1]); add_otherdisp(mrr[3][2][1],r_sw6); lobby_room = r1; entrance_room = r2; first_room = mrr[0][1][0]; sw5_room = r_sw4; sw6_room = r_sw5; sw7_room = r_sw6; curroom = lobby_room; } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); open_display(); setup_random(); setup_visual(); setup_X(); setup_context(); setup_ui(); setup_textures(); setup_switches(); create_window(); setup_gl(); setup_input(); setup_view(); setup_ticks(); setup_lists(); setup_maze(); while (1) tick(); }