Guest User

FlappyBird Powershell

a guest
Sep 19th, 2025
25
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PowerShell 16.02 KB | Gaming | 0 0
  1. Add-Type -AssemblyName System.Windows.Forms
  2. Add-Type -AssemblyName System.Drawing
  3.  
  4. # Enhanced form with simple gradient background
  5. $form = New-Object Windows.Forms.Form
  6. $form.Text = 'PowerShell FlappyBird'
  7. $form.Size = New-Object Drawing.Size(1200, 800)
  8. $form.StartPosition = 'CenterScreen'
  9. $form.FormBorderStyle = 'Sizable'
  10. $form.MaximizeBox = $true
  11. $form.MinimizeBox = $true
  12. $form.BackColor = [System.Drawing.Color]::FromArgb(135, 206, 235) # Sky Blue
  13.  
  14. # Game variables
  15. $script:birdY = 300
  16. $script:gravity = 0.8
  17. $script:jumpStrength = -15
  18. $script:velocity = 0
  19. $script:score = 0
  20. $script:highScore = 0
  21. $script:pipeGap = 200  # Increased from 180 for easier gameplay
  22. $script:minPipeGap = 120
  23. $script:pipeSpeed = 4
  24. $script:maxPipeSpeed = 12
  25. $script:pipes = @()
  26. $script:gameRunning = $false
  27. $script:paused = $false
  28. $script:frameCount = 0
  29. $script:lastPipeFrame = 0
  30.  
  31. # Sound functions (with error handling)
  32. function Play-JumpSound {
  33.     try {
  34.         [System.Console]::Beep(800, 100)
  35.     }
  36.     catch {
  37.         # Silent fallback
  38.     }
  39. }
  40.  
  41. function Play-ScoreSound {
  42.     try {
  43.         [System.Console]::Beep(1000, 200)
  44.     }
  45.     catch {
  46.         # Silent fallback
  47.     }
  48. }
  49.  
  50. function Play-GameOverSound {
  51.     try {
  52.         [System.Console]::Beep(300, 500)
  53.     }
  54.     catch {
  55.         # Silent fallback
  56.     }
  57. }
  58.  
  59. # Function to download bird image
  60. function Get-BirdImage {
  61.     try {
  62.         Write-Host "Attempting to download bird image..."
  63.         # Try multiple reliable image URLs
  64.         $imageUrls = @(
  65.             "https://raw.githubusercontent.com/sourabhv/FlapPyBird/master/assets/sprites/yellowbird-midflap.png",
  66.             "https://flappybird.io/img/bird.png",
  67.             "https://github.com/samuelcust/flappy-bird-assets/raw/master/sprites/yellowbird-midflap.png"
  68.         )
  69.        
  70.         $tempPath = [System.IO.Path]::GetTempPath() + "flappy_bird.png"
  71.        
  72.         foreach ($imageUrl in $imageUrls) {
  73.             try {
  74.                 Write-Host "Trying URL: $imageUrl"
  75.                 # Use .NET WebClient with timeout
  76.                 $webClient = New-Object System.Net.WebClient
  77.                 $webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
  78.                 $webClient.DownloadFile($imageUrl, $tempPath)
  79.                 $webClient.Dispose()
  80.                
  81.                 # Verify file was downloaded and has content
  82.                 if ((Test-Path $tempPath) -and ((Get-Item $tempPath).Length -gt 0)) {
  83.                     Write-Host "Successfully downloaded bird image!"
  84.                     # Load and return the image
  85.                     $image = [System.Drawing.Image]::FromFile($tempPath)
  86.                     return $image
  87.                 }
  88.             }
  89.             catch {
  90.                 Write-Host "Failed to download from: $imageUrl - $($_.Exception.Message)"
  91.                 continue
  92.             }
  93.         }
  94.        
  95.         Write-Host "All image URLs failed, using fallback"
  96.         return $null
  97.     }
  98.     catch {
  99.         Write-Host "Error in Get-BirdImage: $($_.Exception.Message)"
  100.         return $null
  101.     }
  102. }
  103.  
  104. # Create enhanced bird with downloaded image
  105. Write-Host "Initializing bird sprite..."
  106. $birdImage = Get-BirdImage
  107. $bird = New-Object Windows.Forms.PictureBox
  108. $bird.Size = New-Object Drawing.Size(40, 30)
  109. $bird.Location = New-Object Drawing.Point(200, $script:birdY)
  110. $bird.SizeMode = 'StretchImage'
  111. $bird.BackColor = [System.Drawing.Color]::Transparent
  112.  
  113. if ($birdImage) {
  114.     Write-Host "Using downloaded bird image!"
  115.     $bird.Image = $birdImage
  116. } else {
  117.     Write-Host "Using fallback bird design"
  118.     # Fallback if image download fails
  119.     $bird.BackColor = [System.Drawing.Color]::Gold
  120.     $bird.BorderStyle = 'None'
  121.    
  122.     # Create a simple bird shape using a label overlay
  123.     $birdLabel = New-Object Windows.Forms.Label
  124.     $birdLabel.Size = New-Object Drawing.Size(40, 30)
  125.     $birdLabel.Location = New-Object Drawing.Point(0, 0)
  126.     $birdLabel.BackColor = [System.Drawing.Color]::Transparent
  127.     $birdLabel.ForeColor = [System.Drawing.Color]::DarkOrange
  128.     $birdLabel.Text = '🐤'
  129.     $birdLabel.Font = New-Object Drawing.Font('Segoe UI Emoji', 16, [Drawing.FontStyle]::Bold)
  130.     $birdLabel.TextAlign = 'MiddleCenter'
  131.    
  132.     # If emoji doesn't work, use arrow
  133.     if (-not $birdLabel.Text -or $birdLabel.Text -eq '🐤') {
  134.         $birdLabel.Text = '▶'
  135.         $birdLabel.Font = New-Object Drawing.Font('Arial', 16, [Drawing.FontStyle]::Bold)
  136.     }
  137.    
  138.     $bird.Controls.Add($birdLabel)
  139. }
  140.  
  141. $form.Controls.Add($bird)
  142.  
  143. # Score display
  144. $scoreLabel = New-Object Windows.Forms.Label
  145. $scoreLabel.Font = New-Object Drawing.Font('Arial', 18, [Drawing.FontStyle]::Bold)
  146. $scoreLabel.ForeColor = [Drawing.Color]::White
  147. $scoreLabel.BackColor = [Drawing.Color]::FromArgb(120, 0, 0, 0) # Semi-transparent black
  148. $scoreLabel.AutoSize = $false
  149. $scoreLabel.Size = New-Object Drawing.Size(350, 60)
  150. $scoreLabel.Location = New-Object Drawing.Point(20, 20)
  151. $scoreLabel.TextAlign = 'MiddleLeft'
  152. $form.Controls.Add($scoreLabel)
  153.  
  154. # Instructions label (just text, no box at all)
  155. $instructionsLabel = New-Object Windows.Forms.Label
  156. $instructionsLabel.Font = New-Object Drawing.Font('Arial', 10, [Drawing.FontStyle]::Bold)
  157. $instructionsLabel.ForeColor = [Drawing.Color]::White
  158. $instructionsLabel.BackColor = [Drawing.Color]::Transparent
  159. $instructionsLabel.AutoSize = $true  # Let it size itself to the text
  160. $instructionsLabel.Location = New-Object Drawing.Point(($form.Width - 200), ($form.Height - 120))
  161. $instructionsLabel.Text = "CONTROLS:`nSPACE - Jump`nESC - Pause/Resume`nR - Restart Game`nQ - Quit Game"
  162. $instructionsLabel.TextAlign = 'TopLeft'
  163. $form.Controls.Add($instructionsLabel)
  164.  
  165. # Countdown display
  166. $countdownLabel = New-Object Windows.Forms.Label
  167. $countdownLabel.Font = New-Object Drawing.Font('Arial', 72, [Drawing.FontStyle]::Bold)
  168. $countdownLabel.ForeColor = [Drawing.Color]::Red
  169. $countdownLabel.BackColor = [Drawing.Color]::Transparent
  170. $countdownLabel.AutoSize = $true
  171. $countdownLabel.TextAlign = 'MiddleCenter'
  172. $form.Controls.Add($countdownLabel)
  173.  
  174. # Pause label
  175. $pauseLabel = New-Object Windows.Forms.Label
  176. $pauseLabel.Font = New-Object Drawing.Font('Arial', 36, [Drawing.FontStyle]::Bold)
  177. $pauseLabel.ForeColor = [Drawing.Color]::Red
  178. $pauseLabel.BackColor = [Drawing.Color]::FromArgb(200, 255, 255, 255)
  179. $pauseLabel.AutoSize = $false
  180. $pauseLabel.Size = New-Object Drawing.Size(600, 80)
  181. $pauseLabel.Text = 'PAUSED - Press ESC to Resume'
  182. $pauseLabel.Visible = $false
  183. $pauseLabel.TextAlign = 'MiddleCenter'
  184. $form.Controls.Add($pauseLabel)
  185.  
  186. # Enhanced pipe creation with better colors and styling
  187. function New-Pipe {
  188.     $gapStart = 200  # Increased from 180 for easier gameplay
  189.     $gapMin = $script:minPipeGap
  190.     $script:pipeGap = [math]::Max($gapMin, $gapStart - [math]::Floor($script:score / 5))
  191.     $maxTop = $form.Height - $script:pipeGap - 150
  192.     $pipeTopHeight = Get-Random -Minimum 80 -Maximum $maxTop
  193.    
  194.     # Top pipe with enhanced styling
  195.     $topPipe = New-Object Windows.Forms.Panel
  196.     $topPipe.Width = 70
  197.     $topPipe.Height = $pipeTopHeight
  198.     $topPipe.Left = $form.Width
  199.     $topPipe.Top = 0
  200.     $topPipe.BackColor = [System.Drawing.Color]::ForestGreen
  201.     $topPipe.BorderStyle = 'None'
  202.    
  203.     # Add pipe cap as a separate control for 3D effect
  204.     $topCap = New-Object Windows.Forms.Panel
  205.     $topCap.Width = 86
  206.     $topCap.Height = 25
  207.     $topCap.Left = $form.Width - 8
  208.     $topCap.Top = $pipeTopHeight - 25
  209.     $topCap.BackColor = [System.Drawing.Color]::DarkGreen
  210.     $topCap.BorderStyle = 'FixedSingle'
  211.  
  212.     # Bottom pipe with enhanced styling
  213.     $bottomPipe = New-Object Windows.Forms.Panel
  214.     $bottomPipe.Width = 70
  215.     $bottomPipe.Height = $form.Height - $pipeTopHeight - $script:pipeGap
  216.     $bottomPipe.Left = $form.Width
  217.     $bottomPipe.Top = $pipeTopHeight + $script:pipeGap
  218.     $bottomPipe.BackColor = [System.Drawing.Color]::ForestGreen
  219.     $bottomPipe.BorderStyle = 'None'
  220.    
  221.     # Add pipe cap as a separate control for 3D effect
  222.     $bottomCap = New-Object Windows.Forms.Panel
  223.     $bottomCap.Width = 86
  224.     $bottomCap.Height = 25
  225.     $bottomCap.Left = $form.Width - 8
  226.     $bottomCap.Top = $pipeTopHeight + $script:pipeGap
  227.     $bottomCap.BackColor = [System.Drawing.Color]::DarkGreen
  228.     $bottomCap.BorderStyle = 'FixedSingle'
  229.  
  230.     $form.Controls.Add($topPipe)
  231.     $form.Controls.Add($topCap)
  232.     $form.Controls.Add($bottomPipe)
  233.     $form.Controls.Add($bottomCap)
  234.  
  235.     return @($topPipe, $bottomPipe, $topCap, $bottomCap)
  236. }
  237.  
  238. function Check-Collision {
  239.     foreach ($pipeSet in $script:pipes) {
  240.         # Only check collision with main pipes (first two elements)
  241.         for ($i = 0; $i -lt 2; $i++) {
  242.             $pipe = $pipeSet[$i]
  243.             # Simple bounds checking - more reliable in PowerShell
  244.             if ($bird.Left -lt ($pipe.Left + $pipe.Width) -and
  245.                 ($bird.Left + $bird.Width) -gt $pipe.Left -and
  246.                 $bird.Top -lt ($pipe.Top + $pipe.Height) -and
  247.                 ($bird.Top + $bird.Height) -gt $pipe.Top) {
  248.                 return $true
  249.             }
  250.         }
  251.     }
  252.     # Check boundaries
  253.     if ($bird.Top -le 0 -or ($bird.Top + $bird.Height) -ge $form.Height) {
  254.         return $true
  255.     }
  256.     return $false
  257. }
  258.  
  259. function Update-LabelPositions {
  260.     $countdownLabel.Location = New-Object Drawing.Point((($form.Width / 2) - 60), ($form.Height / 2) - 50)
  261.     $pauseLabel.Location = New-Object Drawing.Point((($form.Width / 2) - 300), ($form.Height / 2) - 40)
  262.     # Update instructions position on resize (just text, no box)
  263.     $instructionsLabel.Location = New-Object Drawing.Point(($form.Width - 200), ($form.Height - 120))
  264. }
  265.  
  266. function Restart-Game {
  267.     $script:velocity = 0
  268.     $script:birdY = 300
  269.     $script:score = 0
  270.     $script:frameCount = 0
  271.     $script:lastPipeFrame = 0
  272.     $script:pipeSpeed = 4
  273.     $script:paused = $false
  274.     $pauseLabel.Visible = $false
  275.    
  276.     # Clean up pipes (updated for new pipe structure)
  277.     foreach ($pipeSet in $script:pipes) {
  278.         foreach ($pipe in $pipeSet) {
  279.             $form.Controls.Remove($pipe)
  280.             $pipe.Dispose()
  281.         }
  282.     }
  283.     $script:pipes = @()
  284.    
  285.     $bird.Location = New-Object Drawing.Point(200, $script:birdY)
  286.     Update-LabelPositions
  287.     Start-Countdown
  288. }
  289.  
  290. function Start-Countdown {
  291.     $count = 3
  292.     $startTime = Get-Date
  293.     $form.Controls.Add($countdownLabel)
  294.     $countdownLabel.Visible = $true
  295.    
  296.     # Hide score panel during countdown so it doesn't overlay
  297.     $scoreLabel.Visible = $false
  298.  
  299.     while ($form.Visible) {
  300.         $elapsed = (Get-Date) - $startTime
  301.         $seconds = [math]::Floor($elapsed.TotalSeconds)
  302.        
  303.         if ($seconds -le $count) {
  304.             $remaining = $count - $seconds
  305.             if ($remaining -gt 0) {
  306.                 $countdownLabel.Text = "$remaining"
  307.                 try { [System.Console]::Beep(600, 150) } catch { }
  308.             }
  309.         }
  310.         elseif ($seconds -eq ($count + 1)) {
  311.             $countdownLabel.Text = 'GO!'
  312.             try { [System.Console]::Beep(800, 200) } catch { }
  313.         }
  314.         elseif ($seconds -gt ($count + 1.5)) {
  315.             $countdownLabel.Visible = $false
  316.             $scoreLabel.Visible = $true  # Show score panel when game starts
  317.             $script:gameRunning = $true
  318.             break
  319.         }
  320.        
  321.         Start-Sleep -Milliseconds 200
  322.         [System.Windows.Forms.Application]::DoEvents()
  323.     }
  324. }
  325.  
  326. # Event handlers
  327. $form.Add_KeyDown({
  328.     switch ($_.KeyCode) {
  329.         'Space' {
  330.             if ($script:gameRunning -and -not $script:paused) {
  331.                 $script:velocity = $script:jumpStrength
  332.                 Play-JumpSound
  333.             }
  334.         }
  335.         'Escape' {
  336.             if ($script:gameRunning) {
  337.                 $script:paused = -not $script:paused
  338.                 $pauseLabel.Visible = $script:paused
  339.                 Update-LabelPositions
  340.             }
  341.         }
  342.         'R' {
  343.             if (-not $script:gameRunning -or $script:paused) {
  344.                 Restart-Game
  345.             }
  346.         }
  347.         'Q' {
  348.             $form.Close()
  349.         }
  350.     }
  351. })
  352.  
  353. $form.Add_Resize({
  354.     Update-LabelPositions
  355. })
  356.  
  357. # Add close button functionality (updated for new pipe structure and image cleanup)
  358. $form.Add_FormClosing({
  359.     # Clean up resources
  360.     foreach ($pipeSet in $script:pipes) {
  361.         foreach ($pipe in $pipeSet) {
  362.             $pipe.Dispose()
  363.         }
  364.     }
  365.    
  366.     # Clean up bird image if it exists
  367.     if ($bird.Image) {
  368.         $bird.Image.Dispose()
  369.     }
  370.    
  371.     # Clean up temp image file
  372.     $tempPath = [System.IO.Path]::GetTempPath() + "flappy_bird.png"
  373.     if (Test-Path $tempPath) {
  374.         Remove-Item $tempPath -Force -ErrorAction SilentlyContinue
  375.     }
  376. })
  377.  
  378. # Show form and start game
  379. $form.Show()
  380. $form.Focus()
  381. $form.KeyPreview = $true  # Important for key events to work
  382. Update-LabelPositions
  383. Start-Countdown
  384.  
  385. # Main game loop
  386. while ($form.Visible) {
  387.     if ($script:gameRunning -and -not $script:paused) {
  388.         # Bird physics
  389.         $script:velocity += $script:gravity
  390.         $script:birdY += $script:velocity
  391.         $bird.Top = [math]::Max(0, [math]::Min($form.Height - $bird.Height, [math]::Round($script:birdY)))
  392.  
  393.         # Move pipes (updated for new pipe structure)
  394.         foreach ($pipeSet in $script:pipes) {
  395.             foreach ($pipe in $pipeSet) {
  396.                 $pipe.Left -= $script:pipeSpeed
  397.             }
  398.         }
  399.  
  400.         # Remove off-screen pipes (updated for new pipe structure)
  401.         $newPipes = @()
  402.         foreach ($pipeSet in $script:pipes) {
  403.             if (($pipeSet[0].Left + $pipeSet[0].Width) -gt -50) {
  404.                 $newPipes += ,$pipeSet
  405.             } else {
  406.                 # Remove all parts of the pipe set from form and dispose
  407.                 foreach ($pipe in $pipeSet) {
  408.                     $form.Controls.Remove($pipe)
  409.                     $pipe.Dispose()
  410.                 }
  411.             }
  412.         }
  413.         $script:pipes = $newPipes
  414.  
  415.         # Generate new pipes
  416.         if ($script:frameCount - $script:lastPipeFrame -ge 100) {
  417.             $script:pipes += , (New-Pipe)
  418.             $script:lastPipeFrame = $script:frameCount
  419.         }
  420.  
  421.         # Score detection (updated for new pipe structure)
  422.         foreach ($pipeSet in $script:pipes) {
  423.             $pipeRight = $pipeSet[0].Left + $pipeSet[0].Width
  424.             $birdLeft = $bird.Left
  425.            
  426.             # Check if bird just passed the pipe
  427.             if ($pipeRight -lt $birdLeft -and $pipeRight -gt ($birdLeft - $script:pipeSpeed - 5)) {
  428.                 $script:score++
  429.                 Play-ScoreSound
  430.                 if ($script:score -gt $script:highScore) {
  431.                     $script:highScore = $script:score
  432.                 }
  433.                 # Increase difficulty gradually
  434.                 $script:pipeSpeed = [math]::Min($script:maxPipeSpeed, 4 + [math]::Floor($script:score / 10))
  435.                 break
  436.             }
  437.         }
  438.  
  439.         # Update score display
  440.         $scoreLabel.Text = "Score: $script:score  |  High Score: $script:highScore"
  441.  
  442.         # Win condition
  443.         if ($script:score -ge 50) {
  444.             $script:gameRunning = $false
  445.             [System.Windows.Forms.MessageBox]::Show("🎉 Congratulations! You're a Flappy Bird Master!`nFinal Score: $script:score", 'Victory!', 'OK', 'Information')
  446.             $form.Close()
  447.         }
  448.  
  449.         # Collision detection
  450.         if (Check-Collision) {
  451.             $script:gameRunning = $false
  452.             Play-GameOverSound
  453.             $result = [System.Windows.Forms.MessageBox]::Show("Game Over!`nScore: $script:score`nHigh Score: $script:highScore`n`nWould you like to play again?", 'Game Over', 'YesNo', 'Question')
  454.             if ($result -eq 'Yes') {
  455.                 Restart-Game
  456.             }
  457.             else {
  458.                 $form.Close()
  459.             }
  460.         }
  461.  
  462.         $script:frameCount++
  463.     }
  464.  
  465.     Start-Sleep -Milliseconds 30
  466.     [System.Windows.Forms.Application]::DoEvents()
  467. }
Advertisement
Add Comment
Please, Sign In to add comment