;------------------------------------------------------------------------------- ; Graphics handling ;------------------------------------------------------------------------------- ; Resolution constants from Blitz Const WindowMode_AutoDetect = 0 Const WindowMode_FullScreen = 1 Const WindowMode_Windowed = 2 Const WindowMode_Scaled = 3 ;------------------------------------------------------------------------------- ; Graphics resolution Global ScreenWidth Global ScreenHeight Global WindowMode Global ColorDepth ;------------------------------------------------------------------------------- ; Check if a resolution is listed in the video card's supported videomodes Function ResAvail ( Width , Height ) Local ColorDepth For ColorDepth = 16 To 32 Step 8 If GfxModeExists ( Width , Height , ColorDepth ) Return ColorDepth End If Next End Function ;------------------------------------------------------------------------------- ; Audio ;------------------------------------------------------------------------------- ; Sound channel tracker Type ActiveChannel ; Channel containing the sound playing Field ChannelHandle ; Reference to the sound being played ; (each sound has a different address) Field SoundHandle ; Time when started playing Field Created ; How long to wait before being able to play same sound. Field TimeOut End Type ;------------------------------------------------------------------------------- ; Soundtrack Global GameMusic ; Background music channel handle Global MusicChannel ; Effects Global SpawnSound Global ThrustSound Global FireSound Global CollideSound Global ExplodeSound ;------------------------------------------------------------------------------- ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; # Play sample with overlapping prevention ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Play a sound from SoundHandle, but only if it is not already playing, ; depending on a custom overlapping time, defined by TimeOut. ; - If TimeOut equals 0, the new sound to play will be allowed to overlap. ; - Channels are automatically updated when calling PlaySample, ; regardless of how many times you call it or how long you wait. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Function PlaySample ( SoundHandle , TimeOut = False ) Local FindChannel.ActiveChannel Local FoundChannel.ActiveChannel Local Playing = False Local PlayNew = False ; Check all previously playing sounds/channels For FindChannel = Each ActiveChannel ; If this channel is still playing If ChannelPlaying ( FindChannel\ChannelHandle ) ; And the sound being played is identical to the new sound to play. If FindChannel\SoundHandle = SoundHandle ; Remember that it is already being played Playing = True ; And store it for later access FoundChannel = FindChannel End If ; This channel is no longer playing Else ; Remove it from the list Delete FindChannel End If Next ; The new sound to play is already playing (it was found in the list) If Playing ; If the time since creation exceeds the channel's overlap (time out) If MilliSecs - FoundChannel\Created >= FoundChannel\TimeOut ; Playing the new sound is allowed (overlapping) PlayNew = True End If ; The new sound to play is not yet playing Else ; So playing the new sound is allowed (without hesitation) PlayNew = True End If ; Allowed to play new sound If PlayNew ; Create an object to track the channel FoundChannel = New ActiveChannel ; Remember the time of creation FoundChannel\Created = MilliSecs ; Record the overlapping time FoundChannel\TimeOut = TimeOut ; Also remember which sound is used FoundChannel\SoundHandle = SoundHandle ; Play the sound (and keep track) FoundChannel\ChannelHandle = PlaySound ( SoundHandle ) ; Finally return the channel, ; so you can optionally modify its properties. Return FoundChannel\ChannelHandle End If End Function ;------------------------------------------------------------------------------- ; Player ;------------------------------------------------------------------------------- ; Player 1 (left) Const KeyW = 17 Const KeyA = 30 Const KeyS = 31 Const KeyD = 32 ; Player 2 (right) Const KeyUp = 200 Const KeyDown = 208 Const KeyLeft = 203 Const KeyRight = 205 ; Player (ship) thrust speed in pixels Const ThrustSpeed# = 0.12 ; Player (ship) rotation speed in degrees Const RotateSpeed# = 4 ; Player (ship) maximum velocity in pixels Const MaxSpeed = 5 ; Downward gravity strength in pixels (on ship) Const GravityPull# = 0.03 ;------------------------------------------------------------------------------- Type Player ; True or False Field Dead ; Time of death Field DiedWhen ; Unique player number (1 or 2) Field Number ; Current ship angle Field Angle# ; Current ship heading (direction) Field Heading# ; Current ship speed (velocity) Field Speed# ; Request to thrust ship Field Thrusting ; Sound channel handle Field ThrustChannel ; Current position Field PosX# Field PosY# End Type ;------------------------------------------------------------------------------- ; ( 1-based player number , 0-based angle ) Dim PlayerImages( 2 , 360 ) ; ( 1-based player number ) Dim Player.Player( 2 ) ;------------------------------------------------------------------------------- Function CreatePlayer.Player ( PlayerNumber ) Local This.Player This = New Player This\Number = PlayerNumber This\Dead = True ; Center view on tilemap by default This\PosX = (TilemapWidth -1) * TileWidth / 2 This\PosY = (TilemapHeight-1) * TileHeight / 2 ; Prepare thrust sound This\ThrustChannel = PlaySound ( ThrustSound ) PauseChannel This\ThrustChannel ChannelPan This\ThrustChannel , ( PlayerNumber - 1 ) * 2 - 1 Return This End Function Function PlayersInput () Local Player.Player Local Thrust Local RotateLeft Local RotateRight Local Fire For Player = Each Player Select Player\Number Case 1 Thrust = KeyS RotateLeft = KeyA RotateRight = KeyD Fire = KeyW Case 2 Thrust = KeyDown RotateLeft = KeyLeft RotateRight = KeyRight Fire = KeyUp End Select If KeyDown ( Thrust ) If Player\Dead RespawnPlayer Player Else Player\Thrusting = True ; Play thrust sound ResumeChannel Player\ThrustChannel End If Else ; Stop thrust sound PauseChannel Player\ThrustChannel End If If KeyDown ( RotateLeft ) Then RotatePlayerLeft Player If KeyDown ( RotateRight ) Then RotatePlayerRight Player If KeyHit ( Fire ) Then FirePlayer Player Next End Function Function RespawnPlayer ( This.Player ) Local Here.SpawnSpot Local Channel Local Respawn ; How much time to wait (in ms) before being able to spawn after dying If MilliSecs - This\DiedWhen >= 2000 ; Find a random spawn spot Here = RandomSpawnSpot () ; Convert tilemap scale (array) to pixel scale (world) This\PosX = (Here\PosX-1) * TileWidth This\PosY = (Here\PosY-1) * TileHeight ; Point up and reset speed This\Angle = 0 This\Speed = 0 This\Heading = 0 ; Prevent spawning over another player, if a player is alive If ( Not Player( 1 )\Dead ) Or ( Not Player( 2 )\Dead ) If Not PlayersCollide () Respawn = True End If Else Respawn = True End If If Respawn ; Make alive and kicking This\Dead = False ; Play respawn sound Channel = PlaySample ( SpawnSound , 10 ) ChannelPan Channel , ( This\Number - 1 ) * 2 - 1 End If End If End Function Function KillPlayer ( This.Player ) Local Channel This\Dead = True This\DiedWhen = MilliSecs ; Stop thrust sound PauseChannel This\ThrustChannel ; Create a bunch of shrapnel pieces in random directions CreateBulletRay This\PosX , This\PosY ; Play explode sound Channel = PlaySample ( ExplodeSound , 50 ) ChannelPan Channel , ( This\Number - 1 ) * 2 - 1 End Function Function RotatePlayerLeft ( This.Player ) If Not This\Dead This\Angle = This\Angle - RotateSpeed If This\Angle < 0 Then This\Angle = This\Angle + 360 End If End Function Function RotatePlayerRight ( This.Player ) If Not This\Dead This\Angle = This\Angle + RotateSpeed If This\Angle >= 360 Then This\Angle = This\Angle - 360 End If End Function Function FirePlayer ( This.Player ) Local PosX Local PosY Local VelX# Local VelY# Local Channel If Not This\Dead ; Calculate where to create bullet in front of ship PosX = This\PosX + VectorX ( BulletDistance , This\Angle ) PosY = This\PosY + VectorY ( BulletDistance , This\Angle ) ; Initial bullet velocity vector VelX = VectorX ( BulletSpeed , This\Angle ) VelY = VectorY ( BulletSpeed , This\Angle ) ; Add player velocity vector to bullet velocity vector (realism) VelX = VelX + VectorX ( This\Speed , This\Heading ) VelY = VelY + VectorY ( This\Speed , This\Heading ) CreateBullet PosX , PosY , VelX , VelY ; Play fire sound Channel = PlaySample ( FireSound , 10 ) ChannelPan Channel , ( This\Number - 1 ) * 2 - 1 End If End Function Function UpdatePlayer ( This.Player ) Local ThrustX# Local ThrustY# Local SpeedX# Local SpeedY# If Not This\Dead If This\Thrusting This\Thrusting = False ; Thrust vector ThrustX = VectorX ( ThrustSpeed , This\Angle ) ThrustY = VectorY ( ThrustSpeed , This\Angle ) Else ThrustX = 0 ThrustY = 0 End If ; Thrust vector + old velocity vector = new velocity vector SpeedX = VectorX ( This\Speed , This\Heading ) + ThrustX SpeedY = VectorY ( This\Speed , This\Heading ) + ThrustY ; Add gravity vector to velocity vector (180 degrees = down) SpeedX = SpeedX + VectorX ( GravityPull , 180 ) SpeedY = SpeedY + VectorY ( GravityPull , 180 ) ; Update speed and heading with new velocity vector This\Speed = VectorDistance ( SpeedX , SpeedY ) This\Heading = VectorAngle ( SpeedX , SpeedY ) ; Limit velocity (speed of ship) If This\Speed > MaxSpeed This\Speed = MaxSpeed ; Recalculate limited velocity vector SpeedX = VectorX ( This\Speed , This\Heading ) SpeedY = VectorY ( This\Speed , This\Heading ) End If ; Update position with velocity vector This\PosX = This\PosX + SpeedX This\PosY = This\PosY + SpeedY End If End Function Function DrawPlayers ( Main.Player ) Local This.Player Local PosX Local PosY For This = Each Player ; Player is dead If Not This\Dead ; But it's not the player being drawn If This <> Main ; Make this player relative to the one being drawn PosX = This\PosX - Main\PosX PosY = This\PosY - Main\PosY ; This player is being drawn Else ; Center it PosX = 0 PosY = 0 End If ; Limit the angle to increments of 5, starting from 0 ; to match with the image frames stored in the array DrawImage PlayerImages( This\Number , LimitAngle ( This\Angle , 5 ) ) , PosX , PosY End If Next End Function ;------------------------------------------------------------------------------- ; Bullet ;------------------------------------------------------------------------------- ; Bullet spawning distance from player image hotspot in pixels Const BulletDistance = 24 ; Bullet velocity in pixels Const BulletSpeed = 4 ; Bullet time to live in milliseconds Const BulletTime = 5000 ;------------------------------------------------------------------------------- Type Bullet ; Time of creation Field TimeCreated ; Duration of life Field TimeToLive ; Position Field PosX# Field PosY# ; Velocity Field VelX# Field VelY# End Type ;------------------------------------------------------------------------------- Global BulletImage ;------------------------------------------------------------------------------- Function CreateBullet ( PosX , PosY , VelX# , VelY# , NormalBullet = True ) Local This.Bullet This = New Bullet This\PosX = PosX This\PosY = PosY This\VelX = VelX This\VelY = VelY This\TimeCreated = MilliSecs ; Bullet If NormalBullet ; Use default time to live This\TimeToLive = BulletTime ; Shrapnel Else ; Use slightly randomized default time to live This\TimeToLive = BulletTime / 2 + Rand ( BulletTime ) End If End Function Function CreateBulletRay ( InitialPosX , InitialPosY , InitialVelX# = 0 , InitialVelY# = 0 ) Local Angle Local RandomAngle Local VelX# Local VelY# Local PosX# Local PosY# ; Divide 360 degrees in sections of 5 degrees For Angle = 0 To 360 Step 360/5 ; Then choose a random angle inside that peace of pie RandomAngle = Angle + Rand ( 360/5 ) ; Velocity: use half of bullet speed plus the initial speed (optional) VelX = InitialVelX + VectorX ( BulletSpeed / 2 , RandomAngle ) VelY = InitialVelY + VectorY ( BulletSpeed / 2 , RandomAngle ) ; Position: use half of ship tip distance plus the offset position PosX = InitialPosX + VectorX ( BulletDistance / 2 , RandomAngle ) PosY = InitialPosY + VectorY ( BulletDistance / 2 , RandomAngle ) ; Finally create it, but it's not a normal bullet CreateBullet PosX , PosY , VelX , VelY , False Next End Function Function UpdateBullets () Local This.Bullet For This = Each Bullet ; Update position This\PosX = This\PosX + This\VelX This\PosY = This\PosY + This\VelY ; Time to die If MilliSecs >= This\TimeCreated + This\TimeToLive Delete This End If Next End Function Function DrawBullets ( Orientation.Player ) Local This.Bullet Local PosX Local PosY ; Use an alterable display offset PosX = Orientation\PosX PosY = Orientation\PosY For This = Each Bullet ; Draw image relative to the display position DrawImage BulletImage , This\PosX - PosX , This\PosY - PosY Next End Function ;------------------------------------------------------------------------------- ; Tilemap ;------------------------------------------------------------------------------- ; Size of a single tile Const TileWidth = 32 Const TileHeight = 32 ; Number of tiles in anim image Const TileCount = 42 ;------------------------------------------------------------------------------- ; Tiles images Global TileStrip ; ( 1-based tile position index ) Dim Tilemap( 0 , 0 ) Global TilemapWidth Global TilemapHeight ;------------------------------------------------------------------------------- Function DrawTilemap ( Orientation.Player ) Local StartTileX Local StartTileY Local EndTileX Local EndTileY Local TileX Local TileY Local TilePosX Local TilePosY Local FrameNumber Local PosX Local PosY ; Use a relative display position PosX = Orientation\PosX PosY = Orientation\PosY ; Use top-left of player window as offset ; centering the player in the middle of the window ; taking split screen into account ; Then find the tile that's there at this moment ; Add one tile to overlap the window edge StartTileX = ( PosX - ScreenWidth /2/2 ) / TileWidth - 1 StartTileY = ( PosY - ScreenHeight/2 ) / TileHeight - 1 ; Use the player window size as the range ; again centering the player in the middle ; and taking split screen into account ; Then find the tile that's there at this moment ; Add one tile to overlap the window edge EndTileX = ( PosX + ScreenWidth /2/2 ) / TileWidth + 1 EndTileY = ( PosY + ScreenHeight/2 ) / TileHeight + 1 ; Make sure that the starting and ending tiles ; are not out of tilemap range (edges) If StartTileX < 0 Then StartTileX = 0 If StartTileY < 0 Then StartTileY = 0 If EndTileX > TilemapWidth -1 Then EndTileX = TilemapWidth -1 If EndTileY > TilemapHeight-1 Then EndTileY = TilemapHeight-1 ; Now go through only those tiles that are within the player's window For TileX = StartTileX To EndTileX For TileY = StartTileY To EndTileY FrameNumber = Tilemap( TileX+1 , TileY+1 ) ; Is non-zero If FrameNumber TilePosX = TileX * TileWidth - PosX TilePosY = TileY * TileHeight - PosY DrawImage TileStrip , TilePosX , TilePosY , FrameNumber End If Next Next End Function Function LoadTilemap ( FileName$ ) Local FileHandle Local PosX Local PosY Local Line$ Local TileNumber ; Attempt to open file (return False if unsuccesful) FileHandle = OpenFile ( FileName ) If Not FileHandle Then Return False ; Grab tilemap dimensions TilemapWidth = ReadLine ( FileHandle ) TilemapHeight = ReadLine ( FileHandle ) ; Reset tilemap Dim Tilemap( TilemapWidth , TilemapHeight ) ; And spawn spots Delete Each SpawnSpot SpawnSpots = 0 For PosY = 1 To TilemapHeight Line = ReadLine ( FileHandle ) For PosX = 1 To TilemapWidth ; Convert tilenumber in map to framenumber in anim image (chr 33 = tile 0) TileNumber = Asc ( Mid ( Line , PosX , 1 ) ) - 33 ; Discard tiles which are out of range If TileNumber >= 0 And TileNumber <= TileCount-1 ; This is a spawn spot (tile 0) ; Add it to the list and dont put in tilemap If TileNumber = 0 ; Tilemap scale AddSpawnSpot PosX , PosY SpawnSpots = SpawnSpots + 1 ; Add all other tiles to tilemap Else Tilemap( PosX , PosY ) = TileNumber End If End If Next Next CloseFile FileHandle ; Return true if any spawn spots found, otherwise false Return SpawnSpots > 0 End Function ;------------------------------------------------------------------------------- ; Spawn spots ;------------------------------------------------------------------------------- Type SpawnSpot ; Position Field PosX Field PosY End Type ;------------------------------------------------------------------------------- ; Total number of spawn spots in tilemap Global SpawnSpots ;------------------------------------------------------------------------------- Function AddSpawnSpot ( PosX , PosY ) Local This.SpawnSpot This = New SpawnSpot This\PosX = PosX This\PosY = PosY End Function ; Grab a random spawnspot from the list Function RandomSpawnSpot.SpawnSpot () Local Number Local Times Local This.SpawnSpot ; Start at the front This = First SpawnSpot ; Pick a random one using the known number of spawn spots Times = Rand ( 0 , SpawnSpots - 1 ) ; Count until reached target value For Number = 1 To Times ; Next spawn spot This = After This Next Return This End Function ;------------------------------------------------------------------------------- ; Collision ;------------------------------------------------------------------------------- Function PlayerCollideWithPlayer () ; Both players must be alive If ( Not Player( 1 )\Dead ) And ( Not Player( 2 )\Dead ) ; Then see if they collide If PlayersCollide () ; And kill them if so KillPlayer Player( 1 ) KillPlayer Player( 2 ) End If End If End Function Function PlayersCollideWithTilemap () Local Player.Player Local TileX Local TileY Local StartTileX Local StartTileY Local EndTileX Local EndTileY Local TilePosX Local TilePosY Local PlayerImg For Player = Each Player If Not Player\Dead PlayerImg = PlayerImages( Player\Number , LimitAngle ( Player\Angle , 5 ) ) ; Tile under player TileX = Player\PosX / TileWidth TileY = Player\PosY / TileHeight ; 3x3 tile block under player StartTileX = TileX - 1 StartTileY = TileY - 1 EndTileX = TileX + 1 EndTileY = TileY + 1 ; Limit to edges of tilemap If StartTileX < 0 Then StartTileX = 0 If StartTileY < 0 Then StartTileY = 0 If EndTileX > TilemapWidth -1 Then EndTileX = TilemapWidth -1 If EndTileY > TilemapHeight-1 Then EndTileY = TilemapHeight-1 For TileX = StartTileX To EndTileX For TileY = StartTileY To EndTileY ; Is non-zero If Tilemap( TileX+1 , TileY+1 ) TilePosX = TileX * TileWidth TilePosY = TileY * TileHeight If ImagesOverlap ( PlayerImg , Player\PosX , Player\PosY , TileStrip , TilePosX , TilePosY ) If ImagesCollide ( PlayerImg , Player\PosX , Player\PosY , 0 , TileStrip , TilePosX , TilePosY , Tilemap( TileX+1 , TileY+1 ) ) KillPlayer Player End If End If End If Next Next End If Next End Function Function PlayersCollideWithBullets () Local Player.Player Local This.Bullet Local PlayerImg For Player = Each Player If Not Player\Dead PlayerImg = PlayerImages( Player\Number , LimitAngle ( Player\Angle , 5 ) ) For This = Each Bullet If ImagesOverlap ( PlayerImg , Player\PosX , Player\PosY , BulletImage , This\PosX , This\PosY ) If ImagesCollide ( PlayerImg , Player\PosX , Player\PosY , 0 , BulletImage , This\PosX , This\PosY , 0 ) Delete This KillPlayer Player ; This player is dead, don't check for any more bullets Exit End If End If Next End If Next End Function Function BulletsCollideWithTilemap () Local This.Bullet Local TileX Local TileY Local StartTileX Local StartTileY Local EndTileX Local EndTileY Local TilePosX Local TilePosY Local ExitLoops For This = Each Bullet TileX = This\PosX / TileWidth TileY = This\PosY / TileHeight StartTileX = TileX - 1 StartTileY = TileY - 1 EndTileX = TileX + 1 EndTileY = TileY + 1 If StartTileX < 0 Then StartTileX = 0 If StartTileY < 0 Then StartTileY = 0 If EndTileX > TilemapWidth -1 Then EndTileX = TilemapWidth -1 If EndTileY > TilemapHeight-1 Then EndTileY = TilemapHeight-1 ExitLoops = False For TileX = StartTileX To EndTileX For TileY = StartTileY To EndTileY ; Is non-zero If Tilemap( TileX+1 , TileY+1 ) TilePosX = TileX * TileWidth TilePosY = TileY * TileHeight If ImagesOverlap ( BulletImage , This\PosX , This\PosY , TileStrip , TilePosX , TilePosY ) If ImagesCollide ( BulletImage , This\PosX , This\PosY , 0 , TileStrip , TilePosX , TilePosY , Tilemap( TileX+1 , TileY+1 ) ) Delete This ; Play collide sound PlaySample CollideSound , 50 ExitLoops = True Exit End If End If End If Next If ExitLoops Then Exit Next Next End Function Function PlayersCollide () Local PlayerImg1 Local PlayerImg2 PlayerImg1 = PlayerImages( 1 , LimitAngle ( Player( 1 )\Angle , 5 ) ) PlayerImg2 = PlayerImages( 2 , LimitAngle ( Player( 2 )\Angle , 5 ) ) If ImagesOverlap ( PlayerImg1 , Player( 1 )\PosX , Player( 1 )\PosY , PlayerImg2 , Player( 2 )\PosX , Player( 2 )\PosY ) If ImagesCollide ( PlayerImg1 , Player( 1 )\PosX , Player( 1 )\PosY , 0 , PlayerImg2 , Player( 2 )\PosX , Player( 2 )\PosY , 0 ) Return True End If End If End Function ;------------------------------------------------------------------------------- ; Mathematics ;------------------------------------------------------------------------------- ; Trigonometry: return horizontal vector Function VectorX# ( Distance# , Angle# ) Return Sin ( Angle ) * Distance End Function ; Trigonometry: return vertical vector Function VectorY# ( Distance# , Angle# ) Return Sin ( Angle - 90 ) * Distance End Function ; Trigonometry: return distance using vector Function VectorDistance# ( X# , Y# ) Return Sqr ( X * X + Y * Y ) End Function ; Trigonometry: return angle using vector Function VectorAngle# ( X# , Y# ) Return 180 - ATan2 ( X , Y ) End Function ; Return angle divided in steps Function LimitAngle ( Angle , Stepsize ) Return Int ( Floor ( Float Angle / Stepsize ) ) * Stepsize End Function ;------------------------------------------------------------------------------- ; Miscellaneous ;------------------------------------------------------------------------------- ; Locations of external files Const AudioFolder$ = "Audio\" Const GraphicsFolder$ = "Graphics\" Const TilemapsFolder$ = "Tilemaps\" ; Game control Const KeyP = 25 Const KeyEsc = 1 ;------------------------------------------------------------------------------- ; Game states Global GamePaused ; Stars in the background Global BackgroundImage ; Current time (updated once every frame) Global MilliSecs ; Game speed limiter Global GameTimer ;------------------------------------------------------------------------------- Function Verify ( TargetHandle , ErrorMessage$ = "probs :(" ) If Not TargetHandle Then RuntimeError ErrorMessage End Function ;------------------------------------------------------------------------------- ; Main sub-routines ;------------------------------------------------------------------------------- Function InitGame () Local IntroImage Local PlayerNumber Local Angle Local LoadStart ; Windowed in debug mode, otherwise fullscreen WindowMode = WindowMode_AutoDetect ; Autodetect optimal resolution ; - Wide screen medium resolution (2.13x wide) ScreenWidth = 1024 ScreenHeight = 480 ColorDepth = ResAvail ( ScreenWidth , ScreenHeight ) If Not ColorDepth ; - Wide screen high resolution (1.78x wide) ScreenWidth = 1280 ScreenHeight = 720 ColorDepth = ResAvail ( ScreenWidth , ScreenHeight ) If Not ColorDepth ; - Wide screen medium-low resolution (1.77x wide) ScreenWidth = 848 ScreenHeight = 480 ColorDepth = ResAvail ( ScreenWidth , ScreenHeight ) If Not ColorDepth ; - Wide screen low resolution (1.5x wide) ScreenWidth = 720 ScreenHeight = 480 ColorDepth = ResAvail ( ScreenWidth , ScreenHeight ) If Not ColorDepth ; - CRT screen high resolution (1.33x wide) ScreenWidth = 1024 ScreenHeight = 768 ColorDepth = ResAvail ( ScreenWidth , ScreenHeight ) If Not ColorDepth ; - CRT screen medium resolution (1.33x wide) ScreenWidth = 800 ScreenHeight = 600 ColorDepth = ResAvail ( ScreenWidth , ScreenHeight ) If Not ColorDepth ; - CRT screen low resolution (1.33x wide) ScreenWidth = 640 ScreenHeight = 480 ColorDepth = ResAvail ( ScreenWidth , ScreenHeight ) ; - If this resolution will not work either, ; Blitz will show an error message anyway End If End If End If End If End If End If ; Try the detected screen resolution Graphics ScreenWidth , ScreenHeight , ColorDepth , WindowMode ; Start music first GameMusic = LoadSound ( AudioFolder + "TheChance - Gravity Force.MP3" ) Verify GameMusic , "Unable to load intro music" LoopSound GameMusic MusicChannel = PlaySound ( GameMusic ) ; - At half volume, to put it in the background ChannelVolume MusicChannel , 0.5 ; Show splash screen / front-end IntroImage = LoadImage ( GraphicsFolder + "X-NON Logo.PNG" ) Verify IntroImage , "Unable to load intro image" MidHandle IntroImage DrawImage IntroImage , ScreenWidth / 2 , ScreenHeight / 2 ; Sync time MilliSecs = MilliSecs () ; Remember when started loading LoadStart = MilliSecs ; Load effects SpawnSound = LoadSound ( AudioFolder + "Spawn.WAV" ) Verify SpawnSound , "Unable to load spawn sound" ThrustSound = LoadSound ( AudioFolder + "Thrust.WAV" ) Verify ThrustSound , "Unable to load thrust sound" LoopSound ThrustSound FireSound = LoadSound ( AudioFolder + "Fire.WAV" ) Verify FireSound , "Unable to load fire sound" CollideSound = LoadSound ( AudioFolder + "Collide.WAV" ) Verify CollideSound , "Unable to load collide sound" ExplodeSound = LoadSound ( AudioFolder + "Explode.WAV" ) Verify ExplodeSound , "Unable to load explode sound" ; Load pre-rotated player images For PlayerNumber = 1 To 2 For Angle = 0 To 360 Step 5 PlayerImages( PlayerNumber , Angle ) = LoadImage ( GraphicsFolder + "Ships\" + "Ship " + PlayerNumber + " - " + Angle + ".BMP" ) Verify PlayerImages( PlayerNumber , Angle ) , "Unable to load ship image " + PlayerNumber + "," + Angle MidHandle PlayerImages( PlayerNumber , Angle ) Next Next ; Load bullet image BulletImage = LoadImage ( GraphicsFolder + "Bullet.BMP" ) Verify BulletImage , "Unable to load bullet image" MidHandle BulletImage ; Load tiles images TileStrip = LoadAnimImage ( GraphicsFolder + "Tiles.PNG" , TileWidth , TileHeight , 0 , TileCount ) Verify TileStrip , "Unable to load tile strip" ; Load background image BackgroundImage = LoadImage ( GraphicsFolder + "Star Background.PNG" ) Verify BackgroundImage , "Unable to load background image" ; Load tile map data Verify LoadTilemap ( TilemapsFolder + "Arena 1.TXT" ) , "Unable to load tilemap" ; Setup players Player( 1 ) = CreatePlayer ( 1 ) Player( 2 ) = CreatePlayer ( 2 ) ; Set default game states GamePaused = False ; Reset randomizer ; (set number of milliseconds since system boot as randomizer base value) SeedRnd MilliSecs ; Setup game speed limiter at 60 updates per second GameTimer = CreateTimer( 60 ) ; Double buffering SetBuffer BackBuffer () ; If loading took less than 3 seconds, ; wait until those same 3 seconds are up. ; This, in order to make the game logo visible ; for at least a little while. While MilliSecs () - LoadStart < 3000 Wend ; Clear any pressed keys FlushKeys End Function ;------------------------------------------------------------------------------- ; Main execution ;------------------------------------------------------------------------------- ; Initialize the entire game InitGame ; Main loop Repeat ; Sync time MilliSecs = MilliSecs () If KeyHit ( KeyP ) GamePaused = Not GamePaused FlushKeys ; Forget pressed keys End If If Not GamePaused ; Control both ships PlayersInput ; Update game UpdatePlayer Player( 1 ) UpdatePlayer Player( 2 ) UpdateBullets ; Collisions PlayerCollideWithPlayer PlayersCollideWithTilemap PlayersCollideWithBullets BulletsCollideWithTilemap End If ; Render game ; - Player 1 Viewport 0 , 0 , ScreenWidth / 2 , ScreenHeight TileBlock BackgroundImage , -Player( 1 )\PosX/2 , -Player( 1 )\PosY/2 Origin ScreenWidth / 4 , ScreenHeight / 2 DrawTilemap Player( 1 ) DrawBullets Player( 1 ) DrawPlayers Player( 1 ) ; - Player 2 Viewport ScreenWidth / 2 , 0 , ScreenWidth / 2 , ScreenHeight TileBlock BackgroundImage , -Player( 2 )\PosX/2 , -Player( 2 )\PosY/2 Origin ScreenWidth / 2 + ScreenWidth / 4 , ScreenHeight / 2 DrawTilemap Player( 2 ) DrawBullets Player( 2 ) DrawPlayers Player( 2 ) ; - Screen Viewport 0 , 0 , ScreenWidth , ScreenHeight Origin 0 , 0 Color 0 , 0 , 0 Rect ScreenWidth/2 , 0 , 1 , ScreenHeight Flip ; Sync game speed WaitTimer GameTimer Until KeyHit ( KeyEsc ) ; Blitz cleans up End ;------------------------------------------------------------------------------- ;~IDEal Editor Parameters: ;~C#Blitz3D198