digesteroids.bmx 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506
  1. ' *******************************************************************
  2. ' Digesteroids - V1.0
  3. ' --------------------------------------------------------
  4. ' An evil biscuit corporation are using their army of
  5. ' sweet snack food to take over the world. Use your ship
  6. ' to take down the evil invading corporation before
  7. ' they infest the world with dunkables sweetmeal biscuits.
  8. ' REQUIRES 1024x768x32! @75Hz
  9. ' --------------------------------------------------------
  10. ' Author: Rob Hutchinson 2004
  11. ' Email: [email protected]
  12. ' Written entirely in Protean IDE:
  13. ' WWW: http://www.proteanide.co.uk/
  14. ' --------------------------------------------------------
  15. ' Yep, there's stuff in here, I didnt get time to implement
  16. ' Pickups, Weapons and Vortexes.
  17. ' *******************************************************************
  18. Strict
  19. Framework SDL.SDLRenderMax2D
  20. Import brl.pngloader
  21. Import brl.wavloader
  22. Import brl.oggloader
  23. Import brl.Random
  24. Import sdl.sdlfreeaudio
  25. Import Brl.StandardIO 'for debuglog()
  26. ' Import various utilities.
  27. Import "simplephysics.bmx"
  28. Import "dynamicgame.bmx"
  29. Import "MathUtil.bmx"
  30. SetAudioDriver("FreeAudio SDL")
  31. ' Screen settings.
  32. Const WIDTH = 1024 ' Width of the screen.
  33. Const HEIGHT = 768 ' Height of the screen.
  34. Const DEPTH = 0 ' Depth of the screen.
  35. Const REFRESHRATE = 75 ' How often to update the screen (Per second).
  36. ' Game Settings
  37. Const DYNAMICTIMING = True ' If true then the game will try to keep up with
  38. Const DESIREDFPS = 75 ' The Desired FPS of the game, can be independant of Refresh Rate.
  39. Function ScreenPan(OffsetX:Float, OffsetY:Float)
  40. ' Local Width,Height,Depth,Hz
  41. ' Width=GraphicsWidth()
  42. ' Height=GraphicsHeight()
  43. ' Depth=GraphicsDepth()
  44. ' Hz=GraphicsHertz()
  45. ' glMatrixMode GL_PROJECTION
  46. ' glLoadIdentity
  47. ' glOrtho 0,Width,Height,0,-1,1
  48. 'glTranslatef OffsetX, OffsetY,0
  49. 'glMatrixMode GL_MODELVIEW
  50. EndFunction
  51. Function QFlushKeys()
  52. ' Quick implementation of flushkeys as BMX doesnt have one at time of writing, sure it
  53. ' will be added though.
  54. For Local Key:Int = 0 To 255
  55. KeyHit(Key)
  56. KeyDown(Key)
  57. Next
  58. End Function
  59. '#Region Scene: MainMenu
  60. ' The main menu scene.
  61. Type MainMenuScene Extends T2DDynamicGameScene
  62. '#Region DrawTextCentered
  63. Function DrawTextCentered:Int(Text:String,Y:Int)
  64. Local Out:Int = (WIDTH / 2) - (TextWidth(Text) / 2)
  65. DrawText(Text, Out, Y)
  66. Return Out
  67. End Function
  68. '#End Region
  69. '#Region Declarations
  70. Const STARS_PER_SECOND = 6
  71. Field BackColor:Float
  72. Field Direction:Float = 0.01
  73. Field ExitGame:Int = False
  74. Field Music:TSound
  75. Field MusicChannel:TChannel
  76. Field Title:TImage
  77. Field LowerTitle:TImage
  78. Field Options:TImage
  79. Field Craft:TImage
  80. Field EnteredText:String = ""
  81. Field LastHighScore:Int
  82. Field ShowCredits:Int = False
  83. Field Viewing:Int = VIEW_MENU
  84. Field HoverOver:Int = VIEW_START
  85. Const VIEW_MENU = 0
  86. Const VIEW_START = 0
  87. Const VIEW_INSTRUCTIONS = 1
  88. Const VIEW_HIGHSCORES = 2
  89. Const VIEW_QUIT = 3
  90. Const VIEW_ENTERHIGH = 4
  91. Field Stars:TPhysicsProviderCollection = New TPhysicsProviderCollection
  92. Field World:TWorldPhysicsProvider = TWorldPhysicsProvider.Create(0.0, 0.0, 0.0, True)
  93. '#End Region
  94. '#Region Method: FinishScoreEntry
  95. Method FinishScoreEntry()
  96. Self.ViewMenu(VIEW_HIGHSCORES)
  97. Scores.Add(Self.LastHighScore, Self.EnteredText)
  98. ' Add this to the high scores..
  99. Self.LastHighScore = 0
  100. Self.EnteredText = ""
  101. End Method
  102. '#End Region
  103. '#Region Method: ViewMenu
  104. Method ViewMenu(Menu:Int)
  105. Self.Viewing = Menu
  106. Self.HoverOver = Menu
  107. QFlushKeys()
  108. End Method
  109. '#End Region
  110. '#Region Method: Update
  111. Method Update()
  112. Self.Stars.ApplyPhysics()
  113. ' Add new stars..
  114. For Local CountStars:Int = 0 To STARS_PER_SECOND
  115. Local Star:TMenuStar = TMenuStar.Create(World, Self, 10.0)
  116. Self.Stars.AddLast(Star)
  117. Next
  118. ' Do enter key checking..
  119. Select Self.Viewing
  120. Case VIEW_MENU
  121. If KeyHit(KEY_ENTER) Then
  122. Select Self.HoverOver
  123. Case VIEW_START
  124. Self.TerminateMainLoop = True
  125. Case VIEW_INSTRUCTIONS
  126. Self.ViewMenu(VIEW_INSTRUCTIONS)
  127. Case VIEW_HIGHSCORES
  128. Self.ViewMenu(VIEW_HIGHSCORES)
  129. Case VIEW_QUIT
  130. Self.TerminateMainLoop = True
  131. Self.ExitGame = True
  132. End Select
  133. EndIf
  134. If KeyHit(KEY_UP)
  135. Self.HoverOver :- 1
  136. If Self.HoverOver < VIEW_START Then Self.HoverOver = VIEW_START
  137. EndIf
  138. If KeyHit(KEY_DOWN)
  139. Self.HoverOver :+ 1
  140. If Self.HoverOver > VIEW_QUIT Then Self.HoverOver = VIEW_QUIT
  141. EndIf
  142. If KeyHit(KEY_ESCAPE) Then
  143. Self.TerminateMainLoop = True
  144. Self.ExitGame = True
  145. Else
  146. If KeyHit(KEY_C)
  147. Self.TerminateMainLoop = True
  148. Self.ShowCredits = True
  149. EndIf
  150. EndIf
  151. Case VIEW_INSTRUCTIONS, VIEW_HIGHSCORES
  152. If KeyHit(KEY_ENTER) Or KeyHit(KEY_ESCAPE)
  153. Self.Viewing = VIEW_MENU
  154. EndIf
  155. Case VIEW_ENTERHIGH
  156. ' Check for alpha keys.
  157. For Local Count:Int = KEY_A To KEY_Z
  158. If KeyHit(Count) Then Self.EnteredText = Self.EnteredText + Chr(Count)
  159. Next
  160. ' Check for number key hits
  161. For Local Count:Int = KEY_0 To KEY_9
  162. If KeyHit(Count) Then Self.EnteredText = Self.EnteredText + Chr(Count)
  163. Next
  164. ' Special case, check for space bar
  165. If KeyHit(KEY_SPACE)
  166. Self.EnteredText = Self.EnteredText + " "
  167. EndIf
  168. ' Remove a character when user hits backspace.
  169. If KeyHit(KEY_BACKSPACE)
  170. If Len(Self.EnteredText) > 0
  171. Self.EnteredText = Self.EnteredText[..Len(Self.EnteredText)]
  172. EndIf
  173. EndIf
  174. ' If 5 characters are entered, end high score entry.
  175. If Len(Self.EnteredText) = 5 Then Self.FinishScoreEntry()
  176. If KeyHit(KEY_ENTER)
  177. Self.FinishScoreEntry()
  178. EndIf
  179. End Select
  180. End Method
  181. '#End Region
  182. '#Region Method: Render
  183. Method Render()
  184. Cls
  185. ScreenPan(0,0)
  186. SetTransform
  187. SetAlpha 1.0
  188. SetBlend(SOLIDBLEND)
  189. Self.Stars.Draw()
  190. SetColor 255,255,255
  191. SetBlend(ALPHABLEND)
  192. DrawImage(Self.Title,0,40)
  193. DrawImage(Self.LowerTitle,0,660)
  194. Local StartPosX:Int = 150
  195. Local StartPosY:Int = 245
  196. Select Self.Viewing
  197. Case VIEW_MENU
  198. ' Showing main menu
  199. DrawImage(Self.Options,450,330)
  200. SetRotation -90
  201. Select Self.HoverOver
  202. Case VIEW_START
  203. DrawImage(Self.Craft,416,352)
  204. Case VIEW_INSTRUCTIONS
  205. DrawImage(Self.Craft,416,352 + 37)
  206. Case VIEW_HIGHSCORES
  207. DrawImage(Self.Craft,416,352 + 74)
  208. Case VIEW_QUIT
  209. DrawImage(Self.Craft,416,352 + 145)
  210. End Select
  211. Case VIEW_HIGHSCORES
  212. Scores.Render(385,310)
  213. Case VIEW_ENTERHIGH
  214. DrawTextCentered("CONGRATULATIONS!",StartPosY)
  215. DrawTextCentered("You have a new high score!",StartPosY+50)
  216. DrawTextCentered("Please enter your name (5 characters)",StartPosY+65)
  217. Local TextX:Int = DrawTextCentered(Self.EnteredText,StartPosY+250)
  218. DrawRect(TextX + TextWidth(Self.EnteredText), StartPosY+250, 12, 15)
  219. Case VIEW_INSTRUCTIONS
  220. DrawText("Welcome to Digesteroids!",StartPosX,StartPosY)
  221. DrawText("------------------------",StartPosX,StartPosY+20)
  222. DrawText("An evil biscuit corporation are using their army of sweet snack food to take over the world.",StartPosX,StartPosY+50)
  223. DrawText("Fortunately, the ingredients the evil corporation used are vulnerable to big sweaty laser",StartPosX,StartPosY+70)
  224. DrawText("blasts. Use your ship's cannon to take out the corporation before they infest the world with",StartPosX,StartPosY+90)
  225. DrawText("sweetmeal dunkables biscuits. But be careful, space is small (just big enough to fit inside",StartPosX,StartPosY+110)
  226. DrawText("your screen), it is also cyclical, so beware of incoming oval shaped objects.",StartPosX,StartPosY+130)
  227. DrawText("Keys:",StartPosX,StartPosY+180)
  228. DrawText(" - UP CURSOR ARROW = Thrust",StartPosX,StartPosY+200)
  229. DrawText(" - DOWN CURSOR ARROW = Teleport",StartPosX,StartPosY+220)
  230. DrawText(" - LEFT CURSOR ARROW = Rotate Ship Left",StartPosX,StartPosY+240)
  231. DrawText(" - RIGHT CURSOR ARROW = Rotate Ship Right",StartPosX,StartPosY+260)
  232. DrawText(" - ESCAPE = QUIT",StartPosX,StartPosY+280)
  233. DrawText("Yes, that's right, it's a glorified asteroids game!",StartPosX,StartPosY+320)
  234. SetColor 255,0,0
  235. DrawText("Note: Your score is based on the speed at which your ship is travelling when it fired.",StartPosX,StartPosY+340)
  236. End Select
  237. End Method
  238. '#End Region
  239. '#Region Method: Start
  240. Method Start()
  241. Self.MusicChannel = AllocChannel()
  242. If Self.Music = Null Then Self.Music = LoadSound("sounds\menu.ogg",True)
  243. PlaySound(Self.Music,Self.MusicChannel)
  244. SetChannelVolume MusicChannel,1.0
  245. ' Load GFX..
  246. If Self.Title = Null Then Self.Title = LoadImage("graphics\title.png")
  247. If Self.LowerTitle = Null Then Self.LowerTitle = LoadImage("graphics\lower.png")
  248. If Self.Options = Null Then Self.Options = LoadImage("graphics\options.png")
  249. If Self.Craft = Null Then Self.Craft = LoadImage("graphics\ship.png")
  250. ' Add a screen full of stars.
  251. For Local CountStars:Int = 0 To 1000
  252. Local Star:TMenuStar = TMenuStar.Create(World, Self, 10.0)
  253. Star.X = Rnd(WIDTH)
  254. Self.Stars.AddLast(Star)
  255. Next
  256. End Method
  257. '#End Region
  258. '#Region Method: Finish
  259. Method Finish()
  260. Self.Stars.Clear()
  261. Self.MusicChannel.Stop()
  262. Self.MusicChannel = Null
  263. End Method
  264. '#End Region
  265. End Type
  266. '#End Region
  267. '#Region Scene: MainGame
  268. ' Main Game scene
  269. Type MainGameScene Extends T2DDynamicGameScene
  270. '#Region Declarations
  271. Const STARS_PER_SECOND = 3
  272. Const FINISH_AT_LEVEL = 6
  273. Field Level:Int = 1
  274. Field Completed:Int = False
  275. Field DigestiveImage:TImage
  276. Field ShipImage:TImage
  277. Field StarsImage:TImage
  278. Field LargeImpactSound:TSound
  279. Field World:TWorldPhysicsProvider = TWorldPhysicsProvider.Create(0.0, 0.0, 0.0, True)
  280. Field Digestives:TPhysicsProviderCollection = New TPhysicsProviderCollection
  281. Field Bullets:TPhysicsProviderCollection = New TPhysicsProviderCollection
  282. Field Stars:TPhysicsProviderCollection = New TPhysicsProviderCollection
  283. Field Chunks:TFountain
  284. Const CHUNK_COUNT = 2
  285. Field ChunkImages:TImage[CHUNK_COUNT + 1]
  286. Field Player:TPlayer
  287. Field Shake:Float
  288. Field Pickups:TList = New TList
  289. '#End Region
  290. '#Region Method: Update
  291. Method Update()
  292. If KeyHit(KEY_ESCAPE) Then Self.TerminateMainLoop = True
  293. CheckEndOfLevel() ' Check if we need to move to the next level (IE, all digestives gone)
  294. ' Add new stars..
  295. For Local CountStars:Int = 0 To STARS_PER_SECOND
  296. Local Speed:Float = Self.Player.Speed()
  297. If Speed < 1.0 Then Speed = 1.0
  298. Local Star:TStar = TStar.Create(World, Self, Speed)
  299. Self.Stars.AddLast(Star)
  300. Next
  301. ' Update screenshake...
  302. Self.Shake :/ 1.02
  303. If Self.Shake < 0.0 Then Self.Shake = 0.0
  304. If Self.Shake > 6.0 Then Self.Shake = 6.0
  305. ' Update all the stars.
  306. Self.Stars.ApplyPhysics()
  307. ' Update the chunks
  308. Self.Chunks.UpdateWithFriction(TPhysicsProvider.AxisX | TPhysicsProvider.AxisY)
  309. ' Update all the digestives.
  310. Self.Digestives.ApplyPhysics()
  311. ' Update all the bullets.
  312. Self.Bullets.ApplyPhysics()
  313. ' Update the player.
  314. Self.Player.Update()
  315. Self.Player.ApplyFriction(TPhysicsProvider.AxisX | TPhysicsProvider.AxisY)
  316. Self.Player.ApplyPhysics()
  317. End Method
  318. '#End Region
  319. '#Region Method: Render
  320. Method Render()
  321. SetClsColor 0,0,0
  322. Cls
  323. If Self.Shake > 0.0 Then
  324. ScreenPan(Float(Rnd(Shake)),Float(Rnd(Shake)))
  325. EndIf
  326. ' Draw the spazzy psychodelic effect if it is enabled.
  327. Self.DrawPsychodelic()
  328. SetAlpha 1
  329. SetTransform
  330. ' Draw all the stars.
  331. Self.Stars.Draw()
  332. Self.Chunks.Draw()
  333. ' Draw the digestives.
  334. SetColor 255,255,255
  335. SetBlend(ALPHABLEND)
  336. Digestives.Draw()
  337. ' Draw the bullets
  338. SetBlend(LIGHTBLEND)
  339. Bullets.Draw()
  340. ' Draw the player.
  341. SetBlend(ALPHABLEND)
  342. Player.Draw()
  343. ' Draw the hud
  344. DrawHUD()
  345. End Method
  346. '#End Region
  347. '#Region Method: Start
  348. Method Start()
  349. AutoMidHandle True
  350. Self.Shake = 0
  351. Self.Chunks = TFountain.Create(Self.World,TRectangle.Create(0,0,WIDTH,HEIGHT))
  352. ' Load in the required Images...
  353. If Self.DigestiveImage = Null Then Self.DigestiveImage = LoadImage("graphics\digestive.png")
  354. If Self.ShipImage = Null Then Self.ShipImage = LoadImage("graphics\ship.png")
  355. If Self.StarsImage = Null Then Self.StarsImage = LoadImage("graphics\stars.png")
  356. For Local LoadCount:Int = 0 To CHUNK_COUNT
  357. Self.ChunkImages[LoadCount] = LoadImage("graphics\piece" + String(LoadCount + 1) + ".png")
  358. Next
  359. ' Set up the player
  360. Self.Player = TPlayer.Create(Self.World, Self.ShipImage,Self)
  361. Self.Player.LoadAssets()
  362. ' Load up the sounds.
  363. If Self.LargeImpactSound = Null Then Self.LargeImpactSound = LoadSound("sounds\impactlarge.wav")
  364. ' Reset the game..
  365. Self.ResetGame()
  366. ' Create all the weapons..
  367. Self.Pickups.Clear()
  368. Local Bullet1Image:TImage = LoadImage("graphics\bullet1.png")
  369. Local Fire1Sound:TSound = LoadSound("sounds\fire.wav")
  370. ' Local Blaster:TWeapon = TWeapon.Create(Speed,Graphic,Size,Radius,Veer,FireRate,MainGame,Sound)
  371. Local Blaster:TWeapon = TWeapon.Create(10, Bullet1Image, 0.65, 4.0, 0,30, Self, Fire1Sound, Bullet1Image, "Blaster")
  372. Local FastBlaster:TWeapon = TWeapon.Create(9, Bullet1Image, 0.3, 4.0, 0.0, 15, Self, Fire1Sound, Bullet1Image, "Blaster Mk2")
  373. Local SprayBlaster:TWeapon = TWeapon.Create(8, Bullet1Image, 0.35, 4.0, 5.0, 1, Self, Fire1Sound, Bullet1Image, "Blaster Mk3")
  374. Local BlasterPickup:TPickup = TPickup.Create(TPickup.MODIFIER_WEAPON,0,0,True,Blaster,Bullet1Image,0.01,Bullet1Image)
  375. Local FastBlasterPickup:TPickup = TPickup.Create(TPickup.MODIFIER_WEAPON,0,0,True,FastBlaster,Bullet1Image,0.01,Bullet1Image)
  376. Local SprayBlasterPickup:TPickup = TPickup.Create(TPickup.MODIFIER_WEAPON,0,0,True,SprayBlaster,Bullet1Image,0.008,Bullet1Image)
  377. Local Score1000Pickup:TPickup = TPickup.Create(TPickup.MODIFIER_SCORE,1000,0,True,Null,Null,0.02,Bullet1Image)
  378. Local Score500Pickup:TPickup = TPickup.Create(TPickup.MODIFIER_SCORE,500,0,True,Null,Null,0.03,Bullet1Image)
  379. Local RotationPickup:TPickup = TPickup.Create(TPickup.MODIFIER_ROTATION,1.1,0,True,Null,Null,0.01,Bullet1Image)
  380. Local PsychodelicPickup:TPickup = TPickup.Create(TPickup.MODIFIER_PSYCHODELIC,0,1000,False,Null,Null,0.04,Bullet1Image)
  381. Local LivesPickup:TPickup = TPickup.Create(TPickup.MODIFIER_LIVES,1,0,True,Null,Null,0.001,Bullet1Image)
  382. Self.Player.Weapon = Blaster
  383. End Method
  384. '#End Region
  385. '#Region Method: Finish
  386. Method Finish()
  387. Self.Player.ThrusterChannel.Stop
  388. Self.Player.ThrusterChannel = Null
  389. End Method
  390. '#End Region
  391. '#Region Method: CheckEndOfLevel
  392. Method CheckEndOfLevel()
  393. If Digestives.IsEmpty() = True
  394. ' No more digestives..
  395. Level :+ 1
  396. If Level = FINISH_AT_LEVEL Then
  397. ' This was the last level.
  398. Self.Completed = True
  399. Self.TerminateMainLoop = True
  400. Else
  401. ' Move to the next level
  402. GotoLevel(Level)
  403. EndIf
  404. EndIf
  405. End Method
  406. '#End Region
  407. '#Region Method: DrawHUD
  408. Method DrawHUD()
  409. SetColor 255,255,255
  410. SetAlpha 1.0
  411. SetRotation 0
  412. DrawText "Level: " + Level,0,0
  413. Local Lives:String = "Lives: " + Player.Lives
  414. Local Score:String = "Score: " + Player.Score
  415. DrawText Score,(WIDTH / 2) - (TextWidth(Score) / 2) ,0
  416. DrawText Lives,(WIDTH - TextWidth(Lives)) ,0
  417. Local fr$="000"+Int(Self.Player.Speed()*1000) Mod 1000
  418. Local sp$=Int(Self.Player.Speed()*1000)/1000+"."+fr[fr.length-3..]
  419. DrawText "Speed: " + sp,0,15
  420. DrawText "Teleports: " + Self.Player.Teleports,0,30
  421. End Method
  422. '#End Region
  423. '#Region Method: ResetGame
  424. Method ResetGame()
  425. Digestives.Clear()
  426. Bullets.Clear()
  427. Stars.Clear()
  428. Chunks.Particles.Clear()
  429. Player.Lives = 5
  430. Player.Reset()
  431. GotoLevel(1)
  432. End Method
  433. '#End Region
  434. '#Region Method: GotoLevel
  435. Method GotoLevel(ToLevel:Int)
  436. Level = ToLevel
  437. For Local Count:Int = 1 To ToLevel
  438. Local Digestive:TDigestive = TDigestive.Create(World, DigestiveImage,Self)
  439. Digestive.X = Rnd(0,WIDTH)
  440. Digestive.Y = Rnd(0,HEIGHT)
  441. Digestive.Rotation = Rnd(360)
  442. Digestive.Weight = 5
  443. Digestive.SetVelocityFromAngle(Rnd(360.0),Rnd(0.5,1.5))
  444. Digestive.HitSound = Self.LargeImpactSound
  445. Digestives.AddLast(Digestive)
  446. Next
  447. End Method
  448. '#End Region
  449. '#Region Method: DrawPsychodelic
  450. Field PsychoAngle:Float = 0
  451. Field Psycho:Int = False
  452. Method DrawPsychodelic()
  453. If Psycho = True
  454. SetColor Int(Rnd(0.0,255)),Int(Rnd(0.0,255)),Int(Rnd(0.0,255))
  455. PsychoAngle :+ 8
  456. SetBlend(SOLIDBLEND)
  457. TileImage(StarsImage,Float(Sin(PsychoAngle) * 200),Float(Cos(PsychoAngle) * 200))
  458. Else
  459. SetColor 255,255,255
  460. EndIf
  461. End Method
  462. '#End Region
  463. '#Region Method: FindSafeLocation
  464. Method FindSafeLocation:TPointD(Radius:Float)
  465. While True
  466. Local X:Int = Rnd(0,WIDTH)
  467. Local Y:Int = Rnd(0,HEIGHT)
  468. If Self.IsLocationSafe(X,Y,Radius)
  469. Local Out:TPointD = New TPointD
  470. Out.X = X
  471. Out.Y = Y
  472. Return Out
  473. EndIf
  474. Wend
  475. End Method
  476. '#End Region
  477. '#Region Method: IsLocationSafe
  478. Method IsLocationSafe:Int(X:Int,Y:Int,Radius:Float)
  479. Local TryCircle:TCircle = New TCircle
  480. TryCircle.X = X
  481. TryCircle.Y = Y
  482. TryCircle.Radius = Radius
  483. For Local Item:TDigestive = EachIn Self.Digestives
  484. If Item.Circle.CollidesWith(TryCircle)
  485. Return False
  486. EndIf
  487. Next
  488. Return True
  489. End Method
  490. '#End Region
  491. End Type
  492. '#End Region
  493. '#Region Scene: Ending
  494. ' The ending scene.
  495. Type EndingScene Extends T2DDynamicGameScene
  496. '#Region Declarations
  497. Const STARS_PER_SECOND = 0
  498. Const MAGNET_COUNT = 5
  499. Field ScrollPoint:Float
  500. Field Music:TSound
  501. Field MusicChannel:TChannel
  502. Field Title:TImage
  503. Field Time:Int = 1000000
  504. Field TEXTS_COUNT = 23
  505. Field Texts:String[TEXTS_COUNT]
  506. Field Stars:TPhysicsProviderCollection = New TPhysicsProviderCollection
  507. Field World:TWorldPhysicsProvider = TWorldPhysicsProvider.Create(0.2, 0.0, 0.0, True)
  508. '#End Region
  509. '#Region Method: Update
  510. Method Update()
  511. Self.Stars.ApplyPhysics()
  512. If KeyHit(KEY_ESCAPE) Then
  513. Self.TerminateMainLoop = True
  514. EndIf
  515. Self.Time :+ 1
  516. If Self.Time > 100 Then
  517. Self.Time = 0
  518. ' Add a new magnet
  519. Local ThisMagnet:TEndMagnet = New TEndMagnet
  520. ThisMagnet.X = Rnd(WIDTH)
  521. ThisMagnet.Y = Rnd(HEIGHT)
  522. ThisMagnet.Radius = Rnd(200,500)
  523. ThisMagnet.Polarity = TMagnet.PositivePolarity
  524. ThisMagnet.Strength = Rnd(0.1,0.5)
  525. Self.World.Magnets.AddLast(ThisMagnet)
  526. Self.World.ApplyMagnets = True
  527. EndIf
  528. Local X:Int = WIDTH/2
  529. Local Y:Int = HEIGHT/2
  530. For Local Magnet:TEndMagnet = EachIn Self.World.Magnets
  531. For Local Count:Int = 0 To STARS_PER_SECOND
  532. Local ThisStar:TEndingStar = TEndingStar.Create(Self.World, Self, Float(Rnd(1.0,5.0)), Int(Magnet.X + (Sin(Rnd(360.0)) * 5.0)), Int(Magnet.Y + (Cos(Rnd(360.0)) * 5.0)))
  533. ThisStar.SetVelocityFromAngle(Rnd(360),Rnd(1.0,5.0))
  534. Self.Stars.AddLast(ThisStar)
  535. Magnet.Stars :+ 1
  536. Next
  537. If Magnet.Stars > 500 Then
  538. Self.World.Magnets.Remove(Magnet)
  539. EndIf
  540. Next
  541. ' Update the scoller
  542. Self.ScrollPoint :- 0.5
  543. If Self.Scrollpoint < -((TEXTS_COUNT * 25) + 100)
  544. Self.ScrollPoint = HEIGHT + 20
  545. EndIf
  546. End Method
  547. '#End Region
  548. '#Region Method: Render
  549. Method Render()
  550. Cls
  551. Self.Stars.Draw()
  552. SetColor 255,255,255
  553. For Local Count:Int = 0 To TEXTS_COUNT - 1
  554. MainMenuScene.DrawTextCentered(Self.Texts[Count],Int(Self.ScrollPoint + (25 * Count)))
  555. Next
  556. End Method
  557. '#End Region
  558. '#Region Method: Start
  559. Method Start()
  560. Self.MusicChannel = AllocChannel()
  561. If Self.Music = Null Then Self.Music = LoadSound("sounds\ending.ogg",True)
  562. If Self.MusicChannel <> Null
  563. PlaySound(Self.Music,Self.MusicChannel)
  564. EndIf
  565. Self.Texts[0] = "Cast of Characters"
  566. Self.Texts[1] = "------------------"
  567. Self.Texts[2] = ""
  568. Self.Texts[3] = "Space Craft.......................Intrepid"
  569. Self.Texts[4] = "Digestive...............Sweet Meal Biscuit"
  570. Self.Texts[5] = "Star...............................Himself"
  571. Self.Texts[6] = ""
  572. Self.Texts[7] = "Written by Rob Hutchinson in roughly 5 days."
  573. Self.Texts[8] = "Email: [email protected]"
  574. Self.Texts[9] = "Web: http://www.proteanide.co.uk/"
  575. Self.Texts[10] = ""
  576. Self.Texts[11] = "Special Thanks To"
  577. Self.Texts[12] = "-----------------"
  578. Self.Texts[13] = "Richard Makepeace.............Inspiration"
  579. Self.Texts[14] = "James Readman.................Inspiration"
  580. Self.Texts[15] = ""
  581. Self.Texts[16] = "Greets"
  582. Self.Texts[17] = "------"
  583. Self.Texts[18] = "Pickup, Cabsy, Peters, ZanaX, Compona, Helen, + all other family and friends."
  584. Self.Texts[19] = ""
  585. Self.Texts[20] = ""
  586. Self.Texts[21] = ""
  587. Self.Texts[22] = "Thank you for playing."
  588. Self.ScrollPoint = HEIGHT + 20
  589. End Method
  590. '#End Region
  591. '#Region Method: Finish
  592. Method Finish()
  593. Self.Stars.Clear()
  594. If Self.MusicChannel <> Null Then
  595. Self.MusicChannel.Stop()
  596. Self.MusicChannel = Null
  597. EndIf
  598. End Method
  599. '#End Region
  600. End Type
  601. '#End Region
  602. Type TCircle
  603. Field X:Int
  604. Field Y:Int
  605. Field Radius:Float
  606. Method CollidesWith:Int(Circle:TCircle)
  607. Local Distance:Double = TPhysicsUtility.DistanceBetweenPoints(Self.X,Self.Y,Circle.X,Circle.Y)
  608. If Distance < (Self.Radius + Circle.Radius) Then Return True
  609. Return False
  610. End Method
  611. Method Draw()
  612. SetBlend(ALPHABLEND)
  613. SetAlpha(0.5)
  614. SetRotation 0
  615. DrawCircle(X,Y,Int(Radius))
  616. End Method
  617. Function DrawCircle(X,Y,Radius)
  618. DrawOval(X - Radius,Y - Radius,Radius * 2,Radius * 2)
  619. End Function
  620. End Type
  621. Type TDigestive Extends TPhysicsProvider
  622. Field Image:TImage
  623. Field Alpha:Float = 1.0
  624. Field Rotation:Float = 0
  625. Field Scale:Float = 1.0
  626. Field Circle:TCircle = New TCircle
  627. Field GameScene:MainGameScene
  628. Const InitialRadius:Float = 70.0
  629. Field HitSound:TSound
  630. Method SplitIntoChunks(X:Int,Y:Int,Multiplier:Float)
  631. GameScene.Digestives.Remove(Self)
  632. Local Channel:TChannel = AllocChannel()
  633. If Channel <> Null
  634. Channel.SetVolume(Self.Scale)
  635. PlaySound(Self.HitSound, Channel)
  636. EndIf
  637. Self.GameScene.Chunks.X = X
  638. Self.GameScene.Chunks.Y = Y
  639. Self.GameScene.Player.Score :+ Int(((Self.Scale * 100.0) + 4.0) * Multiplier)
  640. Self.GameScene.Shake :+ (Self.Scale * 3.5)
  641. If Self.Scale > 0.2
  642. For Local Count:Int = 0 To Int(Self.Scale * 5)
  643. Local Digestive:TDigestive = TDigestive.Create(Self.World, Self.Image, Self.GameScene)
  644. Digestive.X = Self.X
  645. Digestive.Y = Self.Y
  646. Digestive.Rotation = Rnd(360)
  647. Digestive.Weight = 5
  648. Digestive.SetVelocityFromAngle(Rnd(360.0),Rnd(0.5,1.5))
  649. Digestive.SetDrawScale(Self.Scale / 2)
  650. Digestive.HitSound = Self.HitSound
  651. GameScene.Digestives.AddLast(Digestive)
  652. Next
  653. For Local ChunkCount:Int = 0 To 4 * (Self.Scale * 10)
  654. Local Particle:TStaticParticle = Self.GameScene.Chunks.AddStaticParticle(Self.GameScene.ChunkImages[(Int(Rnd(0,MainGameScene.CHUNK_COUNT)))], Float(Rnd(360.0)), Float(Rnd(0.5,10.0)), 0, 0.01, [255,255,255])
  655. Particle.Friction = 0.97
  656. Particle.Size = Rnd(0.5,1.0)
  657. Particle.Rotation = Rnd(-5.0,5.0)
  658. Next
  659. Else
  660. For Local ChunkCount2:Int = 0 To Rnd(3,8)
  661. Local Particle:TStaticParticle = Self.GameScene.Chunks.AddStaticParticle(Self.GameScene.ChunkImages[(Int(Rnd(0,MainGameScene.CHUNK_COUNT)))], Float(Rnd(360.0)), Float(Rnd(0.5,10.0)), 0, 0.01, [255,255,255])
  662. Particle.Friction = 0.97
  663. Particle.Size = Rnd(0.2,0.5)
  664. Particle.Rotation = Rnd(-5.0,5.0)
  665. Next
  666. End If
  667. End Method
  668. Method Draw()
  669. SetAlpha Self.ALPHA
  670. SetRotation Self.Rotation
  671. SetScale Self.Scale,Self.Scale
  672. DrawImage(Self.Image, Float(Self.X), Float(Self.Y))
  673. SetScale 1,1
  674. End Method
  675. Method SetDrawScale(Scale:Float)
  676. Self.Scale = Scale
  677. Self.Circle.Radius = Self.InitialRadius * Self.Scale
  678. End Method
  679. Method PhysicsApplied()
  680. Rotation:+2
  681. Local HalfWidth:Int = ((Image.Width * Self.Scale) / 2)
  682. Local HalfHeight:Int = ((Image.Height * Self.Scale) / 2)
  683. If X < -HalfWidth Then X = WIDTH + HalfWidth
  684. If Y < -HalfHeight Then Y = HEIGHT + HalfHeight
  685. If X > WIDTH + HalfWidth Then X = -HalfWidth
  686. If Y > HEIGHT + HalfHeight Then Y = -HalfHeight
  687. Self.Circle.X = Self.X
  688. Self.Circle.Y = Self.Y
  689. End Method
  690. Function Create:TDigestive(World:TWorldPhysicsProvider,Image:TImage,GameScene:MainGameScene)
  691. Local Out:TDigestive = New TDigestive
  692. Out.GameScene = GameScene
  693. Out.Circle.Radius = InitialRadius
  694. Out.World = World
  695. Out.Image = Image
  696. Return Out
  697. End Function
  698. End Type
  699. Type TPlayer Extends TPhysicsProvider
  700. Field Lives:Int
  701. Field Score:Int
  702. Field Weapon:TWeapon
  703. Field Dead:Int = True
  704. Field Teleports:Int = 2
  705. Field Image:TImage
  706. Field Rotation:Float
  707. Field Acceleration:Float
  708. Field MaxAcceleration:Float = 4.0
  709. Field Motion:Float = 0.001
  710. Field AccelerationDropMultiplier:Float = 8
  711. Field Circle:TCircle = New TCircle
  712. Field GameScene:MainGameScene
  713. Field FireRateCount:Float = 0
  714. Field Thruster:TFountain
  715. Field Death:TFountain
  716. Field ThrusterImage:TImage
  717. Field DeathImage:TImage
  718. Field SparkleImage:TImage
  719. Field ThrusterSound:TSound
  720. Field CrashSound:TSound
  721. Field ThrusterChannel:TChannel
  722. Field TeleportSound:TSound
  723. Method Draw()
  724. SetTransform
  725. ' Render the thruster...
  726. SetBlend LIGHTBLEND
  727. SetScale 0.6, 0.6
  728. SetAlpha 1.0
  729. Self.Thruster.Draw()
  730. Self.Death.Draw()
  731. If Not Self.Dead Then
  732. ' Render the player.
  733. SetBlend ALPHABLEND
  734. SetAlpha 1.0
  735. SetScale 1.0,1.0
  736. SetColor 255,255,255
  737. SetRotation 360 - Self.Rotation
  738. DrawImage(Self.Image, Float(Self.X), Float(Self.Y))
  739. EndIf
  740. End Method
  741. Method PhysicsApplied()
  742. Local HalfWidth:Int = (Image.Width / 2)
  743. Local HalfHeight:Int = (Image.Height / 2)
  744. If X < -HalfWidth Then X = WIDTH + HalfWidth
  745. If Y < -HalfHeight Then Y = HEIGHT + HalfHeight
  746. If X > WIDTH + HalfWidth Then X = -HalfWidth
  747. If Y > HEIGHT + HalfHeight Then Y = -HalfHeight
  748. Self.Circle.X = Self.X
  749. Self.Circle.Y = Self.Y
  750. End Method
  751. Method Update()
  752. ' Update thruster and death particles.
  753. Local Particle:TStaticParticle = Self.Thruster.AddStaticParticle(Self.ThrusterImage, Float(Self.Rotation + 180 + Rnd(-5.0,5.0)), 1.0, 1, 0.04, [255,255,255])
  754. Local ActualRotation:Float = Self.Rotation + 180
  755. Self.Thruster.X = Self.X + (Sin(ActualRotation) * 10)
  756. Self.Thruster.Y = Self.Y + (Cos(ActualRotation) * 10)
  757. Local ThisSpeed:Float = Self.Speed()
  758. If ThisSpeed < 0 Then ThisSpeed = 0
  759. If ThisSpeed > 10.0 Then ThisSpeed = 10.0
  760. ' Add particles to the thruster..
  761. Particle.Size = (ThisSpeed / 10)
  762. Self.ThrusterChannel.SetVolume(ThisSpeed / 10)
  763. Self.ThrusterChannel.SetPan(Float(Self.X / Float(WIDTH / 2.0) - 1.0))
  764. Self.GameScene.Shake:+ 0.10 * (ThisSpeed / 10.0)
  765. Self.Thruster.Update()
  766. Self.Death.UpdateWithFriction(TPhysicsProvider.AxisX | TPhysicsProvider.AxisY)
  767. If Self.Dead
  768. ' Player is currently dead, wait for a nice chance to put them back down.
  769. If Self.Lives < 1 Then
  770. ' Wait for the death particles to become zeroed.
  771. If Self.Death.Particles.IsEmpty() Then
  772. Self.GameScene.TerminateMainLoop = True
  773. EndIf
  774. Else
  775. ' Check to see if the center spot is safe to plop the ship on.
  776. If Self.GameScene.IsLocationSafe(WIDTH/2, HEIGHT/2, 70)
  777. Self.Dead = False
  778. For Local Pops:Int = 0 To 200
  779. Local Particle:TStaticParticle = Self.Death.AddStaticParticle(Self.SparkleImage, Float(Rnd(360.0)), Float(Rnd(0.5,2.0)), 0, Float(Rnd(0.01, 0.03)), [255,255,255])
  780. Particle.X = Self.X
  781. Particle.Y = Self.Y
  782. Particle.Rotation = Rnd(-1.0,1.0)
  783. Particle.Friction = 0.97
  784. Particle.Size = Rnd(0.1,0.2)
  785. Next
  786. EndIf
  787. EndIf
  788. Else
  789. ' Update fire rate counter...
  790. Self.FireRateCount :- 1
  791. If KeyDown(KEY_UP)
  792. ' Thrust
  793. Self.Acceleration :+ Self.Motion
  794. ' Clamp the acceleration
  795. If Self.Acceleration > Self.MaxAcceleration Then Self.Acceleration = Self.MaxAcceleration
  796. ' Add acceleration.
  797. Self.IncreaseVelocityFromAngle(Self.Rotation,Self.Acceleration)
  798. Else
  799. Self.Acceleration :- (Self.Motion * Self.AccelerationDropMultiplier)
  800. If Self.Acceleration < 0.0 Then Self.Acceleration = 0
  801. EndIf
  802. If KeyDown(KEY_LEFT)
  803. ' RotateLeft
  804. Self.Rotation :+ 3
  805. EndIf
  806. If KeyDown(KEY_RIGHT)
  807. ' RotateLeft
  808. Self.Rotation :- 3
  809. EndIf
  810. If KeyDown(KEY_SPACE)
  811. ' FIRE!
  812. If Self.FireRateCount <= 0 Then
  813. Self.FireRateCount = Self.Weapon.FireRate
  814. Self.Weapon.Fire(Self)
  815. End If
  816. EndIf
  817. If KeyHit(KEY_DOWN)
  818. ' TELEPORT
  819. If Self.Teleports > 0 Then
  820. Self.Teleports :- 1
  821. Local Location:TPointD = Self.GameScene.FindSafeLocation(20)
  822. PlaySound(Self.TeleportSound)
  823. Self.Death.X = Self.X
  824. Self.Death.Y = Self.Y
  825. For Local Pops:Int = 0 To 50
  826. Local Particle:TStaticParticle = Self.Death.AddStaticParticle(Self.SparkleImage, Float(Rnd(360.0)), Float(Rnd(0.5,10.0)), 0, 0.02, [255,255,255])
  827. Particle.X = Self.X
  828. Particle.Y = Self.Y
  829. Particle.Rotation = Rnd(-1.0,1.0)
  830. Particle.Friction = 0.97
  831. Particle.Size = Rnd(0.1,0.2)
  832. Next
  833. Self.X = Int(Location.X)
  834. Self.Y = Int(Location.Y)
  835. Self.Death.X = Self.X
  836. Self.Death.Y = Self.Y
  837. For Local Pops:Int = 0 To 20
  838. Local Particle:TStaticParticle = Self.Death.AddStaticParticle(Self.SparkleImage, Float(Rnd(360.0)), Float(Rnd(0.5,2.0)), 0, 0.02, [255,255,255])
  839. Particle.X = Self.X
  840. Particle.Y = Self.Y
  841. Particle.Rotation = Rnd(-1.0,1.0)
  842. Particle.Friction = 0.97
  843. Particle.Size = Rnd(0.1,0.2)
  844. Next
  845. EndIf
  846. EndIf
  847. ' COLLISION DETECT AGAINST DIGESTIVES!
  848. ' ---------------------------------------------------------
  849. For Local Item:TDigestive = EachIn Self.GameScene.Digestives
  850. If Item.Circle.CollidesWith(Self.Circle)
  851. Self.Lives :- 1
  852. Self.Dead = True
  853. PlaySound(Self.CrashSound)
  854. Local PlayerSpeed:Double = Self.Speed()
  855. Self.GameScene.Shake :+ PlayerSpeed / 1.5
  856. If PlayerSpeed > 10.0
  857. Item.SplitIntoChunks(Int(Self.X),Int(Self.Y),Float(Self.Speed()))
  858. EndIf
  859. Self.Death.X = Self.X
  860. Self.Death.Y = Self.Y
  861. For Local Pops:Int = 0 To 100
  862. If PlayerSpeed > 10 Then PlayerSpeed = 10
  863. Local Angle:Double = Self.Angle() + Rnd(-(80 - (8.0 * PlayerSpeed)), 80 - (8.0 * PlayerSpeed))
  864. Local Particle:TStaticParticle = Self.Death.AddStaticParticle(Self.DeathImage, Float(Angle), Float(Rnd(0.1, Self.Speed())), 0, 0.03, [255,255,255])
  865. Particle.Friction = 0.98
  866. Particle.Size = Rnd(0.1,0.7)
  867. Particle.Color = [Rand(80,255),24,Rand(24,128)]
  868. Next
  869. For Local Pops:Int = 0 To 300
  870. Local Particle:TStaticParticle = Self.Death.AddStaticParticle(Self.DeathImage, Float(Rnd(360.0)), Float(Rnd(0.1, 7.0)), 0, Float(Rnd(0.01,0.04)), [255,255,255])
  871. Particle.Friction = 0.99
  872. Particle.Size = Rnd(0.1,0.4)
  873. Particle.Color = [Rand(80,255),24,Rand(24,200)]
  874. Next
  875. Self.Reset()
  876. EndIf
  877. Next
  878. EndIf
  879. End Method
  880. Method Reset()
  881. Self.X = WIDTH / 2
  882. Self.Y = HEIGHT / 2
  883. Self.Acceleration = 0
  884. Self.VelocityX = 0
  885. Self.VelocityY = 0
  886. Self.Teleports = 2
  887. Self.Dead = 2
  888. End Method
  889. Method LoadAssets()
  890. If Self.ThrusterImage = Null Then Self.ThrusterImage = LoadImage("graphics\bullet1.png")
  891. If Self.DeathImage = Null Then Self.DeathImage = LoadImage("graphics\pop.png")
  892. If Self.SparkleImage = Null Then Self.SparkleImage = LoadImage("graphics\sparkle.png")
  893. ' Load in the sounds...
  894. If Self.ThrusterSound = Null Then Self.ThrusterSound = LoadSound("sounds\thrust.wav",True)
  895. If Self.CrashSound = Null Then Self.CrashSound = LoadSound("sounds\crash.wav",False)
  896. If Self.TeleportSound = Null Then Self.TeleportSound = LoadSound("sounds\teleport.wav",False)
  897. Self.ThrusterChannel = AllocChannel()
  898. Self.ThrusterChannel.SetVolume(0)
  899. PlaySound(Self.ThrusterSound,Self.ThrusterChannel)
  900. End Method
  901. Function Create:TPlayer(World:TWorldPhysicsProvider, Image:TImage, GameScene:MainGameScene)
  902. Local Out:TPlayer = New TPlayer
  903. Out.Thruster = TFountain.Create(World, TRectangle.Create(0,0,WIDTH,HEIGHT))
  904. Out.Death = TFountain.Create(World, TRectangle.Create(0,0,WIDTH,HEIGHT))
  905. Out.GameScene = GameScene
  906. Out.Circle.Radius = 10
  907. Out.World = World
  908. Out.Image = Image
  909. Out.Friction = 0.985
  910. Out.Reset()
  911. Return Out
  912. End Function
  913. End Type
  914. Type TBullet Extends TPhysicsProvider
  915. Field Image:TImage
  916. Field Circle:TCircle = New TCircle
  917. Field GameScene:MainGameScene
  918. Field Size:Float = 1.0
  919. Field ScoreMultiplier:Float = 1
  920. Field TTL:Int
  921. Field Fading:Float = 1.0
  922. Const TIME_TO_LIVE = 100
  923. Method Draw()
  924. SetScale(Self.Size,Self.Size)
  925. SetAlpha(Self.Fading)
  926. DrawImage(Self.Image, Float(Self.X), Float(Self.Y))
  927. End Method
  928. Method PhysicsApplied()
  929. Local HalfWidth:Int = (Image.Width / 2)
  930. Local HalfHeight:Int = (Image.Height / 2)
  931. If X < -HalfWidth Then X = WIDTH + HalfWidth
  932. If Y < -HalfHeight Then Y = HEIGHT + HalfHeight
  933. If X > WIDTH + HalfWidth Then X = -HalfWidth
  934. If Y > HEIGHT + HalfHeight Then Y = -HalfHeight
  935. ' Shall we kill it off?
  936. Self.TTL :+ 1
  937. If Self.TTL > TIME_TO_LIVE Then
  938. Self.Fading :- 0.015
  939. If Self.Fading < 0.05
  940. Self.GameScene.Bullets.Remove(Self)
  941. EndIf
  942. EndIf
  943. ' If X < -HalfWidth Or Y < -HalfHeight Or X > WIDTH + HalfWidth Or Y > HEIGHT + HalfHeight Then
  944. ' ' Bullet went off't screen
  945. ' Self.GameScene.Bullets.Remove(Self)
  946. ' EndIf
  947. Self.Circle.X = Self.X
  948. Self.Circle.Y = Self.Y
  949. ' COLLISION DETECT AGAINST DIGESTIVES!
  950. ' ---------------------------------------------------------
  951. For Local Item:TDigestive = EachIn Self.GameScene.Digestives
  952. If Item.Circle.CollidesWith(Self.Circle)
  953. If Self.Fading > 0.5
  954. Self.GameScene.Bullets.Remove(Self)
  955. Item.SplitIntoChunks(Self.Circle.X,Self.Circle.Y,Self.ScoreMultiplier)
  956. Exit
  957. EndIf
  958. EndIf
  959. Next
  960. End Method
  961. Function Create:TBullet(World:TWorldPhysicsProvider, Image:TImage, Player:TPlayer, Speed:Float, GameScene:MainGameScene, Size:Float, Radius:Float, Veer:Float)
  962. Local Out:TBullet = New TBullet
  963. Out.GameScene = GameScene
  964. Out.SetVelocityFromAngle(Player.Rotation + Rnd(-Veer,Veer),Speed)
  965. Out.Size = Size
  966. Out.Circle.Radius = Radius * Size
  967. Out.X = Player.X
  968. Out.Y = Player.Y
  969. Out.World = World
  970. Out.Image = Image
  971. Return Out
  972. End Function
  973. End Type
  974. Type TStar Extends TPhysicsProvider
  975. Field Color[]
  976. Field GameScene:MainGameScene
  977. Method Draw()
  978. SetColor Self.Color[0],Self.Color[1],Self.Color[2]
  979. DrawRect(Float(X),Float(Y),1,1)
  980. End Method
  981. Method PhysicsApplied()
  982. Self.VelocityX :* 1.01
  983. Self.VelocityY :* 1.01
  984. Local HalfWidth:Int = 5
  985. Local HalfHeight:Int = 5
  986. If X < -HalfWidth Or Y < -HalfHeight Or X > WIDTH + HalfWidth Or Y > HEIGHT + HalfHeight Then
  987. ' Bullet went off't screen
  988. Self.GameScene.Stars.Remove(Self)
  989. EndIf
  990. End Method
  991. Function Create:TStar(World:TWorldPhysicsProvider,GameScene:MainGameScene, Speed:Float)
  992. Local Out:TStar = New TStar
  993. Local Pigment = Rand(24,255)
  994. Out.GameScene = GameScene
  995. Out.Color = [Pigment,Pigment,Pigment]
  996. Out.SetVelocityFromAngle(Rnd(360),Rnd(0.2,0.9) * Speed)
  997. Out.X = WIDTH / 2
  998. Out.Y = HEIGHT / 2
  999. Out.World = World
  1000. Return Out
  1001. End Function
  1002. End Type
  1003. Type TMenuStar Extends TPhysicsProvider
  1004. Field Color[]
  1005. Field MenuScene:MainMenuScene
  1006. Method Draw()
  1007. SetColor Self.Color[0],Self.Color[1],Self.Color[2]
  1008. DrawRect(Float(X),Float(Y),1,1)
  1009. End Method
  1010. Method PhysicsApplied()
  1011. ' Self.VelocityX :* 1.01
  1012. ' Self.VelocityY :* 1.01
  1013. Local HalfWidth:Int = 5
  1014. Local HalfHeight:Int = 5
  1015. If X < -HalfWidth Or Y < -HalfHeight Or X > WIDTH + HalfWidth Or Y > HEIGHT + HalfHeight Then
  1016. ' Bullet went off't screen
  1017. Self.MenuScene.Stars.Remove(Self)
  1018. EndIf
  1019. End Method
  1020. Function Create:TMenuStar(World:TWorldPhysicsProvider,MenuScene:MainMenuScene, Speed:Float)
  1021. Local Out:TMenuStar = New TMenuStar
  1022. Local Pigment = Rand(24,255)
  1023. Out.MenuScene = MenuScene
  1024. Out.Color = [Pigment,Pigment,Pigment]
  1025. Out.SetVelocityFromAngle(90,Rnd(0.2,0.9) * Speed)
  1026. Out.X = 0
  1027. Out.Y = Rnd(HEIGHT)
  1028. Out.World = World
  1029. Return Out
  1030. End Function
  1031. End Type
  1032. Type TEndingStar Extends TPhysicsProvider
  1033. Field Color[]
  1034. Field EndScene:EndingScene
  1035. Method Draw()
  1036. SetColor Self.Color[0],Self.Color[1],Self.Color[2]
  1037. DrawRect(Float(X),Float(Y),1,1)
  1038. End Method
  1039. Method PhysicsApplied()
  1040. ' Self.VelocityX :* 1.01
  1041. ' Self.VelocityY :* 1.01
  1042. Local HalfWidth:Int = 5
  1043. Local HalfHeight:Int = 5
  1044. If X < -HalfWidth Or Y < -HalfHeight Or X > WIDTH + HalfWidth Or Y > HEIGHT + HalfHeight Then
  1045. ' Bullet went off't screen
  1046. Self.EndScene.Stars.Remove(Self)
  1047. EndIf
  1048. End Method
  1049. Function Create:TEndingStar(World:TWorldPhysicsProvider, EndScene:EndingScene, Speed:Float, X:Int, Y:Int)
  1050. Local Out:TEndingStar = New TEndingStar
  1051. Local Pigment = Rand(24,255)
  1052. Out.EndScene = EndScene
  1053. Out.Color = [Pigment,Pigment,Pigment]
  1054. Out.X = X
  1055. Out.Y = Y
  1056. Out.World = World
  1057. Return Out
  1058. End Function
  1059. End Type
  1060. Type TPickup Extends TLink
  1061. Const MODIFIER_SCORE = 0
  1062. Const MODIFIER_LIVES = 1
  1063. Const MODIFIER_ROTATION = 2
  1064. Const MODIFIER_PSYCHODELIC = 3
  1065. Const MODIFIER_WEAPON = 4
  1066. Field Modifies:Int = MODIFIER_SCORE
  1067. Field ByValue:Float
  1068. Field LastsFor:Int
  1069. Field Time:Int
  1070. Field IsPermanent:Int = False
  1071. Field Weapon:TWeapon
  1072. Field Icon:TImage
  1073. Field Probability:Double
  1074. Field OwnedIcon:TImage
  1075. Method Clone:TPickup()
  1076. Local Out:TPickup = New TPickup
  1077. Out.Modifies = Self.Modifies
  1078. Out.ByValue = Self.ByValue
  1079. Out.LastsFor = Self.LastsFor
  1080. Out.IsPermanent = Self.IsPermanent
  1081. Out.Weapon = Self.Weapon
  1082. Out.Icon = Self.Icon
  1083. Out.Probability = Self.Probability
  1084. Out.OwnedIcon = Out.OwnedIcon
  1085. Return Out
  1086. End Method
  1087. Function Create:TPickup(Modifies:Int,ByValue:Float,LastsFor:Int,IsPermanent:Int,Weapon:TWeapon,Icon:TImage,Probability:Double,OwnedIcon:TImage)
  1088. Local Out:TPickup = New TPickup
  1089. Out.Modifies = Modifies
  1090. Out.ByValue = ByValue
  1091. Out.LastsFor = LastsFor
  1092. Out.IsPermanent = IsPermanent
  1093. Out.Weapon = Weapon
  1094. Out.Icon = Icon
  1095. Out.Probability = Probability
  1096. Out.OwnedIcon = OwnedIcon
  1097. Return Out
  1098. End Function
  1099. End Type
  1100. Type TWeapon Extends TLink
  1101. Field Speed:Float
  1102. Field Graphic:TImage
  1103. Field Size:Float
  1104. Field Radius:Float
  1105. Field Veer:Float
  1106. Field FireRate:Int
  1107. Field MainGame:MainGameScene
  1108. Field Sound:TSound
  1109. Field Name:String
  1110. Field Icon:TImage
  1111. Method Fire(Player:TPlayer)
  1112. Local Shot:TBullet = TBullet.Create(MainGame.World, Self.Graphic, Player, Float(Self.Speed + Player.Speed()), Self.MainGame, Self.Size, Self.Radius, Self.Veer)
  1113. Shot.ScoreMultiplier = (Player.Speed() * 4.0)
  1114. If Shot.ScoreMultiplier < 1.0 Then Shot.ScoreMultiplier = 1.0
  1115. Self.MainGame.Bullets.AddLast(Shot)
  1116. PlaySound(Self.Sound)
  1117. End Method
  1118. Function Create:TWeapon(Speed:Float,Graphic:TImage,Size:Float,Radius:Float,Veer:Float,FireRate:Int,MainGame:MainGameScene,Sound:TSound,Icon:TImage,Name:String)
  1119. Local Out:TWeapon = New TWeapon
  1120. Out.Speed = Speed
  1121. Out.Graphic = Graphic
  1122. Out.Size = Size
  1123. Out.Radius = Radius
  1124. Out.Veer = Veer
  1125. Out.FireRate = FireRate
  1126. Out.MainGame = MainGame
  1127. Out.Sound = Sound
  1128. Out.Icon = Icon
  1129. Out.Name = Name
  1130. Return Out
  1131. End Function
  1132. End Type
  1133. Type TEndMagnet Extends TMagnet
  1134. Field Stars:Int
  1135. End Type
  1136. Type THighScores
  1137. Const SCORE_COUNT = 10
  1138. Field Scores:Int[SCORE_COUNT]
  1139. Field Names:String[SCORE_COUNT]
  1140. Method Render(X:Int, Y:Int)
  1141. For Local Count:Int = 0 To SCORE_COUNT - 1
  1142. DrawText(Self.Names[Count], X,Y + (Count * 20))
  1143. Next
  1144. For Local Count2:Int = 0 To SCORE_COUNT - 1
  1145. DrawText(Self.Scores[Count2], X + 200,Y + (Count2 * 20))
  1146. Next
  1147. End Method
  1148. Method IsHighScore:Int(Score:Int)
  1149. For Local Count:Int = 0 To SCORE_COUNT - 1
  1150. If Score > Self.Scores[Count] Then
  1151. Return True
  1152. EndIf
  1153. Next
  1154. Return False
  1155. End Method
  1156. Method Add(Score:Int,Name:String)
  1157. ' Find out where we should put the score..
  1158. Local PlaceAt:Int = SCORE_COUNT - 1
  1159. For Local Count:Int = SCORE_COUNT - 1 To 0 Step -1
  1160. If Score > Self.Scores[Count] Then
  1161. PlaceAt = Count
  1162. EndIf
  1163. Next
  1164. ' Shuffle them all down..
  1165. For Local Shuffle:Int = SCORE_COUNT - 2 To PlaceAt Step -1
  1166. Self.Scores[Shuffle + 1] = Self.Scores[Shuffle]
  1167. Self.Names[Shuffle + 1] = Self.Names[Shuffle]
  1168. Next
  1169. Self.Scores[PlaceAt] = Score
  1170. Self.Names[PlaceAt] = Name
  1171. End Method
  1172. Method Save(File:String)
  1173. Local Out:TStream = OpenStream(File,False,True)
  1174. If Out <> Null
  1175. For Local Count:Int = 0 To SCORE_COUNT - 1
  1176. WriteInt(Out,Self.Scores[Count])
  1177. WriteLine(Out,Self.Names[Count])
  1178. Next
  1179. CloseStream(Out)
  1180. EndIf
  1181. End Method
  1182. Function Load:THighScores(File:String)
  1183. Local In:TStream = OpenStream(File,True,False)
  1184. Local Out:THighScores = New THighScores
  1185. If In = Null
  1186. ' Fill it full of high scores..
  1187. Out.Names[0] = "Loki"
  1188. Out.Names[1] = "Booty"
  1189. Out.Names[2] = "Rix"
  1190. Out.Names[3] = "Jamez"
  1191. Out.Names[4] = "Pies"
  1192. Out.Names[5] = "Bobny"
  1193. Out.Names[6] = "Berty"
  1194. Out.Names[7] = "Billy"
  1195. Out.Names[8] = "Bonny"
  1196. Out.Names[9] = "Boxer"
  1197. Out.Scores[0] = 100000
  1198. Out.Scores[1] = 80000
  1199. Out.Scores[2] = 50000
  1200. Out.Scores[3] = 40000
  1201. Out.Scores[4] = 30000
  1202. Out.Scores[5] = 20000
  1203. Out.Scores[6] = 10000
  1204. Out.Scores[7] = 5000
  1205. Out.Scores[8] = 4000
  1206. Out.Scores[9] = 1000
  1207. Else
  1208. For Local Count:Int = 0 To SCORE_COUNT - 1
  1209. Out.Scores[Count] = ReadInt(In)
  1210. Out.Names[Count] = ReadLine(In)
  1211. Next
  1212. CloseStream(In)
  1213. EndIf
  1214. Return Out
  1215. End Function
  1216. End Type
  1217. ' Main game setup.
  1218. ' ------------------------
  1219. Local Game:T2DDynamicGame = T2DDynamicGame.Create(WIDTH, HEIGHT, DEPTH, REFRESHRATE)
  1220. Global Scores:THighScores = THighScores.Load("hi.dat")
  1221. Game.DynamicTiming = DYNAMICTIMING
  1222. Game.DesiredFPS = DESIREDFPS
  1223. Game.Initialize() ' Go into graphics mode.
  1224. HideMouse
  1225. ' Create the main game scene.
  1226. Local MenuScene:MainMenuScene = New MainMenuScene
  1227. Local GameScene:MainGameScene = New MainGameScene
  1228. Local EndScene:EndingScene = New EndingScene
  1229. Local QuitFlag:Int = False
  1230. While Not QuitFlag
  1231. ' Attach The Game Scene To The Dynamic Game.
  1232. Game.Setscene(Menuscene)
  1233. ' Start The Main Loop.
  1234. Game.Mainloop()
  1235. If Not MenuScene.Exitgame
  1236. If MenuScene.ShowCredits
  1237. MenuScene.ShowCredits = False
  1238. Game.SetScene(EndScene)
  1239. Game.Mainloop()
  1240. Else
  1241. Game.Setscene(GameScene)
  1242. Game.Mainloop()
  1243. QFlushKeys()
  1244. If GameScene.Completed
  1245. GameScene.Completed = False
  1246. Game.SetScene(EndScene)
  1247. Game.Mainloop()
  1248. EndIf
  1249. QFlushKeys()
  1250. ' Check for high score.
  1251. If Scores.IsHighScore(GameScene.Player.Score) Then
  1252. ' We got a high score.
  1253. MenuScene.ViewMenu(MenuScene.VIEW_ENTERHIGH)
  1254. MenuScene.LastHighScore = GameScene.Player.Score
  1255. MenuScene.EnteredText = ""
  1256. EndIf
  1257. EndIf
  1258. Else
  1259. QuitFlag = True
  1260. EndIf
  1261. Wend
  1262. Scores.Save("hi.dat")
  1263. ' Clean up after we shut down.
  1264. Game.ShutDown()
  1265. ' Fin
  1266. Const DEBUG = True
  1267. Function DebugLog(Text:String)
  1268. If DEBUG Then Print Text
  1269. End Function