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

Username
  

Password
  





Search Forums

(Advanced Search)

Forum Statistics
» Members: 37
» Latest member: Ludwig
» Forum threads: 195
» Forum posts: 1,498

Full Statistics

Online Users
There are currently 64 online users.
» 0 Member(s) | 63 Guest(s)
Bing

Latest Threads
BASIC replicator!
Forum: Everything else
Last Post: 1micha.elok
3 hours ago
» Replies: 1
» Views: 6
Backspace Key
Forum: NaaLaa 7 Questions
Last Post: 1micha.elok
3 hours ago
» Replies: 4
» Views: 56
BASIC games, Pascal games...
Forum: Everything else
Last Post: luwal
04-23-2025, 02:57 AM
» Replies: 0
» Views: 27
C64 Game Maker v1.5.5
Forum: Programming
Last Post: luwal
04-22-2025, 04:55 AM
» Replies: 0
» Views: 31
City Fighter
Forum: NaaLaa 7 Code
Last Post: 1micha.elok
04-17-2025, 11:37 PM
» Replies: 7
» Views: 548
RW3
Forum: NaaLaa 7 Code
Last Post: Marcus
04-11-2025, 05:22 AM
» Replies: 3
» Views: 349
ugBASIC! Good BASIC!
Forum: Programming
Last Post: luwal
04-04-2025, 11:35 PM
» Replies: 6
» Views: 718
Happy Birthday
Forum: Everything else
Last Post: johnno56
04-03-2025, 10:52 PM
» Replies: 3
» Views: 444
Pool - Cue Sports (Simple...
Forum: NaaLaa 7 Code
Last Post: johnno56
04-03-2025, 06:41 PM
» Replies: 3
» Views: 477
Nintendo Switch 2
Forum: Everything else
Last Post: johnno56
04-03-2025, 05:41 AM
» Replies: 2
» Views: 347

 
  Pixel Editor
Posted by: kevin - 03-29-2025, 06:27 PM - Forum: NaaLaa 7 Code - Replies (12)

Hi,
Attached is a project that I have actually managed to persevere with, so that it is nearly finished. It's yet another picture/tileset/character designer - there are many of these available to download, all of which will be more complete, and more polished, but I have just wanted to make this for my own enjoyment Smile
I've included 2 things that I haven't been able to find elsewhere:
1 - the functionality to save finished images as both png files and text files. The text files are there to include in N7 files, so that the images can be generated within the program, rather than having to include png files in your projects. I'm using a variation of some code that Marcus provided for this, and there is an example program to illustrate.
2 - to the lower right of the screen is a button that can be used to test your tileset (to make sure that the tiles fit together correctly), before saving it. 
I've added some pop up hints to explain what most of the buttons do, but if you have any questions, please ask.
A quick explanation of what you need to do:
1 - choose the image size in pixels (max is 64 * 64) in the top right of the screen. There are some pre-sets available also.
2 - Press  the "start drawing" button - this is important, and I keep forgetting this step!
3 - Draw in the main frame, using the brush options to the right. Colours can be changed below the main frame, from the colour palette (other paletts are available to load, or you can make and save your own).
4 - New frames can be added using the button to the left. If you have more than one frame, the active one is shown with a red bar to the left. If you are drawing an animated sprite, the animation can be previewed in the bottom left of the screen.
5 - When ready, save the frames as png or text files using the green buttons to the bottom right. There are also options here to load and edit previous png files, or png files that have been created in different programs.
There is other functionality in the program, which hopefully is self-expanatory.
I've not been able to test this as much as I would like, so please let me know what issues you find. I have tested it up to 8 * 32pixel * 32pixel frames (8000 active pixels) and it works fine, albeit a little slow - this is something that I am till working on.

   
   

.zip   Pixel Editor 2 - published v4.zip (Size: 1.73 MB / Downloads: 5)

Print this item

  Shell
Posted by: johnno56 - 03-29-2025, 05:18 AM - Forum: NaaLaa 7 Questions - Replies (3)

Quick question. Does N7 possess the ability to issue system shell commands? I could not find any reference in 'The Manual'...

J

Print this item

  Rectangle (simple physics)
Posted by: 1micha.elok - 03-23-2025, 10:16 AM - Forum: NaaLaa 7 Code - Replies (3)

       
click the image to zoom in

The code is simply an attempt to simulate a rectangle falling from the top to the ground. Please be aware that there are still some bugs and errors, which do not accurately reflect real-world physics.

Code:
'===================================
'
' Physics Simulation of a Rectangle
'
'===================================

set window "Rectangle", 800, 600, false
set redraw off

' Initialize variables
x = 400          ' Center x-coordinate of the rectangle
y = 100          ' Center y-coordinate of the rectangle
width1 = 100     ' Width of the rectangle
height1 = 50     ' Height of the rectangle

' Physics
vx = 0           ' Horizontal velocity
vy = 0           ' Vertical velocity
gravity = 0.5    ' Gravity acceleration
elasticity = 0.7 ' Bounciness factor (0 to 1)
damping = 0.98   ' Damping factor to simulate softness
friction = 0.02  ' Friction coefficient
ground = 450     ' Y-coordinate of the ground
angle = 0        ' Initial angle of rotation (in degrees)
angularVelocity = 15 ' Angular velocity (rotation speed)
momentOfInertia = (width1^2 + height1^2) / 12 ' Moment of inertia for a rectangle
torqueFactor = 0.01 ' Factor for torque applied during collision

' miscellaneous
visible newX, newY
contact_x1 = 0
contact_x2 = 0

' Function to rotate a point around the origin
function rotatePoint(px, py, angleDeg)
    cosA = cos(rad(angleDeg))
    sinA = sin(rad(angleDeg))
    newX = px * cosA - py * sinA
    newY = px * sinA + py * cosA
endfunc

' Function to find the lowest value among four numbers
function lowestValue(a, b, c, d)
    smallest = a
    if b < smallest then smallest = b
    if c < smallest then smallest = c
    if d < smallest then smallest = d
    return smallest
endfunc

' Determine the sign of the input value vx
function sign(vx)
    if vx > 0 then
        return 1
    else if vx < 0 then
        return -1
    else
        return 0
    endif
endfunc


'-----------
' Main loop
'-----------
while not keydown(KEY_ESCAPE, true)
    ' Clear the screen
    set color 0, 0, 0
    cls
   
    ' Update velocities and positions
    vy = vy + gravity       ' Apply gravity
    y = y + vy              ' Update vertical position
    x = x + vx              ' Update horizontal position
    angle = angle + angularVelocity ' Update rotation angle
   
    ' Calculate the four corners of the rectangle after rotation
    cx1 = -width1/2
    cy1 = -height1/2
    cx2 = width1/2
    cy2 = -height1/2
    cx3 = width1/2
    cy3 = height1/2
    cx4 = -width1/2
    cy4 = height1/2
   
    ' Rotate the corners around the center of the rectangle
    rotatePoint(cx1, cy1, angle); cx1 = newX; cy1 = newY
    rotatePoint(cx2, cy2, angle); cx2 = newX; cy2 = newY
    rotatePoint(cx3, cy3, angle); cx3 = newX; cy3 = newY
    rotatePoint(cx4, cy4, angle); cx4 = newX; cy4 = newY
   
    ' Translate the corners to the rectangle's position
    x1 = x + cx1; y1 = y + cy1
    x2 = x + cx2; y2 = y + cy2
    x3 = x + cx3; y3 = y + cy3
    x4 = x + cx4; y4 = y + cy4
   
    ' Check for collision with the floor
    min_y = lowestValue(y1, y2, y3, y4) ' Find the lowest point of the rectangle
    if min_y >= ground then
        ' Prevent the rectangle from going below the ground
        penetration = min_y - ground
        y = y - penetration
       
        ' Reverse and reduce vertical velocity
        vy = -vy * elasticity
       
        ' Identify the contact points (corners touching the ground)
        contact_x1 = 0; contact_x2 = 0
        if abs(y1 - ground) < 1 then contact_x1 = x1
        if abs(y2 - ground) < 1 then contact_x2 = x2
        if abs(y3 - ground) < 1 then contact_x2 = x3
        if abs(y4 - ground) < 1 then contact_x1 = x4
       
        ' Ensure contact_x1 <= contact_x2
        if contact_x1 > contact_x2 then
            temp = contact_x1
            contact_x1 = contact_x2
            contact_x2 = temp
        endif
       
        ' Calculate torque based on contact points
        torque = 0
        if contact_x1 <> 0 then torque = torque + (contact_x1 - x) * torqueFactor
        if contact_x2 <> 0 then torque = torque + (contact_x2 - x) * torqueFactor
        angularVelocity = angularVelocity + torque / momentOfInertia
       
        ' Apply friction to horizontal velocity
        if abs(vx) > 0 then
            frictionForce = sign(vx) * friction
            vx = vx - frictionForce
        endif
       
        ' Gradually reduce angular velocity due to damping
        angularVelocity = angularVelocity * damping
    endif
   
    ' Simulate softness by gradually reducing velocities
    vy = vy * damping
    vx = vx * damping
   
    ' Stability check: Determine if the rectangle should topple
    com_x = x ' Center of mass x-coordinate
    com_y = y ' Center of mass y-coordinate
   
    ' Check if the center of mass is outside the base of support
    if com_x < contact_x1 or com_x > contact_x2 then
        ' Apply additional torque to simulate toppling
        angularVelocity = angularVelocity + sign(com_x - x) * 0.1
    endif
   
    ' Draw the rotated rectangle by connecting the corners
    set color 255, 255, 255
    draw poly [x1, y1, x2, y2, x3, y3, x4, y4]
   
    ' Refresh
    fwait 60
    redraw
wend

Print this item

  Some textures
Posted by: Marcus - 03-22-2025, 12:53 PM - Forum: Everything else - Replies (5)

Here's a bunch of textures that can be used for wolf3d and enginea games. There's also a small enginea example with a map using some of the textures. All textures were generated using the image creator from microsoft bing, so you can use them however you want (yes, I've read the terms of use). Trying to get good assets for robowack 3 Big Grin

https://naalaa.com/tmp/rw3.mp4



Attached Files
.zip   some_textures.zip (Size: 5.14 MB / Downloads: 4)
Print this item

  Print Using
Posted by: johnno56 - 03-20-2025, 01:00 AM - Forum: Programming - Replies (4)

I am trying to convert a QB64 program and I have encountered the command PRINT USING.

Does anyone know of a procedure to format text to simulate the command?

eg: PRINT USING "#,###,#00  Lives: #  <--JAKE"; score2; lives2

if the score is 1234 and lives are 3: output would be:   1,234  Lives: 3 <--JAKE

Print this item

  Naalaanoid (another arkanoid clone)
Posted by: Marcus - 03-16-2025, 05:56 PM - Forum: NaaLaa 7 Code - Replies (7)

Here's another arkanoid clone!

   

Code:
' naalaanoid.n7
' -------------

#win32

include "tilemap.n7"
include "sfx.n7"
include "file.n7"

' Level generation thing.
visible SEED = 241' 239
' Bonuses.
constant BIG_PADDLE = 1, SMALL_PADDLE = 2, SPEED_UP = 3, SPEED_DOWN = 4, MULTIBALL = 5, CANNONS = 6

' Images.
visible vTilemapImage
visible vBgImage
visible vRedBrickImage, vGreenBrickImage, vBlueBrickImage, vYellowBrickImage, vMagentaBrickImage
visible vCyanBrickImage, vBrickHitImage
visible vPaddleImage, vCannonImage
visible vBallImage, vBulletImage
visible vBigPaddleBonusImage, vSmallPaddleBonusImage, vSpeedUpBonusImage, vSpeedDownBonusImage
visible vMultiballBonusImage, vCannonsBonusImage
visible vNaalaanoidTextImage, vGetReadyTextImage, vGameOverTextImage

' Colors.
visible vRedColor = [157, 40, 92], vGreenColor = [0, 118, 69], vBlueColor = [0, 110, 138]
visible vYellowColor = [179, 175, 12], vMagentaColor = [149, 31, 169], vCyanColor = [71, 193, 197]
visible vWhiteColor = [252, 252, 252], vBlackColor = [0, 0, 0], vGrayColor = [185, 185, 185]
visible vDarkRedColor = [90, 0, 24], vDarkGreenColor = [0, 57, 36], vDarkBlueColor = [9, 19, 128]
visible vDarkMagentaColor = [85, 0, 86]

' Sound effects.
visible vPaddleSound, vBrickHitSound, vBrickDestroyedSound, vWallSound, vLifeLostSound, vBonusSound
visible vShootSound

' Sprite lists.
visible vBalls = []
visible vBullets = []
visible vAnims = []
visible vBonuses = []

' Flying texts.
visible vFlyingTexts = FlyingTexts(64)

' Stats.
visible vScore, vTopScore
visible vBrickCount, vBricksLeft

' Create window.
set window "Naalaanoid", 256, 240, false, int((screenh() - 160)/240)
set redraw off

' Create and set save folder.
folder = GetPathCombined(GetAppDataDirectory(), "naalaa7")
CreateDirectory(folder)
saveFilename = GetPathCombined(folder, "naalaanoid.bin")
f = openfile(saveFilename, true)
if typeof(f)
    vTopScore = fread(f, 32)
    free file f
else
    vTopScore = 0
endif

' Generate assets.
CreateAssets()
vBgImage = createimage(256, 240)
create font 1, "courier new", 13
set font 1

TM_SetImage(vTilemapImage)
TM_SetBorder(true, true, true, false)
for i = 0 to 17  TM_SetObstacle(i, true)

do
    ' Show title screen, returns false if user wants to quit.
    if not TextScreen(vNaalaanoidTextImage, ["TOP SCORE: " + str(vTopScore, 5), "",
            "PRESS ESC TO QUIT"])  break
    level = 1
    lives = 3
    vScore = 0

    do
        ' Init level.
        BuildLevel(level)
        TextScreen(vGetReadyTextImage, ["STAGE " + str(level, 2)])
        clear vAnims
        clear vBonuses
        clear vBalls
        clear vBullets
        vFlyingTexts.Clear()
        paddleX = mousex()
        paddleY = 248
        paddleW = 40
        paddleWantedW = 40
        paddleBullets = 0
        vBalls[0] = Ball(paddleX, paddleY - 3, 0, -1)
        vBalls[0].SetCaptured(true)
        helpTimer = -1   
        do
            ' Fetch ball.
            if sizeof(vBalls)
                paddleY = max(paddleY - 1, 208)
            elseif vBricksLeft > 0
                paddleY = min(paddleY + 2, 248)
                if paddleY = 248
                    lives = lives - 1
                    if lives >= 0
                        vBalls[0] = Ball(paddleX, paddleY - 3, 0, -1)
                        vBalls[0].SetCaptured(true)
                    else
                        break
                    endif
                endif
            endif
            ' Change paddle size.
            if paddleW < paddleWantedW  paddleW = min(paddleW + 1, paddleWantedW)
            elseif paddleW > paddleWantedW  paddleW = max(paddleW - 1, paddleWantedW)
            ' Move.
            paddleX = TM_ToWorldX(min(max(mousex(), 8), 216))
            ' Launch ball or fire bullets.
            if mousebutton(0, true)
                if sizeof(vBalls) = 1 and vBalls[0].captured and paddleY < 216
                    vBalls[0].SetCaptured(false)
                    vBalls[0].SetDir((vBalls[0].X() - paddleX)/paddleW, -0.5)
                elseif paddleBullets > 0
                    paddleBullets = paddleBullets - 1
                    vBullets[sizeof(vBullets)] = Bullet(paddleX - paddleW/2 + 5, paddleY - 4)
                    vBullets[sizeof(vBullets)] = Bullet(paddleX + paddleW/2 - 5, paddleY - 4)
                    play sound vShootSound
                endif
            endif
            ' Add helping powerups every now and then if there are few bricks left.
            if vBricksLeft/vBrickCount < 0.25
                helpTimer = helpTimer - 1
                if helpTimer <= 0
                    if sizeof(vBalls) <= 2 and paddleBullets <= 0
                        helpTimer = 10*60
                        if rnd(2) = 0  AddBonus(MULTIBALL, rnd(13)*16 + 8, -8)
                        else  AddBonus(CANNONS, rnd(13)*16 + 8, -8)
                    else
                        helpTimer = 3*60
                    endif
                endif
            endif
            ' Update sprites.
            before = sizeof(vBalls)
            UpdateSprites(vBalls)
            if sizeof(vBalls) = 0 and before > 0  play sound vLifeLostSound
            UpdateSprites(vBullets)
            UpdateSprites(vBonuses)
            UpdateSprites(vAnims)
            vFlyingTexts.Update()
            ' Update captured balls and collisions with paddle.
            foreach b in vBalls
                if b.captured
                    b.SetPos(min(max(b.X(), paddleX - paddleW/2), paddleX + paddleW/2), paddleY - 3)
                elseif b.dy > 0 and b.y + 6 >= paddleY and b.y < paddleY + 8
                    x = b.x + 3;  dx = x - paddleX
                    if |dx| < paddleW/2 + 3
                        b.SetDir(dx/paddleW, -0.5)
                        play sound vPaddleSound
                    endif
                endif
            next
            ' Pick up bonuses.
            i = 0
            px = paddleX - paddleW/2
            while i < sizeof(vBonuses)
                p = vBonuses[i]
                if p.x + 16 >= px and p.x < px + paddleW and p.y + 8 >= paddleY and p.y < paddleY + 8
                    vScore = vScore + 10
                    select p.type
                    case BIG_PADDLE
                        if paddleWantedW < 80  paddleWantedW = paddleWantedW + 8
                        txt = "BIG PADDLE"
                    case SMALL_PADDLE
                        if paddleWantedW > 24 paddleWantedW = paddleWantedW - 8
                        txt = "SMALL PADDLE"
                    case SPEED_UP
                        foreach b in vBalls  b.IncSpeed()
                        txt = "SPEED UP"
                    case SPEED_DOWN
                        foreach b in vBalls  b.DecSpeed()
                        txt = "SPEED DOWN"
                    case MULTIBALL
                        foreach b in vBalls  if not b.captured
                            a = atan2(b.dy, b.dx)
                            vBalls[sizeof(vBalls)] = Ball(b.X(), b.Y(), cos(a + PI/4), sin(a + PI/4))
                            vBalls[sizeof(vBalls)] = Ball(b.X(), b.Y(), cos(a - PI/4), sin(a - PI/4))
                        endif
                        txt = "MULTIBALL"                    
                    case CANNONS
                        paddleBullets = paddleBullets + 10
                        txt = "CANNONS"
                    endsel
                    free key vBonuses, i
                    vFlyingTexts.Add(txt, 1, p.X(), p.Y(), 0.5, 2)
                    play sound vBonusSound
                else
                    i = i + 1
                endif
            wend
           
            ' Draw.
            set color 255, 255, 255
            draw image vBgImage, 0, 0
            TM_Render()
            DrawPaddle(TM_ToScreenX(paddleX), TM_ToScreenY(paddleY), paddleW, paddleBullets > 0)
            draw image vBgImage, 0, paddleY, 0, paddleY, 8, 240 - paddleY
            draw image vBgImage, 216, paddleY, 216, paddleY, 256 - 216, 240 - paddleY
            DrawSprites(vAnims)
            DrawSprites(vBalls)
            DrawSprites(vBullets)
            DrawSprites(vBonuses)
            vFlyingTexts.Draw()
            clear clip rect
            set caret 256 - 16, 0
            set color vWhiteColor;  center "STAGE"
            set color vBlueColor;  center str(level, 2)
            center
            set color vWhiteColor;  center "LIVES"
            set color vRedColor;  center max(lives, 0)
            center
            set color vWhiteColor;  center "SCORE"
            set color vYellowColor;  center str(vScore, 5)
           
            redraw
            fwait 60
        until vBricksLeft = 0 and vFlyingTexts.IsEmpty() or keydown(KEY_ESCAPE, true)
        if vBricksLeft = 0  level = level + 1
        else  break
    loop
    if lives < 0
        if vScore > vTopScore
            vTopScore = vScore
            f = createfile(saveFilename, true)
            if typeof(f)
                write file f, vScore, 32
                free file f
            endif
            TextScreen(vGameOverTextImage, ["NEW HIGH SCORE: " + str(vScore, 5)])
        else
            TextScreen(vGameOverTextImage, ["SCORE: " + str(vScore, 5)])
        endif
    endif
loop

' TextScreen
' ----------
function TextScreen(img, texts)
    set color 0, 0, 0;  cls;  redraw;  wait 500
    blinkTimer = 40
    abort = false
    do
        if keydown(KEY_ESCAPE, true)
            abort = true
            break
        endif
        if mousebutton(0, true)  break
        blinkTimer = (blinkTimer - 1)%80
        set color 0, 0, 0;  cls
        set color 255, 255, 255
        draw image img, (width(primary) - width(img))/2, height(primary)/3 - height(img)/2
        set caret width(primary)/2, height(primary)/2 - sizeof(texts)*fheight()/2
        set color vWhiteColor;  for i = 0 to sizeof(texts) - 1  center texts[i]
        center
        if blinkTimer < 40
            set caret width(primary)/2, 2*height(primary)/3 - fheight()/2
            center "CLICK TO CONTINUE ..."
        endif
        redraw
        fwait 60
    loop
    set color 0, 0, 0;  cls;  redraw;  wait 500
    return not abort
endfunc

' UpdateSprites
' -------------
function UpdateSprites(list)
    i = 0
    while i < sizeof(list)  if list[i].Update()  i = i + 1
    else  free key list, i
endfunc

' DrawSprites
' -----------
function DrawSprites(list)
    if sizeof(list)  for i = 0 to sizeof(list) - 1  list[i].Draw()
endfunc

' BuildLevel
' ----------
function BuildLevel(level)
    ' Build level from random rectangles.
    if level = 1  blobCount = 2
    else  blobCount = 3 + min(int((level - 1)*0.6), 24);  levelHeight = 16 + min(int(level/2), 6)
    randomize SEED + level
    TM_InitMap(13, levelHeight)
    TM_SetView(8, 8, 13*16, levelHeight*8)
    for i = 1 to blobCount
        bw = 1 + rnd(5);  bh = 1 + rnd(5);  bx = rnd(7);  by = 1 + rnd(levelHeight - bh - 1)
        innerCel = rnd(min(6 + 6*int((level - 1)*0.5), 18))
        outerCel = rnd(min(6 + 6*int((level)*0.5), 18))
        for y = by to by + bh - 1  for x = bx to bx + bw - 1  if x <= 6
            if x = bx or x = bx + bw - 1 or y = by or y = by + bh - 1  cel = outerCel
            else  cel = innerCel
            TM_SetCel(x, y, cel)
            if x < 6  TM_SetCel(6 + (6 - x), y, cel)
        endif
    next
    ' Count bricks and add powerups.
    vBrickCount = 0
    for y = 0 to levelHeight - 1  for x = 0 to 12  if TM_GetCel(x, y) >= 0
        vBrickCount = vBrickCount + 1
        if rnd(5) = 0  TM_SetFlag(x, y, 1 + rnd(6))
    endif
    vBricksLeft = vBrickCount
   
    ' Create background image.
    patternImage = createimage(16, 16)
    set image patternImage
    select rnd(4)
    case 0  set color vDarkRedColor
    case 1  set color vDarkGreenColor
    case 2  set color vDarkBlueColor
    case 3  set color vDarkMagentaColor
    endsel
    cls
    set color 0, 0, 0
    for i = 1 to 4
        select rnd(3)
            case 0  draw ellipse rnd(16), rnd(16), rnd(12), rnd(12), false
            case 1  draw rect rnd(16), rnd(16), rnd(12), rnd(12), false
            case 2  draw line rnd(16), rnd(16), rnd(16), rnd(16)
        endsel
    next
    set image primary
    set image vBgImage
    set color 0, 0, 0;  cls
    set color 255, 255, 255
    for y = 0 to 14  for x = 0 to 12  draw image patternImage, 8 + x*16, 8 + y*16
    free image patternImage
    set color vGrayColor
    draw rect 0, 8, 8, 232, true;  draw rect 216, 8, 8, 232, true;  draw rect 8, 0, 208, 8, true
    set clip rect 0, 0, 8, 8;  draw ellipse 8, 8, 8, 8, true
    set clip rect 216, 0, 8, 8;  draw ellipse 215, 8, 8, 8, true
    clear clip rect
    set color 0, 0, 0
    draw line 8, 7, 215, 7;  draw line 7, 8, 7, 240;  draw line 216, 8, 216, 240
    set image primary
endfunc

' Ball
' ----
function Ball(x, y, dx, dy)
    b = []
    b.SetCaptured = function(value)
        this.captured = value
    endfunc
    b.SetPos = function(x, y)
        this.x = min(max(x - 3, 0), 202)
        this.y = y - 3
    endfunc
    b.X = function();  return this.x + 3;  endfunc
    b.Y = function();  return this.y + 3;  endfunc
    b.SetDir = function(dx, dy)
        k = 1/sqr(dx*dx + dy*dy)
        this.dx = k*dx
        this.dy = k*dy
    endfunc
    b.IncSpeed = function()
        this.spd = this.spd + 0.5
        this.spdt = 0
    endfunc
    b.DecSpeed = function()
        this.spd = max(this.spd - 0.5, 1)
        if this.spd < 2  this.spdt = 60*10
    endfunc
    b.Update = function()
        if this.captured  return true
        if this.spdt > 0
            this.spdt = this.spdt - 1
            if this.spdt = 0  this.IncSpeed()
        endif
        ' Move one unit at a time for the brick hits to work better.
        move = this.spd
        res = -1
        while move > 0
            spd = min(move, 1)
            move = move - 1
            TM_MoveSprite(this, this.dx*spd, this.dy*spd)
            if TM_CollisionUp()
                ixl = floor((this.x)/16);  ixr = floor((this.x + 5)/16);  y = floor((this.y - 4)/8)
                res = max(res, HitBrick(ixl, y))
                if ixr <> ixl  res = max(res, HitBrick(ixr, y))
                this.dy = |this.dy|
            elseif TM_CollisionDown()
                ixl = floor((this.x)/16);  ixr = floor((this.x + 5)/16);  y = floor((this.y + 10)/8)
                res = max(res, HitBrick(ixl, y))
                if ixr <> ixl  res = max(res, HitBrick(ixr, y))
                this.dy = - |this.dy|
            endif
            if TM_CollisionLeft()
                iyt = floor((this.y)/8);  iyb = floor((this.y + 5)/8);  x = floor((this.x - 8)/16)
                res = max(res, HitBrick(x, iyt))
                if iyb <> iyt  res = max(res, HitBrick(x, iyb))
                this.dx = |this.dx|
            elseif TM_CollisionRight()
                iyt = floor((this.y)/8);  iyb = floor((this.y + 5)/8);  x = floor((this.x + 14)/16)
                res = max(res, HitBrick(x, iyt))
                if iyb <> iyt  res = max(res, HitBrick(x, iyb))
                this.dx = -|this.dx|
            endif
        wend
        ' Prevent ball from going too much horizontally.
        if |this.dy| < 0.1
            if this.dy < 0  this.SetDir(this.dx, -0.15)
            else  this.SetDir(this.dx, 0.15)
        endif
        'endif
        if res = 0  play sound vWallSound
        elseif res = 1  play sound vBrickHitSound
        elseif res = 2  play sound vBrickDestroyedSound
        a = atan2(this.dy, this.dx)
        return this.y < 240
    endfunc
    b.Draw = function()
        draw image vBallImage, TM_ToScreenX(this.x), TM_ToScreenY(this.y)
    endfunc
    b.captured = false
    b.r = 3
    b.w = 6
    b.h = 6
    b.spd = 2
    b.spdt = 0
    b.SetPos(x, y)
    b.SetDir(dx, dy)
   
    return b
endfunc

' Bullet
' ------
function Bullet(x, y)
    return [x: x - width(vBulletImage)/2, y: y - height(vBulletImage)/2,
            w: width(vBulletImage), h: height(vBulletImage),
            Update: function()
                TM_MoveSprite(this, 0, -4)
                if TM_CollisionUp()
                    ixl = floor(this.x/16);  ixr = floor((this.x + 4)/16)
                    iy = floor((this.y - 4)/8)
                    res = HitBrick(ixl, iy)
                    if ixr <> ixl  res = max(res, HitBrick(ixr, iy))
                    if res = 0  play sound vWallSound
                    elseif res = 1  play sound vBrickHitSound
                    elseif res = 2  play sound vBrickDestroyedSound
                    return false
                endif
                return true
            endfunc,
            Draw: function()
                draw image vBulletImage, TM_ToScreenX(this.x), TM_ToScreenY(this.y)
            endfunc]
endfunc

' AddAnim
' -------
function AddAnim(img, x, y, spd, startCel)
    vAnims[sizeof(vAnims)] = [
            img: img, cel: startCel, x: x, y: y, spd: spd,
            Update: function()
                this.cel = this.cel + this.spd
                return this.cel < cels(this.img)
            endfunc,
            Draw: function()
                draw image this.img, this.x, this.y, int(this.cel)
            endfunc]
endfunc

' HitBrick
' --------
function HitBrick(x, y)
    cel = TM_GetCel(x, y)
    if cel >= 0
        cel = cel - 6
        if cel >= 0
            TM_SetCel(x, y, cel)
            AddAnim(vBrickHitImage, 8 + x*16, 8 + y*8, 0.25, 1 - int(cel/6))
            return 1
        else
            vBricksLeft = vBricksLeft - 1
            vScore = vScore + 5
            TM_SetCel(x, y, -1)
            pu = TM_GetFlag(x, y)
            if pu > 0  AddBonus(pu, x*16 + 8, y*8 + 4)
            vFlyingTexts.Add(5, 1, x*16 + 8, y*8 + 4, 0.5, 1)
            return 2
        endif
    else
        return 0
    endif
endfunc

' AddBonus
' --------
function AddBonus(type, x, y)
    select type
        case BIG_PADDLE  img = vBigPaddleBonusImage
        case SMALL_PADDLE  img = vSmallPaddleBonusImage
        case SPEED_UP  img = vSpeedUpBonusImage
        case SPEED_DOWN  img = vSpeedDownBonusImage
        case MULTIBALL  img = vMultiballBonusImage
        case CANNONS  img = vCannonsBonusImage
        default  img = unset
    endsel
    assert img, "AddBonus: Invalid type"
    vBonuses[sizeof(vBonuses)] = [
            type: type, img: img, x: x - 8, y: y - 4, spd: 1,
            X: function();  return this.x + 8;  endfunc,
            Y: function();  return this.y + 4;  endfunc,
            Update: function()
                this.y = this.y + this.spd
                return this.y < 240
            endfunc,
            Draw: function()
                draw image this.img, TM_ToScreenX(this.x), TM_ToScreenY(this.y)
            endfunc]
endfunc

' DrawPaddle
' ----------
function DrawPaddle(x, y, w, cannons)
    x = x - int(w/2)
    draw image vPaddleImage, x, y, 0
    draw image vPaddleImage, x + w - 8, y, 2
    set clip rect x + 8, 0, w - 16, height(primary)
    for i = 1 to int(w/8) - 1  draw image vPaddleImage, x + i*8, y, 1
    clear clip rect
    if cannons
        h = height(vCannonImage)
        draw image vCannonImage, x + 2, y - h
        draw image vCannonImage, x + w - 2 - width(vCannonImage), y - h
    endif
endfunc

' CreateAssets
' ------------
function CreateAssets()
    ' Brick images.
    brickBitmap = [
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3],
            [1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3],
            [1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
            [1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3],
            [1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
            [1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3],
            [1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1]]
    vRedBrickImage = Create3ColorImage(brickBitmap, [vRedColor, vWhiteColor, vBlackColor])
    vGreenBrickImage = Create3ColorImage(brickBitmap, [vGreenColor, vWhiteColor, vBlackColor])
    vBlueBrickImage = Create3ColorImage(brickBitmap, [vBlueColor, vWhiteColor, vBlackColor])
    vYellowBrickImage = Create3ColorImage(brickBitmap, [vYellowColor, vWhiteColor, vBlackColor])
    vMagentaBrickImage = Create3ColorImage(brickBitmap, [vMagentaColor, vWhiteColor, vBlackColor])
    vCyanBrickImage = Create3ColorImage(brickBitmap, [vCyanColor, vWhiteColor, vBlackColor])
    set image grid vRedBrickImage, 1, 3
    set image grid vGreenBrickImage, 1, 3
    set image grid vBlueBrickImage, 1, 3
    set image grid vYellowBrickImage, 1, 3
    set image grid vMagentaBrickImage, 1, 3
    set image grid vCyanBrickImage, 1, 3
    vTilemapImage = createimage(16*6, 24)
    set image vTilemapImage
    set color 255, 255, 255
    draw image vRedBrickImage, 0, 0
    draw image vGreenBrickImage, 16, 0
    draw image vBlueBrickImage, 32, 0
    draw image vYellowBrickImage, 48, 0
    draw image vMagentaBrickImage, 64, 0
    draw image vCyanBrickImage, 80, 0
    set image primary
    set image grid vTilemapImage, 6, 3
    ' Brick hit anim.
    vBrickHitImage = Create3ColorImage([
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
            [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
            [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
            [vWhiteColor, unset, unset])
    set image grid vBrickHitImage, 1, 3
    ' Paddle.
    vPaddleImage = Create3ColorImage([
            [0, 0, 2, 2, 2, 2, 2, 3,  1, 1, 1, 1, 1, 1, 1, 1,  3, 2, 2, 2, 2, 2, 0, 0],
            [0, 2, 2, 2, 2, 2, 2, 1,  1, 1, 1, 1, 1, 1, 1, 1,  1, 2, 2, 2, 2, 2, 2, 0],
            [2, 2, 2, 2, 2, 2, 2, 1,  1, 1, 1, 1, 1, 1, 1, 1,  1, 2, 2, 2, 2, 2, 2, 2],
            [2, 2, 2, 2, 2, 2, 2, 1,  1, 1, 1, 1, 1, 1, 1, 1,  1, 2, 2, 2, 2, 2, 2, 2],
            [2, 2, 2, 2, 2, 2, 2, 1,  1, 1, 1, 1, 1, 1, 1, 1,  1, 2, 2, 2, 2, 2, 2, 2],
            [3, 2, 2, 2, 2, 2, 2, 3,  1, 1, 1, 1, 1, 1, 1, 1,  3, 2, 2, 2, 2, 2, 2, 3],
            [0, 3, 2, 2, 2, 2, 2, 3,  1, 1, 1, 1, 1, 1, 1, 1,  3, 2, 2, 2, 2, 2, 3, 0],
            [0, 0, 3, 3, 3, 3, 3, 3,  3, 3, 3, 3, 3, 3, 3, 3,  3, 3, 3, 3, 3, 3, 0, 0]],
            [vGrayColor, vRedColor, vBlackColor])
    set image grid vPaddleImage, 3, 1
    ' Cannon.
    vCannonImage = Create3ColorImage([
            [0, 0, 1, 1, 0, 0],
            [0, 0, 3, 3, 0, 0],
            [0, 0, 1, 1, 0, 0],
            [0, 0, 1, 1, 0, 0],
            [0, 0, 1, 1, 0, 0],
            [1, 1, 1, 1, 1, 1]],
            [vGrayColor, vWhiteColor, vBlackColor])
    ' Ball.
    vBallImage = Create3ColorImage([
            [0, 1, 1, 1, 1, 0],
            [1, 1, 2, 1, 1, 1],
            [1, 2, 2, 2, 1, 1],
            [1, 1, 2, 1, 1, 1],
            [1, 1, 1, 1, 1, 1],
            [0, 1, 1, 1, 1, 0]],
            [vGrayColor, vWhiteColor, vBlackColor])
    'Bullet.
    vBulletImage = Create3ColorImage([
            [0, 3, 3, 0],
            [3, 2, 2, 3],
            [3, 1, 1, 3],
            [3, 1, 1, 3],
            [0, 2, 2, 0],
            [0, 2, 2, 0],
            [0, 3, 3, 0]],
            [vWhiteColor, vYellowColor, vRedColor])
    ' Powerups.
    vBigPaddleBonusImage = Create3ColorImage([
            [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
            [0, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0],
            [1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1],
            [1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1],
            [1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1],
            [3, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 3],
            [0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0],
            [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0]],
            [vGreenColor, vWhiteColor, vBlackColor])
    vSmallPaddleBonusImage = Create3ColorImage([
            [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
            [0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0],
            [1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1],
            [1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1],
            [1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1],
            [3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 3],
            [0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0],
            [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0]],
            [vRedColor, vWhiteColor, vBlackColor])
    vSpeedUpBonusImage = Create3ColorImage([
            [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
            [0, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 0],
            [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [3, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 3],
            [0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0],
            [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0]],
            [vMagentaColor, vWhiteColor, vBlackColor])
    vSpeedDownBonusImage = Create3ColorImage([
            [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
            [0, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 0],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1],
            [3, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 3],
            [0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0],
            [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0]],
            [vYellowColor, vWhiteColor, vBlackColor])
    vMultiballBonusImage = Create3ColorImage([
            [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
            [0, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 0],
            [1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1],
            [1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1],
            [1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 1],
            [3, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 3],
            [0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0],
            [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0]],
            [vCyanColor, vWhiteColor, vBlackColor])
    vCannonsBonusImage = Create3ColorImage([
            [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
            [0, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0],
            [1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1],
            [1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [3, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 3],
            [0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0],
            [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0]],
            [vBlueColor, vWhiteColor, vBlackColor])
    ' Cool texts.
    vNaalaanoidTextImage = CreateCoolText("NAALAANOID", 3, vWhiteColor, vBlueColor)
    vGetReadyTextImage = CreateCoolText("GET READY!", 2, vWhiteColor, vDarkGreenColor)
    vGameOverTextImage = CreateCoolText("GAME OVER!", 2, vWhiteColor, vRedColor)
    ' Sound effects (stealing from the example breakout3d.n7).
    sfx = SFX()
    vPaddleSound = sfx.SineWave(0.15, [350], [0.5, 0])
    vBrickHitSound = sfx.SquareWave(0.1, [800], [0.15, 0, 0])
    vBrickDestroyedSound = sfx.SquareWave(0.15, [500], [0.5, 0, 0])
    vWallSound = sfx.Noise(0.15, [100, 1000], [0.25, 0])
    vLifeLostSound = sfx.Noise(0.54, [1000, 5000], [0.75, 0])
    vBonusSound = sfx.SineWave(0.25, [500, 1500], [0.5, 0])
    vShootSound = sfx.SineWave(0.15, [1000, 2000, 1000], [0.4, 0])
endfunc

' Create3ColorImage
' -----------------
function Create3ColorImage(data, colors)
    img = createimage(sizeof(data[0]), sizeof(data))
    set image img
    for y = 0 to sizeof(data) - 1  for x = 0 to sizeof(data[0]) - 1
        if data[y][x] > 0  set color colors[data[y][x] - 1]
        else  set color 0, 0, 0, 0
        set pixel x, y
    next
    set image primary
    return img
endfunc

' CreateCoolText
' --------------
function CreateCoolText(txt, size, borderColor, fillColor)
    w = fwidth(txt);  h = fheight()
    tmp = createimage(w, h)
    set image tmp
    set caret 0, 0;  set color 255, 255, 255;  write txt
    set image primary
    res = createimage(w*size + 2, h*size + 2)
    set image res
    set color 0, 0, 0, 0;  for y = 0 to height(res) - 1  for x = 0 to width(res) - 1  set pixel x, y
    for y = 0 to h - 1  for x = 0 to w - 1  if pixeli(tmp, x, y)%256
        t = pixeli(tmp, x, y - 1)%256;  b = pixeli(tmp, x, y + 1)%256
        l = pixeli(tmp, x - 1, y)%256;  r = pixeli(tmp, x + 1, y)%256
        set color fillColor;  draw rect x*size, y*size, size, size, true
        set color borderColor
        if not t  draw line x*size, y*size - 1, x*size + size - 1, y*size - 1
        if not b  draw line x*size, y*size + size, x*size + size - 1, y*size + size
        if not l  draw line x*size - 1, y*size, x*size - 1, y*size + size - 1
        if not r  draw line x*size + size, y*size, x*size + size, y*size + size - 1
    endif
    set image primary
    free image tmp
    return res
endfunc

' FlyingTexts
' -----------
' Encapsulate flying texts.
function FlyingTexts(maxTexts)
    ft = []
    ft.texts = fill(
        [txt: unset,
        fnt: unset,
        x: 0,
        y: 0,
        spd: 0],
        maxTexts)
   
    ft.empty = true
    ' Clear
    ' -----
    ft.Clear = function()
        foreach t in this.texts  t.txt = unset
    endfunc
   
    ' IsEmpty
    ' -------
    ft.IsEmpty = function()
        return .empty
    endfunc
   
    ' Add
    ' ---
    ft.Add = function(txt, fnt, x, y, spd, duration)
        foreach t in this.texts  if not t.txt
            t.txt = txt
            t.fnt = fnt
            t.x = x
            t.y = y - fheight(fnt)/2
            t.spd = spd
            t.t = 60*duration
            break
        endif
    endfunc
   
    ' Update
    ' ------
    ft.Update = function()
        .empty = true
        foreach t in this.texts  if t.txt
            .empty = false
            t.t = t.t - 1
            if t.t > 0
                t.y = t.y - t.spd
            else
                t.txt = unset
            endif
        endif
    endfunc
   
    ' Draw
    ' ----
    ft.Draw = function()
        foreach t in this.texts  if t.txt
            set font t.fnt
            set caret TM_ToScreenX(t.x), TM_ToScreenY(t.y)
            center t.txt
        endif
    endfunc
   
    return ft
endfunc

Print this item

  Bridge (simple physics)
Posted by: 1micha.elok - 03-16-2025, 09:51 AM - Forum: NaaLaa 7 Code - Replies (3)

BRIDGE
A demo of simple physics

       
click the images to zoom in
How It Works
1. The circle falls due to gravity and collides with the bridge.
2. Upon collision, the circle applies a downward force to the bridge segment it lands on, proportional to its mass.
3. Springs pull the displaced segments back toward their rest positions, creating a bending effect
4. When the ball stops moving, increased damping stabilizes the bridge, preventing further vibrations

Code:
'=================================
'
'  BRIDGE
'  A demo of simple physics
'
'=================================

#win32
set window "bridge",800, 600,false 
set redraw off

' Gravity value
visible gravity = 0.5                      

' Parameters for the bridge
visible bridge  = []
bridge.segments = 15                     ' number of segments in the bridge
bridge.swidth   = 40                     ' width of each segment
bridge.sheight  = 10                     ' height of each segment
bridge.gap      = 5                      ' gap between segments
bridge.y        = height()/2             ' vertical position of the bridge

' Calculate total width of the bridge
bridge.total    = bridge.segments * bridge.swidth + (bridge.segments - 1) * bridge.gap

' Calculate starting x-coordinate to center the bridge horizontally
bridge.start    = (width() - bridge.total) / 2

' Bridge segment position
bridge.xs       = []
bridge.ys       = []
bridge.vy       = []
bridge.rest     = []                      ' Rest position of each segment 
bridge.spring   = []                      ' Spring elasticity

' Initialize bridge segments
for i = 0 to bridge.segments - 1
    bridge.xs[i]    = bridge.start + i * (bridge.swidth + bridge.gap)
    bridge.ys[i]    = bridge.y
    bridge.rest[i]  = bridge.y 
    bridge.vy[i]    = 0
    bridge.spring[i]= 0.1  
next

' Parameters for the falling circle
visible circle = []
circle.radius       = 15                        ' Radius of the circle
circle.x            = width()/2                 ' Initial x position of the circle
circle.y            = bridge.y-circle.radius    ' Initial y position of the circle
circle.vx           = 0                         ' Horizontal velocity
circle.vy           = 0                         ' Vertical velocity
circle.mass         = 0                    
circle.bounce       = 0.7                       ' Energy loss on collision
circle.velocity     = 0.1   

' Function to update the circle's position
function update_circle()
    ' Apply gravity to the vertical velocity
    circle.vy = circle.vy + gravity
   
    ' Update the circle's position based on velocity
    circle.x = circle.x + circle.vx
    circle.y = circle.y + circle.vy
   
endfunc

' Function to handle collisions between the circle and the bridge
function handle_collisions()
    for i = 0 to bridge.segments - 1
        ' Check if the circle overlaps with the current bridge segment
        if circle.x + circle.radius > bridge.xs[i] and
        circle.x - circle.radius < bridge.xs[i] + bridge.swidth and
           circle.y + circle.radius > bridge.ys[i] and
           circle.y - circle.radius < bridge.ys[i] + bridge.sheight then
            ' Collision detected: Reposition the circle and apply force to the bridge segment
            overlap = (circle.y + circle.radius) - bridge.ys[i]
            circle.y = bridge.ys[i] - circle.radius  ' Reposition the circle above the bridge
           
            ' Reverse and dampen vertical velocity
            circle.vy = circle.vy * -circle.bounce
           
            ' Stop bouncing if velocity is below the threshold
            if abs(circle.vy) < circle.velocity then
                circle.vy = 0
            endif
           
            ' Apply downward force to the bridge segment based on the circle's mass
            bridge.vy[i] = bridge.vy[i] + overlap * circle.mass * 0.01
        endif
    next
endfunc

' Simulate spring forces between bridge segments
function simulate_springs()
    for i = 1 to bridge.segments - 1
        ' Calculate displacement from rest position
        dy = bridge.ys[i] - bridge.rest[i]
       
        ' Apply spring force to pull the segment back to its rest position
        bridge.vy[i] = bridge.vy[i] - dy * bridge.spring[i]
    next
   
    ' Apply spring forces between adjacent segments
    for i = 1 to bridge.segments - 1
        ' Calculate displacement between adjacent segments
        dy = bridge.ys[i] - bridge.ys[i - 1]
        force = dy * bridge.spring[i]
       
        ' Apply spring force
        bridge.vy[i] = bridge.vy[i] - force
        bridge.vy[i - 1] = bridge.vy[i - 1] + force
    next   
   
endfunc

' Update bridge positions
function update_bridge()
    for i = 0 to bridge.segments - 1
        ' If the ball has stopped moving, increase damping to stabilize the bridge
        if abs(circle.vy) < circle.velocity then
           bridge.vy[i] = 0  ' Stronger damping when the ball stops
        else
            bridge.vy[i] = bridge.vy[i] * 0.99  ' Normal damping during movement
        endif       
       
        bridge.ys[i] = bridge.ys[i] + bridge.vy[i]
       
        ' Prevent the bridge from bending too far downward
        if bridge.ys[i] > bridge.rest[i] + 50 then
            bridge.ys[i] = bridge.rest[i] + 50
            bridge.vy[i] = 0
        endif
       
        ' Ensure the bridge doesn't go above its rest position
        if bridge.ys[i] < bridge.rest[i] then
            bridge.ys[i] = bridge.rest[i]
            bridge.vy[i] = 0
        endif
    next
endfunc


' Render the bridge and the circle
function render_scene()
    'clear screen
    set color 0,0,0;cls
   
    'white
    set color 255,255,255
   
    ' Draw the bridge
    for i = 0 to bridge.segments - 1
        draw rect bridge.xs[i], bridge.ys[i], bridge.swidth, bridge.sheight
    next
   
    ' Draw the circle
    draw ellipse circle.x, circle.y, circle.radius, circle.radius
endfunc


'--------------
' Main Loop
'--------------
while not keydown(KEY_ESCAPE,true)
    update_circle()      ' Update the circle's position
    handle_collisions()  ' Handle collisions with the bridge
    simulate_springs()
    update_bridge()
    render_scene()       ' Render the scene
   
    'control keys
    if keydown(KEY_1,true) then
        circle.mass = 10
        circle.y    = -50
    endif
    if keydown(KEY_2,true) then
        circle.mass = 150
        circle.y    = -50
    endif
    set caret 10, bridge.y+100
    set color 255,255,255
    wln "Simple Physics"
    wln "================================================"
    wln
    wln "Please watch how the bridge reacts to the circle"
    wln
    wln "Press Key 1 : weight = 10  (light)"
    wln "Press Key 2 : weight = 150 (heavy)"
    wln
    wln "Circle Mass = "+circle.mass
    wln

   
    fwait 10          
    redraw
wend

Print this item

  Primitive Angry Bird
Posted by: 1micha.elok - 03-13-2025, 03:45 AM - Forum: NaaLaa 7 Code - Replies (9)

       
click the images to zoom in

Code:
'====================================
' Primitive Angry Bird
' It's just a demo of simple physics
'
'
'====================================

' Screen dimensions
set window "Primitive Angry Bird", 1000, 600,false
set redraw off

'color definition
black       = [0,0,0]
white       = [255,255,255]
green       = [0,100,0]
lightgreen  = [0,255,0]
brown       = [255,100,10]
gray        = [100,100,100]
red         = [255,0,0]

' Gravity, Friction, Ground
gravity     = 0.1   
friction    = 0.99   ' Friction to slow down movement
groundLevel = 350   

' Bird,Pig,Boxes
visible bird        = []
visible pig         = []
visible boxes       = [] 
Initialize(groundLevel)

' Mouse
startX      = 0 
startY      = 0


'----------------
' Main game loop
'----------------
while not keydown(KEY_ESCAPE,true)
    ' clear screen
    set color black
    cls
   
    ' info box
    set color white
    set caret 10,420
    wln "--------------------"
    wln "Primitive Angry Bird"
    wln "--------------------"
    wln
    wln "Drag the red cirle by pressing your left mouse button"
    wln "Then hit the green circle !"
    wln
    wln "Control Keys :"
    wln "- SPACE BAR    = restart"
    wln "- ESCAPE       = quit"
   
    ' Draw ground
    set color green
    draw rect 0, groundLevel, 1000, 50,true
    set color white
    draw rect 0, groundLevel, 1000, 50
   
    ' Draw bird
    set color red
    draw ellipse bird.x, bird.y, bird.size,bird.size,true
    set color white
    draw ellipse bird.x, bird.y, bird.size,bird.size
   
    ' Draw pig
    set color lightgreen
    draw ellipse pig.x, pig.y, pig.size, pig.size,true
    set color white
    draw ellipse pig.x, pig.y, pig.size, pig.size
   
    ' Draw boxes
    for i = 0 to 2
        set color brown
        draw rect boxes[i].x, boxes[i].y, boxes.size, boxes.size, true
        set color white 
        draw rect boxes[i].x, boxes[i].y, boxes.size, boxes.size
    next
   
    ' Handle mouse input
    if not mousebutton(0) then
        ' Start dragging
        startX = mousex()
        startY = mousey()
    else
        ' Show trajectory line while dragging
        set color gray
        draw line bird.x, bird.y, mousex(), mousey()
       
        ' Release bird and calculate velocity
        bird.vx = (startX - mousex()) / 10
        bird.vy = (startY - mousey()) / 10       
    endif
   
    'Handle keyboard input to restart the game
    if keydown(KEY_SPACE,true) then Initialize(groundLevel)
           
    ' Update bird position
    ' Apply gravity
    bird.vy = bird.vy + gravity
       
    ' Update position
    bird.x = bird.x + bird.vx
    bird.y = bird.y + bird.vy
       
    ' Apply friction
    bird.vx = bird.vx * friction
    bird.vy = bird.vy * friction
       
    ' Check for ground collision
    if bird.y >= groundLevel - bird.size then
        bird.y = groundLevel - bird.size
        bird.vy = -bird.vy * 0.7  ' Bounce with reduced energy
    endif
       
    ' Check for pig collision
    distance = ((bird.x - pig.x)^2 + (bird.y - pig.y)^2)^0.5
    if distance <= bird.size + pig.size then
        pig.vx = bird.vx * 0.5  ' Transfer some velocity to the pig
        pig.vy = bird.vy * 0.5
        pig.hit = true
           
        ' Update position
        bird.x = width()+500
    endif
       
    ' Check for boxes and bird collisions
    for i = 0 to 2
        if bird.x + bird.size > boxes[i].x and bird.x - bird.size < boxes[i].x + boxes.size and
            bird.y + bird.size > boxes[i].y and bird.y - bird.size < boxes[i].y + boxes.size then
           
            ' Resolve overlap by moving bird away from box
            dx = bird.x - (boxes[i].x + boxes.size / 2)
            dy = bird.y - (boxes[i].y + boxes.size / 2)
            distance = (dx^2 + dy^2)^0.5
           
            if distance < bird.size + boxes.size / 2 then
                    ' Move bird away from box
                    overlap = (bird.size + boxes.size / 2) - distance
                    bird.x = bird.x + (dx / distance) * overlap
                    bird.y = bird.y + (dy / distance) * overlap
                   
                    ' Transfer velocity between bird and box
                    tempVx = bird.vx
                    tempVy = bird.vy
                    bird.vx = boxes[i].vx * 0.5
                    bird.vy = boxes[i].vy * 0.5
                    boxes[i].vx = tempVx * 0.5
                    boxes[i].vy = tempVy * 0.5
            endif
            boxes.hit = true
        endif
    next 
   
    if boxes.hit or pig.hit then
        ' Update pig position
        pig.y = pig.y + pig.vy
        pig.x = pig.x + pig.vx
       
        ' Apply gravity to pig
        pig.vy = pig.vy + gravity
    endif
   
    ' Check for ground collision for pig
    if pig.y >= groundLevel - pig.size then
        pig.y = groundLevel - pig.size
        pig.vy = -pig.vy * 0.5  ' Bounce with reduced energy
        pig.vx = -pig.vx * 0.5
    endif
   
    ' Update box positions
    if boxes.hit then
        for i = 0 to 2
            boxes[i].y = boxes[i].y + boxes[i].vy
            boxes[i].x = boxes[i].x + boxes[i].vx
           
            ' Apply gravity to boxes
            boxes[i].vy = boxes[i].vy + gravity
           
            ' Check for ground collision for boxes
            if boxes[i].y >= groundLevel - boxes.size then
                boxes[i].y = groundLevel - boxes.size
                boxes[i].vy = -boxes[i].vy * 0.01  ' Bounce with reduced energy
                boxes[i].vx = -boxes[i].vx * 0.01
            endif
        next
    endif
   
    'Check for collisions between boxes
    for i = 0 to 2
        for j = 0 to 2
            ' Check if boxes overlap
            if boxes[i].x < boxes[j].x + boxes.size and boxes[i].x + boxes.size > boxes[j].x and
               boxes[i].y < boxes[j].y + boxes.size and boxes[i].y + boxes.size > boxes[j].y then
               
                ' Resolve overlap by moving boxes apart
                dx = boxes[j].x - boxes[i].x
                dy = boxes[j].y - boxes[i].y
                overlapX = boxes.size - abs(dx)
                overlapY = boxes.size - abs(dy)
               
                if overlapX < overlapY then
                    ' Resolve horizontally
                    if dx < 0 then
                        boxes[i].x = boxes[i].x - overlapX+rnd(2,6)
                        boxes[j].x = boxes[j].x + overlapX+rnd(2,6)
                    else
                        boxes[i].x = boxes[i].x + overlapX+rnd(2,6)
                        boxes[j].x = boxes[j].x - overlapX+rnd(2,6)
                    endif
                else
                    ' Resolve vertically
                    if dy < 0 then
                        boxes[i].y = boxes[i].y - overlapY
                        boxes[j].y = boxes[j].y + overlapY
                    else
                        boxes[i].y = boxes[i].y + overlapY
                        boxes[j].y = boxes[j].y - overlapY
                    endif
                endif
            endif
        next
    next
   
    'Check for collisions between pig and boxes
    for i = 0 to 2
        'Check if pig overlaps with box
        if pig.x + pig.size > boxes[i].x and pig.x - pig.size < boxes[i].x + boxes.size and
           pig.y + pig.size > boxes[i].y and pig.y - pig.size < boxes[i].y + boxes.size then
            ' Resolve overlap
            dx = (pig.x - (boxes[i].x + boxes.size / 2))
            dy = (pig.y - (boxes[i].y + boxes.size / 2))
            distance = (dx^2 + dy^2)^0.5
           
            if distance < pig.size + boxes.size / 2 then
                ' Move pig away from box
                overlap = (pig.size + boxes.size / 2) - distance
                pig.x = pig.x + (dx / distance) * overlap
                pig.y = pig.y + (dy / distance) * overlap
               
                ' Transfer velocity between pig and box
                tempVx = pig.vx
                tempVy = pig.vy
                pig.vy = boxes[i].vx * 0.5
                pig.vy = boxes[i].vy * 0.5
                boxes[i].vx = tempVx * 0.5
                boxes[i].vy = tempVy * 0.5
            endif
        endif
    next
   
    ' Reset bird if it goes off-screen
    if bird.x < 0 or bird.x > width() or bird.y > height() or bird.y < 0 then
        bird.x = 100
        bird.y = groundLevel - bird.size
        bird.vx = 0
        bird.vy = 0
    endif
   
    ' Refresh screen
    fwait 60
    redraw
wend


'----------
' FUNCTION
'----------
function Initialize(groundLevel)
    ' Bird properties
    bird.size   = 10                       
    bird.x      = 100                       
    bird.y      = groundLevel - bird.size   
    bird.vx     = 0                         ' Horizontal velocity
    bird.vy     = 0                         ' Vertical velocity
   
    ' Pig properties
    pig.size    = 15                                   
    pig.x       = 700                                   ' Pig's x position
    pig.y       = groundLevel - pig.size - 3 * 30       ' Pig's y position (on top of boxes)
    pig.vx      = 0                                     ' Pig's horizontal velocity
    pig.vy      = 0                                     ' Pig's vertical velocity
    pig.hit     = false
   
    ' Box properties (stack of 3 boxes)
    boxes.size     = 30                                 ' Size of each box (width and height)   
    for i = 0 to 2
        boxes[i] = []
        boxes[i].x = pig.x-pig.size                      ' Staggered x positions
        boxes[i].y = groundLevel - (i + 1) * boxes.size  ' Stacked y positions
        boxes[i].vx = 0                                  ' Horizontal velocity
        boxes[i].vy = 0                                  ' Vertical velocity
    next
    boxes.hit = false
endfunc

Print this item

  3d text
Posted by: Marcus - 03-08-2025, 12:05 PM - Forum: NaaLaa 7 Code - Replies (2)

Johnno posted an old screensaver effect, so I'm doing the same Smile

It's a voxel text based on the pixels of a string printed with the default naalaa font.


Code:
' 3dtext.n7
' ---------

include "s3d.n7"

res = 480

set window "3D Text", res*min(screenw()/screenh(), 2), res, true
set redraw off

S3D_SetView(primary, rad(90), 0.1, 50)

mesh = Create3DText("NaaLaa VII", 4, 255, 255, 255, 64, 64, 96)

a = 0
while not keydown(KEY_ESCAPE)
    ay = (ay + 1)%360
    ax = (ax + 0.55)%360
   
    set color 0, 0, 0, 64
    cls
    S3D_Clear()
    S3D_Translate(0, 0, 4.5)
    S3D_Scale(0.1, 0.1, 0.1)
    S3D_RotateY(sin(rad(ay))*rad(65))
    S3D_RotateX(sin(rad(ax))*rad(22.5))
    S3D_RotateZ(sin(rad(ax*2))*rad(22.5))
    S3D_Mesh(mesh, 0)
    redraw
    fwait 60
wend

' Create3DText
' ------------
function Create3DText(txt, thickness, rtop, gtop, btop, rbtm, gbtm, bbtm)
    tex = createimage(64, 1)
    set image tex
    for x = 0 to width(tex) - 1
        p = x/width(tex)
        set color rtop*(1 - p) + rbtm*p, gtop*(1 - p) + gbtm*p, btop*(1 - p) + bbtm*p
        set pixel x, 0
    next
    set image primary
    w = fwidth(txt)
    h = fheight()
    tmpImage = createimage(w, h)
    set image tmpImage
    set color 255, 255, 255
    set caret 0, 0
    write txt
    set image primary
    maxy = 0
    for y = 0 to height(tmpImage) - 1  for x = 0 to width(tmpImage) - 1
        if Red(pixeli(tmpImage, x, y)) maxy = max(maxy, y)
    next
    mesh = S3D_BeginMesh()
    S3D_Begin(S3D_QUADS)
    S3D_Texture(tex)
    ht = thickness/2
    for y = 0 to height(tmpImage) - 1  for x = 0 to width(tmpImage) - 1
        if Red(pixeli(tmpImage, x, y))
            t = Red(pixeli(tmpImage, x, y - 1))
            b = Red(pixeli(tmpImage, x, y + 1))
            l = Red(pixeli(tmpImage, x - 1, y))
            r = Red(pixeli(tmpImage, x + 1, y))
            vx = x - w/2; vy = y - h/2
            tt = y/maxy; tb = (y + 1)/maxy
            S3D_Color(160, 160, 160)
            S3D_Vertex(vx, vy, -ht, tt, 0)
            S3D_Vertex(vx + 1, vy, -ht, tt, 0)
            S3D_Vertex(vx + 1, vy + 1, -ht, tb, 0)
            S3D_Vertex(vx, vy + 1, -ht, tb, 0)
            if not t
                S3D_Color(255, 255, 255)
                S3D_Vertex(vx, vy, ht, tt, 0)
                S3D_Vertex(vx + 1, vy, ht, tt, 0)
                S3D_Vertex(vx + 1, vy, -ht, tt, 0)
                S3D_Vertex(vx, vy, -ht, tt, 0)
            endif
            if not b
                S3D_Color(64, 64, 64)
                S3D_Vertex(vx, vy + 1, ht, tb, 0)
                S3D_Vertex(vx, vy + 1, -ht, tb, 0)
                S3D_Vertex(vx + 1, vy + 1, -ht, tb, 0)
                S3D_Vertex(vx + 1, vy + 1, ht, tb, 0)
            endif
            if not l
                S3D_Color(255, 255, 255)
                S3D_Vertex(vx, vy, ht, tt, 0)
                S3D_Vertex(vx, vy, -ht, tt, 0)
                S3D_Vertex(vx, vy + 1, -ht, tb, 0)
                S3D_Vertex(vx, vy + 1, ht, tb, 0)
            endif
            if not r
                S3D_Color(64, 64, 64)
                S3D_Vertex(vx + 1, vy, ht, tt, 0)
                S3D_Vertex(vx + 1, vy + 1, ht, tb, 0)
                S3D_Vertex(vx + 1, vy + 1, -ht, tb, 0)
                S3D_Vertex(vx + 1, vy, -ht, tt, 0)
            endif
        endif
    next
    S3D_End()
    S3D_EndMesh()
    free image tmpImage
    return mesh
endfunc

' Pixeli helpers.
function Alpha(c); return int(c/16777216); endfunc
function Red(c); return int((c/65536))%256; endfunc
function Green(c); return int((c/256))%256; endfunc
function Blue(c); return c%256; endfunc

Print this item

  Mystify
Posted by: johnno56 - 03-07-2025, 10:54 PM - Forum: NaaLaa 6 Code - Replies (2)

As well as the N7 version... Let's not forget about its younger cousin... N6

J


.zip   mystify.nala.zip (Size: 798 bytes / Downloads: 5)

Print this item