Author Topic: 🎄🎁✨ Holiday Season - are you ready to code?  (Read 39124 times)

0 Members and 1 Guest are viewing this topic.

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #75 on: December 15, 2020, 01:27:36 pm »
OK. Source code upgraded, font is now in the source code.  Try it again, please.  It is running correctly under Windows. (previous version also)

Petr,

Thank you for the upgrade. Everything functions as it should.

Very nicely done! Great job!

J
Logic is the beginning of wisdom.

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #76 on: December 15, 2020, 05:44:15 pm »
@bplus: Glad you enjoy the program!

Thanks @Dav what's your favorite charity?

As far as donations go, I don't have a favorite. Me and extra money don't seem to meet that often -- so my contributions are of a local volunteering nature.  Sometime those TV commercials (like for the hospital that helps sick kids 100% free) nearly makes my eyes puddle up and wish I did have money to contribute to charities.

- Dav

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #77 on: December 15, 2020, 06:19:41 pm »
That's really awesome Dav! When I made my Starfield Clock I thought about something like this for the hourly chimes. Right now it only has the PLAY command. You did an impressive job here. Do you mind if I use 1 or 2 of your .ogg sound files for my clock? But I need to know something first, can the chimes play when there's other things going on in the background? That is why I couldn't use the chime example I found a long time ago.
« Last Edit: December 15, 2020, 06:22:25 pm by SierraKen »

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #78 on: December 15, 2020, 06:39:14 pm »
@SierraKen: Thanks! Sure, you may use anything i make and post here in your projects.  The way the SPLAY SUB is set up (with my added delay) the sound playback would suspend your clock from running while chiming. But you could easily adapt the code to eliminate that problem. Cool thing about _SNDPLAYCOPY is that it won't suspend your program while playing a sound. All you need to do is call the sounds from in your main program (forget the SPLAY SUB) and perhaps set a special TIMER for calling each note in your main loop.

- Dav

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #79 on: December 15, 2020, 10:17:25 pm »
Thanks Dav, I tried for a bit tonight but couldn't figure it out, it kept making the sound a bunch of times on each chime. But that's OK, I'm satisfied with what I have. :) If I decide to check it out later I might.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #80 on: December 17, 2020, 03:36:59 pm »
Here's a little something Xmassy which I tossed together this afternoon.  It works better (as in much faster) with external files, but part of the rules here said we should avoid those, so this is what I've got at the moment:

[edit]

Grab the attachment below and run it; the data length in the BAS file is too large for the forum database to handle properly, and can't be included in a post.

If I'd known I couldn't get this to load as is, I wouldn't have bothered to turn it into a singluar file and would've just kept everything externally available.  :P

Ah well...  Now I need to decide what I want to do next to it (if anything).  I'm thinking of having Santa fly across the screen in his sled, with a ribbon trailing behind him, showing the words of the song as it plays for us...   And then maybe making this a screensaver for the holidays, on a soundplay loop...

Or maybe I'll just leave it as it is, and not worry over it...

I dunno yet what I'm going to end up doing with it, but here's my little Xmas contribution!
 
Screenshot 2020-12-17 16.01.15.png
* Xmas.bas (Filesize: 1.11 MB, Downloads: 252)
« Last Edit: December 17, 2020, 04:01:59 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #81 on: December 17, 2020, 04:53:36 pm »
Well that's pretty, Steve.  I really like the floating snow effect.  Has a nice lilt to it that makes it more like snow than just dropping like rain drops.  Good screensaver.

Took a while for the converting on my slower laptop, but worth the wait.  Nice converting functions there you made....
 
- Dav
« Last Edit: December 17, 2020, 05:02:12 pm by Dav »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #82 on: December 17, 2020, 05:12:26 pm »
Well that's pretty, Steve.  I really like the floating snow effect.  Has a nice lilt to it that makes it more like snow than just dropping like rain drops.  Good screensaver.

Took a while for the converting on my slower laptop, but worth the wait.  Nice converting functions there you made....
 
- Dav

If you're just interested in the conversion routines, you can find them alone here: https://www.qb64.org/forum/index.php?topic=3379.msg126862#msg126862

Compress and convert images/sound/resource files to BASE-128 encoding, so we can use them internally in DATA statements without any problems.  They need to be optimized for speed (use of readbit and setbit would help a lot, but they weren't in our language when I first did these routines), but they're functional and usage can often be hidden behind a startup splash screen, or such, in many cases.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #83 on: December 17, 2020, 05:24:58 pm »
Amazing Steve, very nice.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #84 on: December 18, 2020, 02:31:52 am »
Beautiful snow scene Steve!

I finally found an oldie I did in 2015 and translated it to QB64 with some mods:
Code: QB64: [Select]
  1. _TITLE "Sierpinski Decorated" 'b+ 2020-12-18 finally found this oldie
  2. 'sierpinski decorated.bas 2015-12-06 SmallBASIC 0.12.1 [B+=MGA]
  3. 'Thanks to PeterMaria W for code snips
  4. CONST SSQ = 700, horizon = 485 ' Side of Square Screen
  5. DIM SHARED QB(15) 'modified old QB 16 color system
  6. QB(0) = &HFF000000
  7. QB(1) = &HFF000088
  8. QB(2) = &HFF008800
  9. QB(3) = &HFF008888
  10. QB(4) = &HFF880000
  11. QB(5) = &HFF880088
  12. QB(6) = &HFF888800
  13. QB(7) = &HFFCCCCCC
  14. QB(8) = &HFF888888
  15. QB(9) = &HFF0000FF
  16. QB(10) = &HFF00FF00
  17. QB(11) = &HFF00FFFF
  18. QB(12) = &HFFFF0000
  19. QB(13) = &HFFFF00FF
  20. QB(14) = &HFFFFFF00
  21. QB(15) = &HFFFFFFFF
  22.  
  23. SCREEN _NEWIMAGE(SSQ + 100, SSQ, 32)
  24. _DELAY .25
  25.  
  26. xplus = SSQ
  27. yplus = horizon
  28. nstars = 100
  29. DIM xstar(100), ystar(100), rstar(100)
  30. FOR i = 1 TO 100
  31.     xstar(i) = RND * (SSQ + 100): ystar(i) = RND * horizon:
  32.     IF i < 75 THEN
  33.         rstar(i) = 0
  34.     ELSEIF i < 95 THEN
  35.         rstar(i) = 1
  36.     ELSE
  37.         rstar(i) = 2
  38.     END IF
  39.     CLS
  40.     FOR i = 0 TO horizon
  41.         LINE (0, i)-(SSQ + 100, i), _RGB32(i / horizon * 70, i / horizon * 22, 60 * (i) / horizon)
  42.     NEXT
  43.     land = SSQ - horizon
  44.     FOR i = horizon TO SSQ
  45.         cc = 128 + (i - horizon) / land * 127
  46.         LINE (0, i)-(SSQ + 100, i), _RGB32(cc, cc, cc)
  47.     NEXT
  48.     FOR i = 1 TO 100
  49.         fcirc xstar(i), ystar(i), rstar(i), QB(11)
  50.     NEXT
  51.     pinetree 200, 100
  52.     star xplus, yplus
  53.     xdist = 420 - xplus
  54.     xplus = xplus + .1 * xdist
  55.     ydist = 80 - yplus
  56.     yplus = yplus + .1 * ydist
  57.     _DISPLAY
  58.     _LIMIT 7
  59.  
  60. SUB star (x, y)
  61.     fcirc x, y, 3, &HFFAAFF00
  62.     LINE (x - 7, y)-(x - 3, y), &HFFAAFF00
  63.     LINE (x + 3, y)-(x + 7, y), &HFFAAFF00
  64.     LINE (x, y - 7)-(x, y - 3), &HFFAAFF00
  65.     LINE (x, y + 3)-(x, y + 7), &HFFAAFF00
  66.     IF x > 419 AND x < 421 AND y > 79 AND y < 81 THEN
  67.         CIRCLE (x, y), 12, &HFFAAFF00
  68.         COLOR &HFFFFFF88, &H00000000
  69.         Text 35, horizon + 90, 85, &HFFAA0000, "Seasons Greetings"
  70.         sier 210, 125, 420, 340
  71.         SLEEP
  72.     END IF
  73.  
  74. SUB sier (tlx, tly, width, height)
  75.     ' Sierpinski Christmas.bas modified from Petermaria's
  76.     ax = tlx
  77.     ay = tly + height
  78.     bx = tlx + width
  79.     by = tly + height
  80.     cx = tlx + width / 2
  81.     cy = tly
  82.     px = tlx
  83.     py = tly + height
  84.     FOR n = 0 TO 4000
  85.         fcirc px, py, 2, QB(INT(RND * 16))
  86.         SELECT CASE INT(RND * 3)
  87.             CASE 0
  88.                 px = (px + ax) / 2.0
  89.                 py = (py + ay) / 2.0
  90.             CASE 1
  91.                 px = (px + bx) / 2.0
  92.                 py = (py + by) / 2.0
  93.             CASE 2
  94.                 px = (px + cx) / 2.0
  95.                 py = (py + cy) / 2.0
  96.         END SELECT
  97.     NEXT
  98.     _DISPLAY
  99.  
  100. SUB pinetree (x, y)
  101.     'tannen baum by PeterMaria W  orig 440x460
  102.     bpx = 220: bpy = 410
  103.     tpx = bpx
  104.     FOR aa = -4 TO 4
  105.         bpxx = bpx + aa
  106.         bpyy = bpy - 390
  107.         LINE (x + bpxx, y + bpy)-(x + bpx, y + bpyy), _RGB32(30, 30, 0)
  108.     NEXT
  109.     ra = 160
  110.     tpy = bpy - 40
  111.     FOR ht = 1 TO 40
  112.         FOR xs = -100 TO 100 STEP 40
  113.             xsh = xs / 100
  114.             rs = RND * 4 / 10
  115.             tpxx = tpx + (xsh * ra)
  116.             tpyy = tpy - rs * ra
  117.             LINE (x + tpx, y + tpy)-(x + tpxx, y + tpyy), _RGB32(50, 40, 20)
  118.             FOR aa = 1 TO 30
  119.                 fra = RND * 10 / 10 * ra
  120.                 x1 = tpx + (xsh * fra)
  121.                 y1 = tpy - rs * fra
  122.                 x2 = tpx + xsh * (fra + ra / 5)
  123.                 y2 = tpy - rs * fra + (-rs + (RND * 8) / 10 - 0.4) * (ra / 5)
  124.                 LINE (x + x1, y + y1)-(x + x2, y + y2), _RGB32(RND * 120, RND * 70 + 70, RND * 80)
  125.             NEXT
  126.         NEXT
  127.         ra = ra - 4
  128.         tpy = tpy - 9
  129.     NEXT
  130.     _DISPLAY
  131.  
  132. 'from Steve Gold standard
  133. SUB fcirc (CX AS INTEGER, CY AS INTEGER, R AS INTEGER, C AS _UNSIGNED LONG)
  134.     DIM Radius AS INTEGER, RadiusError AS INTEGER
  135.     DIM X AS INTEGER, Y AS INTEGER
  136.     Radius = ABS(R): RadiusError = -Radius: X = Radius: Y = 0
  137.     IF Radius = 0 THEN PSET (CX, CY), C: EXIT SUB
  138.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  139.     WHILE X > Y
  140.         RadiusError = RadiusError + Y * 2 + 1
  141.         IF RadiusError >= 0 THEN
  142.             IF X <> Y + 1 THEN
  143.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  144.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  145.             END IF
  146.             X = X - 1
  147.             RadiusError = RadiusError - X * 2
  148.         END IF
  149.         Y = Y + 1
  150.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  151.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  152.     WEND
  153.  
  154. SUB Text (x, y, textHeight, K AS _UNSIGNED LONG, txt$)
  155.     DIM fg AS _UNSIGNED LONG, cur&, I&, multi, xlen
  156.     fg = _DEFAULTCOLOR
  157.     'screen snapshot
  158.     cur& = _DEST
  159.     I& = _NEWIMAGE(8 * LEN(txt$), 16, 32)
  160.     _DEST I&
  161.     COLOR K, _RGBA32(0, 0, 0, 0)
  162.     _PRINTSTRING (0, 0), txt$
  163.     multi = textHeight / 16
  164.     xlen = LEN(txt$) * 8 * multi
  165.     _PUTIMAGE (x, y)-STEP(xlen, textHeight), I&, cur&
  166.     COLOR fg
  167.     _FREEIMAGE I&
  168.  
  169.  

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #85 on: December 18, 2020, 02:57:30 am »
Very nice work, @Steve. Your program is also IDE destructor :) - goto to row with DATA and press "end" key on the keyboard :) That's why I cut long strings to 450 characters per line to do our IDE well.

@Nice work, BPlus! Better tree than mine :)
« Last Edit: December 18, 2020, 03:04:52 am by Petr »

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #86 on: December 18, 2020, 08:13:09 am »
Nice one, @bplus

I was looking for an Christmas oldie to post too, but I think it was on my HD that died.  Anyone happen to save that old school Jingle Bell piano thing I posted a few years ago on the [abandoned, outdated and now likely malicious qb64 dot net website - don’t go there] forum?  It played jingle bells while showing the piano keyboard playing the notes, and displayed the words. 

- Dav

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #87 on: December 18, 2020, 09:31:05 am »
Nice work guys, I hope I can contribute something when I get some free time soon.

An honorable mention is TheBOB's christmas train.  It would get posted on TQBF every year as a tradition, would be nice if someone can pull it up and/or translate it to QB64. (@Pete ?)

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #88 on: December 18, 2020, 11:09:21 am »
Nice work guys, I hope I can contribute something when I get some free time soon.

An honorable mention is TheBOB's christmas train.  It would get posted on TQBF every year as a tradition, would be nice if someone can pull it up and/or translate it to QB64. (@Pete ?)

Here 'tis: https://www.qb64.org/forum/index.php?topic=820.msg100360#msg100360

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: 🎄🎁✨ Holiday Season - are you ready to code?
« Reply #89 on: December 18, 2020, 11:20:58 am »
Here is the Sanctum Winter Wonderland update. Let it load, and walk forward. Look all around, find the snowmen and the christmas trees. When you're bored with that, you can wander some more: through the desert, up in space, inside a planet -- or you may go down, down, down all the way to hell.

Code: QB64: [Select]
  1. ' Version 2020-12-18
  2.  
  3.  
  4. _TITLE "Sanctum"
  5.  
  6. ' Hardware.
  7. 'SCREEN _NEWIMAGE(640, 480, 32)
  8. SCREEN _NEWIMAGE(800, 600, 32)
  9. 'SCREEN _NEWIMAGE(1024, 768, 32)
  10.  
  11. ' Meta
  12.  
  13. ' Color constants.
  14. CONST Aquamarine = _RGB32(127, 255, 212)
  15. CONST Black = _RGB32(0, 0, 0)
  16. CONST Blue = _RGB32(0, 0, 255)
  17. CONST BlueViolet = _RGB32(138, 43, 226)
  18. CONST Chocolate = _RGB32(210, 105, 30)
  19. CONST Cyan = _RGB32(0, 255, 255)
  20. CONST DarkBlue = _RGB32(0, 0, 139)
  21. CONST DarkGoldenRod = _RGB32(184, 134, 11)
  22. CONST DarkGray = _RGB32(169, 169, 169)
  23. CONST DarkKhaki = _RGB32(189, 183, 107)
  24. CONST DeepPink = _RGB32(255, 20, 147)
  25. CONST DodgerBlue = _RGB32(30, 144, 255)
  26. CONST ForestGreen = _RGB32(34, 139, 34)
  27. CONST Gray = _RGB32(128, 128, 128)
  28. CONST Green = _RGB32(0, 128, 0)
  29. CONST Indigo = _RGB32(75, 0, 130)
  30. CONST Ivory = _RGB32(255, 255, 240)
  31. CONST LightSeaGreen = _RGB32(32, 178, 170)
  32. CONST Lime = _RGB32(0, 255, 0)
  33. CONST LimeGreen = _RGB32(50, 205, 50)
  34. CONST Magenta = _RGB32(255, 0, 255)
  35. CONST PaleGoldenRod = _RGB32(238, 232, 170)
  36. CONST Purple = _RGB32(128, 0, 128)
  37. CONST Red = _RGB32(255, 0, 0)
  38. CONST RoyalBlue = _RGB32(65, 105, 225)
  39. CONST SaddleBrown = _RGB32(139, 69, 19)
  40. CONST Sienna = _RGB32(160, 82, 45)
  41. CONST SlateGray = _RGB32(112, 128, 144)
  42. CONST Snow = _RGB32(255, 250, 250)
  43. CONST Sunglow = _RGB32(255, 207, 72)
  44. CONST SunsetOrange = _RGB32(253, 94, 83)
  45. CONST Teal = _RGB32(0, 128, 128)
  46. CONST White = _RGB32(255, 255, 255)
  47. CONST Yellow = _RGB32(255, 255, 0)
  48.  
  49. ' Mathematical constants.
  50. CONST pi = 4 * ATN(1)
  51. CONST ee = EXP(1)
  52.  
  53. ' Types.
  54.  
  55. TYPE Vector
  56.     x AS DOUBLE
  57.     y AS DOUBLE
  58.     z AS DOUBLE
  59.  
  60. TYPE Camera
  61.     Position AS Vector
  62.     Velocity AS Vector
  63.     Shade AS _UNSIGNED LONG
  64.  
  65. TYPE ClusterElement
  66.     Index AS LONG
  67.     FirstGroup AS LONG
  68.     LastGroup AS LONG
  69.     Centroid AS Vector
  70.     Velocity AS Vector
  71.     Visible AS INTEGER
  72.     MotionType AS INTEGER
  73.  
  74. TYPE GroupElement
  75.     Identity AS LONG
  76.     GroupName AS STRING
  77.     Pointer AS LONG
  78.     Lagger AS LONG
  79.     Volume AS Vector
  80.     FirstVector AS LONG
  81.     LastVector AS LONG
  82.     Centroid AS Vector
  83.     Velocity AS Vector
  84.     Visible AS INTEGER
  85.     Distance2 AS DOUBLE
  86.  
  87. TYPE MissionGroup
  88.     Label AS STRING
  89.     Discovered AS INTEGER
  90.  
  91. ' Scale.
  92. DIM SHARED bignumber AS LONG
  93. bignumber = 6000000
  94.  
  95. ' Cluster setup.
  96. DIM SHARED ClusterIndexTicker AS LONG
  97. DIM SHARED ClusterFillCounter AS INTEGER
  98. DIM SHARED Cluster(bignumber / 100) AS ClusterElement
  99. ClusterIndexTicker = 0
  100. ClusterFillCounter = 0
  101.  
  102. ' Group linked list setup.
  103. DIM SHARED Group(bignumber / 10) AS GroupElement
  104. DIM SHARED GroupIdTicker AS LONG
  105. GroupIdTicker = 0
  106.  
  107. ' Path
  108. DIM SHARED FixedPath(25, 86400) AS Vector
  109. DIM SHARED PathIndexTicker AS LONG
  110. PathIndexTicker = 0
  111.  
  112. ' Basis vectors defined in three-space.
  113. DIM SHARED xhat(3), yhat(3), zhat(3)
  114. xhat(1) = 1: xhat(2) = 0: xhat(3) = 0
  115. yhat(1) = 0: yhat(2) = 1: yhat(3) = 0
  116. zhat(1) = 0: zhat(2) = 0: zhat(3) = 1
  117.  
  118. ' Camera orientation vectors.
  119. DIM SHARED uhat(3), vhat(3), nhat(3)
  120.  
  121. ' Camera position.
  122. DIM SHARED PlayerCamera AS Camera
  123.  
  124. ' Field-of-view distance.
  125. DIM SHARED fovd
  126. fovd = -192
  127.  
  128. ' Clipping planes.
  129. DIM SHARED nearplane(4), farplane(4), rightplane(4), leftplane(4), topplane(4), bottomplane(4)
  130. nearplane(4) = 1
  131. farplane(4) = -180
  132. rightplane(4) = -17 ' (17=35/2=tilesize/2 looks better than 1.)
  133. leftplane(4) = -17
  134. topplane(4) = -17
  135. bottomplane(4) = -17
  136.  
  137. ' World vectors.
  138. DIM SHARED vec(bignumber, 3) ' Relative Position
  139. DIM SHARED vec3Dpos(bignumber, 3) ' Absolute osition
  140. DIM SHARED vec3Dvel(bignumber, 3) ' Linear velocity
  141. DIM SHARED vec3Dvis(bignumber) ' Visible toggle
  142. DIM SHARED vec2D(bignumber, 2) ' Projection onto 2D plane
  143. DIM SHARED vec3Dcolor(bignumber) AS LONG ' Original color
  144. DIM SHARED vec2Dcolor(bignumber) AS LONG ' Projected color
  145.  
  146. ' Mission.
  147. DIM SHARED MissionTicker AS INTEGER
  148. DIM SHARED Mission(20) AS MissionGroup
  149.  
  150. ' Interface.
  151. DIM SHARED ToggleHUD AS INTEGER
  152. DIM SHARED ToggleAnimate AS INTEGER
  153. DIM SHARED FPSReport AS INTEGER
  154. DIM SHARED NumClusterVisible AS LONG
  155. DIM SHARED NumVectorVisible AS LONG
  156. DIM SHARED NumGroupVisible AS LONG
  157. DIM SHARED ClosestGroup AS LONG
  158.  
  159. ' Initialize.
  160. ToggleAnimate = 1
  161. ToggleHUD = 1
  162. PlayerCamera.Position.x = -40
  163. PlayerCamera.Position.y = 500 '30
  164. PlayerCamera.Position.z = 40
  165. uhat(1) = COS(0): uhat(2) = SIN(0): uhat(3) = 0
  166. vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
  167. CALL CalculateScreenVectors
  168.  
  169. ' Prime main loop.
  170. CALL CreateLevel
  171. CALL CreateMission(10)
  172.  
  173. ' Start main loop.
  174. CALL MainLoop
  175.  
  176.  
  177. ' Subs and Functions
  178.  
  179. SUB CreateMission (ListSize AS INTEGER)
  180.     DIM k AS INTEGER
  181.     DIM a AS STRING
  182.     DIM b AS STRING
  183.     a = ""
  184.     b = ""
  185.     REDIM MissionPool(0) AS STRING
  186.  
  187.     '''
  188.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Atmospheric dust"
  189.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Black hole"
  190.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Cloudy sky"
  191.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Comet 1"
  192.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Comet 2"
  193.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Comet 3"
  194.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Demon orb"
  195.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Dirt and sand"
  196.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Grave"
  197.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Hell sparks"
  198.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Laser"
  199.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Lush terrain"
  200.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Tornado 1"
  201.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Tornado 2"
  202.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Tornado 3"
  203.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Planet center"
  204.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Planet column"
  205.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Planet interior"
  206.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Plasma core"
  207.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Pyramid"
  208.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Rain"
  209.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Singularity"
  210.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Snowman 1"
  211.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Snowman 2"
  212.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Snowman 3"
  213.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Tree 1"
  214.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Tree 2"
  215.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Tree 3"
  216.  
  217.     '''
  218.  
  219.     MissionTicker = 0
  220.     DO
  221.         k = 1 + INT(RND * UBOUND(MissionPool))
  222.         a = MissionPool(k)
  223.         IF (INSTR(b, a) = 0) THEN
  224.             b = b + a
  225.             MissionTicker = MissionTicker + 1
  226.             Mission(MissionTicker).Label = a
  227.             Mission(MissionTicker).Discovered = 0
  228.         END IF
  229.     LOOP UNTIL (MissionTicker = ListSize)
  230.  
  231. SUB CreateLevel
  232.     DIM gtmp AS LONG
  233.     DIM p AS LONG
  234.     DIM n AS SINGLE
  235.     DIM i, j, k AS SINGLE
  236.     DIM u, v, w AS SINGLE
  237.     DIM x0, y0, z0 AS SINGLE
  238.  
  239.     ' Initialize linked list.
  240.     gtmp = CreateNewGroup&(0, 0, 0, 0, 0, 1)
  241.  
  242.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  243.  
  244.     ' Atmospheric dust
  245.     FOR u = -1400 TO 1400 - 350 STEP 350
  246.         FOR v = -1400 TO 1400 - 350 STEP 350
  247.             FOR i = u TO u + 350 STEP 35
  248.                 FOR j = v TO v + 350 STEP 35
  249.                     FOR w = (100 + 35 / 2) TO (800 - 35 + 35 / 2) STEP 35
  250.                         gtmp = NewCube&(gtmp, "Atmospheric dust", 5 * (1 - w / 800), i, j, w, 35, 35, 35, DarkGray, White, Snow, -1)
  251.                     NEXT
  252.                 NEXT
  253.             NEXT
  254.             CALL ClusterPinch(gtmp)
  255.         NEXT
  256.     NEXT
  257.     CALL ClusterPinch(gtmp)
  258.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.     'GOTO moo
  270.  
  271.  
  272.  
  273.  
  274.  
  275.  
  276.     ' Black hole
  277.     k = RND * 2 * pi
  278.     x0 = 1000 * COS(k)
  279.     y0 = 1000 * SIN(k)
  280.     z0 = 300
  281.     i = 100
  282.     j = 25
  283.     FOR u = -i TO i STEP j
  284.         FOR v = -i TO i STEP j
  285.             FOR w = -i TO i STEP j
  286.                 k = SQR(u * u + v * v + w * w)
  287.                 IF k <= i AND k >= i - j THEN
  288.                     gtmp = NewCube&(gtmp, "Black hole", j, x0 + u, y0 + v, z0 + w, j, j, j, DarkGray, White, Snow, -1)
  289.                     CALL SetParticleVelocity(gtmp, -.25 * u / k, -.25 * v / k, -.25 * w / k)
  290.                 END IF
  291.             NEXT
  292.         NEXT
  293.     NEXT
  294.     gtmp = NewShell&("Singularity", 10, x0, y0, z0, Black, Black, Black, 2, 5, -1)
  295.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  296.  
  297.     ' Comet
  298.     FOR n = 1 TO 3
  299.         u = RND * 2 * pi
  300.         PathIndexTicker = PathIndexTicker + 1
  301.         FOR p = 1 TO 86400
  302.             FixedPath(PathIndexTicker, p).x = .25 * COS(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  303.             FixedPath(PathIndexTicker, p).y = .25 * SIN(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  304.             FixedPath(PathIndexTicker, p).z = .15 * COS(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  305.         NEXT
  306.         x0 = 150 + (RND - .5) * 2 * 550
  307.         y0 = 150 + (RND - .5) * 2 * 550
  308.         z0 = 200 + RND * 100
  309.         gtmp = NewCube&(gtmp, "Comet" + STR$(n), 300, x0, y0, z0, 25, 25, 25, Teal, Cyan, DodgerBlue, PathIndexTicker)
  310.         FOR k = Group(gtmp).FirstVector TO Group(gtmp).LastVector
  311.             vec3Dvel(k, 1) = (RND - .5) * .20
  312.             vec3Dvel(k, 2) = (RND - .5) * .20
  313.             vec3Dvel(k, 3) = (RND - .5) * .20
  314.         NEXT
  315.         gtmp = NewShell&("Comet" + STR$(n), 15, x0, y0, z0, Teal, Cyan, DodgerBlue, 1, 80, PathIndexTicker)
  316.         CALL ClusterPinch(gtmp)
  317.     NEXT
  318.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  319.  
  320.     ' Demon orb
  321.     FOR n = 1 TO 6
  322.         u = RND * 2 * pi
  323.         PathIndexTicker = PathIndexTicker + 1
  324.         FOR p = 1 TO 86400
  325.             FixedPath(PathIndexTicker, p).x = .25 * COS(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  326.             FixedPath(PathIndexTicker, p).y = .25 * SIN(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  327.             FixedPath(PathIndexTicker, p).z = .15 * COS(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  328.         NEXT
  329.         x0 = (RND - .5) * 2 * 1000
  330.         y0 = (RND - .5) * 2 * 1000
  331.         z0 = -700 - RND * 100
  332.         gtmp = NewCube&(gtmp, "Demon orb", 300, x0, y0, z0, 25, 25, 25, Red, SunsetOrange, Sunglow, PathIndexTicker)
  333.         FOR k = Group(gtmp).FirstVector TO Group(gtmp).LastVector
  334.             vec3Dvel(k, 1) = (RND - .5) * .20
  335.             vec3Dvel(k, 2) = (RND - .5) * .20
  336.             vec3Dvel(k, 3) = (RND - .5) * .20
  337.         NEXT
  338.         gtmp = NewShell&("Demon orb", 15, x0, y0, z0, Red, SunsetOrange, Sunglow, 1, 80, PathIndexTicker)
  339.         CALL ClusterPinch(gtmp)
  340.     NEXT
  341.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  342.  
  343.     ' Death Star
  344.     PathIndexTicker = PathIndexTicker + 1
  345.     FOR p = 1 TO 86400
  346.         FixedPath(PathIndexTicker, p).x = -.1 * COS(2 * pi * (24 * 60) * (p - 1) / 86400)
  347.         FixedPath(PathIndexTicker, p).y = -.1 * SIN(2 * pi * (24 * 60) * (p - 1) / 86400)
  348.         FixedPath(PathIndexTicker, p).z = 0
  349.     NEXT
  350.     x0 = 100 + (RND - .5) * 2 * 250
  351.     y0 = 100 + (RND - .5) * 2 * 250
  352.     gtmp = NewShell&("Death Star", 75, x0, y0, 400, Gray, DarkGray, White, 0, 0, PathIndexTicker)
  353.     gtmp = NewShell&("Death Star reactor", 15, x0, y0, 400, Cyan, Teal, Blue, 1, 20, PathIndexTicker)
  354.     gtmp = NewCube&(gtmp, "Plasma core", 300, x0, y0, 400, 25, 25, 25, Cyan, Teal, Blue, PathIndexTicker)
  355.     FOR p = Group(gtmp).FirstVector TO Group(gtmp).LastVector
  356.         vec3Dvel(p, 1) = (RND - .5) * .20
  357.         vec3Dvel(p, 2) = (RND - .5) * .20
  358.         vec3Dvel(p, 3) = (RND - .5) * .20
  359.     NEXT
  360.     FOR n = 10 TO 1 STEP -.5
  361.         gtmp = NewCube&(gtmp, "Laser", 300, x0 + 75 / SQR(2) + (n - 1) * 25, y0 + .5 * n * 25, 400 + 75 / SQR(2) + .5 * (n - 1) * 25, 25, 25, 25, Lime, Green, White, PathIndexTicker)
  362.         FOR p = Group(gtmp).FirstVector TO Group(gtmp).LastVector
  363.             vec3Dvel(p, 1) = (RND - .5) * .50
  364.             vec3Dvel(p, 2) = (RND - .5) * .50
  365.             vec3Dvel(p, 3) = (RND - .5) * .50
  366.         NEXT
  367.     NEXT
  368.     CALL ClusterPinch(gtmp)
  369.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  370.  
  371.     ' Dirt and sand
  372.     FOR u = -1400 TO 1400 - 350 STEP 350
  373.         FOR v = -1400 TO 1400 - 350 STEP 350
  374.             FOR i = u TO u + 350 STEP 35
  375.                 FOR j = v TO v + 350 STEP 35
  376.                     FOR w = 0 - 35 / 2 TO -770 - 35 / 2 STEP -35
  377.                         gtmp = NewCube&(gtmp, "Dirt and sand", 5, i, j, w, 35, 35, 35, SaddleBrown, DarkKhaki, Sienna, -1)
  378.                     NEXT
  379.                 NEXT
  380.             NEXT
  381.             CALL ClusterPinch(gtmp)
  382.         NEXT
  383.     NEXT
  384.     CALL ClusterPinch(gtmp)
  385.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  386.  
  387.     ' Grave and Pyramid (1/2)
  388.     k = RND * pi / 2 - pi / 4
  389.     x0 = 1000 * COS(k)
  390.     y0 = 1000 * SIN(k)
  391.     FOR u = x0 - 160 TO x0 + 160 STEP 80
  392.         FOR v = y0 - 160 TO y0 + 160 STEP 80
  393.             gtmp = NewCube&(gtmp, "Grave", 200, u, v, (1) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  394.             gtmp = NewCube&(gtmp, "Grave", 200, u, v, (2) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  395.             gtmp = NewCube&(gtmp, "Grave", 200, u, v, (3) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  396.             gtmp = NewCube&(gtmp, "Grave", 200, u - 10, v, (4) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  397.             gtmp = NewCube&(gtmp, "Grave", 200, u, v, (4) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  398.             gtmp = NewCube&(gtmp, "Grave", 200, u + 10, v, (4) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  399.             gtmp = NewCube&(gtmp, "Grave", 200, u, v, (5) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  400.         NEXT
  401.     NEXT
  402.     CALL ClusterPinch(gtmp)
  403.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  404.  
  405.     ' Grave and Pyramid (2/2)
  406.     k = k + pi
  407.     x0 = 1000 * COS(k)
  408.     y0 = 1000 * SIN(k)
  409.     FOR w = 0 TO 75 STEP 15
  410.         FOR u = -75 + w TO 75 - w STEP 15
  411.             FOR v = -75 + w TO 75 - w STEP 15
  412.                 gtmp = NewCube&(gtmp, "Pyramid", 35, x0 + u, y0 + v, w, 13, 13, 13, DarkKhaki, SunsetOrange, DarkGoldenRod, -1)
  413.             NEXT
  414.         NEXT
  415.     NEXT
  416.     CALL ClusterPinch(gtmp)
  417.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  418.  
  419.     ' Hell Sparks
  420.     FOR u = -1400 TO 1400 - 350 STEP 350
  421.         FOR v = -1400 TO 1400 - 350 STEP 350
  422.             FOR i = u TO u + 350 STEP 35
  423.                 FOR j = v TO v + 350 STEP 35
  424.                     gtmp = NewCube&(gtmp, "Hell sparks", 15, i, j, -850, 35, 35, 100, Red, SunsetOrange, Sunglow, -1)
  425.                     CALL SetParticleVelocity(gtmp, 0, 0, .75 + RND * .25)
  426.                 NEXT
  427.             NEXT
  428.         NEXT
  429.     NEXT
  430.     CALL ClusterPinch(gtmp)
  431.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  432.  
  433.     ' Lake of Fire
  434.     gtmp = NewTerrain&("Lake of Fire", 2800, 2800, 35, 0, 0, -900, Red, SunsetOrange, Sunglow, 4, 30)
  435.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  436.  
  437.     ' Planet
  438.     PathIndexTicker = PathIndexTicker + 1
  439.     FOR p = 1 TO 86400
  440.         FixedPath(PathIndexTicker, p).x = .1 * COS(2 * pi * (24 * 60) * (p - 1) / 86400)
  441.         FixedPath(PathIndexTicker, p).y = .1 * SIN(2 * pi * (24 * 60) * (p - 1) / 86400)
  442.         FixedPath(PathIndexTicker, p).z = 0
  443.     NEXT
  444.     x0 = 100 + (RND - .5) * 2 * 250
  445.     y0 = 100 + (RND - .5) * 2 * 250
  446.     gtmp = NewPlanet&(350, 35, x0, y0, 900, PathIndexTicker)
  447.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  448.  
  449.     ' Rain
  450.     FOR u = -350 TO 350 STEP 35
  451.         FOR v = -350 TO 350 STEP 35
  452.             IF (u * u + v * v <= 350 ^ 2) THEN
  453.                 gtmp = NewCube&(gtmp, "Rain", 15, u, v, 50, 35, 35, 100, Blue, DodgerBlue, Blue, -1)
  454.                 CALL SetParticleVelocity(gtmp, 0, 0, -.75 - RND * .25)
  455.             END IF
  456.         NEXT
  457.     NEXT
  458.     CALL ClusterPinch(gtmp)
  459.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  460.  
  461.     ' Realm of Heaven
  462.     gtmp = NewTerrain&("Realm of Heaven", 2800, 2800, 35, 0, 0, 1400, BlueViolet, Cyan, White, 4, 30)
  463.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  464.  
  465.     ' Sky and Terrain: Center
  466.     gtmp = NewTerrain&("Cloudy sky", 1400, 1400, 35, 0, 0, 100, RoyalBlue, DarkGray, Snow, 4, 30)
  467.     gtmp = NewTerrain&("Lush terrain", 1400, 1400, 35, 0, 0, 0, Green, LightSeaGreen, Blue, 4, 30)
  468.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  469.  
  470.     ' Sky and Terrain: North
  471.     gtmp = NewTerrain&("Northern winter sky", 2800, 700, 35, 0, 700 + 350, 100, White, DarkBlue, SlateGray, 4, 30)
  472.     gtmp = NewTerrain&("Northern winter terrain", 2800, 700, 35, 0, 700 + 350, 0, White, Snow, SlateGray, 4, 30)
  473.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  474.  
  475.     ' Sky and Terrain: South
  476.     gtmp = NewTerrain&("Southern desert sky", 2800, 700, 35, 0, -700 - 350, 100, RoyalBlue, SunsetOrange, Indigo, 4, 30)
  477.     gtmp = NewTerrain&("Southern desert terrain", 2800, 700, 35, 0, -700 - 350, 0, DarkKhaki, PaleGoldenRod, Sunglow, 4, 30)
  478.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  479.  
  480.     ' Sky and Terrain: West
  481.     gtmp = NewTerrain&("Western desert sky", 700, 1400, 35, -700 - 350, 0, 100, RoyalBlue, DarkGray, Indigo, 4, 30)
  482.     gtmp = NewTerrain&("Western desert terrain", 700, 1400, 35, -700 - 350, 0, 0, DarkKhaki, PaleGoldenRod, Sunglow, 4, 30)
  483.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  484.  
  485.     ' Sky and Terrain: East
  486.     gtmp = NewTerrain&("Eastern desert sky", 700, 1400, 35, 700 + 350, 0, 100, RoyalBlue, DarkGray, Indigo, 4, 30)
  487.     gtmp = NewTerrain&("Eastern desert terrain", 700, 1400, 35, 700 + 350, 0, 0, DarkKhaki, PaleGoldenRod, Sunglow, 4, 30)
  488.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  489.  
  490.     ' Snow
  491.     FOR u = -175 TO 175 STEP 35
  492.         FOR v = -175 TO 175 STEP 35
  493.             IF (u * u + v * v <= 175 ^ 2) THEN
  494.                 gtmp = NewCube&(gtmp, "Snow", 15, u, v + 1050, 50, 35, 35, 100, White, Snow, SlateGray, -1)
  495.                 CALL SetParticleVelocity(gtmp, 0, 0, -.75 - RND * .25)
  496.             END IF
  497.         NEXT
  498.     NEXT
  499.     CALL ClusterPinch(gtmp)
  500.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  501.  
  502.     ' Snowman
  503.     FOR j = 1 TO 10
  504.         u = 5 + RND * 20
  505.         v = 0
  506.         x0 = RND * 2800
  507.         y0 = RND * 700 + 700
  508.         FOR n = 1 TO 3
  509.             z0 = u + v
  510.             gtmp = NewCube&(gtmp, "Snowman" + STR$(n), 300, x0, y0, z0, u * 2 / SQR(3), u * 2 / SQR(3), u * 2 / SQR(3), Teal, Cyan, DodgerBlue, -1)
  511.             FOR k = Group(gtmp).FirstVector TO Group(gtmp).LastVector
  512.                 vec3Dvel(k, 1) = (RND - .5) * .20
  513.                 vec3Dvel(k, 2) = (RND - .5) * .20
  514.                 vec3Dvel(k, 3) = (RND - .5) * .20
  515.             NEXT
  516.             gtmp = NewShell&("Snowman" + STR$(n), u, x0, y0, z0, Teal, Cyan, DodgerBlue, 1, 80, -1)
  517.             CALL ClusterPinch(gtmp)
  518.             v = v + 2 * u
  519.             u = u / 1.618
  520.         NEXT
  521.     NEXT
  522.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  523.  
  524.     ' Tornado
  525.     FOR n = 1 TO 3
  526.         u = RND * 2 * pi
  527.         PathIndexTicker = PathIndexTicker + 1
  528.         FOR p = 1 TO 86400
  529.             FixedPath(PathIndexTicker, p).x = 1 * COS(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  530.             FixedPath(PathIndexTicker, p).y = 1 * SIN(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  531.             FixedPath(PathIndexTicker, p).z = 0
  532.         NEXT
  533.         x0 = (RND - .5) * 2 * 750
  534.         y0 = (RND - .5) * 2 * 750
  535.         FOR k = 1 TO 30
  536.             u = RND * 100
  537.             v = RND * u / 3
  538.             w = RND * 2 * pi
  539.             gtmp = NewCube&(gtmp, "Tornado" + STR$(n), 35, x0 + v * COS(w), y0 + v * SIN(w), u, 15, 15, 15, DarkGray, SunsetOrange, DarkGoldenRod, PathIndexTicker)
  540.             CALL SetParticleVelocity(gtmp, -SIN(w), COS(w), 0)
  541.         NEXT
  542.         CALL ClusterPinch(gtmp)
  543.     NEXT
  544.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  545.  
  546.     ' Tree
  547.     FOR n = 1 TO 10
  548.         v = 30 + RND * 60
  549.         u = v / 10
  550.         w = u
  551.         x0 = RND * 2800
  552.         y0 = RND * 700 + 700
  553.         z0 = u / 2
  554.         FOR j = 0 TO v STEP u
  555.             gtmp = NewCube&(gtmp, "Tree" + STR$(n), 5 * (v - j), x0, y0, z0 + j, w, w, u, Chocolate, SaddleBrown, Gray, -1)
  556.             w = w * .9
  557.             CALL ClusterPinch(gtmp)
  558.         NEXT
  559.         w = v / 1.618
  560.         FOR j = u TO (v + 3 * u) STEP u
  561.             gtmp = NewCube&(gtmp, "Tree" + STR$(n), 5 * (v - j), x0, y0, z0 + j, w, w, u, Lime, Green, Teal, -1)
  562.             w = w * .8
  563.             CALL ClusterPinch(gtmp)
  564.         NEXT
  565.         z0 = u / 2 + v
  566.         gtmp = NewCube&(gtmp, "Tree" + STR$(n), 100, x0, y0, z0, u, u, u, White, Yellow, Teal, -1)
  567.         FOR k = Group(gtmp).FirstVector TO Group(gtmp).LastVector
  568.             vec3Dvel(k, 1) = (RND - .5) * .20
  569.             vec3Dvel(k, 2) = (RND - .5) * .20
  570.             vec3Dvel(k, 3) = (RND - .5) * .20
  571.         NEXT
  572.         CALL ClusterPinch(gtmp)
  573.     NEXT
  574.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  575.  
  576.     ' Stellar dust
  577.     FOR u = -1400 TO 1400 - 350 STEP 350
  578.         FOR v = -1400 TO 1400 - 350 STEP 350
  579.             FOR i = u TO u + 350 STEP 35
  580.                 FOR j = v TO v + 350 STEP 35
  581.                     gtmp = NewCube&(gtmp, "Stellar dust", 15, i, j, 1350, 35, 35, 100, BlueViolet, Cyan, White, -1)
  582.                     CALL SetParticleVelocity(gtmp, 0, 0, -.75 - RND * .25)
  583.                 NEXT
  584.             NEXT
  585.         NEXT
  586.     NEXT
  587.     CALL ClusterPinch(gtmp)
  588.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  589.  
  590.     ''' Rocket
  591.     gtmp = NewCube&(gtmp, "Rocket", 200, 0, 0, 0, 15, 15, 35, Green, Green, Green, 0)
  592.     gtmp = NewCube&(gtmp, "Rocket", 200, 0, 0, 0, 15, 15, 15, Red, Red, Red, 0)
  593.     Cluster(ClusterIndexTicker).Velocity.x = 0
  594.     Cluster(ClusterIndexTicker).Velocity.y = 0
  595.     Cluster(ClusterIndexTicker).Velocity.z = 1
  596.     CALL ClusterPinch(gtmp)
  597.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  598.  
  599.     moo:
  600.  
  601.     'PRINT
  602.     'PRINT "Total clusters:  " + STR$(ClusterIndexTicker)
  603.     'PRINT "Total groups:    " + STR$(GroupIdTicker)
  604.     'PRINT "Total particles: " + STR$(Group(gtmp).LastVector)
  605.     '_DISPLAY
  606.     'SLEEP 3
  607.  
  608.  
  609. SUB MainLoop
  610.     DIM fps AS INTEGER
  611.     DIM fpstimer AS INTEGER
  612.     DIM tt AS INTEGER
  613.     fps = 0
  614.     fpstimer = INT(TIMER)
  615.     CALL CalculateScreenVectors
  616.     DO
  617.         CALL PlayerDynamics
  618.         CALL ComputeVisibleScene
  619.         CALL PlotWorld
  620.         CALL DisplayHUD
  621.         CALL KeyProcess
  622.  
  623.         fps = fps + 1
  624.         tt = INT(TIMER)
  625.         IF (tt = fpstimer + 1) THEN
  626.             fpstimer = tt
  627.             FPSReport = fps
  628.             fps = 0
  629.         END IF
  630.  
  631.         _DISPLAY
  632.         _LIMIT 30
  633.     LOOP
  634.  
  635. ' Elevated-order primitive group.
  636.  
  637. FUNCTION NewPlanet& (TheRadius AS DOUBLE, ChunkSize AS DOUBLE, PosX AS DOUBLE, PosY AS DOUBLE, PosZ AS DOUBLE, TheDynamic AS INTEGER)
  638.     DIM ShadeA AS _UNSIGNED LONG
  639.     DIM ShadeB AS _UNSIGNED LONG
  640.     DIM ShadeC AS _UNSIGNED LONG
  641.     DIM gtmp AS LONG
  642.     DIM rtemp AS DOUBLE
  643.     DIM u AS DOUBLE
  644.     DIM v AS DOUBLE
  645.     DIM w AS DOUBLE
  646.     DIM r AS DOUBLE
  647.     FOR rtemp = ChunkSize TO TheRadius STEP ChunkSize
  648.         FOR u = -rtemp TO rtemp STEP ChunkSize
  649.             FOR v = -rtemp TO rtemp STEP ChunkSize
  650.                 FOR w = -rtemp TO rtemp STEP ChunkSize
  651.                     r = SQR(u * u + v * v + w * w)
  652.                     IF r <= rtemp AND r >= rtemp - ChunkSize THEN
  653.                         ShadeA = ShadeMix~&(Red, SaddleBrown, r / TheRadius)
  654.                         ShadeB = ShadeMix~&(SunsetOrange, DarkKhaki, r / TheRadius)
  655.                         ShadeC = DarkKhaki
  656.                         gtmp = NewCube&(1, "Planet Interior", ChunkSize, PosX + u, PosY + v, PosZ + w, ChunkSize, ChunkSize, ChunkSize, ShadeA, ShadeB, ShadeC, TheDynamic)
  657.                         IF (r < TheRadius / 2.5) THEN
  658.                             Group(gtmp).GroupName = "Planet center"
  659.                             CALL SetParticleVelocity(gtmp, .25 * u / r, .25 * v / r, .25 * w / r)
  660.                         END IF
  661.                     END IF
  662.                 NEXT
  663.             NEXT
  664.         NEXT
  665.         CALL ClusterPinch(gtmp)
  666.     NEXT
  667.     CALL ClusterPinch(gtmp)
  668.     gtmp = NewShell&("Planet surface", TheRadius, PosX, PosY, PosZ, Green, DarkKhaki, DodgerBlue, 2, 20, TheDynamic)
  669.     gtmp = NewShell&("Planet core", TheRadius / 10, PosX, PosY, PosZ, Red, Yellow, DarkGoldenRod, 2, 5, TheDynamic)
  670.     FOR w = 0 + ChunkSize / 4 TO PosZ - TheRadius - ChunkSize / 4 STEP ChunkSize / 2
  671.         gtmp = NewCube&(1, "Planet column", 2 * ChunkSize, PosX, PosY, w, ChunkSize / 2, ChunkSize / 2, ChunkSize / 2, Cyan, Teal, Blue, TheDynamic)
  672.         CALL SetParticleVelocity(gtmp, .15 * (RND - .5), .15 * (RND - .5), .25 * RND)
  673.     NEXT
  674.     CALL ClusterPinch(gtmp)
  675.     NewPlanet& = gtmp
  676.  
  677. ' Medium-order primitive group(s).
  678.  
  679. FUNCTION NewTerrain& (TheName AS STRING, SizeX AS DOUBLE, SizeY AS DOUBLE, TheResolution AS DOUBLE, PosX AS DOUBLE, PosY AS DOUBLE, PosZ AS DOUBLE, ShadeA AS _UNSIGNED LONG, ShadeB AS _UNSIGNED LONG, ShadeC AS _UNSIGNED LONG, BumpFactor AS DOUBLE, SmoothFactor AS INTEGER)
  680.     DIM g AS LONG
  681.     DIM q AS LONG
  682.     DIM vindex AS LONG
  683.     DIM k AS INTEGER
  684.     DIM factor1 AS DOUBLE
  685.     DIM factor2 AS DOUBLE
  686.     DIM a AS DOUBLE
  687.     DIM b AS DOUBLE
  688.     DIM i AS DOUBLE
  689.     DIM j AS DOUBLE
  690.     DIM u AS DOUBLE
  691.     DIM v AS DOUBLE
  692.     DIM w AS DOUBLE
  693.     DIM ia AS DOUBLE
  694.     DIM jb AS DOUBLE
  695.     DIM rr AS DOUBLE
  696.     DIM wi AS INTEGER
  697.     DIM wj AS INTEGER
  698.     DIM wa AS INTEGER
  699.     DIM wb AS INTEGER
  700.     DIM ci AS INTEGER
  701.     DIM cj AS INTEGER
  702.     DIM ca AS INTEGER
  703.     DIM cb AS INTEGER
  704.  
  705.     factor1 = TheResolution
  706.     factor2 = factor1 / 10
  707.     wi = 0
  708.     wj = 0
  709.     wa = 0
  710.     wb = 0
  711.     FOR i = -SizeX / 2 + factor1 / 2 TO SizeX / 2 - factor1 / 2 STEP factor1
  712.         wi = wi + 1
  713.     NEXT
  714.     FOR j = -SizeY / 2 + factor1 / 2 TO SizeY / 2 - factor1 / 2 STEP factor1
  715.         wj = wj + 1
  716.     NEXT
  717.     FOR a = 0 TO factor1 - 0 * factor2 STEP factor2
  718.         wa = wa + 1
  719.     NEXT
  720.     FOR b = 0 TO factor1 - 0 * factor2 STEP factor2
  721.         wb = wb + 1
  722.     NEXT
  723.  
  724.     DIM vgrid(wi * wa, wj * wb, 2) AS DOUBLE
  725.     DIM vgrid2(wi * wa, wj * wb) AS DOUBLE
  726.     FOR ia = 1 TO wi * wa
  727.         FOR jb = 1 TO wj * wb
  728.             SELECT CASE (RND)
  729.                 CASE IS < .005
  730.                     vgrid(ia, jb, 1) = factor1 * RND
  731.                     vgrid(ia, jb, 2) = 1
  732.                 CASE IS > 1 - .005
  733.                     vgrid(ia, jb, 1) = -factor1 * RND / 2
  734.                     vgrid(ia, jb, 2) = 1
  735.                 CASE ELSE
  736.                     vgrid(ia, jb, 1) = 0
  737.                     vgrid(ia, jb, 2) = 0
  738.             END SELECT
  739.         NEXT
  740.     NEXT
  741.  
  742.     IF (SmoothFactor > 0) THEN
  743.         FOR k = 1 TO SmoothFactor
  744.             FOR ia = 1 TO wi * wa
  745.                 FOR jb = 1 TO wj * wb
  746.                     vgrid2(ia, jb) = vgrid(ia, jb, 1)
  747.                 NEXT
  748.             NEXT
  749.             FOR ia = 1 + 1 TO wi * wa - 1
  750.                 FOR jb = 1 + 1 TO wj * wb - 1
  751.                     IF (k = SmoothFactor - 5) THEN
  752.                         vgrid(ia, jb, 2) = 0
  753.                     END IF
  754.                     IF vgrid(ia, jb, 2) = 0 THEN
  755.                         vgrid(ia, jb, 1) = (1 / 4) * (vgrid2(ia - 1, jb) + vgrid2(ia + 1, jb) + vgrid2(ia, jb - 1) + vgrid2(ia, jb + 1))
  756.                     END IF
  757.                 NEXT
  758.             NEXT
  759.         NEXT
  760.     END IF
  761.  
  762.     ci = 0
  763.     cj = 0
  764.     ca = 0
  765.     cb = 0
  766.     g = 1
  767.     FOR i = -SizeX / 2 + factor1 / 2 - TheResolution / 2 TO SizeX / 2 - factor1 / 2 - TheResolution / 2 STEP factor1
  768.         ci = ci + 1
  769.         cj = 0
  770.         FOR j = -SizeY / 2 + factor1 / 2 - TheResolution / 2 TO SizeY / 2 - factor1 / 2 - TheResolution / 2 STEP factor1
  771.             cj = cj + 1
  772.             u = i
  773.             v = j
  774.             w = 0
  775.             q = LatestIdentity&(g)
  776.             vindex = Group(q).LastVector
  777.             g = CreateNewGroup&(q, PosX + u, PosY + v, PosZ + w, 0, 24)
  778.             Group(g).GroupName = TheName
  779.             Group(g).Volume.x = SizeX
  780.             Group(g).Volume.y = SizeY
  781.             Group(g).Volume.z = SQR(SizeX * SizeX + SizeY * SizeY)
  782.             Group(g).FirstVector = vindex + 1
  783.             ca = 0
  784.             FOR a = i TO i + factor1 - 0 * factor2 STEP factor2
  785.                 ca = ca + 1
  786.                 cb = 0
  787.                 FOR b = j TO j + factor1 - 0 * factor2 STEP factor2
  788.                     cb = cb + 1
  789.                     rr = vgrid((ci - 1) * wa + ca, (cj - 1) * wb + cb, 1)
  790.                     vindex = vindex + 1
  791.                     vec3Dpos(vindex, 1) = -u + a + BumpFactor * (RND - .5)
  792.                     vec3Dpos(vindex, 2) = -v + b + BumpFactor * (RND - .5)
  793.                     vec3Dpos(vindex, 3) = -w + rr + BumpFactor * (RND - .5)
  794.                     IF (rr > 0) THEN
  795.                         vec3Dcolor(vindex) = ShadeMix~&(ShadeA, ShadeB, rr / 10)
  796.                     ELSE
  797.                         vec3Dcolor(vindex) = ShadeC
  798.                     END IF
  799.                 NEXT
  800.             NEXT
  801.             Group(g).LastVector = vindex
  802.         NEXT
  803.         CALL ClusterPinch(g)
  804.     NEXT
  805.     CALL ClusterPinch(g)
  806.     NewTerrain& = g
  807.  
  808. FUNCTION NewShell& (TheName AS STRING, TheRadius AS DOUBLE, PosX AS DOUBLE, PosY AS DOUBLE, PosZ AS DOUBLE, ShadeA AS _UNSIGNED LONG, ShadeB AS _UNSIGNED LONG, ShadeC AS _UNSIGNED LONG, BumpFactor AS DOUBLE, SmoothFactor AS INTEGER, TheDynamic AS INTEGER)
  809.     DIM g AS LONG
  810.     DIM q AS LONG
  811.     DIM vindex AS LONG
  812.     DIM k AS INTEGER
  813.     DIM factor1 AS DOUBLE
  814.     DIM factor2 AS DOUBLE
  815.     DIM a AS DOUBLE
  816.     DIM b AS DOUBLE
  817.     DIM i AS DOUBLE
  818.     DIM j AS DOUBLE
  819.     DIM u AS DOUBLE
  820.     DIM v AS DOUBLE
  821.     DIM w AS DOUBLE
  822.     DIM ia AS DOUBLE
  823.     DIM jb AS DOUBLE
  824.     DIM rr AS DOUBLE
  825.     DIM wi AS LONG
  826.     DIM wj AS LONG
  827.     DIM wa AS LONG
  828.     DIM wb AS LONG
  829.     DIM ci AS LONG
  830.     DIM cj AS LONG
  831.     DIM ca AS LONG
  832.     DIM cb AS LONG
  833.  
  834.     IF (TheRadius > 200) THEN
  835.         factor1 = (2) * 2 * pi / (10 ^ (INT(LOG(TheRadius) / LOG(10))))
  836.         factor2 = factor1 / (10)
  837.     ELSE
  838.         factor1 = 2 * pi / (10)
  839.         factor2 = factor1 / (5)
  840.     END IF
  841.  
  842.  
  843.     wi = 0
  844.     wj = 0
  845.     wa = 0
  846.     wb = 0
  847.     FOR i = 0 TO 2 * pi - factor1 STEP factor1
  848.         wi = wi + 1
  849.     NEXT
  850.     FOR j = factor2 TO pi - 0 STEP factor1
  851.         wj = wj + 1
  852.     NEXT
  853.     FOR a = 0 TO factor1 STEP factor2
  854.         wa = wa + 1
  855.     NEXT
  856.     FOR b = 0 TO factor1 STEP factor2
  857.         wb = wb + 1
  858.     NEXT
  859.  
  860.     DIM vgrid(wi * wa, wj * wb, 2) AS DOUBLE
  861.     DIM vgrid2(wi * wa, wj * wb) AS DOUBLE
  862.  
  863.     FOR ia = 1 TO wi * wa
  864.         FOR jb = 1 TO wj * wb
  865.             SELECT CASE (RND)
  866.                 CASE IS < .025
  867.                     vgrid(ia, jb, 1) = TheRadius - BumpFactor * (TheRadius / 50)
  868.                     vgrid(ia, jb, 2) = 1
  869.                 CASE IS > 1 - .025
  870.                     vgrid(ia, jb, 1) = TheRadius + BumpFactor * (TheRadius / 50)
  871.                     vgrid(ia, jb, 2) = 1
  872.                 CASE ELSE
  873.                     vgrid(ia, jb, 1) = TheRadius
  874.                     vgrid(ia, jb, 2) = 0
  875.             END SELECT
  876.         NEXT
  877.     NEXT
  878.  
  879.     IF (SmoothFactor > 0) THEN
  880.         FOR k = 1 TO SmoothFactor
  881.             FOR ia = 1 TO wi * wa
  882.                 FOR jb = 1 TO wj * wb
  883.                     vgrid2(ia, jb) = vgrid(ia, jb, 1)
  884.                 NEXT
  885.             NEXT
  886.             FOR ia = 1 + 1 TO wi * wa - 1
  887.                 FOR jb = 1 + 1 TO wj * wb - 1
  888.                     IF (k = SmoothFactor - 5) THEN
  889.                         vgrid(ia, jb, 2) = 0
  890.                     END IF
  891.                     IF vgrid(ia, jb, 2) = 0 THEN
  892.                         vgrid(ia, jb, 1) = (1 / 4) * (vgrid2(ia - 1, jb) + vgrid2(ia + 1, jb) + vgrid2(ia, jb - 1) + vgrid2(ia, jb + 1))
  893.                     END IF
  894.                 NEXT
  895.             NEXT
  896.         NEXT
  897.     END IF
  898.  
  899.     ci = 0
  900.     cj = 0
  901.     ca = 0
  902.     cb = 0
  903.     g = 1
  904.     FOR i = 0 TO 2 * pi - factor1 STEP factor1
  905.         ci = ci + 1
  906.         cj = 0
  907.         FOR j = factor2 TO pi - 0 STEP factor1
  908.             cj = cj + 1
  909.             u = TheRadius * SIN(j) * COS(i)
  910.             v = TheRadius * SIN(j) * SIN(i)
  911.             w = TheRadius * COS(j)
  912.             q = LatestIdentity&(g)
  913.             vindex = Group(q).LastVector
  914.             g = CreateNewGroup&(q, PosX + u, PosY + v, PosZ + w, TheDynamic, 64)
  915.             Group(g).GroupName = TheName
  916.             Group(g).Volume.x = TheRadius
  917.             Group(g).Volume.y = TheRadius
  918.             Group(g).Volume.z = TheRadius
  919.             Group(g).FirstVector = vindex + 1
  920.             ca = 0
  921.             FOR a = i TO i + factor1 STEP factor2
  922.                 ca = ca + 1
  923.                 cb = 0
  924.                 FOR b = j TO j + factor1 STEP factor2
  925.                     cb = cb + 1
  926.                     rr = vgrid((ci - 1) * wa + ca, (cj - 1) * wb + cb, 1)
  927.                     vindex = vindex + 1
  928.                     vec3Dpos(vindex, 1) = -u + rr * SIN(b) * COS(a) + BumpFactor * (RND - .5) * 2
  929.                     vec3Dpos(vindex, 2) = -v + rr * SIN(b) * SIN(a) + BumpFactor * (RND - .5) * 2
  930.                     vec3Dpos(vindex, 3) = -w + rr * COS(b) + BumpFactor * (RND - .5) * 2
  931.                     IF (rr > TheRadius) THEN
  932.                         vec3Dcolor(vindex) = ShadeMix~&(ShadeA, ShadeB, COS(j) ^ 2)
  933.                     ELSE
  934.                         vec3Dcolor(vindex) = ShadeC
  935.                     END IF
  936.                 NEXT
  937.             NEXT
  938.             Group(g).LastVector = vindex
  939.         NEXT
  940.         CALL ClusterPinch(g)
  941.     NEXT
  942.     CALL ClusterPinch(g)
  943.  
  944.     NewShell& = g
  945.  
  946. ' Low-order primitive group(s).
  947.  
  948. FUNCTION NewCube& (StartingIdentity AS LONG, TheName AS STRING, Weight AS INTEGER, PosX AS DOUBLE, PosY AS DOUBLE, PosZ AS DOUBLE, VolX AS DOUBLE, VolY AS DOUBLE, VolZ AS DOUBLE, ShadeA AS _UNSIGNED LONG, ShadeB AS _UNSIGNED LONG, ShadeC AS _UNSIGNED LONG, TheDynamic AS INTEGER)
  949.     DIM k AS INTEGER
  950.     DIM g AS LONG
  951.     DIM q AS LONG
  952.     DIM vindex AS LONG
  953.     q = LatestIdentity&(StartingIdentity)
  954.     vindex = Group(q).LastVector
  955.     g = CreateNewGroup(q, PosX, PosY, PosZ, TheDynamic, 64)
  956.     Group(g).GroupName = TheName
  957.     Group(g).Volume.x = VolX
  958.     Group(g).Volume.y = VolY
  959.     Group(g).Volume.z = VolZ
  960.     Group(g).FirstVector = vindex + 1
  961.     FOR k = 1 TO Weight
  962.         vindex = vindex + 1
  963.         vec3Dpos(vindex, 1) = (RND - .5) * VolX
  964.         vec3Dpos(vindex, 2) = (RND - .5) * VolY
  965.         vec3Dpos(vindex, 3) = (RND - .5) * VolZ
  966.         IF (RND > .5) THEN
  967.             vec3Dcolor(vindex) = ShadeA
  968.         ELSE
  969.             IF (RND > .5) THEN
  970.                 vec3Dcolor(vindex) = ShadeB
  971.             ELSE
  972.                 vec3Dcolor(vindex) = ShadeC
  973.             END IF
  974.         END IF
  975.     NEXT
  976.     Group(g).LastVector = vindex
  977.     NewCube& = g
  978.  
  979. ' Linked list utility.
  980.  
  981. FUNCTION LatestIdentity& (StartingID AS LONG)
  982.     DIM TheReturn AS LONG
  983.     DIM p AS LONG
  984.     DIM q AS LONG
  985.     p = StartingID
  986.     DO
  987.         q = p
  988.         p = Group(q).Pointer
  989.         IF (p = -999) THEN EXIT DO
  990.     LOOP
  991.     TheReturn = q
  992.     LatestIdentity& = TheReturn
  993.  
  994. FUNCTION CreateNewGroup& (TheLaggerIn AS LONG, CenterX AS DOUBLE, CenterY AS DOUBLE, CenterZ AS DOUBLE, TheDynamic AS INTEGER, ClusterSize AS INTEGER)
  995.     GroupIdTicker = GroupIdTicker + 1
  996.     Group(GroupIdTicker).Identity = GroupIdTicker
  997.     Group(GroupIdTicker).Pointer = -999
  998.     Group(GroupIdTicker).Lagger = TheLaggerIn
  999.     Group(GroupIdTicker).Centroid.x = CenterX
  1000.     Group(GroupIdTicker).Centroid.y = CenterY
  1001.     Group(GroupIdTicker).Centroid.z = CenterZ
  1002.     IF (TheLaggerIn <> 0) THEN
  1003.         Group(TheLaggerIn).Pointer = Group(GroupIdTicker).Identity
  1004.     END IF
  1005.     '''
  1006.     ClusterFillCounter = ClusterFillCounter + 1
  1007.  
  1008.     IF (ClusterFillCounter = 1) THEN
  1009.         ClusterIndexTicker = ClusterIndexTicker + 1
  1010.         Cluster(ClusterIndexTicker).FirstGroup = Group(GroupIdTicker).Identity
  1011.         Cluster(ClusterIndexTicker).MotionType = TheDynamic
  1012.     END IF
  1013.  
  1014.     IF (ClusterFillCounter = ClusterSize) THEN
  1015.         CALL ClusterPinch(Group(GroupIdTicker).Identity)
  1016.     END IF
  1017.     '''
  1018.     CreateNewGroup& = Group(GroupIdTicker).Identity
  1019.  
  1020. SUB ClusterPinch (TheLastGroup AS LONG)
  1021.     ClusterFillCounter = 0
  1022.     Cluster(ClusterIndexTicker).LastGroup = TheLastGroup
  1023.     CALL ClusterCentroidCalc(ClusterIndexTicker)
  1024.  
  1025. SUB ClusterCentroidCalc (TheClusterIndex AS LONG)
  1026.     DIM k AS LONG
  1027.     DIM n AS INTEGER
  1028.     Cluster(TheClusterIndex).Centroid.x = 0
  1029.     Cluster(TheClusterIndex).Centroid.y = 0
  1030.     Cluster(TheClusterIndex).Centroid.z = 0
  1031.     k = Cluster(TheClusterIndex).FirstGroup
  1032.     n = 0
  1033.     DO
  1034.         Cluster(TheClusterIndex).Centroid.x = Cluster(TheClusterIndex).Centroid.x + Group(k).Centroid.x
  1035.         Cluster(TheClusterIndex).Centroid.y = Cluster(TheClusterIndex).Centroid.y + Group(k).Centroid.y
  1036.         Cluster(TheClusterIndex).Centroid.z = Cluster(TheClusterIndex).Centroid.z + Group(k).Centroid.z
  1037.         n = n + 1
  1038.         IF (k = Cluster(TheClusterIndex).LastGroup) THEN EXIT DO
  1039.         k = Group(k).Pointer
  1040.     LOOP
  1041.     Cluster(TheClusterIndex).Centroid.x = Cluster(TheClusterIndex).Centroid.x / n
  1042.     Cluster(TheClusterIndex).Centroid.y = Cluster(TheClusterIndex).Centroid.y / n
  1043.     Cluster(TheClusterIndex).Centroid.z = Cluster(TheClusterIndex).Centroid.z / n
  1044.  
  1045. ' Vector manipulation.
  1046.  
  1047. SUB SetParticleVelocity (TheGroup AS LONG, vx AS DOUBLE, vy AS DOUBLE, vz AS DOUBLE)
  1048.     DIM j AS LONG
  1049.     DIM m AS LONG
  1050.     DIM n AS LONG
  1051.     m = Group(TheGroup).FirstVector
  1052.     n = Group(TheGroup).LastVector
  1053.     FOR j = m TO n
  1054.         vec3Dvel(j, 1) = vx
  1055.         vec3Dvel(j, 2) = vy
  1056.         vec3Dvel(j, 3) = vz
  1057.     NEXT
  1058.  
  1059. ' Cluster manipulation.
  1060.  
  1061. SUB EvolveCluster (TheClusterIndex AS LONG)
  1062.     DIM dx, dy, dz, t, u, v AS DOUBLE
  1063.     IF (Cluster(TheClusterIndex).MotionType = 0) THEN
  1064.         dx = Cluster(TheClusterIndex).Velocity.x
  1065.         dy = Cluster(TheClusterIndex).Velocity.y
  1066.         dz = Cluster(TheClusterIndex).Velocity.z
  1067.     ELSE
  1068.         t = TIMER
  1069.         u = INT(t)
  1070.         v = t - u
  1071.         dx = v * FixedPath(Cluster(TheClusterIndex).MotionType, u).x + (1 - v) * FixedPath(Cluster(TheClusterIndex).MotionType, u - 1).x
  1072.         dy = v * FixedPath(Cluster(TheClusterIndex).MotionType, u).y + (1 - v) * FixedPath(Cluster(TheClusterIndex).MotionType, u - 1).y
  1073.         dz = v * FixedPath(Cluster(TheClusterIndex).MotionType, u).z + (1 - v) * FixedPath(Cluster(TheClusterIndex).MotionType, u - 1).z
  1074.     END IF
  1075.     IF ((dx <> 0) OR (dy <> 0) OR (dz <> 0)) THEN
  1076.         CALL TranslateCluster(TheClusterIndex, dx, dy, dz)
  1077.     END IF
  1078.  
  1079. SUB TranslateCluster (TheClusterIndex AS LONG, dx AS DOUBLE, dy AS DOUBLE, dz AS DOUBLE)
  1080.     DIM k AS LONG
  1081.     k = Cluster(TheClusterIndex).FirstGroup
  1082.     DO
  1083.         Group(k).Centroid.x = Group(k).Centroid.x + dx
  1084.         Group(k).Centroid.y = Group(k).Centroid.y + dy
  1085.         Group(k).Centroid.z = Group(k).Centroid.z + dz
  1086.         IF (k = Cluster(TheClusterIndex).LastGroup) THEN EXIT DO
  1087.         k = Group(k).Pointer
  1088.     LOOP
  1089.     Cluster(TheClusterIndex).Centroid.x = Cluster(TheClusterIndex).Centroid.x + dx
  1090.     Cluster(TheClusterIndex).Centroid.y = Cluster(TheClusterIndex).Centroid.y + dy
  1091.     Cluster(TheClusterIndex).Centroid.z = Cluster(TheClusterIndex).Centroid.z + dz
  1092.  
  1093. ' Particle manipulation.
  1094.  
  1095. SUB EvolveParticles (TheGroup AS LONG)
  1096.     DIM xdim AS DOUBLE
  1097.     DIM ydim AS DOUBLE
  1098.     DIM zdim AS DOUBLE
  1099.     DIM dx AS DOUBLE
  1100.     DIM dy AS DOUBLE
  1101.     DIM dz AS DOUBLE
  1102.     DIM px AS DOUBLE
  1103.     DIM py AS DOUBLE
  1104.     DIM pz AS DOUBLE
  1105.     DIM k AS LONG
  1106.  
  1107.     xdim = Group(TheGroup).Volume.x
  1108.     ydim = Group(TheGroup).Volume.y
  1109.     zdim = Group(TheGroup).Volume.z
  1110.  
  1111.     FOR k = Group(TheGroup).FirstVector TO Group(TheGroup).LastVector
  1112.  
  1113.         ' Position update with periodic boundaries inside group volume
  1114.         dx = 1 * vec3Dvel(k, 1)
  1115.         dy = 1 * vec3Dvel(k, 2)
  1116.         dz = 1 * vec3Dvel(k, 3)
  1117.         IF (dx <> 0) THEN
  1118.             px = vec3Dpos(k, 1) + dx
  1119.             IF ABS(px) > xdim / 2 THEN
  1120.                 IF (px > xdim / 2) THEN
  1121.                     px = -xdim / 2
  1122.                 ELSE
  1123.                     px = xdim / 2
  1124.                 END IF
  1125.             END IF
  1126.             vec3Dpos(k, 1) = px
  1127.         END IF
  1128.         IF (dy <> 0) THEN
  1129.             py = vec3Dpos(k, 2) + dy
  1130.             IF ABS(py) > ydim / 2 THEN
  1131.                 IF (py > ydim / 2) THEN
  1132.                     py = -ydim / 2
  1133.                 ELSE
  1134.                     py = ydim / 2
  1135.                 END IF
  1136.             END IF
  1137.             vec3Dpos(k, 2) = py
  1138.         END IF
  1139.         IF (dz <> 0) THEN
  1140.             pz = vec3Dpos(k, 3) + dz
  1141.             IF ABS(pz) > zdim / 2 THEN
  1142.                 IF (pz > zdim / 2) THEN
  1143.                     pz = -zdim / 2
  1144.                 ELSE
  1145.                     pz = zdim / 2
  1146.                 END IF
  1147.             END IF
  1148.             vec3Dpos(k, 3) = pz
  1149.         END IF
  1150.     NEXT
  1151.  
  1152. SUB CalculateScreenVectors
  1153.     DIM uhatmag AS DOUBLE
  1154.     DIM vhatmag AS DOUBLE
  1155.     DIM h2 AS DOUBLE
  1156.     DIM w2 AS DOUBLE
  1157.     DIM mag AS DOUBLE
  1158.     DIM uhatdotvhat AS DOUBLE
  1159.     uhatmag = SQR(uhat(1) * uhat(1) + uhat(2) * uhat(2) + uhat(3) * uhat(3))
  1160.     uhat(1) = uhat(1) / uhatmag: uhat(2) = uhat(2) / uhatmag: uhat(3) = uhat(3) / uhatmag
  1161.     vhatmag = SQR(vhat(1) * vhat(1) + vhat(2) * vhat(2) + vhat(3) * vhat(3))
  1162.     vhat(1) = vhat(1) / vhatmag: vhat(2) = vhat(2) / vhatmag: vhat(3) = vhat(3) / vhatmag
  1163.     uhatdotvhat = uhat(1) * vhat(1) + uhat(2) * vhat(2) + uhat(3) * vhat(3)
  1164.     nhat(1) = uhat(2) * vhat(3) - uhat(3) * vhat(2)
  1165.     nhat(2) = uhat(3) * vhat(1) - uhat(1) * vhat(3)
  1166.     nhat(3) = uhat(1) * vhat(2) - uhat(2) * vhat(1)
  1167.     h2 = _HEIGHT / 2
  1168.     w2 = _WIDTH / 2
  1169.     nearplane(1) = -nhat(1)
  1170.     nearplane(2) = -nhat(2)
  1171.     nearplane(3) = -nhat(3)
  1172.     farplane(1) = nhat(1)
  1173.     farplane(2) = nhat(2)
  1174.     farplane(3) = nhat(3)
  1175.     rightplane(1) = h2 * fovd * uhat(1) - h2 * w2 * nhat(1)
  1176.     rightplane(2) = h2 * fovd * uhat(2) - h2 * w2 * nhat(2)
  1177.     rightplane(3) = h2 * fovd * uhat(3) - h2 * w2 * nhat(3)
  1178.     mag = SQR(rightplane(1) * rightplane(1) + rightplane(2) * rightplane(2) + rightplane(3) * rightplane(3))
  1179.     rightplane(1) = rightplane(1) / mag
  1180.     rightplane(2) = rightplane(2) / mag
  1181.     rightplane(3) = rightplane(3) / mag
  1182.     leftplane(1) = -h2 * fovd * uhat(1) - h2 * w2 * nhat(1)
  1183.     leftplane(2) = -h2 * fovd * uhat(2) - h2 * w2 * nhat(2)
  1184.     leftplane(3) = -h2 * fovd * uhat(3) - h2 * w2 * nhat(3)
  1185.     mag = SQR(leftplane(1) * leftplane(1) + leftplane(2) * leftplane(2) + leftplane(3) * leftplane(3))
  1186.     leftplane(1) = leftplane(1) / mag
  1187.     leftplane(2) = leftplane(2) / mag
  1188.     leftplane(3) = leftplane(3) / mag
  1189.     topplane(1) = w2 * fovd * vhat(1) - h2 * w2 * nhat(1)
  1190.     topplane(2) = w2 * fovd * vhat(2) - h2 * w2 * nhat(2)
  1191.     topplane(3) = w2 * fovd * vhat(3) - h2 * w2 * nhat(3)
  1192.     mag = SQR(topplane(1) * topplane(1) + topplane(2) * topplane(2) + topplane(3) * topplane(3))
  1193.     topplane(1) = topplane(1) / mag
  1194.     topplane(2) = topplane(2) / mag
  1195.     topplane(3) = topplane(3) / mag
  1196.     bottomplane(1) = -w2 * fovd * vhat(1) - h2 * w2 * nhat(1)
  1197.     bottomplane(2) = -w2 * fovd * vhat(2) - h2 * w2 * nhat(2)
  1198.     bottomplane(3) = -w2 * fovd * vhat(3) - h2 * w2 * nhat(3)
  1199.     mag = SQR(bottomplane(1) * bottomplane(1) + bottomplane(2) * bottomplane(2) + bottomplane(3) * bottomplane(3))
  1200.     bottomplane(1) = bottomplane(1) / mag
  1201.     bottomplane(2) = bottomplane(2) / mag
  1202.     bottomplane(3) = bottomplane(3) / mag
  1203.  
  1204. SUB ProjectGroup (TheGroup AS LONG)
  1205.     DIM i AS LONG
  1206.     DIM f AS INTEGER
  1207.     DIM vectorinview AS INTEGER
  1208.     DIM vec3Ddotnhat AS DOUBLE
  1209.     FOR i = Group(TheGroup).FirstVector TO Group(TheGroup).LastVector
  1210.         vec(i, 1) = Group(TheGroup).Centroid.x + vec3Dpos(i, 1) - PlayerCamera.Position.x
  1211.         vec(i, 2) = Group(TheGroup).Centroid.y + vec3Dpos(i, 2) - PlayerCamera.Position.y
  1212.         vec(i, 3) = Group(TheGroup).Centroid.z + vec3Dpos(i, 3) - PlayerCamera.Position.z
  1213.         f = -1
  1214.         vec3Dvis(i) = 0
  1215.         vectorinview = 1
  1216.         IF vec(i, 1) * nearplane(1) + vec(i, 2) * nearplane(2) + vec(i, 3) * nearplane(3) - nearplane(4) < 0 THEN vectorinview = 0
  1217.         'IF vec(i, 1) * farplane(1) + vec(i, 2) * farplane(2) + vec(i, 3) * farplane(3) - farplane(4) < 0 THEN vectorinview = 0
  1218.         IF vec(i, 1) * farplane(1) + vec(i, 2) * farplane(2) + vec(i, 3) * farplane(3) - farplane(4) * .85 < 0 THEN f = 1
  1219.         'IF vec(i, 1) * rightplane(1) + vec(i, 2) * rightplane(2) + vec(i, 3) * rightplane(3) - rightplane(4) < 0 THEN vectorinview = 0
  1220.         'IF vec(i, 1) * leftplane(1) + vec(i, 2) * leftplane(2) + vec(i, 3) * leftplane(3) - leftplane(4) < 0 THEN vectorinview = 0
  1221.         'IF vec(i, 1) * topplane(1) + vec(i, 2) * topplane(2) + vec(i, 3) * topplane(3) - topplane(4) < 0 THEN vectorinview = 0
  1222.         'IF vec(i, 1) * bottomplane(1) + vec(i, 2) * bottomplane(2) + vec(i, 3) * bottomplane(3) - bottomplane(4) < 0 THEN vectorinview = 0
  1223.         IF (vectorinview = 1) THEN
  1224.             vec3Dvis(i) = 1
  1225.             vec3Ddotnhat = vec(i, 1) * nhat(1) + vec(i, 2) * nhat(2) + vec(i, 3) * nhat(3)
  1226.             vec2D(i, 1) = (vec(i, 1) * uhat(1) + vec(i, 2) * uhat(2) + vec(i, 3) * uhat(3)) * fovd / vec3Ddotnhat
  1227.             vec2D(i, 2) = (vec(i, 1) * vhat(1) + vec(i, 2) * vhat(2) + vec(i, 3) * vhat(3)) * fovd / vec3Ddotnhat
  1228.             IF (f = 1) THEN
  1229.                 vec2Dcolor(i) = Gray
  1230.             ELSE
  1231.                 vec2Dcolor(i) = vec3Dcolor(i)
  1232.             END IF
  1233.         END IF
  1234.     NEXT
  1235.  
  1236. ' TheDynamics
  1237.  
  1238. SUB ComputeVisibleScene
  1239.     DIM closestdist2 AS DOUBLE
  1240.     DIM fp42 AS DOUBLE
  1241.     DIM dist2 AS DOUBLE
  1242.     DIM dx AS DOUBLE
  1243.     DIM dy AS DOUBLE
  1244.     DIM dz AS DOUBLE
  1245.     DIM GroupInView AS INTEGER
  1246.     DIM k AS LONG
  1247.     DIM i AS LONG
  1248.     ClosestGroup = 1
  1249.     closestdist2 = 10000000
  1250.     fp42 = farplane(4) * farplane(4)
  1251.     FOR i = 1 TO ClusterIndexTicker
  1252.         dx = Cluster(i).Centroid.x - PlayerCamera.Position.x
  1253.         dy = Cluster(i).Centroid.y - PlayerCamera.Position.y
  1254.         dz = Cluster(i).Centroid.z - PlayerCamera.Position.z
  1255.         dist2 = dx * dx + dy * dy + dz * dz
  1256.         IF (dist2 > 600 * 600) THEN
  1257.             Cluster(i).Visible = 0
  1258.             IF (Cluster(i).MotionType > -1) THEN
  1259.                 CALL EvolveCluster(i)
  1260.             END IF
  1261.         ELSE
  1262.             Cluster(i).Visible = 1
  1263.             k = Cluster(i).FirstGroup
  1264.             IF ((Cluster(i).MotionType > -1) AND (ToggleAnimate = 1)) THEN
  1265.                 CALL EvolveCluster(i)
  1266.             END IF
  1267.             DO
  1268.                 dx = Group(k).Centroid.x - PlayerCamera.Position.x
  1269.                 dy = Group(k).Centroid.y - PlayerCamera.Position.y
  1270.                 dz = Group(k).Centroid.z - PlayerCamera.Position.z
  1271.                 dist2 = dx * dx + dy * dy + dz * dz
  1272.                 Group(k).Visible = 0
  1273.                 IF (dist2 < fp42) THEN
  1274.                     GroupInView = 1
  1275.                     IF dx * nearplane(1) + dy * nearplane(2) + dz * nearplane(3) - nearplane(4) < 0 THEN GroupInView = 0
  1276.                     'IF dx * farplane(1) + dy * farplane(2) + dz * farplane(3) - farplane(4) < 0 THEN groupinview = 0 ''' Redundant
  1277.                     IF dx * rightplane(1) + dy * rightplane(2) + dz * rightplane(3) - rightplane(4) < 0 THEN GroupInView = 0
  1278.                     IF dx * leftplane(1) + dy * leftplane(2) + dz * leftplane(3) - leftplane(4) < 0 THEN GroupInView = 0
  1279.                     IF dx * topplane(1) + dy * topplane(2) + dz * topplane(3) - topplane(4) < 0 THEN GroupInView = 0
  1280.                     IF dx * bottomplane(1) + dy * bottomplane(2) + dz * bottomplane(3) - bottomplane(4) < 0 THEN GroupInView = 0
  1281.                     IF (GroupInView = 1) THEN
  1282.                         Group(k).Visible = 1
  1283.                         IF (dist2 < closestdist2) THEN
  1284.                             closestdist2 = dist2
  1285.                             ClosestGroup = k
  1286.                         END IF
  1287.                         Group(k).Distance2 = dist2
  1288.                         IF (ToggleAnimate = 1) THEN CALL EvolveParticles(k)
  1289.                         CALL ProjectGroup(k)
  1290.                     END IF
  1291.                 END IF
  1292.                 IF (k = Cluster(i).LastGroup) THEN EXIT DO
  1293.                 k = Group(k).Pointer
  1294.             LOOP
  1295.         END IF
  1296.     NEXT
  1297.  
  1298. ' Graphics
  1299.  
  1300. SUB PlotWorld
  1301.     DIM a AS DOUBLE
  1302.     DIM b AS DOUBLE
  1303.     DIM c AS LONG
  1304.     DIM d AS LONG
  1305.     DIM i AS LONG
  1306.     DIM j AS INTEGER
  1307.     DIM k AS LONG
  1308.     DIM x1 AS DOUBLE
  1309.     DIM y1 AS DOUBLE
  1310.     DIM x2 AS DOUBLE
  1311.     DIM y2 AS DOUBLE
  1312.     DIM clrtmp AS _UNSIGNED LONG
  1313.     DIM SortedGroups(5000) AS LONG
  1314.     DIM SortedGroupsCount AS INTEGER
  1315.  
  1316.     NumClusterVisible = 0
  1317.     NumVectorVisible = 0
  1318.  
  1319.     SortedGroupsCount = 0
  1320.     FOR i = 1 TO ClusterIndexTicker
  1321.         IF (Cluster(i).Visible = 1) THEN
  1322.             NumClusterVisible = NumClusterVisible + 1
  1323.             k = Cluster(i).FirstGroup
  1324.             DO
  1325.                 IF (Group(k).Visible = 1) THEN
  1326.                     SortedGroupsCount = SortedGroupsCount + 1
  1327.                     SortedGroups(SortedGroupsCount) = k
  1328.                 END IF
  1329.                 IF (k = Cluster(i).LastGroup) THEN EXIT DO
  1330.                 k = Group(k).Pointer
  1331.             LOOP
  1332.         END IF
  1333.     NEXT
  1334.  
  1335.     NumGroupVisible = SortedGroupsCount
  1336.  
  1337.     ' Bubble sort might be a shitty choice but it works for now. Replace with quicksort prolly.
  1338.     FOR j = SortedGroupsCount TO 1 STEP -1
  1339.         FOR i = 2 TO SortedGroupsCount
  1340.             a = Group(SortedGroups(i - 1)).Distance2
  1341.             b = Group(SortedGroups(i)).Distance2
  1342.             IF (a < b) THEN
  1343.                 c = SortedGroups(i - 1)
  1344.                 d = SortedGroups(i)
  1345.                 SortedGroups(i - 1) = d
  1346.                 SortedGroups(i) = c
  1347.             END IF
  1348.         NEXT
  1349.     NEXT
  1350.  
  1351.     CLS
  1352.     PlayerCamera.Shade = ShadeMix~&(PlayerCamera.Shade, vec3Dcolor(Group(ClosestGroup).FirstVector), .01)
  1353.     PAINT (_WIDTH / 2, _HEIGHT / 2), _RGBA(_RED32(PlayerCamera.Shade), _GREEN32(PlayerCamera.Shade), _BLUE32(PlayerCamera.Shade), 100)
  1354.     FOR j = 1 TO SortedGroupsCount
  1355.         k = SortedGroups(j)
  1356.         FOR i = Group(k).FirstVector TO Group(k).LastVector - 1
  1357.             IF (vec3Dvis(i) = 1) THEN
  1358.                 NumVectorVisible = NumVectorVisible + 1
  1359.                 IF (k = ClosestGroup) THEN
  1360.                     clrtmp = Yellow
  1361.                 ELSE
  1362.                     clrtmp = vec2Dcolor(i)
  1363.                 END IF
  1364.                 x1 = vec2D(i, 1)
  1365.                 y1 = vec2D(i, 2)
  1366.                 x2 = vec2D(i + 1, 1)
  1367.                 y2 = vec2D(i + 1, 2)
  1368.                 IF (((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) < 225) THEN
  1369.                     CALL cline(x1, y1, x2, y2, clrtmp) 'lineSmooth
  1370.                 ELSE
  1371.                     CALL ccircle(x1, y1, 1, clrtmp)
  1372.                 END IF
  1373.             END IF
  1374.         NEXT
  1375.     NEXT
  1376.  
  1377. SUB DisplayHUD
  1378.     DIM a AS STRING
  1379.     DIM k AS INTEGER
  1380.     CALL lineSmooth(0, 0, 25 * (xhat(1) * uhat(1) + xhat(2) * uhat(2) + xhat(3) * uhat(3)), 25 * (xhat(1) * vhat(1) + xhat(2) * vhat(2) + xhat(3) * vhat(3)), _RGBA(255, 0, 0, 200))
  1381.     CALL lineSmooth(0, 0, 25 * (yhat(1) * uhat(1) + yhat(2) * uhat(2) + yhat(3) * uhat(3)), 25 * (yhat(1) * vhat(1) + yhat(2) * vhat(2) + yhat(3) * vhat(3)), _RGBA(0, 255, 0, 200))
  1382.     CALL lineSmooth(0, 0, 25 * (zhat(1) * uhat(1) + zhat(2) * uhat(2) + zhat(3) * uhat(3)), 25 * (zhat(1) * vhat(1) + zhat(2) * vhat(2) + zhat(3) * vhat(3)), _RGBA(30, 144, 255, 200))
  1383.     IF (ToggleHUD = 1) THEN
  1384.         COLOR LimeGreen
  1385.         _PRINTSTRING ((1) * 8, _HEIGHT - (10) * 16), " Position "
  1386.         COLOR Teal
  1387.         _PRINTSTRING ((1) * 8, _HEIGHT - (9) * 16), " x:       "
  1388.         _PRINTSTRING ((1) * 8, _HEIGHT - (8) * 16), " y:       "
  1389.         _PRINTSTRING ((1) * 8, _HEIGHT - (7) * 16), " z:       "
  1390.         _PRINTSTRING ((1) * 8, _HEIGHT - (9) * 16), " x: " + LTRIM$(RTRIM$(STR$(INT(PlayerCamera.Position.x))))
  1391.         _PRINTSTRING ((1) * 8, _HEIGHT - (8) * 16), " y: " + LTRIM$(RTRIM$(STR$(INT(PlayerCamera.Position.y))))
  1392.         _PRINTSTRING ((1) * 8, _HEIGHT - (7) * 16), " z: " + LTRIM$(RTRIM$(STR$(INT(PlayerCamera.Position.z))))
  1393.         COLOR LimeGreen
  1394.         _PRINTSTRING ((1) * 8, _HEIGHT - (6) * 16), " O: Reset "
  1395.         COLOR LimeGreen
  1396.         _PRINTSTRING ((1) * 8, _HEIGHT - (4) * 16), " Move  "
  1397.         COLOR DarkKhaki
  1398.         _PRINTSTRING ((1) * 8, _HEIGHT - (3) * 16), " q W e "
  1399.         _PRINTSTRING ((1) * 8, _HEIGHT - (2) * 16), " A S D "
  1400.         COLOR LimeGreen
  1401.         _PRINTSTRING ((1) * 8, (1) * 16), " Abilities "
  1402.         COLOR DarkKhaki
  1403.         _PRINTSTRING ((1) * 8, (2) * 16), "t = Pause  "
  1404.         _PRINTSTRING ((1) * 8, (3) * 16), "b = Create "
  1405.         _PRINTSTRING ((1) * 8, (4) * 16), "n = Explode"
  1406.         COLOR DarkGray
  1407.         _PRINTSTRING ((1) * 8, (5) * 16), "k = Delete "
  1408.         COLOR DarkKhaki
  1409.         _PRINTSTRING ((1) * 8, (6) * 16), "Esc = Quit "
  1410.         'COLOR LimeGreen
  1411.         '_PRINTSTRING ((1) * 8, (9) * 16), "View"
  1412.         'COLOR DarkKhaki
  1413.         '_PRINTSTRING ((1) * 8, (9) * 16), "FPS: " + LTRIM$(RTRIM$(STR$(FPSReport))) + "/30"
  1414.         '_PRINTSTRING ((1) * 8, (10) * 16), "Clusters: " + LTRIM$(RTRIM$(STR$(NumClusterVisible)))
  1415.         '_PRINTSTRING ((1) * 8, (11) * 16), "Groups: " + LTRIM$(RTRIM$(STR$(NumGroupVisible)))
  1416.         '_PRINTSTRING ((1) * 8, (12) * 16), "Particles: " + LTRIM$(RTRIM$(STR$(NumVectorVisible)))
  1417.         COLOR LimeGreen
  1418.         _PRINTSTRING (_WIDTH - (12) * 8, (1) * 16), " Locations "
  1419.         FOR k = 1 TO MissionTicker
  1420.             a = " " + Mission(k).Label + " "
  1421.             IF (Mission(k).Discovered = 0) THEN
  1422.                 COLOR DarkKhaki
  1423.             ELSE
  1424.                 COLOR DarkGray
  1425.             END IF
  1426.             _PRINTSTRING (_WIDTH - (LEN(a) + 1) * 8, (1 + k) * 16), a
  1427.         NEXT
  1428.         COLOR LimeGreen
  1429.         _PRINTSTRING (_WIDTH - (12) * 8, (k + 1) * 16), " M = Reset "
  1430.         COLOR LimeGreen
  1431.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (10) * 16), " Aim    "
  1432.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (9) * 16), " x      "
  1433.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (8) * 16), " y      "
  1434.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (7) * 16), " z      "
  1435.         COLOR Teal
  1436.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (9) * 16), " x: " + LTRIM$(RTRIM$(STR$(INT(-nhat(1) * 100))))
  1437.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (8) * 16), " y: " + LTRIM$(RTRIM$(STR$(INT(-nhat(2) * 100))))
  1438.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (7) * 16), " z: " + LTRIM$(RTRIM$(STR$(INT(-nhat(3) * 100))))
  1439.         COLOR LimeGreen
  1440.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (5) * 16), " Swivel "
  1441.         COLOR DarkKhaki
  1442.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (4) * 16), " 7 8 9  "
  1443.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (3) * 16), " 4 5 6  "
  1444.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (2) * 16), " 1 2 3  "
  1445.         a = " - Closest - "
  1446.         COLOR LimeGreen
  1447.         _PRINTSTRING (_WIDTH / 2 - (LEN(a) / 2) * 8, _HEIGHT - (3) * 16), a
  1448.         a = " SPACE = Hide "
  1449.         COLOR LimeGreen
  1450.         _PRINTSTRING (_WIDTH / 2 - (LEN(a) / 2) * 8, _HEIGHT - (1) * 16), a
  1451.     END IF
  1452.     a = " " + Group(ClosestGroup).GroupName + " "
  1453.     COLOR DarkKhaki
  1454.     _PRINTSTRING (_WIDTH / 2 - (LEN(a) / 2) * 8, _HEIGHT - (2) * 16), a
  1455.  
  1456. ' Interface
  1457.  
  1458. SUB KeyProcess
  1459.     DIM modifier AS DOUBLE
  1460.     modifier = 0.05
  1461.  
  1462.     'IF (_KEYDOWN(100303) <> 0) OR (_KEYDOWN(100304) <> 0) THEN
  1463.     '    modifier = modifier * 10
  1464.     'END IF
  1465.     IF (_KEYDOWN(87) <> 0) OR (_KEYDOWN(119) <> 0) OR (_KEYDOWN(18432) <> 0) THEN ' W or w or uparrow
  1466.         CALL StrafeCameraNhatMinus
  1467.         IF (ToggleAnimate = 1) THEN
  1468.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x - modifier * nhat(1)
  1469.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y - modifier * nhat(2)
  1470.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z - modifier * nhat(3)
  1471.         END IF
  1472.     END IF
  1473.     IF (_KEYDOWN(83) <> 0) OR (_KEYDOWN(115) <> 0) OR (_KEYDOWN(20480) <> 0) THEN ' S or s or downarrow
  1474.         CALL StrafeCameraNhatPlus
  1475.         IF (ToggleAnimate = 1) THEN
  1476.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x + modifier * nhat(1)
  1477.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y + modifier * nhat(2)
  1478.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z + modifier * nhat(3)
  1479.         END IF
  1480.     END IF
  1481.     IF (_KEYDOWN(65) <> 0) OR (_KEYDOWN(97) <> 0) THEN ' A or a
  1482.         CALL StrafeCameraUhatMinus
  1483.         IF (ToggleAnimate = 1) THEN
  1484.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x - modifier * uhat(1)
  1485.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y - modifier * uhat(2)
  1486.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z - modifier * uhat(3)
  1487.         END IF
  1488.     END IF
  1489.     IF (_KEYDOWN(68) <> 0) OR (_KEYDOWN(100) <> 0) THEN ' D or d
  1490.         CALL StrafeCameraUhatPlus
  1491.         IF (ToggleAnimate = 1) THEN
  1492.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x + modifier * uhat(1)
  1493.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y + modifier * uhat(2)
  1494.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z + modifier * uhat(3)
  1495.         END IF
  1496.     END IF
  1497.     IF (_KEYDOWN(81) <> 0) OR (_KEYDOWN(113) <> 0) THEN ' Q or q
  1498.         CALL StrafeCameraVhatMinus
  1499.         IF (ToggleAnimate = 1) THEN
  1500.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x - modifier * vhat(1)
  1501.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y - modifier * vhat(2)
  1502.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z - modifier * vhat(3)
  1503.         END IF
  1504.     END IF
  1505.     IF (_KEYDOWN(69) <> 0) OR (_KEYDOWN(101) <> 0) THEN ' E or e
  1506.         CALL StrafeCameraVhatPlus
  1507.         IF (ToggleAnimate = 1) THEN
  1508.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x + modifier * vhat(1)
  1509.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y + modifier * vhat(2)
  1510.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z + modifier * vhat(3)
  1511.         END IF
  1512.     END IF
  1513.     IF (_KEYDOWN(19200) <> 0) OR (_KEYDOWN(52) <> 0) THEN CALL RotateUhatMinus: CALL CalculateScreenVectors ' 4
  1514.     IF (_KEYDOWN(19712) <> 0) OR (_KEYDOWN(54) <> 0) THEN CALL RotateUhatPlus: CALL CalculateScreenVectors ' 6
  1515.     IF (_KEYDOWN(56) <> 0) THEN CALL RotateVhatPlus: CALL CalculateScreenVectors ' 8
  1516.     IF (_KEYDOWN(50) <> 0) THEN CALL RotateVhatMinus: CALL CalculateScreenVectors ' 2
  1517.     IF (_KEYDOWN(55) <> 0) THEN CALL RotateClockwise ' 7
  1518.     IF (_KEYDOWN(57) <> 0) THEN CALL RotateCounterclockwise ' 9
  1519.     IF (_KEYDOWN(49) <> 0) THEN CALL RotateUhatMinus: CALL CalculateScreenVectors: CALL RotateClockwise ' 1
  1520.     IF (_KEYDOWN(51) <> 0) THEN CALL RotateUhatPlus: CALL CalculateScreenVectors: CALL RotateCounterclockwise ' 3
  1521.  
  1522.     DIM k AS LONG
  1523.     DIM p AS LONG
  1524.     DIM l AS LONG
  1525.     DIM kh AS INTEGER
  1526.     kh = _KEYHIT
  1527.     IF (kh <> 0) THEN
  1528.         SELECT CASE kh
  1529.             CASE 27
  1530.                 SYSTEM
  1531.             CASE ASC(" ")
  1532.                 ToggleHUD = -ToggleHUD
  1533.             CASE ASC("b"), ASC("B")
  1534.                 DIM gtmp AS LONG
  1535.                 gtmp = NewCube&(1, "Custom block", 350, PlayerCamera.Position.x - 40 * nhat(1), PlayerCamera.Position.y - 40 * nhat(2), PlayerCamera.Position.z - 40 * nhat(3), 10, 10, 10, Lime, Purple, Teal, 0)
  1536.                 CALL ClusterPinch(gtmp)
  1537.             CASE ASC("k"), ASC("K")
  1538.                 ' set -999? delete actual vectors? deal with cluster headers
  1539.                 p = Group(ClosestGroup).Pointer
  1540.                 l = Group(ClosestGroup).Lagger
  1541.                 Group(l).Pointer = p
  1542.                 IF (p <> -999) THEN
  1543.                     Group(p).Lagger = l
  1544.                 END IF
  1545.             CASE ASC("m"), ASC("M")
  1546.                 CALL CreateMission(10)
  1547.             CASE ASC("n"), ASC("N")
  1548.                 FOR k = Group(ClosestGroup).FirstVector TO Group(ClosestGroup).LastVector
  1549.                     vec3Dvel(k, 1) = (RND - .5) * .20
  1550.                     vec3Dvel(k, 2) = (RND - .5) * .20
  1551.                     vec3Dvel(k, 3) = (RND - .5) * .20
  1552.                 NEXT
  1553.             CASE ASC("o"), ASC("O")
  1554.                 PlayerCamera.Position.x = -40
  1555.                 PlayerCamera.Position.y = 30
  1556.                 PlayerCamera.Position.z = 40
  1557.                 uhat(1) = -.2078192: uhat(2) = -.9781672: uhat(3) = 0
  1558.                 vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
  1559.                 CALL CalculateScreenVectors
  1560.             CASE ASC("t"), ASC("T")
  1561.                 ToggleAnimate = -ToggleAnimate
  1562.         END SELECT
  1563.     END IF
  1564.     _KEYCLEAR
  1565.  
  1566. ' Color utility.
  1567.  
  1568. FUNCTION ShadeMix~& (Shade1 AS _UNSIGNED LONG, Shade2 AS _UNSIGNED LONG, Weight AS DOUBLE)
  1569.     ShadeMix~& = _RGB32((1 - Weight) * _RED32(Shade1) + Weight * _RED32(Shade2), (1 - Weight) * _GREEN32(Shade1) + Weight * _GREEN32(Shade2), (1 - Weight) * _BLUE32(Shade1) + Weight * _BLUE32(Shade2))
  1570.  
  1571. ' Cartesian plotting.
  1572.  
  1573. SUB cline (x1 AS DOUBLE, y1 AS DOUBLE, x2 AS DOUBLE, y2 AS DOUBLE, col AS _UNSIGNED LONG)
  1574.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  1575.  
  1576. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  1577.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  1578.  
  1579. SUB ccircle (x1 AS DOUBLE, y1 AS DOUBLE, rad AS DOUBLE, col AS _UNSIGNED LONG)
  1580.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  1581.  
  1582. SUB lineSmooth (x0, y0, x1, y1, c AS _UNSIGNED LONG)
  1583.     'Inspiration credit: {(FellippeHeitor)(qb64.org)(2020)}
  1584.     '                    {https://en.wikipedia.org/w/index.php?title=Xiaolin_Wu%27s_line_algorithm&oldid=852445548}
  1585.     'Edit: {(STxAxTIC)(2020-11-20)(Correction to alpha channel.)}
  1586.  
  1587.     DIM plX AS INTEGER, plY AS INTEGER, plI
  1588.  
  1589.     DIM steep AS _BYTE
  1590.     steep = ABS(y1 - y0) > ABS(x1 - x0)
  1591.  
  1592.     IF steep THEN
  1593.         SWAP x0, y0
  1594.         SWAP x1, y1
  1595.     END IF
  1596.  
  1597.     IF x0 > x1 THEN
  1598.         SWAP x0, x1
  1599.         SWAP y0, y1
  1600.     END IF
  1601.  
  1602.     DIM dx, dy, gradient
  1603.     dx = x1 - x0
  1604.     dy = y1 - y0
  1605.     gradient = dy / dx
  1606.  
  1607.     IF dx = 0 THEN
  1608.         gradient = 1
  1609.     END IF
  1610.  
  1611.     'handle first endpoint
  1612.     DIM xend, yend, xgap, xpxl1, ypxl1
  1613.     xend = _ROUND(x0)
  1614.     yend = y0 + gradient * (xend - x0)
  1615.     xgap = (1 - ((x0 + .5) - INT(x0 + .5)))
  1616.     xpxl1 = xend 'this will be used in the main loop
  1617.     ypxl1 = INT(yend)
  1618.     IF steep THEN
  1619.         plX = ypxl1
  1620.         plY = xpxl1
  1621.         plI = (1 - (yend - INT(yend))) * xgap
  1622.         GOSUB plot
  1623.  
  1624.         plX = ypxl1 + 1
  1625.         plY = xpxl1
  1626.         plI = (yend - INT(yend)) * xgap
  1627.         GOSUB plot
  1628.     ELSE
  1629.         plX = xpxl1
  1630.         plY = ypxl1
  1631.         plI = (1 - (yend - INT(yend))) * xgap
  1632.         GOSUB plot
  1633.  
  1634.         plX = xpxl1
  1635.         plY = ypxl1 + 1
  1636.         plI = (yend - INT(yend)) * xgap
  1637.         GOSUB plot
  1638.     END IF
  1639.  
  1640.     DIM intery
  1641.     intery = yend + gradient 'first y-intersection for the main loop
  1642.  
  1643.     'handle second endpoint
  1644.     DIM xpxl2, ypxl2
  1645.     xend = _ROUND(x1)
  1646.     yend = y1 + gradient * (xend - x1)
  1647.     xgap = ((x1 + .5) - INT(x1 + .5))
  1648.     xpxl2 = xend 'this will be used in the main loop
  1649.     ypxl2 = INT(yend)
  1650.     IF steep THEN
  1651.         plX = ypxl2
  1652.         plY = xpxl2
  1653.         plI = (1 - (yend - INT(yend))) * xgap
  1654.         GOSUB plot
  1655.  
  1656.         plX = ypxl2 + 1
  1657.         plY = xpxl2
  1658.         plI = (yend - INT(yend)) * xgap
  1659.         GOSUB plot
  1660.     ELSE
  1661.         plX = xpxl2
  1662.         plY = ypxl2
  1663.         plI = (1 - (yend - INT(yend))) * xgap
  1664.         GOSUB plot
  1665.  
  1666.         plX = xpxl2
  1667.         plY = ypxl2 + 1
  1668.         plI = (yend - INT(yend)) * xgap
  1669.         GOSUB plot
  1670.     END IF
  1671.  
  1672.     'main loop
  1673.     DIM x
  1674.     IF steep THEN
  1675.         FOR x = xpxl1 + 1 TO xpxl2 - 1
  1676.             plX = INT(intery)
  1677.             plY = x
  1678.             plI = (1 - (intery - INT(intery)))
  1679.             GOSUB plot
  1680.  
  1681.             plX = INT(intery) + 1
  1682.             plY = x
  1683.             plI = (intery - INT(intery))
  1684.             GOSUB plot
  1685.  
  1686.             intery = intery + gradient
  1687.         NEXT
  1688.     ELSE
  1689.         FOR x = xpxl1 + 1 TO xpxl2 - 1
  1690.             plX = x
  1691.             plY = INT(intery)
  1692.             plI = (1 - (intery - INT(intery)))
  1693.             GOSUB plot
  1694.  
  1695.             plX = x
  1696.             plY = INT(intery) + 1
  1697.             plI = (intery - INT(intery))
  1698.             GOSUB plot
  1699.  
  1700.             intery = intery + gradient
  1701.         NEXT
  1702.     END IF
  1703.  
  1704.     EXIT SUB
  1705.  
  1706.     plot:
  1707.     ' Change to regular PSET for standard coordinate orientation.
  1708.     CALL cpset(plX, plY, _RGB32(_RED32(c), _GREEN32(c), _BLUE32(c), plI * _ALPHA32(c)))
  1709.     RETURN
  1710.  
  1711. ' Camera transformation
  1712.  
  1713. SUB PlayerDynamics
  1714.     DIM k AS INTEGER
  1715.  
  1716.     IF (ToggleAnimate = 1) THEN
  1717.         PlayerCamera.Velocity.x = .95 * PlayerCamera.Velocity.x
  1718.         PlayerCamera.Velocity.y = .95 * PlayerCamera.Velocity.y
  1719.         PlayerCamera.Velocity.z = .95 * PlayerCamera.Velocity.z
  1720.         PlayerCamera.Position.x = PlayerCamera.Position.x + PlayerCamera.Velocity.x
  1721.         PlayerCamera.Position.y = PlayerCamera.Position.y + PlayerCamera.Velocity.y
  1722.         PlayerCamera.Position.z = PlayerCamera.Position.z + PlayerCamera.Velocity.z
  1723.     END IF
  1724.  
  1725.     FOR k = 1 TO MissionTicker
  1726.         IF (Group(ClosestGroup).GroupName = Mission(k).Label) THEN
  1727.             IF (Mission(k).Discovered = 0) THEN
  1728.                 Mission(k).Discovered = 1
  1729.                 SOUND 500, .25
  1730.             END IF
  1731.         END IF
  1732.     NEXT
  1733.  
  1734. SUB RotateUhatPlus
  1735.     uhat(1) = uhat(1) + nhat(1) * .0333
  1736.     uhat(2) = uhat(2) + nhat(2) * .0333
  1737.     uhat(3) = uhat(3) + nhat(3) * .0333
  1738.  
  1739. SUB RotateUhatMinus
  1740.     uhat(1) = uhat(1) - nhat(1) * .0333
  1741.     uhat(2) = uhat(2) - nhat(2) * .0333
  1742.     uhat(3) = uhat(3) - nhat(3) * .0333
  1743.  
  1744. SUB RotateVhatPlus
  1745.     vhat(1) = vhat(1) + nhat(1) * .0333
  1746.     vhat(2) = vhat(2) + nhat(2) * .0333
  1747.     vhat(3) = vhat(3) + nhat(3) * .0333
  1748.  
  1749. SUB RotateVhatMinus
  1750.     vhat(1) = vhat(1) - nhat(1) * .0333
  1751.     vhat(2) = vhat(2) - nhat(2) * .0333
  1752.     vhat(3) = vhat(3) - nhat(3) * .0333
  1753.  
  1754. SUB RotateCounterclockwise
  1755.     DIM v1 AS DOUBLE
  1756.     DIM v2 AS DOUBLE
  1757.     DIM v3 AS DOUBLE
  1758.     v1 = vhat(1)
  1759.     v2 = vhat(2)
  1760.     v3 = vhat(3)
  1761.     vhat(1) = v1 + uhat(1) * .0333
  1762.     vhat(2) = v2 + uhat(2) * .0333
  1763.     vhat(3) = v3 + uhat(3) * .0333
  1764.     uhat(1) = uhat(1) - v1 * .0333
  1765.     uhat(2) = uhat(2) - v2 * .0333
  1766.     uhat(3) = uhat(3) - v3 * .0333
  1767.  
  1768. SUB RotateClockwise
  1769.     DIM v1 AS DOUBLE
  1770.     DIM v2 AS DOUBLE
  1771.     DIM v3 AS DOUBLE
  1772.     v1 = vhat(1)
  1773.     v2 = vhat(2)
  1774.     v3 = vhat(3)
  1775.     vhat(1) = v1 - uhat(1) * .0333
  1776.     vhat(2) = v2 - uhat(2) * .0333
  1777.     vhat(3) = v3 - uhat(3) * .0333
  1778.     uhat(1) = uhat(1) + v1 * .0333
  1779.     uhat(2) = uhat(2) + v2 * .0333
  1780.     uhat(3) = uhat(3) + v3 * .0333
  1781.  
  1782. SUB StrafeCameraUhatPlus
  1783.     PlayerCamera.Position.x = PlayerCamera.Position.x + uhat(1)
  1784.     PlayerCamera.Position.y = PlayerCamera.Position.y + uhat(2)
  1785.     PlayerCamera.Position.z = PlayerCamera.Position.z + uhat(3)
  1786.  
  1787. SUB StrafeCameraUhatMinus
  1788.     PlayerCamera.Position.x = PlayerCamera.Position.x - uhat(1)
  1789.     PlayerCamera.Position.y = PlayerCamera.Position.y - uhat(2)
  1790.     PlayerCamera.Position.z = PlayerCamera.Position.z - uhat(3)
  1791.  
  1792. SUB StrafeCameraVhatPlus
  1793.     PlayerCamera.Position.x = PlayerCamera.Position.x + vhat(1)
  1794.     PlayerCamera.Position.y = PlayerCamera.Position.y + vhat(2)
  1795.     PlayerCamera.Position.z = PlayerCamera.Position.z + vhat(3)
  1796.  
  1797. SUB StrafeCameraVhatMinus
  1798.     PlayerCamera.Position.x = PlayerCamera.Position.x - vhat(1)
  1799.     PlayerCamera.Position.y = PlayerCamera.Position.y - vhat(2)
  1800.     PlayerCamera.Position.z = PlayerCamera.Position.z - vhat(3)
  1801.  
  1802. SUB StrafeCameraNhatPlus
  1803.     PlayerCamera.Position.x = PlayerCamera.Position.x + nhat(1)
  1804.     PlayerCamera.Position.y = PlayerCamera.Position.y + nhat(2)
  1805.     PlayerCamera.Position.z = PlayerCamera.Position.z + nhat(3)
  1806.  
  1807. SUB StrafeCameraNhatMinus
  1808.     PlayerCamera.Position.x = PlayerCamera.Position.x - nhat(1)
  1809.     PlayerCamera.Position.y = PlayerCamera.Position.y - nhat(2)
  1810.     PlayerCamera.Position.z = PlayerCamera.Position.z - nhat(3)
  1811.  
  1812. '''
  1813.  
You're not done when it works, you're done when it's right.