Just playing around with N7 again and did this sample with shapes.
Code:
' ============================================================================
' SPACE SHOOTER - NaaLaa / N7
' ----------------------------------------------------------------------------
'
' Controls:
' Arrow keys / WASD - Move ship
' Space / Z - Shoot
' Space / Enter - Start / continue
' ESC - Quit
' ============================================================================
#win32
set window "Space Shooter", 800, 600
set redraw off
set mouse false
' ── constants ────────────────────────────────────────────────────────────────
constant SCREEN_W = 800
constant SCREEN_H = 600
constant MAX_BULLETS = 64
constant MAX_ENEMY_BULLETS = 128
constant MAX_ENEMIES = 64
constant MAX_PARTICLES = 256
constant MAX_STARS = 120
constant PLAYER_SPEED = 5
constant BULLET_SPEED = 10
constant ENEMY_BULLET_SPD = 2.2
constant SHOOT_COOLDOWN = 10
constant INVINCIBLE_FRAMES = 120
constant SPAWN_INVINCIBLE = 90
' Game states
constant ST_MENU = 0
constant ST_PLAYING = 1
constant ST_BANNER = 2
constant ST_GAMEOVER = 3
' ── globals ──────────────────────────────────────────────────────────────────
visible bullets = dim(MAX_BULLETS)
visible ebullets = dim(MAX_ENEMY_BULLETS)
visible enemies = dim(MAX_ENEMIES)
visible particles = dim(MAX_PARTICLES)
visible stars = dim(MAX_STARS)
visible enemy_count = 0
visible score = 0
visible lives = 3
visible wave = 1
visible px = SCREEN_W / 2
visible py = SCREEN_H - 60
visible shoot_cd = 0
visible invincible = 0
visible gstate = ST_MENU
visible banner_timer = 0
' Track previous space state for single-press detection on menu/gameover
visible spacePrev = false
' ── helpers ──────────────────────────────────────────────────────────────────
randomize clock()
' Random float in [lo, hi]
function randf(lo, hi)
return lo + rnd() * (hi - lo)
endfunc
function clamp(v, lo, hi)
if v < lo then return lo
if v > hi then return hi
return v
endfunc
function absf(v)
if v < 0 then return -v
return v
endfunc
' ── object factories ─────────────────────────────────────────────────────────
function makeBullet(x, y)
return [x: x, y: y, alive: true]
endfunc
function makeEBullet(x, y, vx, vy)
return [x: x, y: y, vx: vx, vy: vy, alive: true]
endfunc
function makeParticle(x, y, vx, vy, r, g, b)
return [x: x, y: y, vx: vx, vy: vy, life: 1.0, r: r, g: g, b: b]
endfunc
' ── particle spawning ────────────────────────────────────────────────────────
function spawn_particles(x, y, r, g, b, n)
i = 0
while i < n
' find a free slot
j = 0
placed = false
while j < MAX_PARTICLES and not placed
p = particles[j]
if p = 0 or p.life <= 0 then
angle = randf(0, 2 * PI)
spd = randf(1.0, 4.0)
particles[j] = makeParticle(x, y, cos(angle) * spd, sin(angle) * spd, r, g, b)
placed = true
endif
j = j + 1
wend
i = i + 1
wend
endfunc
' ── stars (parallax background) ──────────────────────────────────────────────
function init_stars()
for i = 0 to MAX_STARS - 1
stars[i] = [
x: randf(0, SCREEN_W),
y: randf(0, SCREEN_H),
speed: randf(0.3, 1.2),
brightness: floor(randf(80, 220))
]
next
endfunc
function scroll_stars()
for i = 0 to MAX_STARS - 1
s = stars[i]
s.y = s.y + s.speed
if s.y > SCREEN_H then
s.y = 0
s.x = randf(0, SCREEN_W)
endif
next
endfunc
' ── enemy type table (data + drawing strategy) ──────────────────────────────
' Each entry: { r, g, b, render: function(e, x, y, hw, hh) }
visible enemyTypes = [
[r: 220, g: 80, b: 80, render: function(e, x, y, hw, hh)
' Inverted triangle
for dy = -hh to hh
half = floor(hw * (1 - (dy + hh) / (2 * hh)))
draw line x - half, y + dy, x + half, y + dy
next
endfunc],
[r: 240, g: 190, b: 60, render: function(e, x, y, hw, hh)
draw rect x - hw, y - hh, e.w, e.h, true
set color 255, 240, 80, 200
draw rect x - 8, y - 4, 16, 8, true
endfunc],
[r: 160, g: 120, b: 240, render: function(e, x, y, hw, hh)
' Filled circle
draw ellipse x, y, hw, hh, true
endfunc]
]
' ── enemies ──────────────────────────────────────────────────────────────────
function spawn_enemies(wv)
enemy_count = 0
nCols = 8
nRows = 1 + wv
if nRows > 4 then nRows = 4
base_vx = 0.45 + wv * 0.12
for r = 0 to nRows - 1
for c = 0 to nCols - 1
if enemy_count < MAX_ENEMIES then
dir = -1
if c % 2 = 0 then dir = 1
enemies[enemy_count] = [
x: 70 + c * 85,
y: 50 + r * 50,
vx: dir * base_vx,
w: 28, h: 22,
hp: 1 + wv / 3,
typ: r % 3,
alive: true,
shoot_timer: 180 + floor(rnd() * 240)
]
enemy_count = enemy_count + 1
endif
next
next
endfunc
function reset_game()
score = 0
lives = 3
wave = 1
px = SCREEN_W / 2
py = SCREEN_H - 60
shoot_cd = 0
invincible = SPAWN_INVINCIBLE
for i = 0 to MAX_BULLETS - 1
bullets[i] = 0
next
for i = 0 to MAX_ENEMY_BULLETS - 1
ebullets[i] = 0
next
for i = 0 to MAX_PARTICLES - 1
particles[i] = 0
next
for i = 0 to MAX_ENEMIES - 1
enemies[i] = 0
next
spawn_enemies(wave)
endfunc
' ── update ───────────────────────────────────────────────────────────────────
function update_game()
' Movement
if keydown(KEY_LEFT) or keydown(KEY_A) then px = px - PLAYER_SPEED
if keydown(KEY_RIGHT) or keydown(KEY_D) then px = px + PLAYER_SPEED
if keydown(KEY_UP) or keydown(KEY_W) then py = py - PLAYER_SPEED
if keydown(KEY_DOWN) or keydown(KEY_S) then py = py + PLAYER_SPEED
px = clamp(px, 16, SCREEN_W - 16)
py = clamp(py, 16, SCREEN_H - 16)
' Shoot
if shoot_cd > 0 then shoot_cd = shoot_cd - 1
if (keydown(KEY_SPACE) or keydown(KEY_Z)) and shoot_cd = 0 then
for i = 0 to MAX_BULLETS - 1
if bullets[i] = 0 or not bullets[i].alive then
bullets[i] = makeBullet(px, py - 16)
' Double shot at higher waves
if wave >= 3 and i + 1 < MAX_BULLETS then
if bullets[i + 1] = 0 or not bullets[i + 1].alive then
bullets[i + 1] = makeBullet(px + 10, py - 10)
endif
endif
break
endif
next
shoot_cd = SHOOT_COOLDOWN
endif
' Move player bullets
for i = 0 to MAX_BULLETS - 1
b = bullets[i]
if b <> 0 and b.alive then
b.y = b.y - BULLET_SPEED
if b.y < -10 then b.alive = false
endif
next
' Move enemy bullets
for i = 0 to MAX_ENEMY_BULLETS - 1
b = ebullets[i]
if b <> 0 and b.alive then
b.x = b.x + b.vx
b.y = b.y + b.vy
if b.y > SCREEN_H + 10 or b.x < -10 or b.x > SCREEN_W + 10 then b.alive = false
endif
next
' Move enemies, wall bounce
hit_wall = false
for i = 0 to enemy_count - 1
e = enemies[i]
if e <> 0 and e.alive then
e.x = e.x + e.vx
if (e.x + e.w / 2 > SCREEN_W - 10 and e.vx > 0) or (e.x - e.w / 2 < 10 and e.vx < 0) then
hit_wall = true
endif
endif
next
if hit_wall then
for i = 0 to enemy_count - 1
e = enemies[i]
if e <> 0 and e.alive then
e.vx = -e.vx
e.y = e.y + 14
endif
next
endif
' Enemy shooting
for i = 0 to enemy_count - 1
e = enemies[i]
if e <> 0 and e.alive then
e.shoot_timer = e.shoot_timer - 1
if e.shoot_timer <= 0 then
e.shoot_timer = 150 + floor(rnd() * 200) - wave * 8
if e.shoot_timer < 60 then e.shoot_timer = 60
dx = px - e.x
dy = py - e.y
dist = sqr(dx * dx + dy * dy) + 0.001
spd = ENEMY_BULLET_SPD + wave * 0.1
for j = 0 to MAX_ENEMY_BULLETS - 1
if ebullets[j] = 0 or not ebullets[j].alive then
ebullets[j] = makeEBullet(e.x, e.y, dx / dist * spd, dy / dist * spd)
break
endif
next
endif
endif
next
' Bullet-enemy collisions
for bi = 0 to MAX_BULLETS - 1
b = bullets[bi]
if b <> 0 and b.alive then
for ei = 0 to enemy_count - 1
e = enemies[ei]
if e <> 0 and e.alive then
if absf(b.x - e.x) < e.w / 2 + 3 and absf(b.y - e.y) < e.h / 2 + 3 then
b.alive = false
e.hp = e.hp - 1
t = enemyTypes[e.typ]
if e.hp <= 0 then
e.alive = false
score = score + 10 + wave * 5
spawn_particles(e.x, e.y, t.r, t.g, t.b, 12)
else
spawn_particles(e.x, e.y, 255, 255, 255, 3)
endif
break
endif
endif
next
endif
next
' Enemy bullet-player & enemy-player collisions
if invincible > 0 then
invincible = invincible - 1
else
for i = 0 to MAX_ENEMY_BULLETS - 1
b = ebullets[i]
if b <> 0 and b.alive then
if absf(b.x - px) < 14 and absf(b.y - py) < 12 then
b.alive = false
lives = lives - 1
invincible = INVINCIBLE_FRAMES
spawn_particles(px, py, 80, 160, 255, 15)
if lives <= 0 then
gstate = ST_GAMEOVER
return
endif
endif
endif
next
for i = 0 to enemy_count - 1
e = enemies[i]
if e <> 0 and e.alive then
if absf(e.x - px) < e.w / 2 + 10 and absf(e.y - py) < e.h / 2 + 10 then
lives = lives - 1
invincible = INVINCIBLE_FRAMES
spawn_particles(px, py, 80, 160, 255, 15)
if lives <= 0 then
gstate = ST_GAMEOVER
return
endif
endif
endif
next
endif
' Particles
for i = 0 to MAX_PARTICLES - 1
p = particles[i]
if p <> 0 and p.life > 0 then
p.x = p.x + p.vx
p.y = p.y + p.vy
p.vy = p.vy + 0.06
p.life = p.life - 0.025
endif
next
' Stars scroll
scroll_stars()
' Wave clear?
aliveCount = 0
for i = 0 to enemy_count - 1
if enemies[i] <> 0 and enemies[i].alive then aliveCount = aliveCount + 1
next
if aliveCount = 0 then
wave = wave + 1
score = score + wave * 50
spawn_enemies(wave)
gstate = ST_BANNER
banner_timer = 120
return
endif
' Enemy reached bottom -> game over
for i = 0 to enemy_count - 1
e = enemies[i]
if e <> 0 and e.alive then
if e.y + e.h / 2 > SCREEN_H - 40 then
gstate = ST_GAMEOVER
return
endif
endif
next
endfunc
' ── drawing ──────────────────────────────────────────────────────────────────
function draw_stars()
for i = 0 to MAX_STARS - 1
s = stars[i]
if s <> 0 then
b = s.brightness
set color b, b, b, 255
draw pixel s.x, s.y
endif
next
endfunc
function draw_player(blink)
if blink then return
' Thruster
set color 80, 150, 255, 120
draw rect px - 5, py + 10, 10, 8, true
' Body (triangle approximated by horizontal lines)
set color 60, 140, 255, 255
for dy = -12 to 12
half = floor(14 * (1 - (dy + 12) / 24))
draw line px - half, py + dy, px + half, py + dy
next
' Cockpit
set color 180, 220, 255, 200
draw rect px - 4, py - 7, 8, 10, true
endfunc
function draw_enemy(e)
if e = 0 or not e.alive then return
t = enemyTypes[e.typ]
set color t.r, t.g, t.b, 255
t.render(e, e.x, e.y, e.w / 2, e.h / 2)
endfunc
function draw_bullets()
set color 100, 200, 255, 255
for i = 0 to MAX_BULLETS - 1
b = bullets[i]
if b <> 0 and b.alive then
draw rect b.x - 2, b.y - 8, 4, 14, true
endif
next
set color 255, 140, 60, 255
for i = 0 to MAX_ENEMY_BULLETS - 1
b = ebullets[i]
if b <> 0 and b.alive then
draw rect b.x - 3, b.y - 3, 6, 6, true
endif
next
endfunc
function draw_particles()
set additive true
for i = 0 to MAX_PARTICLES - 1
p = particles[i]
if p <> 0 and p.life > 0 then
a = floor(p.life * 255)
if a < 0 then a = 0
if a > 255 then a = 255
set color p.r, p.g, p.b, a
draw rect p.x - 2, p.y - 2, 4, 4, true
endif
next
set additive false
endfunc
function draw_hud()
set color 220, 220, 255, 255
set caret 10, 10
write "SCORE " + score
set caret SCREEN_W / 2 - 40, 10
write "WAVE " + wave
' Lives as small ship rectangles
for i = 0 to lives - 1
set color 80, 160, 255, 255
draw rect SCREEN_W - 20 - i * 18, 8, 12, 18, true
next
endfunc
function draw_overlay(line1, line2)
set color 5, 5, 20, 200
draw rect SCREEN_W / 2 - 180, SCREEN_H / 2 - 80, 360, 160, true
set color 80, 80, 180, 255
draw rect SCREEN_W / 2 - 180, SCREEN_H / 2 - 80, 360, 160, false
set color 160, 200, 255, 255
set caret SCREEN_W / 2, SCREEN_H / 2 - 40
center line1
set color 180, 180, 220, 255
set caret SCREEN_W / 2, SCREEN_H / 2 - 10
center line2
set color 120, 220, 160, 255
set caret SCREEN_W / 2, SCREEN_H / 2 + 30
center "PRESS SPACE"
endfunc
' ── init ─────────────────────────────────────────────────────────────────────
init_stars()
reset_game()
gstate = ST_MENU
' ── main loop ────────────────────────────────────────────────────────────────
do
' Handle Space/Enter on non-playing states (single-press)
spaceNow = keydown(KEY_SPACE) or keydown(KEY_RETURN)
if spaceNow and not spacePrev then
if gstate = ST_MENU or gstate = ST_GAMEOVER then
reset_game()
gstate = ST_PLAYING
elseif gstate = ST_BANNER then
gstate = ST_PLAYING
endif
endif
spacePrev = spaceNow
if gstate = ST_PLAYING then
update_game()
elseif gstate = ST_BANNER then
' Drift particles only (stars always scroll below)
for i = 0 to MAX_PARTICLES - 1
p = particles[i]
if p <> 0 and p.life > 0 then
p.x = p.x + p.vx
p.y = p.y + p.vy
p.life = p.life - 0.025
endif
next
banner_timer = banner_timer - 1
if banner_timer <= 0 then gstate = ST_PLAYING
endif
' Stars always scroll, even on menu / game-over
if gstate <> ST_PLAYING then scroll_stars()
' ── render ──
set color 5, 5, 16, 255
cls
draw_stars()
draw_bullets()
for i = 0 to enemy_count - 1
draw_enemy(enemies[i])
next
blink = false
if invincible > 0 then
if floor(invincible / 8) % 2 = 0 then blink = true
endif
draw_player(blink)
draw_particles()
draw_hud()
if gstate = ST_MENU then
draw_overlay("SPACE SHOOTER", "ARROW KEYS + SPACE")
elseif gstate = ST_GAMEOVER then
draw_overlay("GAME OVER", "SCORE " + score)
elseif gstate = ST_BANNER then
draw_overlay("WAVE " + wave, "GET READY")
endif
redraw
fwait 60
until keydown(KEY_ESCAPE)
