Welcome, Guest
You have to register before you can post on our site.

Username/Email:
  

Password
  





Search Forums

(Advanced Search)

Forum Statistics
» Members: 501
» Latest member: BryanCheat
» Forum threads: 2,855
» Forum posts: 26,745

Full Statistics

Latest Threads
Curious if I am thinking ...
Forum: Help Me!
Last Post: SMcNeill
1 hour ago
» Replies: 16
» Views: 136
Glow Bug
Forum: Programs
Last Post: SierraKen
1 hour ago
» Replies: 7
» Views: 105
ADPCM compression
Forum: Petr
Last Post: Petr
4 hours ago
» Replies: 0
» Views: 26
Who wants to PLAY?
Forum: QBJS, BAM, and Other BASICs
Last Post: dbox
4 hours ago
» Replies: 15
» Views: 209
Trojan infection !
Forum: Help Me!
Last Post: SMcNeill
6 hours ago
» Replies: 1
» Views: 35
BAM Sample Programs
Forum: QBJS, BAM, and Other BASICs
Last Post: CharlieJV
Today, 02:50 AM
» Replies: 36
» Views: 1,969
Audio storage, stereo swi...
Forum: Programs
Last Post: Petr
Yesterday, 09:03 PM
» Replies: 8
» Views: 369
_CONSOLEINPUT is blocking
Forum: General Discussion
Last Post: mdijkens
Yesterday, 12:24 PM
» Replies: 7
» Views: 129
Most efficient way to bui...
Forum: General Discussion
Last Post: ahenry3068
01-17-2025, 11:36 PM
» Replies: 9
» Views: 137
QBJS - ANSI Draw
Forum: QBJS, BAM, and Other BASICs
Last Post: madscijr
01-17-2025, 11:24 PM
» Replies: 4
» Views: 135

 
  b+ Asteroids M11
Posted by: bplus - 06-01-2022, 01:21 AM - Forum: bplus - Replies (2)

Oldie but goodie:

Code: (Select All)
Option _Explicit
_Title "b+QB64 Asteroids M11" 'started 2018-07-13
'2020 - Oct and Nov: Special thanks to SierraKen who has been helpful with suggestions, ideas and fixes.
Randomize Timer

' Far far away in a universe where Newton's 1st Law of Motion is not obeyed...    ;-))


'                                            New Controls
' key a or left arrow = turn left
' key s or right arrow = turn right
' enter or spacebar =  "hyperspace" show up somewhere else  better or worse use when collision is imminent
' key k or up arrow = thrust  just a burst in direction you are pointed but doesn't last
' fire! owwha, owha, owha...  is now continuous

'=================================================================================================================

' 2020-10-27 remove the alternate subs and get down below 200 LOC almost there now! new shooter action font and background
' 2020-10-28 another makeover explosions and split asteroids
' 2020-10-29 fix baby rock management, break between lives
' 2020-10-29 fix left/right gun, fix explosions over many frames to eliminate pause in action, speed up 60 fps
' 2020-10-30 m3 SierraKen's idea to angle shooter with mousewheel also finish WASD options, more rocks, points system
' points:
' The higher the speed the better    speed range .5 to 2.5, diff = 2 * 50 = 100          s - .5 * 50
' DELETE The lower the color the better   color range 10 to 60, diff =      50 * 2 = 100  50 - (c - 10) * 2
' The smaller the size the better  size range 10 to 100, diff = 90 * 1.1111 = 100  90 - (sz -10) * 1.1111
'        ((speed - .5) * 50 + (90 - (r - 10)) * 1.1111) / 2 = 100 best score per hit
' 2020-10-30 increase level of difficulty, fix double lives lost, add an ending after all lives spent.
' 2020-10-31 M4 They are Here - the aliens have accepted my invitaion for war games don't get caught in their beam.
' rework ending and variable LONG suffix. Aliens on the attack 100 points before or after transformed into the Bolder of Death.
' 2020-11-01 M5 FX Moving through space, Oh yeah, more aliens!
' 2020-11-01 M6 add play again and save high game , continuous shoot
' 2020-11-01 M7 fix hits count when hit alien ship or Bolder of Death. Fix lights on aliens ship. I want to see collsions with ship.
' Ken recommends removing text in middle of screen, yeah, distracting. Makeover ship as with mouse x, y it's center. Add Splash screen.
' Show mouse in between lives so can be in screen center when press key to start next run.

' 2020-11-03 M9 watching videos on Asteroids, I found a view and description of the control panel, it is:
' 2 buttons on left and lower button in middle and 2 buttons on right level with 2 on left.
' 2 buttons on left are left and right turns they will be Keys A and S
' The middle will be spacebar for Hypespace or these days we might call it worm hole, jump to another location better or worse!
' 2 buttons on right K and L: K will be a thruster of sorts you eventually come to a stop (not what Newton would like) L is the fire button.
' So let's try that. I am impressed by hitting thruster and shooting all around as you dift in direction gun was pointed when hit thruster.
' Lighten rocks and change points, based now only on size and speed of Asteroids.
' 2020-11-04 M9 Install new update to thrust control. Thanks to SierraKen for finding out what happened to Hyperspace jump when press
' spacebar a 2nd time in a life. Oh also Boulders of Death are now more like smooth colored spheres. Ahhhh Fixed the crashing at the borders
' now fly out one side and come back in on the other! This is getting good! Adding some sound effects.
' 2020-11-05 M10 set some constants for starting game so can just change them to level of play desired.
' speed points =  50 * (rockSpeed - minRockSpeed)/rockSpeedRange > fastest rock gets 50 points
' size points = 50 * (rockSizeRange - (rockSize - minRockSize))/rockSizeRange  > smallest rock gets 50 points
' points = points +  50 * ( (rockSpeed - minRockSpeed)/rockSpeedRange +  (rockSizeRange - (rockSize - minRockSize))/rockSizeRange )
' Dav's comments: Add arrow keys for left right turns and up for thrust, continuous fire now! A little score board in top left corner.
' Dav I am keeping spacebar for Hyperspace but also Enter, don't need fire button now.
' Try the A for left and the right arrow for right turns.
' 2020-11-05 p is for pause
' 2020-11-06 Move it or lose it!
' 2020-11-06 Put the dang mouse back in!  Press m for mouse target rich environment! Use left and right mouse buttons to aim guns,
' 2020-11-08 add mouse mode you can enter at splash screen,

' 2021-09-24 b+QB64 Asteroids M11 best mouse only
' fix p for play again because it is also pause
' don't like enter to start next life, spacebar preferred

'================================================================================================================

'    NOTE: !!!!!!!!!!!!!!!   When there is a pause in action, just press enter,  as in enter a new life!

'================================================================================================================

Const xmax = 1200, ymax = 700, pi = _Pi, polyAngle = _Pi / 6, nRocks = 300, nBullets = 2000, bSpeed = 15
Const startRocks = 2, minRockSpeed = 0.7500, maxRockSpeed = 3.2500, minRockSize = 10, maxRockSize = 100
Const rockSpeedRange = maxRockSpeed - minRockSpeed, rockSizeRange = maxRockSize - minRockSize
Const alienSpeed = rockSpeedRange / 2 + minRockSpeed ' average rock speed

Type alienType
    x As Single
    y As Single
    dx As Single
    dy As Single
    ls As Long ' lights offset and gray scale
    c As _Unsigned Long ' color
    live As Long
    attackFrame As Long
    fireX As Single
    fireY As Single
    transform As Long
End Type

Type particle
    x As Single
    y As Single
    dx As Single
    dy As Single
    size As Single
    kolor As _Unsigned Long
End Type

Type bullet
    x As Single
    y As Single
    dx As Single
    dy As Single
    live As Long
End Type

Type shipType
    x As Single
    y As Single
    live As Long
    speed As Single '       just a constant now when Thrust is applied
    thrustAngle As Single ' ship/gun angle at moment Thrust is pressed
    angle As Single '       rotated position ship/gun now A or S keypress or hold down
    thrust As Long '        this now tracks how many frames ship will move at speed and thrustAngle
End Type

Type rock
    x As Single
    y As Single
    r As Long '            radius
    ra As Single '         rotation position   a = a + spin
    heading As Single '    heading from which dx, dy are calc with speed
    speed As Single '      speed
    spin As Single '       rotation direction and amount
    seed As Long '         for drawing rocks with RND USING
    c As Long '            color   rgb(c, c, c)
    live As Long '         need this to track rocks still active like bullets
    explodeFrame As Long ' after a rock is hit by bullet, it explodes and in more than one frame
End Type

ReDim Shared aliens(1 To 5) As alienType
Dim Shared dots(2000) As particle ' explosions
Dim Shared b(nBullets) As bullet
Dim Shared ship As shipType
Dim Shared r(nRocks) As rock
Dim Shared lives As Long
Dim Shared points As Long
Dim Shared rocks As Long 'rocks is the minimum number of parent rocks to have on screen  automatic replace when hit or out of bounds

Dim HS As Long, fnt As Long, fnt2 As Long ' file LOAD handles
Dim i As Long, bullets As Long, fire As Long ' index and bullets
Dim r As Long, newRockN As Long, maxBabyRocks As Long, br As Long, hits As Long ' rock stuff
Dim ai As Long, alienN As Long ' alien index and number
Dim kh As Long 'key hit for ship thrust k or up arrow or hyperspace jump spacebar
Dim hs$, s$, k$, t, lastt 'high score, general string and times for bullets
Dim rockPoints As Long, roundPoints As Long ' various points scores

'Dim mouseMode As Long 'either key press hyperdrive and thrust or mouse moves
ship.speed = 3.5 'this would be a constant but ship has to declared as Ship Type first, Hyperspace jump

Screen _NewImage(xmax, ymax, 32)
_ScreenMove 100, 20
_FullScreen

fnt = _LoadFont("ARLRDBD.ttf", 16, "MONOSPACE")
fnt2 = _LoadFont("ARLRDBD.ttf", 40, "MONOSPACE")
_Font fnt2
Color &HFF00FFFF, &H00000000

If _FileExists("Asteroids High Score.txt") Then
    Open "Asteroids High Score.txt" For Input As #1
    Input #1, HS
    Close #1
End If
hs$ = "High Score:" + Str$(HS)

'a little splash screen
rocks = 7: alienN = 3
For i = 1 To nRocks
    newRock i
    If i > rocks Then r(i).live = 0
Next
For i = 1 To alienN
    newAlien i
Next
i = 0
Do
    drawStars 0
    i = i + 1
    If i Mod 30 = 29 And rocks < nRocks Then rocks = rocks + 1: r(rocks).live = 1
    For r = 1 To nRocks
        If r(r).live Then drawRock r
    Next
    For i = 1 To alienN
        drawAliens i
    Next

    _Font fnt2
    s$ = "*** b+QB64 Asteroids ***"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, 60), s$
    _PrintString ((_Width - _PrintWidth(hs$)) / 2, 140), hs$
    s$ = "A or arrow = Left Spin"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, 300), s$
    s$ = "S or arrow = Right Spin"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, 380), s$
    's$ = "Space or Enter = Hyper Jump"
    '_PrintString ((_Width - _PrintWidth(s$)) / 2, 380), s$
    s$ = "p = Pause or end Pause"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, 460), s$
    s$ = "Mouse L/R Button = Guns"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, 540), s$
    _Font fnt
    If _KeyDown(27) Then System
    k$ = InKey$
    _Display
    _Limit 60
Loop Until Len(k$)

_MouseHide
restart:
If _FileExists("Asteroids High Score.txt") Then
    Open "Asteroids High Score.txt" For Input As #1
    Input #1, HS
    Close #1
End If
hs$ = "  High Score:" + Str$(HS)
lives = 10: alienN = 1: rocks = startRocks ' always active rocks
points = 0: hits = 0: bullets = 0

'If k$ = "m" Then ' undocumented use of mouse
'mouseMode = -1:
alienN = 3: rocks = 5 ' M11 mouse mode assumed only other key is space or enter for hyper jump
'End If

While lives > 0 And _KeyDown(27) = 0 ' init start restart
    ReDim aliens(1 To alienN) As alienType
    For ai = 1 To alienN
        newAlien ai
    Next
    For i = 1 To nRocks 'reset rocks mainly clear baby rocks
        newRock (i)
        If i > rocks Then r(i).live = 0
    Next
    ship.x = xmax / 2 'avoids explosions top left corner at start, dang still get some!
    ship.y = ymax / 2
    ship.angle = 0
    ship.thrustAngle = 0
    ship.thrust = 0
    ship.live = 1
    rockPoints = 0
    roundPoints = 0
    While ship.live And _KeyDown(27) = 0

        'draw everything then process bullets
        drawStars 1
        Locate 1, 1: Print "Lives:"; lives
        Locate 2, 1: Print "Last Rock:"; Str$(rockPoints)
        Locate 3, 1: Print "Round:;"; Str$(roundPoints)
        Locate 4, 1: Print "Points:"; Str$(points)
        Locate 5, 1: Print "High Score:"; Str$(HS)

        For ai = 1 To alienN
            drawAliens ai
        Next
        For i = 1 To nRocks
            If r(i).live Then 'make sure if we crash into a ship it is a live rock, not one sitting on side waiting to be called up
                drawRock i ' while drawing rocks the ship could be blown up
                If ((r(i).x - ship.x) ^ 2 + (r(i).y - ship.y) ^ 2) ^ .5 < r(i).r + 20 Then 'rock collides with ship?
                    For br = 1 To 200 Step 5
                        Circle ((ship.x + r(i).x) / 2, (ship.y + r(i).y) / 2), br, _RGB32(255 - br, 255 - 2 * br, 0)
                    Next
                    drawRock i
                    drawship
                    ship.live = 0
                    If i <= rocks Then newRock i Else r(i).live = 0
                End If
            End If
        Next
        For i = 1 To nRocks 'smoke up the place with rock debris fields still flying out from hit frames ago
            If r(i).explodeFrame Then
                r(i).explodeFrame = r(i).explodeFrame + 1
                If r(i).explodeFrame > .25 * r(i).r Then
                    r(i).explodeFrame = 0
                    If i <= rocks Then newRock i ' now replace the rock
                Else
                    explode r(i).x, r(i).y, r(i).r, r(i).explodeFrame
                End If
            End If
        Next
        If ship.live Then
            For ai = 1 To alienN
                If Sqr((aliens(ai).x - ship.x) ^ 2 + (aliens(ai).y - ship.y) ^ 2) < 55 Then 'aliens and ship collisde boom boom
                    For br = 1 To 200 Step 5
                        Circle ((ship.x + aliens(ai).x) / 2, (ship.y + aliens(ai).y) / 2), br, _RGB32(255 - br, 255 - 2 * br, 0)
                    Next
                    drawship
                    ship.live = 0
                    _Continue
                Else
                    drawship
                End If
            Next
            'If mouseMode Then
            While _MouseInput: Wend
            ship.x = _MouseX: ship.y = _MouseY
            If _MouseButton(1) Then ship.angle = pi
            If _MouseButton(2) Then ship.angle = 0
            'End If
            '                                                                   ship controls update
            'a key or left arrow = left spin
            If _KeyDown(97) Or _KeyDown(19200) Then ship.angle = ship.angle - pi / 48

            's key or right arrow = right spin
            If _KeyDown(115) Or _KeyDown(19712) Then ship.angle = ship.angle + pi / 48

            'l is Fire!  JUST CONTINUOUS FIRE
            fire = 0
            'IF _KEYDOWN(108) THEN
            t = Timer(.01)
            If lastt = 0 Or t - lastt > .15 Then fire = 1: Sound 2088, .01: lastt = t
            'END IF

            kh = _KeyHit
            Select Case kh
                Case 112 ' p for pause
                    kh = 0
                    While _KeyHit <> 112: _Limit 60: Wend
                    'Case 107, 18432 'thrust  k key or up arrow
                    '    If mouseMode = 0 Then
                    '        Sound 488, .01
                    '        ship.thrustAngle = ship.angle: ship.thrust = 120
                    '    End If
                    'Case 13, 32 ' space = hyperspace jump
                    '    'If mouseMode = 0 Then
                    '    Randomize Timer
                    '    ship.x = (xmax - 300) * Rnd + 150: ship.y = (ymax - 300) * Rnd + 150: ship.thrust = 0
                    '    'End If
            End Select

            '                                                                   locate ship
            'If ship.thrust > 0 Then
            '    'relocate ship position
            '    ship.x = ship.x + ship.speed * Cos(ship.thrustAngle)
            '    ship.y = ship.y + ship.speed * Sin(ship.thrustAngle)
            'End If
            '                                                                    jump borders
            'If ship.x < 0 Then ship.x = xmax - Abs(ship.x)
            'If ship.x > xmax Then ship.x = ship.x - xmax
            'If ship.y < 0 Then ship.y = ymax - Abs(ship.y)
            'If ship.y > ymax Then ship.y = ship.y - ymax


            For i = 0 To nBullets '                                               handle bullets
                If b(i).live = 0 And fire = 1 Then 'have inactive bullet to use
                    b(i).x = ship.x + 2 * bSpeed * Cos(ship.angle)
                    b(i).y = ship.y + 2 * bSpeed * Sin(ship.angle)
                    b(i).dx = bSpeed * Cos(ship.angle)
                    b(i).dy = bSpeed * Sin(ship.angle)
                    b(i).live = -1
                    bullets = bullets + 1
                    fire = 0
                End If
                If b(i).live Then 'new location
                    b(i).x = b(i).x + b(i).dx
                    b(i).y = b(i).y + b(i).dy
                    If b(i).x > 0 And b(i).x < xmax And b(i).y > 0 And b(i).y < ymax Then 'in bounds draw it

                        'bullet hit aliens?
                        For ai = 1 To alienN
                            If Sqr((aliens(ai).x - b(i).x) ^ 2 + (aliens(ai).y - b(i).y) ^ 2) < 30 Then
                                For br = 1 To 120
                                    Circle (aliens(ai).x, aliens(ai).y), br / 3, plasma~&(0)
                                Next
                                _Display
                                _Delay .05
                                hits = hits + 1
                                roundPoints = roundPoints + 100
                                points = points + 100
                                aliens(ai).live = 0
                                newAlien ai
                                b(i).live = 0
                                _Continue
                            End If
                        Next
                        For r = 1 To nRocks 'check for collision with rock
                            If r(r).live Then
                                If Sqr((r(r).x - b(i).x) ^ 2 + (r(r).y - b(i).y) ^ 2) < r(r).r Then 'its a hit!
                                    r(r).explodeFrame = 1 'linger with explosion
                                    r(r).live = 0
                                    hits = hits + 1
                                    rockPoints = 50 * ((r(r).speed - minRockSpeed) / rockSpeedRange + (rockSizeRange - (r(r).r - minRockSize)) / rockSizeRange)
                                    roundPoints = roundPoints + rockPoints
                                    points = points + rockPoints
                                    If r(r).r > 30 Then '       split rock  into ? new ones
                                        maxBabyRocks = Int((r(r).r - 10) / 10)
                                        maxBabyRocks = irnd&(2, maxBabyRocks) ' pick a number of baby Rocks
                                        For br = 1 To maxBabyRocks
                                            '                        new rock
                                            newRockN = freeRock& '                          get inactive rock number
                                            newRock newRockN '                              new identity and activate
                                            r(newRockN).r = (r(r).r - 10) / maxBabyRocks '  split in equal parts minus 20% mass
                                            r(newRockN).x = r(r).x + irnd&(-30, 30) '       thrown from parent
                                            r(newRockN).y = r(r).y + irnd&(-30, 30)
                                            r(newRockN).c = r(r).c '                   same color as parent
                                            r(newRockN).heading = rrnd(ship.angle - .75 * pi, ship.angle + .75 * pi)
                                        Next
                                    End If ' big enough to split
                                    b(i).live = 0 'kill bullet
                                End If ' hit rock
                            End If 'rock is there
                        Next ' rock
                        If b(i).live Then fcirc b(i).x, b(i).y, 3, _RGB32(255, 255, 0) 'draws bullet
                    Else
                        b(i).live = 0 'out of bounds
                    End If ' bullet is in bounds
                End If ' bullet live
            Next ' bullet
        End If ' if ship still live
        _Display
        If ship.live = 0 Then
            lives = lives - 1
            If lives Mod 4 = 0 Then rocks = rocks + 1
            If lives Mod 4 = 2 Then alienN = alienN + 1
            s$ = "Lives:" + Str$(lives) + "  Hits:" + Str$(hits) + "  Bullets:" + Str$(bullets) + "  Shooting:" + Str$(Int(hits * 100 / bullets)) + "%"
            _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2 - 80), s$
            _Font fnt2
            s$ = Str$(points) + hs$
            _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2), s$
            _Font fnt
            s$ = "Press enter to enter next life."
            _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2 + 120), s$
            _Display
            kh = 0
            While kh <> 13
                kh = _KeyHit
            Wend 'wait for enter key
        Else
            _Limit 60 ' if ship dies let's rest and regroup  before restart next life
        End If
    Wend
    _Display
Wend
If points > HS Then
    Open "Asteroids High Score.txt" For Output As #1
    Print #1, points
    Close #1
End If
ship.x = -200: ship.y = -200 'get it out of the way
i = 0
Do
    drawStars 0
    i = i + 1
    If i Mod 30 = 29 And rocks < nRocks Then rocks = rocks + 1: r(rocks).live = 1
    For r = 1 To nRocks
        If r(r).live Then drawRock r
    Next
    s$ = "Lives:" + Str$(lives) + "  Hits:" + Str$(hits) + "  Bullets:" + Str$(bullets) + "  Shooting:" + Str$(Int(hits * 100 / bullets)) + "%"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2 - 80), s$
    _Font fnt2
    s$ = Str$(points)
    If points > HS Then s$ = s$ + " a New Record!" Else s$ = Str$(points) + hs$
    _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2), s$
    _Font fnt
    s$ = "Press q to quit, p or a to Play Again..."
    _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2 + 120), s$
    If _KeyDown(Asc("a")) Or _KeyDown(Asc("p")) Then GoTo restart
    _Display
    _Limit 60
Loop Until _KeyDown(Asc("q"))
System

Sub drawStars (moving)
    Type starType
        x As Single
        y As Single
        size As Single
        c As Integer
    End Type
    Static beenHere, stars(200) As starType, cy As Long
    Dim i As Long
    If beenHere = 0 Then 'static part
        For i = 0 To 100
            stars(i).x = Rnd * xmax: stars(i).y = Rnd * ymax: stars(i).size = .3
            stars(i).c = irnd&(80, 140)
        Next
        For i = 101 To 150
            stars(i).x = Rnd * xmax: stars(i).y = Rnd * ymax: stars(i).size = .6
            stars(i).c = irnd&(110, 170)
        Next
        For i = 151 To 195
            stars(i).x = Rnd * xmax: stars(i).y = Rnd * ymax: stars(i).size = 1.2
            stars(i).c = irnd&(140, 200)
        Next
        For i = 196 To 200
            stars(i).x = Rnd * xmax: stars(i).y = Rnd * ymax: stars(i).size = 2.4
            stars(i).c = irnd&(170, 235)
        Next
        cy = ymax / 2
        beenHere = 1
    End If
    For i = 0 To cy
        Line (0, i)-(xmax, i), _RGB32(0, 0, .1 * i + 4)
        Line (0, ymax - i)-(xmax, ymax - i), _RGB(0, 0, .1 * i + 4)
    Next
    For i = 0 To 200
        If moving Then
            stars(i).x = stars(i).x + .2 * stars(i).size ^ stars(i).size
            If stars(i).x > xmax Then stars(i).x = -1 * Rnd * 20
        End If
        fcirc stars(i).x, stars(i).y, stars(i).size, _RGB32(stars(i).c - 10, stars(i).c, stars(i).c + 10)
    Next
End Sub

Sub newAlien (i As Long)
    Dim side As Long, heading
    Randomize Timer * Rnd 'to avoid making twins
    side = irnd&(1, 4) 'bring rock in from one side, need to set heading according to side
    If Rnd < .6 Then
        aliens(i).fireX = ship.x: aliens(i).fireY = ship.y
    Else
        aliens(i).fireX = irnd(10, xmax - 10): aliens(i).fireY = irnd(10, ymax - 10)
    End If
    aliens(i).attackFrame = irnd(50, 400) ' EDIT a tweak to survive a little long before getting murdered with low lives over and over...
    Select Case side
        Case 1
            aliens(i).x = -10
            aliens(i).y = rrnd(80, ymax - 80)
        Case 2
            aliens(i).x = xmax + 10
            aliens(i).y = rrnd(80, ymax - 80)
        Case 3
            aliens(i).x = rrnd(80, xmax - 80)
            aliens(i).y = -10
        Case 4
            aliens(i).x = rrnd(80, xmax - 80)
            aliens(i).y = ymax + 10
    End Select
    heading = _Atan2(aliens(i).fireY - aliens(i).y, aliens(i).fireX - aliens(i).x)
    aliens(i).dx = alienSpeed * Cos(heading)
    aliens(i).dy = alienSpeed * Sin(heading)
    aliens(i).live = 0
    aliens(i).transform = 0
    aliens(i).c = _RGB32(irnd(128, 255), irnd(0, 255), irnd(0, 255))
End Sub

Function plasma~& (new As Long)
    Static r, g, b, cnt, beenHere
    If beenHere = 0 Or new Then
        r = Rnd: g = Rnd: b = Rnd: beenHere = 1: cnt = 0
    End If
    cnt = cnt + .2
    plasma~& = _RGB32(127 + 127 * Sin(r * cnt), 127 + 127 * Sin(g * cnt), 127 + 127 * Sin(b * cnt))
End Function

Sub drawAliens (i As Long) 'shipType
    Dim light As Long, heading, r As Long, g As Long, b As Long
    If aliens(i).live Then
        Sound 6000 + i * 200, .07
        If aliens(i).transform = 0 Then
            r = _Red32(aliens(i).c): g = _Green32(aliens(i).c): b = _Blue32(aliens(i).c)
            fellipse aliens(i).x, aliens(i).y, 6, 15, _RGB32(r, g - 120, b - 100)
            fellipse aliens(i).x, aliens(i).y, 18, 11, _RGB32(r, g - 60, b - 50)
            fellipse aliens(i).x, aliens(i).y, 30, 7, _RGB32(r, g, b)
            For light = 0 To 5
                fcirc aliens(i).x - 30 + 11 * light + aliens(i).ls, aliens(i).y, 1, _RGB32(aliens(i).ls * 50, aliens(i).ls * 50, aliens(i).ls * 50)
            Next
            aliens(i).ls = aliens(i).ls + 1
            If aliens(i).ls > 5 Then aliens(i).ls = 0
        Else
            drawBall aliens(i).x, aliens(i).y, 30, aliens(i).c
        End If
        'time to shoot?
        aliens(i).x = aliens(i).x + aliens(i).dx
        aliens(i).y = aliens(i).y + aliens(i).dy
        If Sqr((aliens(i).fireX - aliens(i).x) ^ 2 + (aliens(i).fireY - aliens(i).y) ^ 2) < 5 Then 'transform into the bolder of death
            aliens(i).transform = 1
            heading = _Atan2(ship.y - aliens(i).y, ship.x - aliens(i).x)
            aliens(i).dx = 2.5 * Cos(heading)
            aliens(i).dy = 2.5 * Sin(heading)
        End If
        If aliens(i).x < -10 Or aliens(i).x > xmax + 10 Then
            If aliens(i).y < -10 Or aliens(i).y > ymax + 10 Then '  out of bounds goodbye bolder of death!
                aliens(i).live = 0 'man we dodged a bullet here!!!!
                newAlien i 'reset the trap
            End If
        End If
    Else
        If aliens(i).attackFrame Then
            aliens(i).attackFrame = aliens(i).attackFrame - 1
            If aliens(i).attackFrame = 0 Then
                aliens(i).live = 1
            End If
        End If
    End If
End Sub

Function freeRock&
    Dim i As Long
    For i = rocks + 1 To nRocks ' look for inactive rock number
        If r(i).live = 0 And r(i).explodeFrame = 0 Then freeRock& = i: Exit Function
    Next
End Function

Sub explode (x As Long, y As Long, r As Long, frm As Long)
    Dim maxParticles As Long, i As Long, rounds As Long, loopCount As Long
    maxParticles = r * 4
    For i = 1 To r
        NewDot i, x, y, r
    Next
    rounds = r
    For loopCount = 0 To frm
        If _KeyDown(27) Then End
        For i = 1 To rounds
            dots(i).x = dots(i).x + dots(i).dx
            dots(i).y = dots(i).y + dots(i).dy
            fcirc dots(i).x, dots(i).y, dots(i).size, dots(i).kolor
        Next
        If rounds < maxParticles Then
            For i = 1 To r
                NewDot (rounds + i), x, y, r
            Next
            rounds = rounds + r
        End If
    Next
End Sub

Sub NewDot (i As Long, x As Long, y As Long, r As Long)
    Dim angle, rd
    angle = pi * 2 * Rnd
    rd = Rnd * 30
    dots(i).x = x + rd * Cos(angle)
    dots(i).y = y + rd * Sin(angle)
    dots(i).size = Rnd * r * .05
    rd = Rnd 'STxAxTIC recommended for rounder spreads
    dots(i).dx = rd * 10 * (10 - 2 * dots(i).size) * Cos(angle)
    dots(i).dy = rd * 10 * (10 - 2 * dots(i).size) * Sin(angle)
    rd = 20 + Rnd * 70
    dots(i).kolor = _RGBA32(rd, rd, rd, 80)
End Sub

Sub drawship 'simple red iso triangle pointed towards radianAngle
    Dim x1 As Long, y1 As Long, x2 As Long, y2 As Long, x3 As Long, y3 As Long
    Dim x4 As Long, y4 As Long, x5 As Long, y5 As Long
    If ship.thrust > 0 Then
        'burn out flame as thruster dies out
        x4 = ship.x + .5 * ship.thrust * Cos(ship.angle - 17 / 18 * pi)
        y4 = ship.y + .5 * ship.thrust * Sin(ship.angle - 17 / 18 * pi)
        x5 = ship.x + .5 * ship.thrust * Cos(ship.angle - 19 / 18 * pi)
        y5 = ship.y + .5 * ship.thrust * Sin(ship.angle - 19 / 18 * pi)
        ftri ship.x, ship.y, x4, y4, x5, y5, &H99FFFF88
        ship.thrust = ship.thrust - 1
    End If
    'draw ship dead or alive thrust or not, calculate 3 points of triangle ship
    fcirc ship.x, ship.y, 30, &H05FFFFFF
    x1 = ship.x + 30 * Cos(ship.angle) ' front point
    y1 = ship.y + 30 * Sin(ship.angle) '
    x2 = ship.x + 30 * Cos(ship.angle + .6666 * pi) ' wing
    y2 = ship.y + 30 * Sin(ship.angle + .6666 * pi)
    x3 = ship.x + 30 * Cos(ship.angle - .6666 * pi) ' other wing
    y3 = ship.y + 30 * Sin(ship.angle - .6666 * pi)
    ftri ship.x, ship.y, x1, y1, x2, y2, _RGB32(80, 120, 80, 80)
    ftri ship.x, ship.y, x1, y1, x3, y3, _RGB32(60, 100, 60, 80)
    Line (x1, y1)-(ship.x, ship.y), _RGB32(255, 255, 128)
    Line (x1, y1)-(x2, y2), _RGB32(255, 180, 40)
    Line (x1, y1)-(x3, y3), _RGB32(255, 180, 40)
End Sub

Sub drawRock (iRock)
    Randomize Using r(iRock).seed 'this prevents having to save a particular sequence of random number
    Dim dx, dy, j As Long, rRad As Single, leg As Single, x0 As Long, y0 As Long, rc As Long, c~&, x1 As Long, y1 As Long, xoff, yoff, i As Long
    Dim x2 As Long, y2 As Long
    dx = r(iRock).speed * Cos(r(iRock).heading)
    dy = r(iRock).speed * Sin(r(iRock).heading) 'update location
    r(iRock).ra = r(iRock).ra + r(iRock).spin
    If r(iRock).x + dx + r(iRock).r < 0 Or r(iRock).x + dx - r(iRock).r > xmax Or r(iRock).y + dy + r(iRock).r < 0 Or r(iRock).y + dy - r(iRock).r > ymax Then
        If iRock <= rocks Then newRock iRock Else r(iRock).live = 0
        Exit Sub ' reassigned get out of here
    Else
        r(iRock).x = r(iRock).x + dx
        r(iRock).y = r(iRock).y + dy
    End If
    For j = 10 To 3 Step -1 '                  rock drawing (see demo program where developed code)
        rRad = .1 * j * r(iRock).r
        leg = rRad * (Rnd * .7 + .3)
        x0 = r(iRock).x + leg * Cos(r(iRock).ra)
        y0 = r(iRock).y + leg * Sin(r(iRock).ra)
        rc = r(iRock).c + 30 * Rnd - 15
        c~& = _RGB32(rc + 5, rc - 10, rc + 5)
        x1 = x0
        y1 = y0
        xoff = Rnd * 20 - 10 + r(iRock).x
        yoff = Rnd * 20 - 10 + r(iRock).y
        For i = 1 To 12
            leg = rRad * (Rnd * .35 + .65)
            If i = 12 Then
                x2 = x0: y2 = y0
            Else
                x2 = xoff + leg * Cos(i * polyAngle + r(iRock).ra)
                y2 = yoff + leg * Sin(i * polyAngle + r(iRock).ra)
            End If
            ftri r(iRock).x, r(iRock).y, x1, y1, x2, y2, c~&
            x1 = x2: y1 = y2
        Next
    Next
End Sub

Sub newRock (iRock)
    Dim side As Long
    Randomize Timer * Rnd 'to avoid making twins
    side = irnd&(1, 4) 'bring rock in from one side, need to set heading according to side
    Select Case side
        Case 1
            r(iRock).x = -100
            r(iRock).y = rrnd(80, ymax - 80)
            If Rnd < .25 Then r(iRock).heading = _Atan2(ship.y - r(iRock).y, ship.x - r(iRock).x) Else r(iRock).heading = 3 * pi / 2 + Rnd * pi
        Case 2
            r(iRock).x = xmax + 100
            r(iRock).y = rrnd(80, ymax - 80)
            If Rnd < .25 Then r(iRock).heading = _Atan2(ship.y - r(iRock).y, ship.x - r(iRock).x) Else r(iRock).heading = pi / 2 + Rnd * pi
        Case 3
            r(iRock).x = rrnd(80, xmax - 80)
            r(iRock).y = -100
            If Rnd < .25 Then r(iRock).heading = _Atan2(ship.y - r(iRock).y, ship.x - r(iRock).x) Else r(iRock).heading = Rnd * pi
        Case 4
            r(iRock).x = rrnd(80, xmax - 80)
            r(iRock).y = ymax + 100
            If Rnd < .25 Then r(iRock).heading = _Atan2(ship.y - r(iRock).y, ship.x - r(iRock).x) Else r(iRock).heading = pi + Rnd * pi
    End Select
    r(iRock).speed = rrnd(minRockSpeed, maxRockSpeed) 'speed, rotation angle, radius, gray coloring, spin, seed, hit for explosion
    r(iRock).ra = Rnd * 2 * pi
    r(iRock).r = irnd&(minRockSize * 3, maxRockSize) 'every parent rock can be split up into at least 2 - 10 size rocks
    r(iRock).c = irnd&(60, 110) ' Ken request increase in rock color
    r(iRock).spin = rrnd(-pi / 20, pi / 20)
    r(iRock).seed = Int(Rnd * 64000) - 32000
    r(iRock).explodeFrame = 0
    r(iRock).live = 1
End Sub

Function irnd& (n1, n2) 'return an integer between 2 numbers
    Dim l%, h%
    If n1 > n2 Then l% = n2: h% = n1 Else l% = n1: h% = n2
    irnd& = Int(Rnd * (h% - l% + 1)) + l%
End Function

Function rrnd (n1, n2) ' return number (expecting reals =_single, double, _float depending on default / define setup)
    rrnd = (n2 - n1) * Rnd + n1
End Function

Sub drawBall (x, y, r, c As _Unsigned Long)
    Dim red As Long, grn As Long, blu As Long, rr As Long, f
    red = _Red32(c): grn = _Green32(c): blu = _Blue32(c)
    For rr = r To 0 Step -1
        f = 1 - rr / r
        fcirc x, y, rr, _RGB32(red * f, grn * f, blu * f)
    Next
End Sub

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 ftri (x1, y1, x2, y2, x3, y3, K As _Unsigned Long)
    Dim D As Long
    Static a&
    D = _Dest
    If a& = 0 Then a& = _NewImage(1, 1, 32)
    _Dest a&
    _DontBlend a& '  '<<<< new 2019-12-16 fix
    PSet (0, 0), K
    _Blend a& '<<<< new 2019-12-16 fix
    _Dest D
    _MapTriangle _Seamless(0, 0)-(0, 0)-(0, 0), a& To(x1, y1)-(x2, y2)-(x3, y3)
End Sub

Sub fcirc (x As Long, y As Long, R As Long, C As _Unsigned Long) 'vince version
    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

The zip has source and font file if you don't have Windows.

   



Attached Files
.zip   b+ Asteroids M11.zip (Size: 40.21 KB / Downloads: 84)
Print this item

  b+ Asteroids M11
Posted by: bplus - 06-01-2022, 01:21 AM - Forum: Games - No Replies

Oldie but goodie:

Code: (Select All)
Option _Explicit
_Title "b+QB64 Asteroids M11" 'started 2018-07-13
'2020 - Oct and Nov: Special thanks to SierraKen who has been helpful with suggestions, ideas and fixes.
Randomize Timer

' Far far away in a universe where Newton's 1st Law of Motion is not obeyed...    ;-))


'                                            New Controls
' key a or left arrow = turn left
' key s or right arrow = turn right
' enter or spacebar =  "hyperspace" show up somewhere else  better or worse use when collision is imminent
' key k or up arrow = thrust  just a burst in direction you are pointed but doesn't last
' fire! owwha, owha, owha...  is now continuous

'=================================================================================================================

' 2020-10-27 remove the alternate subs and get down below 200 LOC almost there now! new shooter action font and background
' 2020-10-28 another makeover explosions and split asteroids
' 2020-10-29 fix baby rock management, break between lives
' 2020-10-29 fix left/right gun, fix explosions over many frames to eliminate pause in action, speed up 60 fps
' 2020-10-30 m3 SierraKen's idea to angle shooter with mousewheel also finish WASD options, more rocks, points system
' points:
' The higher the speed the better    speed range .5 to 2.5, diff = 2 * 50 = 100          s - .5 * 50
' DELETE The lower the color the better   color range 10 to 60, diff =      50 * 2 = 100  50 - (c - 10) * 2
' The smaller the size the better  size range 10 to 100, diff = 90 * 1.1111 = 100  90 - (sz -10) * 1.1111
'        ((speed - .5) * 50 + (90 - (r - 10)) * 1.1111) / 2 = 100 best score per hit
' 2020-10-30 increase level of difficulty, fix double lives lost, add an ending after all lives spent.
' 2020-10-31 M4 They are Here - the aliens have accepted my invitaion for war games don't get caught in their beam.
' rework ending and variable LONG suffix. Aliens on the attack 100 points before or after transformed into the Bolder of Death.
' 2020-11-01 M5 FX Moving through space, Oh yeah, more aliens!
' 2020-11-01 M6 add play again and save high game , continuous shoot
' 2020-11-01 M7 fix hits count when hit alien ship or Bolder of Death. Fix lights on aliens ship. I want to see collsions with ship.
' Ken recommends removing text in middle of screen, yeah, distracting. Makeover ship as with mouse x, y it's center. Add Splash screen.
' Show mouse in between lives so can be in screen center when press key to start next run.

' 2020-11-03 M9 watching videos on Asteroids, I found a view and description of the control panel, it is:
' 2 buttons on left and lower button in middle and 2 buttons on right level with 2 on left.
' 2 buttons on left are left and right turns they will be Keys A and S
' The middle will be spacebar for Hypespace or these days we might call it worm hole, jump to another location better or worse!
' 2 buttons on right K and L: K will be a thruster of sorts you eventually come to a stop (not what Newton would like) L is the fire button.
' So let's try that. I am impressed by hitting thruster and shooting all around as you dift in direction gun was pointed when hit thruster.
' Lighten rocks and change points, based now only on size and speed of Asteroids.
' 2020-11-04 M9 Install new update to thrust control. Thanks to SierraKen for finding out what happened to Hyperspace jump when press
' spacebar a 2nd time in a life. Oh also Boulders of Death are now more like smooth colored spheres. Ahhhh Fixed the crashing at the borders
' now fly out one side and come back in on the other! This is getting good! Adding some sound effects.
' 2020-11-05 M10 set some constants for starting game so can just change them to level of play desired.
' speed points =  50 * (rockSpeed - minRockSpeed)/rockSpeedRange > fastest rock gets 50 points
' size points = 50 * (rockSizeRange - (rockSize - minRockSize))/rockSizeRange  > smallest rock gets 50 points
' points = points +  50 * ( (rockSpeed - minRockSpeed)/rockSpeedRange +  (rockSizeRange - (rockSize - minRockSize))/rockSizeRange )
' Dav's comments: Add arrow keys for left right turns and up for thrust, continuous fire now! A little score board in top left corner.
' Dav I am keeping spacebar for Hyperspace but also Enter, don't need fire button now.
' Try the A for left and the right arrow for right turns.
' 2020-11-05 p is for pause
' 2020-11-06 Move it or lose it!
' 2020-11-06 Put the dang mouse back in!  Press m for mouse target rich environment! Use left and right mouse buttons to aim guns,
' 2020-11-08 add mouse mode you can enter at splash screen,

' 2021-09-24 b+QB64 Asteroids M11 best mouse only
' fix p for play again because it is also pause
' don't like enter to start next life, spacebar preferred

'================================================================================================================

'    NOTE: !!!!!!!!!!!!!!!   When there is a pause in action, just press enter,  as in enter a new life!

'================================================================================================================

Const xmax = 1200, ymax = 700, pi = _Pi, polyAngle = _Pi / 6, nRocks = 300, nBullets = 2000, bSpeed = 15
Const startRocks = 2, minRockSpeed = 0.7500, maxRockSpeed = 3.2500, minRockSize = 10, maxRockSize = 100
Const rockSpeedRange = maxRockSpeed - minRockSpeed, rockSizeRange = maxRockSize - minRockSize
Const alienSpeed = rockSpeedRange / 2 + minRockSpeed ' average rock speed

Type alienType
    x As Single
    y As Single
    dx As Single
    dy As Single
    ls As Long ' lights offset and gray scale
    c As _Unsigned Long ' color
    live As Long
    attackFrame As Long
    fireX As Single
    fireY As Single
    transform As Long
End Type

Type particle
    x As Single
    y As Single
    dx As Single
    dy As Single
    size As Single
    kolor As _Unsigned Long
End Type

Type bullet
    x As Single
    y As Single
    dx As Single
    dy As Single
    live As Long
End Type

Type shipType
    x As Single
    y As Single
    live As Long
    speed As Single '       just a constant now when Thrust is applied
    thrustAngle As Single ' ship/gun angle at moment Thrust is pressed
    angle As Single '       rotated position ship/gun now A or S keypress or hold down
    thrust As Long '        this now tracks how many frames ship will move at speed and thrustAngle
End Type

Type rock
    x As Single
    y As Single
    r As Long '            radius
    ra As Single '         rotation position   a = a + spin
    heading As Single '    heading from which dx, dy are calc with speed
    speed As Single '      speed
    spin As Single '       rotation direction and amount
    seed As Long '         for drawing rocks with RND USING
    c As Long '            color   rgb(c, c, c)
    live As Long '         need this to track rocks still active like bullets
    explodeFrame As Long ' after a rock is hit by bullet, it explodes and in more than one frame
End Type

ReDim Shared aliens(1 To 5) As alienType
Dim Shared dots(2000) As particle ' explosions
Dim Shared b(nBullets) As bullet
Dim Shared ship As shipType
Dim Shared r(nRocks) As rock
Dim Shared lives As Long
Dim Shared points As Long
Dim Shared rocks As Long 'rocks is the minimum number of parent rocks to have on screen  automatic replace when hit or out of bounds

Dim HS As Long, fnt As Long, fnt2 As Long ' file LOAD handles
Dim i As Long, bullets As Long, fire As Long ' index and bullets
Dim r As Long, newRockN As Long, maxBabyRocks As Long, br As Long, hits As Long ' rock stuff
Dim ai As Long, alienN As Long ' alien index and number
Dim kh As Long 'key hit for ship thrust k or up arrow or hyperspace jump spacebar
Dim hs$, s$, k$, t, lastt 'high score, general string and times for bullets
Dim rockPoints As Long, roundPoints As Long ' various points scores

'Dim mouseMode As Long 'either key press hyperdrive and thrust or mouse moves
ship.speed = 3.5 'this would be a constant but ship has to declared as Ship Type first, Hyperspace jump

Screen _NewImage(xmax, ymax, 32)
_ScreenMove 100, 20
_FullScreen

fnt = _LoadFont("ARLRDBD.ttf", 16, "MONOSPACE")
fnt2 = _LoadFont("ARLRDBD.ttf", 40, "MONOSPACE")
_Font fnt2
Color &HFF00FFFF, &H00000000

If _FileExists("Asteroids High Score.txt") Then
    Open "Asteroids High Score.txt" For Input As #1
    Input #1, HS
    Close #1
End If
hs$ = "High Score:" + Str$(HS)

'a little splash screen
rocks = 7: alienN = 3
For i = 1 To nRocks
    newRock i
    If i > rocks Then r(i).live = 0
Next
For i = 1 To alienN
    newAlien i
Next
i = 0
Do
    drawStars 0
    i = i + 1
    If i Mod 30 = 29 And rocks < nRocks Then rocks = rocks + 1: r(rocks).live = 1
    For r = 1 To nRocks
        If r(r).live Then drawRock r
    Next
    For i = 1 To alienN
        drawAliens i
    Next

    _Font fnt2
    s$ = "*** b+QB64 Asteroids ***"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, 60), s$
    _PrintString ((_Width - _PrintWidth(hs$)) / 2, 140), hs$
    s$ = "A or arrow = Left Spin"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, 300), s$
    s$ = "S or arrow = Right Spin"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, 380), s$
    's$ = "Space or Enter = Hyper Jump"
    '_PrintString ((_Width - _PrintWidth(s$)) / 2, 380), s$
    s$ = "p = Pause or end Pause"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, 460), s$
    s$ = "Mouse L/R Button = Guns"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, 540), s$
    _Font fnt
    If _KeyDown(27) Then System
    k$ = InKey$
    _Display
    _Limit 60
Loop Until Len(k$)

_MouseHide
restart:
If _FileExists("Asteroids High Score.txt") Then
    Open "Asteroids High Score.txt" For Input As #1
    Input #1, HS
    Close #1
End If
hs$ = "  High Score:" + Str$(HS)
lives = 10: alienN = 1: rocks = startRocks ' always active rocks
points = 0: hits = 0: bullets = 0

'If k$ = "m" Then ' undocumented use of mouse
'mouseMode = -1:
alienN = 3: rocks = 5 ' M11 mouse mode assumed only other key is space or enter for hyper jump
'End If

While lives > 0 And _KeyDown(27) = 0 ' init start restart
    ReDim aliens(1 To alienN) As alienType
    For ai = 1 To alienN
        newAlien ai
    Next
    For i = 1 To nRocks 'reset rocks mainly clear baby rocks
        newRock (i)
        If i > rocks Then r(i).live = 0
    Next
    ship.x = xmax / 2 'avoids explosions top left corner at start, dang still get some!
    ship.y = ymax / 2
    ship.angle = 0
    ship.thrustAngle = 0
    ship.thrust = 0
    ship.live = 1
    rockPoints = 0
    roundPoints = 0
    While ship.live And _KeyDown(27) = 0

        'draw everything then process bullets
        drawStars 1
        Locate 1, 1: Print "Lives:"; lives
        Locate 2, 1: Print "Last Rock:"; Str$(rockPoints)
        Locate 3, 1: Print "Round:;"; Str$(roundPoints)
        Locate 4, 1: Print "Points:"; Str$(points)
        Locate 5, 1: Print "High Score:"; Str$(HS)

        For ai = 1 To alienN
            drawAliens ai
        Next
        For i = 1 To nRocks
            If r(i).live Then 'make sure if we crash into a ship it is a live rock, not one sitting on side waiting to be called up
                drawRock i ' while drawing rocks the ship could be blown up
                If ((r(i).x - ship.x) ^ 2 + (r(i).y - ship.y) ^ 2) ^ .5 < r(i).r + 20 Then 'rock collides with ship?
                    For br = 1 To 200 Step 5
                        Circle ((ship.x + r(i).x) / 2, (ship.y + r(i).y) / 2), br, _RGB32(255 - br, 255 - 2 * br, 0)
                    Next
                    drawRock i
                    drawship
                    ship.live = 0
                    If i <= rocks Then newRock i Else r(i).live = 0
                End If
            End If
        Next
        For i = 1 To nRocks 'smoke up the place with rock debris fields still flying out from hit frames ago
            If r(i).explodeFrame Then
                r(i).explodeFrame = r(i).explodeFrame + 1
                If r(i).explodeFrame > .25 * r(i).r Then
                    r(i).explodeFrame = 0
                    If i <= rocks Then newRock i ' now replace the rock
                Else
                    explode r(i).x, r(i).y, r(i).r, r(i).explodeFrame
                End If
            End If
        Next
        If ship.live Then
            For ai = 1 To alienN
                If Sqr((aliens(ai).x - ship.x) ^ 2 + (aliens(ai).y - ship.y) ^ 2) < 55 Then 'aliens and ship collisde boom boom
                    For br = 1 To 200 Step 5
                        Circle ((ship.x + aliens(ai).x) / 2, (ship.y + aliens(ai).y) / 2), br, _RGB32(255 - br, 255 - 2 * br, 0)
                    Next
                    drawship
                    ship.live = 0
                    _Continue
                Else
                    drawship
                End If
            Next
            'If mouseMode Then
            While _MouseInput: Wend
            ship.x = _MouseX: ship.y = _MouseY
            If _MouseButton(1) Then ship.angle = pi
            If _MouseButton(2) Then ship.angle = 0
            'End If
            '                                                                   ship controls update
            'a key or left arrow = left spin
            If _KeyDown(97) Or _KeyDown(19200) Then ship.angle = ship.angle - pi / 48

            's key or right arrow = right spin
            If _KeyDown(115) Or _KeyDown(19712) Then ship.angle = ship.angle + pi / 48

            'l is Fire!  JUST CONTINUOUS FIRE
            fire = 0
            'IF _KEYDOWN(108) THEN
            t = Timer(.01)
            If lastt = 0 Or t - lastt > .15 Then fire = 1: Sound 2088, .01: lastt = t
            'END IF

            kh = _KeyHit
            Select Case kh
                Case 112 ' p for pause
                    kh = 0
                    While _KeyHit <> 112: _Limit 60: Wend
                    'Case 107, 18432 'thrust  k key or up arrow
                    '    If mouseMode = 0 Then
                    '        Sound 488, .01
                    '        ship.thrustAngle = ship.angle: ship.thrust = 120
                    '    End If
                    'Case 13, 32 ' space = hyperspace jump
                    '    'If mouseMode = 0 Then
                    '    Randomize Timer
                    '    ship.x = (xmax - 300) * Rnd + 150: ship.y = (ymax - 300) * Rnd + 150: ship.thrust = 0
                    '    'End If
            End Select

            '                                                                   locate ship
            'If ship.thrust > 0 Then
            '    'relocate ship position
            '    ship.x = ship.x + ship.speed * Cos(ship.thrustAngle)
            '    ship.y = ship.y + ship.speed * Sin(ship.thrustAngle)
            'End If
            '                                                                    jump borders
            'If ship.x < 0 Then ship.x = xmax - Abs(ship.x)
            'If ship.x > xmax Then ship.x = ship.x - xmax
            'If ship.y < 0 Then ship.y = ymax - Abs(ship.y)
            'If ship.y > ymax Then ship.y = ship.y - ymax


            For i = 0 To nBullets '                                               handle bullets
                If b(i).live = 0 And fire = 1 Then 'have inactive bullet to use
                    b(i).x = ship.x + 2 * bSpeed * Cos(ship.angle)
                    b(i).y = ship.y + 2 * bSpeed * Sin(ship.angle)
                    b(i).dx = bSpeed * Cos(ship.angle)
                    b(i).dy = bSpeed * Sin(ship.angle)
                    b(i).live = -1
                    bullets = bullets + 1
                    fire = 0
                End If
                If b(i).live Then 'new location
                    b(i).x = b(i).x + b(i).dx
                    b(i).y = b(i).y + b(i).dy
                    If b(i).x > 0 And b(i).x < xmax And b(i).y > 0 And b(i).y < ymax Then 'in bounds draw it

                        'bullet hit aliens?
                        For ai = 1 To alienN
                            If Sqr((aliens(ai).x - b(i).x) ^ 2 + (aliens(ai).y - b(i).y) ^ 2) < 30 Then
                                For br = 1 To 120
                                    Circle (aliens(ai).x, aliens(ai).y), br / 3, plasma~&(0)
                                Next
                                _Display
                                _Delay .05
                                hits = hits + 1
                                roundPoints = roundPoints + 100
                                points = points + 100
                                aliens(ai).live = 0
                                newAlien ai
                                b(i).live = 0
                                _Continue
                            End If
                        Next
                        For r = 1 To nRocks 'check for collision with rock
                            If r(r).live Then
                                If Sqr((r(r).x - b(i).x) ^ 2 + (r(r).y - b(i).y) ^ 2) < r(r).r Then 'its a hit!
                                    r(r).explodeFrame = 1 'linger with explosion
                                    r(r).live = 0
                                    hits = hits + 1
                                    rockPoints = 50 * ((r(r).speed - minRockSpeed) / rockSpeedRange + (rockSizeRange - (r(r).r - minRockSize)) / rockSizeRange)
                                    roundPoints = roundPoints + rockPoints
                                    points = points + rockPoints
                                    If r(r).r > 30 Then '       split rock  into ? new ones
                                        maxBabyRocks = Int((r(r).r - 10) / 10)
                                        maxBabyRocks = irnd&(2, maxBabyRocks) ' pick a number of baby Rocks
                                        For br = 1 To maxBabyRocks
                                            '                        new rock
                                            newRockN = freeRock& '                          get inactive rock number
                                            newRock newRockN '                              new identity and activate
                                            r(newRockN).r = (r(r).r - 10) / maxBabyRocks '  split in equal parts minus 20% mass
                                            r(newRockN).x = r(r).x + irnd&(-30, 30) '       thrown from parent
                                            r(newRockN).y = r(r).y + irnd&(-30, 30)
                                            r(newRockN).c = r(r).c '                   same color as parent
                                            r(newRockN).heading = rrnd(ship.angle - .75 * pi, ship.angle + .75 * pi)
                                        Next
                                    End If ' big enough to split
                                    b(i).live = 0 'kill bullet
                                End If ' hit rock
                            End If 'rock is there
                        Next ' rock
                        If b(i).live Then fcirc b(i).x, b(i).y, 3, _RGB32(255, 255, 0) 'draws bullet
                    Else
                        b(i).live = 0 'out of bounds
                    End If ' bullet is in bounds
                End If ' bullet live
            Next ' bullet
        End If ' if ship still live
        _Display
        If ship.live = 0 Then
            lives = lives - 1
            If lives Mod 4 = 0 Then rocks = rocks + 1
            If lives Mod 4 = 2 Then alienN = alienN + 1
            s$ = "Lives:" + Str$(lives) + "  Hits:" + Str$(hits) + "  Bullets:" + Str$(bullets) + "  Shooting:" + Str$(Int(hits * 100 / bullets)) + "%"
            _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2 - 80), s$
            _Font fnt2
            s$ = Str$(points) + hs$
            _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2), s$
            _Font fnt
            s$ = "Press enter to enter next life."
            _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2 + 120), s$
            _Display
            kh = 0
            While kh <> 13
                kh = _KeyHit
            Wend 'wait for enter key
        Else
            _Limit 60 ' if ship dies let's rest and regroup  before restart next life
        End If
    Wend
    _Display
Wend
If points > HS Then
    Open "Asteroids High Score.txt" For Output As #1
    Print #1, points
    Close #1
End If
ship.x = -200: ship.y = -200 'get it out of the way
i = 0
Do
    drawStars 0
    i = i + 1
    If i Mod 30 = 29 And rocks < nRocks Then rocks = rocks + 1: r(rocks).live = 1
    For r = 1 To nRocks
        If r(r).live Then drawRock r
    Next
    s$ = "Lives:" + Str$(lives) + "  Hits:" + Str$(hits) + "  Bullets:" + Str$(bullets) + "  Shooting:" + Str$(Int(hits * 100 / bullets)) + "%"
    _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2 - 80), s$
    _Font fnt2
    s$ = Str$(points)
    If points > HS Then s$ = s$ + " a New Record!" Else s$ = Str$(points) + hs$
    _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2), s$
    _Font fnt
    s$ = "Press q to quit, p or a to Play Again..."
    _PrintString ((_Width - _PrintWidth(s$)) / 2, ymax / 2 + 120), s$
    If _KeyDown(Asc("a")) Or _KeyDown(Asc("p")) Then GoTo restart
    _Display
    _Limit 60
Loop Until _KeyDown(Asc("q"))
System

Sub drawStars (moving)
    Type starType
        x As Single
        y As Single
        size As Single
        c As Integer
    End Type
    Static beenHere, stars(200) As starType, cy As Long
    Dim i As Long
    If beenHere = 0 Then 'static part
        For i = 0 To 100
            stars(i).x = Rnd * xmax: stars(i).y = Rnd * ymax: stars(i).size = .3
            stars(i).c = irnd&(80, 140)
        Next
        For i = 101 To 150
            stars(i).x = Rnd * xmax: stars(i).y = Rnd * ymax: stars(i).size = .6
            stars(i).c = irnd&(110, 170)
        Next
        For i = 151 To 195
            stars(i).x = Rnd * xmax: stars(i).y = Rnd * ymax: stars(i).size = 1.2
            stars(i).c = irnd&(140, 200)
        Next
        For i = 196 To 200
            stars(i).x = Rnd * xmax: stars(i).y = Rnd * ymax: stars(i).size = 2.4
            stars(i).c = irnd&(170, 235)
        Next
        cy = ymax / 2
        beenHere = 1
    End If
    For i = 0 To cy
        Line (0, i)-(xmax, i), _RGB32(0, 0, .1 * i + 4)
        Line (0, ymax - i)-(xmax, ymax - i), _RGB(0, 0, .1 * i + 4)
    Next
    For i = 0 To 200
        If moving Then
            stars(i).x = stars(i).x + .2 * stars(i).size ^ stars(i).size
            If stars(i).x > xmax Then stars(i).x = -1 * Rnd * 20
        End If
        fcirc stars(i).x, stars(i).y, stars(i).size, _RGB32(stars(i).c - 10, stars(i).c, stars(i).c + 10)
    Next
End Sub

Sub newAlien (i As Long)
    Dim side As Long, heading
    Randomize Timer * Rnd 'to avoid making twins
    side = irnd&(1, 4) 'bring rock in from one side, need to set heading according to side
    If Rnd < .6 Then
        aliens(i).fireX = ship.x: aliens(i).fireY = ship.y
    Else
        aliens(i).fireX = irnd(10, xmax - 10): aliens(i).fireY = irnd(10, ymax - 10)
    End If
    aliens(i).attackFrame = irnd(50, 400) ' EDIT a tweak to survive a little long before getting murdered with low lives over and over...
    Select Case side
        Case 1
            aliens(i).x = -10
            aliens(i).y = rrnd(80, ymax - 80)
        Case 2
            aliens(i).x = xmax + 10
            aliens(i).y = rrnd(80, ymax - 80)
        Case 3
            aliens(i).x = rrnd(80, xmax - 80)
            aliens(i).y = -10
        Case 4
            aliens(i).x = rrnd(80, xmax - 80)
            aliens(i).y = ymax + 10
    End Select
    heading = _Atan2(aliens(i).fireY - aliens(i).y, aliens(i).fireX - aliens(i).x)
    aliens(i).dx = alienSpeed * Cos(heading)
    aliens(i).dy = alienSpeed * Sin(heading)
    aliens(i).live = 0
    aliens(i).transform = 0
    aliens(i).c = _RGB32(irnd(128, 255), irnd(0, 255), irnd(0, 255))
End Sub

Function plasma~& (new As Long)
    Static r, g, b, cnt, beenHere
    If beenHere = 0 Or new Then
        r = Rnd: g = Rnd: b = Rnd: beenHere = 1: cnt = 0
    End If
    cnt = cnt + .2
    plasma~& = _RGB32(127 + 127 * Sin(r * cnt), 127 + 127 * Sin(g * cnt), 127 + 127 * Sin(b * cnt))
End Function

Sub drawAliens (i As Long) 'shipType
    Dim light As Long, heading, r As Long, g As Long, b As Long
    If aliens(i).live Then
        Sound 6000 + i * 200, .07
        If aliens(i).transform = 0 Then
            r = _Red32(aliens(i).c): g = _Green32(aliens(i).c): b = _Blue32(aliens(i).c)
            fellipse aliens(i).x, aliens(i).y, 6, 15, _RGB32(r, g - 120, b - 100)
            fellipse aliens(i).x, aliens(i).y, 18, 11, _RGB32(r, g - 60, b - 50)
            fellipse aliens(i).x, aliens(i).y, 30, 7, _RGB32(r, g, b)
            For light = 0 To 5
                fcirc aliens(i).x - 30 + 11 * light + aliens(i).ls, aliens(i).y, 1, _RGB32(aliens(i).ls * 50, aliens(i).ls * 50, aliens(i).ls * 50)
            Next
            aliens(i).ls = aliens(i).ls + 1
            If aliens(i).ls > 5 Then aliens(i).ls = 0
        Else
            drawBall aliens(i).x, aliens(i).y, 30, aliens(i).c
        End If
        'time to shoot?
        aliens(i).x = aliens(i).x + aliens(i).dx
        aliens(i).y = aliens(i).y + aliens(i).dy
        If Sqr((aliens(i).fireX - aliens(i).x) ^ 2 + (aliens(i).fireY - aliens(i).y) ^ 2) < 5 Then 'transform into the bolder of death
            aliens(i).transform = 1
            heading = _Atan2(ship.y - aliens(i).y, ship.x - aliens(i).x)
            aliens(i).dx = 2.5 * Cos(heading)
            aliens(i).dy = 2.5 * Sin(heading)
        End If
        If aliens(i).x < -10 Or aliens(i).x > xmax + 10 Then
            If aliens(i).y < -10 Or aliens(i).y > ymax + 10 Then '  out of bounds goodbye bolder of death!
                aliens(i).live = 0 'man we dodged a bullet here!!!!
                newAlien i 'reset the trap
            End If
        End If
    Else
        If aliens(i).attackFrame Then
            aliens(i).attackFrame = aliens(i).attackFrame - 1
            If aliens(i).attackFrame = 0 Then
                aliens(i).live = 1
            End If
        End If
    End If
End Sub

Function freeRock&
    Dim i As Long
    For i = rocks + 1 To nRocks ' look for inactive rock number
        If r(i).live = 0 And r(i).explodeFrame = 0 Then freeRock& = i: Exit Function
    Next
End Function

Sub explode (x As Long, y As Long, r As Long, frm As Long)
    Dim maxParticles As Long, i As Long, rounds As Long, loopCount As Long
    maxParticles = r * 4
    For i = 1 To r
        NewDot i, x, y, r
    Next
    rounds = r
    For loopCount = 0 To frm
        If _KeyDown(27) Then End
        For i = 1 To rounds
            dots(i).x = dots(i).x + dots(i).dx
            dots(i).y = dots(i).y + dots(i).dy
            fcirc dots(i).x, dots(i).y, dots(i).size, dots(i).kolor
        Next
        If rounds < maxParticles Then
            For i = 1 To r
                NewDot (rounds + i), x, y, r
            Next
            rounds = rounds + r
        End If
    Next
End Sub

Sub NewDot (i As Long, x As Long, y As Long, r As Long)
    Dim angle, rd
    angle = pi * 2 * Rnd
    rd = Rnd * 30
    dots(i).x = x + rd * Cos(angle)
    dots(i).y = y + rd * Sin(angle)
    dots(i).size = Rnd * r * .05
    rd = Rnd 'STxAxTIC recommended for rounder spreads
    dots(i).dx = rd * 10 * (10 - 2 * dots(i).size) * Cos(angle)
    dots(i).dy = rd * 10 * (10 - 2 * dots(i).size) * Sin(angle)
    rd = 20 + Rnd * 70
    dots(i).kolor = _RGBA32(rd, rd, rd, 80)
End Sub

Sub drawship 'simple red iso triangle pointed towards radianAngle
    Dim x1 As Long, y1 As Long, x2 As Long, y2 As Long, x3 As Long, y3 As Long
    Dim x4 As Long, y4 As Long, x5 As Long, y5 As Long
    If ship.thrust > 0 Then
        'burn out flame as thruster dies out
        x4 = ship.x + .5 * ship.thrust * Cos(ship.angle - 17 / 18 * pi)
        y4 = ship.y + .5 * ship.thrust * Sin(ship.angle - 17 / 18 * pi)
        x5 = ship.x + .5 * ship.thrust * Cos(ship.angle - 19 / 18 * pi)
        y5 = ship.y + .5 * ship.thrust * Sin(ship.angle - 19 / 18 * pi)
        ftri ship.x, ship.y, x4, y4, x5, y5, &H99FFFF88
        ship.thrust = ship.thrust - 1
    End If
    'draw ship dead or alive thrust or not, calculate 3 points of triangle ship
    fcirc ship.x, ship.y, 30, &H05FFFFFF
    x1 = ship.x + 30 * Cos(ship.angle) ' front point
    y1 = ship.y + 30 * Sin(ship.angle) '
    x2 = ship.x + 30 * Cos(ship.angle + .6666 * pi) ' wing
    y2 = ship.y + 30 * Sin(ship.angle + .6666 * pi)
    x3 = ship.x + 30 * Cos(ship.angle - .6666 * pi) ' other wing
    y3 = ship.y + 30 * Sin(ship.angle - .6666 * pi)
    ftri ship.x, ship.y, x1, y1, x2, y2, _RGB32(80, 120, 80, 80)
    ftri ship.x, ship.y, x1, y1, x3, y3, _RGB32(60, 100, 60, 80)
    Line (x1, y1)-(ship.x, ship.y), _RGB32(255, 255, 128)
    Line (x1, y1)-(x2, y2), _RGB32(255, 180, 40)
    Line (x1, y1)-(x3, y3), _RGB32(255, 180, 40)
End Sub

Sub drawRock (iRock)
    Randomize Using r(iRock).seed 'this prevents having to save a particular sequence of random number
    Dim dx, dy, j As Long, rRad As Single, leg As Single, x0 As Long, y0 As Long, rc As Long, c~&, x1 As Long, y1 As Long, xoff, yoff, i As Long
    Dim x2 As Long, y2 As Long
    dx = r(iRock).speed * Cos(r(iRock).heading)
    dy = r(iRock).speed * Sin(r(iRock).heading) 'update location
    r(iRock).ra = r(iRock).ra + r(iRock).spin
    If r(iRock).x + dx + r(iRock).r < 0 Or r(iRock).x + dx - r(iRock).r > xmax Or r(iRock).y + dy + r(iRock).r < 0 Or r(iRock).y + dy - r(iRock).r > ymax Then
        If iRock <= rocks Then newRock iRock Else r(iRock).live = 0
        Exit Sub ' reassigned get out of here
    Else
        r(iRock).x = r(iRock).x + dx
        r(iRock).y = r(iRock).y + dy
    End If
    For j = 10 To 3 Step -1 '                  rock drawing (see demo program where developed code)
        rRad = .1 * j * r(iRock).r
        leg = rRad * (Rnd * .7 + .3)
        x0 = r(iRock).x + leg * Cos(r(iRock).ra)
        y0 = r(iRock).y + leg * Sin(r(iRock).ra)
        rc = r(iRock).c + 30 * Rnd - 15
        c~& = _RGB32(rc + 5, rc - 10, rc + 5)
        x1 = x0
        y1 = y0
        xoff = Rnd * 20 - 10 + r(iRock).x
        yoff = Rnd * 20 - 10 + r(iRock).y
        For i = 1 To 12
            leg = rRad * (Rnd * .35 + .65)
            If i = 12 Then
                x2 = x0: y2 = y0
            Else
                x2 = xoff + leg * Cos(i * polyAngle + r(iRock).ra)
                y2 = yoff + leg * Sin(i * polyAngle + r(iRock).ra)
            End If
            ftri r(iRock).x, r(iRock).y, x1, y1, x2, y2, c~&
            x1 = x2: y1 = y2
        Next
    Next
End Sub

Sub newRock (iRock)
    Dim side As Long
    Randomize Timer * Rnd 'to avoid making twins
    side = irnd&(1, 4) 'bring rock in from one side, need to set heading according to side
    Select Case side
        Case 1
            r(iRock).x = -100
            r(iRock).y = rrnd(80, ymax - 80)
            If Rnd < .25 Then r(iRock).heading = _Atan2(ship.y - r(iRock).y, ship.x - r(iRock).x) Else r(iRock).heading = 3 * pi / 2 + Rnd * pi
        Case 2
            r(iRock).x = xmax + 100
            r(iRock).y = rrnd(80, ymax - 80)
            If Rnd < .25 Then r(iRock).heading = _Atan2(ship.y - r(iRock).y, ship.x - r(iRock).x) Else r(iRock).heading = pi / 2 + Rnd * pi
        Case 3
            r(iRock).x = rrnd(80, xmax - 80)
            r(iRock).y = -100
            If Rnd < .25 Then r(iRock).heading = _Atan2(ship.y - r(iRock).y, ship.x - r(iRock).x) Else r(iRock).heading = Rnd * pi
        Case 4
            r(iRock).x = rrnd(80, xmax - 80)
            r(iRock).y = ymax + 100
            If Rnd < .25 Then r(iRock).heading = _Atan2(ship.y - r(iRock).y, ship.x - r(iRock).x) Else r(iRock).heading = pi + Rnd * pi
    End Select
    r(iRock).speed = rrnd(minRockSpeed, maxRockSpeed) 'speed, rotation angle, radius, gray coloring, spin, seed, hit for explosion
    r(iRock).ra = Rnd * 2 * pi
    r(iRock).r = irnd&(minRockSize * 3, maxRockSize) 'every parent rock can be split up into at least 2 - 10 size rocks
    r(iRock).c = irnd&(60, 110) ' Ken request increase in rock color
    r(iRock).spin = rrnd(-pi / 20, pi / 20)
    r(iRock).seed = Int(Rnd * 64000) - 32000
    r(iRock).explodeFrame = 0
    r(iRock).live = 1
End Sub

Function irnd& (n1, n2) 'return an integer between 2 numbers
    Dim l%, h%
    If n1 > n2 Then l% = n2: h% = n1 Else l% = n1: h% = n2
    irnd& = Int(Rnd * (h% - l% + 1)) + l%
End Function

Function rrnd (n1, n2) ' return number (expecting reals =_single, double, _float depending on default / define setup)
    rrnd = (n2 - n1) * Rnd + n1
End Function

Sub drawBall (x, y, r, c As _Unsigned Long)
    Dim red As Long, grn As Long, blu As Long, rr As Long, f
    red = _Red32(c): grn = _Green32(c): blu = _Blue32(c)
    For rr = r To 0 Step -1
        f = 1 - rr / r
        fcirc x, y, rr, _RGB32(red * f, grn * f, blu * f)
    Next
End Sub

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 ftri (x1, y1, x2, y2, x3, y3, K As _Unsigned Long)
    Dim D As Long
    Static a&
    D = _Dest
    If a& = 0 Then a& = _NewImage(1, 1, 32)
    _Dest a&
    _DontBlend a& '  '<<<< new 2019-12-16 fix
    PSet (0, 0), K
    _Blend a& '<<<< new 2019-12-16 fix
    _Dest D
    _MapTriangle _Seamless(0, 0)-(0, 0)-(0, 0), a& To(x1, y1)-(x2, y2)-(x3, y3)
End Sub

Sub fcirc (x As Long, y As Long, R As Long, C As _Unsigned Long) 'vince version
    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

The zip has source and font file if you don't have Windows.

   



Attached Files
.zip   b+ Asteroids M11.zip (Size: 40.21 KB / Downloads: 78)
Print this item

  MicroFontEditor
Posted by: dcromley - 05-30-2022, 07:11 PM - Forum: Programs - Replies (4)

MicroFontEditor, to change MicroFont.

Code: (Select All)
Option _Explicit
DefSng A-Z: DefLng I-N: DefStr S
Const TRUE = -1, FALSE = 0
Dim Shared mx, my, m1Hit, m1Rpt, m1Dn, m1End, m2Hit, m2Dn ' for MouseCk
_Title "MicroFontEditor"
Screen _NewImage(1024, 768, 256)
Color 0, 15
Cls

' == MAIN start ==
'  96  (16x6) (iCols, iRows) Characters, each has
'  24   (4x6) (ix, iy) Cells, each has
' 100 (10x10) (iu, iv) Pixels
Const nCols = 16, nRows = 6
Const xHI = 16 * 6, yHI = 6 * 8, uHI = xHI * 10, vHI = yHI * 10
Dim Shared s480 As String * 480, s5 As String * 5, sFont
Dim i, s, iCol, iRow, iu, iv, ix, iy, icolor, iBit

sFont = sFont + "€€€€€€€û€€€à€à€”¾”¾”ªÿª„¢„ˆ¢†¹Í²…€€ð€€€œ¢Á€€Á¢œ€ˆªœªˆˆˆ¾ˆˆ"
sFont = sFont + "€‡†€€ˆˆˆˆˆ€ƒƒ€€‚„ˆ ¾ÅÉѾ€¡ÿ¡ÃÅɱ¢ÉÉɶŒ”¤ÿ„úÉÉÉƾÉÉɦÃÄÈÐà"
sFont = sFont + "¶ÉÉɶ²ÉÉɾ€€¶€€€¶€€€ˆ”¢€€”””€€¢”ˆ€ ÀÅÈ°¾ÁÝż¿ÈÈÈ¿ÿÉÉɶ¾ÁÁÁ¢"
sFont = sFont + "ÿÁÁÁ¾ÿÉÉÉÁÿÈÈÈÀ¾ÁÁŦÿˆˆˆÿ€ÁÿÁ€‚ÁþÀÿˆ”¢Áÿÿ  ÿÿ ˜„ÿ¾ÁÁÁ¾"
sFont = sFont + "ÿÄÄĸ¸ÄÄÄÿÿÈÌʱ²ÉÉɦÀÀÿÀÀþþü‚‚üþ†þÁ¶ˆ¶ÁÀ°°ÀÃÅÉÑကÿÁ€"
sFont = sFont + " ˆ„‚€Áÿ€€„ˆˆ„€Àà €‚•••þ‘‘‘ŽŽ‘‘‘‘Ž‘‘‘þŽ•••Œ€ˆ¿È ˆ•••Ž"
sFont = sFont + "ÿ€€Þ€€Þ€€ÿ„Š‘‘€€þŸŸŸˆŸŽ‘‘‘Ž’’’ŒŒ’’’Ÿˆˆˆ•••‚"
sFont = sFont + "€þ‘ž‚Ÿœ‚‚œž†ž‘Š„Š‘™……‚œ‘“•™‘€ˆ¶Á€€€÷€€€Á¶ˆ€ˆˆ„ˆˆ„ˆ"
s480 = sFont
doAllChars
For i = 0 To 127 ' axes labels
  If i < 16 Then Locate 1, 2 + iLerpLH(5, 117, i, 0, 15): Print "x" + Hex$(i);
  If i < 6 Then Locate 1 + iLerpLH(3, 28, i, 0, 5), 1: Print Hex$(i + 2) + "x";
Next i

' -- print static info
Locate 34, 1
Print "    MicroFont V1.0" + Chr$(13)
Print "    Use mouse to invert cell colors."
Print "    Right-click to copy/paste a character"
Print "    ESC to exit"
Do ' ------------- MAIN LOOP ------------------------
  _Limit 300
  MouseCk ' get mouse data
  If iBox(64, 36, "Font (8 Strings) to clipboard") Then doCopyClip
  If iBox(64, 37, "Load internal font") Then dofill 1
  If iBox(64, 38, "Clear characters") Then dofill 0
  If iBox(64, 39, "Random characters") Then dofill 2
  ' ----------- now look at the characters ------------
  If Not isIn(mx, 26, 986) Or Not isIn(my, 26, 506) Then icolor = 99: GoTo Continue1
  iCol = iLerpLH(0, 15, mx, 26, 986) ' 976 = 26+16*6*10; character column
  iRow = iLerpLH(0, 5, my, 26, 506) ' 506 = 26+6*8*10; row
  ix = iLerpLH(0, 5, (mx + 34) Mod 60, 0, 60) ' +34 = -26; cell x
  iy = iLerpLH(0, 7, (my + 54) Mod 80, 0, 80) ' +54 = -36; y
  If iCol > 15 Or iRow > 5 Or ix > 4 Or iy > 6 Then GoTo Continue1 ' is in borders
  If m2Hit Then copyPaste: GoTo Continue1 ' copy/paste dialog
  If m1Dn Then ' if mouse
    If m1Hit Then ' get the inverse color
      iBit = 1 - igetBit(iCol, iRow, ix, iy)
      If iBit Then icolor = 0 Else icolor = 15
    ElseIf icolor = 99 Then
      GoTo Continue1 ' have no color
    End If
    setBit iCol, iRow, ix, iy, iBit
    doCell iCol, iRow, ix, iy, icolor
  End If
  Continue1: ' -- end of character check
  _Display
Loop While InKey$ <> Chr$(27)
System
' == ROUTINES start ==

Sub doAllChars ()
  Dim iCol, iRow, ix, iy, icolor
  For iRow = 0 To 5 ' character
    For iCol = 0 To 15
      For ix = 0 To 4 ' cell
        For iy = 0 To 6
          If igetBit(iCol, iRow, ix, iy) Then icolor = 0 Else icolor = 15
          doCell iCol, iRow, ix, iy, icolor
        Next iy
      Next ix
    Next iCol
  Next iRow
End Sub

Sub doCell (iC, iR, iX, iY, icolor) ' draw rectangle, interior
  Dim iu, iv
  iu = 26 + (iC * 6 + iX) * 10: iv = 26 + (iR * 8 + iY) * 10
  Line (iu, iv)-(iu + 10, iv + 10), 7, B
  Line (iu + 2, iv + 2)-(iu + 10 - 2, iv + 10 - 2), icolor, BF
End Sub

Function igetBit (iC, iR, iX, iY) ' get bit; 0 or 1
  Dim s1 As String * 1, imask, ich
  s1 = Mid$(s480, 1 + (iC + iR * 16) * 5 + iX, 1)
  imask = 2 ^ (6 - iY) ' 0-6: 1,2,4,8,16,32,64
  ich = Asc(s1)
  If (ich And imask) Then igetBit = 1 Else igetBit = 0
End Function

Sub setBit (iC, iR, iX, iY, iBit) ' set bit
  Dim ipos, imask, icho, ich
  ipos = 1 + (iC + iR * 16) * 5 + iX ' position of ch in s480
  imask = 2 ^ (6 - iY) ' 0-6: 1,2,4,8,16,32,64
  icho = Asc(Mid$(s480, ipos, 1)) ' ch from s480
  ich = icho And (255 - imask) ' ch without bit
  If iBit Then ich = ich Or imask ' OR bit
  Mid$(s480, ipos, 1) = Chr$(ich)
End Sub

Sub copyPaste () ' copy/paste dialog
  Dim iC, iR ' column, row
  Play "v10t64l64c"
  iC = iLerpLH(0, 15, mx, 26, 986) ' 976 = 26+16*6*10
  iR = iLerpLH(0, 5, my, 26, 506) ' 506 = 26+6*8*10
  s5 = Mid$(s480, 1 + (iC + iR * 16) * 5, 5) ' one character
  Log "Right-click to paste or ESC to cancel"
  Do ' -- copy/paste dialog
    _Limit 30
    MouseCk
    If m2Hit Then
      iC = iLerpLH(0, 15, mx, 26, 986) ' 976 = 26+16*6*10
      iR = iLerpLH(0, 5, my, 26, 506) ' 506 = 26+6*8*10
      Mid$(s480, 1 + (iC + iR * 16) * 5, 5) = s5 ' paste
      doAllChars
      Exit Do
    End If
    _Display
  Loop Until InKey$ <> ""
  Log ""
End Sub

Function iLerpLH (ivlo, ivhi, x, xlo, xhi) ' linear interp
  Dim i
  i = ivlo + Int((ivhi + 1 - ivlo) * (x - xlo) / (xhi - xlo))
  If i > ivhi Then iLerpLH = ivhi Else iLerpLH = i
End Function

Sub Log (stxt)
  Play "v10t64l64c"
  If stxt = "" Then
    Locate 34, 64: Print Space$(60);
  Else
    Color , 14: Locate 34, 64: Print stxt: Color , 15
  End If
End Sub

Function iBox (iC, iR, sTxt) ' check box
  Dim iu, iv
  iu = iC * 8: iv = iR * 16
  Line (iu + 1, iv - 15)-(iu + 17, iv - 1), 14, BF
  Line (iu + 1, iv - 15)-(iu + 17, iv - 1), 0, B
  Locate iR, iC + 4: Print sTxt;
  If isInXY(mx, my, iu + 1, iv - 15, iu + 17, iv - 1) And m1Hit Then iBox = TRUE
End Function

Sub doCopyClip () ' copy font (8 strings) to clipboard
  Dim i, s: For i = 1 To 480 Step 60
    s = s + "sFont = sFont + " + Chr$(34) + Mid$(s480, i, 60) + Chr$(34) + Chr$(13)
  Next i
  _Clipboard$ = s
  Log "Font copied to clipboard"
End Sub

Sub dofill (n) ' 0:Clear 1:internal 2: random
  Dim i
  Select Case n
    Case 0: s480 = String$(480, &H80)
    Case 1: s480 = sFont ' internal
    Case 2: For i = 1 To 480 ' random
        Mid$(s480, i, 1) = Chr$(128 + (127 * Rnd) And (127 * Rnd)) ' P(r*r) = .25
      Next i
  End Select
  doAllChars
End Sub

Function isInXY (x, y, xlo, ylo, xhi, yhi)
  If x >= xlo And x <= xhi And y >= ylo And y <= yhi Then isInXY = TRUE
End Function

Function isIn (x, a, b) ' ck between
  If x >= a And x <= b Then isIn = TRUE
End Function

Function iMsecs () ' milliseconds since midnight UTC
  iMsecs = Int(Timer(.001) * 1000 + .5)
End Function

' -- need Dim Shared mx,my,m1Hit,m1Rpt,m1Dn,m1End, m2Hit
Sub MouseCk () ' get mouse info
  Static m1Prev, m2Prev, m1Time, m2Time ' for getting DownEdge (Hit) and Repeating
  Dim mIn, isw1
  m1Hit = 0: m1Rpt = 0: m1Dn = 0: m1End = 0: m2Hit = 0: m2Dn = 0
  Do ' go thru all previous mouse data
    mIn = _MouseInput
    If mIn = 0 Then Exit Do
    mx = _MouseX: my = _MouseY
  Loop
  If _MouseButton(1) Then ' Btn 1 down
    m1Dn = TRUE
    If Not m1Prev Then ' start of downtime
      m1Hit = TRUE: m1Time = iMsecs + 250 ' delay 1/4 sec
    Else ' has been down, ck for repeat
      If iMsecs > m1Time Then m1Rpt = TRUE: m1Time = iMsecs + 50 ' repeat 20/sec
    End If
    m1Prev = TRUE ' for next time
  Else ' Btn 1 up
    If m1Prev Then m1End = TRUE ' end of downtime
    m1Prev = FALSE ' for next time
  End If
  If _MouseButton(2) Then ' Btn 2 down
    m2Dn = TRUE
    If Not m2Prev Then ' start of downtime
      m2Hit = TRUE
    Else
      m2Prev = FALSE ' for next time
    End If
    m2Prev = TRUE
  Else
    m2Prev = FALSE
  End If
End Sub

Print this item

  MicroFont, a 5x7 dot-matrix font
Posted by: dcromley - 05-30-2022, 07:07 PM - Forum: Programs - Replies (2)

Years ago you could see a single pixel.  Nowadays you need a magnifying glass.
 A dot-matrix 5x7 font was quite readable.  Now it's a micro font.
 Just what I wanted to label some things on my plots.
 So I made a routine -- MicroFont.
 It can be drawn anywhere on the screen.
 MicroFont is a self-contained routine at the bottom of the program.

Code: (Select All)
  MicroFont(string, ix, iy)
  ' where string is the text and ix,iy is where it is to be drawn.
 The font is loaded once into a static variable.
 This demo was the easy part - just using the font.
 The hard part was making the font.  I will post MicroFontEditor in a separate thread.


Code: (Select All)
_Title "MicroFont 1.0"
Option _Explicit
DefSng A-Z: DefLng I-N: DefStr S
Randomize Timer
Screen _NewImage(1024, 768, 256)
Color 0, 15
Cls
' == MAIN start ==
Dim Shared void, sWord, sWords(100), xy(100, 4), nWords
Dim i, nloop, velocity
Data Twas,brillig,and,the,slithy,toves,/
Data Did,gyre,and,gimble,in,the,wabe,/
Data All,mimsy,were,the,borogoves,/
Data And,the,mome,raths,outgrabe.,~

MicroFont "Demo of MicroFont", 440, 6 ' == DRAWS THE TITLE ==
Circle (440, 6), 2 ' shows the ix, iy used above
loadWords ' load data into array

Do ' == Main loop ==
  _Limit 60
  nloop = nloop + 1
  If nloop = 180 Then velocity = .01 '
  If nloop > 180 Then velocity = velocity * 1.01
  If velocity > 1 Then velocity = 1
  For i = 1 To nWords ' move all words
    xy(i, 1) = xy(i, 1) + xy(i, 3) * velocity
    xy(i, 2) = xy(i, 2) + xy(i, 4) * velocity
    MicroFont sWords(i), xy(i, 1), xy(i, 2) ' draws individual words
    If xy(i, 1) < 0 Then xy(i, 3) = Abs(xy(i, 3)) ' bounce
    If xy(i, 2) < 6 Then xy(i, 4) = Abs(xy(i, 4))
    If xy(i, 1) > 1000 Then xy(i, 3) = -Abs(xy(i, 3))
    If xy(i, 2) > 767 Then xy(i, 4) = -Abs(xy(i, 4))
  Next i
Loop While InKey$ = ""
System

Sub loadWords ()
  Dim ang, ix, iy, sword: ix = 400: iy = 300
  Do
    Read sword
    If sword = "~" Then Exit Do ' ck EOF
    If sword = "/" Then ix = 400: iy = iy + 12: GoTo continue1 ' ck EOL
    MicroFont sword, ix, iy ' == DRAWS ONE WORD ==
    nWords = nWords + 1 ' into array for moving
    sWords(nWords) = sword
    xy(nWords, 1) = ix
    xy(nWords, 2) = iy
    ang = Rnd * 6.2832
    xy(nWords, 3) = Cos(ang)
    xy(nWords, 4) = Sin(ang)
    ix = ix + Len(sword) * 6 + 5
    continue1:
  Loop
End Sub

DefStr S: DefLng I-N ' This is needed
Sub MicroFont (sstr, ixx0, iyy0) ' ==== THIS IS THE MicroFont ROUTINE ====
  ' -- prints string sstr at position ixx0 and iy0 --
  Static sFont, s96
  If sFont = "" Then ' load once only
    sFont = sFont + "€€€€€€€û€€€à€à€”¾”¾”ªÿª„¢„ˆ¢†¹Í²…€€ð€€€œ¢Á€€Á¢œ€ˆªœªˆˆˆ¾ˆˆ"
    sFont = sFont + "€‡†€€ˆˆˆˆˆ€ƒƒ€€‚„ˆ ¾ÅÉѾ€¡ÿ¡ÃÅɱ¢ÉÉɶŒ”¤ÿ„úÉÉÉƾÉÉɦÃÄÈÐà"
    sFont = sFont + "¶ÉÉɶ²ÉÉɾ€€¶€€€¶€€€ˆ”¢€€”””€€¢”ˆ€ ÀÅÈ°¾ÁÝż¿ÈÈÈ¿ÿÉÉɶ¾ÁÁÁ¢"
    sFont = sFont + "ÿÁÁÁ¾ÿÉÉÉÁÿÈÈÈÀ¾ÁÁŦÿˆˆˆÿ€ÁÿÁ€‚ÁþÀÿˆ”¢Áÿÿ  ÿÿ ˜„ÿ¾ÁÁÁ¾"
    sFont = sFont + "ÿÄÄĸ¸ÄÄÄÿÿÈÌʱ²ÉÉɦÀÀÿÀÀþþü‚‚üþ†þÁ¶ˆ¶ÁÀ°°ÀÃÅÉÑကÿÁ€"
    sFont = sFont + " ˆ„‚€Áÿ€€„ˆˆ„€Àà €‚•••þ‘‘‘ŽŽ‘‘‘‘Ž‘‘‘þŽ•••Œ€ˆ¿È ˆ•••Ž"
    sFont = sFont + "ÿ€€Þ€€Þ€€ÿ„Š‘‘€€þŸŸŸˆŸŽ‘‘‘Ž’’’ŒŒ’’’Ÿˆˆˆ•••‚"
    sFont = sFont + "€þ‘ž‚Ÿœ‚‚œž†ž‘Š„Š‘™……‚œ‘“•™‘€ˆ¶Á€€€÷€€€Á¶ˆ€ˆˆ„ˆˆ„ˆ"
    s96 = s96 + " !##$%&'()*+,-./0123456789:;<=>?"
    s96 = s96 + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_"
    s96 = s96 + "`abcdefghijklmnopqrstuvwxyz{|}~"
    Mid$(s96, 3, 1) = Chr$(34) ' fix quote "
  End If ' end of once only
  Dim iposStr, ipos96, ipos480, ix0, iy0, ix, iy, imask, ich
  ix0 = ixx0 - 1: iy0 = iyy0 + 1 ' byValue
  For iposStr = 1 To Len(sstr) ' one character at a time
    ipos96 = InStr(1, s96, Mid$(sstr, iposStr, 1))
    If ipos96 = 0 Then ipos96 = 4 ' invalid character -> #
    ipos480 = (ipos96 - 1) * 5 ' index to sFont
    For ix = 0 To 6: imask = 1 ' OxxxxxO 5 columns in character
      If 1 <= ix And ix <= 5 Then ich = Asc(Mid$(sFont, ipos480 + ix, 1))
      For iy = 0 To 8 ' OxxxxxxxO 7 rows in character
        If ix < 1 Or ix > 5 Or iy < 1 Or iy > 7 Then
          PSet (ix0 + ix, iy0 - iy), 15 ' BG
        Else ' choose FG or BG
          If ich And imask Then ' ck bit
            PSet (ix0 + ix, iy0 - iy), 0 ' FG
          Else
            PSet (ix0 + ix, iy0 - iy), 15 ' BG
          End If
          imask = imask + imask ' next bit in column
        End If
      Next iy
    Next ix
    ix0 = ix0 + 6 ' next char output
  Next iposStr
  ' could modify ix here
End Sub

Print this item

  Dav IDE
Posted by: aurel - 05-30-2022, 09:01 AM - Forum: Dav - Replies (13)

hello Dav
I really really like to use your IDE for my qb64 programming 
but i don't see a way to set Font size to something smaller
( easpecially when larger programs is case)
i use in my own editor Consolas 10 in my Windows programs
i specially like dark theme named "Davs Colors"
i  remeber that you sayed ..you made it in Purebasic..

or maybe you have newer version ?
tnx

Print this item

Information Wiki Registrations, apply here
Posted by: SMcNeill - 05-29-2022, 11:09 PM - Forum: Wiki Discussion - Replies (1)

As much as it pains us to do so, guys, but since spam bots are all around happily trying to post tons of worthless crap on the Wiki endlessly, we had to close free registrations to the Wiki.

The only way to get an account and become a Wiki editor is to apply for it here. Accounts are now only created by RhoSigma or SMcNeill upon request.

We're not trying to keep anyone from helping with the Wiki, who legitimately wants to help with the Wiki,  but we have to stop the bots from having such easy access to the site.

Apologies for any inconvenience, but I think everyone will understand why we've limited the account creation like we have.

Print this item

  QB64 in WSL2
Posted by: Kernelpanic - 05-29-2022, 03:45 PM - Forum: General Discussion - Replies (5)

How to install QB64 for Linux on WSL2? Installed is openSuSE 15.3

Zypper install qb64-lnx.tar.gz did not work.
Thanks.

Print this item

  Am I roughly correct?
Posted by: James D Jarvis - 05-29-2022, 01:26 PM - Forum: General Discussion - No Replies

So I'm writing a program that generates large bitmaps and I realized I have pushed my little laptop to the edge but it isn't consistent. I realized as I was falling asleep last night I had accidently used a tad bit more memory than I had originally planned.  

Not posting a whole listing as that would be pointless at this stage.

Essentially I'm building a large bitmap by slapping together smaller bit maps. The smallest tiles are 16 x 16 pixels at 32bits.

The source bit maps are under 500K after they are loaded into the program.

Most of the image manipulation happens inside an integer array:  Imap(1000,1000,7) ... That's 14MB + a bit of overhead (correct?).

It all builds an image that is up to   dm&=_newimage(1000*16,1000*16,32) .... that's 1GB+ overhead (correct?).

I'd expect this to be slow and bog down my computer, it doesn't do so consistently. Undoubtedly due to how windows does memory management.  I'm working on speeding it up but really am just asking if my rough memory estimates look correct. Or am I way off?

Print this item

  QB64 odd behavior when Windows Terminal is set to default terminal app
Posted by: hanness - 05-27-2022, 09:25 PM - Forum: General Discussion - No Replies

In Windows 11, I go into Settings and then to the developer settings. Under "Terminal" I select Windows Terminal as the default terminal app to host command-line apps (the other choice would be the Windows Console Host).

After doing this, whenever I open the QB64 IDE, it also opens a Windows Terminal window. If I close that Terminal window, it also shuts down QB64. Likewise, if I close QB64, the Windows Terminal window also closes.

The next time I reopen QB64 it asks if I want to recover my program from an auto-saved backup. 

Is this expected behavior?


My preference is to use Windows Terminal as the default terminal app, but this side-effect makes it awkward.

Print this item

Star Embedding files in programs
Posted by: RhoSigma - 05-27-2022, 11:32 AM - Forum: RhoSigma - No Replies



HISTORY NOTE:
You don't need these programs anymore when using at least QB64-PE v3.10.0 or later. As member of the QB64-PE developer team I've now finally added the functionality directly into the language. From version 3.10.0 on simply use the $EMBED metacommand to embed a file and the _EMBEDDED$ function to later recall the embedded file data. These new commands mainly implement the MakeCARR functionality described below, but in a much more convenient way.

See also: Keyword of the Day ($EMBED and _EMBEDDED$)




Embedding any files into your programs by converting it into DATA lines or an C-Array

The following two small programs are meant to convert any file (eg. Images, Sounds, Databases etc.) into an easy embedable format. Embedding is then as simple as putting a $INCLUDE line at the end of your program. The converted files will have "Ready to use" read and write functions, which either read the data into a string for use with the memory load capabilities of _LOADIMAGE, _LOADFONT and _SNDOPEN, or to write the embedded data back into a file on disk.

The first converter tool is named MakeDATA, as you may imagine by its name, it will convert the given file into a block of DATA lines. This is easy to use and absolutly BASIC code only. It's best to embed small files like icons or sprites. However, for large files like fullsize digital photos or MP3 music it has a big drawback at the compiling speed of your program and the final EXE size on one side and also to the read or write speed on the other side.

So for bigger files the second tool MakeCARR should be your choice, which its advantages are detailed below the MakeDATA codebox.


Note for Windows users:
The codeboxes below contain simple SCREEN 0 (text) based versions of the converter tools. You may have to go in to change your default paths, but they should work reliable on all OS's supported by QB64.
If you're on Windows, then you may rather wish to use the more convenient GuiTools based versions, just move on to
The GuiTools Framework here: https://qb64phoenix.com/forum/forumdisplay.php?fid=32
Both converter tools are available as part of the QB64GuiTools.7z source archive.


And now for the simple (SCREEN 0) people:

Both of the following tools require the 'lzwpacker.bm' file available from my Libraries Collection here:
https://qb64phoenix.com/forum/forumdisplay.php?fid=23


If you're using at least QB64 v1.4 or any Phoenix Edition version and don't wanna use the extra Lzw packer libarary, but the QB64 built-in zlib compression instead, then simply substitute the LzwPack$ call (line 86) with the respective _DEFLATE$ call and the LzwUnpack$ calls (lines 158+233) with an _INFLATE$ call. Also delete the $INCLUDE line at the end.
MakeDATA.bas
Code: (Select All)
'+---------------+---------------------------------------------------+
'| ###### ###### | .--. . .-. |
'| ## ## ## # | | )| ( ) o |
'| ## ## ## | |--' |--. .-. `-. . .-...--.--. .-. |
'| ###### ## | | \ | |( )( ) | ( || | |( ) |
'| ## ## | ' `' `-`-' `-'-' `-`-`|' ' `-`-'`- |
'| ## ## # | ._.' |
'| ## ###### | Sources & Documents placed in the Public Domain. |
'+---------------+---------------------------------------------------+
'| |
'| === MakeDATA.bas === |
'| |
'| == Create a DATA block out of the given file, so you can embed it |
'| == into your program and read it or write it back when needed. |
'| |
'| == The DATAs are written into a .bm file together with ready to |
'| == use read and write back FUNCTIONs. You just $INCLUDE this .bm |
'| == file into your program and call the desired FUNCTION somewhere.|
'| |
'| == This program needs the 'lzwpacker.bm' file available from the |
'| == Libraries Collection here: |
'| == http://qb64phoenix.com/forum/forumdisplay.php?fid=23 |
'| == as it will try to pack the given file to keep the DATA block |
'| == as small as possible. If compression is successful, then your |
'| == program also must $INCLUDE 'lzwpacker.bm' to be able to unpack |
'| == the file data again for write back. MakeDATA.bas is printing |
'| == a reminder message in such a case. |
'| |
'+-------------------------------------------------------------------+
'| Done by RhoSigma, R.Heyder, provided AS IS, use at your own risk. |
'| Find me in the QB64 Forum or mail to support@rhosigma-cw.net for |
'| any questions or suggestions. Thanx for your interest in my work. |
'+-------------------------------------------------------------------+

_TITLE "MakeDATA - Convert File to DATAs v2.0, Done by RhoSigma, Roland Heyder"

'--- if you wish, set any default paths, end with a backslash ---
srcPath$ = "" 'source path
tarPath$ = "" 'target path
'-----
IF srcPath$ <> "" THEN
COLOR 15: PRINT "Default source path: ": COLOR 7: PRINT srcPath$: PRINT
END IF
IF tarPath$ <> "" THEN
COLOR 15: PRINT "Default target path: ": COLOR 7: PRINT tarPath$: PRINT
END IF

'--- collect inputs (relative paths allowed, based on default paths) ---
source:
LINE INPUT "Source Filename: "; src$ 'any file you want to put into DATAs
IF src$ = "" GOTO source
target:
LINE INPUT "Target Basename: "; tar$ 'write stuff into this file (.bm is added)
IF tar$ = "" GOTO target
'-----
ON ERROR GOTO abort
OPEN "I", #1, srcPath$ + src$: CLOSE #1 'file exist check
OPEN "O", #2, tarPath$ + tar$ + ".bm": CLOSE #2 'path exist check
ON ERROR GOTO 0

'--- separate source filename part ---
FOR po% = LEN(src$) TO 1 STEP -1
IF MID$(src$, po%, 1) = "\" OR MID$(src$, po%, 1) = "/" THEN
srcName$ = MID$(src$, po% + 1)
EXIT FOR
ELSEIF po% = 1 THEN
srcName$ = src$
END IF
NEXT po%
'--- separate target filename part ---
FOR po% = LEN(tar$) TO 1 STEP -1
IF MID$(tar$, po%, 1) = "\" OR MID$(tar$, po%, 1) = "/" THEN
tarName$ = MID$(tar$, po% + 1)
EXIT FOR
ELSEIF po% = 1 THEN
tarName$ = tar$
END IF
NEXT po%
MID$(tarName$, 1, 1) = UCASE$(MID$(tarName$, 1, 1)) 'capitalize 1st letter

'--- init ---
OPEN "B", #1, srcPath$ + src$
filedata$ = SPACE$(LOF(1))
GET #1, , filedata$
CLOSE #1
rawdata$ = LzwPack$(filedata$, 20)
IF rawdata$ <> "" THEN
OPEN "O", #1, tarPath$ + tar$ + ".lzw"
CLOSE #1
OPEN "B", #1, tarPath$ + tar$ + ".lzw"
PUT #1, , rawdata$
CLOSE #1
packed% = -1
OPEN "B", #1, tarPath$ + tar$ + ".lzw"
ELSE
packed% = 0
OPEN "B", #1, srcPath$ + src$
END IF
fl& = LOF(1)
cntL& = INT(fl& / 32)
cntB& = (fl& - (cntL& * 32))

'--- .bm include file ---
OPEN "O", #2, tarPath$ + tar$ + ".bm"
PRINT #2, "'============================================================"
PRINT #2, "'=== This file was created with MakeDATA.bas by RhoSigma, ==="
PRINT #2, "'=== you must $INCLUDE this at the end of your program. ==="
IF packed% THEN
PRINT #2, "'=== ---------------------------------------------------- ==="
PRINT #2, "'=== If your program is NOT a GuiTools based application, ==="
PRINT #2, "'=== then it must also $INCLUDE: 'lzwpacker.bm' available ==="
PRINT #2, "'=== from the Libraries Collection here: ==="
PRINT #2, "'=== http://qb64phoenix.com/forum/forumdisplay.php?fid=23 ==="
END IF
PRINT #2, "'============================================================"
PRINT #2, ""
'--- read function ---
PRINT #2, "'"; STRING$(LEN(tarName$) + 17, "-")
PRINT #2, "'--- Read"; tarName$; "Data$ ---"
PRINT #2, "'"; STRING$(LEN(tarName$) + 17, "-")
PRINT #2, "' This function will read the DATAs you've created with MakeDATA.bas"
PRINT #2, "' into a string, no data will be written to disk. If you rather wanna"
PRINT #2, "' rebuild the original file on disk, then use the write function below."
PRINT #2, "'"
PRINT #2, "' You may directly pass the returned string to _SNDOPEN, _LOADIMAGE or"
PRINT #2, "' _LOADFONT when using the memory load capabilities of these commands."
PRINT #2, "'----------"
PRINT #2, "' SYNTAX:"
PRINT #2, "' dataStr$ = Read"; tarName$; "Data$"
PRINT #2, "'----------"
PRINT #2, "' RESULT:"
PRINT #2, "' --- dataStr$ ---"
PRINT #2, "' The data of the embedded file. This is in fact the same as if you"
PRINT #2, "' had opend the file and read its entire content into a single string."
PRINT #2, "'---------------------------------------------------------------------"
PRINT #2, "FUNCTION Read"; tarName$; "Data$"
PRINT #2, "'--- option _explicit requirements ---"
PRINT #2, "DIM numL&, numB&, rawdata$, stroffs&, i&, dat&"
PRINT #2, "'--- read DATAs ---"
PRINT #2, "RESTORE "; tarName$
PRINT #2, "READ numL&, numB&"
PRINT #2, "rawdata$ = SPACE$((numL& * 4) + numB&)"
PRINT #2, "stroffs& = 1"
PRINT #2, "FOR i& = 1 TO numL&"
PRINT #2, " READ dat&"
PRINT #2, " MID$(rawdata$, stroffs&, 4) = MKL$(dat&)"
PRINT #2, " stroffs& = stroffs& + 4"
PRINT #2, "NEXT i&"
PRINT #2, "IF numB& > 0 THEN"
PRINT #2, " FOR i& = 1 TO numB&"
PRINT #2, " READ dat&"
PRINT #2, " MID$(rawdata$, stroffs&, 1) = CHR$(dat&)"
PRINT #2, " stroffs& = stroffs& + 1"
PRINT #2, " NEXT i&"
PRINT #2, "END IF"
PRINT #2, "'--- set result ---"
PRINT #2, "Read"; tarName$; "Data$ = ";
IF packed% THEN PRINT #2, "LzwUnpack$(rawdata$)": ELSE PRINT #2, "rawdata$"
PRINT #2, "END FUNCTION"
PRINT #2, ""
'--- writeback function ---
PRINT #2, "'"; STRING$(LEN(tarName$) + 18, "-")
PRINT #2, "'--- Write"; tarName$; "Data$ ---"
PRINT #2, "'"; STRING$(LEN(tarName$) + 18, "-")
PRINT #2, "' This function will write the DATAs you've created with MakeDATA.bas"
PRINT #2, "' back to disk and so it rebuilds the original file."
PRINT #2, "'"
PRINT #2, "' After the writeback call, only use the returned realFile$ to access the"
PRINT #2, "' written file. It's your given path, but with an maybe altered filename"
PRINT #2, "' (number added) in order to avoid the overwriting of an already existing"
PRINT #2, "' file with the same name in the given location."
PRINT #2, "'----------"
PRINT #2, "' SYNTAX:"
PRINT #2, "' realFile$ = Write"; tarName$; "Data$ (wantFile$)"
PRINT #2, "'----------"
PRINT #2, "' INPUTS:"
PRINT #2, "' --- wantFile$ ---"
PRINT #2, "' The filename you would like to write the DATAs to, can contain"
PRINT #2, "' a full or relative path."
PRINT #2, "'----------"
PRINT #2, "' RESULT:"
PRINT #2, "' --- realFile$ ---"
PRINT #2, "' - On success this is the path and filename finally used after all"
PRINT #2, "' applied checks, use only this returned filename to access the"
PRINT #2, "' written file."
PRINT #2, "' - On failure this function will panic with the appropriate runtime"
PRINT #2, "' error code which you may trap and handle as needed with your own"
PRINT #2, "' ON ERROR GOTO... handler."
PRINT #2, "'---------------------------------------------------------------------"
PRINT #2, "FUNCTION Write"; tarName$; "Data$ (file$)"
PRINT #2, "'--- option _explicit requirements ---"
PRINT #2, "DIM po%, body$, ext$, num%, numL&, numB&, rawdata$, stroffs&, i&, dat&, ff%";
IF packed% THEN PRINT #2, ", filedata$": ELSE PRINT #2, ""
PRINT #2, "'--- separate filename body & extension ---"
PRINT #2, "FOR po% = LEN(file$) TO 1 STEP -1"
PRINT #2, " IF MID$(file$, po%, 1) = "; CHR$(34); "."; CHR$(34); " THEN"
PRINT #2, " body$ = LEFT$(file$, po% - 1)"
PRINT #2, " ext$ = MID$(file$, po%)"
PRINT #2, " EXIT FOR"
PRINT #2, " ELSEIF MID$(file$, po%, 1) = "; CHR$(34); "\"; CHR$(34); " OR MID$(file$, po%, 1) = "; CHR$(34); "/"; CHR$(34); " OR po% = 1 THEN"
PRINT #2, " body$ = file$"
PRINT #2, " ext$ = "; CHR$(34); CHR$(34)
PRINT #2, " EXIT FOR"
PRINT #2, " END IF"
PRINT #2, "NEXT po%"
PRINT #2, "'--- avoid overwriting of existing files ---"
PRINT #2, "num% = 1"
PRINT #2, "WHILE _FILEEXISTS(file$)"
PRINT #2, " file$ = body$ + "; CHR$(34); "("; CHR$(34); " + LTRIM$(STR$(num%)) + "; CHR$(34); ")"; CHR$(34); " + ext$"
PRINT #2, " num% = num% + 1"
PRINT #2, "WEND"
PRINT #2, "'--- write DATAs ---"
PRINT #2, "RESTORE "; tarName$
PRINT #2, "READ numL&, numB&"
PRINT #2, "rawdata$ = SPACE$((numL& * 4) + numB&)"
PRINT #2, "stroffs& = 1"
PRINT #2, "FOR i& = 1 TO numL&"
PRINT #2, " READ dat&"
PRINT #2, " MID$(rawdata$, stroffs&, 4) = MKL$(dat&)"
PRINT #2, " stroffs& = stroffs& + 4"
PRINT #2, "NEXT i&"
PRINT #2, "IF numB& > 0 THEN"
PRINT #2, " FOR i& = 1 TO numB&"
PRINT #2, " READ dat&"
PRINT #2, " MID$(rawdata$, stroffs&, 1) = CHR$(dat&)"
PRINT #2, " stroffs& = stroffs& + 1"
PRINT #2, " NEXT i&"
PRINT #2, "END IF"
PRINT #2, "ff% = FREEFILE"
PRINT #2, "OPEN file$ FOR OUTPUT AS ff%"
IF packed% THEN
PRINT #2, "CLOSE ff%"
PRINT #2, "filedata$ = LzwUnpack$(rawdata$)"
PRINT #2, "OPEN file$ FOR BINARY AS ff%"
PRINT #2, "PUT #ff%, , filedata$"
ELSE
PRINT #2, "PRINT #ff%, rawdata$;"
END IF
PRINT #2, "CLOSE ff%"
PRINT #2, "'--- set result ---"
PRINT #2, "Write"; tarName$; "Data$ = file$"
PRINT #2, "EXIT FUNCTION"
PRINT #2, ""
PRINT #2, "'--- DATAs representing the contents of file "; srcName$
PRINT #2, "'---------------------------------------------------------------------"
PRINT #2, tarName$; ":"
'--- read LONGs ---
PRINT #2, "DATA "; LTRIM$(STR$(cntL& * 8)); ","; LTRIM$(STR$(cntB&))
tmpI$ = SPACE$(32)
FOR z& = 1 TO cntL&
GET #1, , tmpI$: offI% = 1
tmpO$ = "DATA " + STRING$(87, ","): offO% = 6
DO
tmpL& = CVL(MID$(tmpI$, offI%, 4)): offI% = offI% + 4
MID$(tmpO$, offO%, 10) = "&H" + RIGHT$("00000000" + HEX$(tmpL&), 8)
offO% = offO% + 11
LOOP UNTIL offO% > 92
PRINT #2, tmpO$
NEXT z&
'--- read remaining BYTEs ---
IF cntB& > 0 THEN
PRINT #2, "DATA ";
FOR x% = 1 TO cntB&
GET #1, , tmpB%%
PRINT #2, "&H" + RIGHT$("00" + HEX$(tmpB%%), 2);
IF x% <> 16 THEN
IF x% <> cntB& THEN PRINT #2, ",";
ELSE
IF x% <> cntB& THEN
PRINT #2, ""
PRINT #2, "DATA ";
END IF
END IF
NEXT x%
PRINT #2, ""
END IF
PRINT #2, "END FUNCTION"
PRINT #2, ""
'--- ending ---
CLOSE #2
CLOSE #1

'--- finish message ---
COLOR 10: PRINT: PRINT "file successfully processed..."
COLOR 9: PRINT: PRINT "You must $INCLUDE the created file (target name + .bm extension) at"
PRINT "the end of your program. Look into that file to learn about the"
PRINT "available options to read or write back the embedded data."
IF packed% THEN
COLOR 12: PRINT: PRINT "Your program must also $INCLUDE 'lzwpacker.bm' available from"
PRINT "the Libraries Collection here:"
PRINT " http://qb64phoenix.com/forum/forumdisplay.php?fid=23"
PRINT "to be able to read or write back the just processed file."
KILL tarPath$ + tar$ + ".lzw"
END IF
done:
COLOR 7
END
'--- error handler ---
abort:
COLOR 12: PRINT: PRINT "something is wrong with path/file access, check your inputs and try again..."
RESUME done

'--- Function to define/return the program's version string.
'-----
FUNCTION VersionMakeDATA$
VersionMakeDATA$ = MID$("$VER: MakeDATA 2.0 (26-Oct-2023) by RhoSigma :END$", 7, 38)
END FUNCTION

'$INCLUDE: 'QB64Library\LZW-Compress\lzwpacker.bm'



And now the second tool MakeCARR. It will do the whole thing in an array on C/C++ level, rather then in DATAs on the BASIC level. Although it's handling is a bit more tricky, as you get not only a .bm file, but also a .h file, and both must match (ie. the DECLARE LIBRARY path in the .bm must point to the .h), this approch has several advantages especially for big files:
  • Unless DATAs, which are included in the final EXE as written (ie. as ASCII chars), a C-Array containing numbers is embedded as (you guess) array of binary numbers, hence even uncompressed it would not take more space than the original file. This makes the compression even more valuable, as it really reduces the final EXE size, instead of just compensating for the Number-to-Ascii bloat as for the DATAs.
  • As the array is stored as successive numbers in memory, it's possible to write back the entire array with just one disk access, which is much faster than reading all single DATAs and concatenate them into one big string, which is then written out.
  • As the converted data is not in the $INCLUDEd .bm file anymore (but in the .h file now), the syntax checking/compiling in the IDE will need less, depending on filesize much less time to finish, as it doesn't need to check 100s (or even 1000s) of DATA lines.
  • On C/C++ level it's easy to expand the given write path/filename into a full qualified absolut path using a standard library call. This path/filename is returned through the write function and can be used in your program to always safely access the written file, doesn't matter how often you change the current working directory using the CHDIR statement.
  • For any files, which are only needed temporarily during program runtime you can specify an auto-cleanup, which automatically deletes the written file again, as soon as your program terminates. This feature is also easily accessible on C/C++ level through an 'atexit()' function.
Again, if you're using at least QB64 v1.4 or any Phoenix Edition version and don't wanna use the extra Lzw packer libarary, but the QB64 built-in zlib compression instead, then simply substitute the LzwPack$ call (line 101) with the respective _DEFLATE$ call and the LzwUnpack$ calls (lines 293+366) with an _INFLATE$ call. Also delete the $INCLUDE line at the end.
MakeCARR.bas
Code: (Select All)
'+---------------+---------------------------------------------------+
'| ###### ###### | .--. . .-. |
'| ## ## ## # | | )| ( ) o |
'| ## ## ## | |--' |--. .-. `-. . .-...--.--. .-. |
'| ###### ## | | \ | |( )( ) | ( || | |( ) |
'| ## ## | ' `' `-`-' `-'-' `-`-`|' ' `-`-'`- |
'| ## ## # | ._.' |
'| ## ###### | Sources & Documents placed in the Public Domain. |
'+---------------+---------------------------------------------------+
'| |
'| === MakeCARR.bas === |
'| |
'| == Create a C/C++ array out of the given file, so you can embed |
'| == it into your program and read it or write it back when needed. |
'| |
'| == Two files are created, the .h file, which contains the array(s)|
'| == and some functions, and a respective .bm file which needs to |
'| == be $INCLUDEd with your program and does provide the FUNCTIONs |
'| == to read the array(s) into a string or write them back into any |
'| == file. All used functions are standard library calls, no API |
'| == calls are involved, so the read and writeback should work on |
'| == all QB64 supported platforms. |
'| |
'| == Make sure to adjust the path for the .h file for your personal |
'| == needs in the created .bm files (DECLARE LIBRARY), if required. |
'| == You may specify default paths right below this header. |
'| |
'| == This program needs the 'lzwpacker.bm' file available from the |
'| == Libraries Collection here: |
'| == http://qb64phoenix.com/forum/forumdisplay.php?fid=23 |
'| == as it will try to pack the given file to keep the array(s) as |
'| == small as possible. If compression is successful, then your |
'| == program also must $INCLUDE 'lzwpacker.bm' to be able to unpack |
'| == the file data again for write back. MakeCARR.bas is printing |
'| == a reminder message in such a case. |
'| |
'+-------------------------------------------------------------------+
'| Done by RhoSigma, R.Heyder, provided AS IS, use at your own risk. |
'| Find me in the QB64 Forum or mail to support@rhosigma-cw.net for |
'| any questions or suggestions. Thanx for your interest in my work. |
'+-------------------------------------------------------------------+

_TITLE "MakeCARR - Convert File to C-Array v2.0, Done by RhoSigma, Roland Heyder"

'--- if you wish, set any default paths, end with a backslash ---
srcPath$ = "" 'source path
tarPath$ = "" 'target path
'-----
IF srcPath$ <> "" THEN
COLOR 15: PRINT "Default source path: ": COLOR 7: PRINT srcPath$: PRINT
END IF
IF tarPath$ <> "" THEN
COLOR 15: PRINT "Default target path: ": COLOR 7: PRINT tarPath$: PRINT
END IF

'--- collect inputs (relative paths allowed, based on default paths) ---
source:
LINE INPUT "Source Filename: "; src$ 'any file you want to put into a C/C++ array
IF src$ = "" GOTO source
target:
LINE INPUT "Target Basename: "; tar$ 'write stuff into this file(s) (.h/.bm is added)
IF tar$ = "" GOTO target
'-----
ON ERROR GOTO abort
OPEN "I", #1, srcPath$ + src$: CLOSE #1 'file exist check
OPEN "O", #2, tarPath$ + tar$ + ".bm": CLOSE #2 'path exist check
ON ERROR GOTO 0

'--- separate source filename part ---
FOR po% = LEN(src$) TO 1 STEP -1
IF MID$(src$, po%, 1) = "\" OR MID$(src$, po%, 1) = "/" THEN
srcName$ = MID$(src$, po% + 1)
EXIT FOR
ELSEIF po% = 1 THEN
srcName$ = src$
END IF
NEXT po%
'--- separate target filename part ---
FOR po% = LEN(tar$) TO 1 STEP -1
IF MID$(tar$, po%, 1) = "\" OR MID$(tar$, po%, 1) = "/" THEN
tarName$ = MID$(tar$, po% + 1)
EXIT FOR
ELSEIF po% = 1 THEN
tarName$ = tar$
END IF
NEXT po%
MID$(tarName$, 1, 1) = UCASE$(MID$(tarName$, 1, 1)) 'capitalize 1st letter

'---------------------------------------------------------------------
' Depending on the source file's size, one or more array(s) are
' created. This is because some C/C++ compilers seem to have problems
' with arrays with more than 65535 elements. This does not affect the
' write back, as the write function will take this behavior into account.
'---------------------------------------------------------------------

'--- init ---
OPEN "B", #1, srcPath$ + src$
filedata$ = SPACE$(LOF(1))
GET #1, , filedata$
CLOSE #1
rawdata$ = LzwPack$(filedata$, 20)
IF rawdata$ <> "" THEN
OPEN "O", #1, tarPath$ + tar$ + ".lzw"
CLOSE #1
OPEN "B", #1, tarPath$ + tar$ + ".lzw"
PUT #1, , rawdata$
CLOSE #1
packed% = -1
OPEN "B", #1, tarPath$ + tar$ + ".lzw"
ELSE
packed% = 0
OPEN "B", #1, srcPath$ + src$
END IF
fl& = LOF(1)
cntL& = INT(fl& / 32)
cntV& = INT(cntL& / 8180)
cntB& = (fl& - (cntL& * 32))

'--- .h include file ---
OPEN "O", #2, tarPath$ + tar$ + ".h"
PRINT #2, "// ============================================================"
PRINT #2, "// === This file was created with MakeCARR.bas by RhoSigma, ==="
PRINT #2, "// === use it in conjunction with its respective .bm file. ==="
PRINT #2, "// ============================================================"
PRINT #2, ""
PRINT #2, "// --- Array(s) representing the contents of file "; srcName$
PRINT #2, "// ---------------------------------------------------------------------"
'--- read LONGs ---
tmpI$ = SPACE$(32)
FOR vc& = 0 TO cntV&
IF vc& = cntV& THEN numL& = (cntL& MOD 8180): ELSE numL& = 8180
PRINT #2, "static const uint32_t "; tarName$; "L"; LTRIM$(STR$(vc&)); "[] = {"
PRINT #2, " "; LTRIM$(STR$(numL& * 8)); ","
FOR z& = 1 TO numL&
GET #1, , tmpI$: offI% = 1
tmpO$ = " " + STRING$(88, ","): offO% = 5
DO
tmpL& = CVL(MID$(tmpI$, offI%, 4)): offI% = offI% + 4
MID$(tmpO$, offO%, 10) = "0x" + RIGHT$("00000000" + HEX$(tmpL&), 8)
offO% = offO% + 11
LOOP UNTIL offO% > 92
IF z& < numL& THEN PRINT #2, tmpO$: ELSE PRINT #2, LEFT$(tmpO$, 91)
NEXT z&
PRINT #2, "};"
PRINT #2, ""
NEXT vc&
'--- read remaining BYTEs ---
IF cntB& > 0 THEN
PRINT #2, "static const uint8_t "; tarName$; "B[] = {"
PRINT #2, " "; LTRIM$(STR$(cntB&)); ","
PRINT #2, " ";
FOR x% = 1 TO cntB&
GET #1, , tmpB%%
PRINT #2, "0x" + RIGHT$("00" + HEX$(tmpB%%), 2);
IF x% <> 16 THEN
IF x% <> cntB& THEN PRINT #2, ",";
ELSE
IF x% <> cntB& THEN
PRINT #2, ","
PRINT #2, " ";
END IF
END IF
NEXT x%
PRINT #2, ""
PRINT #2, "};"
PRINT #2, ""
END IF
'--- some functions ---
PRINT #2, "// --- Function to copy the array(s) into the provided string buffer."
PRINT #2, "// --- Buffer size is not checked, as MakeCARR makes sure it's sufficient."
PRINT #2, "// ---------------------------------------------------------------------"
PRINT #2, "void Read"; tarName$; "Data(char *Buffer)"
PRINT #2, "{"
FOR vc& = 0 TO cntV&
PRINT #2, " memcpy(Buffer, &"; tarName$; "L"; LTRIM$(STR$(vc&)); "[1], "; tarName$; "L"; LTRIM$(STR$(vc&)); "[0] << 2);"
IF vc& < cntV& OR cntB& > 0 THEN
PRINT #2, " Buffer += ("; tarName$; "L"; LTRIM$(STR$(vc&)); "[0] << 2);"
PRINT #2, ""
END IF
NEXT vc&
IF cntB& > 0 THEN
PRINT #2, " memcpy(Buffer, &"; tarName$; "B[1], "; tarName$; "B[0]);"
END IF
PRINT #2, "}"
PRINT #2, ""
PRINT #2, "// --- Saved full qualified output path and filename, so we've no troubles"
PRINT #2, "// --- when cleaning up, even if the current working folder was changed"
PRINT #2, "// --- during program runtime."
PRINT #2, "// ---------------------------------------------------------------------"
PRINT #2, "char "; tarName$; "Name[8192]; // it's a safe size for any current OS"
PRINT #2, ""
PRINT #2, "// --- Cleanup function to delete the written file, called by the atexit()"
PRINT #2, "// --- handler at program termination time, if requested by user."
PRINT #2, "// ---------------------------------------------------------------------"
PRINT #2, "void Kill"; tarName$; "Data(void)"
PRINT #2, "{"
PRINT #2, " remove("; tarName$; "Name);"
PRINT #2, "}"
PRINT #2, ""
PRINT #2, "// --- Function to write the array(s) back into a file, will return the"
PRINT #2, "// --- full qualified output path and filename on success, otherwise an"
PRINT #2, "// --- empty string is returned (access/write errors, file truncated)."
PRINT #2, "// ---------------------------------------------------------------------"
PRINT #2, "const char *Write"; tarName$; "Data(const char *FileName, int16_t AutoClean)"
PRINT #2, "{"
PRINT #2, " FILE *han = NULL; // file handle"
PRINT #2, " int32_t num = NULL; // written elements"
PRINT #2, ""
PRINT #2, " #ifdef QB64_WINDOWS"
PRINT #2, " if (!_fullpath("; tarName$; "Name, FileName, 8192)) return "; CHR$(34); CHR$(34); ";"
PRINT #2, " #else"
PRINT #2, " if (!realpath(FileName, "; tarName$; "Name)) return "; CHR$(34); CHR$(34); ";"
PRINT #2, " #endif"
PRINT #2, ""
PRINT #2, " if (!(han = fopen("; tarName$; "Name, "; CHR$(34); "wb"; CHR$(34); "))) return "; CHR$(34); CHR$(34); ";"
PRINT #2, " if (AutoClean) atexit(Kill"; tarName$; "Data);"
PRINT #2, ""
FOR vc& = 0 TO cntV&
PRINT #2, " num = fwrite(&"; tarName$; "L"; LTRIM$(STR$(vc&)); "[1], 4, "; tarName$; "L"; LTRIM$(STR$(vc&)); "[0], han);"
PRINT #2, " if (num != "; tarName$; "L"; LTRIM$(STR$(vc&)); "[0]) {fclose(han); return "; CHR$(34); CHR$(34); ";}"
PRINT #2, ""
NEXT vc&
IF cntB& > 0 THEN
PRINT #2, " num = fwrite(&"; tarName$; "B[1], 1, "; tarName$; "B[0], han);"
PRINT #2, " if (num != "; tarName$; "B[0]) {fclose(han); return "; CHR$(34); CHR$(34); ";}"
PRINT #2, ""
END IF
PRINT #2, " fclose(han);"
PRINT #2, " return "; tarName$; "Name;"
PRINT #2, "}"
PRINT #2, ""
'--- ending ---
CLOSE #2
CLOSE #1

'--- .bm include file ---
OPEN "O", #2, tarPath$ + tar$ + ".bm"
PRINT #2, "'============================================================"
PRINT #2, "'=== This file was created with MakeCARR.bas by RhoSigma, ==="
PRINT #2, "'=== you must $INCLUDE this at the end of your program. ==="
IF packed% THEN
PRINT #2, "'=== ---------------------------------------------------- ==="
PRINT #2, "'=== If your program is NOT a GuiTools based application, ==="
PRINT #2, "'=== then it must also $INCLUDE: 'lzwpacker.bm' available ==="
PRINT #2, "'=== from the Libraries Collection here: ==="
PRINT #2, "'=== http://qb64phoenix.com/forum/forumdisplay.php?fid=23 ==="
END IF
PRINT #2, "'============================================================"
PRINT #2, ""
PRINT #2, "'-----------------"
PRINT #2, "'--- Important ---"
PRINT #2, "'-----------------"
PRINT #2, "' If you need to move around this .bm file and its respective .h file"
PRINT #2, "' to fit in your project, then make sure the path in the DECLARE LIBRARY"
PRINT #2, "' statement below does match the actual .h file location. It's best to"
PRINT #2, "' specify a relative path assuming your QB64 installation folder as root."
PRINT #2, "'---------------------------------------------------------------------"
PRINT #2, ""
PRINT #2, "'--- declare C/C++ functions ---"
PRINT #2, "DECLARE LIBRARY "; CHR$(34); tarPath$; tar$; CHR$(34); " 'Do not add .h here !!"
PRINT #2, " SUB Read"; tarName$; "Data (StrBuf$)"
PRINT #2, " FUNCTION Write"; tarName$; "Data$ (FileName$, BYVAL AutoClean%)"
PRINT #2, "END DECLARE"
PRINT #2, ""
'--- read function ---
PRINT #2, "'"; STRING$(LEN(tarName$) + 18, "-")
PRINT #2, "'--- Read"; tarName$; "Array$ ---"
PRINT #2, "'"; STRING$(LEN(tarName$) + 18, "-")
PRINT #2, "' This function will read the array(s) you've created with MakeCARR.bas"
PRINT #2, "' into a string, no data will be written to disk. If you rather wanna"
PRINT #2, "' rebuild the original file on disk, then use the write function below."
PRINT #2, "'"
PRINT #2, "' You may directly pass the returned string to _SNDOPEN, _LOADIMAGE or"
PRINT #2, "' _LOADFONT when using the memory load capabilities of these commands."
PRINT #2, "'----------"
PRINT #2, "' SYNTAX:"
PRINT #2, "' arrData$ = Read"; tarName$; "Array$"
PRINT #2, "'----------"
PRINT #2, "' RESULT:"
PRINT #2, "' --- arrData$ ---"
PRINT #2, "' The data of the embedded file. This is in fact the same as if you"
PRINT #2, "' had opend the file and read its entire content into a single string."
PRINT #2, "'---------------------------------------------------------------------"
PRINT #2, "FUNCTION Read"; tarName$; "Array$"
PRINT #2, "'--- option _explicit requirements ---"
PRINT #2, "DIM temp$"
PRINT #2, "'--- get array & set result ---"
PRINT #2, "temp$ = SPACE$("; LTRIM$(STR$(fl&)); ") 'Do not change this number !!"
PRINT #2, "Read"; tarName$; "Data temp$"
IF NOT packed% THEN
PRINT #2, "Read"; tarName$; "Array$ = temp$"
ELSE
PRINT #2, "Read"; tarName$; "Array$ = LzwUnpack$(temp$)"
END IF
PRINT #2, "END FUNCTION"
PRINT #2, ""
'--- writeback function ---
PRINT #2, "'"; STRING$(LEN(tarName$) + 19, "-")
PRINT #2, "'--- Write"; tarName$; "Array$ ---"
PRINT #2, "'"; STRING$(LEN(tarName$) + 19, "-")
PRINT #2, "' This function will write the array(s) you've created with MakeCARR.bas"
PRINT #2, "' back to disk and so it rebuilds the original file."
PRINT #2, "'"
PRINT #2, "' After the writeback call, only use the returned realFile$ to access the"
PRINT #2, "' written file. It's the full qualified absolute path and filename, which"
PRINT #2, "' is made by expanding your maybe given relative path and an maybe altered"
PRINT #2, "' filename (number added) in order to avoid the overwriting of an already"
PRINT #2, "' existing file with the same name in the given location. By this means"
PRINT #2, "' you'll always have safe access to the file, no matter how your current"
PRINT #2, "' working folder changes during runtime."
PRINT #2, "'"
PRINT #2, "' If you wish, the written file can automatically be deleted for you when"
PRINT #2, "' your program will end, so you don't need to do the cleanup yourself."
PRINT #2, "'----------"
PRINT #2, "' SYNTAX:"
PRINT #2, "' realFile$ = Write"; tarName$; "Array$ (wantFile$, autoDel%)"
PRINT #2, "'----------"
PRINT #2, "' INPUTS:"
PRINT #2, "' --- wantFile$ ---"
PRINT #2, "' The filename you would like to write the array(s) to, can contain"
PRINT #2, "' a full or relative path."
PRINT #2, "' --- autoDel% ---"
PRINT #2, "' Shows whether you want the auto cleanup (see description above) at"
PRINT #2, "' the program end or not (-1 = delete file, 0 = don't delete file)."
PRINT #2, "'----------"
PRINT #2, "' RESULT:"
PRINT #2, "' --- realFile$ ---"
PRINT #2, "' - On success this is the full qualified path and filename finally"
PRINT #2, "' used after all applied checks, use only this returned filename"
PRINT #2, "' to access the written file."
PRINT #2, "' - On failure (write/access) this will be an empty string, so you"
PRINT #2, "' should check for this before trying to access/open the file."
PRINT #2, "'---------------------------------------------------------------------"
PRINT #2, "FUNCTION Write"; tarName$; "Array$ (file$, clean%)"
PRINT #2, "'--- option _explicit requirements ---"
PRINT #2, "DIM po%, body$, ext$, num%";
IF packed% THEN PRINT #2, ", real$, ff%, rawdata$, filedata$": ELSE PRINT #2, ""
PRINT #2, "'--- separate filename body & extension ---"
PRINT #2, "FOR po% = LEN(file$) TO 1 STEP -1"
PRINT #2, " IF MID$(file$, po%, 1) = "; CHR$(34); "."; CHR$(34); " THEN"
PRINT #2, " body$ = LEFT$(file$, po% - 1)"
PRINT #2, " ext$ = MID$(file$, po%)"
PRINT #2, " EXIT FOR"
PRINT #2, " ELSEIF MID$(file$, po%, 1) = "; CHR$(34); "\"; CHR$(34); " OR MID$(file$, po%, 1) = "; CHR$(34); "/"; CHR$(34); " OR po% = 1 THEN"
PRINT #2, " body$ = file$"
PRINT #2, " ext$ = "; CHR$(34); CHR$(34)
PRINT #2, " EXIT FOR"
PRINT #2, " END IF"
PRINT #2, "NEXT po%"
PRINT #2, "'--- avoid overwriting of existing files ---"
PRINT #2, "num% = 1"
PRINT #2, "WHILE _FILEEXISTS(file$)"
PRINT #2, " file$ = body$ + "; CHR$(34); "("; CHR$(34); " + LTRIM$(STR$(num%)) + "; CHR$(34); ")"; CHR$(34); " + ext$"
PRINT #2, " num% = num% + 1"
PRINT #2, "WEND"
PRINT #2, "'--- write array & set result ---"
IF NOT packed% THEN
PRINT #2, "Write"; tarName$; "Array$ = Write"; tarName$; "Data$(file$ + CHR$(0), clean%)"
ELSE
PRINT #2, "real$ = Write"; tarName$; "Data$(file$ + CHR$(0), clean%)"
PRINT #2, "IF real$ <> "; CHR$(34); CHR$(34); " THEN"
PRINT #2, " ff% = FREEFILE"
PRINT #2, " OPEN real$ FOR BINARY AS ff%"
PRINT #2, " rawdata$ = SPACE$(LOF(ff%))"
PRINT #2, " GET #ff%, , rawdata$"
PRINT #2, " filedata$ = LzwUnpack$(rawdata$)"
PRINT #2, " PUT #ff%, 1, filedata$"
PRINT #2, " CLOSE ff%"
PRINT #2, "END IF"
PRINT #2, "Write"; tarName$; "Array$ = real$"
END IF
PRINT #2, "END FUNCTION"
PRINT #2, ""
'--- ending ---
CLOSE #2

'--- finish message ---
COLOR 10: PRINT: PRINT "file successfully processed..."
COLOR 9: PRINT: PRINT "You must $INCLUDE the created file (target name + .bm extension) at"
PRINT "the end of your program. Look into that file to learn about the"
PRINT "available options to read or write back the embedded data."
IF packed% THEN
COLOR 12: PRINT: PRINT "Your program must also $INCLUDE 'lzwpacker.bm' available from"
PRINT "the Libraries Collection here:"
PRINT " http://qb64phoenix.com/forum/forumdisplay.php?fid=23"
PRINT "to be able to read or write back the just processed file."
KILL tarPath$ + tar$ + ".lzw"
END IF
done:
COLOR 7
END
'--- error handler ---
abort:
COLOR 12: PRINT: PRINT "something is wrong with path/file access, check your inputs and try again..."
RESUME done

'--- Function to define/return the program's version string.
'-----
FUNCTION VersionMakeCARR$
VersionMakeCARR$ = MID$("$VER: MakeCARR 2.0 (26-Oct-2023) by RhoSigma :END$", 7, 38)
END FUNCTION

'$INCLUDE: 'QB64Library\LZW-Compress\lzwpacker.bm'

Print this item