;_/^^^^^^^^^^^^^\_______________________________________________________________________________________________________ ;-[ 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