#include <stdlib.h>  /* For rand() */
#include <math.h>    /* For sqrt() (distance checks in IR clipping) and sin() cos() for card init */
#include "world.h"
#include "smalloc.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846264338327
#endif

/* Number of repetitions of the infinity room. Has a major impact on performance. Big -> slow. */
#define INFIN_REPEATS 4
/* Smoothness of cylinders used in the tea room. Larger -> more polygons -> slower. */
#define CYLSMOOTHNESS 8

#define INFIN_YMIN ((INFIN_REPEATS * -6.) - 3.)
#define INFIN_YMAX ((INFIN_REPEATS *  6.) + 3.)
#define CARDS 256 /* (INFIN_REPEATS * 256) */

typedef struct {
	triangle* polys;
	movable position;
} playingcard;

triangle* world_makering(GLfloat yoffset); /* Internal method of infinity ring generation */
void world_initcard(); /* Likewise for playing cards */
void world_mushroom(world* w); /* Draw a mushroom */

playingcard world_cards[CARDS]; /* Naughty, but saves indirection for frequently-accessed array */

texture* t_brushedmetal;
texture* t_cardback;
texture* t_cardace;
texture* t_circuit;
texture* t_circuitfade;
texture* t_crystalmetal;
texture* t_darkplastic;
texture* t_dimpledmetal;
texture* t_glass;
texture* t_gold;
texture* t_gravel;
texture* t_hat;
texture* t_helpscreen;
texture* t_patchyrust;
texture* t_pdp11b;
texture* t_pdp11c;
texture* t_pdp11s;
texture* t_pdp11scat; /* Ahem...PDP-11s with Cheshire Cat. */
texture* t_pdp11panelb;
texture* t_rust;
	
void world_init(world* w) {
	int i;
	triangle* cur;

	t_brushedmetal = texture_frompnm("brushedmetal512.pnm", 512); /* ts1 for help room */
	t_cardback     = texture_frompnm("cardback512.pnm",     512);
	t_cardace      = texture_frompnm("cardace512.pnm",      512);
	t_circuit      = texture_frompnm("circuit512.pnm",      512); /* ts1 for help room */
	t_circuitfade  = texture_frompnm("circuitfade512.pnm",  512); /* ts1 for help room */
	t_crystalmetal = texture_frompnm("crystalmetal512.pnm", 512); /* ts1 for help room */
	t_darkplastic  = texture_frompnm("darkplastic256.pnm",  256);
	t_dimpledmetal = texture_frompnm("dimpledmetal512.pnm", 512); /* ts1 for help room */
	t_glass        = texture_frompnm("glass256.pnm",        256);
	t_gold         = texture_frompnm("gold128.pnm",         128);
	t_gravel       = texture_frompnm("gravel128.pnm",       128); /* ts4 for help room */
	t_hat          = texture_frompnm("hat256.pnm",          256);
	t_helpscreen   = texture_frompnm("helpscreen512.pnm",   512);
	t_patchyrust   = texture_frompnm("patchyrust512.pnm",   512); /* ts1 for help room */
	t_pdp11b       = texture_frompnm("pdp11b512.pnm",       512);
	t_pdp11c       = texture_frompnm("pdp11c512.pnm",       512);
	t_pdp11s       = texture_frompnm("pdp11s256.pnm",       256);
	t_pdp11scat    = texture_frompnm("pdp11scat256.pnm",    256);
	t_pdp11panelb  = texture_frompnm("pdp11panelb512.pnm",  512);
	t_rust         = texture_frompnm("rust512.pnm",         512); /* ts1 for help room */
	
	/* Help room */
	w->roomhelp = triangle_makeroom(-2., -2., 2., -2., 2., 2., -2., 2., t_crystalmetal, 1);
	cur = triangle_tail(w->roomhelp);
	cur->next   = triangle_makewall( 2., -2., -2., -2., t_brushedmetal, 1);
	cur = triangle_tail(w->roomhelp);
	cur->next   = triangle_makewall(-2., -2., -2., -1., t_brushedmetal, 1);
	cur = triangle_tail(w->roomhelp);
	cur->next   = triangle_makewall(-2.,  1., -2.,  2., t_brushedmetal, 1);
	cur = triangle_tail(w->roomhelp);
	cur->next   = triangle_makewall(-2.,  2., -1.,  2., t_brushedmetal, 1);
	cur = triangle_tail(w->roomhelp);
	cur->next   = triangle_makewall( 1.,  2.,  2.,  2., t_brushedmetal, 1);
	cur = triangle_tail(w->roomhelp);
	cur->next   = triangle_makewall( 2.,  2.,  2.,  1., t_brushedmetal, 1);
	cur = triangle_tail(w->roomhelp);
	cur->next   = triangle_makewall( 2., -1.,  2., -2., t_brushedmetal, 1);

	w->helpscreen = triangle_makewallx(0.5, 0., -0.5, 0., -0.375, 0.375, t_helpscreen, 1);
	cur = w->helpscreen; cur->r.v = 0.75; cur->next->p.v = 0.75; cur->next->r.v = 0.75;
	cur = triangle_tail(cur); /* Room is built with inverted floor/ceil for inverted norms. */
	cur->next = triangle_makeroomx(-0.5, -0.5, 0.5, -0.5, 0.5, 0., -0.5, 0., 0.375, -0.375, t_crystalmetal, .25); cur = triangle_tail(cur);
	cur->next = triangle_makewallx(-0.5, 0., -0.5, -0.5, -0.375, 0.375, t_crystalmetal, .25);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx(.5, -0.5, .5, 0., -0.375, 0.375, t_crystalmetal, .25);
	
	/* Corridors */
	/* west */
	w->corridors = triangle_makeroom(-6., -1., -2., -1., -2.,  1., -6.,  1., t_crystalmetal,.5);
	cur = triangle_tail(w->corridors);
	/* south */
	cur->next    = triangle_makeroom(-1.,  2.,  1.,  2.,  1.,  4., -1.,  4., t_crystalmetal,.5);
	cur = triangle_tail(cur);
	/* east */
	cur->next    = triangle_makeroom( 2., -1.,  5., -1.,  4.,  1.,  2.,  1., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next    = triangle_makeroom( 5., -1., 11.,  1., 10.,  3.,  4.,  1., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next    = triangle_makeroom(11.,  1., 12.,  1., 12.,  3., 10.,  3., t_gravel, 2);
	/* west */
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall(-2., -1., -6., -1., t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall(-6.,  1., -2.,  1., t_brushedmetal, 1);
	/* south */
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall(-1.,  2., -1.,  4., t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall( 1.,  4.,  1.,  2., t_brushedmetal, 1);
	/* east */
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall( 2.,  1.,  4.,  1., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall( 4.,  1., 10.,  3., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall(10.,  3., 12.,  3., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall(12.,  1., 11.,  1., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall(11.,  1.,  5., -1., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall( 5., -1.,  2., -1., t_gravel, 2);
	
	/* Mirror room */
	w->roommirror = triangle_makeroom(-12., -2., -6., -2., -6., 2., -12., 2., t_crystalmetal,1);
	cur = triangle_tail(w->roommirror);
	cur->next     = triangle_makewall(-6., -1., -6., -2., t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next     = triangle_makewall(-6., -2.,-12., -2., t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next     = triangle_makewall(-12.,-2.,-12.,-0.5, t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next     = triangle_makewall(-12.,0.5,-12., 2.0, t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next     = triangle_makewallx(-12.,-0.5,-12.,.5, -1.,-.8, t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next     = triangle_makewallx(-12.,-0.5,-12.,.5, 0.5,1., t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next     = triangle_makewall(-12., 2., -6.,  2., t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next     = triangle_makewall(-6.,  2., -6.,  1., t_brushedmetal, 1);
	/* PDPs */
	cur = triangle_tail(cur);
	cur->next     = triangle_makewall(-11.,-1.5,-12., -1.5, t_darkplastic, 1);
	cur = triangle_tail(cur);
	cur->next     = triangle_makewall(-12., 1.5,-11.,  1.5, t_darkplastic, 1);
	cur = triangle_tail(cur);
	cur->next     = triangle_makewallx(-11.,-1.5,-11., -2., 1., -1., t_pdp11b, 1);
	cur->next->q.u = 0.28; cur->next->next->q.u = 0.28; cur->next->next->r.u = 0.28;
	cur = triangle_tail(cur);
	cur->next     = triangle_makewallx(-11., 2.,-11.,  1.5, 1., -1., t_pdp11c, 1);
	cur->next->q.u = 0.3; cur->next->next->q.u = 0.3; cur->next->next->r.u = 0.3;
	cur = triangle_tail(cur);
	/* Mirror */
	cur = triangle_tail(cur);
cur->next = triangle_makeroomx(-12, -0.5, -11.9, -0.5, -11.9, 0.5, -12., 0.5, 0.5,-0.8,t_gold,1);
	cur = triangle_tail(cur);
cur->next = triangle_makeroomx(-12, -0.5, -11.9, -0.5, -11.9, 0.5, -12., 0.5,-0.7, 0.4,t_gold,1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-11.9,-.5, -11.9, .5,0.4,0.5,t_gold,1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-11.9,-.5, -11.9, .5,-.8,-.7,t_gold,1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-11.9,-.4, -12.0,-.4,-.7,0.4,t_gold,1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-12.0,-.5, -11.9,-.5,-.8,0.5,t_gold,1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-12.0,0.4, -11.9,0.4,-.7,0.4,t_gold,1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-11.9,0.5, -12.0,0.5,-.8,0.5,t_gold,1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-11.9,-.5, -11.9,-.4,-.8,0.5,t_gold,1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-11.9,0.4, -11.9,0.5,-.8,0.5,t_gold,1);
	/* Wall of PDP-11s (and one cat) */
	cur = triangle_tail(cur);
	cur->next = triangle_makeroom(-14., -2., -12., -2., -12., 2., -14., 2., t_crystalmetal,1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-14.0, -1., -14.0,-3.,1,-1,t_pdp11s, 1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-14.0,  1., -14.0,-1.,1,-1,t_pdp11scat, 1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-14.0,  3., -14.0, 1.,1,-1,t_pdp11s, 1);
cur = triangle_tail(cur); cur->next = triangle_makewall(-12.,-1.,-13.,-1.,t_darkplastic,1);
cur = triangle_tail(cur); cur->next = triangle_makewall(-13., 1.,-12., 1.,t_darkplastic,1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-12.,-0.5, -12.,0.5, -.8,0.5, t_glass,2);
	cur = cur->next; cur->p.a = cur->q.a = cur->r.a = 0.5;
	cur = cur->next; cur->p.a = cur->q.a = cur->r.a = 0.5;

	/* Tea room */
	w->roomtea = triangle_makeroom( 12., -2, 24., -2, 24., 6., 12., 6., t_gravel, 8);
	/* Modify verticies for skewed floor */
	w->roomtea->q.y -= 1.; w->roomtea->r.y -= 1.; w->roomtea->next->p.y -= 1.;
	cur = triangle_tail(w->roomtea);
	cur->next  = triangle_makewallx(12.,  3., 12.,  6., -2., 1., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next  = triangle_makewallx(12.,  6., 24.,  6., -2., 1., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next  = triangle_makewallx(24.,  6., 24., -2., -2., 1., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next  = triangle_makewallx(24., -2., 12., -2., -2., 1., t_gravel, 2);
	cur = triangle_tail(cur);
	cur->next  = triangle_makewallx(12., -2., 12.,  1., -2., 1., t_gravel, 2);
	
	/* Infinity room */
	w->roominfinity = world_makering(-2.); cur = triangle_tail(w->roominfinity);
	cur->next = world_makering(0.); cur = triangle_tail(cur);
	cur->next = world_makering(2.); cur = triangle_tail(cur);
	w->roominfinnr = triangle_makeroomx(-8., 4., 8., 4., 8., 20., -8., 20.,
	                   INFIN_YMIN, INFIN_YMAX, t_circuitfade, 1);
	/* fake doorway for mirrored platforms */
	w->roominfindr = triangle_makeroom(-1., 0., 1., 0., 1., 4., -1., 4., t_crystalmetal, .5);
	cur = triangle_tail(w->roominfindr);
	cur->next    = triangle_makewall(-1.,  0., -1.,  4., t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall( 1.,  4.,  1.,  0., t_brushedmetal, 1);
	cur = triangle_tail(cur);
	cur->next    = triangle_makewall( 1.,  0., -1.,  0., 0, 1);
	/* walkway and platform */
	cur = triangle_tail(w->roominfinity);
cur->next = triangle_makeroomx(-1.,  4.,  1.,  4.,  1., 8.,-1., 8., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
cur->next = triangle_makeroomx( 0.,  8.,  4., 12.,  0.,16.,-4.,12., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
cur->next = triangle_makeroomx(-4., 12., -4., 10., -2., 8., 0., 8., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
cur->next = triangle_makeroomx( 0.,  8.,  2.,  8.,  4.,10., 4.,12., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
cur->next = triangle_makeroomx( 4., 12.,  4., 14.,  2.,16., 0.,16., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
cur->next = triangle_makeroomx( 0., 16., -2., 16., -4.,14.,-4.,12., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx(-1., 4., -1., 8., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx(-1., 8., -2., 8., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx(-2., 8., -4.,10., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx(-4.,10., -4.,14., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx(-4.,14., -2.,16., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx(-2.,16.,  2.,16., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx( 2.,16.,  4.,14., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx( 4.,14.,  4.,10., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx( 4.,10.,  2., 8., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx( 2., 8.,  1., 8., -1., -1.2, t_dimpledmetal, 1);
	cur = triangle_tail(cur);
	cur->next = triangle_makewallx( 1., 8.,  1., 4., -1., -1.2, t_dimpledmetal, 1);
	/* little console-thingy */
	cur = triangle_tail(cur);
	cur->next = triangle_makeroomx(.5, 16., -.5, 16., -.5, 15.5, .5, 15.5, 0., -1., t_pdp11panelb, 1);
	cur = cur->next; cur->r.v = 0.45; cur = cur->next;
	cur->p.v = 0.45; cur->q.v = 0.45;
cur = triangle_tail(cur); cur->next = triangle_makewallx( .5,15.5,-.5,15.5,0.,-1.,t_darkplastic, 1);
cur = triangle_tail(cur); cur->next = triangle_makewallx( .5,16. , .5,15.5,0.,-1.,t_darkplastic, 1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-.5,16. , .5,16. ,0.,-1.,t_darkplastic, 1);
cur = triangle_tail(cur); cur->next = triangle_makewallx(-.5,15.5,-.5,16. ,0.,-1.,t_darkplastic, 1);
	/* endless rain of cards */
	for(i = 0; i < CARDS; i++)
		{ world_initcard(&world_cards[i]); }

	/* Other miscellany */
	movable_init(&w->mushpos); w->mushpos.x = 16.; w->mushpos.z = 2.; w->mushpos.yav = 45.;
	primcylinder_init(&w->unitcyl, CYLSMOOTHNESS);
}

triangle* world_makering(GLfloat y) {
	triangle* t, * cur; /* Return a chain of walls */
	t   = triangle_makewallx( 2., 4.,  1., 4.,y-1,y  , t_circuit, 1); cur = triangle_tail(t);
cur->next = triangle_makewallx( 2., 4.,  1., 4.,y  ,y+1, t_circuit, 1); cur = triangle_tail(cur);
cur->next = triangle_makewallx(-1., 4., -2., 4.,y-1,y  , t_circuit, 1); cur = triangle_tail(cur);
cur->next = triangle_makewallx(-1., 4., -2., 4.,y  ,y+1, t_circuit, 1); cur = triangle_tail(cur);
	if(y != 0) {
cur->next = triangle_makewallx( 1., 4., -1., 4.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
	}
cur->next = triangle_makewallx(-2., 4., -6., 6.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx(-6., 6., -8.,10.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx(-8.,10., -8.,12.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx(-8.,12., -8.,14.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx(-8.,14., -6.,18.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx(-6.,18., -2.,20.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx(-2.,20.,  0.,20.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx( 0.,20.,  2.,20.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx( 2.,20.,  6.,18.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx( 6.,18.,  8.,14.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx( 8.,14.,  8.,12.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx( 8.,12.,  8.,10.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx( 8.,10.,  6., 6.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
cur->next = triangle_makewallx( 6., 6.,  2., 4.,y-1,y+1, t_circuit, 2); cur = triangle_tail(cur);
	return t;
}

void world_initcard(playingcard *p) {
	triangle* t;
	GLfloat r, theta;
	p->polys = triangle_makewallx(0.,  0.1, 0., -0.1, -0.15, 0.15, t_cardback, 1);
	p->polys->q.u = 0.65; p->polys->next->q.u = 0.65; p->polys->next->r.u = 0.65;
	t = triangle_tail(p->polys);
	t->next = triangle_makewallx(0., -0.1, 0.,  0.1, -0.15, 0.15, t_cardace,  1); t = t->next;
	       t->q.u = 0.65;        t->next->q.u = 0.65;        t->next->r.u = 0.65;
	movable_init(&p->position);
	p->position.xav = rand() / (RAND_MAX / 90.);
	p->position.yav = rand() / (RAND_MAX / 90.);
	p->position.zav = rand() / (RAND_MAX / 90.);
	p->position.yv =-(rand() / (RAND_MAX / 2.) + 1.);
	p->position.y   = rand() / (RAND_MAX / (INFIN_YMAX * 2)) - INFIN_YMAX;
	r               = rand() / (RAND_MAX / 1.) + 6.5;
	theta           = rand() / (RAND_MAX / (M_PI * 2));
	p->position.x   = r * sin(theta);
	p->position.z   = r * cos(theta) + 12.;
	if(p->position.x > -1.1 && p->position.x < 1.1 && p->position.z < 12) {
		/* Blocking the 'bridge' bit -- swap sides */
		p->position.z = 19.;
	}
}

void cards_draw(void) {
	int i;
	for(i = 0; i < CARDS; i++) {
		glPushMatrix();
		movable_locate(&(world_cards[i].position));
		triangle_draw(world_cards[i].polys);
		glPopMatrix();
	}
}

void world_draw(world* w, movable* cam) { /* cam used for culling */
	int i;
	GLfloat s[4], t[4];
	glLoadIdentity();

	/* Never did do the doors =( */
	
	/* Help room */
	triangle_draw(w->roomhelp);
	glPushMatrix();
	glRotatef(180, 0., 0., 1.); /* The help screen itself is upside-down */
	/* Draw the rest of it */
	glTranslatef(0., 0.25, -1.9);
	glRotatef(11.25, 1., 0., 0.);
	triangle_draw(w->helpscreen);
	glPopMatrix();
	
	/* Corridors */
	triangle_draw(w->corridors);
	
	/* If not in the infinity room... */
	if(!(cam->x < 10 && cam->z > 4)) {
		/* Infinity room lights off */
		glDisable(GL_LIGHT5);
		
		t[0] = 0.; t[1] = 0.; t[2] = 0.; t[3] = 1.;
		glLightfv(GL_LIGHT4, GL_POSITION, t);
		s[0] = 0.00; s[1] = 0.00; s[2] = 1.00; s[3] = 1.;
		glLightfv(GL_LIGHT4, GL_DIFFUSE, s);
		
		/* Mirror room */
		triangle_draw(w->roommirror);
	
		/* Tea room */
		/* Geometry */
		triangle_draw(w->roomtea);
		/* Teapot */
		texture_use(t_rust);
		glPushMatrix(); /* Start of teapot */
		glTranslatef(22., -1., 2.);
		glRotatef(180., 0., 1., 0.);
		glRotatef(-15., 0., 0., 1.);
		glutSolidTeapot(1.);
		/* legs */
		texture_use(t_patchyrust);
		glPushMatrix(); /* Start of legs */
		glRotatef(90, 0., 1., 0.);
		glRotatef(90, 0., 0., 1.);
		for(i = 0; i < 2; i++) {
		glPushMatrix(); /* Start of first leg rotation */         glTranslatef(-.3,.9,0.);
		glPushMatrix(); glScalef(0.1, 1.0, 0.1); primcylinder_draw(&w->unitcyl);
		glPopMatrix(); glPushMatrix();
		glTranslatef(0.,1.,0.); glRotatef( 30., 0.,0.,1.); glScalef(0.1, 1.0, 0.1);
		primcylinder_draw(&w->unitcyl); glPopMatrix();
		
		glPopMatrix(); glPushMatrix(); glRotatef(-30., 1.,0.,0.); glTranslatef(-.3,.9,0.);
		glPushMatrix(); glScalef(0.1, 1.0, 0.1); primcylinder_draw(&w->unitcyl);
		if(i == 0) {
			glPopMatrix(); glPushMatrix();
			glTranslatef(0.,1.,0.); glRotatef(60., 0.,0.,1.); glScalef(0.1, 1.0, 0.1);
			primcylinder_draw(&w->unitcyl);
		} glPopMatrix();

		glPopMatrix(); glPushMatrix(); glRotatef( 30., 1.,0.,0.); glTranslatef(-.3,.9,0.);
		glPushMatrix(); glScalef(0.1, 1.0, 0.1); primcylinder_draw(&w->unitcyl);
		if(i == 1) {
			glPopMatrix(); glPushMatrix();
			glTranslatef(0.,1.,0.); glRotatef(60., 0.,0.,1.); glScalef(0.1, 1.0, 0.1);
			primcylinder_draw(&w->unitcyl);
		} glPopMatrix();

		glPopMatrix(); /* End of third leg */
		glRotatef(180., 1., 0., 0.);
		}
		glPopMatrix(); /* End of legs */
		/* hat */
		texture_use(t_hat);
		glTranslatef(0.0, 0.5, 0.0);
		glScalef(0.8, 0.5, 0.8); primcylinder_draw(&w->unitcyl);
		glScalef(1.5, 0.2, 1.5); primcylinder_draw(&w->unitcyl);
		glPopMatrix(); /* End of teapot */

		/* Mushrooms */
		t[0] = 0.; t[1] = 0.; t[2] = 0.; t[3] = 1.;
		glPushMatrix();
		 movable_locate(&w->mushpos);
		 glPushMatrix();
		  glTranslatef(2.0, 0.0, 0.0);
		  world_mushroom(w);
		  glLightfv(GL_LIGHT6, GL_POSITION, t);
		 glPopMatrix();
		 glRotatef(120, 0.0, 1.0, 0.0);
		 glPushMatrix();
		  glTranslatef(2.0, 0.0, 0.0);
		  world_mushroom(w);
		  glLightfv(GL_LIGHT7, GL_POSITION, t);
		 glPopMatrix();
		 glRotatef(120, 0.0, 1.0, 0.0);
		 glPushMatrix();
		  glTranslatef(2.0, 0.0, 0.0);
		  world_mushroom(w);
		  glLightfv(GL_LIGHT4, GL_POSITION, t);
		 glPopMatrix();
		glPopMatrix();
	} else {
		/* Infinity room lights on */
		glEnable(GL_LIGHT5);
		
		t[0] = 0.; t[1] =-4.; t[2] = 18.; t[3] = 1.;
		glLightfv(GL_LIGHT4, GL_POSITION, t);
		s[0] = 0.75; s[1] = 1.; s[2] = 0.75; s[3] = 1.;
		glLightfv(GL_LIGHT4, GL_DIFFUSE, s);
	}
	
	/* Infinity room */
	if(!(cam->x < -6 && cam->z < 4) && !(cam->x > 10)) {
		/* Not drawn if in mirror or tea rooms */
		/*  Geometry */
		triangle_draw(w->roominfinity);
		glPushMatrix();
		for(i = 0; i < INFIN_REPEATS; i++) {
			glTranslatef(0.,  6., 0.);
			triangle_draw(w->roominfinity);
			triangle_draw(w->roominfindr);
			cards_draw();
		}
		glPopMatrix(); glPushMatrix();
		for(i = 0; i < INFIN_REPEATS; i++) {
			glTranslatef(0., -6., 0.);
			triangle_draw(w->roominfinity);
			triangle_draw(w->roominfindr);
			cards_draw();
		}
		glPopMatrix();
		triangle_draw(w->roominfinnr);
		/*  Actors   */
		/* for(i = 0; i < CARDS; i++) {
			glPushMatrix();
			movable_locate(&(world_cards[i].position));
			triangle_draw(world_cards[i].polys);
			glPopMatrix();
		} */
		cards_draw();
	}
}

void world_animate(world* w, int elapsed) { /* Remember, elapsed is in milliseconds now */
	int i;
	/* Mushrooms */
	movable_animate(&w->mushpos, elapsed);
	w->mushpos.y = (sin((w->mushpos.ya / 180) * M_PI) - 1) / 2;
	/* Tea (sadly not) */
	/* Playing cards */
	for(i = 0; i < CARDS; i++) {
		movable_animate(&(world_cards[i].position), elapsed);
		if(world_cards[i].position.y < -3. /*INFIN_YMIN*/) {
			world_cards[i].position.y = 3. /*INFIN_YMAX*/;
		}
	}
}

void world_clipcam(movable* cam) {
	GLfloat ez, d; /* Effective Z, distance */
	/* Limits are just inside the walls, to allow for the minimum view distance */
#define SETMAX(p,q) p = p > q ? q : p /* Limit p to max of (p, q) */
#define SETMIN(p,q) p = p < q ? q : p /* Limit p to min of (p, q) */
	/* Very outside of the non-infinityroom world */
	/* (Plus clipping off the PDP-11 end of the mirror room (prev. -11.9)) */
	SETMAX(cam->x, 23.9); SETMIN(cam->x, -10.9); SETMIN(cam->z, -1.9);
	/* Inside the infinity room? */
	if(cam->z > 4 && cam->x < 11) {
		/* Camera wrapping */
		if(cam->y < -3.)
			{ cam->y += 6.; }
		if(cam->y >  3.)
			{ cam->y -= 6.; }
		/* Actual clipping (outer wall) */
		if(cam->x > -1 && cam->x < 1) {
			if(!(cam->y > -1 && cam->y < 1)) {
				/* Block exit through doorway if too high/low */
				SETMIN(cam->z, 4.5); /* Extra allowance to stay in IR bounds */
			}
			/* Can't go through opposing wall! */
			SETMAX(cam->z, 19.9);
		} else {
			/* Clip with outer wall */
			ez = cam->z - 12.; /* ez is now relative to centre of room */
			d = sqrt((cam->x * cam->x) + (ez * ez));
			if(d > 7.9) {
				/* Penetrated wall */
				d /= 7.9;
				cam->x /= d;
				ez /= d; cam->z = ez + 12.;
			}
		}
		/* Platform clipping, including console-thingy */
		if((cam->x > -1 && cam->x < 1 && cam->z > 4 && cam->z <  8) /* bridge */
		|| (cam->x > -4 && cam->x < 4 && cam->z > 8 && cam->z < 16)) { /* platform */
			/* The Y co-ords are weird here for some reason (?) */
			if(cam->y < 1.1) { SETMAX(cam->y, 0.8); }
			if(cam->y > 1.1) { SETMIN(cam->y, 2.3); }
			if(cam->x > -.5 && cam->x < .5 && cam->z > 15.5 && cam->z < 16) {
				/* console */
				SETMAX(cam->y, -0.1);
			}
		}
	} else {
		/* Height constraints */
		SETMAX(cam->y, 0.9); SETMIN(cam->y, -0.9);
		/* South edge of everything west of the help room */
		if(cam->x < -2) { SETMAX(cam->z, 1.9); }
		/* Prevent penetration of the help screen */
		if(cam->x > -0.5 && cam->x < 0.5) { SETMIN(cam->z, -1.8); }
		/* Mirror room corridor (edges from west to east) */
		if(cam->x < -5 && cam->z >  1) { SETMAX(cam->x, -6.1); } /* 'Sloppy' X on these */
		if(cam->x < -5 && cam->z < -1) { SETMAX(cam->x, -6.1); } /* to prevent large    */
		if(cam->x > -3 && cam->z >  1) { SETMIN(cam->x, -1.9); } /* changes in camera   */
		if(cam->x > -3 && cam->z < -1) { SETMIN(cam->x, -1.9); } /* position skipping   */
		if(cam->x > -6 && cam->x < -2)
			{ SETMIN(cam->z, -0.9); SETMAX(cam->z, 0.9); }
		/* Infinity room corridor */
		if(cam->x > -2 && cam->x < -1) { SETMAX(cam->z, 1.9); }
		if(cam->x >  1 && cam->x <  2) { SETMAX(cam->z, 1.9); }
		if(cam->x > -2 && cam->x <  2 && cam->z > 2)
			{ SETMIN(cam->x, -0.9); SETMAX(cam->x, 0.9); }
		/* Tea corridor */
		if(cam->x > 2 && cam->x < 5) { SETMIN(cam->z, -0.9); }
		if(cam->x > 2 && cam->x < 4) { SETMAX(cam->z,  0.9); }
		if(cam->x > 4 && cam->x < 10) /* Lower diagonal */
			/* (x - minxofline) / dx * dy + minyoflinewithpadding */
			{ SETMAX(cam->z, ((((cam->x - 4.) / 6.) * 2.) + 0.9)); }
		if(cam->x > 5 && cam->x < 11) /* Upper diagonal */
			{ SETMIN(cam->z, ((((cam->x - 5.) / 6.) * 2.) - 0.9)); }
		if(cam->x > 11 && cam->x < 12) { SETMIN(cam->z, 1.1); }
		if(cam->x > 10 && cam->x < 12) { SETMAX(cam->z, 2.9); }
		/* West edges of tea room */
		if(cam->x > 11 && cam->z < 1) { SETMIN(cam->x, 12.1); }
		if(cam->x > 11 && cam->z > 3) { SETMIN(cam->x, 12.1); }
		/* Teapot (nice, curvy clipping) */
		d = (cam->z - 2) / 2;
		SETMAX(cam->x, 19.9 + (d * d));
		/* South edge of tea room */
		SETMAX(cam->z, 5.9);
	}
#undef SETMIN
#undef SETMAX
}

void world_mushroom(world* w) {
	texture_use(t_gold); /* Yes, golden mushrooms */
	                             glScalef(0.25, 0.25, 0.25);
	primcylinder_drawx(&w->unitcyl, 0.5);
	glTranslatef(0.0, 1.0, 0.0); glScalef(0.5, 1.0, 0.5);
	primcylinder_drawx(&w->unitcyl, 0.5);
	glTranslatef(0.0, 1.0, 0.0); glScalef(6.0, 1.0, 6.0);
	primcylinder_drawx(&w->unitcyl, 0.5);
	glTranslatef(0.0, 1.0, 0.0); glScalef(0.5, 0.25, 0.5);
	primcylinder_drawx(&w->unitcyl, 0.5);
	glTranslatef(0.0, 1.0, 0.0); glScalef(0.5, 0.25, 0.5);
	primcylinder_drawx(&w->unitcyl, 0.01);
}
