--
-- Copyright (C) 2021  <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/>.
--

--with tput00_h;
with cls_h;

with ada.directories;
with System;
with Interfaces.C;
use  type interfaces.c.unsigned;
with interfaces.c.strings;

with Ada.Command_Line;
with Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO;

with ada.numerics.generic_elementary_functions;
--with ada.numerics.discrete_random;

with text_io;

with ada.calendar;

with ada.strings.fixed;

with sysutils;

with GNATCOLL.Terminal;  use GNATCOLL.Terminal;

with Interfaces.C; use Interfaces.C;
with snd4ada;
with realtime;

with gnat.os_lib;



procedure cfrog is

	--subtype dirtype is integer range 1..4;
	--package random_dir is new ada.numerics.discrete_random(dirtype);
	--gen : random_dir.generator;
	-- this is how we randomize:
	-- random_dir.reset(gen); --time-dependent randomization


	use Ada.Strings.Unbounded;
	use Ada.Strings.Unbounded.Text_IO;
	use text_io;
	use interfaces.c;
	use interfaces.c.strings;

	use ada.calendar;

	mswin: constant boolean := (gnat.os_lib.directory_separator='\');


	package fmath is new
			Ada.Numerics.generic_elementary_functions( float );
	use fmath;

	package myint_io is new text_io.integer_io(integer);


	ggoal, winlevel, endgame, 
	collisionpause, 
		userpause, userexit : boolean := false;

	levelpause : constant duration := 3.0; -- seconds between levels

	pausehalf : constant duration := 0.5;
	pause1sec : constant duration := 1.0;
	pause2sec : constant duration := 1.0;



	points   : integer := 0; --score

	frogch: constant character := 'X';

	--river objects:
	logch: constant character := '=';
	turtch: constant character := 'Q';
	lilych: constant character := '@';

	--freeway objects:
	trukch: constant character := 'T';
	autoch: constant character := 'c';


	nrt: constant integer := 5; -- #rowsOfTraffic

	level_width  : constant integer := 28; --cols
	level_height : constant integer := 2+nrt+1+nrt+2; --rows  2+5+1+5+2 ( + 6 msg lines )
	-- window size >= 56 chars X 21 lines



	-- 5 rows each (river=3,4,5,6,7;  road=9,10,11,12,13)
	nrivo: constant integer := nrt*3*4; --5 rows of 3 [quad] RiverObjects
	nfwyo: constant integer := nrt*1*3; --5 rows of 1 [triple] FreewayObjects


	nobj : constant integer := nrivo + nfwyo; -- max # visible objects

--vehicles: 1..nfwyo
--floaters: nfwyo+1..nobj


	loc : array(0..nobj,1..2) of integer --location V=1 (row), H=2 (col)
		:= (others=>(others=>0));
		
	dir : array(0..nobj,1..2) of integer --V,H-direction
		:= (others=>(others=>0));

	-- 11nov19 note:  dir(0,2) now represents RELATIVE
	-- column velocity; i.e. horizontal velocity w.r.t.
	-- its base.  When its base is a floating log,
	-- then that is already moving rightward.







	numlev: constant integer := 3;

	tick  : array(1..numlev) of duration := (0.15, 0.12, 0.10);
	-- seconds between  moves...
	-- critical gameplay setting...
	-- reduce for faster gameplay
	-- NOTE:  pacman tick is in [0.10 .. 0.19] (default=0.14)



	floating,
	pending: boolean := false;

	rivet1, rivet2,
	coin,intro, extra, hop, splash,
	gameover, 
	squash,
	rtime  : interfaces.c.int;





	shortname: constant string := "level.dat";

	levpath: constant string(1..6) := "../../";
	levdir:  constant string(1..7) := "Layout/";


	leveldata : array(1..level_height,1..level_width) of integer
		:= (others=>(others=>0));







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

function evenrow(i:integer) return boolean is
begin
	return (i mod 2 = 0);
end evenrow;

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





procedure initTraffic(lev: integer) is
	j: integer;
begin

	--[triple] roadway objects, five rows 9,10,11,12,13:
	for i in 1..nfwyo/3 loop --1..30/3=10
		j:=i*3-2; -- 1,4,7,...,16

		loc(j,1):= 9 + i mod nrt; --row
		--              colOffset
		loc(j,2):= 1 + ((i mod nrt + level_width/3 * (i mod 3)) mod level_width);

		loc(j+1,1):=loc(j,1);   --same row
		loc(j+1,2):=loc(j,2)+1; --adjacent col

		loc(j+2,1):=loc(j,1);   --same row
		loc(j+2,2):=loc(j,2)+2; --adjacent col

		dir(j+0,1):=0; --row vel
		dir(j+1,1):=0; --row vel
		dir(j+2,1):=0; --row vel

		if odd( loc(j,1) )  or  (lev<numlev)  then
			--right2left:
			dir(j+0,2):=-1; --col vel
			dir(j+1,2):=-1; --col vel
			dir(j+2,2):=-1; --col vel
		else
			--left2right:
			dir(j+0,2):=+1; --col vel
			dir(j+1,2):=+1; --col vel
			dir(j+2,2):=+1; --col vel
		end if;

	end loop;


	-- [quad] river objects, five rows 3,4,5,6,7 (level_width=28)
	for i in 1..nrivo/4 loop -- 1..60/4=15
		j:=nfwyo + i*4-3; -- 6+1, 6+5, 6+9, ... 6+33

		-- 28 cols with 3 quads
		loc(j+0,1):= 3 + i mod nrt; --row
		--                 colOffset
		loc(j+0,2):= 1 + ((i mod nrt + level_width/4 * (i mod 4)) mod level_width);

		loc(j+1,1):=loc(j,1); --same row
		loc(j+1,2):=loc(j,2)+1; --adjacent col

		loc(j+2,1):=loc(j,1); --same row
		loc(j+2,2):=loc(j,2)+2; --adjacent col

		loc(j+3,1):=loc(j,1); --same row
		loc(j+3,2):=loc(j,2)+3; --adjacent col


		dir(j+0,1):=0; --row velocity
		dir(j+0,2):=1; --col velocity

		dir(j+1,1):=0; --row velocity
		dir(j+1,2):=1; --col velocity

		dir(j+2,1):=0; --row velocity
		dir(j+2,2):=1; --col velocity

		dir(j+3,1):=0; --row velocity
		dir(j+3,2):=1; --col velocity

	end loop;


end initTraffic;


procedure movetraffic(toc:integer) is
	oddtoc: constant boolean := odd(toc);
	froghere,froghopped: boolean := false;
begin

--roadway has 5 lanes, rows(9,10,11,12,13)
for a in 1..nfwyo loop
	if 
		evenrow(loc(a,1)) --row 10,12 move every tic
		or
		oddtoc
	then
		loc(a,2):= 1 + ((loc(a,2)-1+dir(a,2)) mod level_width);
	end if;
	--myassert( loc(a,2)<=level_width, 11 );
end loop;

--river has 5 lanes, rows(3,4,5,6,7)
for a in nfwyo+1..nfwyo+nrivo loop
	if 
		evenrow(loc(a,1)) --row 4,6 move every tic
		or
		oddtoc
	then

		froghere:= (loc(0,1)=loc(a,1)) and (loc(0,2)=loc(a,2));
		loc(a,2):= 1 + ((loc(a,2)-1+dir(a,2)) mod level_width);
		if froghere and not froghopped then --move frog too
			loc(0,2):= loc(a,2);
			froghopped:=true; --only move once @ first match
		end if;
	end if;
	--myassert( loc(a,2)<=level_width, 11 );
end loop;

end movetraffic;



procedure initFrog is
begin

	--frog:
	loc(0,1):=14; --row=10 (near bottom)
	loc(0,2):=14; --col=14 (center)

	dir(0,1):=0;
	dir(0,2):=0;

	ggoal:=false;
	winlevel:=false;

end initFrog;



nfrogs : integer := 0;

procedure movefrog( lev, toc: integer ) is
	lf,rt,cn: boolean := false;
	frow:  constant integer := loc(0,1);
	fcol:  constant integer := loc(0,2);
	frdir: constant integer := dir(0,1);
	fcdir: constant integer := dir(0,2);
	nxtfrow: constant integer := frow+frdir;
begin

--note that logs (row 4,6) move every tic,
--other floaters move only on ODD tics.

	if 
		(frow>2) and (nxtfrow<level_height)
		and (frdir/=0 or fcdir/=0) 
	then

		loc(0,1):= nxtfrow; -- new row

		loc(0,2):= 1 + ((fcol-1+fcdir) mod level_width); -- new col
		dir(0,2):=0;

	end if;






	-- now test for arrival @ goal:
	if loc(0,1)=2 then -- arrived @ goal row

		cn := ( leveldata( loc(0,1), loc(0,2)+0 ) = 2 );
		rt := ( leveldata( loc(0,1), loc(0,2)+1 ) = 2 );
		lf := ( leveldata( loc(0,1), loc(0,2)-1 ) = 2 );
		-- allow leeway of 1 space left or right of goal slot!

		if cn or rt or lf then --goal slot reached

			if lev>1 then
				points:=points+2;
			else
				points:=points+1;
			end if;

			ggoal:=true;
			snd4ada.playSnd(rivet1);

			if cn then
				leveldata( loc(0,1), loc(0,2+0) ) := 3; -- 3 denotes FILLED
			elsif rt then
				leveldata( loc(0,1), loc(0,2)+1 ) := 3; --FILLED
				loc(0,2):=loc(0,2)+1; --kindly adjusting frogCol
			elsif lf then
				leveldata( loc(0,1), loc(0,2)-1 ) := 3; --FILLED
				loc(0,2):=loc(0,2)-1; --kindly adjusting frogCol
			end if;
			nfrogs:=nfrogs+1;

			if nfrogs<4 then
				initFrog; --next frog
			else --final frog, win this level
				snd4ada.playSnd(coin);
				winlevel:=true;
			end if;

		else -- missed goal slot

			-- keep going:
			--initFrog; --try again, no penalty

			-- FAIL:
			collisionpause:=true;
			pending:=false; --empty move queue
			dir(0,1):= 0;  dir(0,2):= 0; --frog stops
			endgame:=true;
			snd4ada.playSnd(squash);

		end if;

	end if;

	dir(0,1):=0; --stop frog's vertical motion
	--if not floating then
	dir(0,2):=0; --stop frog's horizontal motion, too
	--end if;


end movefrog;











procedure loadbkgd is
	fileid : text_io.file_type;
	ini: integer;
begin

	if ada.directories.Exists( levdir&shortname ) then
	text_io.open(fileid,text_io.in_file,levdir&shortname);
	else
	text_io.open(fileid,text_io.in_file,levpath&levdir&shortname);
	end if;

	for row in 1..level_height loop
	for col in 1..level_width loop
		myint_io.get(fileid, ini);
		leveldata(row,col):=ini;

		-- 0 => normal
		-- 1 => wall
		-- 2 => homeGoal @ (row=2, col=5,11,17,23)

	end loop;
	end loop;
	text_io.close(fileid);

end loadbkgd;











procedure draw( lev: integer );


procedure checkcollision is
	 home,road,river: boolean;
begin

	floating:=false;
	river := (3<=loc(0,1) and loc(0,1)<=7);
	road := (9<=loc(0,1) and loc(0,1)<=13);
	home := (loc(0,1)=2); --frog reached upper bank of river

	if home then
		null;
	elsif road then

		for a in 1..nfwyo loop -- check each fwy vehicle (frog: a=0)

			if 
				( loc(a,1)=loc(0,1) and loc(a,2)=loc(0,2) )
			then --vehicle a collides w/frog
				collisionpause:=true;
				pending:=false; --empty move queue
				dir(0,1):= 0;  dir(0,2):= 0; --frog stops
				endgame:=true;
				snd4ada.playSnd(squash);
			end if;
		end loop; -- for each fwy obj



	elsif river then

		for a in nfwyo+1..nfwyo+nrivo loop 
		-- check each floating river object (frog: a=0)

			floating := floating or ((loc(a,1)=loc(0,1)) and (loc(a,2)=loc(0,2))); 
			-- this floater versus this frog

		end loop; -- for each moving object

		if not floating then --froggy dies in river
			collisionpause:=true;
			pending:=false; --empty move queue
			dir(0,1):= 0;  dir(0,2):= 0; --frog stops
			endgame:=true;
			snd4ada.playSnd(splash);

		end if;

	end if; --river


end checkcollision;





procedure draw( lev: integer ) is

	info: terminal_info;

	chr: character;
	val, fr,fc, row,col: integer;
	Ok, roadway, river: boolean;

	-- According to gnatcoll-terminal.ads
	-- these are all the available colors:
	-- m=magenta, y=yellow, r=red, g=grey, 
	-- b=blue, k=black, n=green, c=cyan
	type enum is (m,y,r,g,b,k,n,c,x); -- x => not yet set
	colr : enum := x;

begin

	info.init_for_stdout(auto);

	if mswin then
		--tput00_h.cursorHome; -- put cursor @ UL
		cls_h.cursorHome; -- put cursor @ UL

	else -- linux or osx

		-- quick, but no erase:
		--SysUtils.bShell("tput cup 0 0", Ok); 

		--I prefer the erase ftn on linux better:
		SysUtils.bShell("clear", Ok);

	end if;

   --Info.Set_Color (style=>bright); --this works too
   --Info.Set_Style (style=>bright); --may upset colors
	colr:=x;

	Info.Set_Bg(grey);
	Info.Set_Fg(red); colr:=r;
	put_line("         T e r m i n a l  F r o g g e r           q=quit");

	for rr in 1..level_height loop

		if 3<=rr and rr<=7 then
			Info.Set_Bg(blue);
		elsif 9<=rr and rr<=13 then
			Info.Set_Bg(grey);
		else
			Info.Set_Bg(black);
		end if;

		for cc in 1..level_width loop
			-- we cycle thru all screen positions...

			val:=leveldata(rr,cc);
			case val is
				when 0 => 
					chr:=' ';
				when 1 => 
					chr:='#'; --wall
					if colr/=b then
						info.set_fg(blue); colr:=b;
					end if;
				when 2 =>
					chr:=' ';
				when 3 => -- inactive frog @ home
					chr:='X';
					if colr/=n then
						info.set_fg(green); colr:=n;
					end if;
				when 4 => -- home slot boundary
					chr:='#'; --wall
					if colr/=r then
						info.set_fg(red); colr:=r;
					end if;

				when others => 
					null;
			end case;


			fr:=loc(0,1);
			fc:=loc(0,2);
			if fr=rr and fc=cc then -- active frog @ this screenPos

				if colr/=y then
					info.set_fg(yellow); colr:=y;
				end if;
				chr:='X';

			else -- not frog

				--check object list (cars, logs)
				for jo in 1..nobj loop
					row:=loc(jo,1);
					col:=loc(jo,2);
					roadway := (jo<=nfwyo);
					river   := (jo>nfwyo);
					if row=rr and col=cc then -- vehicle or floater @ screenPos
						
						if roadway then -- road car or truck (rr=7,8,9)

							if rr=10 or rr=12 then --odd lanes
								chr:=trukch;
							else -- edge of road
								chr:=autoch;
							end if;
							if colr/=k then
								info.set_fg(black); colr:=k;
							end if;

						elsif river then -- river log or lillypad (rr=3,4,5)

							if rr=4 or rr=6 then --odd rows are logs
								chr:=logch;
								if colr/=r then
									info.set_fg(red); colr:=r;
								end if;
							elsif rr=5 then
								chr:=turtch;
								if colr/=c then
									info.set_fg(cyan); colr:=c;
								end if;
							else --edge of river greenLillypad
								chr:=lilych;
								if colr/=n then
									info.set_fg(green); colr:=n;
								end if;
							end if;

						end if;

					end if;
				end loop; --for jo

			end if; --not frog

			put(" " & chr); --draw object @ screenPos

		end loop; -- col (cc)
		new_line;

	end loop; -- row (rr)
	new_line;


   Info.Set_Color (Standard_Output, Style => Reset_All);

	put_line(" Level: "&integer'image(lev));
	put_line(" Score: "&integer'image(points));
	if ggoal and lev=numlev then
		put_line("You Win the final level!                           ");
	elsif ggoal then
		put_line(" Level Complete...loading next level.");
	elsif endgame then
		put_line(" Sorry, you lose!  Game Over.");
	elsif userpause then
		put_line(" Game Paused...");
		put_line(" q=quit;  p= pause-toggle;  c= toggle faster colors");
	elsif userexit then
		put_line("Exitting Game...                                   ");
	else -- 51 blanks
		put_line("                                                   ");
	end if;

end draw;



function isvalid(ch: character) return boolean is
	ok: boolean := false;
begin
	case ch is
		when 'H'|'P'|'M'|'K' => ok:=true; --mswin
		when 'A'|'B'|'C'|'D' => ok:=true; --linux/osx
		when 'w'|'s'|'d'|'a' => ok:=true;
		when 'i'|'k'|'l'|'j' => ok:=true;
		when     'x'|'q'|'p' => ok:=true;
		when others => ok:=false;
	end case;
	return ok;
end;



procedure handle_key_down( ch: character; changed: out boolean ) is
begin

	changed:=false;


-- note that arrow keys typically produce cap letter
-- chars, preceded by 1 or 2 non-printable chars.
--
-- on Linux:		<home>='H'	<end>='F'
--   A		
-- D B C
--
-- or on MSWin:	<home>='G'	<end>='O'
--   H
-- K P M




	case ch is


		when 'H' | 'A' | 'w' | 'i'  => --up

			dir(0,1):=-1; 
			dir(0,2):=0; --hvel now governed by floaters!
			changed:=true;
			snd4ada.playSnd(hop);


		when 'P' | 'B' | 's' | 'k' =>	--dn

			dir(0,1):=+1; 
			dir(0,2):=0; 
			changed:=true;
			snd4ada.playSnd(hop);

		when 'K' | 'D' | 'a' | 'j' =>	--lf

			dir(0,1):=0; 
			dir(0,2):=-1;  --applied ONCE, the reZeroed
			changed:=true;
			snd4ada.playSnd(hop);


		when 'M' | 'C' | 'd' | 'l' =>	--rt

			dir(0,1):=0; 
			dir(0,2):=+1;  --applied ONCE, then reZeroed
			changed:=true;
			snd4ada.playSnd(hop);


		when 'x' | 'q' =>	userexit:=true; changed:=true;
		when 'p' => userpause:=not userpause; changed:=true; -- toggle


		-- debug messages to be shown here are immediately erased
		-- unless we pause for user input...
		when others => null;

	end case;


end handle_key_down;


procedure initsounds( path: string ) is
begin

	snd4ada.initSnds;
	-- many versions of frogger have no bkgd music...

	squash := snd4ada.initSnd( --1sec diminishing tone
		Interfaces.C.Strings.New_String(path&"squash.wav"));
	if squash<0 then
		put_line("snd4ada.initSnd ERROR squash");
		raise program_error;
	end if;


	splash := snd4ada.initSnd( --2sec
		Interfaces.C.Strings.New_String(path&"splash.wav"));
	if splash<0 then
		put_line("snd4ada.initSnd ERROR splash");
		raise program_error;
	end if;

	hop := snd4ada.initSnd( -- 1/4 second
		Interfaces.C.Strings.New_String(path&"hop4.wav"));
	if hop<0 then
		put_line("snd4ada.initSnd ERROR hop");
		raise program_error;
	end if;

	coin := snd4ada.initSnd( --2sec powerup
		Interfaces.C.Strings.New_String(path&"coin.wav"));
	if coin<0 then
		put_line("snd4ada.initSnd ERROR coin");
		raise program_error;
	end if;

	intro := snd4ada.initSnd( --7sec jingle
		Interfaces.C.Strings.New_String(path&"intro_frogger.wav"));
	if intro<0 then
		put_line("snd4ada.initSnd ERROR intro");
		raise program_error;
	end if;


	rivet1 := snd4ada.initSnd( --1sec
		Interfaces.C.Strings.New_String(path&"rivet1.wav"));
	if rivet1<0 then
		put_line("snd4ada.initSnd ERROR rivet1");
		raise program_error;
	end if;




	rivet2 := snd4ada.initSnd( --2sec
		Interfaces.C.Strings.New_String(path&"rivet2.wav"));
	if rivet2<0 then
		put_line("snd4ada.initSnd ERROR rivet2");
		raise program_error;
	end if;




	extra := snd4ada.initSnd( --2sec jingle
		Interfaces.C.Strings.New_String(path&"extra.wav"));
	if extra<0 then
		put_line("snd4ada.initSnd ERROR extra");
		raise program_error;
	end if;






	gameover := snd4ada.initSnd(
		Interfaces.C.Strings.New_String(path&"game-over-02.wav"));
	if gameover<0 then
		put_line("snd4ada.initSnd ERROR gameover");
		raise program_error;
	end if;



end initsounds;



	ich, qch: character;
	Ok, avail, digested : boolean := false;

	nextime : Time := clock;
	toc: integer:=0;

	--ispd : integer := 5;

	path0 : constant string(1..7)  := "sounds/";
	path1 : constant string(1..13) := "../../sounds/";

begin --frogger

	if mswin then
		rtime:=realtime.hiPriority;
		-- note:  this seems necessary because some, 
		-- but not all, windows commandline terminals 
		-- seem to randomly freeze at normal priority.
	else
		rtime:=1;
	end if;

	-- holdover from pacman, which might be useful:
	-- GameSpeed 0:slow, 9:fast
	--ispd := 5; -- default
	--if ada.command_line.argument_count = 1 then
	--	declare
	--		pstr : string := ada.command_line.argument(1);
	--		lst: natural;
	--	begin
	--		myint_io.get(pstr, ispd, lst);
	--	end; -- declare
	--end if;
	--tick := 0.19 - ispd*0.01; --fast=0.1 ... 0.19=slowestTick


	if ada.directories.Exists(path0) then
		initsounds(path0);
	else
		initsounds(path1);
	end if;


	--randomize
	--random_dir.reset(gen); --time-dependent randomization


-- requires windowsize at least 57x35:

	-------------------------- outer level loop top -----------------
	for level in 1..numlev loop

		loadbkgd;
		initTraffic(level);
		initFrog;
		nfrogs:=0;

		if mswin then
			SysUtils.bShell("cls", Ok); -- erase-terminal
		else
			SysUtils.bShell("clear", Ok); -- erase-terminal
		end if;



		draw(level);


		snd4ada.playSnd(intro); --Intro

		-- begin countdown:
		put(" Ready... 7");
		for i in reverse 0..6 loop
			nextime:=nextime+pause1sec;  
			delay until nextime;
			put(integer'image(i));
		end loop;
		nextime:=nextime+pause1sec;  
		delay until nextime;


		--snd4ada.playSnd(coin);
		--delay 2.0;

		snd4ada.playSnd(hop);


		-- to try and ameliorate primitive timing...
		-- insert ch into a pending position;
		-- keep trying to use it until either
		-- a) it gets used,
		-- b) or a new "ch" replaces it,
		-- c) or pacman loses a life.

		if mswin then
			SysUtils.bShell("cls", Ok); -- erase-terminal
		else
			SysUtils.bShell("clear", Ok); -- erase-terminal
		end if;



		pending:=false;
		while not userexit  and not winlevel and not endgame loop
--------------------- inner event loop top --------------------------

			toc:=toc+1; --time count...
			if not userpause then
				movefrog(level,toc);
				movetraffic(toc); 
				checkcollision;
			end if;
			draw(level); --=======MAIN DRAW=======================

			if collisionpause then
				collisionpause:=false;
				nextime:=nextime+pause1sec;
				delay until nextime;
			end if;

----------------------- grab keystroke -----------------------------
				avail:=false; get_immediate(ich, avail);
				if avail and then isvalid(ich) then --enqueue new user command
					qch:=ich;
					pending:=true; -- => 1 or more is in the queue
				end if;
				if pending then --deal with most recent user command
					handle_key_down(qch,digested);
					pending := not digested;  --feed again, if need be
				end if;

			nextime:=nextime+tick(level);  -- regulate timing
			delay until nextime; --update timer

----------------------- grab keystroke -----------------------------
				avail:=false; get_immediate(ich, avail);
				if avail and then isvalid(ich) then --enqueue new user command
					qch:=ich;
					pending:=true;
				end if;
				if pending then --deal with most recent user command
					handle_key_down(qch,digested);
					pending := not digested;  --feed again, if need be
				end if;
--------------------- inner event loop bottom -----------------------
		end loop;


		if mswin then
			SysUtils.bShell("cls", Ok); -- erase-terminal
		else
			SysUtils.bShell("clear", Ok); -- erase-terminal
		end if;

		if not endgame and not userexit then
			ggoal:=true;
		end if;
		draw(level); --Completed this Level

		exit when userexit or endgame; --quit

		if level < numlev then
			nextime:=nextime+levelpause;  
			delay until nextime; --update timer

		elsif winlevel then -- conquered Final Level
			--celebrate
			snd4ada.playSnd(intro); --Intro
			delay 7.0;

		else -- gameover
			snd4ada.playSnd(gameover); --game over
			delay 6.5;

		end if;

	end loop; -- outer for level
	------------------------ outer level loop bottom -----------------


	if mswin then
		SysUtils.bShell("cls", Ok); -- erase-terminal
	else
		SysUtils.bShell("clear", Ok); -- erase-terminal
	end if;


	if mswin then
		if rtime=0 then
			put_line("RealTime achieved");
		else
			put_line("RealTime NOT achieved");
		end if;
	end if;


	if endgame then
		snd4ada.playSnd(gameover); --game over
		delay 1.3;
	end if;


	snd4ada.termSnds;

end cfrog;

