REM  +---------------------------------+
     REM  |                                 |
     REM  |  Gorillas // v1.10 [11-03-2012] |
     REM  |                                 |
     REM  +---------------------------------+


     REM  Disable the Escape key
     
*ESC OFF


     REM  Invoke 64-bit (double precision) maths mode
     
*FLOAT 64


     REM  Set up error handler
     
ON ERROR PROC_error( REPORT$, TRUE )


     REM  Make 10 MB of RAM available for this program
     
M% = 10
     HIMEM = LOMEM + M%*&100000


     REM  Align HIMEM to 32-byte boundary for possibly faster program execution speed
     REM  (Not sure this has much effect!)
     
HIMEM = (HIMEM + 31) AND -32


     REM  Change program window title bar text to read "Gorillas vX.YY"
     
ProgTitle$   = "Gorillas"
     ProgVersion$ = "v1.10"
     SYS "SetWindowText", @hwnd%, ProgTitle$ + " " + ProgVersion$


     REM  Prevent the program window from being resized by the user
     REM  (although it can still be minimized)
     
PROC_fixWindowSize


     REM  We want our window to have the same aspect ratio as the screen,
     REM  so first obtain the full-screen dimensions (width & height)
     
SYS "GetSystemMetrics", 0 TO W%
     SYS "GetSystemMetrics", 1 TO H%


     REM  Our window will be 800 pixels in width, and the height will depend
     REM  on the aspect ratio of the screen.

     
WinW% = 800
     WinH% = (H%/W%) * WinW%
     WinH% = (WinH% + 3) AND -4 : REM  Make the height divisible by 4

     REM  Set our program window size to dimensions WinW% by WinH% pixels
     
VDU 23, 22, WinW%; WinH%; 8, 16, 16, 0


     REM  ----------------------------------------------------------------------------
     REM  Note that the internal bitmap (screen memory) to which the program's
     REM  graphics and text are rendered also has the dimenions WinW% by WinH% pixels.
     REM  ----------------------------------------------------------------------------


     REM  Display program title and version
     
*FONT VERDANA, 16
     COLOUR 15
     PRINT '"  Gorillas " + ProgVersion$ + ""''
     *FONT


     REM  Get user to select if she wants to play the game in 'Windowed' mode, or in 'Full-screen' mode
     
IF FN_getDisplayModeChoiceFromUser = 0 THEN FullScreen% = FALSE ELSE FullScreen% = TRUE

     PRINT
'

     REM  Install and initialise GFXLIB
     
INSTALL @lib$ + "GFXLIB2"
     PROCInitGFXLIB( dispVars{}, 200 )


     REM  Let the user know that things are being loaded...
     
PRINT '''"  Loading..."' : PRINT "  ";


     REM  Install and initialise all required external GFXLIB modules
     
PROCLoadModule( "BoxBlurNxN",                  0 )
     PROCLoadModule( "BoxBlurNxNR",                 0 )
     PROCLoadModule( "BPlotFlip",                   0 )
     PROCLoadModule( "BPlotGreyscale",              0 )
     PROCLoadModule( "BPlotScale",                  0 )
     PROCLoadModule( "ClrX",                        0 )
     PROCLoadModule( "PlotSubpixel",                0 ) : REM  Required by GFXLIB_DrawAntialiasedLine2
     
PROCLoadModule( "DrawAntialiasedLine2",        0 )
     PROCLoadModule( "DrawFilledCircle",            0 )
     PROCLoadModule( "GetBmFontStrWidth",           0 )
     PROCLoadModule( "MMXSubtract64",               0 )
     PROCLoadModule( "PlotAddARGBSaturate",         0 )
     PROCLoadModule( "PlotAvg",                     0 )
     PROCLoadModule( "PlotBlend",                   0 )
     PROCLoadModule( "PlotBlendLD",                 0 )
     PROCLoadModule( "PlotPixelList3",              0 )
     PROCLoadModule( "PlotRotateScale2",            0 )
     PROCLoadModule( "PlotSetAlphaBit",             0 )
     PROCLoadModule( "PlotTestAlphaBit",            0 )
     PROCLoadModule( "PlotAND3",                    0 ) : REM  Required by GFXLIB_DrawBmFont4
     
PROCLoadModule( "DrawBmFont4",                 0 )
     PROCLoadModule( "PlotScaleBlend",              0 )
     PROCLoadModule( "PlotSetAlphaBit3",            0 )
     PROCLoadModule( "PlotSetAlphaValue",           0 )
     PROCLoadModule( "PlotShapeBlend",              0 )
     PROCLoadModule( "PlotShapeHalfIntensity",      0 )
     PROCLoadModule( "ReadAlphaValue",              0 )
     PROCLoadModule( "RectangleSolid",              0 )
     PROCLoadModule( "RotatePoints3D_0",            0 )
     PROCLoadModule( "ShapeGetCumulativeAlphaBits", 0 )
     PROCLoadModule( "TestPixelAlphaBit",           0 )


     REM  Most GFXLIB routines will need to know if the program's running in DirectX full-screen mode
     REM  because if that's the case, bitmaps will need to be flipped ('turned upside down') otherwise
     REM  they will appear upside down when displayed!
     
IF FullScreen% THEN
       
dispVars.flags.flipY& = 1
     ELSE
       
dispVars.flags.flipY& = 0
     ENDIF


     
REM  ==============================================
     REM  Define global variables, structures and arrays
     REM  ==============================================

     
D% = dispVars{} : REM  dispVars{} is accessed *very* frequently, so copy it into a static integer variable (D%) for faster access

     
GetForegroundWindow% = FNSYS_NameToAddress( "GetForegroundWindow" )
     SetWindowText%       = FNSYS_NameToAddress( "SetWindowText" )
     GetTickCount%        = FNSYS_NameToAddress( "GetTickCount" )
     PlaySound%           = FNSYS_NameToAddress( "PlaySound" )
     Sleep%               = FNSYS_NameToAddress( "Sleep" )

     g#                   = -0.08 : REM  Acceleration due to gravity

     
WinningScore%        = 5     : REM  The first to reach this score wins the game

     
NumBlueBananas%           = 20
     MaxNumBananaParticles%    = 300
     MaxNumExplosionParticles% = 300
     MaxNumFireBlobs%          = 100
     MaxNumBuildings%          = 30
     ParticleColourListSize%   = 128

     BananaParticles%    = TRUE
     
ExplosionParticles% = TRUE
     
Fire%               = TRUE
     
ShowPreviousArrow%  = TRUE

     
REM  The base addresses of bitmaps (graphics & game sprites) are stored in the bm{} structure
     
DIM bm{ gorillasTitle_400x100%, blueBanana_69x98%, buttonSelected%, buttonNotSelected%, triButtonLeft%, triButtonRight%, \
     
\ tickBox%, tickBoxTicked%, smallTickBox%, smallTickBoxTicked%, startButton%, greenPtr%, banana%, fireBlob%, close%, \
     
\ gorilla{normal%, leftArmUp%, rightArmUp%}, explosion%, mousePtr%, \
     
\ sky%, city%, bg%, banana20x20%, ptr2%, temp% }


     REM  The base addresses of the WAV sound data are stored in the wav{} structure
     
DIM wav{ blank%, click%, kill%, miss%, out%, throw%(5) }


     REM  Load all the graphics into memory
     REM  Note that the image loading function FNLoadImg automatically converts the images to the
     REM  headerless bitmap format (32-bpp ARGB) required by GFXLIB.
     REM  All addresses are QWORD-aligned (i.e. they are divisible by 8)
     
F% = dispVars.flags.flipY&
     bm.gorillasTitle_400x100%          = FNLoadImg( @dir$ + "resources\gorillas_400x100.GIF",               F% )
     bm.blueBanana_69x98%               = FNLoadImg( @dir$ + "resources\blue_banana_69x98.BMP",              F% )
     bm.buttonSelected%                 = FNLoadImg( @dir$ + "resources\button_selected_32x32x8.BMP",        F% )
     bm.buttonNotSelected%              = FNLoadImg( @dir$ + "resources\button_nonselected_32x32x8.BMP",     F% )
     bm.triButtonLeft%                  = FNLoadImg( @dir$ + "resources\tri_button_left_32x32x8.BMP",        F% )
     bm.triButtonRight%                 = FNLoadImg( @dir$ + "resources\tri_button_right_32x32x8.BMP",       F% )
     bm.tickBox%                        = FNLoadImg( @dir$ + "resources\tickbox_32x32.GIF",                  F% )
     bm.tickBoxTicked%                  = FNLoadImg( @dir$ + "resources\tickbox_ticked_32x32.GIF",           F% )
     bm.smallTickBox%                   = FNLoadImg( @dir$ + "resources\tickbox_20x20.GIF",                  F% )
     bm.smallTickBoxTicked%             = FNLoadImg( @dir$ + "resources\tickbox_ticked_20x20.GIF",           F% )
     bm.startButton%                    = FNLoadImg( @dir$ + "resources\start_button_160x64.BMP",            F% )
     bm.greenPtr%                       = FNLoadImg( @dir$ + "resources\green_ptr_32x32.GIF",                F% )
     bm.banana%                         = FNLoadImg( @dir$ + "resources\banana_128x128x24.BMP",              F% )
     bm.fireBlob%                       = FNLoadImg( @dir$ + "resources\fireblob_32x32.BMP",                 F% )
     bm.close%                          = FNLoadImg( @dir$ + "resources\close_21x21.BMP",                    F% )
     bm.gorilla.normal%                 = FNLoadImg( @dir$ + "resources\gorilla_27x29.GIF",                  F% )
     bm.gorilla.leftArmUp%              = FNLoadImg( @dir$ + "resources\gorilla_throw_left_27x29.GIF",       F% )
     bm.gorilla.rightArmUp%             = FNLoadImg( @dir$ + "resources\gorilla_throw_right_27x29.GIF",      F% )
     bm.explosion%                      = FNLoadImg( @dir$ + "resources\explosion_128x128x24.BMP",           F% )
     bm.mousePtr%                       = FNLoadImg( @dir$ + "resources\ptr_48x48.GIF",                      F% )
     bm.ptr2%                           = FNLoadImg( @dir$ + "resources\ptr2_16x16.GIF",                     F% )


     REM  Set up some bitmap buffers (addresses bm.sky%, bm.city%, etc., are all QWORD-aligned)
     
bm.sky%          = FNmalloc( 4 * WinW%*WinH% ) : REM  The graduated-colour sky, stars and moon are rendered to this bitmap
     
bm.city%         = FNmalloc( 4 * WinW%*WinH% ) : REM  The buildings are rendered to this bitmap
     
bm.bg%           = FNmalloc( 4 * WinW%*WinH% ) : REM  The buildings and sky are combined and rendered to this bitmap
     
bm.banana20x20%  = FNmalloc( 4 * 20*20 )       : REM  The rotated and scaled banana is rendered to this bitmap before plotting
     
bm.temp%         = FNmalloc( 4 * WinW%*WinH% ) : REM  A tempory bitmap buffer



     REM  Load bitmap font definitions (Arial 12, 16 and 32 pt)
     
arial12% = FNLoadData( @lib$ + "GFXLIB_media\arial12pt.DAT" )
     arial16% = FNLoadData( @lib$ + "GFXLIB_media\arial16pt.DAT" )
     arial32% = FNLoadData( @lib$ + "GFXLIB_media\arial32pt.DAT" )


     REM  Flip the font character bitmaps if need be (turn 'em upside down)
     
IF FullScreen% THEN
       PROC
FlipBmFont( arial12% )
       PROCFlipBmFont( arial16% )
       PROCFlipBmFont( arial32% )
     ENDIF


     
REM  Load in the sounds (.WAV) files to be played using the standard Windows PlaySound API function
     
wav.blank%    = FNLoadData( @dir$ + "resources\blank.WAV" )
     wav.kill%     = FNLoadData( @dir$ + "resources\kill.WAV" )
     wav.miss%     = FNLoadData( @dir$ + "resources\miss.WAV" )
     wav.out%      = FNLoadData( @dir$ + "resources\out.WAV" )
     wav.click%    = FNLoadData( @dir$ + "resources\click.WAV" )
     wav.throw%(0) = FNLoadData( @dir$ + "resources\throw0.WAV" )
     wav.throw%(1) = FNLoadData( @dir$ + "resources\throw1.WAV" )
     wav.throw%(2) = FNLoadData( @dir$ + "resources\throw2.WAV" )
     wav.throw%(3) = FNLoadData( @dir$ + "resources\throw3.WAV" )
     wav.throw%(4) = FNLoadData( @dir$ + "resources\throw4.WAV" )
     wav.throw%(5) = FNLoadData( @dir$ + "resources\throw5.WAV" )

     SYS PlaySound%, wav.blank%, 0, 5



     DIM player{(1) alive%, xPos%, yPos%, bm%, oldArrow%, autoImprove%, \
     
\ type&, baseSkill&, skill&, thinking&, delay&, celebrating&, throw&, \
     
\ score&, firstGo& }

     REM  player{()}.type& = 0 (human) or 1 (computer)
     REM  player{()}.baseSkill& (range 0 to 100) only applies to computer player

     
player{( 0 )}.type&        = 0
     player{( 0 )}.baseSkill&   = 50
     player{( 0 )}.skill&       = player{( 0 )}.baseSkill&
     player{( 0 )}.autoImprove% = FALSE

     
player{( 1 )}.type&        = 1
     player{( 1 )}.baseSkill&   = 50
     player{( 1 )}.skill&       = player{( 1 )}.baseSkill&
     player{( 1 )}.autoImprove% = FALSE


     
REM  ====================================================
     REM  Set up DirectX full-screen (if the user selected it)
     REM  ====================================================

     
IF FullScreen% THEN
       COLOUR
6 : PRINT ''"  You can press Alt+F4 at any time to close this program"; : COLOUR 7 : WAIT 350 : PRINT '' : CLS

       
REM  Install and intialise GFXD3D9LIB
       
INSTALL @lib$ + "GFXD3D9LIB.BBC"
       PROC_InitGFXD3D9LIB( TRUE, FALSE ) : REM TRUE=fullscreen; FALSE=don't preserve aspect ratio

       REM  Close the program if the user presses Alt+F4
       
ON CLOSE PROC_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT

       
REM  If there's an error then try to exit cleanly...
       
ON ERROR PROC_GFXD3D9LIB_CleanUp(FALSE) : VDU 4 : CLS : ON : COLOUR 15, 255, 255, 255 : COLOUR 15 : PRINT '" "; : REPORT : PRINT " at line "; ERL; : VDU 7 : WAIT 350 : QUIT

       
REM  This next line is probably not strictly necessary, but what the hell...
       
SYS "timeBeginPeriod", 1
     ENDIF


     
REM  Seed the random number generator
     
R% = RND( -TIME )


     REM  Switch off the mouse pointer and the flashing cursor
     
MOUSE OFF
     OFF



     
REM  ----------------------------------------------------------------------------------------------------------------------


     REM  +-------------------------------+
     REM  |                               |
     REM  |     THE MAIN PROGRAM LOOP     |
     REM  |                               |
     REM  +-------------------------------+


     
REPEAT

       PROC
_TitlePage

       PROC_Main

     UNTIL FALSE

     END
: REM  Just for completeness :)


     REM  ----------------------------------------------------------------------------------------------------------------------






     
DEF PROC_Main

     LOCAL B%, I%
     LOCAL arrowScale%, player%, createNewScene%
     LOCAL celebration%, celebrationCounter%, celebrationCounterStartValue%, celebrator%
     LOCAL close%, quit%, win%
     LOCAL pressEscMsg%, pressAltF4Msg%, debounce%, frames%, frameRate%, time0%, time1%
     LOCAL msX%, msY%, msBtn%

     LOCAL explosion{}, fireBlob{}, banana{}, quadratic{}, buildings{}
     LOCAL bananaParticle{()}, explosionParticle{()}
     LOCAL oldArrow{()}

     PRIVATE arrowObjDefined%, arrowObj{()}
     PRIVATE particleColourTableBuilt%, particleColourList%()

     DIM explosion{ active%, x%, y%, size#, inc# }
     DIM fireBlob{( MaxNumFireBlobs%-1 ) active%, life%, x0%, y0%, x#, y#, xv#, yv#, acc# }
     DIM banana{ thrown%, t%, x#, y#, xv#, yv#, v#, nxv#, nyv#, angle#, angleInc# }
     DIM quadratic{ x1%, y1%, x2%, y2%, x3%, y3%, a#, b#, c# }
     DIM buildings{ numBuildings%, maxHeight%, x%(MaxNumBuildings%-1), width%(MaxNumBuildings%-1), height%(MaxNumBuildings%-1) }
     DIM bananaParticle{( MaxNumBananaParticles% ) active%, life%, xv#, yv#, yacc#, x#, y#, colour%, padding%(2)}
     DIM explosionParticle{( MaxNumExplosionParticles% ) active%, life%, xv#, yv#, x#, y#, colour%, padding%(3)}
     DIM arrowObj{(10) x#, y#, x`#, y`#}
     DIM oldArrow{(10) x1#(1), y1#(1), x2#(1), y2#(1)}

     IF arrowObjDefined% = 0 THEN
       
arrowScale% = 35
       arrowObj{( 0 )}.x# = arrowScale% * -0.25
       arrowObj{( 1 )}.x# = arrowScale% * 0.25
       arrowObj{( 2 )}.x# = arrowScale% * 0.25
       arrowObj{( 3 )}.x# = arrowScale% * 0.6
       arrowObj{( 4 )}.x# = arrowScale% * 0.0
       arrowObj{( 5 )}.x# = arrowScale% * -0.6
       arrowObj{( 6 )}.x# = arrowScale% * -0.25
       arrowObj{( 7 )}.x# = arrowScale% * -0.25
       arrowObjDefined% = 1
     ENDIF

     IF
particleColourTableBuilt% = 0 THEN
       DIM
particleColourTable%( ParticleColourListSize% )
       PROC_buildParticleColourList( particleColourList%(), ParticleColourListSize% )
       particleColourTableBuilt% = 1
     ENDIF

     
player% = 0                              : REM  0 = Player One,  1 = Player Two (sorry if this confuses!)

     
createNewScene% = TRUE                   : REM  PROC_createNewScene called when createNewScene% is TRUE

     
banana.thrown% = FALSE                   : REM  Banana thrown flag

     
celebration% = FALSE                     : REM  When a player wins a game (or round), this flag is set
     
celebrationCounter% = 0                  : REM  While positive, winning gorilla will celebrate if celebration% is TRUE
     
celebrationCounterStartValue% = 260      : REM  Determines for how long a victorious gorilla will celebrate
     
celebrator% = 0                          : REM  Identifies which player (0 or 1) threw the fatal banana

     
player{( 0 )}.score& = 0                 : REM  Init players' scores to zero
     
player{( 1 )}.score& = 0

     close% = FALSE                           : REM  Program close flag
     
quit% = FALSE                            : REM  Quit game flag
     
win% = FALSE                             : REM  If a player attains the winning score, then this flag is set

     
pressEscMsg% = 300                       : REM  Display "Press Escape..." message while pressEscMsg% > 0
     
pressAltF4Msg% = 600                     : REM  Display "Press Alt+F4..." message while pressAltF4Msg% > 0

     
debounce% = TRUE                         : REM  Mouse button debounce flag

     
frames% = 0                              : REM  Number of frames counted in 1 second (1000 ms)
     
frameRate% = 0                           : REM  Frame rate (fps) value reported for full-screen display

     REM  In Windowed display mode, it's important to disable the automatic window refresh
     
IF NOT FullScreen% THEN
       
*REFRESH OFF
     ENDIF


     SYS
GetTickCount% TO time0%              : REM  Start timing frame rendering times!


     
REPEAT


       
REM  See who's go the input focus (hopefully us, especially in full-screen mode!)
       
SYS GetForegroundWindow% TO hwnd%


       REM  If in full-screen mode, check for input focus -- if lost, then quit abruptly.
       REM  This is not ideal - far from it, but sometimes instability occurs when the program loses focus (for example,
       REM  when the "Window button is" pressed).  Must find a robust and reliable way of dealing with loss of focus.
       
IF FullScreen% THEN
         IF
hwnd% <> @hwnd% THEN
           PROC
_GFXD3D9LIB_CleanUp( FALSE ) : VDU 7 : QUIT
           
REMPROC_GFXD3D9LIB_LOSTDEVICE <--- Seems to be causing instability, so safer - though uglier - just to quit
         
ELSE
           
REM  Test for Alt+F4 keypresses (-3 Alt, -21 F4)
           
IF INKEY-3 AND INKEY-21 THEN
             PROC
_GFXD3D9LIB_CleanUp( FALSE )
             VDU 7
             QUIT
           ENDIF
         ENDIF
         
REM  If we have focus then proceed with the full-screen drawing process
         
PROC_GFXD3D9LIB_Start_GFX_Drawing
       ENDIF


       
REM  Do we need to draw a brand new scene?
       
IF createNewScene% THEN
         PROC
_createNewScene
         createNewScene% = FALSE
       ENDIF


       
REM  Draw the background bitmap (i.e. sky and city)
       
SYS GFXLIB_BPlot%, D%, bm.bg%, WinW%, WinH%, 0, 0


       REM  Get (possibly scaled) mouse coordinates and button state
       
PROC_readMouse( msX%, msY%, msBtn% )

       IF debounce% THEN
         IF
msBtn% = 0 THEN
           
debounce% = FALSE
         ELSE
           
msBtn% = 0
         ENDIF
       ENDIF


       
REM  Choose the correct gorilla sprites to display
       
IF NOT celebration% THEN

         FOR
I% = 0 TO 1
           IF I% = 0 THEN B% = bm.gorilla.rightArmUp% ELSE B% = bm.gorilla.leftArmUp%
           IF player{( I% )}.throw& > 0 THEN
             
player{( I% )}.throw& -= 1
             player{( I% )}.bm% = B%
             player{( I% EOR 1 )}.bm% = bm.gorilla.normal%
           ELSE
             
player{( I% )}.bm% = bm.gorilla.normal%
           ENDIF
         NEXT

       ELSE

         IF
(celebrationCounter% DIV 20) MOD 2 = 0 THEN
           
player{( celebrator% )}.bm% = bm.gorilla.leftArmUp%
           player{( celebrator% EOR 1 )}.bm% = bm.gorilla.normal%
         ELSE
           
player{( celebrator% )}.bm% = bm.gorilla.rightArmUp%
           player{( celebrator% EOR 1 )}.bm% = bm.gorilla.normal%
         ENDIF

       ENDIF


       
REM  Display the gorillas
       REM
       REM  Player 1's gorilla sets bit 1 in the alpha byte of overwritten background pixels
       REM  Player 2's gorilla sets bit 2 ...
       REM
       
FOR I% = 0 TO 1

         IF player{( I% )}.alive% THEN

           
REM  First draw the gorilla's shadow
           REM  X = player{( I% )}.xPos% - 27/2 + 8,  Y = player{( I% )}.yPos%-14 - 2
           
SYS GFXLIB_PlotShapeBlend%,  D%, player{( I% )}.bm%, 27, 29, player{( I% )}.xPos%-21, player{( I% )}.yPos%-16, 0, 64

           REM  Now draw the gorilla proper, setting the appropriate bit (I%+1) in the aforementioned alpha bytes
           REM  X = player{( I% )}.xPos% - 27/2,  Y = player{( I% )}.yPos%-14
           
SYS GFXLIB_PlotSetAlphaBit%, D%, player{( I% )}.bm%, 27, 29, player{( I% )}.xPos%-13, player{( I% )}.yPos%-14, I%+1

         ENDIF

       NEXT



       IF NOT
banana.thrown% THEN
         IF NOT
celebration% THEN

           IF
player{( player% )}.type& = 0 THEN

             PROC
_getHumanAction

           ELSE

             PROC
_getComputerAction

           ENDIF

         ENDIF
       ENDIF


       IF
player{( player% )}.type& = 0 THEN
         IF
player{( player% )}.delay& > 0 THEN
           
player{( player% )}.delay& -= 1
         ENDIF
       ENDIF


       
REM  **********************************************
       REM  *                                            *
       REM  *  To cheat, the human player can press F12  *
       REM  *                                            *
       REM  **********************************************
       
IF NOT banana.thrown% THEN
         IF
player{( player% )}.type& = 0 THEN
           IF INKEY
-30 THEN
             PROC
_getCorrectInitialVelocity( ux#, uy# )
             PROC_initBanana( player%, ux#, uy# )
             player{( player% )}.throw& = 20
             player{( player% EOR 1 )}.throw& = 0
           ENDIF
         ENDIF
       ENDIF


       IF
banana.thrown% THEN

         
banana.t% += 1

         REM  Draw the banana, and while doing so, collect any cumulative
         REM  set alpha bits in the overwritten background pixels.
         REM    Bit 0 set means collision with building
         REM    Bit 1 set means collision with Player 1
         REM    Bit 2 set means collision with Player 2
         
alphaBits% = FN_drawBanana

         REM  Create a 'tail' of yellow particles as the banana flies through the air.
         REM  The number of particles produced each frame depends on the speed of the banana
         REM  (hence the banana.v#/2 value passed to PROC_createBananaParticles).
         
IF BananaParticles% THEN
           PROC
_createBananaParticles( INT(banana.v#/2) )
         ENDIF


         
REM  ---------------------------------
         REM  Banana-Player collision detection
         REM  ---------------------------------
         REM  Player 1 alpha collision bit is bit 1
         REM  Player 2 alpha collision bit is bit 2

         REM  Check for banana collision with Player 1
         
IF banana.t% > 15 AND (alphaBits% AND 2) THEN
           IF
player% = 1 THEN
             PROC
_handleBananaHumanCollision( 0 )
           ELSE
             
REM  Check if banana has fallen back onto Player 1 (with lethal consequences!)
             
IF (banana.y# + banana.yv#) < banana.y# THEN
               PROC
_handleBananaHumanCollision( 0 )
             ENDIF
           ENDIF
           
alphaBits% = 0
         ENDIF

         
REM  Check for banana collision with Player 2
         
IF banana.t% > 15 AND (alphaBits% AND 4) THEN
           IF
player% = 0 THEN
             PROC
_handleBananaHumanCollision( 1 )
           ELSE
             
REM  Check if banana has fallen back onto Player 2
             
IF (banana.y# + banana.yv#) < banana.y# THEN
               PROC
_handleBananaHumanCollision( 1 )
             ENDIF
           ENDIF
           
alphaBits% = 0
         ENDIF


         
REM  Check for collision of banana with building
         REM  The building graphics set bit 0 (hence the AND mask of 1) in the alpha byte of the background pixels
         
IF (alphaBits% AND 1) THEN
           PROC
_handleBananaBuildingCollision
         ENDIF


         
REM  Update the banana's position and rotation angle
         REM  (Better check the banana's still flying through the air first!)
         
IF banana.thrown% THEN
           PROC
_updateBanana
         ENDIF

       ENDIF


       
REM  Draw and update all active banana particles
       
PROC_drawBananaParticles


       REM  Draw explosion particles
       
IF ExplosionParticles% THEN
         PROC
_drawExplosionParticles
       ENDIF


       
REM  Draw all active fires
       
IF Fire% THEN
         PROC
_drawFires
       ENDIF


       
REM  Draw and update explosion (if active)
       
IF explosion.active% THEN
         PROC
_drawExplosion
       ENDIF


       
REM  If one of the gorillas is celebrating a kill, then check if it's
       REM  time to draw a new scene (as long as the winning score hasn't yet
       REM  been attained).
       
IF celebration% THEN
         IF
celebrationCounter% > 0 THEN
           
celebrationCounter% -= 1
         ELSE
           IF
player{( celebrator% )}.score& = WinningScore% THEN
             
win% = TRUE
           ELSE
             
createNewScene% = TRUE
           ENDIF
           
celebration% = FALSE
         ENDIF
       ENDIF


       
REM  If there's no kill celebration taking place, then display
       REM  the "Player 1" and "Player 2" text ('highlighted' for the current player)
       
IF player{( celebrator% )}.score& < WinningScore% THEN
         IF
player% = 0 THEN
           SYS
GFXLIB_PlotAvg%, D%,bm.greenPtr%, 32, 32, 0, WinH% - 32
           SYS GFXLIB_DrawBmFont4%, D%, arial16%, "Player 1", 36, WinH%-28, &FF60
           SYS GFXLIB_DrawBmFont4%, D%, arial16%, "Player 2", WinW%-120, WinH%-28, &8030
         ELSE
           SYS
GFXLIB_PlotAvg%, D%,bm.greenPtr%, 32, 32, WinW% - 156, WinH% - 32
           SYS GFXLIB_DrawBmFont4%, D%, arial16%, "Player 1", 36, WinH%-28, &8030
           SYS GFXLIB_DrawBmFont4%, D%, arial16%, "Player 2", WinW%-120, WinH%-28, &FF60
         ENDIF
       ENDIF


       
REM  Display the scores so far each player
       
SYS GFXLIB_GetBmFontStrWidth%, arial32%, STR$player{(0)}.score& TO Z%
       SYS GFXLIB_DrawBmFont4%, D%, arial32%, STR$player{(0)}.score&, 36 + (100-Z%)DIV2, WinH%-80, &FF20A0

       SYS GFXLIB_GetBmFontStrWidth%, arial32%, STR$player{(1)}.score& TO Z%
       SYS GFXLIB_DrawBmFont4%, D%, arial32%, STR$player{(1)}.score&, WinW%-120 + (100-Z%)DIV2, WinH%-80, &FF20A0


       REM  Press F11 to reveal current computer player's skill level
       
IF INKEY-29 THEN
         IF
player{( 0 )}.type& = 1 THEN
           SYS
GFXLIB_DrawBmFont4%, D%, arial12%, STR$player{(0)}.skill&, 36, WinH%-100, &0080FF
         ENDIF

         IF
player{( 1 )}.type& = 1 THEN
           SYS
GFXLIB_DrawBmFont4%, D%, arial12%, STR$player{(1)}.skill&,  WinW%-120, WinH%-100, &0080FF
         ENDIF
       ENDIF


       
REM  Display the "Press Escape..." message for a few seconds
       
IF pressEscMsg% > 0 THEN
         SYS
GFXLIB_GetBmFontStrWidth%, arial12%, "Press Escape to exit to Title Page" TO Z%
         SYS GFXLIB_DrawBmFont4%, D%, arial12%, "Press Escape to exit to Title Page", (WinW% - Z%)DIV2-2, WinH%-18-1, &303030
         SYS GFXLIB_DrawBmFont4%, D%, arial12%, "Press Escape to exit to Title Page", (WinW% - Z%)DIV2, WinH%-18, &A0A0A0
         pressEscMsg% -= 1
       ENDIF


       
REM  Draw small mouse pointer (16x16 crosshair)
       
SYS GFXLIB_Plot%, D%, bm.ptr2%, 16, 16, msX%-8, msY%-8


       REM  Only in full-screen mode, display the "Press Alt+F4...." message for a few seconds
       
IF FullScreen% THEN
         IF
pressAltF4Msg% > 0 THEN
           IF
pressAltF4Msg% < 300 THEN
             SYS
GFXLIB_GetBmFontStrWidth%, arial12%, "Press Alt+F4 to close this program" TO Z%
             SYS GFXLIB_DrawBmFont4%, D%, arial12%, "Press Alt+F4 to close this program", (WinW% - Z%)DIV2-2, WinH%-40-1, &303030
             SYS GFXLIB_DrawBmFont4%, D%, arial12%, "Press Alt+F4 to close this program", (WinW% - Z%)DIV2, WinH%-40, &F0F080
           ENDIF
           
pressAltF4Msg% -= 1
         ENDIF
       ENDIF


       
REM  Only in full-screen mode, display the frame rate (fps)
       REM  In Windowed mode, this frame rate is displayed in the program window's title bar
       
IF FullScreen% THEN
         SYS
GFXLIB_DrawBmFont4%, D%, arial12%, STR$frameRate% + " fps", 5, 5, 0
         SYS GFXLIB_DrawBmFont4%, D%, arial12%, STR$frameRate% + " fps", 4, 4, &FFFF00
       ENDIF


       
REM SYS GFXLIB_PlotShapeBlend%, D%, bm.mousePtr%, 48, 48, msX%-27, msY%-27, 0, 90
       REM SYS GFXLIB_PlotAvg%, D%, bm.mousePtr%, 48, 48, msX%-24, msY%-24


       REM  Either update the program window in the case of Windowed display mode, or
       REM  update the entire screen in full-screen mode
       
IF NOT FullScreen% THEN
         PROC
display
       ELSE
         SYS
GetForegroundWindow% TO hwnd%
         IF hwnd% = @hwnd% THEN
           PROC
_GFXD3D9LIB_End_GFX_Drawing
           PROC_GFXD3D9LIB_Render_GFX_to_Fullscreen
           SYS Sleep%, 1
         ELSE
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
           
PROC_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
         ENDIF
       ENDIF


       
REM  A frame has just been displayed, so bump the frame counter
       
frames% += 1


       REM  After 1 second (give or take a few milliseconds!), the frames are counted
       
SYS GetTickCount% TO time1%
       IF time1% - time0% >= 1000 THEN
         IF NOT
FullScreen% THEN
           SYS
SetWindowText%, @hwnd%, ProgTitle$ + " | " + STR$frames% + " fps"
         ENDIF
         
frameRate% = frames%
         frames% = 0
         SYS GetTickCount% TO time0%
       ENDIF


       
REM  Check for Escape keypress
       
IF NOT win% THEN
         IF INKEY
-113 OR close% THEN
           SYS
PlaySound%, wav.click%, 0, 5
           quit% = FN_exitGame
           IF NOT quit% THEN close% = FALSE
           
debounce% = TRUE
         ENDIF
       ENDIF

     UNTIL
quit% OR win%

     IF win% THEN
       PROC
_playerWins
     ENDIF

     ENDPROC






     
DEF PROC_TitlePage

     LOCAL msX%, msY%, msBtn%, alpha%, Y%, Z%, delay%, debounce%, I%, hwnd%, click%, close%
     LOCAL blueBanana{()}

     DIM blueBanana{( NumBlueBananas%-1 ) x%, y#, dy#, angle#, angleInc#}

     SYS "SetWindowText", @hwnd%, ProgTitle$ + " " + ProgVersion$

     delay% = 0
     debounce% = TRUE
     
click% = FALSE
     
close% = FALSE

     FOR
I% = 0 TO NumBlueBananas%-1
       blueBanana{( I% )}.x% = RND(WinW%)
       blueBanana{( I% )}.y# = WinH% + RND(WinH%)
       blueBanana{( I% )}.dy# = -(0.5 + RND(1))
       blueBanana{( I% )}.angle# = 360.0 * RND(1)
       blueBanana{( I% )}.angleInc# = FN_rndSgn * 1.0 * RND(1)
     NEXT I%

     IF NOT FullScreen% THEN
       
*REFRESH OFF
     ENDIF

     REPEAT

       SYS
GetForegroundWindow% TO hwnd%

       IF FullScreen% THEN
         IF
hwnd% <> @hwnd% THEN
           PROC
_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
         
ENDIF
         PROC
_GFXD3D9LIB_Start_GFX_Drawing
       ENDIF

       PROC
_readMouse( msX%, msY%, msBtn% )

       SYS GFXLIB_Clr%, D%, &202060

       FOR I% = 0 TO NumBlueBananas%-1
         SYS GFXLIB_PlotRotateScale2%, D%, bm.blueBanana_69x98%, 69, 98, blueBanana{(I%)}.x%, blueBanana{(I%)}.y#, blueBanana{(I%)}.angle#*&10000, 0.5*&10000
         blueBanana{(I%)}.angle# += blueBanana{(I%)}.angleInc#
         blueBanana{(I%)}.y# += blueBanana{(I%)}.dy#
         IF blueBanana{(I%)}.y# < -64 THEN
           
blueBanana{( I% )}.x% = RND(WinW%)
           blueBanana{( I% )}.y# = WinH% + RND(WinH%)
           blueBanana{( I% )}.dy# = -(0.5 + RND(1))
           blueBanana{( I% )}.angle# = 360.0 * RND(1)
           blueBanana{( I% )}.angleInc# = FN_rndSgn * 1.0 * RND(1)
         ENDIF
         IF ABS
blueBanana{(I%)}.angle# >= 360 THEN
           
blueBanana{(I%)}.angle# -= SGN(blueBanana{(I%)}.angleInc#) * 360.0
         ENDIF
       NEXT

       SYS
GFXLIB_PlotShapeHalfIntensity%, D%, bm.gorillasTitle_400x100%, 400, 100, (WinW% - 400)DIV2-13, WinH%-100-8-12
       SYS GFXLIB_Plot%, D%, bm.gorillasTitle_400x100%, 400, 100, (WinW% - 400)DIV2, WinH%-100-8

       Y% = 0.6 * WinH%

       SYS GFXLIB_DrawBmFont4%, D%, arial32%, "Player 1", 64, Y%, &FFFF00

       SYS GFXLIB_GetBmFontStrWidth%, arial32%, "Player 2" TO W%
       SYS GFXLIB_DrawBmFont4%, D%, arial32%, "Player 2", WinW%-W%-64, Y%, &FFFF00

       IF player{( 0 )}.type& = 0 THEN
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.buttonSelected%, 32, 32, 64, Y%-64, 1
         SYS GFXLIB_PlotSetAlphaValue%, D%, bm.buttonNotSelected%, 32, 32, 64, Y%-64 - 40, 2
       ELSE
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.buttonNotSelected%, 32, 32, 64, Y%-64, 1
         SYS GFXLIB_PlotSetAlphaValue%, D%, bm.buttonSelected%, 32, 32, 64, Y%-64 - 40, 2
       ENDIF

       IF
player{( 1 )}.type& = 0 THEN
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.buttonSelected%, 32, 32, WinW%-W%-64, Y%-64, 3
         SYS GFXLIB_PlotSetAlphaValue%, D%, bm.buttonNotSelected%, 32, 32, WinW%-W%-64, Y%-64 - 40, 4
       ELSE
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.buttonNotSelected%, 32, 32, WinW%-W%-64, Y%-64, 3
         SYS GFXLIB_PlotSetAlphaValue%, D%, bm.buttonSelected%, 32, 32, WinW%-W%-64, Y%-64 - 40, 4
       ENDIF

       SYS
GFXLIB_DrawBmFont4%, D%, arial16%, "Human", 108, Y%-64 + 5, &FFA000
       SYS GFXLIB_DrawBmFont4%, D%, arial16%, "Computer", 108, Y%-64 + 5 - 40, &FFA000
       SYS GFXLIB_DrawBmFont4%, D%, arial16%, "Human", WinW%-W%-20, Y%-64 + 5, &FFA000
       SYS GFXLIB_DrawBmFont4%, D%, arial16%, "Computer", WinW%-W%-20, Y%-64 + 5 - 40, &FFA000

       IF player{(0)}.type& = 1 THEN
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.triButtonLeft%, 32, 32, 64, Y%-200, 5
         SYS GFXLIB_RectangleSolid%, D%, 100, Y%-200, 140, 32, &000040
         SYS GFXLIB_RectangleSolid%, D%, 102, Y%-200+2, player{(0)}.baseSkill&/100 * (140-4), 32-4, &2040A0
         SYS GFXLIB_PlotSetAlphaValue%, D%, bm.triButtonRight%, 32, 32, 104+140, Y%-200, 6
         SYS GFXLIB_DrawBmFont4%, D%, arial12%, "Skill level", 100+12, Y%-160, &D0D0D0
         SYS GFXLIB_GetBmFontStrWidth%, arial16%, STR$player{(0)}.baseSkill& TO Z%
         SYS GFXLIB_DrawBmFont4%, D%, arial16%, STR$player{(0)}.baseSkill&, 100 + (140 - Z%)DIV2, Y%-197, &EEEE00
         IF player{(0)}.autoImprove% THEN
           SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.tickBoxTicked%, 32, 32, 64, Y%-250, 12
         ELSE
           SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.tickBox%, 32, 32, 64, Y%-250, 12
         ENDIF
         SYS
GFXLIB_DrawBmFont4%, D%, arial12%, "Auto-improve", 108, Y%-246, &EEEE00
       ENDIF

       IF
player{(1)}.type& = 1 THEN
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.triButtonLeft%, 32, 32, WinW%-W%-64, Y%-200, 7
         SYS GFXLIB_RectangleSolid%, D%, WinW%-W%-28, Y%-200, 140, 32, &000040
         SYS GFXLIB_RectangleSolid%, D%, WinW%-W%-26, Y%-200+2, player{(1)}.baseSkill&/100 * (140-4), 32-4, &2040A0
         SYS GFXLIB_PlotSetAlphaValue%, D%, bm.triButtonRight%, 32, 32, WinW%-W%+116, Y%-200, 8
         SYS GFXLIB_DrawBmFont4%, D%, arial12%, "Skill level", WinW%-W%-28+12, Y%-160, &D0D0D0
         SYS GFXLIB_GetBmFontStrWidth%, arial16%, STR$player{(1)}.baseSkill& TO Z%
         SYS GFXLIB_DrawBmFont4%, D%, arial16%, STR$player{(1)}.baseSkill&, WinW%-W%-28 + (140 - Z%)DIV2, Y%-197, &EEEE00
         IF player{(1)}.autoImprove% THEN
           SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.tickBoxTicked%, 32, 32, WinW%-W%-64, Y%-250, 13
         ELSE
           SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.tickBox%, 32, 32, WinW%-W%-64, Y%-250, 13
         ENDIF
         SYS
GFXLIB_DrawBmFont4%, D%, arial12%, "Auto-improve", WinW%-W%-22, Y%-246, &EEEE00
       ENDIF

       SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.startButton%, 160, 64, (WinW% - 160)DIV2, 16, 9

       SYS GFXLIB_GetBmFontStrWidth%, arial16%, "Winning score" TO Z%
       SYS GFXLIB_DrawBmFont4%, D%, arial16%, "Winning score", (WinW% - Z%)DIV2, Y%-128, &C0C0C0
       SYS GFXLIB_GetBmFontStrWidth%, arial32%, STR$WinningScore% TO Z%
       SYS GFXLIB_DrawBmFont4%, D%, arial32%, STR$WinningScore%, (WinW% - Z%)DIV2, Y%-184, &F0F0F0
       SYS GFXLIB_PlotSetAlphaValue%, D%, bm.triButtonLeft%, 32, 32, 310, Y%-176, 10
       SYS GFXLIB_PlotSetAlphaValue%, D%, bm.triButtonRight%, 32, 32, 466, Y%-176, 11

       SYS GFXLIB_DrawBmFont4%, D%, arial12%, "Banana particles", 310, Y%+10, &EEEE00
       IF BananaParticles% THEN
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.smallTickBoxTicked%, 20, 20, 310-26, Y%+10, 16
       ELSE
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.smallTickBox%, 20, 20, 310-26, Y%+10, 16
       ENDIF

       SYS
GFXLIB_DrawBmFont4%, D%, arial12%, "Explosion particles", 310, Y%-20, &EEEE00
       IF ExplosionParticles% THEN
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.smallTickBoxTicked%, 20, 20, 310-26, Y%-20, 17
       ELSE
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.smallTickBox%, 20, 20, 310-26, Y%-20, 17
       ENDIF

       SYS
GFXLIB_DrawBmFont4%, D%, arial12%, "Fires", 310, Y%-50, &EEEE00
       IF Fire% THEN
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.smallTickBoxTicked%, 20, 20, 310-26, Y%-50, 18
       ELSE
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.smallTickBox%, 20, 20, 310-26, Y%-50, 18
       ENDIF

       SYS
GFXLIB_DrawBmFont4%, D%, arial12%, "Show previous arrow", 310, Y%-80, &EEEE00
       IF ShowPreviousArrow% THEN
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.smallTickBoxTicked%, 20, 20, 310-26, Y%-80, 19
       ELSE
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.smallTickBox%, 20, 20, 310-26, Y%-80, 19
       ENDIF

       IF
FullScreen% THEN
         SYS
GFXLIB_PlotSetAlphaValue%, D%, bm.close%, 21, 21, WinW% - 24, WinH% - 24, 15
       ENDIF

       SYS
GFXLIB_PlotShapeBlend%, D%, bm.mousePtr%, 48, 48, msX%-27, msY%-27, 0, 90
       SYS GFXLIB_PlotAvg%, D%, bm.mousePtr%, 48, 48, msX%-24, msY%-24

       IF hwnd% = @hwnd% AND msBtn% = 4 AND delay% = 0 AND debounce% = FALSE THEN

         SYS
GFXLIB_ReadAlphaValue%, D%, msX%, msY% TO alpha%

         IF alpha% = 0 THEN
           
debounce% = TRUE
           
click% = FALSE
         ENDIF

         
REM  Select Player 1 as Human
         
IF alpha% = 1 THEN
           
player{( 0 )}.type& = 0
           debounce% = TRUE
           
click% = TRUE
         ENDIF

         
REM  Select Player 1 as Computer
         
IF alpha% = 2 THEN
           
player{( 0 )}.type& = 1
           debounce% = TRUE
           
click% = TRUE
         ENDIF

         
REM  Select Player 2 as Human
         
IF alpha% = 3 THEN
           
player{( 1 )}.type& = 0
           debounce% = TRUE
           
click% = TRUE
         ENDIF

         
REM  Select Player 2 as Computer
         
IF alpha% = 4 THEN
           
player{( 1 )}.type& = 1
           debounce% = TRUE
           
click% = TRUE
         ENDIF

         
REM  Player 1 (computer) skill level (left button)
         
IF alpha% = 5 THEN
           IF
player{( 0 )}.baseSkill& > 0 THEN
             
player{( 0 )}.baseSkill& -= 1
             delay% = 5
           ENDIF
         ENDIF

         
REM  Player 1 (computer) skill level (right button)
         
IF alpha% = 6 THEN
           IF
player{( 0 )}.baseSkill& < 100 THEN
             
player{( 0 )}.baseSkill& += 1
             delay% = 5
           ENDIF
         ENDIF

         
REM  Player 2 (computer) skill level (left button)
         
IF alpha% = 7 THEN
           IF
player{( 1 )}.baseSkill& > 0 THEN
             
player{( 1 )}.baseSkill& -= 1
             delay% = 5
           ENDIF
         ENDIF

         
REM  Player 2 (computer) skill level (right button)
         
IF alpha% = 8 THEN
           IF
player{( 1 )}.baseSkill& < 100 THEN
             
player{( 1 )}.baseSkill& += 1
             delay% = 5
           ENDIF
         ENDIF

         
REM. Adjust winning score (left button)
         
IF alpha% = 10 THEN
           IF
WinningScore% > 1 THEN
             
WinningScore% -= 1
             delay% = 10
           ENDIF
         ENDIF

         
REM. Adjust winning score (right button)
         
IF alpha% = 11 THEN
           IF
WinningScore% < 100 THEN
             
WinningScore% += 1
             delay% = 10
           ENDIF
         ENDIF

         
REM. Select/deselect player 1 (computer) auto-improve option
         
IF alpha% = 12 THEN
           
player{(0)}.autoImprove% = NOT player{(0)}.autoImprove%
           debounce% = TRUE
           
click% = TRUE
         ENDIF

         
REM. Select/deselect player 2 (computer) auto-improve option
         
IF alpha% = 13 THEN
           
player{(1)}.autoImprove% = NOT player{(1)}.autoImprove%
           debounce% = TRUE
           
click% = TRUE
         ENDIF

         
REM. Toggle "banana particles" flag
         
IF alpha% = 16 THEN
           
BananaParticles% = NOT BananaParticles%
           debounce% = TRUE
           
click% = TRUE
         ENDIF

         
REM. Toggle "explosion particles" flag
         
IF alpha% = 17 THEN
           
ExplosionParticles% = NOT ExplosionParticles%
           debounce% = TRUE
           
click% = TRUE
         ENDIF

         
REM. Toggle "Fire" flag
         
IF alpha% = 18 THEN
           
Fire% = NOT Fire%
           debounce% = TRUE
           
click% = TRUE
         ENDIF

         
REM. Toggle "PreviousArrow" flag
         
IF alpha% = 19 THEN
           
ShowPreviousArrow% = NOT ShowPreviousArrow%
           debounce% = TRUE
           
click% = TRUE
         ENDIF


         IF
FullScreen% THEN
           IF
alpha% = 15 THEN
             
close% = TRUE
             
click% = TRUE
           ENDIF
         ENDIF

       ENDIF

       IF
debounce% = TRUE AND msBtn% = 0 THEN
         
debounce% = FALSE
         
click% = FALSE
       ENDIF

       IF
delay% > 0 THEN
         
delay% -= 1
       ENDIF

       IF
click% THEN
         SYS
PlaySound%, wav.click%, 0, 5
         click% = FALSE
       ENDIF

       IF NOT
FullScreen% THEN
         PROC
display
       ELSE
         SYS
GetForegroundWindow% TO hwnd%
         IF hwnd% = @hwnd% THEN
           PROC
_GFXD3D9LIB_End_GFX_Drawing
           PROC_GFXD3D9LIB_Render_GFX_to_Fullscreen
           SYS Sleep%, 1
         ELSE
           PROC
_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
         
ENDIF
       ENDIF

     UNTIL
close% = TRUE OR alpha% = 9

     IF close% THEN
       PROC
_GFXD3D9LIB_CleanUp( FALSE )
       VDU 7
       SOUND 1, 0, 0, 0
       SOUND OFF
       QUIT
     ENDIF

     SYS
PlaySound%, wav.click%, 0, 5

     ENDPROC


     
DEF FN_getDisplayModeChoiceFromUser

     LOCAL M%, H%, V%

     COLOUR 11 : PRINT'"  Please select:";
     COLOUR 3  : PRINT "  Windowed or full-screen display? (W/F) ";
     COLOUR 7

     V% = FALSE : REM  Valid keypress flag (the REPEAT...UNTIL loop will loop until V% is TRUE)
     
M% = 0     : REM  M% = 0 for 'Windowed' mode, or 1 for 'Full-screen' display

     
REPEAT
       SYS
"GetForegroundWindow" TO H% :  REM  Make sure our program window's got input focus
       
IF H% = @hwnd% THEN
         
REM  Test 'W' key
         
IF INKEY-34 THEN
           PRINT
"Windowed";
           V% = TRUE
         ENDIF

         
REM  Test 'F' key
         
IF INKEY-68 THEN
           PRINT
"Full-screen";
           V% = TRUE
           
M% = 1
         ENDIF
       ENDIF
     UNTIL
V% OR INKEY(1)=0

     SOUND 1, -10, 200, 1
     WAIT 50

     = M%


     DEF PROC_getHumanAction

     LOCAL x0#, y0#, x1#, y1#, len#, ndx#, ndy#, xv#, yv#

     IF hwnd% <> @hwnd% THEN ENDPROC
     IF
player{( player% )}.delay& > 0 THEN ENDPROC

     PROC
_getHumanPlayerBananaVelocity( player%, msX%, msY%, len#, ndx#, ndy# )

     IF len# > 200 THEN len# = 200

     xv# = 0.1 * len# * ndx#
     yv# = 0.1 * len# * ndy#

     x0# = 1.0 * player{( player% )}.xPos%
     y0# = 1.0 * player{( player% )}.yPos%

     x1# = x0# + len#*ndx#
     y1# = y0# + len#*ndy#

     IF ShowPreviousArrow% THEN
       IF
player{( player% )}.oldArrow% THEN
         PROC
_drawArrowCopy( player% )
       ENDIF
     ENDIF

     PROC
_drawArrow( x0#, y0#, x1#, y1#, 0.5, &40FF40 )

     IF msBtn% = 4 AND msX% >= 0 AND msX% < WinW% AND msY% >= 0 AND msY% < WinH% THEN

       
player{( player% )}.oldArrow% = TRUE

       PROC
_copyCurrentArrow( player% )

       PROC_initBanana( player%, xv#, yv# )

       player{( player% )}.firstGo& = 0
       player{( player% )}.throw& = 20
       player{( player% EOR 1 )}.throw& = 0

       SYS PlaySound%, wav.throw%( RND(6)-1 ), 0, 5

     ENDIF

     ENDPROC


     
DEF PROC_getComputerAction

     LOCAL ux#, uy#, k#, x_err#, y_err#, skill#

     IF player{( player% )}.thinking& > 0 THEN
       
player{( player% )}.thinking& -= 1
       ENDPROC
     ENDIF

     PROC
_getCorrectInitialVelocity( ux#, uy# )

     REM  Let's try not to obliterate the human player on the computer's first go!
     
IF player{( player% )}.firstGo& = 1 THEN
       
k# = 1.8
     ELSE
       
k# = 0.75
     ENDIF

     
REM  ux# and uy# are the 'perfect' initial velocity components.
     REM  Let's add an error to them proportional to the computer's skill level.
     
skill# = 1.0 - player{( player% )}.skill& / 100
     x_err# = FN_rndSgn * RND(1) * k# * skill#
     y_err# = FN_rndSgn * RND(1) * k# * skill#

     ux# += x_err#
     uy# += y_err#

     IF player% = 0 AND SGN(ux#) = -1 THEN ux# *= -1
     IF player% = 1 AND SGN(ux#) = 1 THEN ux# *= -1

     PROC_initBanana( player%, ux#, uy# )

     IF player{( player% )}.autoImprove% THEN
       
player{( player% )}.skill& += RND(5)
       IF  player{( player% )}.skill& > 100 THEN
         
player{( player% )}.skill& = 100
       ENDIF
     ENDIF

     
player{( player% )}.firstGo&     = 0
     player{( player% )}.throw&       = 20
     player{( player% EOR 1 )}.throw& = 0
     player{( player% )}.thinking&    = 100 + RND(100)

     SYS PlaySound%, wav.throw%( RND(6)-1 ), 0, 5

     ENDPROC



     
DEF PROC_playerWins
     LOCAL C%, I%, X%, Y%, W%, H%, S%, T%, U%, S$, P$
     IF NOT FullScreen% THEN SYS SetWindowText%, @hwnd%, ProgTitle$
     SYS GFXLIB_DWORDCopy%, dispVars.bmBuffAddr%, bm.temp%, WinW%*WinH%
     IF celebrator% = 0 THEN P$ = "ONE" ELSE P$ = "TWO"
     S$ = "PLAYER " + P$ + " WINS"
     SYS GFXLIB_GetBmFontStrWidth%, arial32%, S$ TO S%
     SYS GFXLIB_GetBmFontStrWidth%, arial32%, "WELL DONE !" TO U%
     FOR W% = WinW% TO 1 STEP -8

       IF FullScreen% THEN
         SYS
GetForegroundWindow% TO hwnd%
         IF hwnd% <> @hwnd% THEN
           PROC
_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
         
ENDIF
         PROC
_GFXD3D9LIB_Start_GFX_Drawing
       ENDIF

       
REM SYS GFXLIB_Clr%, dispVars{}, 0
       
H% = WinH%/WinW% * W%
       X% = (WinW% - W%) / 2
       Y% = (WinH% - H%) / 2
       T% = TIME
       
C% = FNrgb( 128+127*SIN(T%/19), 128+127*COS(T%/25), 128+127*SIN(COS(T%/20)) )
       SYS GFXLIB_BPlotScale%, dispVars{}, bm.temp%, WinW%, WinH%, W%, H%, X%, Y%
       SYS GFXLIB_MMXSubtract64%, dispVars{}, dispVars.bmBuffAddr%, 4*(WinW%*WinH% DIV 64)-1, 255*(1.0 - W%/WinW%)
       SYS GFXLIB_DrawBmFont4%, dispVars{}, arial32%, S$, (WinW% - S%)/2, WinH%/2 + 30, C%
       IF player{( celebrator% )}.type& = 0 THEN
         SYS
GFXLIB_DrawBmFont4%, dispVars{}, arial32%, "WELL DONE !", (WinW% - U%)/2, WinH%/2 - 70, &FF00
       ENDIF

       IF NOT
FullScreen% THEN
         PROC
display
       ELSE
         SYS
GetForegroundWindow% TO hwnd%
         IF hwnd% = @hwnd% THEN
           PROC
_GFXD3D9LIB_End_GFX_Drawing
           PROC_GFXD3D9LIB_Render_GFX_to_Fullscreen
           SYS Sleep%, 1
         ELSE
           PROC
_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
         
ENDIF
       ENDIF
     NEXT
W%

     FOR I% = 1 TO 100
       IF FullScreen% THEN
         SYS
GetForegroundWindow% TO hwnd%
         IF hwnd% <> @hwnd% THEN
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
           
PROC_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
         ENDIF
         PROC
_GFXD3D9LIB_Start_GFX_Drawing
       ENDIF
       
T% = TIME
       
C% = FNrgb( 128+127*SIN(T%/29), 128+127*COS(T%/25), 128+127*SIN(COS(T%/30)) )
       SYS GFXLIB_DrawBmFont4%, dispVars{}, arial32%, S$, (WinW% - S%)/2, WinH%/2 + 30, C%
       IF player{( celebrator% )}.type& = 0 THEN
         SYS
GFXLIB_DrawBmFont4%, dispVars{}, arial32%, "WELL DONE !", (WinW% - U%)/2, WinH%/2 - 70, &FF00
       ENDIF
       IF NOT
FullScreen% THEN
         PROC
display
       ELSE
         SYS
GetForegroundWindow% TO hwnd%
         IF hwnd% = @hwnd% THEN
           PROC
_GFXD3D9LIB_End_GFX_Drawing
           PROC_GFXD3D9LIB_Render_GFX_to_Fullscreen
           SYS Sleep%, 1
         ELSE
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
           
PROC_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
         ENDIF
       ENDIF
     NEXT
I%

     FOR  I% = 1 TO 63
       IF FullScreen% THEN
         SYS
GetForegroundWindow% TO hwnd%
         IF hwnd% <> @hwnd% THEN
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
           
PROC_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
         ENDIF
         PROC
_GFXD3D9LIB_Start_GFX_Drawing
       ENDIF
       
T% = TIME
       
C% = FNrgb( 128+127*SIN(T%/29), 128+127*COS(T%/25), 128+127*SIN(COS(T%/30)) )
       SYS GFXLIB_DrawBmFont4%, dispVars{}, arial32%, S$, (WinW% - S%)/2, WinH%/2 + 30, C%
       IF player{( celebrator% )}.type& = 0 THEN
         SYS
GFXLIB_DrawBmFont4%, dispVars{}, arial32%, "WELL DONE !", (WinW% - U%)/2, WinH%/2 - 70, &FF00
       ENDIF
       SYS
GFXLIB_MMXSubtract64%, dispVars{}, dispVars.bmBuffAddr%, 4*(WinW%*WinH% DIV 64)-1, 4*I%
       IF NOT FullScreen% THEN
         PROC
display
       ELSE
         SYS
GetForegroundWindow% TO hwnd%
         IF hwnd% = @hwnd% THEN
           PROC
_GFXD3D9LIB_End_GFX_Drawing
           PROC_GFXD3D9LIB_Render_GFX_to_Fullscreen
           SYS Sleep%, 1
         ELSE
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
           
PROC_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
         ENDIF
       ENDIF
     NEXT
I%
     ENDPROC


     
DEF FN_exitGame
     LOCAL exit%, btnW%, btnH%, yesBtn%, noBtn%, yesStrW%, noStrW%, exitStrW%
     LOCAL msX%, msY%, msBtn%, selected%, alpha%, hwnd%

     exit% = FALSE

     
btnW% = 128
     btnH% = 64

     SYS GFXLIB_GetBmFontStrWidth%, arial32%, "YES" TO yesStrW%
     SYS GFXLIB_GetBmFontStrWidth%, arial32%, "NO" TO noStrW%
     SYS GFXLIB_GetBmFontStrWidth%, arial32%, "Exit?" TO exitStrW%

     SYS "GlobalAlloc", 64, 4*btnW%*btnH% TO yesBtn%
     SYS "GlobalAlloc", 64, 4*btnW%*btnH% TO noBtn%

     SYS GFXLIB_ClrX%, dispVars{}, yesBtn%, btnW%, btnH%, FNrgb( 0, 128, 0 )
     SYS GFXLIB_ClrX%, dispVars{}, noBtn%, btnW%, btnH%, FNrgb( 128, 0, 0 )

     SYS GFXLIB_SaveAndSetDispVars%, dispVars{}, yesBtn%, btnW%, btnH%
     SYS GFXLIB_DrawBmFont4%, dispVars{}, arial32%, "YES", (btnW% - yesStrW%)/2, 8, &C0C0C0

     SYS GFXLIB_SetDispVars2%, dispVars{}, noBtn%, btnW%, btnH%
     SYS GFXLIB_DrawBmFont4%, dispVars{}, arial32%, "NO", (btnW% - noStrW%)/2, 8, &C0C0C0

     SYS GFXLIB_RestoreDispVars%, dispVars{}

     SYS GFXLIB_BPlotGreyscale%, dispVars{}, dispVars.bmBuffAddr%, WinW%, WinH%, 0, 0
     SYS GFXLIB_BoxBlurNxNR%, dispVars.bmBuffAddr%, WinW%, WinH%, 9

     SYS GFXLIB_DrawBmFont4%, dispVars{}, arial32%, "Exit?", (WinW% - exitStrW%)/2-2, WinH%DIV2 + 32-2, &0A0A0A
     SYS GFXLIB_DrawBmFont4%, dispVars{}, arial32%, "Exit?", (WinW% - exitStrW%)/2, WinH%DIV2 + 32, &FFA000

     SYS GFXLIB_PlotSetAlphaValue%, dispVars{}, noBtn%, btnW%, btnH%, (WinW% DIV 2) - btnW%-64, WinH%DIV2 - 64, 1
     SYS GFXLIB_PlotSetAlphaValue%, dispVars{}, yesBtn%, btnW%, btnH%, (WinW% DIV 2) + 64, WinH%DIV2 - 64, 2

     SYS GFXLIB_DWORDCopy%, dispVars.bmBuffAddr%, bm.temp%, WinW%*WinH%

     SYS "GlobalFree", yesBtn%
     SYS "GlobalFree", noBtn%

     selected% = FALSE

     REPEAT

       SYS
GetForegroundWindow% TO hwnd%

       IF FullScreen% THEN
         IF
hwnd% <> @hwnd% THEN
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
           
PROC_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
         ENDIF
         PROC
_GFXD3D9LIB_Start_GFX_Drawing
       ENDIF

       SYS
GFXLIB_BPlot%, D%, bm.temp%, WinW%, WinH%, 0, 0

       PROC_readMouse( msX%, msY%, msBtn% )

       SYS GFXLIB_PlotShapeBlend%, D%, bm.mousePtr%, 48, 48, msX%-27, msY%-27, 0, 90
       SYS GFXLIB_PlotAvg%, D%, bm.mousePtr%, 48, 48, msX%-24, msY%-24

       IF hwnd% = @hwnd% THEN

         SYS
GFXLIB_ReadAlphaValue%, dispVars{}, msX%, msY% TO alpha%

         IF (msBtn% = 4 AND alpha% = 1) OR INKEY-86 THEN
           
exit% = FALSE
           
selected% = TRUE
         ENDIF

         IF
(msBtn% = 4 AND alpha% = 2) OR INKEY-69 THEN
           
exit% = TRUE
           
selected% = TRUE
         ENDIF

       ENDIF


       IF NOT
FullScreen% THEN
         PROC
display
         *REFRESH ON
       ELSE
         SYS
GetForegroundWindow% TO hwnd%
         IF hwnd% = @hwnd% THEN
           PROC
_GFXD3D9LIB_End_GFX_Drawing
           PROC_GFXD3D9LIB_Render_GFX_to_Fullscreen
           SYS Sleep%, 1
         ELSE
           
REM PROC_GFXD3D9LIB_LOSTDEVICE
           
PROC_GFXD3D9LIB_CleanUp(FALSE) : VDU 7 : QUIT
         ENDIF
       ENDIF

     UNTIL
selected% OR INKEY(1)=0

     SYS PlaySound%, wav.click%, 0, 5

     IF NOT FullScreen% THEN
       
*REFRESH OFF
     ENDIF

     MOUSE OFF

     
= exit%


     DEF PROC_createNewFireBlobs( N%, x0%, y0% )
     LOCAL I%, M%, P%
     PRIVATE fireBlobPtr%

     P% = fireBlobPtr%
     M% = MaxNumFireBlobs%

     FOR I% = 0 TO N%-1
       fireBlob{(P%)}.active% = TRUE
       
fireBlob{(P%)}.life%   = RND(&64)
       fireBlob{(P%)}.x0%     = x0% + &5*(RND(&1)-RND(&1))
       fireBlob{(P%)}.y0%     = y0% + &5*(RND(&1)-RND(&1))
       fireBlob{(P%)}.x#      = x0%
       fireBlob{(P%)}.y#      = y0%
       fireBlob{(P%)}.xv#     = 0.5*(RND(&1)-RND(&1))
       fireBlob{(P%)}.yv#     = 0.005*RND(&1)
       fireBlob{(P%)}.acc#    = 0.01*RND(&1)
       P% -= TRUE
       IF
P% = M% THEN
         
P% = FALSE
       ENDIF
     NEXT
     
fireBlobPtr% = P%
     ENDPROC


     
DEF PROC_createNewScene
     LOCAL I%

     REM  Re-seed the random number generator
     
SYS "GetTickCount" TO I%
     I% = RND( -I% )

     PROC_drawSkyBackground( bm.sky%, WinW%, WinH% )
     PROC_drawCity( bm.city%, WinW%, WinH% )
     PROC_drawMoon( bm.sky%, WinW%, WinH%, RND(WinW%), RND(WinH%), WinH% )
     PROC_updateBackground

     REM  Choose the buildings upon which the gorillas stand
     
building1% = RND(4) - 1
     building2% = (buildings.numBuildings% - 5) + RND(3)

     player{(0)}.xPos% = buildings.x%( building1% ) + buildings.width%( building1% )/2
     player{(0)}.yPos% = buildings.height%( building1% ) + 14

     player{(1)}.xPos% = buildings.x%( building2% ) + buildings.width%( building2% )/2
     player{(1)}.yPos% = buildings.height%( building2% ) + 14

     FOR I% = 0 TO 1
       player{( I% )}.alive%    = TRUE
       
player{( I% )}.throw&    = 0
       player{( I% )}.oldArrow% = FALSE
       
player{( I% )}.thinking& = 100 + RND(100)
       player{( I% )}.delay&    = 60
       player{( I% )}.firstGo&  = 1
       player{( I% )}.skill&    = player{( 0 )}.baseSkill&
     NEXT

     
REM  De-activate all the explosion particles
     
FOR I% = 0 TO MaxNumExplosionParticles%-1
       explosionParticle{( I% )}.active% = FALSE
       
explosionParticle{( I% )}.life%   = 0
       explosionParticle{( I% )}.x#      = -4.0
       explosionParticle{( I% )}.y#      = -4.0
       explosionParticle{( I% )}.colour% = &FFFFFF
       explosionParticle{( I% )}.xv#     = 0.0
       explosionParticle{( I% )}.yv#     = 0.0
     NEXT

     
REM  De-activate all the banana particles
     
FOR I% = 0 TO MaxNumBananaParticles%-1
       bananaParticle{( I% )}.active%    = FALSE
       
bananaParticle{( I% )}.life%      = 0
       bananaParticle{( I% )}.x#         = -4.0
       bananaParticle{( I% )}.y#         = -4.0
       bananaParticle{( I% )}.xv#        = 0.0
       bananaParticle{( I% )}.yv#        = 0.0
       bananaParticle{( I% )}.yacc#      = 0.0
     NEXT

     
REM  De-activate all the fire blobs
     
FOR I% = 0 TO MaxNumFireBlobs%-1
       fireBlob{(I%)}.active% = FALSE
     NEXT

     
REM  De-activate the explosion
     
explosion.active% = FALSE
     
explosion.size#   = 32.0
     explosion.inc#    = 0.0
     explosion.x%      = WinW% DIV 2
     explosion.y%      = WinH% DIV 2

     REM  Determine which player has the largest Y co-ordinate
     
playerHighestY% = 0
     IF player{(0)}.yPos% > player{(1)}.yPos% THEN
       
playerHighestY% = player{(0)}.yPos%
     ELSE
       
playerHighestY% = player{(1)}.yPos%
     ENDIF

     
REM  Determine which building is the tallest
     
maxY% = 0
     IF buildings.maxHeight% > playerHighestY% THEN
       
maxY% = buildings.maxHeight%
     ELSE
       
maxY% = playerHighestY%
     ENDIF

     
REM  Define the start and end points of the parabola (quadratic curve)
     REM  The X co-ordinate of the midpoint is known
     
quadratic.x1% = player{(0)}.xPos%
     quadratic.y1% = player{(0)}.yPos%
     quadratic.x3% = player{(1)}.xPos%
     quadratic.y3% = player{(1)}.yPos%

     quadratic.x2% = (quadratic.x1% + quadratic.x3%) DIV 2

     REM  Determine the Y co-ordinate of the parabola's midpoint, and calculate
     REM  the coefficients (a, b, c) of the quadratic curve  Y = aX^2 + bX + c
     REM  We need to find the parabolic curve which avoids intersecting the buildings.
     REM  There is, of course, a much better - and faster - way of doing this, but it
     REM  can wait until a possibly later version of this program.
     
SYS GFXLIB_SaveAndSetDispVars%, dispVars{}, bm.bg%, WinW%, WinH%

     FOR Y% = maxY% TO WinH%+2000 STEP 8

       quadratic.y2% = Y%

       PROC_getQuadraticCoeffs( quadratic.x1%, quadratic.y1%, quadratic.x2%, quadratic.y2%, \
       
\ quadratic.x3%, quadratic.y3%, quadratic.a#, quadratic.b#, quadratic.c# )

       FOR X% = quadratic.x1% TO quadratic.x3% STEP 4
         y% = quadratic.a#*X%^2 + quadratic.b#*X% + quadratic.c#
         SYS GFXLIB_TestPixelAlphaBit%, dispVars{}, X%, y%, 0 TO R%
         IF R% <> 0 THEN
           
X% = quadratic.x3%
         ENDIF
       NEXT

       IF
R% = 0 THEN
         
quadratic.y2% += 48 + RND(200)
         Y% = WinH%+2000 : REM  EXIT FOR
       
ENDIF

     NEXT

     SYS
GFXLIB_RestoreDispVars%, dispVars{}
     ENDPROC


     
REM  explosionParticle{( MaxNumExplosionParticles% ) active%, life%, xv#, yv#, x#, y#, colour%, padding%(3)}
     REM                                                  0        4      8    16   24  32  40
     REM                                                  &0       &4     &8   &10  &18 &20 &28
     
DEF PROC_createParticleExplosion( N%, X%, Y% )
     LOCAL I%, B%, P%, A%, S%, M%, Z%, x#, y#
     PRIVATE explosionParticleIndex%

     Z% = MaxNumExplosionParticles%
     P% = explosionParticleIndex%
     B% = ^explosionParticle{( 0 )}.active%
     A% = ^explosionParticle{( P% )}.active%
     S% = DIM(explosionParticle{(0)})
     M% = ParticleColourListSize%
     x# = 1.0 * X%
     y# = 1.0 * Y%

     REM  This needs to be quite fast, hence the recourse to indirection operators.
     REM  Perhaps using ordinary array/structure access is faster after all -- haven't tested it.
     
FOR I% = 0 TO N%-1
       !A% = TRUE
       
A%!&4 = RND(&12C)
       |(A%+&8) = &4*(RND(&1) - RND(&1))
       |(A%+&10) = &6*(RND(&1) - RND(&1))
       |(A%+&18) = x#
       |(A%+&20) = y#
       A%!&28 = particleColourList%( RND(M%)+TRUE )
       A% += S%
       P% -= TRUE
       IF
P% = Z% THEN
         
P% = 0
         A% = B%
       ENDIF
     NEXT

     
explosionParticleIndex% = P%
     ENDPROC


     
DEF PROC_createBananaParticles( N% )
     LOCAL I%, M%, P%, r#, x#, y#
     PRIVATE bananaParticleIndex%

     P% = bananaParticleIndex%
     M% = MaxNumBananaParticles%

     IF banana.y# < 2*WinH% THEN
       
x# = banana.x#
       y# = banana.y#
       FOR I% = 1 TO N%
         r#                             = banana.v# * RND(&1)
         bananaParticle{( P% )}.active% = TRUE
         
bananaParticle{( P% )}.life%   = &C8 + RND(&64)
         bananaParticle{( P% )}.x#      = x# - r#*banana.nxv# + &4*(RND(&1) - RND(&1))
         bananaParticle{( P% )}.y#      = y# - r#*banana.nyv# + &4*(RND(&1) - RND(&1))
         bananaParticle{( P% )}.xv#     = banana.xv# * 0.025 * RND(&1)
         bananaParticle{( P% )}.yv#     = banana.yv# * 0.01 * RND(&1)
         bananaParticle{( P% )}.yacc#   = -0.01 * RND(&1)
         bananaParticle{( P% )}.colour% = &FFFF00
         P% -= TRUE
         IF
P% = M% THEN
           
P% = FALSE
         ENDIF
       NEXT
       
bananaParticleIndex% = P%
     ENDIF
     ENDPROC


     
REM  This subroutine merges the city bitmap (bm.city%) with the sky bitmap (bm.sky%)
     REM  The resulting combined bitmap is written to the background bitmap (bm.bg%)
     
DEF PROC_updateBackground
     REM  First clear the background bitmap (bm.bg%) -- although this is not strictly necessary!
     
SYS GFXLIB_ClrX%, dispVars{}, bm.bg%, WinW%, WinH%, 0
     REM  Re-direct GFXLIB_BPlot and GFXLIB_Plot's output to our background bitmap
     
SYS GFXLIB_SaveAndSetDispVars%, dispVars{}, bm.bg%, WinW%, WinH%
     SYS GFXLIB_BPlot%, dispVars{}, bm.sky%, WinW%, WinH%, 0, 0
     SYS GFXLIB_Plot%, dispVars{}, bm.city%, WinW%, WinH%, 0, 0
     SYS GFXLIB_RestoreDispVars%, dispVars{}
     ENDPROC


     
DEF PROC_getHumanPlayerBananaVelocity( player%, msX%, msY%, RETURN len#, RETURN ndx#, RETURN ndy# )
     LOCAL x0%, y0%, dx%, dy%

     x0% = player{( player% )}.xPos%
     y0% = player{( player% )}.yPos%

     IF msY% < y0% THEN msY% = y0%

     IF player% = 0 THEN
       IF
msX% < x0%+2 THEN msX% = x0%+2
     ELSE
       IF
msX% > x0%-2 THEN msX% = x0%-2
     ENDIF

     
len# = SQR((msX% - x0%)^2 + (msY% - y0%)^2)

     IF len# < 1 THEN len# = 1

     dx% = msX% - x0%
     dy% = msY% - y0%

     ndx# = dx% / len#
     ndy# = dy% / len#

     ENDPROC


     
REM  Set the initial velocity of the banana, and compute some other possibly useful values
     
DEF PROC_initBanana( player%, xVel#, yVel# )
     banana.x#        = player{( player% )}.xPos%
     banana.y#        = player{( player% )}.yPos%
     banana.xv#       = xVel#
     banana.yv#       = yVel#
     banana.v#        = SQR( xVel#^2 + yVel#^2 )
     banana.nxv#      = xVel# / banana.v#
     banana.nyv#      = yVel# / banana.v#
     banana.angle#    = 0.0
     banana.angleInc# = 5 + banana.v#
     banana.thrown%   = TRUE
     
banana.t%        = 0
     IF player% = 1 THEN banana.angleInc# *= -1
     ENDPROC


     
REM  Update the banana's position and angle of rotation
     
DEF PROC_updateBanana
     banana.x# += banana.xv#
     banana.y# += banana.yv#
     banana.yv# += g#
     banana.angle# += banana.angleInc#
     IF ABSbanana.angle# >= 360 THEN
       
banana.angle# -= 360 * SGNbanana.angleInc#
     ENDIF
     IF
banana.x# < -64 OR banana.x# >= WinW%+64 OR banana.y# < 0 THEN
       
player{( player% )}.delay& = 60
       player% = player% EOR 1
       banana.thrown% = FALSE
       SYS
PlaySound%, wav.out%, 0, 5
     ENDIF
     ENDPROC


     
DEF PROC_handleBananaBuildingCollision
     banana.thrown% = FALSE
     
player{( player% )}.delay& = 60
     player% = player% EOR 1
     PROC_makeHoleInBuilding( bm.city%, WinW%, WinH%, banana.x#, banana.y#, 10 )
     PROC_updateBackground
     IF Fire% THEN
       PROC
_createNewFireBlobs( 5+RND(10), banana.x#-16, banana.y#-24 )
     ENDIF
     IF
ExplosionParticles% THEN
       PROC
_createParticleExplosion( RND(30), banana.x#, banana.y#-16)
     ENDIF
     SYS
PlaySound%, wav.miss%, 0, 5
     ENDPROC


     
DEF PROC_handleBananaHumanCollision( P% )

     REM  Where P% (0 or 1) is the player with which the banana has collided

     
banana.thrown% = FALSE                     : REM  Disable the banana

     
player{( P% )}.alive% = FALSE              : REM  Kill player P%
     
player{( P% EOR 1 )}.delay& = 60           : REM  Set "throw delay" for other computer player (delay& doesn't apply to human player)
     
player{( P% EOR 1 )}.score& += 1           : REM  Other player gets a point

     
explosion.active% = TRUE                   : REM  Set the explosion flag
     
explosion.x% = player{( P% )}.xPos%        : REM  Define explosion coordinates
     
explosion.y% = player{( P% )}.yPos% - 16

     celebrator% = P% EOR 1                     : REM  The other player can now start celebrating!
     
celebration% = TRUE
     
celebrationCounter% = celebrationCounterStartValue%

     PROC_makeHoleInBuilding( bm.city%, WinW%, WinH%, player{( P% )}.xPos%, player{( P% )}.yPos%-14, 25 )

     IF Fire% THEN
       PROC
_createNewFireBlobs( 30+RND(30), player{( P% )}.xPos%-14, player{( P% )}.yPos%-55 )
     ENDIF

     IF
ExplosionParticles% THEN
       PROC
_createParticleExplosion( 200, player{( P% )}.xPos%, player{( P% )}.yPos%-16 )
     ENDIF

     
player% = player% EOR 1

     PROC_updateBackground

     SYS PlaySound%, wav.kill%, 0, 5
     ENDPROC


     
DEF PROC_makeHoleInBuilding( B%, W%, H%, X%, Y%, M% )
     LOCAL I%, A%, R%
     SYS GFXLIB_SaveDispVars%, dispVars{}
     SYS GFXLIB_SetDispVars2%, dispVars{}, B%, W%, H%
     FOR I% = 0 TO 4
       A% = RND(360) - 1
       R% = RND(10)
       SYS GFXLIB_DrawFilledCircle%, dispVars{}, X%+R%*SINRADA%, Y%+R%*COSRADA%, 5+RND(M%), 0
     NEXT
     SYS
GFXLIB_RestoreDispVars%, dispVars{}
     ENDPROC


     
REMSYS GFXLIB_PlotRotateScale2%, dispVars{}, bmAddr%, bmW%, bmH%, xPos%, yPos%, angle*&100000, scale*&100000
     
DEF FN_drawBanana
     LOCAL B%, R%
     B% = bm.banana20x20%
     SYS GFXLIB_ClrX%, D%, B%, 20, 20, 0
     SYS GFXLIB_SaveAndSetDispVars%, D%, B%, 20, 20
     SYS GFXLIB_PlotRotateScale2%, D%, bm.banana%, 128, 128, 10, 10, banana.angle#*&10000, 0.2*&10000
     SYS GFXLIB_RestoreDispVars%, D%
     SYS GFXLIB_ShapeGetCumulativeAlphaBits%, D%, B%, 20, 20, banana.x#-10, banana.y#-10, 0 TO R%
     SYS GFXLIB_PlotShapeBlend%, D%, B%, 20, 20, banana.x#-10 + 4, banana.y#-10 - 4, 0, 80
     SYS GFXLIB_Plot%, D%, B%, 20, 20, banana.x#-10, banana.y#-10
     = R%


     DEF PROC_drawFires
     LOCAL B%, I%, P%

     B% = bm.fireBlob%
     P% = GFXLIB_PlotAddARGBSaturate%

     FOR I% = 0 TO MaxNumFireBlobs%-1
       IF fireBlob{(I%)}.active% THEN
         SYS
P%, D%, B%, &20, &20, fireBlob{(I%)}.x#, fireBlob{(I%)}.y#
         fireBlob{(I%)}.x# += fireBlob{(I%)}.xv#
         fireBlob{(I%)}.y# += fireBlob{(I%)}.yv#
         fireBlob{(I%)}.yv# += fireBlob{(I%)}.acc#
         IF fireBlob{(I%)}.life% > FALSE THEN
           
fireBlob{(I%)}.life% += TRUE
         ELSE
           
fireBlob{(I%)}.x# = fireBlob{(I%)}.x0%
           fireBlob{(I%)}.y# = fireBlob{(I%)}.y0%
           fireBlob{(I%)}.xv# = FN_rndSgn * 0.075*RND(1)
           fireBlob{(I%)}.yv# = 0.01*RND(1)
           fireBlob{(I%)}.acc# = 0.01*RND(1)
           fireBlob{(I%)}.life% = RND(&64)
         ENDIF
       ENDIF
     NEXT
     ENDPROC


     
DEF PROC_drawExplosion
     SYS GFXLIB_PlotScaleBlend%, D%, bm.explosion%, 128, 128, explosion.size#, explosion.size#, \
     
\ explosion.x%-explosion.size#/2, explosion.y%-explosion.size#/2, 255-explosion.size#
     explosion.size# += explosion.inc#
     explosion.inc# += 2.0
     IF explosion.size# >= 256 THEN
       
explosion.active% = FALSE
     ENDIF
     ENDPROC


     
DEF PROC_drawExplosionParticles
     LOCAL A%, I%, S%

     A% = ^explosionParticle{(0)}.x#
     S% = DIM(explosionParticle{(0)})

     REM  Draw the particles each four times (but at different X,Y offsets)
     
SYS GFXLIB_PlotPixelList3%, dispVars{}, dispVars.bmBuffAddr%, WinW%, WinH%, A%, 5, S%, MaxNumExplosionParticles%, 0, 0
     SYS GFXLIB_PlotPixelList3%, dispVars{}, dispVars.bmBuffAddr%, WinW%, WinH%, A%, 5, S%, MaxNumExplosionParticles%, 1, 0
     SYS GFXLIB_PlotPixelList3%, dispVars{}, dispVars.bmBuffAddr%, WinW%, WinH%, A%, 5, S%, MaxNumExplosionParticles%, 0, 1
     SYS GFXLIB_PlotPixelList3%, dispVars{}, dispVars.bmBuffAddr%, WinW%, WinH%, A%, 5, S%, MaxNumExplosionParticles%, 1, 1

     FOR I% = 0 TO MaxNumExplosionParticles%-1
       IF explosionParticle{(I%)}.active% THEN
         
explosionParticle{(I%)}.x# += explosionParticle{(I%)}.xv#
         explosionParticle{(I%)}.y# += explosionParticle{(I%)}.yv#
         explosionParticle{(I%)}.yv# -= 0.035
         IF explosionParticle{(I%)}.life% > FALSE THEN
           
explosionParticle{(I%)}.life% += TRUE
         ELSE
           
explosionParticle{(I%)}.active% = FALSE
           
explosionParticle{(I%)}.x# = -4.0
           explosionParticle{(I%)}.y# = -4.0
         ENDIF
       ENDIF
     NEXT
     ENDPROC


     
DEF PROC_drawBananaParticles
     LOCAL A%, H%, I%, P%, S%, W%

     A% = ^bananaParticle{(0)}.x#
     W% = WinW%
     H% = WinH%
     S% = DIM(bananaParticle{(0)})

     SYS GFXLIB_PlotPixelList3%, D%, !D%, WinW%, WinH%, A%, 5, S%, MaxNumBananaParticles%, 0, 0

     FOR I% = 0 TO MaxNumBananaParticles%-1
       IF bananaParticle{(I%)}.active% THEN
         
bananaParticle{(I%)}.x# += bananaParticle{(I%)}.xv#
         bananaParticle{(I%)}.y# += bananaParticle{(I%)}.yv#
         bananaParticle{(I%)}.yv# += bananaParticle{(I%)}.yacc#
         IF bananaParticle{(I%)}.life% > FALSE THEN
           
bananaParticle{(I%)}.life% += TRUE
         ELSE
           
bananaParticle{(I%)}.active% = FALSE
           
bananaParticle{(I%)}.x# = TRUE
           
bananaParticle{(I%)}.y# = TRUE
         ENDIF
       ENDIF
     NEXT
     ENDPROC


     
REM  Draw a moon with random phase and orientation, and make its size and colour
     REM  depend on its altitude (small and bright when high up overhead, dull orange and big
     REM  when closer to the horizon)
     
DEF PROC_drawMoon( pBuffer%, buffW%, buffH%, X%, Y%, maxY% )

     LOCAL I%, bm%, bm2%, maxPts%
     LOCAL radius#, moonRelY#, scale#, inc#, theta#, dt#, a#, b#, c#
     LOCAL moonY%, colour%, x0%, y0%, N%
     LOCAL x#(), y#(), z#(), x2#(), y2#(), z2#()

     maxPts% = 1000

     DIM x#( maxPts%-1 ), y#( maxPts%-1 ), z#( maxPts%-1 )
     DIM x2#( maxPts%-1 ), y2#( maxPts%-1 ), z2#( maxPts%-1 )

     SYS "GlobalAlloc", 64, 4*(256*256 + 1) TO bm%
     SYS "GlobalAlloc", 64, 4*(256*256 + 1) TO bm2%

     SYS GFXLIB_ClrX%, dispVars{}, bm%, 256, 256, 0
     SYS GFXLIB_ClrX%, dispVars{}, bm2%, 256, 256, 0

     radius# = 110.5

     moonRelY# = Y% / maxY%

     scale# = 1.0 - moonRelY#

     moonY% = (moonRelY# * @vdu%!212) - scale#*(2 * radius#)

     colour% = FNrgb( 255*(1-scale#^3)+150, 255*(1-scale#^2)+64, 255*(1-scale#)+32 )

     x0% = 128
     y0% = 128

     inc# = 1.0

     dt# = ACS(1.0 - 0.5*(inc#/radius#)^2)

     N% = 0
     FOR theta# = 0.0 TO PI STEP dt#
       x#( N% ) = 1.0 * radius# * SIN( theta# )
       y#( N% ) = 1.0 * radius# * COS( theta# )
       z#( N% ) = 0.0
       N% += 1
     NEXT theta#

     a# = 0.0
     b# = 1.0 * RAD(5 + RND(180-2*5))
     c# = 0.0

     SYS GFXLIB_RotatePoints3D_0%, ^x#(0), ^y#(0), ^z#(0), ^x2#(0), ^y2#(0), ^z2#(0), ^a#, ^b#, ^c#, N%

     SYS GFXLIB_SaveAndSetDispVars%, dispVars{}, bm%, 256, 256
     FOR I% = 0 TO N%-1
       SYS GFXLIB_Line%, dispVars{}, x0% + x#(I%), y0% + y#(I%), x0% + x2#(I%), y0% + y2#(I%), colour%
     NEXT I%
     SYS GFXLIB_RestoreDispVars%, dispVars{}

     IF RND(2)-2 THEN
       PROC
flipBitmap( bm%, 256, 256, 1 )
     ENDIF

     SYS
GFXLIB_SaveAndSetDispVars%, dispVars{}, bm2%, 256, 256
     SYS GFXLIB_PlotRotateScale2%, dispVars{}, bm%, 256, 256, 128, 128, (-60 + RND(120))*&10000, scale#*&10000
     SYS GFXLIB_RestoreDispVars%, dispVars{}

     SYS GFXLIB_BoxBlurNxNR%, bm2%, 256, 256, 5 + 2*RND(7)

     SYS GFXLIB_SaveAndSetDispVars%, dispVars{}, pBuffer%, buffW%, buffH%
     SYS GFXLIB_PlotBlendLD%, dispVars{}, bm2%, 256, 256, X% - scale#*256, Y% - scale#*256
     SYS GFXLIB_RestoreDispVars%, dispVars{}

     SYS "GlobalFree", bm%
     SYS "GlobalFree", bm2%
     ENDPROC


     
REM  Calculate the 'correct' initial velocity of the banana that would score a hit
     REM  (at least in the absence of wind and air drag!)
     
DEF PROC_getCorrectInitialVelocity( RETURN ux#, RETURN uy_1# )
     LOCAL yDisp1#, yDisp2#, vy_2#, t1#, t2#, t#

     REM  Calculate Y displacements for each 'half' of the banana's trajectory (quadratic curve)
     REM  quadratic.y2% should always be greater than both quadratic.y1% and quadratic.y3%
     
IF player% = 0 THEN
       
yDisp1# = quadratic.y2% - quadratic.y1%
       yDisp2# = quadratic.y2% - quadratic.y3%
     ELSE
       
yDisp1# = quadratic.y2% - quadratic.y3%
       yDisp2# = quadratic.y2% - quadratic.y1%
     ENDIF

     
REM  Calculate the Y component of the initial velocity for the first half of the curve
     
uy_1# = SQR(2 * ABSg# * yDisp1#)

     REM  Calculate the Y component of the final velocity for the second half of the curve
     
vy_2# = SQR(2 * ABSg# * yDisp2#)

     REM  Calculate the total time
     
t1# = uy_1# / ABSg#
     t2# = vy_2# / ABSg#
     t# = t1# + t2#

     REM  Calculate the X component of the initial velocity
     
ux# = (quadratic.x3% - quadratic.x1%) / t#

     IF player% = 1 THEN ux# *= -1
     ENDPROC


     
REM  Draw the sky bitmap (graduated colour fill and stars)
     
DEF PROC_drawSkyBackground( B%, W%, H% )
     LOCAL C%, Y%, M%, I%, G%, R%, m#, a#, b#, starX%, starY#
     SYS GFXLIB_SaveAndSetDispVars%, dispVars{}, B%, W%, H%
     M% = H% - 1
     R% = RND(128)
     G% = RND(128)
     a# = RND(1)
     b# = RND(1)
     m# = 128.0 / M%
     FOR Y% = 0 TO H%-1
       C% = INT(a#*R%*(1-Y%/M%))*&10000 + INT(b#*G%*(1-Y%/M%))*&100 + INT(128-Y%*m#)
       SYS GFXLIB_Line%, dispVars{}, 0, Y%, W%, Y%, C%
     NEXT

     FOR
I% = 1 TO 50 + RND(150)
       starY# = (WinH% + 2) * (1.0 - RND(1)^2)
       SYS GFXLIB_PlotPixel%, D%, RND(WinW%), starY#, FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
     NEXT

     FOR
I% = 1 TO 5 + RND(15)
       starX% = RND(WinW%)
       starY# = (WinH% + 2) * (1.0 - RND(1)^2)
       SYS GFXLIB_PlotPixel%, D%, starX%, starY#, FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
       SYS GFXLIB_PlotPixel%, D%, starX%+1, starY#, FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
       SYS GFXLIB_PlotPixel%, D%, starX%, starY#+1, FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
       SYS GFXLIB_PlotPixel%, D%, starX%+1, starY#+1, FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
     NEXT

     FOR
I% = 1 TO 3 + RND(5)
       starX% = RND(WinW%)
       starY# = (WinH% + 2) * (1.0 - RND(1)^2)
       SYS GFXLIB_PlotPixel%, D%, starX%-1, starY#, FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
       SYS GFXLIB_PlotPixel%, D%, starX%, starY#, FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
       SYS GFXLIB_PlotPixel%, D%, starX%+1, starY#, FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
       SYS GFXLIB_PlotPixel%, D%, starX%, starY#-1, FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
       SYS GFXLIB_PlotPixel%, D%, starX%, starY#+1, FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
     NEXT

     FOR
I% = 1 TO RND(4)
       starX% = RND(WinW%)
       starY# = (WinH% + 2) * (1.0 - RND(1)^2)
       SYS GFXLIB_DrawFilledCircle%, D%, starX%, starY#, RND(5), FNrgb( 200+RND(55), 200+RND(55), RND(64) )
     NEXT

     SYS
GFXLIB_BoxBlurNxNR%, B%, W%, H%, 3

     SYS GFXLIB_RestoreDispVars%, dispVars{}
     ENDPROC


     
DEF PROC_drawCity( B%, W%, H% )
     LOCAL x%, y%, w%, h%, col%
     SYS GFXLIB_ClrX%, dispVars{}, bm.city%, WinW%, WinH%, 0
     x% = 0
     h% = 0.25*WinH% + RND(100)
     buildings.numBuildings% = 0
     buildings.maxHeight% = 0
     REPEAT
       
w% = 32 + RND(80)
       col% = FNrgb( 32+RND(64), 32+RND(64), 32+RND(64) )
       PROC_drawBuilding( bm.city%, WinW%, WinH%, x%, w%, h%, col% )
       buildings.x%( buildings.numBuildings% ) = x%
       buildings.width%( buildings.numBuildings% ) = w%
       buildings.height%( buildings.numBuildings% ) = h%
       IF h% > buildings.maxHeight% THEN buildings.maxHeight% = h%
       buildings.numBuildings% += 1
       h% += FN_rndSgn * (16 + RND(200))
       IF h% < 32 THEN h% = 32 + RND(32)
       IF h% > 0.7*WinH% THEN h% = 0.7*WinH% - RND(50)
       x% += w%
     UNTIL x% >= WinW%
     ENDPROC


     
DEF PROC_drawBuilding( pBuffer%, buffW%, buffH%, x%, width%, height%, colour% )
     LOCAL X%, Y%, winWidth%, winHeight%, winXGap%, winYGap%, winCol%
     winWidth% = 8
     winHeight% = 12
     winXGap% = 16
     winYGap% = 6
     SYS GFXLIB_SaveAndSetDispVars%, dispVars{}, pBuffer%, buffW%, buffH%
     SYS GFXLIB_RectangleSolid%, dispVars{}, x%, 0, width%, height%, colour% + (1<<24)
     FOR Y% = height%-winHeight%-5 TO 0 STEP -(winHeight% + winYGap%)
       FOR X% = x%+5 TO x%+width%-winXGap% STEP winXGap%
         IF RND(10) > 5 THEN
           
winCol% = FNrgb( 200+RND(55), 200+RND(55), 200+RND(55) )
         ELSE
           
winCol% = FNrgb( 32+RND(32), 32+RND(32), 32+RND(32) )
         ENDIF
         IF RND
(100) = 1 THEN
           
winCol% = FNrgb( 180+RND(75), RND(32), RND(32) )
         ENDIF
         SYS
GFXLIB_RectangleSolid%, dispVars{}, X%+4, Y%-2, winWidth%, winHeight%, winCol% + (1<<24)
       NEXT
     NEXT
     SYS
GFXLIB_RestoreDispVars%, dispVars{}
     ENDPROC


     
DEF PROC_drawArrow( x0#, y0#, x1#, y1#, inc#, colour% )
     LOCAL I%, len#, l#, angle#, x1`#, y1`#, x2`#, y2`#

     angle# = FN_atan2( y1#-y0#, x1#-x0# )

     len# = 1.0 * SQR((x0# - x1#)^2 + (y0# - y1#)^2)
     l# = 0.8 * len#

     arrowObj{(2)}.y# = l#
     arrowObj{(3)}.y# = l#
     arrowObj{(4)}.y# = len#
     arrowObj{(5)}.y# = l#
     arrowObj{(6)}.y# = l#

     FOR I% = 0 TO 7
       PROC_rotPoint( 0, 0, arrowObj{(I%)}.x#, arrowObj{(I%)}.y#, angle#, arrowObj{(I%)}.x`#, arrowObj{(I%)}.y`# )
       arrowObj{(I%)}.x`# += x0#
       arrowObj{(I%)}.y`# += y0#
     NEXT

     
REM. Draw arrow shadow
     
FOR I% = 0 TO 6
       y1`# = arrowObj{(I%)}.y`# + 2
       y2`# = arrowObj{(I%+1)}.y`# + 2
       IF FullScreen% THEN
         
y1`# = WinH% - 1 - y1`#
         y2`# = WinH% - 1 - y2`#
       ENDIF
       SYS
GFXLIB_DrawAntialiasedLine2%, dispVars{}, &10000*(arrowObj{(I%)}.x`# - 2), &10000*y1`#, \
       
\ &10000*(arrowObj{(I%+1)}.x`# - 2), &10000*y2`#, &10000*inc#, 0
     NEXT

     
REM. Draw the arrow proper
     
FOR I% = 0 TO 6
       y1`# = arrowObj{(I%)}.y`#
       y2`# = arrowObj{(I%+1)}.y`#
       IF FullScreen% THEN
         
y1`# = WinH% - 1 - y1`#
         y2`# = WinH% - 1 - y2`#
       ENDIF
       SYS
GFXLIB_DrawAntialiasedLine2%, dispVars{}, &10000*arrowObj{(I%)}.x`#, &10000*y1`#, \
       
\ &10000*arrowObj{(I%+1)}.x`#, &10000*y2`#, &10000*inc#, colour%
     NEXT

     ENDPROC


     
DEF PROC_copyCurrentArrow( P% )
     REM  P% = player (0 or 1)
     
LOCAL I%
     FOR I% = 0 TO 6
       oldArrow{( I% )}.x1#( P% ) = arrowObj{(I%)}.x`#
       oldArrow{( I% )}.y1#( P% ) = arrowObj{(I%)}.y`#
       oldArrow{( I% )}.x2#( P% ) = arrowObj{(I%+1)}.x`#
       oldArrow{( I% )}.y2#( P% ) = arrowObj{(I%+1)}.y`#
     NEXT
     ENDPROC


     
DEF PROC_drawArrowCopy( P% )
     REM  P% = player (0 or 1)
     
LOCAL I%, y1`#, y2`#
     FOR I% = 0 TO 6
       y1`# = oldArrow{( I% )}.y1#( P% )
       y2`# = oldArrow{( I% )}.y2#( P% )
       IF FullScreen% THEN
         
y1`# = WinH% - 1 - y1`#
         y2`# = WinH% - 1 - y2`#
       ENDIF
       SYS
GFXLIB_DrawAntialiasedLine2%, dispVars{}, \
       
\ &10000*oldArrow{( I% )}.x1#( P% ), &10000*y1`#, &10000*oldArrow{( I% )}.x2#( P% ), &10000*y2`#, &10000, &A0A000
     NEXT
     ENDPROC


     
DEF PROC_buildParticleColourList( RETURN t%(), N% )
     LOCAL I%, red&, green&, blue&
     DIM t%( N%-1 )

     FOR I% = 0 TO N%-1
       CASE RND(8) OF
         WHEN
1
           red& = 230 + RND(25)
           t%( I% ) = red& * &10000
         WHEN 2, 3
           red& = 230 + RND(25)
           green& = red&
           t%( I% ) = red&*&10000 + green&*&100
         WHEN 4, 5
           red& = 230 + RND(35)
           green& = 0.75 * red&
           t%( I% ) = red&*&10000 + green&*&100
         WHEN 6, 7, 8
           red& = 230 + RND(25)
           green& = 0.9 * red&
           blue& = 230 + RND(25)
           t%( I% ) = red&*&10000 + green&*&100 + blue&
       ENDCASE
     NEXT
I%
     ENDPROC


     
REM  'Compatible' with FNatan2 function!
     
DEF PROC_rotPoint( x0#, y0#, x#, y#, a#, RETURN rx#, RETURN ry# )
     LOCAL dx#, dy#, s#, c#
     dx# = x# - x0#
     dy# = y# - y0#
     s# = COSa#
     c# = SINa#
     rx# = dx#*c# + dy#*s#
     ry# = dy#*c# - dx#*s#
     ENDPROC


     
REM  Given three points (x1,y1), (x2,y2) and (x3,y3) on a quadratic curve,
     REM  return the coefficients a, b, c of the quadratic equation Y = aX^2 + bX + c
     
DEF PROC_getQuadraticCoeffs( x1, y1, x2, y2, x3, y3, RETURN a, RETURN b, RETURN c )
     PROC_solve2x2( x1^2-x2^2, x1-x2, y1-y2, \
     
\ x1^2-x3^2, x1-x3, y1-y3, \
     
\ a, b )
     c = y1 - (a*x1^2 + b*x1)
     ENDPROC


     
DEF PROC_solve2x2(A, B, C, D, E, F, RETURN x, RETURN y)
     REM  This is not a robust solver!
     
LOCAL d
     d = (A*E - B*D)^-1
     x = d * (E*C - B*F)
     y = d * (A*F - D*C)
     ENDPROC


     
DEF FN_atan2(y,x) : ON ERROR LOCAL = SGN(y)*PI/2
     IF x>0 THEN = ATN(y/x) ELSE IF y>0 THEN = ATN(y/x)+PI ELSE = ATN(y/x)-PI


     
DEF FN_rndSgn : IF RND(2)-2 THEN =1 ELSE =-1


     DEF PROC_readMouse( RETURN X%, RETURN Y%, RETURN B% )
     MOUSE X%, Y%, B%
     IF FullScreen% THEN
       
X% = (X% / 2) * WinW% / dispVars.fsWidth%
       Y% = (Y% / 2) * WinH% / dispVars.fsHeight%
     ELSE
       
X% /= 2
       Y% /= 2
     ENDIF
     ENDPROC


     
DEF PROC_fixWindowSize
     LOCAL GWL_STYLE, WS_THICKFRAME, WS_MAXIMIZEBOX, ws%
     GWL_STYLE = -16
     WS_THICKFRAME = &40000
     WS_MAXIMIZEBOX = &10000
     SYS "GetWindowLong", @hwnd%, GWL_STYLE TO ws%
     SYS "SetWindowLong", @hwnd%, GWL_STYLE, ws% AND NOT (WS_THICKFRAME+WS_MAXIMIZEBOX)
     ENDPROC


     
DEF PROC_error( msg$, L% )
     OSCLI "REFRESH ON" : CLS : ON
     COLOUR
1, &FF, &FF, &FF
     COLOUR 1
     PRINT TAB(1,1)msg$;
     IF L% THEN
       PRINT
" at line "; ERL;
     ENDIF
     VDU
7
     REPEAT UNTIL INKEY(1)=0
     ENDPROC


     
DEF FN_getYesNo( s$ )
     LOCAL F%, K%, V%
     PRINT '"" + s$ + " (Y/N) ";
     V% = FALSE
     
F% = FALSE
     REPEAT
       
K% = GET
       IF INSTR(
"Yy", CHR$K%) THEN PRINT "Yes" : V% = TRUE : F% = TRUE
       IF INSTR(
"Nn", CHR$K%) THEN PRINT "No" : V% = TRUE
     UNTIL
V%
     WAIT 100
     = F%