
--
-- Copyright (C) 2019  <fastrgv@gmail.com>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You may read the full text of the GNU General Public License
-- at <http://www.gnu.org/licenses/>.
--

----------------------------------------------------------------
-- Virtual Camera System:  [relatively-smart] tracking type
--
-- This variation of camera motion is pretty nice in most respects:
-- it is a "tracking" algorithm, i.e. generally follows the avatar...
-- slews moderately while moving forward, 
-- slews slowly while moving backward,
-- does NOT slew while stopped (except when sokobaning),
-- and when sokobaning, the vertical offset is longer
-- to allow a better view of the puzzle to be solved.
--
-- While sokobaning, if we do not slew at all then
-- we can get stuck looking in the wrong direction
-- and unintentionally create an impossible deadlock.
--
-- Also many other refinements like:
-- .) circular KeepOuts on beach to reduce jitter,
-- .) reduced mouse sensitivity
-- .) greater care making vertical angle adjustments
--		which can cause jitter.
----------------------------------------------------------------



with interfaces;
with interfaces.c;


with matutils;  use matutils;
with gametypes;  use gametypes;
with snd4ada_hpp;

with text_io; use text_io;
with utex;

with gl;  use gl;

with sdl;  use sdl;

with tunnelobj;

with cubemapobj;
with rectobj;
with cyl2obj;
with cyl2lit;
with pictobj;
with twictobj;
with pict1obj;
with xtreeobj;
with w3treeobj;
with zfishobj;
with circtexsurfobj;
with annusurfobj;
with ellipsoid;

with gl, gl.binding, gl.pointers;
with glu, glu.binding, glu.pointers;
with glext, glext.binding, glext.pointers;

with Interfaces.C;
use  type interfaces.c.unsigned;
with Interfaces.C.Pointers;
with interfaces.c.strings;
with ada.numerics;
with ada.characters.handling;

with pngloader;
with shader;

with avatarobj;
--with avatarfine;



package body gameutils is

	use pngloader;
	use shader;



	-- added 16jan18
	procedure fclamp(x: in out float; a, b : float) is
	begin
		if x<a then x:=a;
		elsif x>b then x:=b;
		end if;
	end;


	procedure updateFerryPos(
		tm: in float;
		xb,yb,zb,      -- body pos
		ab,
		xh,yh,zh,      -- head pos
		ah: out float  -- head angle
	) is separate;







	function odd( i: integer ) return boolean is
	begin
		return ( i mod 2 = 1 );
	end odd;



	function min( x,y: float ) return float is
	begin
		if x<y then return x;
		else return y; end if;
	end min;

	function mini(i,j: integer) return integer is
	begin
		if i<j then return i;
		else return j; end if;
	end mini;


	procedure myassert( 
		condition : boolean;  
		flag: integer:=0;
		msg: string := ""
		) is
	begin
	  if condition=false then
			put("ASSERTION Failed!  ");
			if flag /= 0 then
				put( "@ " & integer'image(flag) &" : " );
			end if;
			put_line(msg);
			new_line;
			raise program_error;
	  end if;
	end myassert;




	function max( x,y : float ) return float is
	begin
		if y>x then return y;
		else return x; end if;
	end;

	function sqr( x:float ) return float is
	begin
		return x*x;
	end;

	function dotprod( x,y,z, a,b,c : float ) return float is
	begin
		return x*a + y*b + z*c;
	end;


	function atleftportal return boolean is
		dx,dy,dz,dy0 : float;
	begin

		--yfloor := -ymax+aheight;
		dy0 := (yme-aheight) - ytgt1; --feet versus ytgt
		dx := xme - xtgt1;
		dy := yme - ytgt1;
		dz := zme - ztgt1;

		if not worm_active then 
			return false;
		--elsif Xit then return false; --9apr18
		elsif 
			(abs(dx)>wormradius) or 
			((abs(dy)>wormradius) and (pone/=2) ) or 
			(abs(dz)>wormradius) then return false;
		else

			if    (pone=1) and (abs(dx)<event_horizon) then return true;
			elsif (pone=2) and (abs(dy0)<event_horizon) then return true; --feet vs floor
			elsif (pone=2) and (abs(dy)<event_horizon) then return true; --head vs ceiling
			elsif (pone=3) and (abs(dz)<event_horizon) then return true;
			else return false;
			end if;

		end if;

	end atleftportal;


	function atrightportal return boolean is
		dx,dy,dz,dy0 : float;
	begin

		--yfloor := -ymax+aheight;
		dy0 := (yme-aheight) - ytgt2; --feet versus ytgt
		dx := xme - xtgt2;
		dy := yme - ytgt2;
		dz := zme - ztgt2;

		if not worm_active then 
			return false;
		--elsif Xit then return false; --9apr18
		elsif 
			(abs(dx)>wormradius) or 
			((abs(dy)>wormradius) and (ptwo/=2)) or 
			(abs(dz)>wormradius) then return false;
		else

			if    (ptwo=1) and (abs(dx)<event_horizon) then return true;
			elsif (ptwo=2) and (abs(dy0)<event_horizon) then return true;--feet vs floor
			elsif (ptwo=2) and (abs(dy)<event_horizon) then return true;--head vs ceiling
			elsif (ptwo=3) and (abs(dz)<event_horizon) then return true;
			else return false;
			end if;

		end if;

	end atrightportal;


	function atexit return boolean is
		xxx,zzz,dx,dz,hrad : float;
		--closeEnough : constant float := 1.4+margin; -- 1.525
		closeEnough : constant float := 1.0+margin; -- 1.125 (16jul18)
	begin

		xxx:=xboxpos(1);
		zzz:=xboxpos(3);
		dx := xme - xxx;
		dz := zme - zzz;
		hrad := fmath.sqrt( dx*dx + dz*dz );

		if not Xit then return false;
		elsif (hrad<closeEnough) then return true;
		else return false;
		end if;

	end atexit;


	function intersectsko( 
		x0,y0,z0, x1,y1,z1 : float;  
		k : in out integer ) 
	return boolean is
		result: boolean := false;
		tx,ty,tz,tt,xx,yy,zz : float;
	begin
		k:=-1;
		for j in 0..1000 loop 
		-- NOTE:  this loop traverses from p0 to p1 and selects 
		--        obstacle closest to p1
			tt:=float(j)/1000.0;
			xx:=x0+tt*(x1-x0);
			yy:=y0+tt*(y1-y0);
			zz:=z0+tt*(z1-z0);

			for i in 1..nko loop
			if (i /= pko ) then --disregard pool keepout
			  tx:=(xx-koxlo(i))*(koxhi(i)-xx);
			  ty:=(yy-koylo(i))*(koyhi(i)-yy);
			  tz:=(zz-kozlo(i))*(kozhi(i)-zz);
			  if (tx>0.0) and (ty>0.0) and (tz>0.0) then
				result:=true;
				k:=i;
			  end if;
			end if;
			end loop; --for i

		end loop; --for j
		return result;
	end intersectsko;





	function inpool( xx,yy,zz : float ) return boolean is
		result: boolean := false;
		tx,ty,tz:float;
		k : integer;
	begin
		k:=-1;

		for i in 1..nko loop
		  tx:=(xx-koxlo(i)+margin)*(koxhi(i)+margin-xx);
		  ty:=(yy-koylo(i))*(koyhi(i)-yy);
		  tz:=(zz-kozlo(i)+margin)*(kozhi(i)+margin-zz);
		  if (tx>=0.0) and (ty>=0.0) and (tz>=0.0) then
			result:=true;
			k:=i;
		  end if;
		end loop; --for i

		result := result and (k=pko);

		return result;

	end inpool;


	function anybarrelisat( xx,zz : float ) return boolean is
		result:boolean:=false;
		dist : float;
	begin
	  for i in 1..nbarrels loop
		 dist:=fmath.sqrt( sqr(xx-posbar(i)(1)) + sqr(zz-posbar(i)(3)) );
		 if dist<barr then result:=true; end if;
	  end loop;
	  return result;
	end anybarrelisat;




procedure testbarrel( nbx,nby,nbz: in out float;
	wasmoved, hitko, hitwall : in out boolean ) is

	contact: boolean := false;
	iwhich: integer := -1;
	tbx,tby,tbz : float; -- position after roll plus one radius
	xlo,xhi,zlo,zhi, otx,otz, tx,tz : float;
begin


	nbx:=barrl(1); nby:=barrl(2); nbz:=barrl(3);
	tbx:=nbx; tby:=nby; tbz:=nbz;
	wasmoved:=false; hitko:=false; hitwall:=false;

	xlo:=nbx-barr-margin/2.0;	xhi:=nbx+barr+margin/2.0;
	zlo:=nbz-barr-margin/2.0;	zhi:=nbz+barr+margin/2.0;

	otx:=(oxme-xlo)*(xhi-oxme);
	otz:=(ozme-zlo)*(zhi-ozme);

	tx:=(xme-xlo)*(xhi-xme);
	tz:=(zme-zlo)*(zhi-zme);

	if (tx>0.0) and (tz>0.0) then contact:=true; end if;

	if contact then

		if (tx<tz) then --primarily X contact

			if abs(xme-xlo) < abs(xme-xhi) then --  +X
				nbx:= barrl(1) + onepi*barr;
				tbx:= barrl(1) + (1.0+onepi)*barr;
				wasmoved:=true;
			else -- -X
				nbx:= barrl(1) - onepi*barr;
				tbx:= barrl(1) - (1.0+onepi)*barr;
				wasmoved:=true;
			end if;

			if (nbx < -xmax+margin) then
				nbx:=barrl(1); wasmoved:=false;  hitwall:=true;
			end if;
			if (nbx >  xmax-margin) then
				nbx:=barrl(1); wasmoved:=false;  hitwall:=true;
			end if;

		else --primarily Z contact

			if abs(zme-zlo) < abs(zme-zhi) then --  +Z
				nbz:= barrl(3) + onepi*barr;
				tbz:= barrl(3) + (1.0+onepi)*barr;
				wasmoved:=true;
			else -- -Z
				nbz:= barrl(3) - onepi*barr;
				tbz:= barrl(3) - (1.0+onepi)*barr;
				wasmoved:=true;
			end if;

			if (nbz < -zmax+margin) then
				nbz:=barrl(3); wasmoved:=false;  hitwall:=true;
			end if;
			if (nbz >  zmax-margin) then
				nbz:=barrl(3); wasmoved:=false;  hitwall:=true;
			end if;

		end if;

		if wasmoved then
			if 
				intersectsko(
					barrl(1),barrl(2),barrl(3), 
					tbx,tby,tbz, iwhich) 
			then
				nbx:=barrl(1);  nby:=barrl(2);  nbz:=barrl(3);
				wasmoved:=false;
				hitko:=true;
			end if;
		end if;

	end if;


end testbarrel;




---------- begin island utils ----------------------------------------

-- Modified for Atoll with central lagoon...
-- Function to define island contour as ftn of radius from center...
-- height = hmax [above water level] @ radius r1(5), 
-- then linearly slopes downward to sea level @ r2(7), 
-- and below sea level beyond r2.
function sandht( x,z, hmax, wlev, r1, r2 : float ) return float is
	ht, htx, r, slope, ang : float;
	chwid : constant float :=  onepi/8.0;
	chcen : constant float := -onepi/4.0;
	-- note: currently r1=5, r2=7, hmax=0.16
begin
	r := fmath.sqrt(x*x+z*z);
	slope := hmax/(r1-r2);
	if( r<r1 ) then --lagoon
		ht := -0.9*fmath.cos(onepi*r/r1) + r/r1*(wlev+hmax-0.9);
	elsif r>10.0 then
		ht := wlev + slope*(10.0-r2);
	else
		ht := wlev + slope*(r-r2);
	end if;


	if (r>1.0) then
		ang:=fmath.arctan(z,x);
		if abs(ang-chcen) < chwid then
			tt := abs(ang-chcen)/chwid;

			htx := wlev-0.35;
			if htx < ht then
				ht:=(1.0-tt)*htx + tt*ht;
			end if;
		end if;
	end if;


	return ht;

end sandht;



---------- end island utils ----------------------------------------











-- version used on beach...
-- assumes the following values are defined:
-- barrl (vec3)
-- barad
procedure mtestbarrel( nbx,nby,nbz: in out float;
	wasmoved, hitko : in out boolean ) is

	contact: boolean := false;
	iwhich: integer := -1;
	tbx,tby,tbz : float;
	xlo,xhi,ylo,yhi,zlo,zhi, tx,ty,tz : float;
	oy, ny, dy : float;
begin


	nbx:=barrl(1); nby:=barrl(2); nbz:=barrl(3);

	oy := sandht(nbx,nbz,hmax,mwaterlevel,r1,r2);

	tbx:=nbx; tby:=nby; tbz:=nbz;
	wasmoved:=false; hitko:=false;

	-- point of contact is 10% beyond actual size:
	xlo:=nbx-barad*1.1;	xhi:=nbx+barad*1.1;
	ylo:=nby-barad*5.0;	yhi:=nby+barad*5.0;
	zlo:=nbz-barad*1.1;	zhi:=nbz+barad*1.1;

	tx:=(xme-xlo)*(xhi-xme);
	ty:=(yme-ylo)*(yhi-yme);
	tz:=(zme-zlo)*(zhi-zme);

	if (tx>0.0) and (ty>0.0) and (tz>0.0) then contact:=true; end if;

	if contact then

		if (tx<tz) then --primarily X contact

			if abs(xme-xlo) < abs(xme-xhi) then --  +X
				nbx:= barrl(1) + onepi*barad;
				tbx:= barrl(1) + (1.0+onepi)*barad;
				wasmoved:=true;
			else -- -X
				nbx:= barrl(1) - onepi*barad;
				tbx:= barrl(1) - (1.0+onepi)*barad;
				wasmoved:=true;
			end if;


		else --primarily Z contact

			if abs(zme-zlo) < abs(zme-zhi) then --  +Z
				nbz:= barrl(3) + onepi*barad;
				tbz:= barrl(3) + (1.0+onepi)*barad;
				wasmoved:=true;
			else -- -Z
				nbz:= barrl(3) - onepi*barad;
				tbz:= barrl(3) - (1.0+onepi)*barad;
				wasmoved:=true;
			end if;

		end if;


		ny := sandht(nbx,nbz,hmax,mwaterlevel,r1,r2);
		dy := ny-oy;
		nby := nby + dy;



		if wasmoved then
			if 
				intersectsko(
					barrl(1),barrl(2),barrl(3), 
					tbx,tby,tbz, iwhich) 
			then
				nbx:=barrl(1);  nby:=barrl(2);  nbz:=barrl(3);
				wasmoved:=false;
				hitko:=true;
			end if;
		end if;

	end if;


end mtestbarrel;



procedure testPortals is separate;



-- jump physics here
function ypos( nowtime, yp0, yvinit, deltaSecs : float ) return float is
	epsilon : constant float := 0.1;
	gravity : constant float :=-3.0;

	-- pos = p0 + v*t + 0.5*a*t*t :
	yp : float := yp0 + yvinit*deltasecs + 0.5*gravity*sqr(deltasecs);

	yfloor : float;
begin

	if (yp>ymax) then --bumped into ceiling
		yp:=ymax;  pyjump:=yp;  vyjump:=0.0;  jumptime:=nowtime;
	end if;

	yfloor := -ymax+aheight;
	for i in 1..nko loop
	if onledge(i) then yfloor:=max(yfloor, koyhi(i)+aheight); end if;
	end loop;

	if (yp<yfloor) then --hit floor
		yp:=yfloor;
		jumping:=false;
		vyjump:=0.0;
		landingtime:=nowtime;
	end if;

	return yp;

end ypos;



function distance( x1,y1,z1, x2,y2,z2 : float ) return float is
begin
	return fmath.sqrt( sqr(x2-x1) + sqr(y2-y1) + sqr(z2-z1) );
end distance;

function hordistance( x1,y1, x2,y2 : float ) return float is
begin
	return fmath.sqrt( sqr(x2-x1) + sqr(y2-y1) );
end hordistance;

function signum( x : float ) return float is
begin
	if x>0.0 then
		return +1.0;
	elsif x<0.0 then
		return -1.0;
	else
		return 0.0;
	end if;
end signum;











-- 29feb16 remark:  the small dimension should be just smaller
-- than the distance from the eye to the nearest object at the
-- destination.  Level 5 has eye @ (0,0,0) and water @ y=-0.12
-- Thus, 0.11 should be just barely Ok.
procedure prepwormhole( 
	currenttime, oxme,oyme,ozme, nxme,nyme,nzme, fxme,fyme,fzme : float ) is

	xc : constant float := 0.5*(oxme+nxme);
	yc : constant float := 0.5*(oyme+nyme);
	zc : constant float := 0.5*(ozme+nzme);

	xr :  float := 0.5*abs(oxme-nxme);
	yr :  float := 0.5*abs(oyme-nyme);
	zr :  float := 0.5*abs(ozme-nzme);

begin 
-- WARNING:  as written, tunnel requires 
-- horiang be a multiple of halfpi

	if yr>xr and yr>zr then --we are coming from up/dn
		xr:=0.11;
		zr:=0.11;

	elsif xr<zr then -- from no/so
		xr:=0.11;
		yr:=0.11;

	else             -- from ea/we
		zr:=0.11;
		yr:=0.11;

	end if;

	-- set flags, vars, for wormhole cutscene
	owxme:=oxme;
	owyme:=oyme;
	owzme:=ozme;

	nwxme:=fxme;
	nwyme:=fyme;
	nwzme:=fzme;

	wormangle:=0.0;

	worming:=true;
	wormstart:= currenttime;

	portalStatus:=portalEnabled;
	portalEnabled:=false; --want see-thru center while worming

	tunnelobj.setorect(wmo,xc,yc,zc,xr,yr,zr);

end prepwormhole;



procedure worm(
currenttime, xme,yme,zme : in out float;
worming : in out boolean ) is

	localWormDuration : constant float := 2.0; --seconds
	islandWormDuration : constant float := 5.0; --seconds
	tti : constant float := (currenttime - wormstart)/islandWormDuration;
	ttl : constant float := (currenttime - wormstart)/localWormDuration;
	tt : float;

begin

	if dungeonLocal then
		tt:=ttl;
	else -- to or from island
		tt:=tti;
	end if;

if tt>1.0 then

	worming:=false;
	fromIsland:=false;
	toIsland:=false;
	dungeonLocal:=false;
	portalEnabled:=portalStatus;
	xme:=nwxme;
	zme:=nwzme;
	wormangle:=0.0;
	mm:=identity;

	yme:=nwyme;
	if (yme<aheight-ymax) then yme:=aheight-ymax; end if;
	if (yme>aheight-ymax) then
		jumptime:=currenttime;
		pyjump:=yme;
		vyjump:=0.0;
		jumping:=true;
	end if;


else

	xme:=(1.0-tt)*owxme + tt*nwxme;
	yme:=(1.0-tt)*owyme + tt*nwyme;
	zme:=(1.0-tt)*owzme + tt*nwzme;

end if;

end worm;









--NOTICE:  typically, dt<<0.1 second  (between iterations of main loop)
--         but when it is that big, it's probably because foldtime is stale
procedure moveforward( currenttime: float ) is
	dt : float := currenttime-foldtime;
	lagfac : constant float := 1.0;

	dx,dy,dz,
	kxl,kxh,kyl,kyh,kzl,kzh,
	okxl,okxh, okzl, okzh: float;

begin

if level>0 then

	-- intent here is to prevent sudden
	-- jumps due to inactivity:
	if dt>2.0 then -- foldtime is stale!
		dt:=0.0;
	end if;


	if turtlenear then mounting:=true; end if;


	dx:=dt*speed*xlook;
	dy:=0.0;  -- only move horizontally
	dz:=dt*speed*zlook;
	direction:=+1;

	foldtime:=currenttime;
	oxme:=xme;
	oyme:=yme;
	ozme:=zme;

	for i in 1..nbarrels loop
		if rolling(i) then anyrolling:=true; end if;
	end loop;




	if not exitwait and not anyrolling 
	and forwardok and not pauseatlevelchange then
		xme := xme + dx;
		yme := yme + dy;
		zme := zme + dz;
	end if;



	-- limit pos to be within walls:
	if (xme>+xmax-margin) then xme:=+xmax-margin;  end if;
	if (xme<-xmax+margin) then xme:=-xmax+margin;  end if;
	if (zme>+zmax-margin) then zme:=+zmax-margin;  end if;
	if (zme<-zmax+margin) then zme:=-zmax+margin;  end if;



	-- further, limit pos to avoid ko zones:
	for i in 1..nko loop
	if (i /= pko) or not inpool(oxme,oyme,ozme) then

		kxl:=xme-koxlo(i)+margin;
		kxh:=koxhi(i)+margin-xme;

		--NOTE:  using half-height for this test to
		--       respect low obstacles like sokWalls.
		kyl:=yme-aheight/2.0-koylo(i);
		kyh:=koyhi(i)-yme+aheight/2.0;

		kzl:=zme-kozlo(i)+margin;
		kzh:=kozhi(i)+margin-zme;



		if (kxl*kxh>0.0) and (kyl*kyh>0.0) and (kzl*kzh>0.0) then 
		--intrusion into ko

			okxl:=oxme-koxlo(i)+margin;
			okxh:=koxhi(i)+margin-oxme;
			okzl:=ozme-kozlo(i)+margin;
			okzh:=kozhi(i)+margin-ozme;

			if      ( (okxl*okxh>0.0) and (okzl*okzh<=0.0) ) then
				zme:=ozme;

			elsif ( (okzl*okzh>0.0) and (okxl*okxh<=0.0) ) then
				xme:=oxme;

			end if;

		end if; --intrusion into ko



	end if;
	end loop; --for i


	testPortals;


end if; -- level>0

end moveforward;




-- not able to back thru a stargate
procedure movebackward( currenttime: float ) is
	dt : float := currenttime-boldtime;
	lagfac : constant float := 1.0;
	dx,dy,dz,
	kxl,kxh,kyl,kyh,kzl,kzh,
	okxl,okxh, okzl, okzh: float;
begin

if level>0 then

	-- intent here is to prevent sudden
	-- jumps due to inactivity:
	if (dt>2.0) then dt:=0.0; end if;


	forwardok:=true;

	dx:=dt*speed*xlook;
	dy:=0.0;  -- only move horizontally
	dz:=dt*speed*zlook;
	direction:=-1;

	boldtime:=currenttime;
	oxme:=xme;
	oyme:=yme;
	ozme:=zme;

	for i in 1..nbarrels loop
		if rolling(i) then anyrolling:=true; end if;
	end loop;


	if not exitwait and not anyrolling 
	and forwardok and not pauseatlevelchange then
		xme := xme - dx;
		yme := yme - dy;
		zme := zme - dz;
	end if;


	-- limit pos to be within walls:
	if (xme>+xmax-margin) then xme:=+xmax-margin; end if;
	if (xme<-xmax+margin) then xme:=-xmax+margin; end if;
	if (zme>+zmax-margin) then zme:=+zmax-margin; end if;
	if (zme<-zmax+margin) then zme:=-zmax+margin; end if;


	-- further, limit pos to avoid ko zones:
	for i in 1..nko loop
	if (i /= pko) or not inpool(oxme,oyme,ozme) then

		kxl:=xme-koxlo(i)+margin;
		kxh:=koxhi(i)+margin-xme;

		--NOTE:  using half-height for this test to
		--       respect low obstacles like sokWalls.
		kyl:=yme-aheight/2.0-koylo(i);
		kyh:=koyhi(i)-yme+aheight/2.0;

		kzl:=zme-kozlo(i)+margin;
		kzh:=kozhi(i)+margin-zme;


		if (kxl*kxh>0.0) and (kyl*kyh>0.0) and (kzl*kzh>0.0) then 
		--intrusion into ko

			okxl:=oxme-koxlo(i)+margin;
			okxh:=koxhi(i)+margin-oxme;
			okzl:=ozme-kozlo(i)+margin;
			okzh:=kozhi(i)+margin-ozme;

			if      ( (okxl*okxh>=0.0) and (okzl*okzh<0.0) ) then
				zme:=ozme;
			elsif ( (okzl*okzh>=0.0) and (okxl*okxh<0.0) ) then
				xme:=oxme;
			end if;

		end if; -- KO intrusion

	end if;
	end loop; --for i

end if; --level>0

end movebackward;













--procedure handle_gc_left( gcx,gcy:sdl.sint16 ) is
procedure handle_gc_look( gcx,gcy:sdl.sint16 ) is
-- to update look direction using left game controller stick
	ux : float := float(gcx)/float(32768);
	uy : float := float(gcy)/float(32768);
begin


	if abs(ux)<0.15 then 
		ux:=0.0;
	else
		ux:=ux-0.15*signum(ux);
	end if;

	if abs(uy)<0.15 then 
		uy:=0.0; 
	else
		uy:=uy-0.15*signum(uy);
	end if;


	horiAng := horiAng - 0.04 * ux * Lsens;
	vertAng := vertAng + 0.02 * uy * Lsens;
	--slewToAvLook;
	slewToAv; --26jan18

	if thirdperson then -- vertang represents camera
		xlook := fmath.cos(0.0)*fmath.sin(horiAng);
		zlook := fmath.cos(0.0)*fmath.cos(horiAng);
	else
		xlook := fmath.cos(vertAng)*fmath.sin(horiAng);
		zlook := fmath.cos(vertAng)*fmath.cos(horiAng);
	end if;
	cylook := fmath.sin(vertAng);



	if
	( 
	not forwardOk 
	and (abs(badHoriAng-horiAng)>fourthpi) 
	and not pauseAtLevelChange 
	)  then
		forwardOk:=true;
		badHoriAng:=-10.0*twopi;
	end if;

end handle_gc_look;

--procedure handle_gc_right( nowtime: float; gcx,gcy:sdl.sint16 ) is
procedure handle_gc_move( nowtime: float; gcx,gcy:sdl.sint16 ) is
-- to update move direction using right game controller stick
	ux : float := Rsens*float(gcx)/float(32768);
	uy : float := Rsens*float(gcy)/float(32768);
begin

	if    uy < -0.05 then
		moveforward(nowTime);

	elsif uy > +0.05 then
		movebackward(nowTime);

	end if;

	handle_gc_look(gcx,0); -- turns left/right

end handle_gc_move;










procedure aim(x,y,z: out float) is
	x1,y1,z1,x2,y2,z2,tt: float := 0.0;
begin

	loop
		tt:=tt+1.0;

		if thirdperson then
			x2 := xcam + tt*xlook;
			y2 := ycam + tt*cylook;
			z2 := zcam + tt*zlook;
		else
			x2 := xme + tt*xlook;
			y2 := yme + tt*cylook;
			z2 := zme + tt*zlook;
		end if;

		exit when abs(x2)>xmax or abs(y2)>ymax or abs(z2)>zmax;
	end loop;
	x1 := x2 - xlook;
	y1 := y2 - cylook;
	z1 := z2 - zlook;

	tt:=1.0;
	if abs(x2)>xmax then 
		tt:= min(tt,(xmax-abs(x1))/(abs(x2)-abs(x1)));
	end if;

	if abs(y2)>ymax then 
		tt:= min(tt,(ymax-abs(y1))/(abs(y2)-abs(y1)));
	end if;

	if abs(z2)>zmax then 
		tt:= min(tt,(zmax-abs(z1))/(abs(z2)-abs(z1)));
	end if;

	x:=x1 + tt*(x2-x1);
	y:=y1 + tt*(y2-y1);
	z:=z1 + tt*(z2-z1);

	
end aim;




procedure shootleft( mbtntime: float ) is separate;
procedure shootright( mbtntime: float ) is separate;







	-- this assumes mm=ID [almost] always:
	procedure updateMVP( wid,hit : float) is
		xlk,ylk,zlk,
		xpos,ypos,zpos,
		xrt,yrt,zrt,
		xup,yup,zup : float;
		-- The look vector (xlk,ylk,zlk) is mapped to
		-- the viewport center.  UP does not refer to (0,1,0), 
		-- but a direction perpendicular to the L.O.S. that 
		-- is mapped to the upward direction in the viewport
	begin

		if thirdPerson and not worming then

			xpos:=xcam; ypos:=ycam; zpos:=zcam;

			-- camera look + choriang are already defined

		else -- firstPerson

			xpos:=xme; ypos:=yme; zpos:=zme;

			choriang:=horiang;
			cxlook := fmath.cos(vertang)*fmath.sin(choriang);
			cylook := fmath.sin(vertang);
			czlook := fmath.cos(vertang)*fmath.cos(choriang);

		end if;



		-- Look Vector
		xlk:=xpos+cxlook;
		ylk:=ypos+cylook;
		zlk:=zpos+czlook;

		-- Right unit-Direction
		xrt:= fmath.sin(choriang-halfpi);
		yrt:= 0.0;
		zrt:= fmath.cos(choriang-halfpi);

		-- calculate UP unit-Direction
		cross( xrt,yrt,zrt, cxlook,cylook,czlook, xup,yup,zup );

		perspective(projMatrix, 45.0, wid/hit,  0.1, 100.0);

		lookat(viewMatrix, xpos,ypos,zpos, xlk,ylk,zlk, xup,yup,zup );



		-- note: mm=id
		-- MV = mm*vm = id*vm = vm
		--mv:=viewMatrix;

		mvp:=viewMatrix;
		matXmat(mvp,projMatrix); -- mvp := mv * pm

		-- For a given modelmatrix (MM: mat44), 
		-- we can get a normalMatrix (NM: mat33) by:
		-- myInvTransp(MM,NM)
		-- However, in this app, MM=ID

	end updateMVP;












	-- this assumes mm=ID always;
	-- This version takes pos,lookDir,rightDir as input:
	procedure updateMVP2( 
		wid,hit,
		xpos,ypos,zpos,
		cxluk,cyluk,czluk,
		xrt,yrt,zrt : float) is

		xlk,ylk,zlk,
		xup,yup,zup : float;
		-- The look vector (xlk,ylk,zlk) is mapped to
		-- the viewport center.  UP does not refer to (0,1,0), 
		-- but a direction perpendicular to the L.O.S. that 
		-- is mapped to the upward direction in the viewport
	begin

		-- calculate UP unit-Direction
		cross( xrt,yrt,zrt, cxluk,cyluk,czluk, xup,yup,zup );

		-- Look Vector
		xlk:=xpos+cxluk;
		ylk:=ypos+cyluk;
		zlk:=zpos+czluk;

		perspective(projMatrix, 45.0, wid/hit,  0.1, 100.0);

		lookat(viewMatrix, xpos,ypos,zpos, xlk,ylk,zlk, xup,yup,zup );

		-- note: mm=id
		-- MV = mm*vm = id*vm = vm
		--mv:=viewMatrix;

		mvp:=viewMatrix;
		matXmat(mvp,projMatrix); -- mvp := mv * pm

		-- For a given modelmatrix (MM: mat44), 
		-- we can get a normalMatrix (NM: mat33) by:
		-- myInvTransp(MM,NM)
		-- However, in this app, MM=ID

	end updateMVP2;














-- adjusts Ypos, Barrels, eXit cube:
procedure updategamestate( currenttime : float ) is separate;








procedure drawbeams( nowtime : float ) is -- assumes mvp is visible here
	v4, vcc, vcc1, vm1, vcc2, vm2 : vec4;
	xt,yt,zt,xt1,yt1,zt1,xt2,yt2,zt2, 
	rtgt,dot,dot1,dot2, rtgt1,rtgt2,dt,fi, timeelapsed : float;
	k,mswormtime,i: integer;
begin

	if lshooting and lport_located and not lport_stable then

		deltabtntime := nowtime - portalbtn1time;
		i := 1+integer( deltabtntime*99.0 );
		if (i>99) and not lport_stable then
			xtgt1:=xpre1; ytgt1:=ypre1; ztgt1:=zpre1;
			xt1:=xtgt1;
			yt1:=ytgt1;
			zt1:=ztgt1;
			lport_stable:=true;
			lshooting:=false;
		else
			xt1:=xpt(i);
			yt1:=ypt(i);
			zt1:=zpt(i);
			worm_defined:=false; --unneccesary...done in shoot*
		end if;
		rtgt1:=fmath.sqrt( sqr(xt1-xme) + sqr(yt1-yme) + sqr(zt1-zme) );
		dot1:=(xt1-xme)*xlook + (yt1-yme)*cylook + (zt1-zme)*zlook;
		vm1:=(xt1,yt1,zt1,1.0);
		matXvec( mvp, vm1, vcc1 );

		if lshooting and (dot1/rtgt1>mindot) then
			utex.print3d("O",
				vcc1(1),vcc1(2),
				vcc1(3),vcc1(4), 900, rtgt1);
			null;
		end if;

	end if;


	if rshooting and rport_located and not rport_stable then

		deltabtntime := nowtime - portalbtn2time;
		i := 1+integer( deltabtntime*99.0 );
		if (i>99) and not rport_stable then
			xtgt2:=xpre2; ytgt2:=ypre2; ztgt2:=zpre2;
			xt2:=xtgt2;
			yt2:=ytgt2;
			zt2:=ztgt2;
			rport_stable:=true;
			rshooting:=false;
		else
			xt2:=xpt(i);
			yt2:=ypt(i);
			zt2:=zpt(i);
			worm_defined:=false; --unneccesary...done in shoot*
		end if;
		rtgt2:=fmath.sqrt( sqr(xt2-xme) + sqr(yt2-yme) + sqr(zt2-zme) );
		dot2:=(xt2-xme)*xlook + (yt2-yme)*cylook + (zt2-zme)*zlook;
		vm2:=(xt2,yt2,zt2,1.0);
		matXvec( mvp, vm2, vcc2 );

		if rshooting and (dot2/rtgt2>mindot) then
			utex.print3d("O",
				vcc2(1),vcc2(2),
				vcc2(3),vcc2(4), 900, rtgt2);
		end if;

	end if;




	if lport_stable and rport_stable and portalEnabled then

		timeelapsed:= nowtime - portaltime;
		if 
			not lshooting and 
			not rshooting and 
			not worm_defined and 
			(timeelapsed>3.0) 
		then
			for i in 1..100 loop
				fi := float(i-1)/99.0;
				xpt(i) := xtgt1 + fi*(xtgt2-xtgt1);
				ypt(i) := ytgt1 + fi*(ytgt2-ytgt1);
				zpt(i) := ztgt1 + fi*(ztgt2-ztgt1);
			end loop;
			worm_defined:=true;
			wormsettime:=nowtime;

		end if;


		if not lshooting and not rshooting and portalEnabled then
			dt := 200.0*(nowtime-wormsettime);
			mswormtime := integer(dt);
			k := mswormtime/100;
			i := 1 + mswormtime mod 100;
			if (k mod 2 = 1) then
				null;
			else
				i:=101 - i;
			end if;
			xt:=xpt(i);
			yt:=ypt(i);
			zt:=zpt(i);
			rtgt := fmath.sqrt( sqr(xt-xme) + sqr(yt-yme) + sqr(zt-zme) );
			dot := (xt-xme)*xlook + (yt-yme)*cylook + (zt-zme)*zlook;

			v4 := (xt,yt,zt,1.0);
			matXvec( mvp, v4, vcc );

			if (dot/rtgt > mindot) then
				utex.print3d("O",
					vcc(1),vcc(2),
					vcc(3),vcc(4), 200, rtgt);
			end if;

		end if;

	end if;

end drawbeams;





------- added 26sep14 -----------------------------------------








oldMdTime : float := 0.0;

procedure handle_mouse_move( nowTime : float ) is

-- to update look direction
-- also handles touch pad...

	mousedx, mousedy : aliased interfaces.c.int;
	mouseBtnState : Uint32;
	dt : float := nowTime-oldMdTime; -- dt>0.1 => stale

begin

	mouseBtnState := 
		SDL_GetRelativeMouseState(mousedx'access, mousedy'access);

	oldMdTime:=nowTime;

	if( dt<0.1 ) then
		horiAng := horiAng - 0.001 * float(mousedx); -- 0.002
		vertAng := vertAng - 0.001 * float(mousedy); -- chg 16jan18
		--slewToAvLook; 26jan disable
		slewToAv; --26jan18 better!
	end if;


	if thirdperson then -- vertang represents camera
		xlook := fmath.cos(0.0)*fmath.sin(horiAng);
		zlook := fmath.cos(0.0)*fmath.cos(horiAng);
	else
		xlook := fmath.cos(vertAng)*fmath.sin(horiAng);
		zlook := fmath.cos(vertAng)*fmath.cos(horiAng);
	end if;
	cylook := fmath.sin(vertAng);




	if
	( 
	not forwardOk 
	and (abs(badHoriAng-horiAng)>fourthpi) 
	and not pauseAtLevelChange 
	)  then
		forwardOk:=true;
		badHoriAng:=-10.0*twopi;
	end if;


end handle_mouse_move;














------------------ end game specific code -----------------------------


function bitmatch( x, y : integer ) return boolean is
	result : boolean := false;
	a : integer := x;
	b : integer := y;
begin
	for i in 1..32 loop
		if ( odd(a) and odd(b) ) then result:=true; end if;
		a:=a/2;
		b:=b/2;
	end loop;
	return result;
end;


--for debugging:
procedure output( a : mat44 ) is
begin
	for row in 1..4 loop
	for col in 1..4 loop
		put( float'image( a(row,col) ) &" ");
	end loop;
	new_line;
	end loop;
	new_line;
end;




procedure readPuzzle( level: integer ) is separate;




------- begin addendum 5jan15 -------------------------------------------------

-------------- begin myst setup ---------------------------------------


procedure island_texture_setup is separate;




procedure zeroBtns is
begin
	btn_bak:=0;
	btn_for:=0;
	btn_pic1:=0;
	btn_pic2:=0;
	btn_slf:=0;
	btn_srt:=0;
	btn_jmp:=0;
end zeroBtns;









procedure drawWormHole( nowTime: float; lmvp: mat44 ) is
begin

		-- fancy fragshader draws stars...awesome!
		if dungeonLocal then
			glext.binding.glUseProgram( tunshadid2 );
			glext.binding.gluniformmatrix4fv( tunmatid2, 1, gl_false, lmvp(1,1)'address );
			glext.binding.gluniform1f(tuntimeid2, glfloat(nowTime) );
			glext.binding.gluniform2f(tunresid2, glfloat(Fwid), glfloat(Fhit) );
		elsif toIsland then
			glext.binding.glUseProgram( tunshadid3 );
			glext.binding.gluniformmatrix4fv( tunmatid3, 1, gl_false, lmvp(1,1)'address );
			glext.binding.gluniform1f(tuntimeid3, glfloat(nowTime) );
			glext.binding.gluniform2f(tunresid3, glfloat(Fwid), glfloat(Fhit) );
		elsif fromIsland then
			glext.binding.glUseProgram( tunshadid1 );
			glext.binding.gluniformmatrix4fv( tunmatid1, 1, gl_false, lmvp(1,1)'address );
			glext.binding.gluniform1f(tuntimeid1, glfloat(nowTime) );
			glext.binding.gluniform2f(tunresid1, glfloat(Fwid), glfloat(Fhit) );
		end if;
		tunnelobj.draw(wmo,vertbuff,elembuff);


end drawWormHole;







--used for debugging:
function dumpGLerrorQueue(id: string) return integer is
	use gl.binding;
	errno: interfaces.c.unsigned;
	isAnError: boolean;
	ercount: integer := 0;
begin
	isAnError:=false;
	loop
		errno:=glGetError;
		exit when errno=gl_no_error;
		ercount:=ercount+1;
		isAnError:=true;
		put("GLerror=");
		put(interfaces.c.unsigned'image(errno));
		new_line;
	end loop;
	if isAnError then
		put_line("...@ id="&id);
	end if;
	return ercount;
end dumpGLerrorQueue;
--
-- 16#0#   =    0 = no_error
-- 16#500# = 1280 = invalid_enum
-- 16#501# = 1281 = invalid_value
-- 16#502# = 1282 = invalid_operation ?reusing uniformID?
-- 16#503# = 1283 = stack_overflow
-- 16#504# = 1284 = stack_underflow
-- 16#505# = 1285 = out_of_memory
--








--circular angle is reset to lie between -pi..+pi
function mindifangl( angle: float ) return float is
	angl: float := angle;
begin

	while angl > +onepi loop
		angl:=angl-twopi;
	end loop;

	while angl < -onepi loop
		angl:=angl+twopi;
	end loop;

	myassert( angl>=-onepi );
	myassert( angl<=+onepi );

	return angl;

end mindifangl;




-- choose camera angle closest to avatar-look-dir:
function branchnear( angAva, angCam : float ) return float is
begin 
	return angAva+mindifangl(angCam-angAva);
end branchnear;


--gradually slew choriang toward avatar's look-direction:
procedure slewToAvLook is -- uses me-look=horiang to set cam-look=choriang
	ahoriang : float; -- look angle towards avatar
	ghoriang : float; -- ideal goal camera angle
	dang: float;
begin

	if abs(xme-xcam)+abs(zme-zcam) < 0.001 then
		ahoriang:=horiang;
	else
		ahoriang := branchnear(horiang,fmath.arctan(xme-xcam,zme-zcam));
	end if;

	dang := mindifangl(horiang-ahoriang);
	ghoriang := ahoriang + 0.15*dang;
	-- reset camera goal to turn partly [15%] toward horiang
	-- where horiang = avatar's walking direction

	dang := mindifangl(ghoriang-choriang);
	choriang := choriang + 0.02*dang; --move 2% toward goal

	cxlook := fmath.cos(vertang)*fmath.sin(choriang);
	cylook := fmath.sin(vertang);
	czlook := fmath.cos(vertang)*fmath.cos(choriang);



exception -- added 8jan18

	when ada.numerics.argument_error =>

		new_line;
		put("slew2look:  xme="&float'image(xme));
		put("slew2look:  zme="&float'image(zme));
		new_line;

		put("slew2look:  xcam="&float'image(xcam));
		put("slew2look:  zcam="&float'image(zcam));
		new_line;

		put("slew2look:  xme-xcam="&float'image(xme-xcam));
		put("slew2look:  zme-zcam="&float'image(zme-zcam));
		new_line;

		raise;


end slewToAvLook;








--gradually slew choriang to look toward avatar
procedure slewToAv is -- uses me-look=horiang to set cam-look=choriang
	ahoriang : float; -- look angle towards avatar
	dang: float;
begin

	if abs(xme-xcam)+abs(zme-zcam) < 0.001 then
		ahoriang:=horiang;
	else
		ahoriang := branchnear(horiang,fmath.arctan(xme-xcam,zme-zcam));
	end if;

	dang := mindifangl(ahoriang-choriang);
	choriang := choriang + 0.02*dang; --move 2% toward goal

	cxlook := fmath.cos(vertang)*fmath.sin(choriang);
	cylook := fmath.sin(vertang);
	czlook := fmath.cos(vertang)*fmath.cos(choriang);


exception -- added 8jan18

	when ada.numerics.argument_error =>

		new_line;
		put("s2av:  xme="&float'image(xme));
		put("s2av:  zme="&float'image(zme));
		new_line;

		put("s2av:  xcam="&float'image(xcam));
		put("s2av:  zcam="&float'image(zcam));
		new_line;

		put("s2av:  xme-xcam="&float'image(xme-xcam));
		put("s2av:  zme-zcam="&float'image(zme-zcam));
		new_line;

		raise;

end slewToAv;










procedure updateCamera( init: boolean := false )  is separate;

procedure updateVert is separate;











procedure drawAvatar( mytime, xme,yme,zme,hang : float ) is
	use glext.binding;
	use gl.binding;
	ypos: float;
begin

	glUseProgram(pidava20);
	glUniform1f(hang20, glfloat(hang));

	if anyrolling or mrolling then --ZPM is rolling...arms fixed forward
		glUniform1i(idir20, 4);
		ypos:=yme;
	elsif ferrying then --stop walking
		glUniform1i(idir20, 0);
		ypos:=yme-0.5;
	elsif moving and inwater then
		glUniform1i(idir20, 2);
		ypos:=yme;
	elsif inwater then
		glUniform1i(idir20, 3);
		ypos:=yme;
	elsif lshooting then
		glUniform1i(idir20, 6);
		ypos:=yme;
	elsif rshooting then
		glUniform1i(idir20, 5);
		ypos:=yme;
	else
		glUniform1i(idir20, glint(direction));
		ypos:=yme;
	end if;

	if fadeAvatar then
		glUniform1i(fade20,1);
	else
		glUniform1i(fade20,0);
	end if;

	if interior then
		glUniform1i(side20, 1); -- dungeons ymax=3
		glUniformMatrix4fv(imvp20, 1, GL_FALSE, mvp(1,1)'address);
	else
		glUniform1i(side20, 0); -- beach ymax=20
		glUniformMatrix4fv(imvp20, 1, GL_FALSE, imvp(1,1)'address);
	end if;

	glUniform1f(time20, glfloat(mytime));
	glUniform3f(icen20, 
		glfloat(xme),glfloat(ypos),glfloat(zme) );
	glUniform1i(samp20, 0);

	glbindtexture(gl_texture_2d, ava_texid);

	avatarobj.draw(ava, vertbuff,uvbuff,elembuff);

end drawAvatar;



--this lonely monkey follows the avatar
--around on the island...
procedure updateMonkey( --25may18
	mytime, xme,zme: float;
	xm,ym,zm,am: in out float;
	idir: out integer
	) is

	dt : float := mytime-monkeytime;

	kxc,kzc, kxr,kzr, kr2, kt2, fac, 
	odx,odz, oxluk,ozluk,
	ndx,ndz,nam,dam,dif,
	oam,oxm,oym,ozm,iangl,
	xluk,zluk,dx,dz,hdist: float;

	chwid : constant float :=  onepi/8.0;
	chcen : constant float := -onepi/4.0;

	orad: constant float := 7.0;
	irad: constant float := 3.5;

	monkeyIsClose: constant float := 0.9; --chatter threshold
	closeEnough: constant float := 0.3; --personal space

begin
	idir:=1; --set to zero if blocked
	monkeytime:=mytime;

	oam:=mindifangl(am);
	oxm:=xm;
	oym:=ym;
	ozm:=zm;

	oxluk:=fmath.sin(am);
	ozluk:=fmath.cos(am);
	odx:=dt*0.04*speed*oxluk;
	odz:=dt*0.04*speed*ozluk;

	dx:=xme-xm;
	dz:=zme-zm;
	hdist:=fmath.sqrt(dx*dx+dz*dz);
	xluk:=dx/hdist;
	zluk:=dz/hdist;
	nam:=mindifangl(fmath.arctan(xluk,zluk));

	dam := mindifangl(nam-oam);
	oam := nam-dam;

	ndx:=dt*0.04*speed*xluk;
	ndz:=dt*0.04*speed*zluk;

	dx:=0.99*odx+0.01*ndx; --gradually [1%] change
	dz:=0.99*odz+0.01*ndz; --direction toward
	am:=0.99*oam+0.01*nam; --avatar

	xm:=xm+dx;
	zm:=zm+dz;


	iangl:=fmath.arctan(zm,xm);--not too close to -pi/4

	if abs(iangl-chcen) < chwid then
		xm:=oxm;
		zm:=ozm;
		idir:=0;
	end if;

	rad:=fmath.sqrt(xm*xm+zm*zm);

	if rad<irad then 
	--try tangential deflection on inner limit
		xm:=xm*irad/rad;
		zm:=zm*irad/rad;
	end if;





	-- further, limit monkey pos to avoid ko zones:
	for i in 1..nko loop

		kxc:=0.5*(koxhi(i)+koxlo(i));
		kzc:=0.5*(kozhi(i)+kozlo(i));

		kxr:=0.5*(koxhi(i)-koxlo(i));
		kzr:=0.5*(kozhi(i)-kozlo(i));
		kr2:=kxr*kxr+kzr*kzr; --square of circular radius

		dx:=xm-kxc;
		dz:=zm-kzc;
		kt2:=dx*dx+dz*dz;

		if kt2<kr2 then --intrusion into KO
			fac:=fmath.sqrt(kr2/kt2);
			xm:=kxc+fac*dx;
			zm:=kzc+fac*dz; --move radially away from KO center
		end if;

	end loop; --for i







	--amount of movement since last pos:
	dif:=fmath.sqrt( sqr(xm-oxm)+sqr(zm-ozm) );

	--distance between monkey, avatar:
	hdist:= fmath.sqrt( sqr(xm-xme)+sqr(zm-zme) );

	--finally, test for stopping conditions
	if
		( hdist < closeEnough )
		or
		(dif<0.0001)
		or
		(rad>=orad)
		--or
		--(rad<=irad)
	then
		idir:=0;
		xm:=oxm;
		zm:=ozm;
	end if;

	ym:=sandht(xm,zm,hmax,mwaterlevel,r1,r2);


	if hdist<monkeyIsClose and not chattering then
		snd4ada_hpp.playLoop(chatter);
		chattering:=true;
	elsif hdist>monkeyIsClose and chattering then
		snd4ada_hpp.stoploop(chatter);
		chattering:=false;
	end if;



end updateMonkey;


-- this lonely monkey paces the island back & forth:
imang:float:=0.0; --islandAngle of monkey
dma: float:=+1.0; -- +1=>increasing imang,  -1=>decreasing
procedure xupdateMonkey( 
	mytime, xme,zme: float;
	xm,ym,zm,am: in out float;
	idir: out integer
	) is

	dt : float := mytime-monkeytime;

	half: float;

	rad: constant float := 4.6;
	ama: constant float := 0.00007*onepi;
begin
	monkeytime:=mytime;
	idir:=1;

	half := 0.5*(imang+fourthpi);
	if( fmath.sin(half)<fmath.sin(0.1*onepi) ) then
		dma:=-dma;
	end if;
	imang := imang+dma*ama;

	xm:=rad*fmath.cos(imang);
	zm:=rad*fmath.sin(imang);
	ym:=sandht(xm,zm,hmax,mwaterlevel,r1,r2);

	am:=fmath.arctan(xm,zm)-dma*halfpi; 
	--monkey Look-Angle [perp to radius]

end xupdateMonkey;

procedure drawMonkey( 
	mytime, xme,yme,zme,hang : float;
	idir: integer
	) is
	use glext.binding;
	use gl.binding;
begin

	glUseProgram(pidmky22);
	glUniform1f(hang22, glfloat(hang));

	glUniform1i(idir22, glint(idir));

	glUniform1i(side22, 0); -- beach ymax=20
	glUniformMatrix4fv(imvp22, 1, GL_FALSE, imvp(1,1)'address);

	glUniform1f(time22, glfloat(mytime));
	glUniform3f(icen22, 
		glfloat(xme),glfloat(yme),glfloat(zme) );
	glUniform1i(samp22, 0);

	glbindtexture(gl_texture_2d, mky_texid);

	avatarobj.draw(monkey, vertbuff,uvbuff,elembuff);

end drawMonkey;









-- Begin helpers to read puzzle files: --------------------------------------

--we skip blank lines and comments beginning with "#":
function is_blank( line : string; len:integer ) return boolean is
	ch: character;
begin

	if( len < 1 ) then return true; end if;

	ch:=line( line'first );

	if ch='#' then return true;
	elsif ch = ' ' then	return true;

	--4jul18 added safeguard to handle DOS-format files:
	elsif ada.characters.handling.is_line_terminator(ch) then return true;

	end if;

	return false;

end is_blank;



package mygint_io is new text_io.integer_io(glint);
package myfloat_io is new text_io.float_io(float);



procedure GetInt( Rcd:string;
						Bgn:in out natural;
						Int: in out glint ) is
begin
mygint_io.get( From => Rcd(Bgn..Rcd'last),
					Item => Int,
					Last => Bgn);
Bgn:=Bgn+1;
end GetInt;



procedure getNbInt(tfile:file_type; rcd: in out string; k: in out glint) is
	len,bgn: natural:=1;
begin
	while not end_of_file(tfile) loop
		get_line(tfile, rcd, len);
		if not is_blank(rcd,len) then exit; end if;
	end loop;
	bgn:=rcd'first;

	GetInt(rcd,bgn,k);

end getNbInt;


procedure GetFlt( Rcd:string;
					  Bgn:in out natural;
					  Flt: in out float ) is
nd: positive;
begin
myfloat_io.get( From => Rcd(Bgn..Rcd'last),
					 Item => Flt,
					 Last => nd);
Bgn := nd+1;
end GetFlt;


procedure getNbFlt(tfile:file_type; rcd: in out string; t: in out float) is
	len,bgn: natural:=1;
begin
	while not end_of_file(tfile) loop
		get_line(tfile, rcd, len);
		if not is_blank(rcd,len) then exit; end if;
	end loop;
	bgn:=rcd'first;
	GetFlt(rcd,bgn,t);
end getNbFlt;

-- end helpers to read puzzle files: --------------------------------------




----------- texture prep begin ------------------------------------------------

procedure release_textures is separate; -- prepare to close down

procedure setup_textures is separate; -- prepare dungeon textures


function oppo( dir: dirtype ) return dirtype is
begin
	if    dir=no then return so;
	elsif dir=so then return no;
	elsif dir=ea then return we;
	elsif dir=we then return ea;
	elsif dir=up then return dn;
	elsif dir=dn then return up;
	else
		return none;
	end if;
end oppo;



procedure drawKawhoosh( 
	kcenx,kceny,kcenz, kradx,krady,kradz: glfloat;
	levl: glint;
	matr : mat44
	) is
	use glext.binding;
	use gl.binding;
	use interfaces.c;
begin


		glUseProgram(pidkw02);

		glUniform1i(ilev02, levl); -- shader defaulted to 0

		glUniform1i(samp02, 0);
		glBindTexture(GL_TEXTURE_2D, kw_texid);


		glUniform3f(icen02, kcenx,kceny,kcenz);
		glUniform3f(irad02, kradx,krady,kradz );


		if thirdPerson then
			glUniform3f(ieye02, glfloat(xcam),glfloat(ycam),glfloat(zcam));
		else
			glUniform3f(ieye02, glfloat(xme),glfloat(yme),glfloat(zme));
		end if;

		--window-discard (unused):
		--glUniform1i(kwunifside, 0 ); --shader defaulted to 0
		--glUniform3f(kwunifport1, glfloat(xtgt1),glfloat(ytgt1),glfloat(ztgt1) ); 
		--glUniform3f(kwunifport2, glfloat(xtgt2),glfloat(ytgt2),glfloat(ztgt2) ); 


		gluniformmatrix4fv( imvp02, 1, gl_false, matr(1,1)'address );
		glUniform1f(time02, glfloat(currenttime));

		kwhooshobj.draw(kwhoosh,vertbuff,elembuff);


end drawKawhoosh;


end gameutils;


-----------------------------------------
-- nprng => i in [ 1..16] 
-----------------------------------------
-- spalm => i in [ 1.. 2]		 
-- tpalm => i in [ 3.. 8]		 
-- bamboo=> i in [ 9..11]		
-- grass => i in [12..14]		
-- tgrass=> i in [15..16]		
-----------------------------------------

