QB64 Phoenix Edition
Mini Space Invaders - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1)
+--- Forum: Code and Stuff (https://qb64phoenix.com/forum/forumdisplay.php?fid=3)
+---- Forum: Works in Progress (https://qb64phoenix.com/forum/forumdisplay.php?fid=9)
+---- Thread: Mini Space Invaders (/showthread.php?tid=3928)

Pages: 1 2


Mini Space Invaders - Pete - 09-11-2025

Inspired by Mark (bplus) I decided to come out of retirement to fiddle with this...

Code: (Select All)
_Title "Pete's Mini Space Invaders"
Width 40, 20: _Font 16: bit = 1: TopRow = 4: a$ = "A A A A A A A A A A A A": For i = 0 To 4: a$(i) = a$: Next
trail = _Width \ 2 - Len(a$) \ 2: ms = .05: lag = .35: xbase = _Width \ 2: RowHeight = 5: terminate = _Height
Do
    Cls: Locate 1, 1: Print " Altitude ="; _Height * 1000 - (TopRow - 1 + RowHeight) * 1000; "  Score ="; Score;
    Locate _Height, xbase: Print Chr$(234);: z1 = Timer: zenith = 0
    For i = 0 To RowHeight - 1
        Locate TopRow + i, trail + l: Print RTrim$(Mid$(a$(i), l + 1));
        If Len(LTrim$(a$(i))) And zenith = 0 Then zenith = TopRow + i
    Next
    Sound 500, .1
    Do
        If yfire Then
            If Abs(z2 - Timer) > ms Then
                z2 = Timer
                If Screen(yfire, xfire) = 24 Then Locate yfire, xfire: Print " ";: yfire = yfire - 1
                If Screen(yfire, xfire) = 65 Then
                    RowCheck = 0: l = 0: r = 0: Score = Score + 2500
                    Mid$(a$(yfire - TopRow), xfire - trail + 1, 1) = " ": Sound 1000, .5: yfire = 0
                    For i = 4 To 0 Step -1
                        If Len(LTrim$(a$(i))) And RowCheck = 0 Then RowHeight = i + 1: RowCheck = 1
                        If Len(RTrim$(a$(i))) > r Then r = Len(RTrim$(a$(i)))
                        If Len(LTrim$(a$(i))) > l Then l = Len(LTrim$(a$(i)))
                    Next
                    r = Len(a$) - r: l = Len(a$) - l
                Else
                    If yfire < zenith Then yfire = 0 Else Locate yfire, xfire: Print Chr$(24);
                End If
            End If
        End If
        Select Case _KeyHit
            Case 19200
                If xbase > 1 And restrict = 0 Then xbase = xbase - 1: restrict = 1
            Case 19712
                If xbase < _Width And restrict = 0 Then xbase = xbase + 1: restrict = 1
            Case -19200, -19712
                restrict = 0: _KeyClear
            Case 32
                If yfire = 0 Then yfire = _Height: xfire = xbase
        End Select
        If Abs(z1 - Timer) > lag Then z1 = Timer: Exit Do
    Loop
    If LTrim$(a$(0) + a$(1) + a$(2) + a$(3) + a$(4)) = "" Then
        View Print 1 To _Height - 1: Cls 2: Print " Altitude ="; _Height * 1000 - (TopRow - 1 + RowHeight) * 1000; "  Score ="; Score;
        Locate 3, 2: Print "GAME OVER - YOU WON!": _Delay 5: Exit Do
    End If
    If TopRow - 1 + RowHeight = terminate Then Locate 3, 2: Print "GAME OVER - YOU LOST!": _Delay 5: Exit Do
    If trail + Len(a$) - r > _Width And bit = 1 Or trail = 1 - l And bit = -1 Then
        bit = -bit: TopRow = TopRow + 1: lag = lag - .02
        If lag < .1 Then lag = .1: ms = .025
    Else
        trail = trail + bit
    End If
Loop

It's beatable, but it's not easy!

Tap lt/rt arrow keys repeatedly to move. Holding down keys is death!

Press space bar to fire. Another missile is ready when previous missile either hits a target or vanishes. Missiles vanish when they are past the uppermost line of ships.

It has some good points and bad point like it is scalable but if you messed a lot with the repeat rates, you'd see that the structure is dependent on the timing of events. It could be made better if events were totally independent. Also, for a bigger version, it would be better to have subroutines for the aliens, base, missiles, and scoring.

Pete


RE: Mini Space Invaders - Pete - 09-12-2025

Okay, pretty much the same thing, but now with more independence and in sub procedures...

Code: (Select All)
_Title "Pete's Mini Space Invaders"
Width 40, 20: _Font 16
s$ = "A A A A A A A A A A A A": For i = 0 To 4: a$(i) = s$: Next
bit = 1
TopRow = 4
RowHeight = 5
terminate = _Height
trail = _Width \ 2 - Len(s$) \ 2
ms = .05
lag = .35
xbase = _Width \ 2
Refresh TopRow, RowHeight, trail, l, zenith, xbase, a$()
Do
    scoreKeeper score
    If yfire <> 0 And Abs(z2 - Timer) > ms Then MissileAction z2, yfire, xfire, xbase, TopRow, RowHeight, trail, zenith, l, r, outcome, score, s$, a$()
    poll xbase, BaseActive, yfire
    If Abs(z1 - Timer) > lag Then MoveAliens z1, xbase, TopRow, RowHeight, trail, zenith, l, r, terminate, outcome, ms, bit, lag, s$, a$()
    If BaseActive Then BaseAction xbase, BaseActive, yfire, xfire
    If outcome Then finish outcome: _Delay 5: Exit Do
Loop

Sub scoreKeeper (score)
    Locate 1, 1: Print " Altitude ="; _Height * 1000 - (TopRow - 1 + RowHeight) * 1000; "  Score ="; score;
End Sub

Sub poll (xbase, BaseActive, yfire)
    Static restrict
    Select Case _KeyHit
        Case 19200: If xbase > 1 And restrict = 0 Then xbase = xbase - 1: restrict = 1: BaseActive = -1
        Case 19712: If xbase < _Width And restrict = 0 Then xbase = xbase + 1: restrict = 1: BaseActive = -1
        Case -19200, -19712: restrict = 0: _KeyClear:: BaseActive = 0
        Case 32: If yfire = 0 Then BaseActive = 1
    End Select
End Sub

Sub MissileAction (z2, yfire, xfire, xbase, TopRow, RowHeight, trail, zenith, l, r, outcome, score, s$, a$())
    z2 = Timer
    If Screen(yfire, xfire) = 24 Then Locate yfire, xfire: Print " ";
    yfire = yfire - 1
    If Screen(yfire, xfire) = 65 Then
        RowCheck = 0: l = 0: r = 0: score = score + 2500
        Locate yfire, xfire: Print Chr$(15);: _Delay .1: Locate yfire, xfire: Print " ";
        Mid$(a$(yfire - TopRow), xfire - trail + 1, 1) = " ": yfire = 0: Sound 1000, .5
        For i = 4 To 0 Step -1
            If Len(LTrim$(a$(i))) And RowCheck = 0 Then RowHeight = i + 1: RowCheck = 1
            If Len(RTrim$(a$(i))) > r Then r = Len(RTrim$(a$(i)))
            If Len(LTrim$(a$(i))) > l Then l = Len(LTrim$(a$(i)))
        Next
        r = Len(s$) - r: l = Len(s$) - l
        Refresh TopRow, RowHeight, trail, l, zenith, xbase, a$()
        If LTrim$(a$(0) + a$(1) + a$(2) + a$(3) + a$(4)) = "" Then outcome = 1 ' You won!
    Else
        If yfire < zenith Then yfire = 0 Else Locate yfire, xfire: Print Chr$(24);
    End If
End Sub

Sub MoveAliens (z1, xbase, TopRow, RowHeight, trail, zenith, l, r, terminate, outcome, ms, bit, lag, s$, a$())
    z1 = Timer
    If TopRow - 1 + RowHeight = terminate Then
        outcome = -1
    Else
        If trail + Len(s$) - r > _Width And bit = 1 Or trail = 1 - l And bit = -1 Then
            bit = -bit: TopRow = TopRow + 1: lag = lag - .02
            If lag < .1 Then lag = .1: ms = .025
        Else
            trail = trail + bit
        End If
    End If
    Refresh TopRow, RowHeight, trail, l, zenith, xbase, a$()
    Sound 500, .1
End Sub

Sub Refresh (TopRow, RowHeight, trail, l, zenith, xbase, a$())
    View Print TopRow - 1 To TopRow + RowHeight - 1: zenith = 0: Cls 2
    For i = 0 To RowHeight - 1
        Locate TopRow + i, trail + l
        Print RTrim$(Mid$(a$(i), l + 1));
        If Len(LTrim$(a$(i))) And zenith = 0 Then zenith = TopRow + i
    Next
    View Print
    Locate _Height, xbase: Print Chr$(234);
End Sub

Sub BaseAction (xbase, BaseActive, yfire, xfire)
    Select Case BaseActive
        Case -1
            Locate _Height, 1: Print Space$(_Width);
            Locate _Height, xbase: Print Chr$(234);
        Case 1
            yfire = _Height: xfire = xbase
    End Select
    BaseActive = 0
End Sub

Sub finish (outcome)
    Select Case outcome
        Case -1
            Locate 3, 2: Print "GAME OVER - YOU LOST!"
        Case 1
            View Print 1 To _Height - 1: Cls 2: Print " Altitude ="; _Height * 1000 - (TopRow - 1 + RowHeight) * 1000; "  Score ="; score;
            Locate 3, 2: Print "GAME OVER - YOU WON!"
    End Select
End Sub



RE: Mini Space Invaders - Pete - 09-12-2025

And now with TYPE variables...

Code: (Select All)
_Title "Pete's Mini Space Invaders"
Width 40, 20: _Font 16
Type invaders
    bit As Integer
    TopRow As Integer
    RowHeight As Integer
    terminate As Integer
    trail As Integer
    ms As Single
    lag As Single
    xbase As Integer
    BaseActive As Integer
    zenith As Integer
    l As Integer
    r As Integer
    z1 As Single
    z2 As Single
    yfire As Integer
    xfire As Integer
    outcome As Integer
    score As _Integer64
    array As String
End Type
Dim si As invaders
si.array = "A A A A A A A A A A A A": For i = 0 To 4: a$(i) = si.array: Next
si.bit = 1
si.TopRow = 4
si.RowHeight = 5
si.terminate = _Height
si.trail = _Width \ 2 - Len(si.array) \ 2
si.ms = .05
si.lag = .35
si.xbase = _Width \ 2
Refresh si, a$()
Do
    ScoreKeeper si
    If si.yfire <> 0 And Abs(si.z2 - Timer) > si.ms Then MissileAction si, a$()
    poll si
    If Abs(si.z1 - Timer) > si.lag Then MoveAliens si, a$()
    If si.BaseActive Then BaseAction si
    If si.outcome Then finish si: _Delay 5: Exit Do
Loop

Sub ScoreKeeper (si As invaders)
    info$ = Space$(_Width)
    ' x$ = "Altitude:" + Str$(_Height * 1000 - (si.TopRow - 1 + si.RowHeight) * 1000) + " Ft  Score:" + Str$(si.score)
    ' i = _Width \ 2 - Len(x$) \ 2
    ' Mid$(info$, i) = x$
    ' Locate 1, 1: Print info$;
    Locate 1, 2: Print "Altitude:"; _Height * 1000 - (si.TopRow - 1 + si.RowHeight) * 1000; "Ft    ";
    Locate 1, _Width - 15: Print "Score:"; si.score;
End Sub

Sub poll (si As invaders)
    Static restrict
    Select Case _KeyHit
        Case 19200: If si.xbase > 1 And restrict = 0 Then si.xbase = si.xbase - 1: restrict = 1: si.BaseActive = -1
        Case 19712: If si.xbase < _Width And restrict = 0 Then si.xbase = si.xbase + 1: restrict = 1: si.BaseActive = -1
        Case -19200, -19712: restrict = 0: _KeyClear: si.BaseActive = 0
        Case 32: If si.yfire = 0 Then si.BaseActive = 1
    End Select
End Sub

Sub MissileAction (si As invaders, a$())
    si.z2 = Timer
    If Screen(si.yfire, si.xfire) = 24 Then Locate si.yfire, si.xfire: Print " ";
    si.yfire = si.yfire - 1
    If Screen(si.yfire, si.xfire) = 65 Then
        RowCheck = 0: si.l = 0: si.r = 0: si.score = si.score + 2500
        Locate si.yfire, si.xfire: Print Chr$(15);: _Delay .1: Locate si.yfire, si.xfire: Print " ";
        Mid$(a$(si.yfire - si.TopRow), si.xfire - si.trail + 1, 1) = " ": si.yfire = 0: Sound 1000, .5
        For i = 4 To 0 Step -1
            If Len(LTrim$(a$(i))) And RowCheck = 0 Then si.RowHeight = i + 1: RowCheck = 1
            If Len(RTrim$(a$(i))) > si.r Then si.r = Len(RTrim$(a$(i)))
            If Len(LTrim$(a$(i))) > si.l Then si.l = Len(LTrim$(a$(i)))
        Next
        si.r = Len(si.array) - si.r: si.l = Len(si.array) - si.l
        Refresh si, a$()
        If LTrim$(a$(0) + a$(1) + a$(2) + a$(3) + a$(4)) = "" Then si.outcome = 1 ' You won!
    Else
        If si.yfire < si.zenith Then si.yfire = 0 Else Locate si.yfire, si.xfire: Print Chr$(24);
    End If
End Sub

Sub MoveAliens (si As invaders, a$())
    si.z1 = Timer
    If si.TopRow - 1 + si.RowHeight = si.terminate Then
        si.outcome = -1
    Else
        If si.trail + Len(si.array) - si.r > _Width And si.bit = 1 Or si.trail = 1 - si.l And si.bit = -1 Then
            si.bit = -si.bit: si.TopRow = si.TopRow + 1: si.lag = si.lag - .02
            If si.lag < .1 Then si.lag = .1: si.ms = .025
        Else
            si.trail = si.trail + si.bit
        End If
    End If
    Refresh si, a$()
    Sound 500, .1
End Sub

Sub Refresh (si As invaders, a$())
    View Print si.TopRow - 1 To si.TopRow + si.RowHeight - 1: si.zenith = 0: Cls 2
    For i = 0 To si.RowHeight - 1
        Locate si.TopRow + i, si.trail + si.l
        Print RTrim$(Mid$(a$(i), si.l + 1));
        If Len(LTrim$(a$(i))) And si.zenith = 0 Then si.zenith = si.TopRow + i
    Next
    View Print
    Locate _Height, si.xbase: Print Chr$(234);
End Sub

Sub BaseAction (si As invaders)
    Select Case si.BaseActive
        Case -1
            Locate _Height, 1: Print Space$(_Width);
            Locate _Height, si.xbase: Print Chr$(234);
        Case 1
            si.yfire = _Height: si.xfire = si.xbase
    End Select
    si.BaseActive = 0
End Sub

Sub finish (si As invaders)
    Select Case si.outcome
        Case -1
            Locate 3, 2: Print "GAME OVER - YOU LOST!"
        Case 1
            ScoreKeeper si: Locate 3, 2: Print "GAME OVER - YOU WON!"
    End Select
End Sub

This got me thinking how using TYPE variables lengthens the code of a short routine, but if you were to substantially add to a project, it would pay off in the end.

I wonder if we have coders here who are so meticulous they try or actually achieve separate type variables so only those needed in a sub get passed to the sub? The IDE will give warnings if you pass variables that are not used, but this is not the case when using TYPE variables.

Pete


RE: Mini Space Invaders - Pete - 09-12-2025

And what I usually do to finish up a stage of a project, improve the names and make a few minor changes...

Code: (Select All)
_Title "Pete's Mini Space Invaders"
Width 40, 20: _Font 16
Type invaders
    Direction As Integer
    TopRow As Integer
    RowHeight As Integer
    BaseLine As Integer
    TrailColumn As Integer
    MissileSpeed As Single
    AlienSpeed As Single
    HomeBase As Integer
    BaseAction As Integer
    TrailRow As Integer
    LtColReduce As Integer
    RtColReduce As Integer
    z1 As Single
    z2 As Single
    yfire As Integer
    xfire As Integer
    Outcome As Integer
    Score As _Integer64
    Array As String
End Type
Dim si As invaders
si.Array = "A A A A A A A A A A A A": For i = 0 To 4: a$(i) = si.Array: Next
si.Direction = 1
si.TopRow = 4
si.RowHeight = 5
si.BaseLine = _Height
si.TrailColumn = _Width \ 2 - Len(si.Array) \ 2
si.MissileSpeed = .05
si.AlienSpeed = .325
si.HomeBase = _Width \ 2
Refresh si, a$(): _Delay 1
Do
    ScoreKeeper si
    If si.yfire <> 0 And Abs(si.z2 - Timer) > si.MissileSpeed Then MissileAction si, a$()
    If si.Outcome = 0 Then
        KeyBoard si
        If Abs(si.z1 - Timer) > si.AlienSpeed Then MoveAliens si, a$()
        If si.BaseAction Then BaseAction si
    Else
        Finish si
    End If
Loop

Sub ScoreKeeper (si As invaders)
    info$ = Space$(_Width)
    x$ = " Altitude:" + Str$(_Height * 1000 - (si.TopRow - 1 + si.RowHeight) * 1000) + " Ft  Score:" + Str$(si.Score)
    i = _Width / 2 - Len(x$) / 2
    Mid$(info$, i) = x$
    Locate 1, 1: Print info$;
End Sub

Sub KeyBoard (si As invaders)
    Static restrict
    Select Case _KeyHit
        Case 19200: If si.HomeBase > 1 And restrict = 0 Then si.HomeBase = si.HomeBase - 1: restrict = 1: si.BaseAction = -1
        Case 19712: If si.HomeBase < _Width And restrict = 0 Then si.HomeBase = si.HomeBase + 1: restrict = 1: si.BaseAction = -1
        Case -19200, -19712: restrict = 0: _KeyClear: si.BaseAction = 0
        Case 32: If si.yfire = 0 Then si.BaseAction = 1
    End Select
End Sub

Sub MissileAction (si As invaders, a$())
    si.z2 = Timer
    If Screen(si.yfire, si.xfire) = 24 Then Locate si.yfire, si.xfire: Print " ";
    si.yfire = si.yfire - 1
    If Screen(si.yfire, si.xfire) = 65 Then
        RowCheck = 0: si.LtColReduce = 0: si.RtColReduce = 0: si.Score = si.Score + 2500
        Locate si.yfire, si.xfire: Print Chr$(15);: _Delay .1: Locate si.yfire, si.xfire: Print " ";
        Mid$(a$(si.yfire - si.TopRow), si.xfire - si.TrailColumn + 1, 1) = " ": si.yfire = 0: Sound 1000, .5
        For i = 4 To 0 Step -1
            If Len(LTrim$(a$(i))) And RowCheck = 0 Then si.RowHeight = i + 1: RowCheck = 1
            If Len(RTrim$(a$(i))) > si.RtColReduce Then si.RtColReduce = Len(RTrim$(a$(i)))
            If Len(LTrim$(a$(i))) > si.LtColReduce Then si.LtColReduce = Len(LTrim$(a$(i)))
        Next
        si.RtColReduce = Len(si.Array) - si.RtColReduce: si.LtColReduce = Len(si.Array) - si.LtColReduce
        If LTrim$(a$(0) + a$(1) + a$(2) + a$(3) + a$(4)) = "" Then si.Outcome = 1 Else Refresh si, a$()
    Else
        If si.yfire < si.TrailRow Then si.yfire = 0 Else Locate si.yfire, si.xfire: Print Chr$(24);
    End If
End Sub

Sub MoveAliens (si As invaders, a$())
    si.z1 = Timer
    If si.TopRow - 1 + si.RowHeight = si.BaseLine Then
        si.Outcome = -1
    Else
        If si.TrailColumn + Len(si.Array) - si.RtColReduce > _Width And si.Direction = 1 Or si.TrailColumn = 1 - si.LtColReduce And si.Direction = -1 Then
            si.Direction = -si.Direction: si.TopRow = si.TopRow + 1: si.AlienSpeed = si.AlienSpeed - .02
            If si.AlienSpeed < .1 Then si.AlienSpeed = .1: si.MissileSpeed = .025
        Else
            si.TrailColumn = si.TrailColumn + si.Direction
        End If
    End If
    Refresh si, a$()
    Sound 500, .1
End Sub

Sub Refresh (si As invaders, a$())
    View Print si.TopRow - 1 To si.TopRow + si.RowHeight - 1: si.TrailRow = 0: Cls 2
    For i = 0 To si.RowHeight - 1
        Locate si.TopRow + i, si.TrailColumn + si.LtColReduce
        Print RTrim$(Mid$(a$(i), si.LtColReduce + 1));
        If Len(LTrim$(a$(i))) And si.TrailRow = 0 Then si.TrailRow = si.TopRow + i
    Next
    View Print
    Locate _Height, si.HomeBase: Print Chr$(234);
End Sub

Sub BaseAction (si As invaders)
    Select Case si.BaseAction
        Case -1
            Locate _Height, 1: Print Space$(_Width);
            Locate _Height, si.HomeBase: Print Chr$(234);
        Case 1
            si.yfire = _Height: si.xfire = si.HomeBase
    End Select
    si.BaseAction = 0
End Sub

Sub Finish (si As invaders)
    ScoreKeeper si: Locate 3, 2
    Select Case si.Outcome
        Case -1: x$ = "GAME OVER - YOU LOST!": Locate 3, _Width / 2 - Len(x$) / 2
        Case 1: x$ = "GAME OVER - YOU WON!": Locate 3, _Width / 2 - Len(x$) / 2
    End Select
    Print x$;: x$ = "Replay? Y/N": Locate 5, _Width / 2 - Len(x$) / 2: Print x$;
    Do: _Limit 10: b$ = InKey$
        If UCase$(b$) = "Y" Then Cls: Run
        If UCase$(b$) = "N" Then _Delay .5: Cls: Print " Bye!": _Delay 1.5: System
    Loop
End Sub

Beta testing was actually fun on this one. It's fairly addictive. Now that this stage is done, I wonder if I'll get around to adding returned fire, saucer fly overs, and levels.

Pete


RE: Mini Space Invaders - Pete - 09-12-2025

You want the aliens to shoot back. Okay, here you go...

Code: (Select All)
_Title "Pete's Mini Space Invaders"
Width 40, 20: _Font 16
Type invaders
Direction As Integer
TopRow As Integer
RowHeight As Integer
BaseLine As Integer
TrailColumn As Integer
MissileSpeed As Single
AlienSpeed As Single
HomeBase As Integer
BaseAction As Integer
TrailRow As Integer
LtColReduce As Integer
RtColReduce As Integer
z1 As Single
z2 As Single
z3 As Single
yfire As Integer
xfire As Integer
ybomb As Integer
xbomb As Integer
Outcome As Integer
Score As _Integer64
Array As String
End Type
Dim si As invaders
si.Array = "A A A A A A A A A A A A": For i = 0 To 4: a$(i) = si.Array: Next
si.Direction = 1
si.TopRow = 4
si.RowHeight = 5
si.BaseLine = _Height
si.TrailColumn = _Width \ 2 - Len(si.Array) \ 2
si.MissileSpeed = .04
si.AlienSpeed = .375
si.HomeBase = _Width \ 2
Refresh si, a$(): _Delay 1
Do
ScoreKeeper si
If si.yfire <> 0 And Abs(si.z2 - Timer) > si.MissileSpeed Then MissileAction si, a$()
If si.Outcome = 0 Then
KeyBoard si
If Abs(si.z1 - Timer) > si.AlienSpeed Then MoveAliens si, a$()
If Abs(si.z3 - Timer) > 1 Or si.ybomb And Abs(si.z3 - Timer) > .15 Then AlienBomb si, a$()
If si.BaseAction Then BaseAction si
Else
Finish si, a$()
End If
Loop

Sub ScoreKeeper (si As invaders)
info$ = Space$(_Width)
x$ = " Altitude:" + Str$(_Height * 1000 - (si.TopRow - 1 + si.RowHeight) * 1000) + " Ft Score:" + Str$(si.Score)
i = _Width / 2 - Len(x$) / 2
Mid$(info$, i) = x$
Locate 1, 1: Print info$;
End Sub

Sub KeyBoard (si As invaders)
Static restrict
Select Case _KeyHit
Case 19200: If si.HomeBase > 1 And restrict = 0 Then si.HomeBase = si.HomeBase - 1: restrict = 1: si.BaseAction = -1
Case 19712: If si.HomeBase < _Width And restrict = 0 Then si.HomeBase = si.HomeBase + 1: restrict = 1: si.BaseAction = -1
Case -19200, -19712: restrict = 0: _KeyClear: si.BaseAction = 0
Case 32: If si.yfire = 0 Then si.BaseAction = 1
End Select
End Sub

Sub MissileAction (si As invaders, a$())
si.z2 = Timer
If Screen(si.yfire, si.xfire) = 24 Then Locate si.yfire, si.xfire: Print " ";
si.yfire = si.yfire - 1
If Screen(si.yfire, si.xfire) = 65 Then
RowCheck = 0: si.LtColReduce = 0: si.RtColReduce = 0: si.Score = si.Score + 2500
Locate si.yfire, si.xfire: Print Chr$(15);: _Delay .1: Locate si.yfire, si.xfire: Print " ";
Mid$(a$(si.yfire - si.TopRow), si.xfire - si.TrailColumn + 1, 1) = " ": si.yfire = 0: Sound 1000, .5
For i = 4 To 0 Step -1
If Len(LTrim$(a$(i))) And RowCheck = 0 Then si.RowHeight = i + 1: RowCheck = 1
If Len(RTrim$(a$(i))) > si.RtColReduce Then si.RtColReduce = Len(RTrim$(a$(i)))
If Len(LTrim$(a$(i))) > si.LtColReduce Then si.LtColReduce = Len(LTrim$(a$(i)))
Next
si.RtColReduce = Len(si.Array) - si.RtColReduce: si.LtColReduce = Len(si.Array) - si.LtColReduce
If LTrim$(a$(0) + a$(1) + a$(2) + a$(3) + a$(4)) = "" Then si.Outcome = 1 Else Refresh si, a$()
Else
If si.yfire < si.TrailRow Then si.yfire = 0 Else Locate si.yfire, si.xfire: Print Chr$(24);
End If
End Sub

Sub MoveAliens (si As invaders, a$())
si.z1 = Timer
If si.TopRow - 1 + si.RowHeight = si.BaseLine Then
si.Outcome = -1: si.HomeBase = 0
Else
If si.TrailColumn + Len(si.Array) - si.RtColReduce > _Width And si.Direction = 1 Or si.TrailColumn = 1 - si.LtColReduce And si.Direction = -1 Then
si.Direction = -si.Direction: si.TopRow = si.TopRow + 1: si.AlienSpeed = si.AlienSpeed - .0225
If si.AlienSpeed < .1 Then si.AlienSpeed = .1: si.MissileSpeed = .025
Else
si.TrailColumn = si.TrailColumn + si.Direction
End If
End If
Refresh si, a$()
Sound 500, .1
End Sub

Sub AlienBomb (si As invaders, a$())
si.z3 = Timer
If si.TopRow + si.RowHeight < _Height - 3 Then
If si.ybomb = 0 Then ' Designate alien ship.
i = si.RowHeight - 1
k = Int(Rnd * (Len(si.Array) \ 2 + 1)) * 2 + 1
Do
For j = 1 To Len(si.Array)
If Mid$(a$(i), k, 1) = "A" Then
g = k: row = i: Exit Do
Else
For h = i To 0 Step -1
If Mid$(a$(h), k, 1) = "A" Then g = k: row = h: Exit Do
Next
End If
k = k + 2: If k > Len(a$(i)) Then k = 1
Next
Exit Do
Loop
si.ybomb = row + si.TopRow + 1
si.xbomb = si.TrailColumn - 1 + g
Locate si.ybomb - 1, si.xbomb: Color 4: Print "A";: Color 7: Sound 200, 1
Else
Locate si.ybomb, si.xbomb: Print " ";
If si.ybomb = _Height Then
si.ybomb = 0
Else
si.ybomb = si.ybomb + 1
If Screen(si.ybomb, si.xbomb) = 234 Then
Locate si.ybomb, si.xbomb: Color 4, 0: Print Chr$(236);: _Delay .15: Color 7, 0
Locate si.ybomb, si.xbomb
si.Outcome = -2
Else
Locate si.ybomb, si.xbomb: Print Chr$(249);
End If
End If
End If
End If
End Sub

Sub Refresh (si As invaders, a$())
View Print si.TopRow - 1 To si.TopRow + si.RowHeight - 1: si.TrailRow = 0: Cls 2
For i = 0 To si.RowHeight - 1
Locate si.TopRow + i, si.TrailColumn + si.LtColReduce
Print RTrim$(Mid$(a$(i), si.LtColReduce + 1));
If Len(LTrim$(a$(i))) And si.TrailRow = 0 Then si.TrailRow = si.TopRow + i
Next
View Print
If si.HomeBase Then Locate _Height, si.HomeBase: Print Chr$(234);
End Sub

Sub BaseAction (si As invaders)
Select Case si.BaseAction
Case -1
Locate _Height, 1: Print Space$(_Width);
Locate _Height, si.HomeBase: Print Chr$(234);
Case 1
si.yfire = _Height: si.xfire = si.HomeBase
End Select
si.BaseAction = 0
End Sub

Sub AlienBlitz (si As invaders, a$())
Cls: si.HomeBase = 0
ScoreKeeper si
Do
si.TopRow = si.TopRow + 1
Refresh si, a$()
Sound 500, .1: _Delay .1
Loop Until si.TopRow + si.RowHeight > _Height
Sound 200, 1
End Sub

Sub Finish (si As invaders, a$())
ScoreKeeper si
x$ = "GAME OVER - "
Select Case si.Outcome
Case -1: x$ = x$ + "YOU LOST!"
Case -2: x$ = x$ + "YOU LOST!": AlienBlitz si, a$()
Case 1: x$ = "YOU WON!"
End Select
Locate 3, _Width / 2 - Len(x$) / 2: Print x$;
x$ = "Replay? Y/N": Locate 5, _Width / 2 - Len(x$) / 2: Print x$;
Do
_Limit 10
b$ = InKey$
Select Case UCase$(b$)
Case "Y", Chr$(13): Cls: Run
Case "N", Chr$(27)
_Delay .5: x$ = "Bye!"
Locate 7, _Width / 2 - Len(x$) / 2
Print x$;: _Delay 1.5: System
End Select
Loop
End Sub

Edit: Made two small optimizations and added red color to ship that drops a bomb.


RE: Mini Space Invaders - Pete - 09-13-2025

And due to the OVERWHELMING interest in this thread, I decided to add 3 levels!

Code: (Select All)
_Title "Pete's Mini Space Invaders"
Width 40, 20: _Font 16
Type invaders
    RenewVars As Integer
    Level As Integer
    Direction As Integer
    TopRow As Integer
    RowHeight As Integer
    BaseLine As Integer
    TrailColumn As Integer
    MissileSpeed As Single
    AlienSpeed As Single
    HomeBase As Integer
    BaseAction As Integer
    TrailRow As Integer
    LtColReduce As Integer
    RtColReduce As Integer
    z1 As Single
    z2 As Single
    z3 As Single
    yfire As Integer
    xfire As Integer
    ybomb As Integer
    xbomb As Integer
    BombSpeed As Single
    BombFreq As Single
    Outcome As Integer
    Score As _Integer64
    Array As String
End Type
Dim si As invaders: ReDim a$(4)
Do
    Levels si, a$()
    Do
        ScoreKeeper si
        If si.yfire <> 0 And Abs(si.z2 - Timer) > si.MissileSpeed Then MissileAction si, a$(): If si.Outcome Then Exit Do
        If Abs(si.z1 - Timer) > si.AlienSpeed Then
            MoveAliens si, a$(): If si.Outcome Then Exit Do
        End If
        If Abs(si.z3 - Timer) > si.BombFreq Or si.ybomb And Abs(si.z3 - Timer) > si.BombSpeed Then
            AlienBomb si, a$(): If si.Outcome Then Exit Do
        End If
        KeyBoard si
        If si.BaseAction Then BaseAction si
        If si.RenewVars Then si.RenewVars = 0
    Loop
    If si.Level = 3 And si.Outcome = 1 Then si.Outcome = 2 ' Winner!
    If si.Outcome <> 1 Then
        Finish si, a$() ' Loser!
    Else
        View Print 2 To _Height - 1: Cls 2: View Print: _Delay 1.5 ' CLear any remaining bombs or missiles.
    End If
Loop

Sub Levels (si As invaders, a$())
    si.RenewVars = 1
    si.Outcome = 0
    si.LtColReduce = 0
    si.RtColReduce = 0
    si.Direction = 1
    si.TopRow = 4
    si.RowHeight = 5
    si.BaseLine = _Height
    si.MissileSpeed = .04
    si.HomeBase = _Width \ 2
    si.Level = si.Level + 1
    Select Case si.Level
        Case 1
            si.Array = "A A A A A A A A A"
            si.AlienSpeed = .325
            si.BombSpeed = .125
            si.BombFreq = 1
        Case 2
            si.Array = "A A A A A A A A A A"
            si.AlienSpeed = .35
            si.BombSpeed = .1
            si.BombFreq = .85
        Case 3
            si.Array = "A A A A A A A A A A A"
            si.AlienSpeed = .375
            si.BombSpeed = .075
            si.BombFreq = .7
    End Select
    ReDim a$(4): For i = 0 To si.RowHeight - 1: a$(i) = si.Array: Next
    si.TrailColumn = _Width \ 2 - Len(si.Array) \ 2
    Cls
    ScoreKeeper si
    Refresh si, a$()
    _Delay .5
    Locate si.TopRow + si.RowHeight + 2: msg$ = "LEVEL 0" + LTrim$(Str$(si.Level)): CenterX msg$: _Delay 2.5
    Locate si.TopRow + si.RowHeight + 2, 1: EraseRow
    _Delay .5
End Sub

Sub CenterX (msg$)
    Locate , _Width \ 2 - Len(msg$) \ 2: Print msg$;
End Sub

Sub EraseRow
    Locate , 1: Print Space$(_Width);
End Sub

Sub ScoreKeeper (si As invaders)
    info$ = Space$(_Width)
    x$ = " Altitude:" + Str$(_Height * 1000 - (si.TopRow - 1 + si.RowHeight) * 1000) + " Ft  Score:" + Str$(si.Score)
    i = _Width / 2 - Len(x$) / 2
    Mid$(info$, i) = x$
    Locate 1, 1: Print info$;
End Sub

Sub KeyBoard (si As invaders)
    Static restrict
    If RenewVars Then restrict = 0
    Select Case _KeyHit
        Case 19200: If si.HomeBase > 1 And restrict = 0 Then si.HomeBase = si.HomeBase - 1: restrict = 1: si.BaseAction = -1
        Case 19712: If si.HomeBase < _Width And restrict = 0 Then si.HomeBase = si.HomeBase + 1: restrict = 1: si.BaseAction = -1
        Case -19200, -19712: restrict = 0: _KeyClear: si.BaseAction = 0
        Case 32: If si.yfire = 0 Then si.BaseAction = 1
    End Select
End Sub

Sub MissileAction (si As invaders, a$())
    si.z2 = Timer
    If Screen(si.yfire, si.xfire) = 24 Then Locate si.yfire, si.xfire: Print " ";
    si.yfire = si.yfire - 1
    If Screen(si.yfire, si.xfire) = 65 Then
        RowCheck = 0: si.LtColReduce = 0: si.RtColReduce = 0: si.Score = si.Score + 625
        Locate si.yfire, si.xfire: Print Chr$(15);: _Delay .1: Locate si.yfire, si.xfire: Print " ";
        Mid$(a$(si.yfire - si.TopRow), si.xfire - si.TrailColumn + 1, 1) = " ": si.yfire = 0: Sound 1000, .5
        For i = 4 To 0 Step -1
            If Len(LTrim$(a$(i))) And RowCheck = 0 Then si.RowHeight = i + 1: RowCheck = 1
            If Len(RTrim$(a$(i))) > si.RtColReduce Then si.RtColReduce = Len(RTrim$(a$(i)))
            If Len(LTrim$(a$(i))) > si.LtColReduce Then si.LtColReduce = Len(LTrim$(a$(i)))
        Next
        si.RtColReduce = Len(si.Array) - si.RtColReduce: si.LtColReduce = Len(si.Array) - si.LtColReduce
        If LTrim$(a$(0) + a$(1) + a$(2) + a$(3) + a$(4)) = "" Then
            si.Outcome = 1
        Else
            Refresh si, a$()
        End If
    Else
        If si.yfire < si.TrailRow Then si.yfire = 0 Else Locate si.yfire, si.xfire: Print Chr$(24);
    End If
End Sub

Sub MoveAliens (si As invaders, a$())
    si.z1 = Timer
    If si.TopRow - 1 + si.RowHeight = si.BaseLine Then
        si.Outcome = -1
    Else
        If si.TrailColumn + Len(si.Array) - si.RtColReduce > _Width And si.Direction = 1 Or si.TrailColumn = 1 - si.LtColReduce And si.Direction = -1 Then
            si.Direction = -si.Direction: si.TopRow = si.TopRow + 1: si.AlienSpeed = si.AlienSpeed - .0225
            If si.AlienSpeed < .1 Then si.AlienSpeed = .1: si.MissileSpeed = .025
        Else
            si.TrailColumn = si.TrailColumn + si.Direction
        End If
        Refresh si, a$()
        Sound 500, .1
    End If
End Sub

Sub AlienBomb (si As invaders, a$())
    si.z3 = Timer
    If si.TopRow + si.RowHeight < _Height - 3 Then
        If si.ybomb = 0 Then ' Designate alien ship.
            i = si.RowHeight - 1
            k = Int(Rnd * (Len(si.Array) \ 2 + 1)) * 2 + 1
            Do
                For j = 1 To Len(si.Array)
                    If Mid$(a$(i), k, 1) = "A" Then
                        g = k: row = i: Exit Do
                    Else
                        For h = i To 0 Step -1
                            If Mid$(a$(h), k, 1) = "A" Then g = k: row = h: Exit Do
                        Next
                    End If
                    k = k + 2: If k > Len(a$(i)) Then k = 1
                Next
                Exit Do
            Loop
            si.ybomb = row + si.TopRow + 1
            si.xbomb = si.TrailColumn - 1 + g
            Locate si.ybomb - 1, si.xbomb: Color 4: Print "A";: Color 7: Sound 200, 1
        Else
            Locate si.ybomb, si.xbomb: Print " ";
            If si.ybomb = _Height Then
                si.ybomb = 0
            Else
                si.ybomb = si.ybomb + 1
                If Screen(si.ybomb, si.xbomb) = 234 Then
                    Locate si.ybomb, si.xbomb: Color 4, 0: Print Chr$(236);: _Delay .15: Color 7, 0
                    Locate si.ybomb, si.xbomb
                    si.Outcome = -2
                Else
                    Locate si.ybomb, si.xbomb: Print Chr$(249);
                End If
            End If
        End If
    End If
End Sub

Sub Refresh (si As invaders, a$())
    si.TrailRow = 0
    View Print si.TopRow - 1 To si.TopRow + si.RowHeight - 1: Cls 2
    For i = 0 To si.RowHeight - 1
        Locate si.TopRow + i, si.TrailColumn + si.LtColReduce
        Print RTrim$(Mid$(a$(i), si.LtColReduce + 1));
        If Len(LTrim$(a$(i))) And si.TrailRow = 0 Then si.TrailRow = si.TopRow + i
    Next
    View Print
    If si.HomeBase Then Locate _Height, si.HomeBase: Print Chr$(234);
End Sub

Sub BaseAction (si As invaders)
    Select Case si.BaseAction
        Case -1
            Locate _Height, 1: Print Space$(_Width);
            Locate _Height, si.HomeBase: Print Chr$(234);
        Case 1
            si.yfire = _Height: si.xfire = si.HomeBase
    End Select
    si.BaseAction = 0
End Sub

Sub AlienBlitz (si As invaders, a$())
    Cls: si.HomeBase = 0
    ScoreKeeper si
    Do
        si.TopRow = si.TopRow + 1
        Refresh si, a$()
        Sound 500, .1: _Delay .1
    Loop Until si.TopRow + si.RowHeight > _Height
    Sound 200, 1
End Sub

Sub Finish (si As invaders, a$())
    ScoreKeeper si
    msg$ = "GAME OVER - "
    Select Case si.Outcome
        Case -2: msg$ = msg$ + "YOU LOST!": AlienBlitz si, a$()
        Case -1: msg$ = msg$ + "YOU LOST!"
        Case 2: msg$ = msg$ + "YOU WON!"
    End Select
    Locate 3, _Width / 2 - Len(x$) / 2: CenterX msg$
    msg$ = "Replay? Y/N": Locate 5: CenterX msg$
    Do
        _Limit 10
        b$ = InKey$
        Select Case UCase$(b$)
            Case "Y", Chr$(13): Cls: Run
            Case "N", Chr$(27)
                _Delay .5: msg$ = "Bye!"
                Locate 7: CenterX msg$
                _Delay 1.5: System
        End Select
    Loop
End Sub



RE: Mini Space Invaders - Pete - 09-18-2025

Sometimes to better manage the time to code and run a new routine in an existing project, I'll, instead, just make a separate stand alone routine to test out the effects.

If line 2 works for your system to make the screen larger, you can unremark it. That's right, just like most of my routines, it's unremarkable.

So here's just the saucer part without player interaction. I think it's a nice effect for under 100 lines of code.

Code: (Select All)
Width 60, 20
Rem myfont& = _LoadFont(Environ$("SYSTEMROOT") + "\fonts\lucon.ttf", 21, "MONOSPACE"):_Font myfont&
Dim Obj As String: Obj = "-é-"
lm = 1: rm = _Width: spd = .05
ReDim sbomby(10), sbombx(10)
Locate 7, 20: Print "A A A A A A A A A A A A"
Locate 8, 20: Print "A A A A A A A A A A A A"
Locate 9, 20: Print "A A A A A A A A A A A A"
Locate _Height, _Width \ 2: Print Chr$(234);
Do
    If Abs(z4 - Timer) > .015 Then
        z4 = Timer: If SaucerAttack = 0 Then i = Int(Rnd * 20): If i = 7 Then SaucerAttack = 1
    End If
    If SaucerAttack Then
        If Abs(z - Timer) > spd Then
            saucer Obj, z, lm, rm, xsaucer, sbomb, sbomboff, sbomby(), sbombx(), OffScrn
        End If
        If OffScrn = 1 Then SaucerAttack = 0: xsaucer = -1: OffScrn = -1
    End If
    If sbomb Then SaucerBombs sbomb, sbomboff, sbomby(), sbombx()
Loop

Sub saucer (obj As String, z, lm, rm, xsaucer, sbomb, sbomboff, sbomby(), sbombx(), OffScrn)
    Static way, oldway, nodc
    If sbomboff = 0 And xsaucer = 0 Then ReDim sbomby(10), sbombx(10)
    If xsaucer < 0 Then way = 1: xsaucer = 0: nodc = 0
    If way < 0 Then rebound = 1 Else rebound = 0
    MarqueeLIB obj, lm, rm, way, 3, xsaucer, wrapper, rebound, 1, OffScrn: z = Timer
    Sound 1000, .1, .3, 1: Sound 300, .2, .1, 1, 7
    If nodc < 4 And xsaucer > lm + 5 And xsaucer <= rm - Len(obj) Then
        i = Int(Rnd * 30): If i = 1 Then way = -way
    End If
    If way <> oldway Then oldway = way: nodc = nodc + 1
    If Int(Rnd * 10) = 1 And sbomb < 10 Then
        If xsaucer - way * 2 >= lm And xsaucer - way * 2 <= rm Then
            sbomb = sbomb + 1: sbomboff = sbomboff + 1
            sbomby(sbomb) = 4: sbombx(sbomb) = xsaucer - way * 2
            Locate sbomby(sbomb), sbombx(sbomb)
        End If
    End If
End Sub

Sub SaucerBombs (sbomb, sbomboff, sbomby(), sbombx())
    Static z5
    If Abs(z5 - Timer) > .075 Then
        For i = 1 To sbomb
            If sbomby(i) Then
                If Screen(sbomby(i), sbombx(i)) = 249 Then Locate sbomby(i), sbombx(i): Print " ";
                If sbomby(i) < _Height Then
                    sbomby(i) = sbomby(i) + 1
                    If Screen(sbomby(i), sbombx(i)) = 32 Then Locate sbomby(i), sbombx(i): Print Chr$(249);
                Else
                    If Screen(sbomby(i), sbombx(i)) = 234 Then End
                    sbomboff = sbomboff - 1: sbomby(i) = 0: If sbomboff = 0 Then sbomb = 0
                End If
            End If
        Next
        z5 = Timer
    End If
End Sub

Sub MarqueeLIB (Object As String, LtMargin, RtMargin, way, y, x, wrapper, rebound, AllowOffScrn, OffScrn)
    Static j, k, oldway
    If wrapper Then AllowOffScrn = 0: rebound = 0
    Dim a As String
    If j = 0 Or OffScrn = -1 Then
        j = Len(Object): oldway = 0: oldwrapper = wrapper
        If way = 0 Then way = 1
        If way > 0 Then x = LtMargin Else x = RtMargin
    End If
    If oldway And way <> oldway Then
        If way < 0 Then If x = 1 Or x = 2 Then x = k - (2 - x) Else x = x - j - 1
        If way > 0 Then If x = k Or x = k - 1 Then x = 2 - (k - x) Else x = x + j + 1
    End If
    oldway = way: OffScrn = 0
    k = RtMargin + 1 - LtMargin
    a = Space$(RtMargin + 1 - LtMargin)
    If way > 0 Then
        i = x - j + 1: If i < 1 Then i = 1
        Mid$(a, i) = Mid$(Object, j + 1 - x)
        If wrapper Then If x > k Then Mid$(a, 1) = Mid$(Object, j - (x - 1 - k)) Else Mid$(a, k - Len(Object) + x + 1) = Mid$(Object, 1, Len(Object) - x)
    End If
    If way < 0 Then
        Mid$(a, x) = Mid$(Object, 1)
        If wrapper Then If x < 0 Then Mid$(a, k + x) = Mid$(Object, 1, -x + 1) Else Mid$(a, 1) = Mid$(Object, k + 2 - x)
    End If
    Locate y, LtMargin: Print a;
    If way > 0 Then
        x = x + 1: If x > k + j Then If wrapper Then x = j Else x = LtMargin
    Else
        x = x - 1: If j + x = 1 Then If wrapper Then x = k + 1 - j Else x = k
    End If
    If rebound Then If x = 0 Or x > k Then way = -way
    If AllowOffScrn Then If way > 0 And x = 1 Or way < 0 And x = k Then OffScrn = 1: Locate y, LtMargin: Print Space$(k);
End Sub



RE: Mini Space Invaders - Pete - 09-19-2025

Getting close, but hit a snag...

Code: (Select All)
_Title "Pete's Mini Space Invaders"
Randomize Timer
Width 40, 20: _Font 16

Rem myfont& = _LoadFont(Environ$("SYSTEMROOT") + "\fonts\lucon.ttf", 21, "MONOSPACE"):_Font myfont&
Dim Obj As String: Obj = "-é-"
lm = 1: rm = _Width: spd = .05
ReDim sbomby(10), sbombx(10)

Type invaders
    RenewVars As Integer
    Level As Integer
    Direction As Integer
    TopRow As Integer
    RowHeight As Integer
    BaseLine As Integer
    TrailColumn As Integer
    MissileSpeed As Single
    AlienIntSpeed As Single
    AlienSpeedUp As Single
    HomeBase As Integer
    BaseAction As Integer
    TrailRow As Integer
    LtColReduce As Integer
    RtColReduce As Integer
    z1 As Single
    z2 As Single
    z3 As Single
    yfire As Integer
    xfire As Integer
    ybomb As Integer
    xbomb As Integer
    BombSpeed As Single
    BombFreq As Single
    sbomb As Integer
    sbomboff As Integer
    Outcome As Integer
    Score As _Integer64
    Array As String
    saucer As String
    xsaucer As Single ''' Integer
End Type
Dim si As invaders: ReDim a$(4): si.saucer = "-é-"

Do
    Levels si, a$(): _KeyClear
    Do
        ScoreKeeper si
        If si.yfire <> 0 And Abs(si.z2 - Timer) > si.MissileSpeed Then MissileAction si, a$(): If si.Outcome Then Exit Do
        If Abs(si.z1 - Timer) > si.AlienIntSpeed Then
            MoveAliens si, a$(): If si.Outcome Then Exit Do
        End If
        If Abs(si.z3 - Timer) > si.BombFreq And si.ybomb = 0 Or si.ybomb And Abs(si.z3 - Timer) > si.BombSpeed Then
            AlienBomb si, a$(): If si.Outcome Then Exit Do
        End If
        KeyBoard si
        If si.BaseAction Then BaseAction si
        If si.RenewVars Then si.RenewVars = 0
        If Abs(z4 - Timer) > .015 Then
            z4 = Timer: If SaucerAttack = 0 Then i = Int(Rnd * 100): If i = 7 Then SaucerAttack = 1
        End If
        If si.TopRow > _Height - 7 Then SaucerAttack = 0
        If SaucerAttack Then
            If Abs(z - Timer) > spd Then
                saucer si, z, lm, rm, sbomby(), sbombx(), OffScrn
            End If
            If OffScrn = 1 Then SaucerAttack = 0: si.xsaucer = -1: OffScrn = -1
        End If
        If si.sbomb Then SaucerBombs si, sbomby(), sbombx()
    Loop
    If si.Level = 3 And si.Outcome = 1 Then si.Outcome = 2 ' Winner!
    If si.Outcome <> 1 Then
        Finish si, a$() ' Loser!
    Else
        View Print 2 To _Height - 1: Cls 2: View Print: _Delay 1.5 ' CLear any remaining bombs or missiles.
    End If
Loop

Sub Levels (si As invaders, a$())
    si.RenewVars = 1
    si.Outcome = 0
    si.LtColReduce = 0
    si.RtColReduce = 0
    si.Direction = 1
    si.TopRow = 4
    si.RowHeight = 5
    si.BaseLine = _Height
    si.MissileSpeed = .03
    si.HomeBase = _Width \ 2
    si.Level = si.Level + 1
    Select Case si.Level
        Case 1
            si.Array = "A A A A A A A A A A A A"
            si.AlienIntSpeed = .375
            si.AlienSpeedUp = .02
            si.BombSpeed = .1
            si.BombFreq = 1
        Case 2
            si.Array = "A A A A A A A A A A A A"
            si.AlienIntSpeed = .35
            si.AlienSpeedUp = .05
            si.BombSpeed = .1
            si.BombFreq = .85
        Case 3
            si.Array = "A A A A A A A A A A A A"
            si.AlienIntSpeed = .325
            si.AlienSpeedUp = .08
            si.BombSpeed = .07
            si.BombFreq = .7
    End Select
    ReDim a$(4): For i = 0 To si.RowHeight - 1: a$(i) = si.Array: Next
    si.TrailColumn = _Width \ 2 - Len(si.Array) \ 2
    Cls
    ScoreKeeper si
    Refresh si, a$()
    _Delay .5
    Locate si.TopRow + si.RowHeight + 2: msg$ = "LEVEL 0" + LTrim$(Str$(si.Level)): CenterX msg$: _Delay 2.5
    Locate si.TopRow + si.RowHeight + 2, 1: EraseRow
    _Delay .5
End Sub

Sub CenterX (msg$)
    Locate , _Width \ 2 - Len(msg$) \ 2: Print msg$;
End Sub

Sub EraseRow
    Locate , 1: Print Space$(_Width);
End Sub

Sub ScoreKeeper (si As invaders)
    info$ = Space$(_Width)
    x$ = " Altitude:" + Str$(_Height * 1000 - (si.TopRow - 1 + si.RowHeight) * 1000) + " Ft  Score:" + Str$(si.Score)
    i = _Width / 2 - Len(x$) / 2
    Mid$(info$, i) = x$
    Locate 1, 1: Print info$;
End Sub

Sub KeyBoard (si As invaders)
    Static restrict
    If RenewVars Then restrict = 0
    Select Case _KeyHit
        Case 19200: If si.HomeBase > 1 And restrict = 0 Then si.HomeBase = si.HomeBase - 1: restrict = 1: si.BaseAction = -1
        Case 19712: If si.HomeBase < _Width And restrict = 0 Then si.HomeBase = si.HomeBase + 1: restrict = 1: si.BaseAction = -1
        Case -19200, -19712: restrict = 0: _KeyClear: si.BaseAction = 0
        Case 32: If si.yfire = 0 Then si.BaseAction = 1
    End Select
End Sub

Sub MissileAction (si As invaders, a$())
    si.z2 = Timer
    If Screen(si.yfire, si.xfire) = 24 Then Locate si.yfire, si.xfire: Print " ";
    si.yfire = si.yfire - 1
    If Screen(si.yfire, si.xfire) = 65 Then
        RowCheck = 0: si.LtColReduce = 0: si.RtColReduce = 0: si.Score = si.Score + 500
        Locate si.yfire, si.xfire: Print Chr$(15);: _Delay .1: Locate si.yfire, si.xfire: Print " ";
        Mid$(a$(si.yfire - si.TopRow), si.xfire - si.TrailColumn + 1, 1) = " ": si.yfire = 0: Sound 1000, .5
        For i = 4 To 0 Step -1
            If Len(LTrim$(a$(i))) And RowCheck = 0 Then si.RowHeight = i + 1: RowCheck = 1
            If Len(RTrim$(a$(i))) > si.RtColReduce Then si.RtColReduce = Len(RTrim$(a$(i)))
            If Len(LTrim$(a$(i))) > si.LtColReduce Then si.LtColReduce = Len(LTrim$(a$(i)))
        Next
        si.RtColReduce = Len(si.Array) - si.RtColReduce: si.LtColReduce = Len(si.Array) - si.LtColReduce
        If LTrim$(a$(0) + a$(1) + a$(2) + a$(3) + a$(4)) = "" Then
            si.Outcome = 1
        Else
            Refresh si, a$()
        End If
    Else
        If si.yfire < si.TrailRow Then si.yfire = 0 Else Locate si.yfire, si.xfire: Print Chr$(24);
    End If
End Sub

Sub MoveAliens (si As invaders, a$())
    si.z1 = Timer
    If si.TrailColumn + Len(si.Array) - si.RtColReduce > _Width And si.Direction = 1 Or si.TrailColumn = 1 - si.LtColReduce And si.Direction = -1 Then
        si.Direction = -si.Direction: si.TopRow = si.TopRow + 1: si.AlienIntSpeed = si.AlienIntSpeed - .0225
        If si.AlienIntSpeed < .1 Then si.AlienIntSpeed = .1: si.MissileSpeed = .015
    Else
        si.TrailColumn = si.TrailColumn + si.Direction
    End If
    If si.TopRow - 1 + si.RowHeight = si.BaseLine Then si.Outcome = -1: si.HomeBase = 0
    Refresh si, a$()
    Sound 500, .1
End Sub

Sub AlienBomb (si As invaders, a$())
    si.z3 = Timer
    If si.ybomb = 0 And si.TopRow + si.RowHeight < _Height - 3 Then ' Designate alien ship.
        i = si.RowHeight - 1
        k = Int(Rnd * (Len(si.Array) \ 2 + 1)) * 2 + 1
        Do
            For j = 1 To Len(si.Array)
                If Mid$(a$(i), k, 1) = "A" Then
                    g = k: row = i: Exit Do
                Else
                    For h = i To 0 Step -1
                        If Mid$(a$(h), k, 1) = "A" Then g = k: row = h: Exit Do
                    Next
                End If
                k = k + 2: If k > Len(a$(i)) Then k = 1
            Next
            Exit Do
        Loop
        si.ybomb = row + si.TopRow + 1
        si.xbomb = si.TrailColumn - 1 + g
        Locate si.ybomb - 1, si.xbomb: Color 4: Print "A";: Color 7: Sound 200, 1
    ElseIf si.ybomb Then
        Locate si.ybomb, si.xbomb: Print " ";
        If si.ybomb = _Height Then
            si.ybomb = 0
        Else
            si.ybomb = si.ybomb + 1
            If Screen(si.ybomb, si.xbomb) = 234 Then
                Locate si.ybomb, si.xbomb: Color 4, 0: Print Chr$(236);: _Delay .15: Color 7, 0
                Locate si.ybomb, si.xbomb
                si.Outcome = -2
            Else
                Locate si.ybomb, si.xbomb: Print Chr$(249);
            End If
        End If
    End If
End Sub

Sub Refresh (si As invaders, a$())
    si.TrailRow = 0
    View Print si.TopRow - 1 To si.TopRow + si.RowHeight - 1: Cls 2
    For i = 0 To si.RowHeight - 1
        Locate si.TopRow + i, si.TrailColumn + si.LtColReduce
        Print RTrim$(Mid$(a$(i), si.LtColReduce + 1));
        If Len(LTrim$(a$(i))) And si.TrailRow = 0 Then si.TrailRow = si.TopRow + i
    Next
    View Print
    If si.HomeBase Then Locate _Height, si.HomeBase: Print Chr$(234);
End Sub

Sub BaseAction (si As invaders)
    Select Case si.BaseAction
        Case -1
            Locate _Height, 1: Print Space$(_Width);
            Locate _Height, si.HomeBase: Print Chr$(234);
        Case 1
            si.yfire = _Height: si.xfire = si.HomeBase
    End Select
    si.BaseAction = 0
End Sub

Sub AlienBlitz (si As invaders, a$())
    Cls: si.HomeBase = 0
    ScoreKeeper si
    Do
        si.TopRow = si.TopRow + 1
        Refresh si, a$()
        Sound 500, .1: _Delay .1
    Loop Until si.TopRow + si.RowHeight > _Height
    Sound 200, 1
End Sub

Sub Finish (si As invaders, a$())
    ScoreKeeper si
    msg$ = "GAME OVER - "
    Select Case si.Outcome
        Case -2: msg$ = msg$ + "YOU LOST!": AlienBlitz si, a$()
        Case -1: msg$ = msg$ + "YOU LOST!"
        Case 2: msg$ = msg$ + "YOU WON!"
    End Select
    Locate 3: CenterX msg$: msg$ = "Replay? Y/N": Locate 5: CenterX msg$
    Do
        _Limit 10
        b$ = InKey$
        Select Case UCase$(b$)
            Case "Y", Chr$(13): Cls: Run
            Case "N", Chr$(27): _Delay .5: msg$ = "Bye!": Locate 7: CenterX msg$: _Delay 1.5: System
        End Select
    Loop
End Sub

Sub saucer (si As invaders, z, lm, rm, sbomby(), sbombx(), OffScrn)
    Static way, oldway, nodc, sounder
    Dim obj As String: obj = "-é-"
    If si.sbomboff = 0 And si.xsaucer = 0 Then ReDim sbomby(10), sbombx(10): sounder = 0
    If si.xsaucer < 0 Then way = 1: si.xsaucer = 0: nodc = 0
    If way < 0 Then rebound = 1 Else rebound = 0
    MarqueeLIB obj, lm, rm, way, 3, si.xsaucer, wrapper, rebound, 1, OffScrn: z = Timer
    sounder = sounder + 1: If sounder Mod 5 = 0 Then sounder = 0: Sound 300, .2, .5, 1, 9: Sound 0, 0, 1, 0, 1
    If nodc < 4 And si.xsaucer > lm + 5 And si.xsaucer <= rm - Len(obj) Then
        i = Int(Rnd * 30): If i = 1 Then way = -way
    End If
    If way <> oldway Then oldway = way: nodc = nodc + 1
    If Int(Rnd * 10) = 1 And si.sbomb < 10 Then
        If si.xsaucer - way * 2 >= lm And si.xsaucer - way * 2 <= rm Then
            si.sbomb = si.sbomb + 1: si.sbomboff = si.sbomboff + 1
            sbomby(si.sbomb) = 4: sbombx(si.sbomb) = si.xsaucer - way * 2
            Locate sbomby(si.sbomb), sbombx(si.sbomb)
        End If
    End If
End Sub

Sub SaucerBombs (si As invaders, sbomby(), sbombx())
    Static z5
    If Abs(z5 - Timer) > .075 Then
        For i = 1 To si.sbomb
            If sbomby(i) Then
                If Screen(sbomby(i), sbombx(i)) = 249 Then Locate sbomby(i), sbombx(i): Print " ";
                If sbomby(i) < _Height Then
                    sbomby(i) = sbomby(i) + 1
                    If Screen(sbomby(i), sbombx(i)) = 32 Then Locate sbomby(i), sbombx(i): Print Chr$(249);
                Else
                    If Screen(sbomby(i), sbombx(i)) = 234 Then
                        Locate sbomby(i), sbombx(i)
                        Color 4, 0: Print Chr$(236);: _Delay .15: Color 7, 0
                        si.Outcome = -2
                        Exit For
                    End If
                    si.sbomboff = si.sbomboff - 1: sbomby(i) = 0: If si.sbomboff = 0 Then si.sbomb = 0
                End If
            End If
        Next
        z5 = Timer
    End If
End Sub

Sub MarqueeLIB (Object As String, LtMargin, RtMargin, way, y, x, wrapper, rebound, AllowOffScrn, OffScrn)
    Static j, k, oldway
    If wrapper Then AllowOffScrn = 0: rebound = 0
    Dim a As String
    If j = 0 Or OffScrn = -1 Then
        j = Len(Object): oldway = 0: oldwrapper = wrapper
        If way = 0 Then way = 1
        If way > 0 Then x = LtMargin Else x = RtMargin
    End If
    If oldway And way <> oldway Then
        If way < 0 Then If x = 1 Or x = 2 Then x = k - (2 - x) Else x = x - j - 1
        If way > 0 Then If x = k Or x = k - 1 Then x = 2 - (k - x) Else x = x + j + 1
    End If
    oldway = way: OffScrn = 0
    k = RtMargin + 1 - LtMargin
    a = Space$(RtMargin + 1 - LtMargin)
    If way > 0 Then
        i = x - j + 1: If i < 1 Then i = 1
        Mid$(a, i) = Mid$(Object, j + 1 - x)
        If wrapper Then If x > k Then Mid$(a, 1) = Mid$(Object, j - (x - 1 - k)) Else Mid$(a, k - Len(Object) + x + 1) = Mid$(Object, 1, Len(Object) - x)
    End If
    If way < 0 Then
        Mid$(a, x) = Mid$(Object, 1)
        If wrapper Then If x < 0 Then Mid$(a, k + x) = Mid$(Object, 1, -x + 1) Else Mid$(a, 1) = Mid$(Object, k + 2 - x)
    End If
    Locate y, LtMargin: Print a;
    If way > 0 Then
        x = x + 1: If x > k + j Then If wrapper Then x = j Else x = LtMargin
    Else
        x = x - 1: If j + x = 1 Then If wrapper Then x = k + 1 - j Else x = k
    End If
    If rebound Then If x = 0 Or x > k Then way = -way
    If AllowOffScrn Then If way > 0 And x = 1 Or way < 0 And x = k Then OffScrn = 1: Locate y, LtMargin: Print Space$(k);
End Sub

So the snag part is what to do about shooting down the saucer? I've purposely kept the missile part as a single fire routine. That means the existing missile needs to hit an alien, or top out. I made all missiles top out at the highest row of invaders; however, to hit the saucer this top out level would need to be lifted to the saucer row, and that would slow play down too much.

My options:

Make the saucer always only one row above the aliens, which would mean only a slight flight extension over the present routine.

Speed up the missiles so the firing intervals are not too long.

Modify the missile routine to include multiple missiles in the air like the saucer bomb routine.

I'm not sure which method will work the best.

Pete


RE: Mini Space Invaders - bplus - 09-20-2025

+1 Man you really got into this! Hopefully your personal attention and interest is worth more than a point. 

There is a point where a graphics screen starts to make sense for making enhancements, maybe 150-200 LOC even for old dogs that dont like learning new tricks. Smile


Here take my spaceship out for a spin:
Code: (Select All)
Screen _NewImage(1024, 700, 32)
_Delay .25
_ScreenMove _Middle

Dim sc As _Unsigned Long ' ship color
sc = _RGB32(Rnd * 215 + 40, Rnd * 255, Rnd * 255)
_MouseHide
Do
    Cls
    lc = lc + 1 'loop counter
    If lc > 3 * 60 Then sc = _RGB32(Rnd * 215 + 40, Rnd * 255, Rnd * 255): lc = 0 'every 3 secs change ship color
    While _MouseInput: Wend
    drawShip _MouseX, _MouseY, sc ' here is how to call drawShip   easier than using circle
    'drawShip _WIDTH / 2, _HEIGHT / 2, sc
    _Display
    _Limit 60
Loop Until _KeyDown(27)

Sub drawShip (x, y, colr As _Unsigned Long) 'shipType     collisions same as circle x, y radius = 30
    Static ls
    Dim light As Long, r As Long, g As Long, b As Long
    r = _Red32(colr): g = _Green32(colr): b = _Blue32(colr)
    fellipse x, y, 6, 15, _RGB32(r, g - 120, b - 100)
    fellipse x, y, 18, 11, _RGB32(r, g - 60, b - 50)
    fellipse x, y, 30, 7, _RGB32(r, g, b)
    For light = 0 To 5
        fcirc x - 30 + 11 * light + ls, y, 1, _RGB32(ls * 50, ls * 50, ls * 50)
    Next
    ls = ls + 1
    If ls > 5 Then ls = 0
End Sub


' ======== helper subs for drawShip that you can use for other things specially fcirc = fill_circle  x, y, radius, color

Sub fellipse (CX As Long, CY As Long, xr As Long, yr As Long, C As _Unsigned Long)
    If xr = 0 Or yr = 0 Then Exit Sub
    Dim h2 As _Integer64, w2 As _Integer64, h2w2 As _Integer64
    Dim x As Long, y As Long
    w2 = xr * xr: h2 = yr * yr: h2w2 = h2 * w2
    Line (CX - xr, CY)-(CX + xr, CY), C, BF
    Do While y < yr
        y = y + 1
        x = Sqr((h2w2 - y * y * w2) \ h2)
        Line (CX - x, CY + y)-(CX + x, CY + y), C, BF
        Line (CX - x, CY - y)-(CX + x, CY - y), C, BF
    Loop
End Sub

Sub fcirc (x As Long, y As Long, R As Long, C As _Unsigned Long) 'vince version  fill circle x, y, radius, color
    Dim x0 As Long, y0 As Long, e As Long
    x0 = R: y0 = 0: e = 0
    Do While y0 < x0
        If e <= 0 Then
            y0 = y0 + 1
            Line (x - x0, y + y0)-(x + x0, y + y0), C, BF
            Line (x - x0, y - y0)-(x + x0, y - y0), C, BF
            e = e + 2 * y0
        Else
            Line (x - y0, y - x0)-(x + y0, y - x0), C, BF
            Line (x - y0, y + x0)-(x + y0, y + x0), C, BF
            x0 = x0 - 1: e = e - 2 * x0
        End If
    Loop
    Line (x - R, y)-(x + R, y), C, BF
End Sub



RE: Mini Space Invaders - NakedApe - 09-20-2025

Very cool, Pete. +1 

I want to be able to hold down the arrow keys to move around instead of having to keep pressing them. Why did you limit the motion, to make it harder for the poor players?   Big Grin