#!/usr/local/bin/perl 

# simple maze thingy

# in case module not "installed" :
BEGIN{ unshift(@INC,"./blib"); unshift(@INC,"../blib"); }
BEGIN{ unshift(@INC,"../blib/arch"); } # 5.002 gamma needs this
BEGIN{ unshift(@INC,"../blib/lib"); } # 5.002 gamma needs this
use OpenGL;

@walldata = (
	"****************",
	"* *     *      *",
	"* * *** * *    *",
	"* *   * ** * * *",
	"*   * *      * *",
	"********** *** *",
	"*           *  *",
	"* ***** *** ****",
	"* *   *   *    *",
	"*   * *** *    *",
	"* *   *   *  * *",
	"* ***** **** * *",
	"*     *      * *",
	"***** ******** *",
	"*   * *     *  *",
	"* ***   *** ****",
	"*     *   *    *",
	"****************",
	);


$x_size = scalar(@walldata);
$y_size = length($walldata[0]);
($x_size > 3 && $x_size<200) || die "bad data - number of rows $x_size ";
($y_size > 3 && $y_size<200) || die "bad data - number of columns $y_size ";


	for($x=0;$x<$x_size;$x++) {
		($y_size==length($walldata[$x])) || 
		    die "line $x of data ",length($walldata[$x]),"/$y_size chars";
		$walldata[$x] =~ s/ /0/g;
		$walldata[$x] =~ s/\*/1/g;
		my(@ww)=split(//,$walldata[$x]);
		#print "$wall[$x] - @ww -";
		$wall[$x] = \ @ww;
		#print "@{$wall[$x]}\n";
	}
	$s=1;
    	@cx=(0,0,0,0,$s,$s,$s,$s);
    	@cy=(0,0,$s,$s,0,0,$s,$s);
    	@cz=(0,$s,$s,0,0,$s,$s,0);
    	@cf=( 
        	0, 1, 2, 3,
        	3, 2, 6, 7,
        	7, 6, 5, 4,
        	4, 5, 1, 0,
        	5, 6, 2, 1,
        	7, 4, 0, 3,
           );
    @r=(0.5, 0,   0,   0.5, 1.0, 0);
    @g=(0,   0.5, 0,   0.5, 0,   1.0);
    @b=(0,   0,   0.5, 0,   1.0, 1.0);

sub drawwalls {
	local($x,$y,$dl);
	glNewList($dl=glGenLists(1),GL_COMPILE);
	for($x=0;$x<$x_size;$x++) {
		for($y=0;$y<$y_size;$y++) {
			if($wall[$x]->[$y]) {
    				for($i=0;$i<6;$i++){
					if(
					   $i==4 || $i==5 || 
						  ($i==0 && ($x==0  || !($wall[$x-1]->[$y]))) || 
						  ($i==1 && ($y==$y_size-1 || !($wall[$x]->[$y+1]))) ||
						  ($i==2 && ($x==$x_size-1 || !($wall[$x+1]->[$y]))) ||
						  ($i==3 && ($y==0  || !($wall[$x]->[$y-1]))) 
						 ) {
						glColor3f($r[$i],$g[$i],$b[$i]);
 						glBegin(GL_POLYGON);
						#print " begin poly $x,$y  $i\n";
        					for($j=0;$j<4;$j++){
                					$k=$cf[$i*4+$j];
                					glVertex3d($x+$cx[$k],$y+$cy[$k],$cz[$k]);
					        }
						glEnd();
					}
				}
			}
		}
	}
	glColor3f(0,1,0);
	glBegin(GL_POLYGON);
	glVertex3f(1+0.2,1+0.2,0);
	glVertex3f(1+0.2,1+0.8,0);
	glVertex3f(1+0.8,1+0.8,0);
	glVertex3f(1+0.8,1+0.2,0);
	glEnd();

	glColor3f(1,0,0);
	glBegin(GL_POLYGON);
	glVertex3f($x_size-2+0.2,$y_size-2+0.2,0);
	glVertex3f($x_size-2+0.2,$y_size-2+0.8,0);
	glVertex3f($x_size-2+0.8,$y_size-2+0.8,0);
	glVertex3f($x_size-2+0.8,$y_size-2+0.2,0);
	glEnd();

	glEndList();
	$dl;
}

package A;

use OpenGL;
%defaults = (
        'name' => 'unnamed',
        'x' => 0 ,
        'y' => 0 ,
        'z' => 0 ,
        'dx'=> 0,
        'dy'=> 0,
        'dz'=> 0,
	'angle' => 0,
        'dl'=> $main::shot,
);
sub initialize {
        my $self=shift;
        local %v = @_;
        foreach $k (keys(%v)) {
                $self->{$k} = $v{$k};
        }
        $self;
}

sub print{
        my $self=shift;
        print "\tObject '",$self->{'name'},"' is a '",ref($self),"'\n";
        foreach $k (sort keys(%$self)) {
                print("\t\t$k\t$self->{$k}\n") if($k cmp 'name');
        }
        print "\n";
}

sub new {
        my $type = shift;
        my $self = {};
        initialize($self,%defaults);
        initialize($self,@_);
        push(@objects,$self);
        bless $self;
}
sub move {
	my $self=shift;
	local $h;
	($self->{'x'},$self->{'y'},$h) = &main::forward($self->{'x'},
						$self->{'y'},
						$self->{'x'}+$self->{'dx'},
						$self->{'y'}+$self->{'dy'},0.01);
	#print "bullet $self->{'x'},$self->{'y'} $self->{'dx'},$self->{'dy'} $self \n";
	if($h) { 
		local($i,$k);
		$k=-1;
		for($i=0;$i<=$#objects;$i++) {
			($k=$i) if($objects[$i] == $self) ;
		}
		$k==-1 and die "hey k= $k\n";
		splice(@objects,$k,1);
		$self->{'dl'} = 4;
		#print "dead\n";
	}
}
sub draw {
       my $self=shift;
        glPushMatrix();
        glTranslatef($self->{'x'},$self->{'y'},$self->{'z'});
	glRotatef($self->{'angle'},0,0,1);
        #glCallList($self->{'dl'});
        glCallList($self->{'dl'});
        #glCallList($main::shot);
        glPopMatrix ();
}

package main;

sub check_events {
	while(XPending) {
		my @e=&glpXNextEvent;
		my %s;
		&$s(@e) if($s=$cb{$e[0]});
        }
}

sub intro {
  # give a cool intro to the thing
  # spin the maze around on 2 axis then
  # zoom in to the starting position.
  local($spin,$p);
  $spin=360*2;
  while($spin-=5) {
    check_events;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glPushMatrix();
    glTranslatef(0,0,-20);
    glRotatef($spin, 0.0, 1.0, 1.0);
    glTranslatef(-$x_size/2,-$y_size/2,0);
    glCallList($walls);
    glPopMatrix();
    glXSwapBuffers;
  }
  for($p=0 ; $p <= 1 ; $p+=0.02) {
    check_events;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glPushMatrix();
    glRotatef(-90*$p,1,0,0);

    glRotatef($b*$p,0,0,1);

    glTranslatef(-$x*$p + -$x_size/2*(1-$p),-$y*$p+ -$y_size/2*(1-$p),-0.5*$p + -20*(1-$p));

    glCallList($walls);
    glPopMatrix();
    glXSwapBuffers;
  }
}

$x=1.5 ; $y=1.5 ; $b = 90 ;

sub forward {
	# this routine does wall collision detection
	# the inputs to this routine are:
	#         - the current location
	#         - the desired location 
	#         - the minimum distance to wall allowed 
	# returns
	#         - new coordinates after move
	#         - whether a wall caused change in target position
	# This is really easy with these walls that lie only on axes.
	local($x,$y,$px,$py, $bf) = @_;
	local($h)=(0);
	if($px>int($x)+1.0-$bf && $wall[$x+1]->[$y]) {
		$px = int($x)+1.0-$bf;
		$h++;
	}
	if($py>int($y)+1.0-$bf && $wall[$x]->[$y+1]) {
		$py = int($y)+1.0-$bf;
		$h++;
	}
	if($px<int($x)+$bf && $wall[$x-1]->[$y]) {
		$px = int($x)+$bf;
		$h++;
	}
	if($py<int($y)+$bf && $wall[$x]->[$y-1]) {
		$py = int($y)+$bf;
		$h++;
	}
	($px,$py,$h);
}
sub abs {
	($_[0] > 0) ? $_[0] : - $_[0];
}

sub nav {
    check_events;

    # routine for navigating through the maze
    $t++;
    ($px,$py,$pm) = glpXQueryPointer;
    $rot = 0;
    if ($pm & Button1Mask) { $rot = -1; }  
    if ($pm & Button3Mask) { $rot = 1; }  
    $rot = 2*$px/$width - 1 if !$rot && ($pm & Button2Mask);
    $b += (( $pm & (ShiftMask)) ?9 :3) * $rot;
    if ($pm & Button2Mask) { 
	$speed = ( $pm & (ShiftMask)) ?0.15 : 0.05;
	($x,$y) = forward($x,$y,$x+$speed*sin($b*3.14/180),
				$y+$speed*cos($b*3.14/180),0.2);
    } 
    if( $pm & (ControlMask)) {
	#print "fire\n";
	local($d);
	$d=$b + abs($t*7%60-30)-15;
	new A(x=>$x,y=>$y,z=>0.5,
		dx => 0.08*sin(($d)*3.14/180),dy=>0.08*cos(($d)*3.14/180) ,
		dl=>3,angle=>-($d)
	     );
    }
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glPushMatrix();
    glRotatef(-90,1,0,0);
    glRotatef($b,0,0,1);
    glTranslatef(-$x,-$y,-0.5);
    glCallList($walls);
    # make copy of list since it may get modified during list traversal
    @objects = @A::objects;  
    foreach $obj (@objects) {
	$obj->move;
	$obj->draw;
    }
    glPopMatrix();
    glXSwapBuffers;
}

$width = 300;

glpOpenWindow(attributes => [GLX_GREEN_SIZE, 1,GLX_RGBA,GLX_DOUBLEBUFFER],
		mask => StructureNotifyMask|KeyPressMask,
		width=>$width,height=>300);

print <<EOP;

Control: MB1/MB3 - turn left/right, MB2 - go (and turn), Control - shoot a wave

EOP

$cb{&ConfigureNotify} = sub {
 local($e,$w,$h)=@_;
 glViewport(0,0,$w,$h);
 print "new viewport $w,$h\n";
 $width = $w;
};

glNewList($shot=3,GL_COMPILE);
glColor3f(0.5,1.0,0.5);
glBegin(GL_POLYGON);
 glNormal3f( 0.10,   0.0,  0.0);
 glVertex3f( 0.0 ,  0.03, -0.04);
 glVertex3f(  0.0030 , -0.01, -0.04);
 glVertex3f( -0.0030 , -0.01, -0.04);
glEnd();
glEndList();
glNewList($smash=4,GL_COMPILE);
glColor3f(1.0,0.5,0);
glBegin(GL_POLYGON);
 glNormal3f( 0.10,   0.0,  0.0);
 glVertex3f( -0.05 ,  0, -0.02);
 glVertex3f( 0.05 ,  0, -0.02);
 glVertex3f(  0.05 ,0, -0.06);
 glVertex3f( -0.05 ,0, -0.06);
glEnd();
glEndList();

glEnable(GL_DEPTH_TEST);
glClearColor(0,0,0,1);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, 1.0 , 0.1, 60.0); 
    glMatrixMode(GL_MODELVIEW);
glLoadIdentity ();

$walls=drawwalls;

intro;

while(1){nav;}