Page 1 sur 1

Bombardier

Publié : jeu. 01/janv./2026 2:54
par MetalOS
Voici le code du premier jeu vidéo que j'ai joué dans les années 80. Bon c'est une adaptation de mes souvenir et il manque les sons et quelque options.

Le but et de détruire les immeuble avant de les toucher.

Code : Tout sélectionner

; ============================================
; Jeu de Bombardement - Style Années 80
; By MetalOS
; ============================================

EnableExplicit

; Constantes
#SCREEN_WIDTH = 800
#SCREEN_HEIGHT = 600
#GROUND_HEIGHT = 50
#BUILDING_WIDTH = 30
#MAX_BUILDINGS = 25
#HELICOPTER_SIZE = 20
#BOMB_SIZE = 6
#MAX_BOMBS = 10

; Structures
Structure Building
  x.i
  height.i
  width.i
  currentHeight.i
  fullyDestroyed.i
EndStructure

Structure Helicopter
  x.d
  y.d
  row.i
  speed.d
EndStructure

Structure Bomb
  x.d
  y.d
  active.i
  velocity.d
EndStructure

; Variables globales
Global Dim Buildings.Building(#MAX_BUILDINGS - 1)
Global Dim Bombs.Bomb(#MAX_BOMBS - 1)
Global Heli.Helicopter
Global Window.i, Canvas.i, Font.i, FontBig.i
Global Score.i, BestScore.i
Global GameOver.i, GameWon.i
Global RowHeight.i
Global BuildingsDestroyed.i
Global TotalBuildings.i

; ============================================
; Initialisation de l'hélicoptère
; ============================================
Procedure InitHelicopter()
  Heli\x = #SCREEN_WIDTH - 50
  Heli\y = 50
  Heli\row = 0
  Heli\speed = -3 ; Négatif pour aller de droite à  gauche
  RowHeight = 30
EndProcedure

; ============================================
; Initialisation des immeubles
; ============================================
Procedure InitBuildings()
  Protected i.i, currentX.i = 100
  
  For i = 0 To #MAX_BUILDINGS - 1
    Buildings(i)\x = currentX
    Buildings(i)\height = Random(150) + 100
    Buildings(i)\currentHeight = Buildings(i)\height
    Buildings(i)\width = Random(20) + 25
    Buildings(i)\fullyDestroyed = #False
    currentX + Buildings(i)\width + Random(20) + 20
  Next i
  
  BuildingsDestroyed = 0
  TotalBuildings = #MAX_BUILDINGS
EndProcedure

; ============================================
; Initialisation des bombes
; ============================================
Procedure InitBombs()
  Protected i.i
  For i = 0 To #MAX_BOMBS - 1
    Bombs(i)\active = #False
  Next i
EndProcedure

; ============================================
; Dessiner un immeuble pixelisé
; ============================================
Procedure DrawBuilding(x.i, height.i, width.i, currentHeight.i, fullyDestroyed.i)
  Protected i.i, j.i, windowSize.i = 6
  
  If fullyDestroyed Or currentHeight <= 0
    ProcedureReturn
  EndIf
  
  ; Corps de l'immeuble (hauteur actuelle)
  DrawingMode(#PB_2DDrawing_Default)
  Box(x, #SCREEN_HEIGHT - #GROUND_HEIGHT - currentHeight, width, currentHeight, RGB(50, 50, 50))
  
  ; Fenàªtres
  For i = 1 To (currentHeight / 18) - 1
    For j = 1 To (width / 12) - 1
      If Random(100) > 30
        Box(x + j * 12 - 4, #SCREEN_HEIGHT - #GROUND_HEIGHT - currentHeight + i * 18, windowSize, windowSize, RGB(255, 255, 150))
      Else
        Box(x + j * 12 - 4, #SCREEN_HEIGHT - #GROUND_HEIGHT - currentHeight + i * 18, windowSize, windowSize, RGB(70, 70, 70))
      EndIf
    Next j
  Next i
  
  ; Contour
  DrawingMode(#PB_2DDrawing_Outlined)
  Box(x, #SCREEN_HEIGHT - #GROUND_HEIGHT - currentHeight, width, currentHeight, RGB(0, 0, 0))
EndProcedure

; ============================================
; Dessiner l'hélicoptère pixelisé (orienté vers la gauche)
; ============================================
Procedure DrawHelicopter(x.i, y.i)
  Protected size.i = #HELICOPTER_SIZE
  
  ; Corps principal
  DrawingMode(#PB_2DDrawing_Default)
  Box(x - size, y - size/2, size * 2, size, RGB(200, 50, 50))
  
  ; Cabine
  Box(x - size, y - size/2 + 2, size, size - 4, RGB(120, 120, 180))
  
  ; Queue (à  droite maintenant)
  Box(x + size, y - 3, size, 6, RGB(180, 50, 50))
  
  ; Rotor principal (animation)
  Protected rotorFrame.i = (ElapsedMilliseconds() / 40) % 4
  DrawingMode(#PB_2DDrawing_Default)
  Select rotorFrame
    Case 0, 2
      Line(x - size * 1.5, y - size - 3, size * 3, 2, RGB(100, 100, 100))
    Case 1, 3
      Line(x, y - size * 1.5 - 3, 2, size, RGB(100, 100, 100))
  EndSelect
  
  ; Rotor de queue
  Circle(x + size * 2, y, 4, RGB(100, 100, 100))
  
  ; Contour
  DrawingMode(#PB_2DDrawing_Outlined)
  Box(x - size, y - size/2, size * 2, size, RGB(0, 0, 0))
EndProcedure

; ============================================
; Lancer une bombe
; ============================================
Procedure DropBomb()
  Protected i.i
  
  ; Vérifier qu'il n'y a pas déjà  une bombe active
  For i = 0 To #MAX_BOMBS - 1
    If Bombs(i)\active
      ProcedureReturn ; Ne pas autoriser une nouvelle bombe
    EndIf
  Next i
  
  ; Trouver un slot libre
  For i = 0 To #MAX_BOMBS - 1
    If Not Bombs(i)\active
      Bombs(i)\x = Heli\x
      Bombs(i)\y = Heli\y + #HELICOPTER_SIZE
      Bombs(i)\velocity = 0
      Bombs(i)\active = #True
      ProcedureReturn
    EndIf
  Next i
EndProcedure

; ============================================
; Mise à  jour des bombes
; ============================================
Procedure UpdateBombs()
  Protected i.i, j.i
  Protected gravity.d = 0.4
  Protected bombLeft.i, bombRight.i, bombTop.i, bombBottom.i
  Protected buildingLeft.i, buildingRight.i, buildingTop.i
  Protected damageAmount.i = 30 ; Montant de dégà¢ts par bombe
  
  For i = 0 To #MAX_BOMBS - 1
    If Bombs(i)\active
      ; Appliquer la gravité
      Bombs(i)\velocity + gravity
      Bombs(i)\y + Bombs(i)\velocity
      
      ; Vérifier si la bombe touche le sol
      If Bombs(i)\y >= #SCREEN_HEIGHT - #GROUND_HEIGHT
        Bombs(i)\active = #False
        Continue
      EndIf
      
      ; Vérifier collision avec les immeubles
      bombLeft = Bombs(i)\x - #BOMB_SIZE
      bombRight = Bombs(i)\x + #BOMB_SIZE
      bombTop = Bombs(i)\y - #BOMB_SIZE
      bombBottom = Bombs(i)\y + #BOMB_SIZE
      
      For j = 0 To #MAX_BUILDINGS - 1
        If Not Buildings(j)\fullyDestroyed And Buildings(j)\currentHeight > 0
          buildingLeft = Buildings(j)\x
          buildingRight = Buildings(j)\x + Buildings(j)\width
          buildingTop = #SCREEN_HEIGHT - #GROUND_HEIGHT - Buildings(j)\currentHeight
          
          If bombRight >= buildingLeft And bombLeft <= buildingRight
            If bombBottom >= buildingTop
              ; BOOM ! Réduire la hauteur de l'immeuble
              Buildings(j)\currentHeight - damageAmount
              Bombs(i)\active = #False
              
              ; Vérifier si l'immeuble est complètement détruit
              If Buildings(j)\currentHeight <= 0
                Buildings(j)\currentHeight = 0
                Buildings(j)\fullyDestroyed = #True
                BuildingsDestroyed + 1
                Score + 100
              Else
                Score + 10 ; Petit bonus pour chaque hit
              EndIf
              
              If Score > BestScore
                BestScore = Score
              EndIf
              Break
            EndIf
          EndIf
        EndIf
      Next j
    EndIf
  Next i
EndProcedure

; ============================================
; Mise à  jour de l'hélicoptère
; ============================================
Procedure UpdateHelicopter()
  ; Déplacer l'hélicoptère
  Heli\x + Heli\speed
  
  ; Si l'hélicoptère sort de l'écran à  gauche
  If Heli\x < -50
    Heli\x = #SCREEN_WIDTH + 50
    Heli\row + 1
    Heli\y + RowHeight
    
    ; Augmenter légèrement la vitesse (en valeur absolue)
    If Heli\speed > -6
      Heli\speed - 0.2
    EndIf
  EndIf
EndProcedure

; ============================================
; Vérifier les collisions
; ============================================
Procedure CheckCollisions()
  Protected i.i
  Protected heliLeft.i = Heli\x - #HELICOPTER_SIZE
  Protected heliRight.i = Heli\x + #HELICOPTER_SIZE
  Protected heliTop.i = Heli\y - #HELICOPTER_SIZE/2
  Protected heliBottom.i = Heli\y + #HELICOPTER_SIZE/2
  
  ; Collision avec le sol
  If heliBottom >= #SCREEN_HEIGHT - #GROUND_HEIGHT
    ProcedureReturn #True
  EndIf
  
  ; Collision avec les immeubles
  For i = 0 To #MAX_BUILDINGS - 1
    If Not Buildings(i)\fullyDestroyed And Buildings(i)\currentHeight > 0
      If heliRight > Buildings(i)\x And heliLeft < Buildings(i)\x + Buildings(i)\width
        If heliBottom > #SCREEN_HEIGHT - #GROUND_HEIGHT - Buildings(i)\currentHeight
          ProcedureReturn #True
        EndIf
      EndIf
    EndIf
  Next i
  
  ProcedureReturn #False
EndProcedure

; ============================================
; Dessiner le jeu
; ============================================
Procedure DrawGame()
  Protected i.i
  
  If StartDrawing(CanvasOutput(Canvas))
    ; Ciel
    DrawingMode(#PB_2DDrawing_Default)
    Box(0, 0, #SCREEN_WIDTH, #SCREEN_HEIGHT - #GROUND_HEIGHT, RGB(135, 206, 235))
    
    ; Sol avec damier
    For i = 0 To #SCREEN_WIDTH / 20
      If (i % 2) = 0
        Box(i * 20, #SCREEN_HEIGHT - #GROUND_HEIGHT, 20, #GROUND_HEIGHT, RGB(100, 200, 100))
      Else
        Box(i * 20, #SCREEN_HEIGHT - #GROUND_HEIGHT, 20, #GROUND_HEIGHT, RGB(80, 180, 80))
      EndIf
    Next i
    
    ; Dessiner les immeubles
    For i = 0 To #MAX_BUILDINGS - 1
      DrawBuilding(Buildings(i)\x, Buildings(i)\height, Buildings(i)\width, Buildings(i)\currentHeight, Buildings(i)\fullyDestroyed)
    Next i
    
    ; Dessiner les bombes
    DrawingMode(#PB_2DDrawing_Default)
    For i = 0 To #MAX_BOMBS - 1
      If Bombs(i)\active
        ; Bombe (cercle noir)
        Circle(Bombs(i)\x, Bombs(i)\y, #BOMB_SIZE, RGB(0, 0, 0))
        ; Aileron
        Line(Bombs(i)\x - 3, Bombs(i)\y - 8, 6, 0, RGB(50, 50, 50))
      EndIf
    Next i
    
    ; Dessiner l'hélicoptère
    If Not GameOver And Not GameWon
      DrawHelicopter(Heli\x, Heli\y)
    ElseIf GameOver
      ; Explosion
      DrawingMode(#PB_2DDrawing_Default)
      Circle(Heli\x, Heli\y, 40, RGB(255, 100, 0))
      Circle(Heli\x, Heli\y, 30, RGB(255, 200, 0))
      Circle(Heli\x, Heli\y, 20, RGB(255, 255, 100))
    EndIf
    
    ; Afficher le score et les stats
    DrawingMode(#PB_2DDrawing_Transparent)
    DrawingFont(FontID(Font))
    DrawText(20, 20, "SCORE " + Str(Score), RGB(0, 0, 0))
    DrawText(#SCREEN_WIDTH - 220, 20, "MEILLEUR " + Str(BestScore), RGB(0, 0, 0))
    DrawText(20, 50, "BÂTIMENTS: " + Str(BuildingsDestroyed) + "/" + Str(TotalBuildings), RGB(0, 0, 0))
    
    ; Message Game Over
    If GameOver
      DrawingFont(FontID(FontBig))
      Protected textWidth.i = TextWidth("GAME OVER")
      DrawText((#SCREEN_WIDTH - textWidth) / 2, #SCREEN_HEIGHT / 2 - 60, "GAME OVER", RGB(255, 0, 0))
      
      DrawingFont(FontID(Font))
      Protected restartText.s = "Appuyez sur ESPACE pour redémarrer"
      textWidth = TextWidth(restartText)
      DrawText((#SCREEN_WIDTH - textWidth) / 2, #SCREEN_HEIGHT / 2, restartText, RGB(0, 0, 0))
    EndIf
    
    ; Message Victory
    If GameWon
      DrawingFont(FontID(FontBig))
      textWidth = TextWidth("VICTOIRE!")
      DrawText((#SCREEN_WIDTH - textWidth) / 2, #SCREEN_HEIGHT / 2 - 60, "VICTOIRE!", RGB(0, 200, 0))
      
      DrawingFont(FontID(Font))
      restartText.s = "Tous les bâtiments sont détruits ! Appuyez sur ESPACE pour rejouer."
      textWidth = TextWidth(restartText)
      DrawText((#SCREEN_WIDTH - textWidth) / 2, #SCREEN_HEIGHT / 2, restartText, RGB(0, 0, 0))
    EndIf
    
    StopDrawing()
  EndIf
EndProcedure

; ============================================
; Réinitialiser le jeu
; ============================================
Procedure ResetGame()
  InitHelicopter()
  InitBuildings()
  InitBombs()
  Score = 0
  GameOver = #False
  GameWon = #False
EndProcedure

; ============================================
; Programme principal
; ============================================

; Initialiser
BestScore = 0

; Créer la fenàªtre
Window = OpenWindow(#PB_Any, 0, 0, #SCREEN_WIDTH, #SCREEN_HEIGHT, "Bombardier - Années 80", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

If Window
  Canvas = CanvasGadget(#PB_Any, 0, 0, #SCREEN_WIDTH, #SCREEN_HEIGHT)
  
  ; Charger les polices
  Font = LoadFont(#PB_Any, "Courier New", 18, #PB_Font_Bold)
  If Not Font
    Font = LoadFont(#PB_Any, "", 18, #PB_Font_Bold)
  EndIf
  
  FontBig = LoadFont(#PB_Any, "Courier New", 40, #PB_Font_Bold)
  If Not FontBig
    FontBig = LoadFont(#PB_Any, "", 40, #PB_Font_Bold)
  EndIf
  
  ; Ajouter les raccourcis clavier
  AddKeyboardShortcut(Window, #PB_Shortcut_Escape, 1)
  AddKeyboardShortcut(Window, #PB_Shortcut_Space, 2)
  
  ; Initialiser le jeu
  ResetGame()
  
  ; Timer pour l'animation (60 FPS)
  AddWindowTimer(Window, 1, 16)
  
  ; Boucle principale
  Define Event.i, quit.i = 0
  Define spacePressed.i = 0, lastSpaceState.i = 0
  Define currentSpaceState.i
  
  Repeat
    Event = WaitWindowEvent(10)
    
    Select Event
      Case #PB_Event_CloseWindow
        quit = 1
        
      Case #PB_Event_Timer
        If EventTimer() = 1
          If Not GameOver And Not GameWon
            UpdateHelicopter()
            UpdateBombs()
            
            ; Vérifier les collisions
            If CheckCollisions()
              GameOver = #True
            EndIf
            
            ; Vérifier la victoire
            If BuildingsDestroyed >= TotalBuildings
              GameWon = #True
            EndIf
          EndIf
          
          DrawGame()
        EndIf
        
      Case #PB_Event_Menu
        Select EventMenu()
          Case 1 ; à‰chap
            quit = 1
          Case 2 ; Espace
            If GameOver Or GameWon
              ResetGame()
            Else
              ; Larguer une bombe
              spacePressed = 1
            EndIf
        EndSelect
    EndSelect
    
    ; Détecter l'appui sur Espace pour larguer une bombe
    If Not GameOver And Not GameWon
      currentSpaceState = GetAsyncKeyState_(#VK_SPACE) & $8000
      
      If currentSpaceState And Not lastSpaceState
        DropBomb()
      EndIf
      
      lastSpaceState = currentSpaceState
      
      If spacePressed
        DropBomb()
        spacePressed = 0
      EndIf
    EndIf
    
  Until quit
  
  ; Nettoyage
  RemoveWindowTimer(Window, 1)
  
  If Font
    FreeFont(Font)
  EndIf
  If FontBig
    FreeFont(FontBig)
  EndIf
EndIf

End

Re: Bombardier

Publié : jeu. 01/janv./2026 15:30
par Bruce33
Bonjour,

Merci pour ce petit jeu qui me rappelle effectivement ma période MO5 et le jeu Blitz, à une époque où je recopiais (bêtement) des codes de jeux dans les livres et les magazines.
C'est plus joli que sur MO5 !
Mais avec le recul et l'âge, je trouve que le thème de "lâcher des bombes sur des immeubles" ne devrait plus faire l'objet d'un "jeu".
Avec un peu d'imagination, on doit pouvoir trouver un thème plus positif, non ?

Sur Linux, ça n'a pas marché du premier coup à cause de l'instruction de l'API Windows GetAsyncKeyState_().
Mais j'ai remarqué qu'il y avait une double gestion de l'appui sur la touche espace. J'ai mis en commentaire les lignes inefficaces sur Linux et le programme a fonctionné :

Code : Tout sélectionner

    ; Détecter l'appui sur Espace pour larguer une bombe
    If Not GameOver And Not GameWon
;       currentSpaceState = GetAsyncKeyState_(#VK_SPACE) & $8000
;       
;       If currentSpaceState And Not lastSpaceState
;         DropBomb()
;       EndIf
;       
;       lastSpaceState = currentSpaceState
      
      If spacePressed
        DropBomb()
        spacePressed = 0
      EndIf
    EndIf

Re: Bombardier

Publié : jeu. 01/janv./2026 17:58
par MetalOS
Le jeu n'a pas de connotation particulière même si c'est des immeubles, le but était d'essayer de refaire dans le même genre. Libre à toi de le modifier pour en faire un autre genre. C'est des souvenirs d'une bonne époque.