
separate (gameutils)

procedure updateCamera( init: boolean := false )  is
	kxl,kxh,kyl,kyh,kzl,kzh, tt: float;
	ixgoal, iygoal, izgoal, xc,yc,zc, ff: float;
	oycam, dx, dz: float;

	-- camera  buffer  distance:  bigger than the
	-- offset of wall textures from room bounds,
	-- yet smaller than margin:
	buf: constant float := 0.05; 


---------------------------------------------------------------
	-- set camera slew-rates for various modes:

	fb_slewrate: constant float := 0.02;
	--camera lag on beach moving forward

	--possibly bad camera viewpoint if < 15%, but not sure.
	bb_slewrate: constant float := 0.15;
	--camera lag while moving backward on beach


	bi_slewrate: constant float := 0.01; --0.005; (changed 30aug23)
	--camera lag while moving backward in interiors

	--if too small, avatar gets too distant...
	fi_slewrate: constant float := 0.03; --0.04; (changed 30aug23)
	--camera lag while moving forward or zooming in interiors
	--zooming => camera distance is normalizing

---------------------------------------------------------------

	--note:  we must prevent KO between *cam, *me
	okcam: boolean := true;
	sokobaning: boolean := false;

	c: integer;
	nc : constant integer := 100; --steps of ioffset/nc=1/nc
	-- # cuts of segment between "me" and "camera"
	-- increase if jitters noticed

	--ioffsetu,
	ttx, ttz, fv, fh, rng, adj,
	boffsetu, --ideal horizontal offset on Beach (exterior)
	voffsetu, 
	sokradx, sokradz: float;
	xsok,zsok: float := 50.0;

	--standoff factors while solving:
	sokbuf: constant float := 1.0;
	sokdv: constant float := 20.0;
	sokdh: constant float := 2.0;

	zooming: boolean;

begin

	--note: nominal camdist=1.0 ...is controlled by adabindings/glfwada/zoomwheel.ad*

	zooming:=false;
	if camdist/=ocamdist then 
		zooming:=true; 
		ocamdist:=camdist;
	end if;

	oycam:=ycam;

	sokobaning:=false;

	voffsetu:=camdist*voffset; --ideal vertical offset (nominally camdist/10)
	ioffsetu:=camdist*ioffset; --ideal horizontal offset in interiors (nominally camdist)
	boffsetu:=camdist*boffset; --ideal horizontal offset on beach (nominally camdist/2)

	if interior and level>=1 and level<=mxlev then

		-- New method to surround sok area with a "sokbuf"
		-- = 1.0 unit, where camera standoff factors
		-- gradually increase from (1,1) to (sokdh,sokdv)

		sokradx := float(ncols+1)/2.0*rolldist;
		xsok := xorigin(level)+1.0-sokradx;
		sokradz := float(nrows+1)/2.0*rolldist;
		zsok := zorigin(level)+1.0-sokradz;

		dx := abs(xsok-xme);
		dz := abs(zsok-zme);

		ttx:= (dx-sokradx)/sokbuf;
		ttz:= (dz-sokradz)/sokbuf;
		fclamp(ttx,0.0,1.0);
		fclamp(ttz,0.0,1.0);
		tt:=max(ttx,ttz);

		--OLD code:
--		if tt<1.0 then sokobaning:=true; end if;
--		fv:=sokdv*(1.0-tt) + tt; -- @tt=1:  fv=1,  @tt=0, fv=sokdv=20
--		fh:=sokdh*(1.0-tt) + tt; -- @tt=1:  fh=1,  @tt=0, fh=sokdh=2
--		voffsetu:=fv*voffset*camdist; -- 20*0.1*camdist = 2.0*camdist
--		ioffsetu:=fh*ioffset*camdist; --  2*1.0*camdist = 2.0*camdist (horiz-offset)

		--new code as of 8sep23:
		if tt<1.0 then 
			-- during sokoban puzzle solving the camera regimen is different...
			-- we try to minimize camera movement/repointing and to
			-- maintain a long standoff. Otherwise it gets disorienting.
			sokobaning:=true;
			fv:=sokdv*(1.0-tt) + tt; -- @tt=1:  fv=1,  @tt=0, fv=sokdv=20
			fh:=sokdh*(1.0-tt) + tt; -- @tt=1:  fh=1,  @tt=0, fh=sokdh=2
			voffsetu:=fv*voffset*camdist; -- 20*0.1*camdist = 2.0*camdist
			ioffsetu:=fh*ioffset*camdist; --  2*1.0*camdist = 2.0*camdist (horiz-offset)
		end if;


	end if;




	-- initialize ideal camera position:
	tt:=1.0;

	if interior then
		ixcam:=xme - tt*ioffsetu*xlook;
		izcam:=zme - tt*ioffsetu*zlook;
	else
		ixcam:=xme - tt*boffsetu*xlook;
		izcam:=zme - tt*boffsetu*zlook;
	end if;
	iycam:=yme + tt*voffsetu;


	ixgoal:=ixcam;
	iygoal:=iycam;
	izgoal:=izcam;

-- now reduce camera offset, if necessary
-- to avoid KOs, walls:

	c:=0;
	while okcam and interior loop
	-- adjust ideal camera position versus ko zones but
	-- do this only for interiors with convex obstacles.

		ff:=float(c)/float(nc); --step from avatar towards ideal
		xc:= xme*(1.0-ff) + ixgoal*ff;
		yc:= yme*(1.0-ff) + iygoal*ff;
		zc:= zme*(1.0-ff) + izgoal*ff;

			exit when -- we hit walls of room
				(abs(yc)>ymax-buf) or
				(abs(xc)>xmax-buf) or
				(abs(zc)>zmax-buf);


		okcam:=true;
		for i in 1..nko loop -- check all KOs that apply
		if (i /= pko) then
			kxl:=xc-(koxlo(i)-buf);
			kxh:=(koxhi(i)+buf)-xc;
			kyl:=yc-(koylo(i)-buf);
			kyh:=(koyhi(i)+buf)-yc;
			kzl:=zc-(kozlo(i)-buf);
			kzh:=(kozhi(i)+buf)-zc;
			if (kxl*kxh>0.0) and (kyl*kyh>0.0) and (kzl*kzh>0.0) then 
				okcam:=false;
			end if; --intrusion into ko
		end if;
		end loop; --for i

		if okcam then -- this standoff is valid
			ixcam:=xc; iycam:=yc; izcam:=zc;
		else
			exit;
		end if;

		c:=c+1;
		exit when c>nc;

	end loop; --while not okcam


-- at this point (ixcam,iycam,izcam) is set as ideal cam pos


	if init or not thirdPerson then
		xcam:=ixcam; ycam:=iycam; zcam:=izcam;
		-- need these initialized in case user 
		-- switches to 3rd person before moving,
		-- but also when entering new room...
	end if;


	if sokobaning then --redefine ideal camPos

		rng:=fmath.sqrt(sqr(xcam-xme)+sqr(zcam-zme));
		adj:=ioffsetu/rng;

		-- try to maintain ideal camera standoff
		-- but without changing direction since
		-- that soon becomes disorienting:
		ixcam:=xme+(xcam-xme)*adj;
		izcam:=zme+(zcam-zme)*adj;

	end if; --sokobaning


	-- we only adjust horizontal camera if moving
	-- ...otherwise we would never see duke's face !
	--if direction>0 then -- moving forward
	if direction>0 or zooming then -- moving or zooming 8dec18

		if onIsland then
			-- fb_slewrate=0.02 (more lazy camera action on beach)
			-- smoothly move actual camera position toward ideal
			xcam := (1.0-fb_slewrate)*xcam + fb_slewrate*ixcam;
			zcam := (1.0-fb_slewrate)*zcam + fb_slewrate*izcam;
			ycam := (1.0-fb_slewrate)*ycam + fb_slewrate*iycam; 
			slewToAv; --gradual slew towards avatar
		else
			-- fi_slewrate=0.03
			-- smoothly move actual camera position toward ideal
			xcam := (1.0-fi_slewrate)*xcam + fi_slewrate*ixcam;
			zcam := (1.0-fi_slewrate)*zcam + fi_slewrate*izcam;
			ycam := (1.0-fi_slewrate)*ycam + fi_slewrate*iycam; 
			slewToAv; --gradual slew towards avatar
		end if;

	elsif direction=0 then --avatar not moving

		slewToAv; --gradual slew to keep avatar in sight

	elsif direction<0 then --moving backward...immediate

		if interior then
			-- bi_slewrate=0.01 (fairly tight)
			-- move actual camera position toward ideal
			xcam := (1.0-bi_slewrate)*xcam + bi_slewrate*ixcam;
			zcam := (1.0-bi_slewrate)*zcam + bi_slewrate*izcam;
			ycam := (1.0-bi_slewrate)*ycam + bi_slewrate*iycam; 
			slewToAv; --awesome!  way better than slewToAvLook.

		else -- beach
			-- bb_slewrate=0.15 (rapid normalization=tight)
			-- rapidly move actual camera position toward ideal
			xcam := (1.0-bb_slewrate)*xcam + bb_slewrate*ixcam;
			zcam := (1.0-bb_slewrate)*zcam + bb_slewrate*izcam;
			ycam := (1.0-bb_slewrate)*ycam + bb_slewrate*iycam; 
			slewToAvLook; --gradual slew
		end if;

	end if;


	-- we now adjust vertang, moving or not,
	-- whenever ycam has changed noticeably,
	-- except when on hilly beach dunes where
	-- frivolous updates cause jerkiness.
	if direction>=0 and interior and thirdperson then
		if abs(ycam-oycam)>0.001 then
			updateVert;
		end if;
	end if; --dir>=0




exception -- added 8jan18

	when ada.numerics.argument_error =>

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

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

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

		raise;



end updateCamera;




