;------------------------------------------------------------------------------- ; Graphics handling ;------------------------------------------------------------------------------- ; Graphics resolution Global ScreenWidth = 640 Global ScreenHeight = 480 ;------------------------------------------------------------------------------- ; Audio ;------------------------------------------------------------------------------- ; Effects Global ThrustSound Global FireSound ;------------------------------------------------------------------------------- ; Player ;------------------------------------------------------------------------------- ; Player controls Const KeyUp = 200 Const KeyDown = 208 Const KeyLeft = 203 Const KeyRight = 205 Const KeySpace = 57 ; 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 ; 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 ; Position Field PosX# Field PosY# End Type ;------------------------------------------------------------------------------- ; ( 1-based player number , 0-based angle ) Dim PlayerImages( 2 , 360 ) ; Single player Global Player.Player ; Debugging gravity Global GravityEnabled = False ;------------------------------------------------------------------------------- Function CreatePlayer.Player () Local This.Player This = New Player ; 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 Return This End Function Function PlayerInput ( Player.Player ) If KeyDown ( KeyDown ) Player\Thrusting = True ; Play thrust sound ResumeChannel Player\ThrustChannel Else ; Stop thrust sound PauseChannel Player\ThrustChannel End If If KeyDown ( KeyLeft ) Then RotatePlayerLeft Player If KeyDown ( KeyRight ) Then RotatePlayerRight Player If KeyHit ( KeyUp ) Then FirePlayer Player If KeyHit ( KeySpace ) Then ToggleGravity End Function Function RotatePlayerLeft ( This.Player ) This\Angle = This\Angle - RotateSpeed If This\Angle < 0 Then This\Angle = This\Angle + 360 End Function Function RotatePlayerRight ( This.Player ) This\Angle = This\Angle + RotateSpeed If This\Angle >= 360 Then This\Angle = This\Angle - 360 End Function Function FirePlayer ( This.Player ) Local PosX Local PosY Local VelX# Local VelY# ; 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 PlaySound FireSound End Function Function UpdatePlayer ( This.Player ) Local ThrustX# Local ThrustY# Local SpeedX# Local SpeedY# 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 If GravityEnabled ; Add gravity vector to velocity vector (180 degrees = down) SpeedX = SpeedX + VectorX ( GravityPull , 180 ) SpeedY = SpeedY + VectorY ( GravityPull , 180 ) EndIf ; 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 Function Function DrawPlayer ( This.Player ) DrawImage PlayerImages( 1 , LimitAngle ( This\Angle , 5 ) ) , 0 , 0 End Function Function ToggleGravity () GravityEnabled = Not GravityEnabled 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 ; Use default time to live This\TimeToLive = BulletTime 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 = 6*8 ;------------------------------------------------------------------------------- ; 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 TileStart 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 ) / 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 ) / 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 ;------------------------------------------------------------------------------- ; 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 ;------------------------------------------------------------------------------- ; Current time (updated once every frame) Global MilliSecs ; Game speed limiter Global GameTimer ;------------------------------------------------------------------------------- ; Main execution ;------------------------------------------------------------------------------- Local Angle ; Try the detected screen resolution Graphics ScreenWidth,ScreenHeight , 0,2 ; Sync time MilliSecs = MilliSecs () ThrustSound = LoadSound ( "Thrust.WAV" ) FireSound = LoadSound ( "Fire.WAV" ) LoopSound ThrustSound ; Load pre-rotated player images For Angle = 0 To 360 Step 5 PlayerImages( 1 , Angle ) = LoadImage ( "Ship Images\Ship " + Angle + ".BMP" ) MidHandle PlayerImages( 1 , Angle ) Next ; Load bullet image BulletImage = LoadImage ( "Bullet.BMP" ) MidHandle BulletImage ; Load tiles images TileStrip = LoadAnimImage ( "LogoTiles.BMP" , TileWidth , TileHeight , 0 , TileCount ) ; Load tile map data LoadTilemap ( "Test Tilemap 2.TXT" ) ; Setup player Player = CreatePlayer () ; 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 () ; Main loop Repeat ; Sync time MilliSecs = MilliSecs () ; Control both ships PlayerInput Player ; Update game UpdatePlayer Player UpdateBullets ; Render game Cls Origin ScreenWidth/2,ScreenHeight/2 DrawTilemap Player DrawBullets Player DrawPlayer Player Origin 0,0 Color 128,192,255 Text 0, 0 , "Position:("+Int Player\PosX+","+Int Player\PosY+")" Text 0,10 , "Heading: "+Int Player\Heading Text 0,20 , "Angle: "+Int Player\Angle Text 0,30 , "Speed: "+Int Player\Speed Text 0,50 , "Gravity: "+Mid( "OffOn " , GravityEnabled*3+1 , 3 )+" (Space)" Flip ; Sync game speed WaitTimer GameTimer Until KeyHit ( 1 ) ; Blitz cleans up End ;------------------------------------------------------------------------------- ;~IDEal Editor Parameters: ;~C#Blitz3D198