' Missile Lock ' ' Copyright (C) 2006 Ian Cowburn (ianc@noddybox.co.uk) ' ' This program is free software; you can redistribute it and/or modify ' it under the terms of the GNU General Public License as published by ' the Free Software Foundation; either version 2 of the License, or ' (at your option) any later version. ' ' This program is distributed in the hope that it will be useful, ' but WITHOUT ANY WARRANTY; without even the implied warranty of ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ' GNU General Public License for more details. ' ' You should have received a copy of the GNU General Public License ' along with this program; if not, write to the Free Software ' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ' ' ------------------------------------------------------------------------- ' ' $Id$ ' Strict Import noddybox.algorithm Import "global.bmx" Import "particle.bmx" Import "sound.bmx" Const SHIP_TURN:Double=2 Const SHIP_MAX_SPEED:Double=2.5 Const SHIP_SPEED:Double=0.05 Const SHIP_MIN_SPEED:Double=1 Const MIN_TURN:Double=1 Const MAX_TURN:Double=5.0 Const MIN_SPEED:Double=1 Const MAX_SPEED:Double=2.2 Type GameState Global score:Int=0 Global x:Double=0 Global y:Double=0 Global ang:Double=0 Global speed:Double=SHIP_MIN_SPEED Global lives:Int=3 Global level:Int=1 Global max_missile:Int=1 Global missile_turn:Double=0.5 Global missile_speed:Double=1 Global shield:Int=5*HERTZ Global game_over:Int=False Global hit:Int=False Global pause:Int=False Global bonus_timer:Int Global gonads:Int Function Reset() score=0 x=400 y=300 ang=0 speed=SHIP_MIN_SPEED lives=3 SetLevel(1) ShieldShip() game_over=False pause=False End Function Function ShieldShip() shield=3*HERTZ End Function Function ShieldDown() lives:-1 hit=True gonads=False ShieldShip() If lives<0 game_over=True SFX.ShipExplode() Else SFX.ShipHit() EndIf End Function Function AddScore(s:Int) score:+s If score>GameConfig.hiscore GameConfig.hiscore=score EndIf End Function Function LevelUp() SetLevel(level+1) End Function Function SetLevel(l:Int) level=l hit=False gonads=True max_missile=Min(50,l*2) missile_turn=Min(MAX_TURN,MIN_TURN+Double(level)/4) missile_speed=Min(MAX_SPEED,MIN_SPEED+Double(level)/4) bonus_timer=l*500 End Function Function Control() If KeyDown(GameConfig.kleft) ang:-SHIP_TURN End If If KeyDown(GameConfig.kright) ang:+SHIP_TURN End If If KeyDown(GameConfig.kthrust) And speed<=SHIP_MAX_SPEED speed=Min(SHIP_MAX_SPEED,speed+SHIP_SPEED) gonads=False Else speed=Max(SHIP_MIN_SPEED,speed-SHIP_SPEED/2) End If If KeyHit(GameConfig.kpause) pause=True End If If KeyHit(KEY_ESCAPE) game_over=True EndIf End Function Function Move() x=(x+Sin(ang)*speed) Mod 800 y=(y-Cos(ang)*speed) Mod 600 If x<0 Then x:+800 If y<0 Then y:+600 Trail.Add() If shield Then shield:-1 If bonus_timer Then bonus_timer:-1 End Function Function Display() Local s:String="" For Local f:Int=1 To lives s:+"~~" Next GFX.font.Draw("SCORE",0,0) GFX.font.Draw(Number.Format(score),0,16,255,255,0) GFX.font.Centre("SHIELDS",0) GFX.font.Centre(s,16) GFX.font.Draw("LEVEL " + level,0,584) GFX.font.Draw("BONUS " + bonus_timer,350,584) GFX.font.Draw("MISSILES " + MissileSet.Count(),623,584) GFX.font.DrawRight("HISCORE",799,0) GFX.font.DrawRight(Number.Format(GameConfig.hiscore),799,16,255,255,0) End Function End Type Type BackdropStar Field x:Double Field y:Double Field r:Int Field g:Int Field b:Int Field twinkle:Int Field frame:Int Method New() x=Rand(0,800) y=Rand(0,598) r=Rand(128,255) g=Rand(128,255) b=Rand(128,255) End Method End Type Type Backdrop Const NUM:Int=100 Global s:BackdropStar[] Global frame:Int Function Init() s=New BackdropStar[NUM] For Local f:Int=0 Until NUM s[f]=New BackdropStar s[f].frame=f Mod 6 s[f].twinkle=Rand(100)>70 Next frame=0 End Function Function Draw() frame=(frame+1) Mod 6 For Local f:Int=0 Until NUM SetColor(s[f].r,s[f].g,s[f].b) If s[f].twinkle DrawImage(GFX.twinkle,s[f].x,s[f].y,s[f].frame) If Not frame s[f].frame=(s[f].frame+1) Mod 6 s[f].x=(s[f].x+2) Mod 800 EndIf Else DrawImage(GFX.star,s[f].x,s[f].y) If Not frame s[f].x=(s[f].x+1) Mod 800 EndIf EndIf Next End Function End Type Type TrailPart Field x:Double Field y:Double Field ang:Double Field al:Double Field sc:Double Method New() x=GameState.x y=GameState.y ang=GameState.ang al=1 sc=1 End Method End Type Type Trail Global plist:TList Function Init() plist=CreateList() End Function Function Clear() plist.Clear() End Function Function Add() plist.AddLast(New TrailPart) End Function Function Draw() SetColor(255,255,255) Local l:TEasyLink=TEasyLink.Create(plist) While l.Value() Local t:TrailPart=TrailPart(l.Value()) If t.al>0.1 SetRotation(t.ang) SetScale(t.sc,t.sc) SetAlpha(t.al) DrawImage(GFX.flame,t.x,t.y) t.al:-0.05 't.sc:+0.1 l.MoveNext() Else l.Remove() EndIf Wend SetRotation(0) SetAlpha(1) SetScale(1,1) End Function End Type Type Missile Field x:Double Field y:Double Field ang:Double Field onscreen:Int Field turn:Double Field speed:Double Field accurate:Int Field smoke:Int Method New() Select Rand(1,4) Case 1 x=Rand(20,780) y=Rand(-100,0) ang=180 Case 2 x=Rand(20,780) y=Rand(600,700) ang=0 Case 3 y=Rand(20,580) x=Rand(-100,0) ang=90 Case 4 y=Rand(20,580) x=Rand(800,900) ang=270 End Select speed=Rnd(Max(MIN_SPEED,GameState.missile_speed-0.1),GameState.missile_speed) turn=Rnd(Max(MIN_TURN,GameState.missile_turn-0.1),GameState.missile_turn) onscreen=False If Rand(100)>50 accurate=True EndIf smoke=0 End Method Method Update(tx:Double,ty:Double) If onscreen Local a:Double If accurate a=ATan2(tx-x,y-ty) Else a=ATan2(GameState.x-x,y-GameState.y) EndIf If a<0 Then a:+360 a:-ang If a<0 If a>-180 ang:-turn Else ang:+turn EndIf ElseIf a>0 If a>180 ang:-turn Else ang:+turn EndIf EndIf ang=ang Mod 360 If ang<0 Then ang:+360 EndIf SetRotation(ang) x:+Sin(ang)*speed y:-Cos(ang)*speed DrawImage(GFX.missile,x,y) CollideImage(GFX.missile,x,y,0,0,MISSILE_LAYER,Self) smoke:+1 If smoke=10 Particles.AddScaledImage(GFX.exhaust,x,y,0.1) smoke=0 EndIf If onscreen x=x Mod 800 y=y Mod 600 If x<0 Then x:+800 If y<0 Then y:+600 Else onscreen=(x>=0 And x<800 And y>=0 And y<600) EndIf End Method End Type Type MissileSet Global plist:TList Global create:Int Global wait:Int Function Init() plist=CreateList() create=0 End Function Function StartLevel() plist.Clear() create=GameState.max_missile wait=HERTZ End Function Function Count:Int() Return plist.Count() End Function Function Update() If wait=0 If create plist.AddLast(New Missile) create:-1 SFX.MissileLaunch() EndIf wait=HERTZ Else wait:-1 EndIf SetColor(255,255,255) Local x:Double=(GameState.x+Sin(GameState.ang)*GameState.speed) Mod 800 Local y:Double=(GameState.y-Cos(GameState.ang)*GameState.speed) Mod 600 For Local m:Missile=EachIn plist m.Update(x,y) Next SetRotation(0) End Function Function Nuke:Int() Local c:Int=plist.Count() For Local m:Missile=EachIn plist Particles.AddExplosion(m.x,m.y) Next plist.Clear() Return c End Function Function RemoveMissile(m:Missile) plist.Remove(m) End Function Function AllDestroyed:Int() Return (create=0 And plist.Count()=0) End Function End Type Type Asteroid Field x:Double Field y:Double Field dx:Double Field dy:Double Field ang:Double Field angi:Double Method New() x=Rand(20,780) y=Rand(20,580) Repeat dx=Rnd(-1,1) dy=Rnd(-1,1) Until dx<>0 And dy<>0 ang=Rand(360) If Abs(dx)>Abs(dy) angi=dx/2 Else angi=dy/2 EndIf End Method Method Update() ang:+angi x=(x+dx) Mod 800 y=(y+dy) Mod 600 If x<0 Then x:+800 If y<0 Then y:+600 SetRotation(ang) DrawImage(GFX.asteroid,x,y) Local o:Object[]=CollideImage(GFX.asteroid,x,y,0,MISSILE_LAYER,ASTEROID_LAYER,Self) If o Particles.AddBigExplosion(x,y) GameState.AddScore(10*GameState.level) MissileSet.RemoveMissile(Missile(o[0])) SFX.MissileExplode() Return False Else Return True EndIf End Method End Type Type AsteroidSet Global plist:TList Function Init() plist=CreateList() End Function Function StartLevel() plist.Clear() For Local f:Int=0 Until Max(5,GameState.max_missile) plist.AddLast(New Asteroid) Next End Function Function Update() Local l:TEasyLink=TEasyLink.Create(plist) While l.Value() Local a:Asteroid=Asteroid(l.Value()) If (a.Update()) l.MoveNext() Else l.Remove() EndIf Wend SetRotation(0) End Function Function Nuke() Local c:Int=plist.Count() For Local a:Asteroid=EachIn plist Particles.AddExplosion(a.x,a.y) Next plist.Clear() Return c End Function End Type