Here's a more complete game, now with all assets generated in game. It requires atleast n7 version 24.01.19
Code:
' Denaalaafender
' --------------
' I've never played Defender. This game is just based on my memory of Youtube videos. For the fun
' of it, all assets in this game are generated by code.
'
' Use the arrow keys to move and spacebar to shoot
' Destroy all enemy spaceships to complete a level
' Try to help the humans from being obducted by the aliens
' You get bonus points for each human alive at the end of a level
include "list.n7"
include "file.n7"
#win32
' Images.
visible vPlayerImage, vPlayerBulletImage
visible vDudeImage, vParachuteImage
visible vGrabberImage, vEnemyImage, vEnemyBulletImage
' Sound effects.
visible vShootSound, vExplosionSound, vSmallExplosionSound, vLargeExplosionSound, vEnemyShootSound
' Top score.
visible vTopScore = 0
' Level.
visible VIEW_H = 120, VIEW_W = 160
visible vLevelWidth = VIEW_W*6, vScroll
visible vStars = List(), vTerrain = List(), vParticles = List()
' Player and score.
visible vPlayer, vPlayerBullets = List()
visible vScore
' Grabbers.
visible vGrabbers = List()
visible vMaxGrabbersOnScreen, vGrabberTimer, vGrabberMinDelay, vGrabberRndDelay
' Enemies.
visible vEnemies = List(), vEnemyBullets = List()
visible vLevelEnemies, vEnemiesLeft, vMaxEnemiesOnScreen, vEnemyTimer, vEnemyMinDelay, vEnemyRndDelay
visible vEnemyShootTimer, vEnemyShootMinDelay, vEnemyShootRndDelay
' Dudes.
visible vDudes = List()
' Create window and disable automatic redraw.
set window "Denaalaafender", VIEW_W, VIEW_H, true, 3
set redraw off
' Create and set save folder.
folder = GetPathCombined(GetAppDataDirectory(), "naalaa7")
CreateDirectory(folder)
saveFilename = GetPathCombined(folder, "denaalaafender.bin")
f = openfile(saveFilename, true)
if typeof(f)
vTopScore = fread(f, 32)
free file f
endif
LoadAssets()
do
if not Title() end
level = 0
lives = 3
vScore = 0
do
InitLevel(level, lives)
scrollOffset = 0
vScroll = vPlayer.x + width(vPlayerImage)/2 - VIEW_W/2
' Game loop, use delta time for smoother updates.
lastTick = clock()
do
t = clock()
dt = (t - lastTick)/1000
lastTick = t
quit = keydown(KEY_ESCAPE, true)
' Add grabber?
if vEnemiesLeft or vEnemies.size > 1
vGrabberTimer = vGrabberTimer - dt
if vGrabberTimer <= 0 and vGrabbers.size < vMaxGrabbersOnScreen
vGrabberTimer = vGrabberMinDelay + rnd()*vGrabberRndDelay
dude = GetGrabableDude()
if dude vGrabbers.Add(Grabber(dude))
endif
endif
' Add regular enemy?
if vEnemiesLeft
vEnemyTimer = vEnemyTimer - dt
if vEnemyTimer <= 0 and vEnemies.size < vMaxEnemiesOnScreen
vEnemyTimer = vEnemyMinDelay + rnd()*vEnemyRndDelay
vEnemies.Add(Enemy())
vEnemiesLeft = vEnemiesLeft - 1
endif
endif
' Add enemy bullet?
vEnemyShootTimer = vEnemyShootTimer - dt
if vEnemyShootTimer <= 0
if AddEnemyBullet(level > 3)
vEnemyShootTimer = vEnemyShootMinDelay + rnd()*vEnemyShootRndDelay
else
vEnemyShootTimer = vEnemyShootMinDelay/2
endif
endif
' Update player and sprite lists.
vPlayer.Update(dt)
UpdateSprites(vPlayerBullets, dt)
UpdateSprites(vDudes, dt)
UpdateSprites(vGrabbers, dt)
UpdateSprites(vEnemies, dt)
UpdateSprites(vEnemyBullets, dt)
UpdateSprites(vParticles, dt)
' Adjust view depending on player's direction.
if vPlayer.cel = 0 wso = -0.6*VIEW_W*0.5
else wso = 0.6*VIEW_W*0.5
i = 1.5*dt
scrollOffset = scrollOffset*(1 - i) + wso*i
vScroll = vPlayer.x + width(vPlayerImage)/2 - VIEW_W/2 + scrollOffset
' Draw background.
set color 0, 0, 0
cls
set color 97, 29, 0
for i = 0 to vTerrain.size - 1 DrawLine(vTerrain[i][0], vTerrain[i][1],
vTerrain[i][2], vTerrain[i][3])
for i = 0 to vStars.size - 1 DrawPixel(vStars[i].x, vStars[i].y, 79, 79, 79)
' Draw sprites.
DrawSprites(vDudes)
DrawSprites(vGrabbers)
DrawSprites(vEnemyBullets)
DrawSprites(vEnemies)
DrawSprites(vPlayerBullets)
DrawSprites(vParticles)
vPlayer.Draw()
' Draw map.
DrawMap()
' Draw player stamina.
x = 10
y = 3
set color 102, 102, 102
draw rect x, y, 19, 4
if vPlayer.stamina > 0
set color 193, 224, 254
for i = 0 to vPlayer.stamina - 1 draw rect x + 2 + i*4, y + 1, 3, 2, true
endif
' Draw enemies left.
x = VIEW_W - 28
y = 3
set color 102, 102, 102
draw rect x, y, 18, 4
w = ceil(16*(vEnemiesLeft + vEnemies.size)/vLevelEnemies)
set color 181, 50, 32, 128
draw rect x + 1, y + 1, w, 2, true
' Score
set caret VIEW_W/2, VIEW_H - fheight() + 3
set color 102, 102, 102
center vScore
redraw
wait 1
until quit or (vPlayer.stamina < 0 or (vEnemiesLeft = 0 and vEnemies.size = 0)) and
vParticles.size = 0
' Quit or show mesage.
set color 0, 0, 0
cls
if quit
break
elseif vPlayer.stamina < 0
lives = lives - 1
set caret VIEW_W/2, (VIEW_H - fheight())/2
set color 181, 50, 32
if lives <= 0
' New top score?
if vScore > vTopScore
vTopScore = vScore
f = createfile(saveFilename, true)
if typeof(f)
write file f, vTopScore, 32
free file f
endif
endif
center "GAME OVER!"
else
center "LIFE LOST!"
endif
else
level = level + 1
bonus = vDudes.size*100
vScore = vScore + bonus
set caret VIEW_W/2, VIEW_H/2 - fheight()
set color 93, 239, 48
center "LEVEL COMPLETE!"
set color 254, 254, 254
x = (VIEW_W - (width(vDudeImage) + fwidth(" x " + vDudes.size + " = " + bonus)))/2
draw image vDudeImage, x, VIEW_H/2 + fheight()
x = x + width(vDudeImage)
set caret x, VIEW_H/2 + fheight()
write " x " + vDudes.size + " = " + bonus
endif
' Show message.
redraw
wait 4000
until lives <= 0
loop
' LoadAssets
' ----------
function LoadAssets()
' Images.
vPlayerImage = ImageFromMonoData([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1],
[0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
[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, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]],
254, 254, 254, 1, 2)
vPlayerBulletImage = ImageFromMonoData([
[0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 0]],
189, 191, 0, 1, 2)
vDudeImage = ImageFromMonoData([
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[1, 0, 1, 0, 1],
[1, 0, 1, 0, 1],
[0, 0, 1, 0, 0],
[0, 1, 0, 1, 0],
[1, 0, 0, 0, 1]],
161, 27, 205, 1, 1)
vParachuteImage = ImageFromMonoData([
[0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0],
[0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0],
[0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0],
[1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1],
[1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0]],
100, 176, 254, 1, 1)
vGrabberImage = ImageFromMonoData([
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0]],
0, 144, 50, 4, 1)
vEnemyImage = ImageFromMonoData([
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 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, 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, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]],
181, 50, 32, 4, 1)
vEnemyBulletImage = ImageFromMonoData([
[0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0],
[1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1],
[0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0]],
251, 195, 254, 4, 1)
' Sound effects.
vShootSound = CreateSquareSfx(0.25, 500, 100, 0.75, 8000)
vExplosionSound = CreateNoiseSfx(0.5, 0.9, 0.1, 8000)
vLargeExplosionSound = CreateNoiseSfx(1.0, 0.5, 0.05, 8000)
vSmallExplosionSound = CreateNoiseSfx(0.25, 0.75, 0, 8000)
vEnemyShootSound = CreateSineSfx(0.3, 300, 50, 0.5, 8000)
endfunc
' Title
' -----
function Title()
set color 0, 0, 0
cls
set caret VIEW_W/2, VIEW_H/2 - fheight()*2
set color 254, 254, 254
center "DENAALAAFENDER"
center "TOP SCORE"
center vTopScore
center
set color 100, 176, 254
center "PRESS SPACEBAR"
redraw
do
quit = keydown(KEY_ESCAPE, true)
wait 16
until quit or keydown(KEY_SPACE, true)
return not quit
endfunc
' InitLevel
' ---------
function InitLevel(level, lives)
' Create some stars and moutains.
randomize level
vStars.Clear()
for i = 1 to 100 vStars.Add([x: rnd(vLevelWidth), y: rnd(VIEW_H - 40)])
vTerrain.Clear()
for i = 0 to 5
x = i*VIEW_W
h = 10 + rnd(30)
vTerrain.Add([x, VIEW_H, x + VIEW_W/2, VIEW_H - h])
vTerrain.Add([x + VIEW_W/2, VIEW_H - h, x + VIEW_W, VIEW_H])
next
' Setup player.
vPlayer = Player(vLevelWidth/2, VIEW_H/2)
vPlayer.dx = 0
vPlayer.dy = 0
vPlayerBullets.Clear()
' Grabber settings and list.
vGrabberMinDelay = max(5 - level*0.5, 3)
vGrabberRndDelay = max(4 - level*0.25, 2)
vGrabberTimer = vGrabberMinDelay
vMaxGrabbersOnScreen = 5 + level
vGrabbers.Clear()
' Enemies.
vEnemyMinDelay = max(5 - level*0.5, 2)
vEnemyRndDelay = max(4 - level*0.25, 2)
vEnemyTimer = vEnemyMinDelay + vEnemyRndDelay
vMaxEnemiesOnScreen = 5 + level
vLevelEnemies = 5 + int(level*1.5)
vEnemiesLeft = vLevelEnemies
vEnemies.Clear()
' Enemy bullets timers.
vEnemyShootMinDelay = max(4 - level*0.5, 3)
vEnemyShootRndDelay = max(3 - level*0.25, 1)
vEnemyShooterTimer = vEnemyShootMinDelay
vEnemyBullets = List()
' Place dudes.
vDudes.Clear()
for i = 1 to 10
x = rnd(vLevelWidth) + 0.5
for j = 0 to vTerrain.size - 1; t = vTerrain[j]
if x >= t[0] and x <= t[2]
y = t[1] + (x - t[0])*(t[3] - t[1])/(t[2] - t[0])
vDudes.Add(Dude(x, y))
break
endif
next
next
' Particles
vParticles.Clear()
' Show message.
set color 0, 0, 0
cls
set caret VIEW_W/2, VIEW_H/2 - fheight()
set color 254, 254, 254
center "LEVEL " + (level + 1)
center
center "LIVES " + lives
redraw
wait 3000
endfunc
' Player
' ------
function Player(x, y)
s = Sprite(vPlayerImage, 0)
s.stamina = 4
s.dx = 0
s.dy = 0
s.canShoot = true
s.blinkTimer = 0
s.SpriteDraw = s.Draw
s.Center(x, y)
' Update
' ------
s.Update = function(dt)
if this.stamina < 0 return
' Collision, just blink for a while.
if this.blinkTimer <= 0
if this.HitByAnySprite(vEnemies, false) or
this.HitByAnySprite(vGrabbers, false) or
this.HitByAnySprite(vEnemyBullets, true)
this.stamina = this.stamina - 1
if this.stamina >= 0
this.blinkTimer = 3
play sound vSmallExplosionSound
else
AddExplosion(this.x + this.w/2, this.y + this.h/2, 254, 254, 254, 40)
play sound vLargeExplosionSound
return
endif
endif
else
this.blinkTimer = max(this.blinkTimer - dt, 0)
endif
' Move.
if this.dx > 0 this.dx = max(this.dx - 100*dt, 0)
elseif this.dx < 0 this.dx = min(this.dx + 100*dt, 0)
if this.dy > 0 this.dy = max(this.dy - 100*dt, 0)
elseif this.dy < 0 this.dy = min(this.dy + 100*dt, 0)
if keydown(KEY_LEFT)
this.dx = max(this.dx - 200*dt, -200)
this.cel = 0
elseif keydown(KEY_RIGHT)
this.dx = min(this.dx + 200*dt, 200)
this.cel = 1
endif
if keydown(KEY_UP) this.dy = max(this.dy - 200*dt, -200)
if keydown(KEY_DOWN) this.dy = min(this.dy + 200*dt, 200)
this.x = (this.x + this.dx*dt)%vLevelWidth
this.y = this.y + this.dy*dt
if this.y < 0
this.y = 0
this.dy = 0
elseif this.y + this.h > VIEW_H
this.y = VIEW_H - this.h
this.dy = 0
endif
' Shoot, require release of spacebar between shots.
if keydown(KEY_SPACE)
if this.canShoot
this.canShoot = false
if this.cel = 0 dx = -300
else dx = 300
vPlayerBullets.Add(Bullet(vPlayerBulletImage,
this.x + this.w/2, this.y + 4,
dx, 0))
play sound vShootSound, 0.25
endif
else
this.canShoot = true
endif
endfunc
' Draw
' ----
s.Draw = function()
if this.stamina >= 0 and this.blinkTimer%0.2 < 0.1 this.SpriteDraw()
endfunc
return s
endfunc
' Bullet
' ------
function Bullet(img, x, y, dx, dy)
s = Sprite(img, 0)
s.dx = dx
s.dy = dy
s.Center(x, y)
' Update
' ------
s.Update = function(dt)
this.cel = (this.cel + dt*15)%cels(this.img)
this.x = this.x + this.dx*dt
this.y = this.y + this.dy*dt
this.x = this.x%vLevelWidth
return this.Visible()
endfunc
return s
endfunc
' Dude
' ----
' Stand stills, gets obducted and uses a parachute ...
function Dude(x, y)
s = Sprite(vDudeImage, 0)
s.baseY = int(y - s.h)
s.dy = 0
s.grabber = unset
s.SetPosition(x - s.w/2, s.baseY)
s.SetGrabber = function(grabber)
this.grabber = grabber
endfunc
' Update
' ------
s.Update = function(dt)
if this.grabber
this.y = this.grabber.y + this.grabber.h
elseif this.y < this.baseY
this.y = min(this.y + 25*dt, this.baseY)
endif
return true
endfunc
' Draw
' ----
s.Draw = function()
set color 255, 255, 255
DrawImage(this.img, this.x, this.y, 0)
if this.y < this.baseY and not this.grabber
DrawImage(vParachuteImage, this.x - 4, this.y - height(vParachuteImage), 0)
endif
endfunc
return s
endfunc
' Grabber
' -------
' Obducts dudes.
function Grabber(dude)
s = Sprite(vGrabberImage, 0)
s.dude = dude
s.SetPosition(dude.x -2, -8)
' Update
' ------
s.Update = function(dt)
' Die?
if this.HitByAnySprite(vPlayerBullets, true)
this.dude.grabber = unset
play sound vExplosionSound
AddExplosion(this.x + this.w/2, this.y + this.h/2, 0, 144, 50, 40)
return false
endif
' If the dude's grabber has been set, the grabber should try to leave the screen.
if this.dude.grabber
this.cel = 3
this.y = this.y - 10*dt
if this.y < -(this.h + this.dude.h)
vDudes.Remove(this.dude)
return false
endif
' Grabber has yet not picked up its targeted dude.
else
this.cel = (this.cel + 4*dt)%4
this.y = this.y + 15*dt
' Grab dude?
if this.y + this.h >= this.dude.y
this.dude.grabber = this
endif
endif
return true
endfunc
return s
endfunc
' GetGrabableDude
' ---------------
' Return a dude that is free to grab or unset if none was found.
function GetGrabableDude()
if vDudes.size
list = []
for i = 0 to vDudes.size - 1
ok = true
if vGrabbers.size for j = 0 to vGrabbers.size - 1 if vGrabbers[j].dude = vDudes[i]
ok = false
break
endif
if ok list[sizeof(list)] = vDudes[i]
next
if sizeof(list) return list[rnd(sizeof(list))]
endif
return unset
endfunc
' Enemy
' -----
' Enemies appear at random positions and move around randomly.
function Enemy()
s = Sprite(vEnemyImage, 0)
' Position.
s.x = rnd(vLevelWidth)
s.y = -s.h
' Movement and wanted movement, for smoothness.
s.dx = 0
s.dy = 0
s.wdx = 0
s.wdy = 0
' Timer until next turn.
s.turnTimer = 0
' SetRandomDirection
' ------------------
s.SetRandomDirection = function()
this.turnTimer = 2 + rnd()*2
' Limit angle range based on vertical position.
if this.y < VIEW_H/4 a = 180 + rnd(180)
elseif this.y > 3*VIEW_H/4 a = rnd(180)
else a = rnd(360)
this.wdx = cos(rad(a))*50
this.wdy = sin(rad(a))*50
endfunc
' Update
' ------
s.Update = function(dt)
' Die?
if this.HitByAnySprite(vPlayerBullets, true)
play sound vExplosionSound
AddExplosion(this.x + this.w/2, this.y + this.h/2, 181, 50, 32, 40)
vScore = vScore + 25
return false
endif
i = 1.5*dt
this.dx = this.dx*(1 - i) + this.wdx*i
this.dy = this.dy*(1 - i) + this.wdy*i
this.x = (this.x + this.dx*dt)%vLevelWidth
this.y = this.y + this.dy*dt
if this.y < 0
this.wdy = |this.wdy|
elseif this.y + this.h > VIEW_H
this.y = VIEW_H - this.h
this.dy = 0
this.wdy = -|this.wdy|
endif
this.turnTimer = this.turnTimer - dt
if this.turnTimer <= 0 this.SetRandomDirection()
this.cel = (this.cel + 4*dt)%4
return true
endfunc
s.SetRandomDirection()
return s
endfunc
' AddEnemyBullet
' --------------
' Add enemy bullet.
function AddEnemyBullet(grabbersMayShoot)
' Create a list of visible enemies and grabbers.
list = []
if vEnemies.size for i = 0 to vEnemies.size - 1 if vEnemies[i].Visible()
list[sizeof(list)] = vEnemies[i]
endif
if grabbersMayShoot
if vGrabbers.size for i = 0 to vGrabbers.size - 1 if vGrabbers[i].Visible()
list[sizeof(list)] = vGrabbers[i]
endif
endif
' Pick one if list isn't empty.
if sizeof(list)
e = list[rnd(sizeof(list))]
px = vPlayer.x + vPlayer.w/2
py = vPlayer.y + vPlayer.h/2
ex = e.x + e.w/2
ey = e.y + e.h/2
dy = py - ey
' Shortest horizontal distance with wrapping concidered.
dx = px - ex
dxt = px + vLevelWidth - ex
if |dxt| < |dx| dx = dxt
dxt = px - vLevelWidth - ex
if |dxt| < |dx| dx = dxt
' 60 pixels per second.
k = 60/sqr(dx*dx + dy*dy)
vEnemyBullets.Add(Bullet(vEnemyBulletImage, ex, ey, dx*k, dy*k))
play sound vEnemyShootSound, 0.125
return true
else
return false
endif
endfunc
' Particle
' --------
' Quick hack in the last moment.
function Particle(x, y, r, g, b)
a = rnd()*2*PI
spd = 40 + rnd(40)
dist = rnd()*4
return [
x: x + cos(a)*dist, y: y + sin(a)*dist, r: r, g: g, b: b,
dx: cos(a)*spd, dy: sin(a)*spd,
duration: 1 + rnd()*2,
Update: function(dt)
this.duration = this.duration - dt
this.x = (this.x + this.dx*dt)%vLevelWidth
this.y = this.y + this.dy*dt
this.dy = min(this.dy + 60*dt, 80)
return this.duration > 0
endfunc,
Draw: function()
DrawPixel(this.x, this.y, this.r, this.g, this.b)
endfunc
]
endfunc
' AddExplosion
' ------------
function AddExplosion(x, y, r, g, b, particles)
for i = 1 to particles vParticles.Add(Particle(x, y, r, g, b))
endfunc
' Sprite
' ------
function Sprite(img, cel)
return [
' Data
' ----
img: img, cel: cel,
x: 0, y: 0,
w: width(img), h: height(img),
c: [255, 255, 255, 255],
' SetPosition
' -----------
SetPosition: function(x, y)
this.x = x
this.y = y
if this.x < 0 this.x = this.x + vLevelWidth
elseif this.x >= vLevelWidth this.x = this.x - vLevelWidth
endfunc,
' Center
' ------
Center: function(x, y)
this.SetPosition(x - width(this.img)/2, y - height(this.img)/2)
endfunc,
' SetColor
' --------
SetColor: function(r, g, b, a)
this.c[0] = r
this.c[1] = g
this.c[2] = b
this.c[3] = a
endfunc,
' Update
' ------
Update: function(dt)
endfunc,
' Draw
' ----
Draw: function()
set color this.c
DrawImage(this.img, this.x, this.y, this.cel)
endfunc,
' Overlaps
' --------
Overlaps: function(s)
return this.x + this.w > s.x and this.x < s.x + s.w and
this.y + this.h > s.y and this.y < s.y + s.h
endfunc,
' Visible
' -------
Visible: function()
return Visible(this.x, this.y, this.w, this.h)
endfunc,
' HitByAnySprite
' --------------
' Return > 0 if this sprite overlaps with any sprite in list. If 'delete' is true,
' the overlapping sprites are removed from the list.
HitByAnySprite: function(list, delete)
hits = 0
if list.size
i = 0
while i < list.size
if this.Overlaps(list[i])
hits = hits + 1
if delete list.Remove(list[i])
else i = i + 1
else
i = i + 1
endif
wend
endif
return hits
endfunc
]
endfunc
' UpdateSprites
' -------------
' Update all sprites in list.
function UpdateSprites(list, dt)
if list.size
i = 0
while i < list.size
if list[i].Update(dt) i = i + 1
else list.RemoveByIndex(i)
wend
endif
endfunc
' DrawSprites
' -----------
' Draw all sprites in list.
function DrawSprites(list)
if list.size for i = 0 to list.size - 1 list[i].Draw()
endfunc
' DrawMap
' -------
' Draw mini map.
function DrawMap()
scale = 12
mapw = vLevelWidth/scale
maph = VIEW_H/scale
mapx = (VIEW_W - mapw)/2
mapy = 0
set color 0, 0, 0, 128
draw rect mapx, mapy, mapw, maph, true
set clip rect mapx, mapy, mapw, maph
DrawMapList(vEnemies, mapx, mapy, mapw, maph, scale, 181, 50, 32)
DrawMapList(vDudes, mapx, mapy, mapw, maph, scale, 161, 27, 205)
DrawMapList(vGrabbers, mapx, mapy, mapw, maph, scale, 0, 144, 50)
set color 254, 254, 254
draw pixel mapx + mapw/2, mapy + (vPlayer.y + vPlayer.h/2)/scale
clear clip rect
set color 102, 102, 102
draw rect mapx - 1, mapy - 1, mapw + 2, maph + 2
' DrawMapList
' -----------
function DrawMapList(list, mapx, mapy, mapw, maph, scale, r, g, b)
if list.size
set color r, g, b
px = vPlayer.x + vPlayer.w/2
for i = 0 to list.size - 1
y = (list[i].y + list[i].h/2)/scale
x = ((list[i].x + list[i].w/2 - px)/scale + mapw/2)%mapw
draw pixel mapx + x, mapy + y
next
endif
endfunc
endfunc
' DrawPixel
' ---------
function DrawPixel(x, y, r, g, b)
set color r, g, b
draw pixel floor(x - vScroll), y
if x < VIEW_W draw pixel floor(x + vLevelWidth - vScroll), y
elseif x >= vLevelWidth - VIEW_W draw pixel floor(x - vLevelWidth - vScroll), y
endfunc
' DrawImage
' ---------
function DrawImage(img, x, y, cel)
draw image img, floor(x - vScroll), y, cel
if x < VIEW_W draw image img, floor(x + vLevelWidth - vScroll), y, cel
elseif x >= vLevelWidth - VIEW_W draw image img, floor(x - vLevelWidth - vScroll), y, cel
endfunc
' DrawLine
' --------
function DrawLine(x1, y1, x2, y2)
draw line floor(x1 - vScroll), y1, floor(x2 - vScroll), y2
if x1 < VIEW_W draw line floor(x1 + vLevelWidth - vScroll), y1, floor(x2 + vLevelWidth - vScroll), y2
elseif x2 >= vLevelWidth - VIEW_W draw line floor(x1 - vLevelWidth - vScroll), y1, floor(x2 - vLevelWidth - vScroll), y2
endfunc
' Visible
' -------
' Return true if rectangle is on screen.
function Visible(x, y, w, h)
if y + h < 0 or y > VIEW_H return false
' Check one area.
if vScroll >= 0 and vScroll < vLevelWidth - VIEW_W
return x + w > vScroll and x < vScroll + VIEW_W
' Overlap left from right.
else if vScroll < 0
return x + w > 0 and x < vScroll + VIEW_W or
x + w > vLevelWidth + vScroll and x < vLevelWidth
' Overlap right from left.
else if vScroll >= vLevelWidth - VIEW_W
return x + w > vScroll and x < vLevelWidth or
x + w > 0 and x < vScroll + VIEW_W - vLevelWidth
endif
endfunc
' CreateSineSfx
' -------------
function CreateSineSfx(duration, startFreq, endFreq, fadeOut, sampleRate)
data = []
a = 0
da = 2*PI*startFreq/sampleRate
dda = (2*PI*endFreq/sampleRate - 2*PI*startFreq/sampleRate)/(duration*sampleRate)
vol = 1
fadeOut = fadeOut*duration*sampleRate
fadeOutDelta = 1/(duration*sampleRate - fadeOut)
for i = 0 to duration*sampleRate - 1
data[i] = sin(a)*vol
a = a + da
da = da + dda
if i > fadeOut vol = vol - fadeOutDelta
next
return createsound(data, data, sampleRate)
endfunc
' CreateSquareSfx
' ---------------
function CreateSquareSfx(duration, startFreq, endFreq, fadeOut, sampleRate)
data = []
a = 0
da = 2*PI*startFreq/sampleRate
dda = (2*PI*endFreq/sampleRate - 2*PI*startFreq/sampleRate)/(duration*sampleRate)
vol = 1
fadeOut = fadeOut*duration*sampleRate
fadeOutDelta = 1/(duration*sampleRate - fadeOut)
for i = 0 to duration*sampleRate - 1
sa = sin(a)
if sa < 0 sa = -1
elseif sa > 0 sa = 1
data[i] = sa*vol
a = a + da
da = da + dda
if i > fadeOut vol = vol - fadeOutDelta
next
return createsound(data, data, sampleRate)
endfunc
' CreateNoiseSfx
' --------------
function CreateNoiseSfx(duration, pitch, fadeOut, sampleRate)
assert sampleRate >= 8000, "CreateBoomSfx: invalid sample rate"
assert pitch > 0, "CreateBoomSfx: invalid pitch"
freqs = [
[v: 0, p: sampleRate/500, d: 0, t: 0, w: pitch],
[v: 0, p: sampleRate/1000, d: 0, t: 0, w: pitch^2],
[v: 0, p: sampleRate/2000, d: 0, t: 0, w: pitch^3],
[v: 0, p: sampleRate/8000, d: 0, t: 0, w: pitch^4]]
s = sizeof(freqs)
data = []
vol = 1
fadeOut = fadeOut*duration*sampleRate
fadeOutDelta = 1/(duration*sampleRate - fadeOut)
for i = 0 to duration*sampleRate - 1
v = 0
w = 0
for j = 0 to s - 1; f = freqs[j]
f.t = f.t - 1
if f.t <= 0
f.t = f.p
f.d = ((rnd()*2 - 1) - f.v)/f.p
endif
f.v = f.v + f.d
v = v + f.v*f.w
w = w + f.w
next
data[i] = vol*v/w
if i > fadeOut vol = vol - fadeOutDelta
next
return createsound(data, data, sampleRate)
endfunc
' ImageFromMonoData
' -----------------
' Create one color image from array.
function ImageFromMonoData(data, r, g, b, gridCols, gridRows)
h = sizeof(data)
w = sizeof(data[0])
img = createimage(w, h)
set image img
for y = 0 to h - 1 for x = 0 to w - 1
if data[y][x] set color r, g, b, 255
else set color 0, 0, 0, 0
set pixel x, y
next
set image primary
set image grid img, gridCols, gridRows
return img
endfunc