Source: geocities.com/thechange/pz1

               ( geocities.com/thechange)    
;_/^^^^^^^^^^^^^\_______________________________________________________________________________________________________
;-[ Information ]------------------------------------------------------------------------------------------------1-2-0--
;^\_____________/^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

; Project Z: One (Demo)
; Created by: TheChange
; Date: 16 Jun 2002

; If you wish to play the game, please check out the optimized version; Project Z: One (Opt).
; There are many differences in the optimized version, like the major speed difference.
; If you wish to learn how the game works or wish to check it out anyway, I'd recommend reading this code.
; This is the unoptimized and commented code for Project Z: One.
; Note: This game is resolution dependent (time saving) -> 800x600.
; For more information about this game, please run the optimized version of Project Z: One.

; The first two sections were taken from external files "Keyboard.BB" and "Vector.BB".
; The keyboard file includes all the common used scancodes so you don't have to look them up all the time.
; The vector file includes 4 simple functions to calculate angles and distances.

; Include "Lib\Keyboard.BB"

;_/^^^^^^^^^^^^^^^^^^^^^^\______________________________________________________________________________________________
;-[ Key code definitions ]---------------------------------------------------------------------------------------1-2-0--
;^\______________________/^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  ;-( Numerical )------------------------------------------;

  Const Key1% =  2
  Const Key2% =  3
  Const Key3% =  4
  Const Key4% =  5
  Const Key5% =  6
  Const Key6% =  7
  Const Key7% =  8
  Const Key8% =  9
  Const Key9% = 10
  Const Key0% = 11

  Dim KeyNumeric% ( 9 )
    KeyNumeric ( 0 ) = Key0
    KeyNumeric ( 1 ) = Key1
    KeyNumeric ( 2 ) = Key2
    KeyNumeric ( 3 ) = Key3
    KeyNumeric ( 4 ) = Key4
    KeyNumeric ( 5 ) = Key5
    KeyNumeric ( 6 ) = Key6
    KeyNumeric ( 7 ) = Key7
    KeyNumeric ( 8 ) = Key8
    KeyNumeric ( 9 ) = Key9

  ;-( Alphabetic )-----------------------------------------;

  Const KeyQ% = 16
  Const KeyW% = 17
  Const KeyE% = 18
  Const KeyR% = 19
  Const KeyT% = 20
  Const KeyY% = 21
  Const KeyU% = 22
  Const KeyI% = 23
  Const KeyO% = 24
  Const KeyP% = 25
  Const KeyA% = 30
  Const KeyS% = 31
  Const KeyD% = 32
  Const KeyF% = 33
  Const KeyG% = 34
  Const KeyH% = 35
  Const KeyJ% = 36
  Const KeyK% = 37
  Const KeyL% = 38
  Const KeyZ% = 44
  Const KeyX% = 45
  Const KeyC% = 46
  Const KeyV% = 47
  Const KeyB% = 48
  Const KeyN% = 49
  Const KeyM% = 50

  Dim KeyAlpha% ( 26 )
    KeyAlpha (  1 ) = KeyA
    KeyAlpha (  2 ) = KeyB
    KeyAlpha (  3 ) = KeyC
    KeyAlpha (  4 ) = KeyD
    KeyAlpha (  5 ) = KeyE
    KeyAlpha (  6 ) = KeyF
    KeyAlpha (  7 ) = KeyG
    KeyAlpha (  8 ) = KeyH
    KeyAlpha (  9 ) = KeyI
    KeyAlpha ( 10 ) = KeyJ
    KeyAlpha ( 11 ) = KeyK
    KeyAlpha ( 12 ) = KeyL
    KeyAlpha ( 13 ) = KeyM
    KeyAlpha ( 14 ) = KeyN
    KeyAlpha ( 15 ) = KeyO
    KeyAlpha ( 16 ) = KeyP
    KeyAlpha ( 17 ) = KeyQ
    KeyAlpha ( 18 ) = KeyR
    KeyAlpha ( 19 ) = KeyS
    KeyAlpha ( 20 ) = KeyT
    KeyAlpha ( 21 ) = KeyU
    KeyAlpha ( 22 ) = KeyV
    KeyAlpha ( 23 ) = KeyW
    KeyAlpha ( 24 ) = KeyX
    KeyAlpha ( 25 ) = KeyY
    KeyAlpha ( 26 ) = KeyZ

  ;-( Numeric keypad )-------------------------------------;

  Const KeyPad7% = 71
  Const KeyPad8% = 72
  Const KeyPad9% = 73
  Const KeyPad4% = 75
  Const KeyPad5% = 76
  Const KeyPad6% = 77
  Const KeyPad1% = 79
  Const KeyPad2% = 80
  Const KeyPad3% = 81
  Const KeyPad0% = 82

  Dim KeyPadNumeric% ( 9 )
    KeyPadNumeric ( 0 ) = KeyPad0
    KeyPadNumeric ( 1 ) = KeyPad1
    KeyPadNumeric ( 2 ) = KeyPad2
    KeyPadNumeric ( 3 ) = KeyPad3
    KeyPadNumeric ( 4 ) = KeyPad4
    KeyPadNumeric ( 5 ) = KeyPad5
    KeyPadNumeric ( 6 ) = KeyPad6
    KeyPadNumeric ( 7 ) = KeyPad7
    KeyPadNumeric ( 8 ) = KeyPad8
    KeyPadNumeric ( 9 ) = KeyPad9

  ;-( Function keys )--------------------------------------;

  Const KeyFunction1%  = 59
  Const KeyFunction2%  = 60
  Const KeyFunction3%  = 61
  Const KeyFunction4%  = 62
  Const KeyFunction5%  = 63
  Const KeyFunction6%  = 64
  Const KeyFunction7%  = 65
  Const KeyFunction8%  = 66
  Const KeyFunction9%  = 67
  Const KeyFunction10% = 68
  Const KeyFunction11% = 87
  Const KeyFunction12% = 88

  Dim KeyFunction% ( 12 )
    KeyFunction (  1 ) = KeyFunction1
    KeyFunction (  2 ) = KeyFunction2
    KeyFunction (  3 ) = KeyFunction3
    KeyFunction (  4 ) = KeyFunction4
    KeyFunction (  5 ) = KeyFunction5
    KeyFunction (  6 ) = KeyFunction6
    KeyFunction (  7 ) = KeyFunction7
    KeyFunction (  8 ) = KeyFunction8
    KeyFunction (  9 ) = KeyFunction9
    KeyFunction ( 10 ) = KeyFunction10
    KeyFunction ( 11 ) = KeyFunction11
    KeyFunction ( 12 ) = KeyFunction12

  ;-( Shiftstate )-----------------------------------------;

  Const KeyLeftShift%  =  42
  Const KeyRightShift% =  54
  Const KeyLeftAlt%    =  56
  Const KeyRightAlt%   = 184
  Const KeyLeftCtrl%   =  29
  Const KeyRightCtrl%  = 157
  Const KeyCapsLock%   =  58
  Const KeyNumLock%    =  69  ; Disfunctional
  Const KeyScrollLock% =  70

  Const KeyLeftAlternate%  =  56
  Const KeyRightAlternate% = 184
  Const KeyLeftControl%    =  29
  Const KeyRightControl%   = 157

  ;-( Control keys )---------------------------------------;

  Const KeySpace%        =  57
  Const KeyEnter%        =  28
  Const KeyTab%          =  15
  Const KeyBackSpace%    =  14
  Const KeyInsert%       = 210
  Const KeyDelete%       = 211
  Const KeyHome%         = 199
  Const KeyEnd%          = 207
  Const KeyPageUp%       = 201
  Const KeyPageDown%     = 209
  Const KeyEscape%       =   1
  Const KeyPause%        = 197  ; Numlock
  Const KeyLeftWindows%  = 219
  Const KeyRightWindows% = 220
  Const KeyApps%         = 221
  Const KeyPower%        = 222
  Const KeySleep%        = 223
  Const KeyWake%         = 227

  ;-( Signs )----------------------------------------------;

  Const KeyBackQuote%    = 41
  Const KeyMinus%        = 12
  Const KeyEquals%       = 13
  Const KeyLeftBracket%  = 26
  Const KeyRightBracket% = 27
  Const KeyBackSlash%    = 43
  Const KeySemiColon%    = 39
  Const KeyQuote%        = 40
  Const KeyComma%        = 51
  Const KeyPeriod%       = 52
  Const KeySlash%        = 53

  ;-( Keypad controls )------------------------------------;

  Const KeyPadSlash%    = 181
  Const KeyPadAsterisk% =  55
  Const KeyPadMinus%    =  74
  Const KeyPadPlus%     =  78
  Const KeyPadEnter%    = 156
  Const KeyPadPeriod%   =  83

  ;-( Cursors )--------------------------------------------;

  Const KeyCursorUp%    = 200
  Const KeyCursorDown%  = 208
  Const KeyCursorLeft%  = 203
  Const KeyCursorRight% = 205

;_/^^^^^^^^^^^^^^^^^^^^^^\______________________________________________________________________________________________
;-[ Key name definitions ]---------------------------------------------------------------------------------------1-2-0--
;^\______________________/^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Dim KeyName$ ( 255 )

  ;-( Numerical )------------------------------------------;

  KeyName ( Key1 ) = "1"
  KeyName ( Key2 ) = "2"
  KeyName ( Key3 ) = "3"
  KeyName ( Key4 ) = "4"
  KeyName ( Key5 ) = "5"
  KeyName ( Key6 ) = "6"
  KeyName ( Key7 ) = "7"
  KeyName ( Key8 ) = "8"
  KeyName ( Key9 ) = "9"
  KeyName ( Key0 ) = "0"

  ;-( Alphabetic )-----------------------------------------;

  KeyName ( KeyQ ) = "Q"
  KeyName ( KeyW ) = "W"
  KeyName ( KeyE ) = "E"
  KeyName ( KeyR ) = "R"
  KeyName ( KeyT ) = "T"
  KeyName ( KeyY ) = "Y"
  KeyName ( KeyU ) = "U"
  KeyName ( KeyI ) = "I"
  KeyName ( KeyO ) = "O"
  KeyName ( KeyP ) = "P"
  KeyName ( KeyA ) = "A"
  KeyName ( KeyS ) = "S"
  KeyName ( KeyD ) = "D"
  KeyName ( KeyF ) = "F"
  KeyName ( KeyG ) = "G"
  KeyName ( KeyH ) = "H"
  KeyName ( KeyJ ) = "J"
  KeyName ( KeyK ) = "K"
  KeyName ( KeyL ) = "L"
  KeyName ( KeyZ ) = "Z"
  KeyName ( KeyX ) = "X"
  KeyName ( KeyC ) = "C"
  KeyName ( KeyV ) = "V"
  KeyName ( KeyB ) = "B"
  KeyName ( KeyN ) = "N"
  KeyName ( KeyM ) = "M"

  ;-( Numeric keypad )-------------------------------------;

  KeyName ( KeyPad7 ) = "Pad 7"
  KeyName ( KeyPad8 ) = "Pad 8"
  KeyName ( KeyPad9 ) = "Pad 9"
  KeyName ( KeyPad4 ) = "Pad 4"
  KeyName ( KeyPad5 ) = "Pad 5"
  KeyName ( KeyPad6 ) = "Pad 6"
  KeyName ( KeyPad1 ) = "Pad 1"
  KeyName ( KeyPad2 ) = "Pad 2"
  KeyName ( KeyPad3 ) = "Pad 3"
  KeyName ( KeyPad0 ) = "Pad 0"

  ;-( Function keys )--------------------------------------;

  KeyName ( KeyFunction1  ) = "F1"
  KeyName ( KeyFunction2  ) = "F2"
  KeyName ( KeyFunction3  ) = "F3"
  KeyName ( KeyFunction4  ) = "F4"
  KeyName ( KeyFunction5  ) = "F5"
  KeyName ( KeyFunction6  ) = "F6"
  KeyName ( KeyFunction7  ) = "F7"
  KeyName ( KeyFunction8  ) = "F8"
  KeyName ( KeyFunction9  ) = "F9"
  KeyName ( KeyFunction10 ) = "F10"
  KeyName ( KeyFunction11 ) = "F11"
  KeyName ( KeyFunction12 ) = "F12"

  ;-( Shiftstate )-----------------------------------------;

  KeyName ( KeyLeftShift  ) = "Left Shift"
  KeyName ( KeyRightShift ) = "Right Shift"
  KeyName ( KeyLeftAlt    ) = "Left Alt"
  KeyName ( KeyRightAlt   ) = "Right Alt"
  KeyName ( KeyLeftCtrl   ) = "Left Control"
  KeyName ( KeyRightCtrl  ) = "Right Control"
  KeyName ( KeyCapsLock   ) = "CapsLock"
  KeyName ( KeyNumLock    ) = "NumLock"
  KeyName ( KeyScrollLock ) = "ScrollLock"

  ;-( Control keys )---------------------------------------;

  KeyName ( KeySpace        ) = "SpaceBar"
  KeyName ( KeyEnter        ) = "Enter"
  KeyName ( KeyTab          ) = "Tab"
  KeyName ( KeyBackSpace    ) = "BackSpace"
  KeyName ( KeyInsert       ) = "Insert"
  KeyName ( KeyDelete       ) = "Delete"
  KeyName ( KeyHome         ) = "Home"
  KeyName ( KeyEnd          ) = "End"
  KeyName ( KeyPageUp       ) = "PageUp"
  KeyName ( KeyPageDown     ) = "PageDown"
  KeyName ( KeyEscape       ) = "Escape"
  KeyName ( KeyPause        ) = "Pause"
  KeyName ( KeyLeftWindows  ) = "Left Windows"
  KeyName ( KeyRightWindows ) = "Right Windows"
  KeyName ( KeyApps         ) = "Apps"
  KeyName ( KeyPower        ) = "Power"
  KeyName ( KeySleep        ) = "Sleep"
  KeyName ( KeyWake         ) = "Wake"

  ;-( Signs )----------------------------------------------;

  KeyName ( KeyBackQuote    ) = "BackQuote"
  KeyName ( KeyMinus        ) = "Minus"
  KeyName ( KeyEquals       ) = "Equals"
  KeyName ( KeyLeftBracket  ) = "Left Bracket"
  KeyName ( KeyRightBracket ) = "Right Bracket"
  KeyName ( KeyBackSlash    ) = "BackSlash"
  KeyName ( KeySemiColon    ) = "SemiColon"
  KeyName ( KeyQuote        ) = "Quote"
  KeyName ( KeyComma        ) = "Comma"
  KeyName ( KeyPeriod       ) = "Period"
  KeyName ( KeySlash        ) = "Slash"

  ;-( Keypad controls )------------------------------------;

  KeyName ( KeyPadSlash    ) = "Pad Slash"
  KeyName ( KeyPadAsterisk ) = "Pad Asterisk"
  KeyName ( KeyPadMinus    ) = "Pad Minus"
  KeyName ( KeyPadPlus     ) = "Pad Plus"
  KeyName ( KeyPadEnter    ) = "Pad Enter"
  KeyName ( KeyPadPeriod   ) = "Pad Period"

  ;-( Cursors )--------------------------------------------;

  KeyName ( KeyCursorUp    ) = "Cursor Up"
  KeyName ( KeyCursorDown  ) = "Cursor Down"
  KeyName ( KeyCursorLeft  ) = "Cursor Left"
  KeyName ( KeyCursorRight ) = "Cursor Right"

;_/^^^^^^^^^^^^^^^^^^^^\________________________________________________________________________________________________
;-[ Key initialization ]-----------------------------------------------------------------------------------------1-2-0--
;^\____________________/^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  FlushKeys ()

;-->

; Include "Lib\Vector.BB"

; *------------------------------------*
; |                                    |
; | Vector Matrix Conversion - VXII    |
; |                                    |
; *------------------------------------*

;-[ Sub ]--------------------------------------------------------------------------------------------------------1-2-0--

  Function GetVectorX# ( Distance# , Angle# )  ; Get horizontal size of vector using distance and angle.
    Return Sin ( Angle ) * Distance
  End Function

  Function GetVectorY# ( Distance# , Angle# )  ; Get vertical size of vector using distance and angle.
    Return Sin ( Angle - 90 ) * Distance
  End Function

  ;<--

  Function GetLength# ( X# , Y# )  ; Get true length of a vector (not used in this game).
    Return Sqr ( X * X + Y * Y )
  End Function

  Function GetAngle% ( X# , Y# )  ; Get true angle of a vector.
    Result% = ATan2 ( Y , X ) + 90
    If Result < 0 Result = Result + 360
    Return Result
  End Function

  ; For more explanation on these functions, see further below.

;-->

Graphics 800 , 600 , 0 , 1  ; Force 800 x 600 - detect colordepth - force fullscreen.
ClsColor 0 , 0 , 0
Color 255 , 255 , 255
Print "Starting up manually..."

Font = LoadFont ( "Blitz" , 20 , True )  ; Default font is font from Blitz(tm).
SetFont Font

PlayerBitmap = CreateImage ( 32 , 32 )  ; What the player looks like.
MidHandle PlayerBitmap
SetBuffer ImageBuffer ( PlayerBitmap )
Color 0 , 63 , 127
Rect 0 , 0 , 32 , 32 , False
Color 0 , 127 , 255
Oval 3 , 3 , 26 , 26 , False
Color 0 , 255 , 255
Oval 6 , 6 , 20 , 20 , False
PlayerX = 400  ; Put player in center of screen.
PlayerY = 300

TargetBitmap = CreateImage ( 15 , 15 )  ; The mouse crosshair to use for aiming.
MidHandle TargetBitmap
SetBuffer ImageBuffer ( TargetBitmap )
Color 255 , 0 , 0
Oval 0 , 0 , 15 , 15 , False
Plot 7 , 7

EnemyBitmap = CreateImage ( 25 , 25 )  ; What the enemy looks like.
MidHandle EnemyBitmap
SetBuffer ImageBuffer ( EnemyBitmap )
Color 95 , 127 , 0
Rect 0 , 0 , 25 , 25 , False
Color 191 , 255 , 0
Oval 2 , 2 , 10 , 10 , False
Oval 2 , 13 , 10 , 10 , False
Oval 13 , 2 , 10 , 10 , False
Oval 13 , 13 , 10 , 10 , False
Type Enemy  ; Collection of enemies.
  Field X#  ; Horizontal coordinate.
  Field Y#  ; Vertical coordinate.
  Field Class  ; See Enemy Movement section for descriptions.
End Type
Local Enemy.Enemy  ; Variable to use to iterate through the collection.

BulletBitmap = CreateImage ( 3 , 3 )  ; All bullets looks like this.
MidHandle BulletBitmap
SetBuffer ImageBuffer ( BulletBitmap )
Color 255 , 255 , 255
Plot 1 , 1
Color 127 , 127 , 127
Plot 0 , 1
Plot 1 , 0
Plot 2 , 1
Plot 1 , 2
Color 63 , 63 , 63
Plot 0 , 0
Plot 0 , 2
Plot 2 , 0
Plot 2 , 2
Type Bullet  ; Collection of bullets.
  Field Owner  ; 1 for Player , 2 for Enemy.
  Field X#  ; Coords.
  Field Y#
  Field Angle#  ; Angle or direction in which the bullet is heading.
End Type
Local Bullet.Bullet  ; Iteration variable.
; Note: All bullets travel at 5 pixels per frame.

ShrapnelBitmap = CreateImage ( 12 , 12 )  ; Shrapnel can't hurt in this game.
MidHandle ShrapnelBitmap
SetBuffer ImageBuffer ( ShrapnelBitmap )
Color 95 , 127 , 0
Oval 0 , 0 , 12 , 12 , False
Color 191 , 255 , 0
Oval 2 , 2 , 8 , 8 , False
Type Shrapnel  ; Collection.
  Field X#  ; Horz coord.
  Field Y#  ; Vert coord.
  Field Angle#  ; Angle/direction/heading.
  Field Distance  ; Current distance since creation.
  Field TotalDistance  ; Distance to achieve before dissolving.
End Type
Local Shrapnel.Shrapnel
; Note: All shrapnel travels at 1 pixel per frame.

MaxEnemy = 15  ; Max number of enemies allowed.
; When spawning an enemy and there's already a max number of enemies,
; the first enemy dissolves / explodes into shrapnel.
SpawningTime# = 100  ; Number of frames to wait before spawning an enemy.
; This value decreases slowly during gameplay (the spawning increases).
HighScore = 1000000  ; Default highscore.
SeedRnd MilliSecs ()
SetBuffer BackBuffer ()
Repeat

  ; Scoring:
  ; In this game, all of the difficulty settings are based on your score,
  ; except for the SpawningTime which is independent.
  ; If your score goes above 1,999,999 you become invincible.
  ; A lot of details are different in the optimized version.

  If Score < 1000
    BulletCost = 1    ; Amount of score to use for each bullet.
    ShootDelay = 200  ; Cheap bullets, so a huge delay to Recharge.
    Level = 0         ; Level indicator is shown as red "" signs.
    EnemyClass = 1    ; Maximum Enemy Class to spawn.
    EnemySpeed = 1    ; Maximum Speed (in pixels) of each enemy.
  ElseIf Score < 2000   ; When your score reaches 1000...
    BulletCost = 1    ; Still cheap bullets.
    ShootDelay = 150  ; A bit less delay.
    EnemySpeed = 2    ; Enemies twice as fast.
  ElseIf Score < 3000
    BulletCost = 1
    ShootDelay = 100  ; High shoot delay.
  ElseIf Score < 4000
    BulletCost = 1
    ShootDelay = 80
  ElseIf Score < 10000  ; After 4000 points...
    BulletCost = 2    ; A bit more expensive bullets.
    ShootDelay = 60   ; Medium shoot delay.
    Level = 1         ; Advancing to next level (1).
    EnemyClass = 2    ; Introducing a new type of enemy.
  ElseIf Score < 20000
    BulletCost = 3
    ShootDelay = 40   ; Recharge Indicator disappears.
    Level = 2         ; Advance to next level (2).
    EnemySpeed = 3    ; Fast enemies.
  ElseIf Score < 30000
    BulletCost = 4
    ShootDelay = 24
    EnemyClass = 3    ; New enemy type.
  ElseIf Score < 40000
    BulletCost = 5
    ShootDelay = 12
  ElseIf Score < 50000
    BulletCost = 6
    ShootDelay = 6    ; Low shoot delay.
    Level = 3         ; Next level.
    EnemyClass = 4    ; New enemy.
  ElseIf Score < 100000
    BulletCost = 12
    ShootDelay = 4
    Level = 4
    EnemyClass = 5
  ElseIf Score < 200000
    BulletCost = 24
    ShootDelay = 2
    Level = 5
    EnemyShooting = True  ; Enemies 1 through 3 start shooting at you!
    EnemyShots = 1  ; Chance (%) of enemy shooting.
  ElseIf Score < 300000
    BulletCost = 48
    ShootDelay = 1  ; Turbo shoot speed.
    Level = 6
    EnemyShots = 2  ; Double chance.
  ElseIf Score < 500000
    BulletCost = 96
    ShootDelay = 0  ; Maximum shoot-out.
    Level = 7
    EnemySpeed = 4
    EnemyShots = 3
  ElseIf Score < 700000
    Level = 8
    EnemySpeed = 6
    EnemyShots = 4
  ElseIf Score < 1000000
    Level = 9
    EnemySpeed = 8
    EnemyShots = 5
  ElseIf Score < 2000000
    Level = 10       ; Highest level.
    EnemySpeed = 10  ; Turbo enemy speed.
    EnemyShots = 10  ; Heavy enemy fire.
  Else  ; GodMode at 2,000,000+ points.
    BulletCost = 0  ; Free bullets.
    GodMode = True  ; No score decrement.
  EndIf

  If Score > HighScore Then HighScore = Score  ; Update highscore.

  If ScoreWait > 9  ; Automatically increase score with 1 point per 10 frames.
    ScoreWait = 0
    Score = Score + 1
  Else
    ScoreWait = ScoreWait + 1
  EndIf

  ; Player and Enemy Shooting:
  ; Player can shoot with Left Mouse button when not recharging.
  ; Shooting is Recharging, ShootDelay is RechargeDelay.
  ; But when Player's score is too low, Player can't shoot either.
  ; Enemies of class/type 1 through 3 are able to shoot.

  If Shooting  ; Recharging.
    If ShootingWait >= ShootDelay  ; Recharge time is over.
      Shooting = False
      ShootingWait = 0  ; Reset Recharge timer.
    Else
      ShootingWait = ShootingWait + 1  ; Recharging...
    EndIf
  Else  ; Not recharging - ready to shoot.
    If MouseDown ( 1 )
      If Score >= BulletCost  ; If Player can afford bullet.
        Score = Score - BulletCost  ; Buy bullet.
        Shooting = True  ; Enable Recharging.
        Bullet = New Bullet  ; Create bullet.
        Bullet\Owner = 1  ; Player owns bullet.
        Bullet\X = PlayerX  ; Bullet starts at player position.
        Bullet\Y = PlayerY
        Bullet\Angle = GetAngle ( MouseX () - PlayerX , MouseY () - PlayerY )
        ; Bullet is pointing at where your mouse cursor is, from your Player's point of view.
      EndIf
    EndIf
  EndIf

  If EnemyShooting  ; If enemies are allowed to shoot...
    For Enemy = Each Enemy  ; Iterate through the enemy collection.
      Select Enemy\Class
        Case 1  ; If Enemy\Class = 1...
          If Int ( Rnd ( 1 , 100 / EnemyShots ) ) = 1  ; EnemyShots (%) chance of shooting.
            Bullet = New Bullet
            Bullet\Owner = 2  ; Enemy owns bullet.
            Bullet\X = Enemy\X  ; Bullet starts at enemy position.
            Bullet\Y = Enemy\Y
            Bullet\Angle = GetAngle ( PlayerX - Enemy\X , PlayerY - Enemy\Y ) + Rnd ( -15 , 15 )
            ; Bullet is pointing at Player, from Enemy's point of view,
            ; with a random diffusion of max 15 degrees.
            If Bullet\Angle < 0   Then Bullet\Angle = Bullet\Angle + 360
            If Bullet\Angle > 359 Then Bullet\Angle = Bullet\Angle - 360
            ; If diffusion caused angle to go below 0 or beyond 359 degrees, warp/flip it.
            ; Diffusion causes Enemy's bullets to be more inaccurate for a more realistic effect.
          EndIf
        Case 2
          If Int ( Rnd ( 1 , 200 / EnemyShots ) ) = 1  ; EnemyShots % devided by 2.
            Bullet = New Bullet
            Bullet\Owner = 2
            Bullet\X = Enemy\X
            Bullet\Y = Enemy\Y
            Bullet\Angle = GetAngle ( PlayerX - PlayerX , PlayerY - PlayerY ) + Rnd ( -30 , 30 )
            ; Less accurate (30 degrees diffusion).
            If Bullet\Angle < 0   Then Bullet\Angle = Bullet\Angle + 360
            If Bullet\Angle > 359 Then Bullet\Angle = Bullet\Angle - 360
          EndIf
        Case 3
          If Int ( Rnd ( 1 , 300 / EnemyShots ) ) = 1  ; Chance / 3.
            Bullet = New Bullet
            Bullet\Owner = 2
            Bullet\X = Enemy\X
            Bullet\Y = Enemy\Y
            Bullet\Angle = GetAngle ( PlayerX - PlayerX , PlayerY - PlayerY ) + Rnd ( -45 , 45 )
            ; The Least accurate of the 3 (45 degrees diffusion).
            If Bullet\Angle < 0   Then Bullet\Angle = Bullet\Angle + 360
            If Bullet\Angle > 359 Then Bullet\Angle = Bullet\Angle - 360
          EndIf
        ; Enemies 4 and 5 don't shoot, ever.
      End Select
    Next
  EndIf

  ; Spawning Enemies:
  ; Spawning occurs always, if necessary, the oldest enemy will be removed to make place for a new one.
  ; This will cause a funny effect but also constant refreshment (increased difficulty).
  ; Spawning is WaitingToSpawnNextEnemy, SpawningWait is the timer for waiting before spawning.
  ; Enemies spawn with random position and class.

  If SpawningTime > 1 Then SpawningTime = SpawningTime / 1.0001  ; Automatically decrease SpawningTime slowly.

  If Spawning  ; Waiting to spawn new (SpawningOld).
    If SpawningWait > SpawningTime  ; Ready to spawn new.
      Spawning = False
      SpawningWait = 0  ; Reset timer.
    Else
      SpawningWait = SpawningWait + 1  ; Waiting...
    EndIf
  Else
    Spawning = True  ; Enable waiting...
    ; This part counts the number of enemies currently in the collection (slow).
    Count = 0
    For Enemy = Each Enemy
      Count = Count + 1
    Next
    ; In the optimized version the program keeps track of the number of enemies manually (fast).
    ; E.g. "Enemies = Enemies + 1" after a "New Enemy" command and 
    ;      "Enemies = Enemies - 1" after a "Delete Enemy" command.
    If Count >= MaxEnemy  ; If there's already a max number of enemies...
      Enemy = First Enemy  ; Find the first enemy.
      For x = 0 To 3  ; Make 4 pieces of shrapnel.
        Shrapnel = New Shrapnel
        Shrapnel\X = Enemy\X  ; Position them at the enemy position.
        Shrapnel\Y = Enemy\Y
        Shrapnel\Angle = x * 90 + Rnd ( 90 )  ; With 4 different and random angles in 90 degrees range.
        Shrapnel\Distance = 0  ; No distance travelled since creation.
        Shrapnel\TotalDistance = 9 + Rnd ( 20 )  ; Distance before dissolving is at least 9 pixels
        ;                                        ; plus a random value of max 20 (after rounding).
      Next
      Delete Enemy  ; And make the enemy dissolve.
    EndIf
    ; This part calculates the position for the enemy. A random location is okay.
    ; But not if it's too close to the player! E.g. spawning on a player is nasty.
    px1 = PlayerX - 99  ; Creating 4 coordinates of a rectangle around the player.
    px2 = PlayerX + 99  ; The size of the rectangle is in this case 198 x 198.
    py1 = PlayerY - 99
    py2 = PlayerY + 99
    Repeat
      x = Rnd ( 800 )  ; Get random coordinates for Enemy.
      y = Rnd ( 600 )
    Until Not ( x > px1 ) And ( x < px2 ) And ( y > py1 ) And ( y < py2 )
    ; Repeat getting random values until the coordinates are outside the player range.
    Enemy = New Enemy  ; Then spawn the new enemy.
    Enemy\X = x  ; At the random coordinates.
    Enemy\Y = y
    Enemy\Class = Rnd ( 1 , EnemyClass )  ; With a random class/type, depending on the maximum allowed.
  EndIf

  ; Player, Enemy, Bullet and Shrapnel Movement:
  ; Cursor keys influence player position naturally.
  ; Enemies move according to their type specification.
  ; All bullets act the same, but move in different directions.
  ; For shrapnel basically the same as bullets.

  If KeyDown ( KeyCursorUp    ) Then PlayerY = PlayerY - 2  ; Move player 2 pixels when user wants to.
  If KeyDown ( KeyCursorDown  ) Then PlayerY = PlayerY + 2
  If KeyDown ( KeyCursorLeft  ) Then PlayerX = PlayerX - 2
  If KeyDown ( KeyCursorRight ) Then PlayerX = PlayerX + 2
  If PlayerX < 0   Then PlayerX = 0   ; Adjust player coordinates to prevent player from going off-screen totally.
  If PlayerX > 799 Then PlayerX = 799
  If PlayerY < 0   Then PlayerY = 0
  If PlayerY > 599 Then PlayerY = 599

  For Enemy = Each Enemy  ; Loop through the enemy collection.
    Select Enemy\Class  ; Move enemies according to their type/class.
      Case 1  ; random , max speed = EnemySpeed * sqrt(2) (read explanation below).
        ; This one has 1 cubic speed factor (read explanation below).
        Enemy\X = Enemy\X + Rnd ( -EnemySpeed , EnemySpeed )
        Enemy\Y = Enemy\Y + Rnd ( -EnemySpeed , EnemySpeed )
        ; Similar to player movement physics, cubic movement instead of circular.
        ; Only major difference is that it's totally random.
      Case 2  ; random , avoid target , max speed = EnemySpeed * sqrt(2)
        ; This one has 2 cubic speed factors.
        Enemy\X = Enemy\X + Rnd ( -EnemySpeed/2 , EnemySpeed/2 )
        Enemy\Y = Enemy\Y + Rnd ( -EnemySpeed/2 , EnemySpeed/2 )
        ; Same as first enemy type but half the speed.
        If MouseX () > Enemy\X Then Enemy\X = Enemy\X - EnemySpeed/2
        If MouseX () < Enemy\X Then Enemy\X = Enemy\X + EnemySpeed/2
        If MouseY () > Enemy\Y Then Enemy\Y = Enemy\Y - EnemySpeed/2
        If MouseY () < Enemy\Y Then Enemy\Y = Enemy\Y + EnemySpeed/2
        ; Adding an intelligent bit, avoiding the mouse cursor.
      Case 3  ; random , avoid target , hunt player , max speed = EnemySpeed * sqrt(2)
        ; This one has 3 cubic speed factors.
        Enemy\X = Enemy\X + Rnd ( -EnemySpeed/3 , EnemySpeed/3 )
        Enemy\Y = Enemy\Y + Rnd ( -EnemySpeed/3 , EnemySpeed/3 )
        ; Same as first enemy, but a third of the random speed.
        If PlayerX < Enemy\X Then Enemy\X = Enemy\X - EnemySpeed/3
        If PlayerX > Enemy\X Then Enemy\X = Enemy\X + EnemySpeed/3
        If PlayerY < Enemy\Y Then Enemy\Y = Enemy\Y - EnemySpeed/3
        If PlayerY > Enemy\Y Then Enemy\Y = Enemy\Y + EnemySpeed/3
        ; This piece hunts the player.
        If MouseX () > Enemy\X Then Enemy\X = Enemy\X - EnemySpeed/3
        If MouseX () < Enemy\X Then Enemy\X = Enemy\X + EnemySpeed/3
        If MouseY () > Enemy\Y Then Enemy\Y = Enemy\Y - EnemySpeed/3
        If MouseY () < Enemy\Y Then Enemy\Y = Enemy\Y + EnemySpeed/3
        ; Same as second enemy type, avoiding the mouse cursor.
      Case 4  ; random , hunt 8-way player , max speed = EnemySpeed * sqrt(2)
        ; This one has 2 cubic speed factors.
        Enemy\X = Enemy\X + Rnd ( -EnemySpeed/2 , EnemySpeed/2 )
        Enemy\Y = Enemy\Y + Rnd ( -EnemySpeed/2 , EnemySpeed/2 )
        ; Same as first enemy, but half the speed.
        If PlayerX < Enemy\X Then Enemy\X = Enemy\X - EnemySpeed/2
        If PlayerX > Enemy\X Then Enemy\X = Enemy\X + EnemySpeed/2
        If PlayerY < Enemy\Y Then Enemy\Y = Enemy\Y - EnemySpeed/2
        If PlayerY > Enemy\Y Then Enemy\Y = Enemy\Y + EnemySpeed/2
        ; Again, hunting the player.
      Case 5  ; random , hunt 360-way player , max speed = EnemySpeed
        ; This one has 2 circular speed factors.
        x = GetVectorX ( EnemySpeed/2 , Rnd ( 360 ) )
        y = GetVectorY ( EnemySpeed/2 , Rnd ( 360 ) )
        ; Get the X and Y disposition coordinates of half the enemy speed in a random direction.
        a = GetAngle ( PlayerX - Enemy\X , PlayerY - Enemy\Y )
        ; Get the absolute angle to the Player position from the Enemy's point of view.
        Enemy\X = Enemy\X + GetVectorX ( EnemySpeed/2 , z ) + x
        Enemy\Y = Enemy\Y + GetVectorY ( EnemySpeed/2 , z ) + y
        ; Updates the enemy's position with:
        ; 1) The disposition coordinates of half the enemy speed in the player's direction.
        ; 2) The disposition coordinates of half the enemy speed in a random direction.
      ; Difference between Cubic and Circular motion:
      ;
      ; ooooooooO  |
      ; o      /o  |
      ; o     / o  |
      ; o    /  o  |
      ; o   O   o  | Cubic movement.
      ; o       o  |
      ; o       o  |
      ; o       o  |
      ; ooooooooo  |
      ;
      ; ooooooooO  | Cubic motion vector:
      ; o    V /|  | Vector V [X|Y]
      ; o     / |Y |
      ; o    /  |  | X = EnemySpeed
      ; o   O---O  | Y = EnemySpeed
      ; o     X o  | V = Sqrt( EnemySpeed^2 + EnemySpeed^2 ) = EnemySpeed * Sqrt( 2 )
      ; o       o  |
      ; o       o  | So the length of V is actually more than is allowed in this case.
      ; ooooooooo  | The circular movement preserves the original values, e.g. cubic movement is 'cheating'.
      ;
      ;    ooo     |
      ;  ooo ooO   |
      ;  o    /o   |
      ; oo   / oo  |
      ; o   O   o  | Circular movement.
      ; oo     oo  |
      ;  o     o   |
      ;  ooo ooo   |
      ;    ooo     |
      ;
      ;    ooo     | Circular motion vector:
      ;  ooo ooO   | Vector V [X|Y]
      ;  o  V /|Y  |
      ; oo   / |o  | V = EnemySpeed
      ; o   O--Oo  | X = GetVectorX ( V , Angle )
      ; oo   X oo  | Y = GetVectorY ( V , Angle )
      ;  o     o   |
      ;  ooo ooo   | Angle is the angle of the vector between line X and V.
      ;    ooo     | The Vector routines use an orientation where an angle of 0 is always straight up.
      ;
    End Select
    If Enemy\X < 0   Then Enemy\X = 0   ; Make sure the enemies don't move totally off-screen.
    If Enemy\X > 799 Then Enemy\X = 799
    If Enemy\Y < 0   Then Enemy\Y = 0
    If Enemy\Y > 599 Then Enemy\Y = 599
    ; In an Asteroids type of game one would change the first line to: "If Enemy\X < 0 Then Enemy\X = Enemy\X + 800"
  Next

  For Bullet = Each Bullet  ; Walk through the bullet collection.
    Bullet\X = Bullet\X + GetVectorX ( 5 , Bullet\Angle )  ; Update the bullet's position with a distance/length of 5
    Bullet\Y = Bullet\Y + GetVectorY ( 5 , Bullet\Angle )  ; circular pixels and the angle of the bullet itself.
    If ( Bullet\X < 0 ) Or ( BulletX > 799 ) Or ( Bullet\Y < 0 ) Or ( Bullet\Y > 599 ) Then Delete Bullet
    ; In the case of bullets, they are no longer required when going off-screen.
  Next

  For Shrapnel = Each Shrapnel  ; Iterate through all pieces of shrapnel.
    Shrapnel\X = Shrapnel\X + GetVectorX ( 1 , Shrapnel\Angle )  ; Shrapnel pieces move 1 pixel per frame, in the
    Shrapnel\Y = Shrapnel\Y + GetVectorY ( 1 , Shrapnel\Angle )  ; direction of the angle specified at creation time.
    If Shrapnel\Distance >= Shrapnel\TotalDistance  ; But if the covered distance is enough, wipe it out.
      Delete Shrapnel
    Else  ; Otherwise keep movin'.
      Shrapnel\Distance = Shrapnel\Distance + 1
      If ( Shrapnel\X < 0 ) Or ( Shrapnel\X > 799 ) Or ( Shrapnel\Y < 0 ) Or ( Shrapnel\Y > 599 ) Then Delete Shrapnel
      ; If going off-screen, like bullets, no need in using them anymore.
    EndIf
  Next

  ; Collisions:
  ; Enemy colliding with Player results in Enemy 'exploding' and score going down.
  ; Enemy colliding with Player bullet results in Enemy 'exploding', bullet dissolving, and score going up.
  ; Player colliding with Enemy bullet results in bullet dissolving and score going down.

  For Enemy = Each Enemy
    If ImagesCollide ( EnemyBitmap , Enemy\X , Enemy\Y , 0 , PlayerBitmap , PlayerX , PlayerY , 0 )
    ; Test for collision between Enemy and Player (pixel-perfect)
      For x = 0 To 3  ; If collision detected, make just another quad-shrapnel with the good 'ol values.
        Shrapnel = New Shrapnel
        Shrapnel\X = Enemy\X
        Shrapnel\Y = Enemy\Y
        Shrapnel\Angle = x * 90 + Rnd ( 90 )
        Shrapnel\Distance = 0
        Shrapnel\TotalDistance = 9 + Rnd ( 20 )
      Next
      Delete Enemy  ; Delete Enemy after creating the Shrapnel, because we needed the enemy position for it.
      If Not GodMode Then Score = Score - 1000  ; And score goes down by 1000 points (!) but not in GodMode.
    EndIf
  Next

  For Enemy = Each Enemy
    For Bullet = Each Bullet
    ; To see when 'each' bullet collides with 'each' enemy, we have to loop through both collections simultaneously.
      If Bullet\Owner = 1  ; Only checking the bullets owner by Player.
        If ImagesCollide ( EnemyBitmap , Enemy\X , Enemy\Y , 0 , BulletBitmap , Bullet\X , Bullet\Y , 0 )
        ; Pixel-perfect collision detection between Enemy and Bullet.
          Score = Score + 100 * Enemy\Class + 1000 * EnemyShooting
          ; If there's a collision, the score increases depending on the Enemy type/class.
          ; The simplest enemy is 100 points and the most advanced enemy is 500 points.
          ; If the enemies are shooting, an addition of 1000 points is granted.
          ; In the optimized version of the game, the size of the addition depends on enemy shot accuracy.
          For x = 0 To 3  ; As usual, make the enemy turn into shrapnel.
            Shrapnel = New Shrapnel
            Shrapnel\X = Enemy\X
            Shrapnel\Y = Enemy\Y
            Shrapnel\Angle = x * 90 + Rnd ( 90 )
            Shrapnel\Distance = 0
            Shrapnel\TotalDistance = 9 + Rnd ( 20 )
          Next
          Delete Bullet  ; Don't forget to remove the bullet.
          Delete Enemy  ; As well as the enemy.
          Exit
        EndIf
      EndIf
    Next
  Next

  For Bullet = Each Bullet
    If Bullet\Owner = 2  ; If this bullet is an Enemy bullet...
      If ImagesCollide ( PlayerBitmap , PlayerX , PlayerY , 0 , BulletBitmap , Bullet\X , Bullet\Y , 0 )
      ; Collide between Player and Bullet.
        Delete Bullet  ; Simply remove the bullet.
        If Not GodMode Then Score = Score - 100  ; And decrease the score, but not in GodMode.
      EndIf
    EndIf
  Next

  If Score < 0 Then Score = 0  ; Game isn't totally unfriendly, score is always positive.

  ; Rendering:
  ; Drawing all bitmaps for shrapnel, enemies, bullets, player and mouse crosshair.
  ; (In that order respectively, to make sure the right things overlap each other)
  ; And lots of other neat little thingies that increase the quality of the gaming experience.

  For Shrapnel = Each Shrapnel  ; Draw each piece of shrapnel at the designated coordinates.
    DrawImage ShrapnelBitmap , Shrapnel\X , Shrapnel\Y
  Next

  For Enemy = Each Enemy  ; Same for all enemies.
    DrawImage EnemyBitmap , Enemy\X , Enemy\Y
  Next

  For Bullet = Each Bullet  ; And bullets.
    DrawImage BulletBitmap , Bullet\X , Bullet\Y
  Next

  DrawImage PlayerBitmap , PlayerX , PlayerY  ; Only one player.

  DrawImage TargetBitmap , MouseX () , MouseY ()  ; Target is directly controlled by the mouse.

  Color 0 , 127 , 255  ; Display score and highscore, centered on center coordinates.
  Text 200 , 590 , "Score: " + WithComma (     Score ) , True , True  ; Uses WithComma() function.
  Text 600 , 590 , "High: "  + WithComma ( HighScore ) , True , True  ; See below.

  If Not GodMode  ; Show level in number of red "", unless in godmode.
    Color 255 , 0 , 0
    Text 400 , 590 , String ( "" , Level ) , True , True
  EndIf

  Color 63 , 127 , 0  ; Show the enemy spawn speed in a bar.
  Rect 300 , 596 , ( 100 - SpawningTime ) * 2 , 2 , False  ; Assumes SpawningTime is 100 by default.
  Color 47 , 95 , 0
  Rect 298 , 594 , 200 + 2 , 6 , False  ; Outer frame.

  ; This shows the Recharging meter.
  If ShootDelay > 40  ; Don't show the meter if recharging is fast enough.
    If Shooting  ; Currently recharging (ShootingOld)
      Color 63 , 127 , 0
      Rect 10 , 10 , ShootingWait * 780 / ShootDelay , 10 , True  ; Percent-like formula.
      Color 47 , 95 , 0
      Rect 8 , 8 , 780 + 4 , 14 , False  ; Outer frame.
      Color 95 , 191 , 0
      Text 400 , 15 , "RECHARGING" , True , True
    EndIf
  EndIf

  ; The optimized version features a Pause function and a Frame limiting method switch.

  Flip True  ; Hardware frame-limiting, e.g. wait for a vertical blank each frame.
  Cls  ; Then clear the screen, since we have no background picture or something like that.
Until KeyHit ( KeyEscape )  ; Quit the game when pressing Escape.

Delete Each Shrapnel  ; Clean up all collections.
Delete Each Bullet    ; Blitz can happily do it for you, normally.
Delete Each Enemy     ; But there are lots of cases where even Blitz can't.

FreeImage ShrapnelBitmap  ; And remove all images.
FreeImage BulletBitmap    ; Sometimes you can work behind Blitz' back to accomplish things that
FreeImage EnemyBitmap     ; cannot normally be done with solely native Blitz commands. In these
FreeImage PlayerBitmap    ; cases it is imperative to clean up all things yourself.
FreeFont Font  ; Don't forget font.

FreeTimer Timer  ; Almost forgot the timer.

EndGraphics  ; This command is also automatically executed when exiting your Blitz program.
Color 255 , 255 , 255
Print "Shutting down automatically..."

End

Function WithComma$ ( Number$ )  ; This function generates comma's in a number.
; This function is really slow but it's very easy to understand what's going on.
  Local Reverse$
  Local Comma$
  For x = 1 To Len ( Number )  ; Go through each position in the number (backwards).
    Reverse = Reverse + Mid ( Number , Len ( Number ) - x + 1 , 1 )
    Counter = Counter + 1
    If Counter = 3  ; And put a comma where necessary.
      Counter = 0
      Reverse = Reverse + ","
    EndIf
  Next
  For x = 1 To Len ( Reverse )  ; Reverse the string.
    Comma = Comma + Mid ( Reverse , Len ( Reverse ) - x + 1 , 1 )
  Next
  If Left ( Comma , 1 ) = "," Then Comma = Mid ( Comma , 2 , Len ( Comma ) - 1 )  ; Remove leading comma.
  Return Comma  ; Return generated result.
End Function