Author Topic: Simple Pool Game in QBasic  (Read 5971 times)

0 Members and 1 Guest are viewing this topic.

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Simple Pool Game in QBasic
« on: May 15, 2021, 02:14:05 pm »
Hello

I wrote a simple pool game in QB using my physics engine. The game has no real rules just hit the balls in to the pockets. The instructions on how to play are visible when you start.

Since pool is inherently a 3D game, and my physics engine is 2D only. I do have not real good way to simulate full 'English' on the cue ball, so it only has left and right spin as of now. To help atone for that, I have provided the <TAB> key to help you predict your shots.

While doing these exercises I'm slowly improving the engine and making it more robust. This for example I learned how to "see into the future." Slowly progress is being made on commenting it, and making it easier for other to use in their projects. One day maybe even actual documentation.

I apologize for putting it into a zip file. Its the only good way to keep the directory structure with the assets. All assets are CC0 public domain. Also, for some reason the 13 ball is solid. ¯\_(ツ)_/¯

I would love to hear your feedback good or bad, and any suggestions to help make it more accessible and easier to use in your projects.

If you just want to look over the code. (Note: it will need the provided assets to run.)
Code: QB64: [Select]
  1. '$dynamic
  2. '**********************************************************************************************
  3. '   FzxNGN written by justsomeguy
  4. '   Physics code ported from RandyGaul's Impulse Engine
  5. '   https://github.com/RandyGaul/ImpulseEngine
  6. '   http://RandyGaul.net
  7. '**********************************************************************************************
  8. '/*
  9. '    Copyright (c) 2013 Randy Gaul http://RandyGaul.net
  10.  
  11. '    This software is provided 'as-is', without any express or implied
  12. '    warranty. In no event will the authors be held liable for any damages
  13. '    arising from the use of this software.
  14.  
  15. '    Permission is granted to anyone to use this software for any purpose,
  16. '    including commercial applications, and to alter it and redistribute it
  17. '    freely, subject to the following restrictions:
  18. '      1. The origin of this software must not be misrepresented; you must not
  19. '         claim that you wrote the original software. If you use this software
  20. '         in a product, an acknowledgment in the product documentation would be
  21. '         appreciated but is not required.
  22. '      2. Altered source versions must be plainly marked as such, and must not be
  23. '         misrepresented as being the original software.
  24. '      3. This notice may not be removed or altered from any source distribution.
  25. '
  26. '    Port to QB64 by justsomeguy
  27. '*/
  28. '
  29.  
  30. '**********************************************************************************************
  31. '   Setup Types and Variables
  32. '**********************************************************************************************
  33. TYPE tVECTOR2d
  34.     x AS _FLOAT
  35.     y AS _FLOAT
  36.  
  37. TYPE tLINE2d ' Not used
  38.     a AS tVECTOR2d
  39.     b AS tVECTOR2d
  40.  
  41. TYPE tFACE2d ' Not used
  42.     f0 AS tVECTOR2d
  43.     f1 AS tVECTOR2d
  44.  
  45. TYPE tTRIANGLE ' Not used
  46.     a AS tVECTOR2d
  47.     b AS tVECTOR2d
  48.     c AS tVECTOR2d
  49.  
  50. TYPE tMATRIX2d
  51.     m00 AS _FLOAT
  52.     m01 AS _FLOAT
  53.     m10 AS _FLOAT
  54.     m11 AS _FLOAT
  55.  
  56. TYPE tSHAPE
  57.     ty AS INTEGER ' cSHAPE_CIRCLE = 1, cSHAPE_POLYGON = 2
  58.     radius AS _FLOAT ' Only necessary for circle shapes
  59.     u AS tMATRIX2d ' Only necessary for polygons
  60.     texture AS LONG
  61.     flipTexture AS INTEGER 'flag for flipping texture depending on direction
  62.     scaleTextureX AS _FLOAT
  63.     scaleTextureY AS _FLOAT
  64.     offsetTextureX AS _FLOAT
  65.     offsetTextureY AS _FLOAT
  66.  
  67. TYPE tPOLY 'list of vertices for all objects in simulation
  68.     vert AS tVECTOR2d
  69.     norm AS tVECTOR2d
  70.  
  71. TYPE tPOLYATTRIB 'keep track of polys in monlithic list of vertices
  72.     start AS INTEGER ' starting vertex of the polygon
  73.     count AS INTEGER ' number of vertices in polygon
  74.  
  75. TYPE tBODY
  76.     objectName AS STRING * 64
  77.     objectHash AS _INTEGER64
  78.     position AS tVECTOR2d
  79.     velocity AS tVECTOR2d
  80.     force AS tVECTOR2d
  81.     angularVelocity AS _FLOAT
  82.     torque AS _FLOAT
  83.     orient AS _FLOAT
  84.     mass AS _FLOAT
  85.     invMass AS _FLOAT
  86.     inertia AS _FLOAT
  87.     invInertia AS _FLOAT
  88.     staticFriction AS _FLOAT
  89.     dynamicFriction AS _FLOAT
  90.     restitution AS _FLOAT
  91.     shape AS tSHAPE
  92.     pa AS tPOLYATTRIB
  93.     c AS LONG ' color
  94.     enable AS INTEGER 'Hide a body ;)
  95.     collisionMask AS _UNSIGNED INTEGER
  96.  
  97. TYPE tMANIFOLD
  98.     A AS INTEGER
  99.     B AS INTEGER
  100.     penetration AS _FLOAT
  101.     normal AS tVECTOR2d
  102.     contactCount AS INTEGER
  103.     e AS _FLOAT
  104.     df AS _FLOAT
  105.     sf AS _FLOAT
  106.     cv AS _FLOAT 'contact velocity
  107.  
  108. TYPE tHIT
  109.     A AS INTEGER
  110.     B AS INTEGER
  111.     position AS tVECTOR2d
  112.     cv AS _FLOAT
  113.  
  114. TYPE tJOINT
  115.     jointName AS STRING * 64
  116.     jointHash AS _INTEGER64
  117.     M AS tMATRIX2d
  118.     localAnchor1 AS tVECTOR2d
  119.     localAnchor2 AS tVECTOR2d
  120.     r1 AS tVECTOR2d
  121.     r2 AS tVECTOR2d
  122.     bias AS tVECTOR2d
  123.     P AS tVECTOR2d
  124.     body1 AS INTEGER
  125.     body2 AS INTEGER
  126.     biasFactor AS _FLOAT
  127.     softness AS _FLOAT
  128.  
  129. TYPE tCAMERA
  130.     position AS tVECTOR2d
  131.     zoom AS _FLOAT
  132.  
  133. TYPE tWORLD
  134.     plusLimit AS tVECTOR2d
  135.     minusLimit AS tVECTOR2d
  136.     gravity AS tVECTOR2d
  137.     spawn AS tVECTOR2d
  138.     terrainPosition AS tVECTOR2d
  139.  
  140. TYPE tVEHICLE
  141.     vehicleName AS STRING * 64
  142.     vehicleHash AS _INTEGER64
  143.     body AS INTEGER
  144.     wheelOne AS INTEGER
  145.     wheelTwo AS INTEGER
  146.     axleJointOne AS INTEGER
  147.     axleJointTwo AS INTEGER
  148.     auxBodyOne AS INTEGER
  149.     auxJointOne AS INTEGER
  150.     wheelOneOffset AS tVECTOR2d
  151.     wheelTwoOffset AS tVECTOR2d
  152.     auxOneOffset AS tVECTOR2d
  153.     torque AS _FLOAT
  154.  
  155. TYPE tOBJECTMANAGER
  156.     objectCount AS INTEGER
  157.     jointCount AS INTEGER
  158.  
  159. TYPE tINPUTDEVICE
  160.     x AS INTEGER
  161.     y AS INTEGER
  162.     b1 AS INTEGER
  163.     lb1 AS INTEGER
  164.     b2 AS INTEGER
  165.     lb2 AS INTEGER
  166.     b3 AS INTEGER
  167.     lb3 AS INTEGER
  168.     w AS INTEGER
  169.     wCount AS INTEGER
  170.     keyHit AS LONG
  171.     lastKeyHit AS LONG
  172.  
  173. CONST cSHAPE_CIRCLE = 1
  174. CONST cSHAPE_POLYGON = 2
  175. CONST cPRECISION = 100
  176. CONST cMAXNUMOFTRIANGLES = 100
  177. CONST cMAXNUMBEROFOBJECTS = 1000 ' Max number of objects at one time
  178. CONST cMAXNUMBEROFPOLYGONS = 10000 ' Max number of total vertices included in all objects
  179. CONST cMAXNUMBEROFJOINTS = 100
  180. CONST cMAXNUMBEROFHITS = 10000
  181. CONST cPI = 3.14159
  182. CONST cEPSILON = 0.0001
  183. CONST cEPSILON_SQ = cEPSILON * cEPSILON
  184. CONST cBIAS_RELATIVE = 0.95
  185. CONST cBIAS_ABSOLUTE = 0.01
  186. CONST cDT = 1.0 / 120.0
  187. CONST cITERATIONS = 100
  188. CONST cPENETRATION_ALLOWANCE = 0.05
  189. CONST cPENETRATION_CORRECTION = 0.4 ' misspelled in original code
  190. CONST cGLOBAL_FRICTION = .992
  191. CONST cPARAMETER_POSITION = 1
  192. CONST cPARAMETER_VELOCITY = 2
  193. CONST cPARAMETER_FORCE = 3
  194. CONST cPARAMETER_ANGULARVELOCITY = 4
  195. CONST cPARAMETER_TORQUE = 5
  196. CONST cPARAMETER_ORIENT = 6
  197. CONST cPARAMETER_STATICFRICTION = 7
  198. CONST cPARAMETER_DYNAMICFRICTION = 8
  199. CONST cPARAMETER_RESTITUTION = 9
  200. CONST cPARAMETER_COLOR = 10
  201. CONST cPARAMETER_ENABLE = 11
  202. CONST cPARAMETER_STATIC = 12
  203. CONST cPARAMETER_TEXTURE = 13
  204. CONST cPARAMETER_FLIPTEXTURE = 14
  205. CONST cPARAMETER_COLLISIONMASK = 15
  206. CONST cRENDER_JOINTS = 0
  207. CONST cRENDER_COLLISIONS = 0
  208. CONST cPLAYER_FORCE = 600000
  209.  
  210. DIM SHARED sRESTING AS _FLOAT
  211. DIM SHARED sNUMBEROFBODIES AS INTEGER: sNUMBEROFBODIES = 10 ' 0 is included
  212. DIM SHARED sNUMBEROFJOINTS AS INTEGER: sNUMBEROFJOINTS = 3 ' if zero then no joints at all
  213. DIM SHARED sNUMBEROFVEHICLES AS INTEGER: sNUMBEROFVEHICLES = 1
  214. DIM SHARED sTIMER AS LONG: sTIMER = TIMER
  215.  
  216. DIM poly(cMAXNUMBEROFPOLYGONS) AS tPOLY
  217. DIM body(sNUMBEROFBODIES) AS tBODY
  218. DIM joints(cMAXNUMBEROFJOINTS) AS tJOINT
  219. DIM hits(cMAXNUMBEROFHITS) AS tHIT
  220. DIM veh(sNUMBEROFVEHICLES) AS tVEHICLE
  221. DIM camera AS tCAMERA
  222. DIM inputDevice AS tINPUTDEVICE
  223.  
  224.  
  225. DIM texture(100) AS LONG
  226.  
  227. TYPE tPOOL
  228.     status AS INTEGER
  229.     rackposition AS tVECTOR2d
  230.     objId AS _INTEGER64
  231. TYPE tGENPOOL
  232.     instructions AS INTEGER
  233.     instructionImage AS LONG
  234.     mode AS INTEGER
  235.     english AS tVECTOR2d
  236. TYPE tSENSORS
  237.     objId AS _INTEGER64
  238.  
  239.  
  240. DIM timerOne AS INTEGER
  241. DIM SHARED world AS tWORLD
  242. DIM SHARED objectManager AS tOBJECTMANAGER
  243. DIM SHARED pool(17) AS tPOOL
  244. DIM SHARED genPool AS tGENPOOL
  245. DIM SHARED poolSensors(10) AS tSENSORS
  246.  
  247.  
  248. '**********************************************************************************************
  249. _TITLE "FzxNGN POOL"
  250. SCREEN _NEWIMAGE(1024, 768, 32)
  251. timerOne = _FREETIMER
  252. ON TIMER(timerOne, 1) renderFps
  253. TIMER(timerOne) ON
  254. camera.zoom = .25
  255. 'RANDOMIZE TIMER
  256. CALL buildSimpleScene(poly(), body(), joints(), texture(), veh())
  257. '**********************************************************************************************
  258. DO: fps = fps + 1
  259.     CLS , _RGB32(67, 39, 0)
  260.     LOCATE 1, 55: PRINT "FPS:"; (fpsLast)
  261.  
  262.     CALL renderBodies(poly(), body(), joints(), hits(), camera)
  263.     CALL animateScene(poly(), body(), joints(), hits(), texture(), camera, veh(), inputDevice)
  264.     CALL impulseStep(poly(), body(), joints(), hits(), cDT, cITERATIONS)
  265.  
  266.     _DISPLAY
  267.     _LIMIT 120
  268. '**********************************************************************************************
  269. '   End of Main loop
  270. '**********************************************************************************************
  271.  
  272.  
  273. '**********************************************************************************************
  274. '   Scene Creation and Handling Ahead
  275. '**********************************************************************************************
  276. SUB animateScene (poly() AS tPOLY, body() AS tBODY, joints() AS tJOINT, hits() AS tHIT, textureMap() AS LONG, camera AS tCAMERA, veh() AS tVEHICLE, iDevice AS tINPUTDEVICE)
  277.     ' Cache some widely used ID's
  278.     DIM cueBall AS _INTEGER64: cueBall = objectManagerID(body(), "cSCENE_BALL16")
  279.     DIM cue AS _INTEGER64: cue = objectManagerID(body(), "cSCENE_CUE")
  280.  
  281.     DIM AS INTEGER index, ballCount
  282.     DIM AS _FLOAT cueAngle, cueDistance, cueDrawingDistance
  283.     DIM AS tVECTOR2d cueNormal, cuePos, cueBall2Camera, cuePower, mousePosVector
  284.     DIM guideLine AS tLINE2d
  285.     DIM AS LONG ghostImg
  286.  
  287.     ' Ignore this
  288.     veh(0).vehicleName = "Nothing" 'this is just to clear the warnings
  289.  
  290.     ' Mouse and keyboard handling, mainly mouse, keyboard is too slow for indirect
  291.     CALL handleInputDevice(iDevice)
  292.     ' Change cueball to camera coordinate system.
  293.     CALL worldToCamera(body(), camera, cueBall, cueBall2Camera)
  294.     CALL vectorSet(mousePosVector, iDevice.x, iDevice.y)
  295.  
  296.     ' Some Maths
  297.     cueNormal.x = cueBall2Camera.x - iDevice.x ' x-Component of Distance
  298.     cueNormal.y = cueBall2Camera.y - iDevice.y ' y-Component of Distance
  299.     cueAngle = _ATAN2(-cueNormal.y, -cueNormal.x) ' Use mouse to control the cue angle
  300.     cueDistance = vectorDistance(mousePosVector, cueBall2Camera)
  301.     cueDrawingDistance = (cueDistance * .25) + 450 ' Use this to guage power
  302.     cuePos.x = COS(cueAngle) * cueDrawingDistance + body(cueBall).position.x ' Use these to calculate cue position
  303.     cuePos.y = SIN(cueAngle) * cueDrawingDistance + body(cueBall).position.y
  304.     cuePower.x = cueNormal.x * cPLAYER_FORCE
  305.     cuePower.y = cueNormal.y * cPLAYER_FORCE
  306.  
  307.     guideLine.a.x = COS(cueAngle + _PI) * (25 * camera.zoom) + cueBall2Camera.x
  308.     guideLine.a.y = SIN(cueAngle + _PI) * (25 * camera.zoom) + cueBall2Camera.y
  309.     guideLine.b.x = COS(cueAngle + _PI) * (1000 * camera.zoom) + cueBall2Camera.x
  310.     guideLine.b.y = SIN(cueAngle + _PI) * (1000 * camera.zoom) + cueBall2Camera.y
  311.  
  312.     ' Aim the Stick
  313.     IF bodyAtRest(body(cueBall)) THEN ' AIM
  314.         CALL setBody(body(), cPARAMETER_ENABLE, cue, 1, 0)
  315.         CALL setBody(body(), cPARAMETER_ORIENT, cue, cueAngle, 0)
  316.         CALL setBody(body(), cPARAMETER_POSITION, cue, cuePos.x, cuePos.y)
  317.         LINE (guideLine.a.x, guideLine.a.y)-(guideLine.b.x, guideLine.b.y)
  318.         camera.position = body(cueBall).position
  319.     ELSE
  320.         CALL setBody(body(), cPARAMETER_ENABLE, cue, 0, 0)
  321.     END IF
  322.  
  323.     ' Camera Zoom
  324.     camera.zoom = camera.zoom + (iDevice.wCount * .01): iDevice.wCount = 0
  325.     IF camera.zoom < .125 THEN camera.zoom = .125
  326.  
  327.     ' Hit the ball
  328.     ' Verify cue ball is not moving - I really need to check that all balls are not moving.
  329.     IF bodyAtRest(body(cueBall)) THEN
  330.         IF iDevice.lb1 = -1 AND iDevice.b1 = 0 THEN ' only fire once
  331.             'Apply hitting Force and English
  332.             CALL setBody(body(), cPARAMETER_FORCE, cueBall, cuePower.x, cuePower.y)
  333.             CALL setBody(body(), cPARAMETER_ANGULARVELOCITY, cueBall, genPool.english.x, 0)
  334.             ' Reset English
  335.             genPool.english.x = 0
  336.             genPool.english.y = 0
  337.         END IF
  338.     END IF
  339.  
  340.     IF _KEYDOWN(19200) THEN ' Left English
  341.         genPool.english.x = genPool.english.x + 1
  342.         IF genPool.english.x > 200 THEN genPool.english.x = 200
  343.     END IF
  344.  
  345.     IF _KEYDOWN(19712) THEN ' Right English
  346.         genPool.english.x = genPool.english.x - 1
  347.         IF genPool.english.x < -200 THEN genPool.english.x = -200
  348.     END IF
  349.  
  350.     ' Instruction GUI
  351.     IF _KEYDOWN(ASC("I")) OR _KEYDOWN(ASC("i")) THEN ' Show instructions
  352.         genPool.instructionImage = _COPYIMAGE(textureMap(22))
  353.         genPool.instructions = 1
  354.         sTIMER = TIMER
  355.     END IF
  356.     IF TIMER - sTIMER > 20 THEN genPool.instructions = 0 ' instruction timeout 20 sec
  357.     IF genPool.instructions THEN
  358.         genPool.instructionImage = _COPYIMAGE(textureMap(22))
  359.         IF TIMER - sTIMER > 15 THEN 'After 15 seconds fade it out
  360.             _SETALPHA INT((20 - (TIMER - sTIMER)) * 50), _RGB(255, 255, 255), genPool.instructionImage
  361.         END IF
  362.         _PUTIMAGE (0, 0), genPool.instructionImage, 0 ' INSTRUCTIONS
  363.         _FREEIMAGE genPool.instructionImage
  364.     END IF
  365.  
  366.     IF _KEYDOWN(ASC("R")) OR _KEYDOWN(ASC("r")) THEN ' ReRack balls
  367.         ' Put all balls back in their rack positions
  368.         CALL reRackBalls(body())
  369.     END IF
  370.  
  371.     IF _KEYDOWN(32) THEN ' Spacebar  - For break
  372.         ' Verify cue ball is not moving - I really need to check that all balls are not moving.
  373.         IF bodyAtRest(body(cueBall)) THEN
  374.             CALL setBody(body(), cPARAMETER_ANGULARVELOCITY, cueBall, genPool.english.x, 0)
  375.             CALL setBody(body(), cPARAMETER_FORCE, cueBall, cuePower.x * 2, cuePower.y * 2)
  376.             genPool.english.x = 0
  377.             genPool.english.y = 0
  378.         END IF
  379.     END IF
  380.  
  381.     ' Check your shot feature
  382.     IF _KEYDOWN(9) AND bodyAtRest(body(cueBall)) THEN 'TAB - LOOK ahead
  383.         DIM ghostBody(UBOUND(body)) AS tBODY ' Create a new list of bodies
  384.         CALL copyBodies(body(), ghostBody()) ' Copy all the bodies to the new list
  385.         ' Apply Forces to the Cue Ball in the Bodies
  386.         CALL setBody(ghostBody(), cPARAMETER_FORCE, cueBall, cuePower.x, cuePower.y)
  387.         CALL setBody(ghostBody(), cPARAMETER_ANGULARVELOCITY, cueBall, genPool.english.x, 0)
  388.         ' Create a Ghost Image Overlay
  389.         ghostImg = _NEWIMAGE(_WIDTH(0), _HEIGHT(0), 32)
  390.         _DEST ghostImg
  391.         _SETALPHA 127, 0 TO 255, ghostImg
  392.         ' Hide the table since we only car abou the balls
  393.         CALL setBodyEx(ghostBody(), cPARAMETER_ENABLE, "cSCENE_TABLE", 0, 0)
  394.         ' Run Simulation for about 2 secs
  395.         FOR index = 0 TO 240 ' number of frames for look ahead .. 240 / 120 fps = 2 sec
  396.             CALL impulseStep(poly(), ghostBody(), joints(), hits(), cDT, cITERATIONS)
  397.             CALL handlePocketSensors(ghostBody(), hits(), cueBall)
  398.             CALL renderBodies(poly(), ghostBody(), joints(), hits(), camera)
  399.         NEXT
  400.         ' Return Screen to normal
  401.         _DEST 0
  402.         ' Display overlay
  403.         _PUTIMAGE (0, 0), ghostImg, 0
  404.         _DISPLAY
  405.         ' Free up overlay
  406.         _FREEIMAGE ghostImg
  407.     END IF
  408.  
  409.     ballCount = 0
  410.     FOR index = 1 TO 16 'Make balls roll across ball return
  411.         ' check if balls are off the table and then give it "gravity"
  412.         IF body(pool(index).objId).position.y > 750 THEN
  413.             CALL setBody(body(), cPARAMETER_FORCE, pool(index).objId, 200000, 100000)
  414.             ballCount = ballCount + 1
  415.         END IF
  416.         ' rerack table if all the balls are in the ball return
  417.         IF ballCount > 14 AND bodyAtRest(body(cueBall)) THEN CALL reRackBalls(body())
  418.     NEXT
  419.  
  420.     ' Show english Indicator - Only Left and Right are supported
  421.     _PUTIMAGE (0, _HEIGHT(0) - 140), textureMap(16), 0,
  422.     CIRCLE (70 - genPool.english.x / 4, _HEIGHT(0) - 70), 10, _RGB(72, 111, 183)
  423.  
  424.  
  425.     CALL handlePocketSensors(body(), hits(), cueBall)
  426.     CALL cleanUpInputDevice(iDevice)
  427.  
  428. SUB handlePocketSensors (body() AS tBODY, hits() AS tHIT, cueBallId AS _INTEGER64)
  429.     DIM AS LONG index, ballId
  430.     FOR index = 0 TO 5 ' check if ball has been pocketed
  431.         ballId = isBodyTouching(hits(), poolSensors(index).objId)
  432.         IF ballId > 0 THEN
  433.             IF ballId = cueBallId THEN
  434.                 body(ballId).position = pool(16).rackposition ' Move to break position
  435.             ELSE
  436.                 body(ballId).position = pool(17).rackposition ' Move to ball return
  437.             END IF
  438.             CALL bodyStop(body(ballId))
  439.         END IF
  440.     NEXT
  441.  
  442. SUB reRackBalls (body() AS tBODY)
  443.     DIM AS INTEGER index
  444.     FOR index = 1 TO 16
  445.         body(pool(index).objId).position = pool(index).rackposition
  446.         CALL bodyStop(body(pool(index).objId))
  447.     NEXT
  448.  
  449. SUB handleInputDevice (iDevice AS tINPUTDEVICE)
  450.     ' iDevice.keyHit = _KEYHIT ' --- THIS IS TOO SLOW
  451.         iDevice.x = _MOUSEX
  452.         iDevice.y = _MOUSEY
  453.         iDevice.b1 = _MOUSEBUTTON(1)
  454.         iDevice.b2 = _MOUSEBUTTON(2)
  455.         iDevice.b3 = _MOUSEBUTTON(3)
  456.         iDevice.w = _MOUSEWHEEL
  457.         iDevice.wCount = iDevice.wCount + iDevice.w
  458.     LOOP
  459.  
  460. SUB cleanUpInputDevice (iDevice AS tINPUTDEVICE)
  461.     iDevice.lb1 = iDevice.b1
  462.     iDevice.lb2 = iDevice.b2
  463.     iDevice.lb3 = iDevice.b3
  464.     iDevice.lastKeyHit = iDevice.keyHit
  465.  
  466. FUNCTION bodyAtRest (body AS tBODY)
  467.     bodyAtRest = (body.velocity.x < 1 AND body.velocity.x > -1 AND body.velocity.y < 1 AND body.velocity.y > -1)
  468.  
  469. SUB copyBodies (body() AS tBODY, newBody() AS tBODY)
  470.     DIM AS LONG index
  471.     FOR index = 0 TO UBOUND(body)
  472.         newBody(index) = body(index)
  473.     NEXT
  474.  
  475. SUB buildSimpleScene (p() AS tPOLY, body() AS tBODY, j() AS tJOINT, textureMap() AS LONG, v() AS tVEHICLE)
  476.     DIM AS INTEGER trans, index, ballSize
  477.     DIM AS LONG iD
  478.     DIM b AS STRING
  479.     DIM AS _FLOAT bumperRestitution, bumperSF, bumperDF
  480.  
  481.     trans = 0 ' Adjust <0-255> to see the hidden rail bodies
  482.     ballSize = 36
  483.     bumperRestitution = .5
  484.     bumperSF = .01 '.5
  485.     bumperDF = .01 '.3
  486.     j(0).jointName = "Nothing" ' this is just to clear the warning and does nothing
  487.     v(0).vehicleName = "Nothing" ' Clear Warning
  488.     CALL loadBitmap(textureMap())
  489.  
  490.     '********************************************************
  491.     '   Setup World
  492.     '********************************************************
  493.  
  494.     CALL vectorSet(world.minusLimit, -6500, -2000)
  495.     CALL vectorSet(world.plusLimit, 70000, 10000)
  496.     CALL vectorSet(world.spawn, -5000, -800)
  497.     CALL vectorSet(world.gravity, 0.0, 0.0)
  498.     CALL vectorSet(world.terrainPosition, -7000.0, 1000.0)
  499.     DIM o AS tVECTOR2d: CALL vectorMultiplyScalarND(o, world.gravity, cDT): sRESTING = vectorLengthSq(o) + cEPSILON
  500.  
  501.     '********************************************************
  502.     '   Build Table
  503.     '********************************************************
  504.  
  505.  
  506.     iD = createBoxBodiesEx(p(), body(), "cSCENE_TABLE", 1000, 600)
  507.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_TABLE", 0, 0)
  508.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_TABLE", 0, 0)
  509.     CALL setBodyEx(body(), cPARAMETER_COLLISIONMASK, "cSCENE_TABLE", 0, 0)
  510.     CALL setBodyEx(body(), cPARAMETER_TEXTURE, "cSCENE_TABLE", textureMap(19), 0)
  511.  
  512.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BALLRETURN", 600, 100)
  513.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BALLRETURN", 0, 0)
  514.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BALLRETURN", 0, 800)
  515.     CALL setBodyEx(body(), cPARAMETER_COLLISIONMASK, "cSCENE_BALLRETURN", 0, 0)
  516.     CALL setBodyEx(body(), cPARAMETER_TEXTURE, "cSCENE_BALLRETURN", textureMap(21), 0)
  517.  
  518.  
  519.     '********************************************************
  520.     '   Build Bumpers
  521.     '********************************************************
  522.  
  523.     ' These are hidden using transparent colors
  524.  
  525.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER1", 370, 100)
  526.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BUMPER1", 0, 0)
  527.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BUMPER1", -445, -565)
  528.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BUMPER1", _RGBA32(0, 0, 255, trans), 0) ' blue
  529.     CALL setBodyEx(body(), cPARAMETER_RESTITUTION, "cSCENE_BUMPER1", bumperRestitution, 0)
  530.     CALL setBodyEx(body(), cPARAMETER_STATICFRICTION, "cSCENE_BUMPER1", bumperSF, 0)
  531.     CALL setBodyEx(body(), cPARAMETER_DYNAMICFRICTION, "cSCENE_BUMPER1", bumperDF, 0)
  532.  
  533.  
  534.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER2", 370, 100)
  535.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BUMPER2", 0, 0)
  536.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BUMPER2", -445, 565)
  537.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BUMPER2", _RGBA32(255, 0, 0, trans), 0) ' red
  538.     CALL setBodyEx(body(), cPARAMETER_RESTITUTION, "cSCENE_BUMPER2", bumperRestitution, 0)
  539.     CALL setBodyEx(body(), cPARAMETER_STATICFRICTION, "cSCENE_BUMPER2", bumperSF, 0)
  540.     CALL setBodyEx(body(), cPARAMETER_DYNAMICFRICTION, "cSCENE_BUMPER2", bumperDF, 0)
  541.  
  542.  
  543.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER3", 370, 100)
  544.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BUMPER3", 0, 0)
  545.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BUMPER3", 425, -565)
  546.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BUMPER3", _RGBA32(0, 255, 0, trans), 0) ' green
  547.     CALL setBodyEx(body(), cPARAMETER_RESTITUTION, "cSCENE_BUMPER3", bumperRestitution, 0)
  548.     CALL setBodyEx(body(), cPARAMETER_STATICFRICTION, "cSCENE_BUMPER3", bumperSF, 0)
  549.     CALL setBodyEx(body(), cPARAMETER_DYNAMICFRICTION, "cSCENE_BUMPER3", bumperDF, 0)
  550.  
  551.  
  552.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER4", 370, 100)
  553.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BUMPER4", 0, 0)
  554.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BUMPER4", 425, 565)
  555.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BUMPER4", _RGBA32(255, 255, 0, trans), 0) ' red and green
  556.     CALL setBodyEx(body(), cPARAMETER_RESTITUTION, "cSCENE_BUMPER4", bumperRestitution, 0)
  557.     CALL setBodyEx(body(), cPARAMETER_STATICFRICTION, "cSCENE_BUMPER4", bumperSF, 0)
  558.     CALL setBodyEx(body(), cPARAMETER_DYNAMICFRICTION, "cSCENE_BUMPER4", bumperDF, 0)
  559.  
  560.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER5", 100, 390)
  561.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BUMPER5", 0, 0)
  562.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BUMPER5", -970, 0)
  563.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BUMPER5", _RGBA32(255, 0, 255, trans), 0) 'red and blue
  564.     CALL setBodyEx(body(), cPARAMETER_RESTITUTION, "cSCENE_BUMPER5", bumperRestitution, 0)
  565.     CALL setBodyEx(body(), cPARAMETER_STATICFRICTION, "cSCENE_BUMPER5", bumperSF, 0)
  566.     CALL setBodyEx(body(), cPARAMETER_DYNAMICFRICTION, "cSCENE_BUMPER5", bumperDF, 0)
  567.  
  568.  
  569.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER6", 100, 390)
  570.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BUMPER6", 0, 0)
  571.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BUMPER6", 970, 0)
  572.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BUMPER6", _RGBA32(0, 255, 255, trans), 0) ' green and blue
  573.     CALL setBodyEx(body(), cPARAMETER_RESTITUTION, "cSCENE_BUMPER6", bumperRestitution, 0)
  574.     CALL setBodyEx(body(), cPARAMETER_STATICFRICTION, "cSCENE_BUMPER6", bumperSF, 0)
  575.     CALL setBodyEx(body(), cPARAMETER_DYNAMICFRICTION, "cSCENE_BUMPER6", bumperDF, 0)
  576.  
  577.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BORDER1", 50, 600)
  578.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BORDER1", 0, 0)
  579.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BORDER1", 970, 0)
  580.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BORDER1", _RGBA32(255, 255, 255, trans), 0)
  581.  
  582.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BORDER2", 50, 600)
  583.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BORDER2", 0, 0)
  584.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BORDER2", -970, 0)
  585.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BORDER2", _RGBA32(255, 255, 255, trans), 0)
  586.  
  587.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BORDER3", 1000, 50)
  588.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BORDER3", 0, 0)
  589.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BORDER3", 0, -560)
  590.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BORDER3", _RGBA32(255, 255, 255, trans), 0)
  591.  
  592.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BORDER4", 1000, 50)
  593.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BORDER4", 0, 0)
  594.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BORDER4", 0, 560)
  595.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BORDER4", _RGBA32(255, 255, 255, trans), 0)
  596.  
  597.  
  598.     '********************************************************
  599.     '   Build Ball Return hidden surfaces
  600.     '********************************************************
  601.     ' These are hidden using transparent colors
  602.  
  603.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BALLRETURNBOTTOM", 600, 10)
  604.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BALLRETURNBOTTOM", 0, 0)
  605.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BALLRETURNBOTTOM", 0, 850)
  606.     CALL setBodyEx(body(), cPARAMETER_ORIENT, "cSCENE_BALLRETURNBOTTOM", 0.01, 0)
  607.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BALLRETURNBOTTOM", _RGBA32(255, 255, 255, trans), 0)
  608.     CALL setBodyEx(body(), cPARAMETER_STATICFRICTION, "cSCENE_BALLRETURNBOTTOM", 10, 0)
  609.     CALL setBodyEx(body(), cPARAMETER_DYNAMICFRICTION, "cSCENE_BALLRETURNBOTTOM", 10, 0)
  610.  
  611.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BALLRETURNTOP", 600, 10)
  612.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BALLRETURNTOP", 0, 0)
  613.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BALLRETURNTOP", 0, 750)
  614.     CALL setBodyEx(body(), cPARAMETER_ORIENT, "cSCENE_BALLRETURNTOP", 0.01, 0)
  615.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BALLRETURNTOP", _RGBA32(255, 255, 255, trans), 0)
  616.     CALL setBodyEx(body(), cPARAMETER_STATICFRICTION, "cSCENE_BALLRETURNTOP", 10, 0)
  617.     CALL setBodyEx(body(), cPARAMETER_DYNAMICFRICTION, "cSCENE_BALLRETURNTOP", 10, 0)
  618.  
  619.  
  620.  
  621.  
  622.     iD = createBoxBodiesEx(p(), body(), "cSCENE_BALLRETURNEND", 10, 40)
  623.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_BALLRETURNEND", 0, 0)
  624.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_BALLRETURNEND", 550, 800)
  625.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_BALLRETURNEND", _RGBA32(255, 255, 255, trans), 0)
  626.  
  627.  
  628.     '********************************************************
  629.     '   Pocket sensors
  630.     '********************************************************
  631.  
  632.     iD = createCircleBodyEx(body(), "cSCENE_SENSOR1", 50)
  633.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_SENSOR1", 0, 0)
  634.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_SENSOR1", -5, 525)
  635.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_SENSOR1", _RGBA32(255, 255, 255, trans), 0)
  636.     poolSensors(0).objId = objectManagerID(body(), "cSCENE_SENSOR1")
  637.  
  638.     iD = createCircleBodyEx(body(), "cSCENE_SENSOR2", 50)
  639.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_SENSOR2", 0, 0)
  640.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_SENSOR2", -5, -525)
  641.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_SENSOR2", _RGBA32(255, 255, 255, trans), 0)
  642.     poolSensors(1).objId = objectManagerID(body(), "cSCENE_SENSOR2")
  643.  
  644.     iD = createCircleBodyEx(body(), "cSCENE_SENSOR3", 50)
  645.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_SENSOR3", 0, 0)
  646.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_SENSOR3", -900, -490)
  647.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_SENSOR3", _RGBA32(255, 255, 255, trans), 0)
  648.     poolSensors(2).objId = objectManagerID(body(), "cSCENE_SENSOR3")
  649.  
  650.     iD = createCircleBodyEx(body(), "cSCENE_SENSOR4", 50)
  651.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_SENSOR4", 0, 0)
  652.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_SENSOR4", 900, -490)
  653.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_SENSOR4", _RGBA32(255, 255, 255, trans), 0)
  654.     poolSensors(3).objId = objectManagerID(body(), "cSCENE_SENSOR4")
  655.  
  656.     iD = createCircleBodyEx(body(), "cSCENE_SENSOR5", 50)
  657.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_SENSOR5", 0, 0)
  658.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_SENSOR5", -900, 490)
  659.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_SENSOR5", _RGBA32(255, 255, 255, trans), 0)
  660.     poolSensors(4).objId = objectManagerID(body(), "cSCENE_SENSOR5")
  661.  
  662.     iD = createCircleBodyEx(body(), "cSCENE_SENSOR6", 50)
  663.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_SENSOR6", 0, 0)
  664.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_SENSOR6", 900, 490)
  665.     CALL setBodyEx(body(), cPARAMETER_COLOR, "cSCENE_SENSOR6", _RGBA32(255, 255, 255, trans), 0)
  666.     poolSensors(5).objId = objectManagerID(body(), "cSCENE_SENSOR6")
  667.  
  668.     '********************************************************
  669.     '   Place Balls
  670.     '********************************************************
  671.     DIM AS _FLOAT rX, rY
  672.  
  673.     FOR index = 1 TO 17
  674.         READ rX, rY
  675.         CALL vectorSet(pool(index).rackposition, rX, rY)
  676.     NEXT
  677.  
  678.     FOR index = 1 TO 16
  679.         b = "cSCENE_BALL" + LTRIM$(STR$(index))
  680.         iD = createCircleBodyEx(body(), b, ballSize)
  681.         CALL setBodyEx(body(), cPARAMETER_TEXTURE, b, textureMap(index), 0)
  682.         CALL setBodyEx(body(), cPARAMETER_RESTITUTION, b, 1.00, 0)
  683.         CALL setBodyEx(body(), cPARAMETER_STATICFRICTION, b, .50, 0) '.5
  684.         CALL setBodyEx(body(), cPARAMETER_DYNAMICFRICTION, b, .30, 0) '.3
  685.         CALL setBodyEx(body(), cPARAMETER_POSITION, b, pool(index).rackposition.x, pool(index).rackposition.y)
  686.         pool(index).objId = iD
  687.         pool(index).status = 0
  688.     NEXT
  689.  
  690.     '********************************************************
  691.     '   Cue Stick
  692.     '********************************************************
  693.  
  694.     DIM tv AS tVECTOR2d
  695.     CALL vectorSet(tv, 500, 0)
  696.  
  697.     iD = createBoxBodiesEx(p(), body(), "cSCENE_CUE", 400, 10)
  698.     CALL setBodyEx(body(), cPARAMETER_STATIC, "cSCENE_CUE", 0, 0)
  699.     CALL setBodyEx(body(), cPARAMETER_POSITION, "cSCENE_CUE", 0, 0)
  700.     CALL setBodyEx(body(), cPARAMETER_COLLISIONMASK, "cSCENE_CUE", 0, 0)
  701.     CALL setBodyEx(body(), cPARAMETER_TEXTURE, "cSCENE_CUE", textureMap(18), 0)
  702.  
  703.     DATA -475,0
  704.     DATA -548,38
  705.     DATA -494,-19
  706.     DATA -530,-19
  707.     DATA -548,-38
  708.     DATA -512,38
  709.     DATA -548,75
  710.     DATA -512,0
  711.     DATA -530,-57
  712.     DATA -548,0
  713.     DATA -494,19
  714.     DATA -548,-75
  715.     DATA -530,57
  716.     DATA -512,-38
  717.     DATA -530,19
  718.     DATA 525,0
  719.     DATA -500,800
  720.  
  721.     textureMap(22) = _NEWIMAGE(_WIDTH(0) / 2, _HEIGHT(0) / 2, 32) 'GUI
  722.     _DEST textureMap(22)
  723.     LOCATE 1, 1
  724.     PRINT "QB Pool: No rules."
  725.     PRINT "Instructions:"
  726.     PRINT "- 'I' or 'i' to show these instructions."
  727.     PRINT "- Use Mouse to Aim."
  728.     PRINT "- Mouse also controls power."
  729.     PRINT "- Mousewheel to adjust zoom."
  730.     PRINT "- LEFT and RIGHT arrow keys for English."
  731.     PRINT "- 'R' or 'r' to rerack balls."
  732.     PRINT "- <SPACE> to hit harder. Example for a break."
  733.     PRINT "- <TAB> to test your shot. ;)"
  734.     PRINT "- <ESC> to quit."
  735.     genPool.instructions = 1
  736.     _DEST 0
  737.  
  738.  
  739. SUB bodyStop (body AS tBODY)
  740.     CALL vectorSet(body.velocity, 0, 0)
  741.     body.angularVelocity = 0
  742.  
  743. SUB bodyOffset (body() AS tBODY, p() AS tPOLY, index AS LONG, vec AS tVECTOR2d)
  744.     DIM i AS INTEGER
  745.     FOR i = 0 TO body(index).pa.count
  746.         CALL vectorAddVector(p(body(index).pa.start + i).vert, vec)
  747.     NEXT
  748.  
  749.  
  750. FUNCTION objectContainsString (body() AS tBODY, start AS INTEGER, s AS STRING)
  751.     objectContainsString = -1
  752.     DIM AS INTEGER j
  753.     FOR j = start TO objectManager.objectCount
  754.         IF INSTR(body(j).objectName, s) THEN
  755.             objectContainsString = j
  756.             EXIT FUNCTION
  757.         END IF
  758.     NEXT
  759. '**********************************************************************************************
  760. '   Collision Helper Tools
  761. '**********************************************************************************************
  762.  
  763. FUNCTION isBodyTouchingBody (hits() AS tHIT, A AS INTEGER, B AS INTEGER)
  764.     DIM hitcount AS INTEGER: hitcount = 1
  765.     isBodyTouchingBody = 0
  766.     DO WHILE hits(hitcount).A <> hits(hitcount).B
  767.         IF hits(hitcount).A = A AND hits(hitcount).B = B THEN
  768.             isBodyTouchingBody = hitcount
  769.             EXIT FUNCTION
  770.         END IF
  771.         hitcount = hitcount + 1: IF hitcount > UBOUND(hits) THEN REDIM _PRESERVE hits(hitcount * 1.5) AS tHIT
  772.     LOOP
  773.  
  774. FUNCTION isBodyTouchingStatic (body() AS tBODY, hits() AS tHIT, A AS INTEGER)
  775.     DIM hitcount AS INTEGER: hitcount = 1
  776.     isBodyTouchingStatic = 0
  777.     DO WHILE hits(hitcount).A <> hits(hitcount).B
  778.         IF hits(hitcount).A = A THEN
  779.             IF body(hits(hitcount).B).mass = 0 THEN
  780.                 isBodyTouchingStatic = hitcount
  781.                 EXIT FUNCTION
  782.             END IF
  783.         ELSE
  784.             IF hits(hitcount).B = A THEN
  785.                 IF body(hits(hitcount).A).mass = 0 THEN
  786.                     isBodyTouchingStatic = hitcount
  787.                     EXIT FUNCTION
  788.                 END IF
  789.             END IF
  790.         END IF
  791.         hitcount = hitcount + 1: IF hitcount > UBOUND(hits) THEN REDIM _PRESERVE hits(hitcount * 1.5) AS tHIT
  792.     LOOP
  793.  
  794. FUNCTION isBodyTouching (hits() AS tHIT, A AS INTEGER)
  795.     DIM hitcount AS INTEGER: hitcount = 1
  796.     isBodyTouching = 0
  797.     DO WHILE hits(hitcount).A <> hits(hitcount).B
  798.         IF hits(hitcount).A = A THEN
  799.             isBodyTouching = hits(hitcount).B
  800.             EXIT FUNCTION
  801.         END IF
  802.         IF hits(hitcount).B = A THEN
  803.             isBodyTouching = hits(hitcount).A
  804.             EXIT FUNCTION
  805.         END IF
  806.  
  807.         hitcount = hitcount + 1: IF hitcount > UBOUND(hits) THEN REDIM _PRESERVE hits(hitcount * 1.5) AS tHIT
  808.     LOOP
  809.  
  810. FUNCTION highestCollisionVelocity (hits() AS tHIT, A AS INTEGER) ' this function is a bit dubious and may not do as you think
  811.     DIM hitcount AS INTEGER: hitcount = 1
  812.     DIM hiCv AS _FLOAT: hiCv = 0
  813.     highestCollisionVelocity = 0
  814.     DO WHILE hits(hitcount).A <> hits(hitcount).B
  815.         IF hits(hitcount).A = A AND ABS(hits(hitcount).cv) > hiCv AND hits(hitcount).cv < 0 THEN
  816.             hiCv = ABS(hits(hitcount).cv)
  817.         END IF
  818.         hitcount = hitcount + 1: IF hitcount > UBOUND(hits) THEN REDIM _PRESERVE hits(hitcount * 1.5) AS tHIT
  819.     LOOP
  820.     highestCollisionVelocity = hiCv
  821.  
  822. '**********************************************************************************************
  823. '   Physics and Collision Stuff Ahead
  824. '**********************************************************************************************
  825.  
  826. SUB impulseIntegrateForces (b AS tBODY, dt AS _FLOAT)
  827.     IF b.invMass = 0.0 THEN EXIT SUB
  828.     DIM dts AS _FLOAT
  829.     dts = dt * .5
  830.     CALL vectorAddVectorScalar(b.velocity, b.force, b.invMass * dts)
  831.     CALL vectorAddVectorScalar(b.velocity, world.gravity, dts)
  832.     b.angularVelocity = b.angularVelocity + (b.torque * b.invInertia * dts)
  833.  
  834. SUB impulseIntegrateVelocity (body AS tBODY, dt AS _FLOAT)
  835.     IF body.invMass = 0.0 THEN EXIT SUB
  836.     body.velocity.x = body.velocity.x * (1 - dt)
  837.     body.velocity.y = body.velocity.y * (1 - dt)
  838.     body.angularVelocity = body.angularVelocity * (1 - dt)
  839.     CALL vectorAddVectorScalar(body.position, body.velocity, dt)
  840.     body.orient = body.orient + (body.angularVelocity * dt)
  841.     CALL matrixSetRadians(body.shape.u, body.orient)
  842.     CALL impulseIntegrateForces(body, dt)
  843.  
  844. SUB impulseStep (p() AS tPOLY, body() AS tBODY, j() AS tJOINT, hits() AS tHIT, dt AS _FLOAT, iterations AS INTEGER)
  845.     DIM A AS tBODY
  846.     DIM B AS tBODY
  847.     DIM c(cMAXNUMBEROFOBJECTS) AS tVECTOR2d
  848.     DIM m AS tMANIFOLD
  849.     DIM manifolds(sNUMBEROFBODIES * sNUMBEROFBODIES) AS tMANIFOLD
  850.     DIM collisions(sNUMBEROFBODIES * sNUMBEROFBODIES, cMAXNUMBEROFOBJECTS) AS tVECTOR2d
  851.     DIM manifoldCount AS INTEGER: manifoldCount = 0
  852.     '    // Generate new collision info
  853.     DIM i, j, k AS LONG
  854.     DIM hitCount AS LONG: hitCount = 0
  855.     DIM dHit AS tHIT 'empty
  856.     ' // erase hitlist
  857.     DO
  858.         hits(hitCount) = dHit
  859.         hitCount = hitCount + 1
  860.     LOOP UNTIL hitCount > UBOUND(hits)
  861.     hitCount = 0
  862.  
  863.     FOR i = 0 TO objectManager.objectCount ' number of bodies
  864.         A = body(i)
  865.         IF A.enable THEN
  866.             FOR j = i + 1 TO objectManager.objectCount
  867.                 B = body(j)
  868.                 IF (A.collisionMask AND B.collisionMask) THEN
  869.                     IF A.invMass = 0.0 AND B.invMass = 0.0 THEN _CONTINUE
  870.                     'Mainfold solve - handle collisions
  871.                     IF AABBOverlapVector(A.position, 2000, 2000, B.position, 2000, 2000) THEN
  872.                         IF A.shape.ty = cSHAPE_CIRCLE AND B.shape.ty = cSHAPE_CIRCLE THEN
  873.                             CALL collisionCCHandle(m, c(), A, B)
  874.                         ELSE
  875.                             IF A.shape.ty = cSHAPE_POLYGON AND B.shape.ty = cSHAPE_POLYGON THEN
  876.                                 CALL collisionPPHandle(p(), body(), m, c(), i, j)
  877.                             ELSE
  878.                                 IF A.shape.ty = cSHAPE_CIRCLE AND B.shape.ty = cSHAPE_POLYGON THEN
  879.                                     CALL collisionCPHandle(p(), body(), m, c(), i, j)
  880.                                 ELSE
  881.                                     IF B.shape.ty = cSHAPE_CIRCLE AND A.shape.ty = cSHAPE_POLYGON THEN
  882.                                         CALL collisionPCHandle(p(), body(), m, c(), i, j)
  883.                                     END IF
  884.                                 END IF
  885.                             END IF
  886.                         END IF
  887.  
  888.                         IF m.contactCount > 0 THEN
  889.                             m.A = i 'identify the index of objects
  890.                             m.B = j
  891.                             manifolds(manifoldCount) = m
  892.                             FOR k = 0 TO m.contactCount
  893.                                 hits(hitCount).A = i
  894.                                 hits(hitCount).B = j
  895.                                 hits(hitCount).position = c(k)
  896.                                 collisions(manifoldCount, k) = c(k)
  897.                                 hitCount = hitCount + 1
  898.                                 IF hitCount > UBOUND(hits) THEN REDIM _PRESERVE hits(hitCount * 1.5) AS tHIT
  899.                             NEXT
  900.                             manifoldCount = manifoldCount + 1
  901.                             IF manifoldCount > UBOUND(manifolds) THEN REDIM _PRESERVE manifolds(manifoldCount * 1.5) AS tMANIFOLD
  902.                         END IF
  903.                     END IF
  904.                 END IF
  905.             NEXT
  906.         END IF
  907.     NEXT
  908.  
  909.     '   // Integrate forces
  910.     FOR i = 0 TO objectManager.objectCount
  911.         IF body(i).enable THEN CALL impulseIntegrateForces(body(i), dt)
  912.     NEXT
  913.     '   // Initialize collision
  914.     FOR i = 0 TO manifoldCount - 1
  915.         ' this is the stupidest thing ever since QB will not let you split arrays
  916.         FOR k = 0 TO manifolds(i).contactCount - 1
  917.             c(k) = collisions(i, k)
  918.         NEXT
  919.         CALL manifoldInit(manifolds(i), body(), c())
  920.     NEXT
  921.     ' joint pre Steps
  922.  
  923.     FOR i = 1 TO sNUMBEROFJOINTS
  924.         CALL jointPrestep(j(i), body(), dt)
  925.     NEXT
  926.  
  927.     '// Solve collisions
  928.     FOR j = 0 TO iterations - 1
  929.         FOR i = 0 TO manifoldCount - 1
  930.             FOR k = 0 TO manifolds(i).contactCount - 1
  931.                 c(k) = collisions(i, k)
  932.             NEXT
  933.             CALL manifoldApplyImpulse(manifolds(i), body(), c())
  934.             'store the hit speed for later
  935.             FOR k = 0 TO hitCount - 1
  936.                 IF manifolds(i).A = hits(k).A AND manifolds(i).B = hits(k).B THEN
  937.                     hits(k).cv = manifolds(i).cv
  938.                 END IF
  939.             NEXT
  940.         NEXT
  941.         FOR i = 1 TO sNUMBEROFJOINTS
  942.             CALL jointApplyImpulse(j(i), body())
  943.         NEXT
  944.     NEXT
  945.  
  946.     '// Integrate velocities
  947.     FOR i = 0 TO objectManager.objectCount
  948.         IF body(i).enable THEN CALL impulseIntegrateVelocity(body(i), dt)
  949.     NEXT
  950.     '// Correct positions
  951.     FOR i = 0 TO manifoldCount - 1
  952.         CALL manifoldPositionalCorrection(manifolds(i), body())
  953.     NEXT
  954.     '// Clear all forces
  955.     FOR i = 0 TO objectManager.objectCount
  956.         CALL vectorSet(body(i).force, 0, 0)
  957.         body(i).torque = 0
  958.     NEXT
  959.  
  960. SUB bodyApplyImpulse (body AS tBODY, impulse AS tVECTOR2d, contactVector AS tVECTOR2d)
  961.     CALL vectorAddVectorScalar(body.velocity, impulse, body.invMass)
  962.     body.angularVelocity = body.angularVelocity + body.invInertia * vectorCross(contactVector, impulse)
  963.  
  964. SUB bodySetStatic (body AS tBODY)
  965.     body.inertia = 0.0
  966.     body.invInertia = 0.0
  967.     body.mass = 0.0
  968.     body.invMass = 0.0
  969.  
  970. SUB manifoldInit (m AS tMANIFOLD, body() AS tBODY, contacts() AS tVECTOR2d)
  971.     DIM ra AS tVECTOR2d
  972.     DIM rb AS tVECTOR2d
  973.     DIM rv AS tVECTOR2d
  974.     DIM tv1 AS tVECTOR2d 'temporary Vectors
  975.     DIM tv2 AS tVECTOR2d
  976.     m.e = scalarMin(body(m.A).restitution, body(m.B).restitution)
  977.     m.sf = SQR(body(m.A).staticFriction * body(m.A).staticFriction)
  978.     m.df = SQR(body(m.A).dynamicFriction * body(m.A).dynamicFriction)
  979.     DIM i AS INTEGER
  980.     FOR i = 0 TO m.contactCount - 1
  981.         CALL vectorSubVectorND(contacts(i), body(m.A).position, ra)
  982.         CALL vectorSubVectorND(contacts(i), body(m.B).position, rb)
  983.  
  984.         CALL vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  985.         CALL vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  986.         CALL vectorAddVector(tv1, body(m.B).velocity)
  987.         CALL vectorSubVectorND(tv2, body(m.A).velocity, tv2)
  988.         CALL vectorSubVectorND(rv, tv1, tv2)
  989.  
  990.         IF vectorLengthSq(rv) < sRESTING THEN
  991.             m.e = 0.0
  992.         END IF
  993.     NEXT
  994.  
  995. SUB manifoldApplyImpulse (m AS tMANIFOLD, body() AS tBODY, contacts() AS tVECTOR2d)
  996.     DIM ra AS tVECTOR2d
  997.     DIM rb AS tVECTOR2d
  998.     DIM rv AS tVECTOR2d
  999.     DIM tv1 AS tVECTOR2d 'temporary Vectors
  1000.     DIM tv2 AS tVECTOR2d
  1001.     DIM contactVel AS _FLOAT
  1002.  
  1003.     DIM raCrossN AS _FLOAT
  1004.     DIM rbCrossN AS _FLOAT
  1005.     DIM invMassSum AS _FLOAT
  1006.     DIM i AS INTEGER
  1007.     DIM j AS _FLOAT
  1008.     DIM impulse AS tVECTOR2d
  1009.  
  1010.     DIM t AS tVECTOR2d
  1011.     DIM jt AS _FLOAT
  1012.     DIM tangentImpulse AS tVECTOR2d
  1013.  
  1014.     IF impulseEqual(body(m.A).invMass + body(m.B).invMass, 0.0) THEN
  1015.         CALL manifoldInfiniteMassCorrection(body(m.A), body(m.B))
  1016.         EXIT SUB
  1017.     END IF
  1018.  
  1019.     FOR i = 0 TO m.contactCount - 1
  1020.         '// Calculate radii from COM to contact
  1021.         '// Vec2 ra = contacts[i] - A->position;
  1022.         '// Vec2 rb = contacts[i] - B->position;
  1023.         CALL vectorSubVectorND(ra, contacts(i), body(m.A).position)
  1024.         CALL vectorSubVectorND(rb, contacts(i), body(m.B).position)
  1025.  
  1026.         '// Relative velocity
  1027.         '// Vec2 rv = B->velocity + Cross( B->angularVelocity, rb ) - A->velocity - Cross( A->angularVelocity, ra );
  1028.         CALL vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  1029.         CALL vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  1030.         CALL vectorAddVectorND(rv, tv1, body(m.B).velocity)
  1031.         CALL vectorSubVector(rv, body(m.A).velocity)
  1032.         CALL vectorSubVector(rv, tv2)
  1033.  
  1034.         '// Relative velocity along the normal
  1035.         '// real contactVel = Dot( rv, normal );
  1036.         contactVel = vectorDot(rv, m.normal)
  1037.  
  1038.         '// Do not resolve if velocities are separating
  1039.         IF contactVel > 0 THEN EXIT SUB
  1040.         m.cv = contactVel
  1041.         '// real raCrossN = Cross( ra, normal );
  1042.         '// real rbCrossN = Cross( rb, normal );
  1043.         '// real invMassSum = A->im + B->im + Sqr( raCrossN ) * A->iI + Sqr( rbCrossN ) * B->iI;
  1044.         raCrossN = vectorCross(ra, m.normal)
  1045.         rbCrossN = vectorCross(rb, m.normal)
  1046.         invMassSum = body(m.A).invMass + body(m.B).invMass + (raCrossN * raCrossN) * body(m.A).invInertia + (rbCrossN * rbCrossN) * body(m.B).invInertia
  1047.  
  1048.         '// Calculate impulse scalar
  1049.         j = -(1.0 + m.e) * contactVel
  1050.         j = j / invMassSum
  1051.         j = j / m.contactCount
  1052.  
  1053.         '// Apply impulse
  1054.         CALL vectorMultiplyScalarND(impulse, m.normal, j)
  1055.         CALL vectorNegND(tv1, impulse)
  1056.         CALL bodyApplyImpulse(body(m.A), tv1, ra)
  1057.         CALL bodyApplyImpulse(body(m.B), impulse, rb)
  1058.  
  1059.         '// Friction impulse
  1060.         '// rv = B->velocity + Cross( B->angularVelocity, rb ) - A->velocity - Cross( A->angularVelocity, ra );
  1061.         CALL vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  1062.         CALL vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  1063.         CALL vectorAddVectorND(rv, tv1, body(m.B).velocity)
  1064.         CALL vectorSubVector(rv, body(m.A).velocity)
  1065.         CALL vectorSubVector(rv, tv2)
  1066.  
  1067.         '// Vec2 t = rv - (normal * Dot( rv, normal ));
  1068.         '// t.Normalize( );
  1069.         CALL vectorMultiplyScalarND(t, m.normal, vectorDot(rv, m.normal))
  1070.         CALL vectorSubVectorND(t, rv, t)
  1071.         CALL vectorNormalize(t)
  1072.  
  1073.         '// j tangent magnitude
  1074.         jt = -vectorDot(rv, t)
  1075.         jt = jt / invMassSum
  1076.         jt = jt / m.contactCount
  1077.  
  1078.         '// Don't apply tiny friction impulses
  1079.         IF impulseEqual(jt, 0.0) THEN EXIT SUB
  1080.  
  1081.         '// Coulumb's law
  1082.         IF ABS(jt) < j * m.sf THEN
  1083.             CALL vectorMultiplyScalarND(tangentImpulse, t, jt)
  1084.         ELSE
  1085.             CALL vectorMultiplyScalarND(tangentImpulse, t, -j * m.df)
  1086.         END IF
  1087.  
  1088.         '// Apply friction impulse
  1089.         '// A->ApplyImpulse( -tangentImpulse, ra );
  1090.         '// B->ApplyImpulse( tangentImpulse, rb );
  1091.         CALL vectorNegND(tv1, tangentImpulse)
  1092.         CALL bodyApplyImpulse(body(m.A), tv1, ra)
  1093.         CALL bodyApplyImpulse(body(m.B), tangentImpulse, rb)
  1094.     NEXT i
  1095.  
  1096. SUB manifoldPositionalCorrection (m AS tMANIFOLD, body() AS tBODY)
  1097.     DIM correction AS _FLOAT
  1098.     correction = scalarMax(m.penetration - cPENETRATION_ALLOWANCE, 0.0) / (body(m.A).invMass + body(m.B).invMass) * cPENETRATION_CORRECTION
  1099.     CALL vectorAddVectorScalar(body(m.A).position, m.normal, -body(m.A).invMass * correction)
  1100.     CALL vectorAddVectorScalar(body(m.B).position, m.normal, body(m.B).invMass * correction)
  1101.  
  1102. SUB manifoldInfiniteMassCorrection (A AS tBODY, B AS tBODY)
  1103.     CALL vectorSet(A.velocity, 0, 0)
  1104.     CALL vectorSet(B.velocity, 0, 0)
  1105.  
  1106. '**********************************************************************************************
  1107. '   Collision Stuff Ahead
  1108. '**********************************************************************************************
  1109.  
  1110. SUB collisionCCHandle (m AS tMANIFOLD, contacts() AS tVECTOR2d, A AS tBODY, B AS tBODY)
  1111.     DIM normal AS tVECTOR2d
  1112.     DIM dist_sqr AS _FLOAT
  1113.     DIM radius AS _FLOAT
  1114.  
  1115.     CALL vectorSubVectorND(normal, B.position, A.position) ' Subtract two vectors position A and position B
  1116.     dist_sqr = vectorLengthSq(normal) ' Calculate the distance between the balls or circles
  1117.     radius = A.shape.radius + B.shape.radius ' Add both circle A and circle B radius
  1118.  
  1119.     IF (dist_sqr >= radius * radius) THEN
  1120.         m.contactCount = 0
  1121.     ELSE
  1122.         DIM distance AS _FLOAT
  1123.         distance = SQR(dist_sqr)
  1124.         m.contactCount = 1
  1125.  
  1126.         IF distance = 0 THEN
  1127.             m.penetration = A.shape.radius
  1128.             CALL vectorSet(m.normal, 1.0, 0.0)
  1129.             CALL vectorSetVector(contacts(0), A.position)
  1130.         ELSE
  1131.             m.penetration = radius - distance
  1132.             CALL vectorDivideScalarND(m.normal, normal, distance)
  1133.  
  1134.             CALL vectorMultiplyScalarND(contacts(0), m.normal, A.shape.radius)
  1135.             CALL vectorAddVector(contacts(0), A.position)
  1136.         END IF
  1137.     END IF
  1138.  
  1139. SUB collisionPCHandle (p() AS tPOLY, body() AS tBODY, m AS tMANIFOLD, contacts() AS tVECTOR2d, A AS INTEGER, B AS INTEGER)
  1140.     CALL collisionCPHandle(p(), body(), m, contacts(), B, A)
  1141.     IF m.contactCount > 0 THEN
  1142.         CALL vectorNeg(m.normal)
  1143.     END IF
  1144.  
  1145. SUB collisionCPHandle (p() AS tPOLY, body() AS tBODY, m AS tMANIFOLD, contacts() AS tVECTOR2d, A AS INTEGER, B AS INTEGER)
  1146.     'A is the Circle
  1147.     'B is the POLY
  1148.     m.contactCount = 0
  1149.     DIM center AS tVECTOR2d
  1150.     DIM tm AS tMATRIX2d
  1151.     DIM tv AS tVECTOR2d
  1152.     DIM ARadius AS _FLOAT: ARadius = body(A).shape.radius
  1153.  
  1154.     CALL vectorSubVectorND(center, body(A).position, body(B).position)
  1155.     CALL matrixTranspose(body(B).shape.u, tm)
  1156.     CALL matrixMultiplyVector(tm, center, center)
  1157.  
  1158.     DIM separation AS _FLOAT: separation = -9999999
  1159.     DIM faceNormal AS INTEGER: faceNormal = 0
  1160.     DIM i AS INTEGER
  1161.     DIM s AS _FLOAT
  1162.     FOR i = 0 TO body(B).pa.count
  1163.         CALL vectorSubVectorND(tv, center, p(body(B).pa.start + i).vert)
  1164.         s = vectorDot(p(body(B).pa.start + i).norm, tv)
  1165.         IF s > ARadius THEN EXIT SUB
  1166.         IF s > separation THEN
  1167.             separation = s
  1168.             faceNormal = i
  1169.         END IF
  1170.     NEXT
  1171.     DIM v1 AS tVECTOR2d
  1172.     v1 = p(body(B).pa.start + faceNormal).vert
  1173.     DIM i2 AS INTEGER
  1174.     i2 = body(B).pa.start + arrayNextIndex(faceNormal, body(B).pa.count)
  1175.     DIM v2 AS tVECTOR2d
  1176.     v2 = p(i2).vert
  1177.  
  1178.     IF separation < cEPSILON THEN
  1179.         m.contactCount = 1
  1180.         CALL matrixMultiplyVector(body(B).shape.u, p(body(B).pa.start + faceNormal).norm, m.normal)
  1181.         CALL vectorNeg(m.normal)
  1182.         CALL vectorMultiplyScalarND(contacts(0), m.normal, ARadius)
  1183.         CALL vectorAddVector(contacts(0), body(A).position)
  1184.         m.penetration = ARadius
  1185.         EXIT SUB
  1186.     END IF
  1187.  
  1188.     DIM dot1 AS _FLOAT
  1189.     DIM dot2 AS _FLOAT
  1190.  
  1191.     DIM tv1 AS tVECTOR2d
  1192.     DIM tv2 AS tVECTOR2d
  1193.     DIM n AS tVECTOR2d
  1194.     CALL vectorSubVectorND(tv1, center, v1)
  1195.     CALL vectorSubVectorND(tv2, v2, v1)
  1196.     dot1 = vectorDot(tv1, tv2)
  1197.     CALL vectorSubVectorND(tv1, center, v2)
  1198.     CALL vectorSubVectorND(tv2, v1, v2)
  1199.     dot2 = vectorDot(tv1, tv2)
  1200.     m.penetration = ARadius - separation
  1201.     IF dot1 <= 0.0 THEN
  1202.         IF vectorSqDist(center, v1) > ARadius * ARadius THEN EXIT SUB
  1203.         m.contactCount = 1
  1204.         CALL vectorSubVectorND(n, v1, center)
  1205.         CALL matrixMultiplyVector(body(B).shape.u, n, n)
  1206.         CALL vectorNormalize(n)
  1207.         m.normal = n
  1208.         CALL matrixMultiplyVector(body(B).shape.u, v1, v1)
  1209.         CALL vectorAddVectorND(v1, v1, body(B).position)
  1210.         contacts(0) = v1
  1211.     ELSE
  1212.         IF dot2 <= 0.0 THEN
  1213.             IF vectorSqDist(center, v2) > ARadius * ARadius THEN EXIT SUB
  1214.             m.contactCount = 1
  1215.             CALL vectorSubVectorND(n, v2, center)
  1216.             CALL matrixMultiplyVector(body(B).shape.u, v2, v2)
  1217.             CALL vectorAddVectorND(v2, v2, body(B).position)
  1218.             contacts(0) = v2
  1219.             CALL matrixMultiplyVector(body(B).shape.u, n, n)
  1220.             CALL vectorNormalize(n)
  1221.             m.normal = n
  1222.         ELSE
  1223.             n = p(body(B).pa.start + faceNormal).norm
  1224.             CALL vectorSubVectorND(tv1, center, v1)
  1225.             IF vectorDot(tv1, n) > ARadius THEN EXIT SUB
  1226.             m.contactCount = 1
  1227.             CALL matrixMultiplyVector(body(B).shape.u, n, n)
  1228.             CALL vectorNeg(n)
  1229.             m.normal = n
  1230.             CALL vectorMultiplyScalarND(contacts(0), m.normal, ARadius)
  1231.             CALL vectorAddVector(contacts(0), body(A).position)
  1232.         END IF
  1233.     END IF
  1234.  
  1235. FUNCTION collisionPPClip (n AS tVECTOR2d, c AS _FLOAT, face() AS tVECTOR2d)
  1236.     DIM sp AS INTEGER: sp = 0
  1237.     DIM o(cMAXNUMBEROFPOLYGONS) AS tVECTOR2d
  1238.  
  1239.     o(0) = face(0)
  1240.     o(1) = face(1)
  1241.  
  1242.     DIM d1 AS _FLOAT: d1 = vectorDot(n, face(0)) - c
  1243.     DIM d2 AS _FLOAT: d2 = vectorDot(n, face(1)) - c
  1244.  
  1245.     IF d1 <= 0.0 THEN
  1246.         o(sp) = face(0)
  1247.         sp = sp + 1
  1248.     END IF
  1249.  
  1250.     IF d2 <= 0.0 THEN
  1251.         o(sp) = face(1)
  1252.         sp = sp + 1
  1253.     END IF
  1254.  
  1255.     IF d1 * d2 < 0.0 THEN
  1256.         DIM alpha AS _FLOAT: alpha = d1 / (d1 - d2)
  1257.         DIM tempv AS tVECTOR2d
  1258.         'out[sp] = face[0] + alpha * (face[1] - face[0]);
  1259.         CALL vectorSubVectorND(tempv, face(1), face(0))
  1260.         CALL vectorMultiplyScalar(tempv, alpha)
  1261.         CALL vectorAddVectorND(o(sp), tempv, face(0))
  1262.         sp = sp + 1
  1263.     END IF
  1264.     face(0) = o(0)
  1265.     face(1) = o(1)
  1266.     collisionPPClip = sp
  1267.  
  1268. SUB collisionPPFindIncidentFace (p() AS tPOLY, b() AS tBODY, v() AS tVECTOR2d, RefPoly AS INTEGER, IncPoly AS INTEGER, referenceIndex AS INTEGER)
  1269.     DIM referenceNormal AS tVECTOR2d
  1270.     DIM uRef AS tMATRIX2d: uRef = b(RefPoly).shape.u
  1271.     DIM uInc AS tMATRIX2d: uInc = b(IncPoly).shape.u
  1272.     DIM uTemp AS tMATRIX2d
  1273.     DIM i AS INTEGER
  1274.     referenceNormal = p(b(RefPoly).pa.start + referenceIndex).norm
  1275.  
  1276.     '        // Calculate normal in incident's frame of reference
  1277.     '        // referenceNormal = RefPoly->u * referenceNormal; // To world space
  1278.     CALL matrixMultiplyVector(uRef, referenceNormal, referenceNormal)
  1279.     '        // referenceNormal = IncPoly->u.Transpose( ) * referenceNormal; // To incident's model space
  1280.     CALL matrixTranspose(uInc, uTemp)
  1281.     CALL matrixMultiplyVector(uTemp, referenceNormal, referenceNormal)
  1282.  
  1283.     DIM incidentFace AS INTEGER: incidentFace = 0
  1284.     DIM minDot AS _FLOAT: minDot = 9999999
  1285.     DIM dot AS _FLOAT
  1286.     FOR i = 0 TO b(IncPoly).pa.count
  1287.         dot = vectorDot(referenceNormal, p(b(IncPoly).pa.start + i).norm)
  1288.         IF (dot < minDot) THEN
  1289.             minDot = dot
  1290.             incidentFace = i
  1291.         END IF
  1292.     NEXT
  1293.  
  1294.     '// Assign face vertices for incidentFace
  1295.     '// v[0] = IncPoly->u * IncPoly->m_vertices[incidentFace] + IncPoly->body->position;
  1296.     CALL matrixMultiplyVector(uInc, p(b(IncPoly).pa.start + incidentFace).vert, v(0))
  1297.     CALL vectorAddVector(v(0), b(IncPoly).position)
  1298.  
  1299.     '// incidentFace = incidentFace + 1 >= (int32)IncPoly->m_vertexCount ? 0 : incidentFace + 1;
  1300.     incidentFace = arrayNextIndex(incidentFace, b(IncPoly).pa.count)
  1301.  
  1302.     '// v[1] = IncPoly->u * IncPoly->m_vertices[incidentFace] +  IncPoly->body->position;
  1303.     CALL matrixMultiplyVector(uInc, p(b(IncPoly).pa.start + incidentFace).vert, v(1))
  1304.     CALL vectorAddVector(v(1), b(IncPoly).position)
  1305.  
  1306. SUB collisionPPHandle (p() AS tPOLY, body() AS tBODY, m AS tMANIFOLD, contacts() AS tVECTOR2d, A AS INTEGER, B AS INTEGER)
  1307.     m.contactCount = 0
  1308.  
  1309.     DIM faceA(100) AS INTEGER
  1310.  
  1311.     DIM penetrationA AS _FLOAT
  1312.     penetrationA = collisionPPFindAxisLeastPenetration(p(), body(), faceA(), A, B)
  1313.     IF penetrationA >= 0.0 THEN EXIT SUB
  1314.  
  1315.     DIM faceB(100) AS INTEGER
  1316.  
  1317.     DIM penetrationB AS _FLOAT
  1318.     penetrationB = collisionPPFindAxisLeastPenetration(p(), body(), faceB(), B, A)
  1319.     IF penetrationB >= 0.0 THEN EXIT SUB
  1320.  
  1321.  
  1322.     DIM referenceIndex AS INTEGER
  1323.     DIM flip AS INTEGER
  1324.  
  1325.     DIM RefPoly AS INTEGER
  1326.     DIM IncPoly AS INTEGER
  1327.  
  1328.     IF impulseGT(penetrationA, penetrationB) THEN
  1329.         RefPoly = A
  1330.         IncPoly = B
  1331.         referenceIndex = faceA(0)
  1332.         flip = 0
  1333.     ELSE
  1334.         RefPoly = B
  1335.         IncPoly = A
  1336.         referenceIndex = faceB(0)
  1337.         flip = 1
  1338.     END IF
  1339.  
  1340.     DIM incidentFace(2) AS tVECTOR2d
  1341.  
  1342.     CALL collisionPPFindIncidentFace(p(), body(), incidentFace(), RefPoly, IncPoly, referenceIndex)
  1343.     DIM v1 AS tVECTOR2d
  1344.     DIM v2 AS tVECTOR2d
  1345.     DIM v1t AS tVECTOR2d
  1346.     DIM v2t AS tVECTOR2d
  1347.  
  1348.     v1 = p(body(RefPoly).pa.start + referenceIndex).vert
  1349.     referenceIndex = arrayNextIndex(referenceIndex, body(RefPoly).pa.count)
  1350.     v2 = p(body(RefPoly).pa.start + referenceIndex).vert
  1351.     '// Transform vertices to world space
  1352.     '// v1 = RefPoly->u * v1 + RefPoly->body->position;
  1353.     '// v2 = RefPoly->u * v2 + RefPoly->body->position;
  1354.     CALL matrixMultiplyVector(body(RefPoly).shape.u, v1, v1t)
  1355.     CALL vectorAddVectorND(v1, v1t, body(RefPoly).position)
  1356.     CALL matrixMultiplyVector(body(RefPoly).shape.u, v2, v2t)
  1357.     CALL vectorAddVectorND(v2, v2t, body(RefPoly).position)
  1358.  
  1359.     '// Calculate reference face side normal in world space
  1360.     '// Vec2 sidePlaneNormal = (v2 - v1);
  1361.     '// sidePlaneNormal.Normalize( );
  1362.     DIM sidePlaneNormal AS tVECTOR2d
  1363.     CALL vectorSubVectorND(sidePlaneNormal, v2, v1)
  1364.     CALL vectorNormalize(sidePlaneNormal)
  1365.  
  1366.     '// Orthogonalize
  1367.     '// Vec2 refFaceNormal( sidePlaneNormal.y, -sidePlaneNormal.x );
  1368.     DIM refFaceNormal AS tVECTOR2d
  1369.     CALL vectorSet(refFaceNormal, sidePlaneNormal.y, -sidePlaneNormal.x)
  1370.  
  1371.     '// ax + by = c
  1372.     '// c is distance from origin
  1373.     '// real refC = Dot( refFaceNormal, v1 );
  1374.     '// real negSide = -Dot( sidePlaneNormal, v1 );
  1375.     '// real posSide = Dot( sidePlaneNormal, v2 );
  1376.     DIM refC AS _FLOAT: refC = vectorDot(refFaceNormal, v1)
  1377.     DIM negSide AS _FLOAT: negSide = -vectorDot(sidePlaneNormal, v1)
  1378.     DIM posSide AS _FLOAT: posSide = vectorDot(sidePlaneNormal, v2)
  1379.  
  1380.  
  1381.     '// Clip incident face to reference face side planes
  1382.     '// if(Clip( -sidePlaneNormal, negSide, incidentFace ) < 2)
  1383.     DIM negSidePlaneNormal AS tVECTOR2d
  1384.     CALL vectorNegND(negSidePlaneNormal, sidePlaneNormal)
  1385.  
  1386.     IF collisionPPClip(negSidePlaneNormal, negSide, incidentFace()) < 2 THEN EXIT SUB
  1387.     IF collisionPPClip(sidePlaneNormal, posSide, incidentFace()) < 2 THEN EXIT SUB
  1388.  
  1389.     CALL vectorSet(m.normal, refFaceNormal.x, refFaceNormal.y)
  1390.     IF flip THEN CALL vectorNeg(m.normal)
  1391.  
  1392.     '// Keep points behind reference face
  1393.     DIM cp AS INTEGER: cp = 0 '// clipped points behind reference face
  1394.     DIM separation AS _FLOAT
  1395.     separation = vectorDot(refFaceNormal, incidentFace(0)) - refC
  1396.     IF separation <= 0.0 THEN
  1397.         contacts(cp) = incidentFace(0)
  1398.         m.penetration = -separation
  1399.         cp = cp + 1
  1400.     ELSE
  1401.         m.penetration = 0
  1402.     END IF
  1403.  
  1404.     separation = vectorDot(refFaceNormal, incidentFace(1)) - refC
  1405.     IF separation <= 0.0 THEN
  1406.         contacts(cp) = incidentFace(1)
  1407.         m.penetration = m.penetration + -separation
  1408.         cp = cp + 1
  1409.         m.penetration = m.penetration / cp
  1410.     END IF
  1411.     m.contactCount = cp
  1412.  
  1413. FUNCTION collisionPPFindAxisLeastPenetration (p() AS tPOLY, body() AS tBODY, faceIndex() AS INTEGER, A AS INTEGER, B AS INTEGER)
  1414.     DIM bestDistance AS _FLOAT: bestDistance = -9999999
  1415.     DIM bestIndex AS INTEGER: bestIndex = 0
  1416.  
  1417.     DIM n AS tVECTOR2d
  1418.     DIM nw AS tVECTOR2d
  1419.     DIM buT AS tMATRIX2d
  1420.     DIM s AS tVECTOR2d
  1421.     DIM nn AS tVECTOR2d
  1422.     DIM v AS tVECTOR2d
  1423.     DIM tv AS tVECTOR2d
  1424.     DIM d AS _FLOAT
  1425.     DIM i, k AS INTEGER
  1426.  
  1427.     FOR i = 0 TO body(A).pa.count
  1428.         k = body(A).pa.start + i
  1429.  
  1430.         '// Retrieve a face normal from A
  1431.         '// Vec2 n = A->m_normals[i];
  1432.         '// Vec2 nw = A->u * n;
  1433.         n = p(k).norm
  1434.         CALL matrixMultiplyVector(body(A).shape.u, n, nw)
  1435.  
  1436.  
  1437.         '// Transform face normal into B's model space
  1438.         '// Mat2 buT = B->u.Transpose( );
  1439.         '// n = buT * nw;
  1440.         CALL matrixTranspose(body(B).shape.u, buT)
  1441.         CALL matrixMultiplyVector(buT, nw, n)
  1442.  
  1443.         '// Retrieve support point from B along -n
  1444.         '// Vec2 s = B->GetSupport( -n );
  1445.         CALL vectorNegND(nn, n)
  1446.         CALL vectorGetSupport(p(), body(), B, nn, s)
  1447.  
  1448.         '// Retrieve vertex on face from A, transform into
  1449.         '// B's model space
  1450.         '// Vec2 v = A->m_vertices[i];
  1451.         '// v = A->u * v + A->body->position;
  1452.         '// v -= B->body->position;
  1453.         '// v = buT * v;
  1454.  
  1455.         v = p(k).vert
  1456.         CALL matrixMultiplyVector(body(A).shape.u, v, tv)
  1457.         CALL vectorAddVectorND(v, tv, body(A).position)
  1458.  
  1459.         CALL vectorSubVector(v, body(B).position)
  1460.         CALL matrixMultiplyVector(buT, v, tv)
  1461.  
  1462.         CALL vectorSubVector(s, tv)
  1463.         d = vectorDot(n, s)
  1464.  
  1465.         IF d > bestDistance THEN
  1466.             bestDistance = d
  1467.             bestIndex = i
  1468.         END IF
  1469.  
  1470.     NEXT i
  1471.  
  1472.     faceIndex(0) = bestIndex
  1473.  
  1474.     collisionPPFindAxisLeastPenetration = bestDistance
  1475.  
  1476. '**********************************************************************************************
  1477. '   Shape Creation Ahead
  1478. '**********************************************************************************************
  1479.  
  1480. SUB shapeCreate (sh AS tSHAPE, ty AS INTEGER, radius AS _FLOAT)
  1481.     DIM u AS tMATRIX2d
  1482.     CALL matrixSetScalar(u, 1, 0, 0, 1)
  1483.     sh.ty = ty
  1484.     sh.radius = radius
  1485.     sh.u = u
  1486.     sh.scaleTextureX = 1.0
  1487.     sh.scaleTextureY = 1.0
  1488.  
  1489. '**********************************************************************************************
  1490. '   Scene Creation Tools ahead
  1491. '**********************************************************************************************
  1492.  
  1493. SUB setBody (body() AS tBODY, Parameter AS INTEGER, Index AS INTEGER, arg1 AS _FLOAT, arg2 AS _FLOAT)
  1494.     SELECT CASE Parameter
  1495.         CASE cPARAMETER_POSITION:
  1496.             CALL vectorSet(body(Index).position, arg1, arg2)
  1497.         CASE cPARAMETER_VELOCITY:
  1498.             CALL vectorSet(body(Index).velocity, arg1, arg2)
  1499.         CASE cPARAMETER_FORCE:
  1500.             CALL vectorSet(body(Index).force, arg1, arg2)
  1501.         CASE cPARAMETER_ANGULARVELOCITY:
  1502.             body(Index).angularVelocity = arg1
  1503.         CASE cPARAMETER_TORQUE:
  1504.             body(Index).torque = arg1
  1505.         CASE cPARAMETER_ORIENT:
  1506.             body(Index).orient = arg1
  1507.             CALL matrixSetRadians(body(Index).shape.u, body(Index).orient)
  1508.         CASE cPARAMETER_STATICFRICTION:
  1509.             body(Index).staticFriction = arg1
  1510.         CASE cPARAMETER_DYNAMICFRICTION:
  1511.             body(Index).dynamicFriction = arg1
  1512.         CASE cPARAMETER_RESTITUTION:
  1513.             body(Index).restitution = arg1
  1514.         CASE cPARAMETER_COLOR:
  1515.             body(Index).c = arg1
  1516.         CASE cPARAMETER_ENABLE:
  1517.             body(Index).enable = arg1
  1518.         CASE cPARAMETER_STATIC:
  1519.             CALL bodySetStatic(body(Index))
  1520.         CASE cPARAMETER_TEXTURE:
  1521.             body(Index).shape.texture = arg1
  1522.         CASE cPARAMETER_FLIPTEXTURE: 'does the texture flip directions when moving left or right
  1523.             body(Index).shape.flipTexture = arg1
  1524.         CASE cPARAMETER_COLLISIONMASK:
  1525.             body(Index).collisionMask = arg1
  1526.     END SELECT
  1527.  
  1528. SUB setBodyEx (body() AS tBODY, Parameter AS INTEGER, objName AS STRING, arg1 AS _FLOAT, arg2 AS _FLOAT)
  1529.     DIM index AS INTEGER
  1530.     index = objectManagerID(body(), objName)
  1531.  
  1532.     IF index > -1 THEN
  1533.         CALL setBody(body(), Parameter, index, arg1, arg2)
  1534.     END IF
  1535.  
  1536.  
  1537. FUNCTION createCircleBody (body() AS tBODY, index AS INTEGER, radius AS _FLOAT)
  1538.     DIM shape AS tSHAPE
  1539.     CALL shapeCreate(shape, cSHAPE_CIRCLE, radius)
  1540.     CALL bodyCreate(body(), index, shape)
  1541.     'no vertices have to created for circles
  1542.     CALL circleInitialize(body(), index)
  1543.     ' Even though circles do not have vertices, they still must be included in the vertices list
  1544.     IF index = 0 THEN
  1545.         body(index).pa.start = 0
  1546.     ELSE
  1547.         body(index).pa.start = body(index - 1).pa.start + body(index - 1).pa.count + 1
  1548.     END IF
  1549.     body(index).pa.count = 1
  1550.     body(index).c = _RGB32(255, 255, 255)
  1551.     createCircleBody = index
  1552.  
  1553. FUNCTION createCircleBodyEx (body() AS tBODY, objName AS STRING, radius AS _FLOAT)
  1554.     DIM shape AS tSHAPE
  1555.     DIM index AS INTEGER
  1556.     CALL shapeCreate(shape, cSHAPE_CIRCLE, radius)
  1557.     CALL bodyCreateEx(body(), objName, shape, index)
  1558.     'no vertices have to created for circles
  1559.     CALL circleInitialize(body(), index)
  1560.     ' Even though circles do not have vertices, they still must be included in the vertices list
  1561.     IF index = 0 THEN
  1562.         body(index).pa.start = 0
  1563.     ELSE
  1564.         body(index).pa.start = body(index - 1).pa.start + body(index - 1).pa.count + 1
  1565.     END IF
  1566.     body(index).pa.count = 1
  1567.     body(index).c = _RGB32(255, 255, 255)
  1568.     createCircleBodyEx = index
  1569.  
  1570.  
  1571. FUNCTION createBoxBodies (p() AS tPOLY, body() AS tBODY, index AS INTEGER, xs AS _FLOAT, ys AS _FLOAT)
  1572.     DIM shape AS tSHAPE
  1573.     CALL shapeCreate(shape, cSHAPE_POLYGON, 0)
  1574.     CALL bodyCreate(body(), index, shape)
  1575.     CALL boxCreate(p(), body(), index, xs, ys)
  1576.     CALL polygonInitialize(body(), p(), index)
  1577.     body(index).c = _RGB32(255, 255, 255)
  1578.     createBoxBodies = index
  1579.  
  1580. FUNCTION createBoxBodiesEx (p() AS tPOLY, body() AS tBODY, objName AS STRING, xs AS _FLOAT, ys AS _FLOAT)
  1581.     DIM shape AS tSHAPE
  1582.     DIM index AS INTEGER
  1583.     CALL shapeCreate(shape, cSHAPE_POLYGON, 0)
  1584.     CALL bodyCreateEx(body(), objName, shape, index)
  1585.     CALL boxCreate(p(), body(), index, xs, ys)
  1586.     CALL polygonInitialize(body(), p(), index)
  1587.     body(index).c = _RGB32(255, 255, 255)
  1588.     createBoxBodiesEx = index
  1589.  
  1590. SUB createTrapBody (p() AS tPOLY, body() AS tBODY, index AS INTEGER, xs AS _FLOAT, ys AS _FLOAT, yoff1 AS _FLOAT, yoff2 AS _FLOAT)
  1591.     DIM shape AS tSHAPE
  1592.     CALL shapeCreate(shape, cSHAPE_POLYGON, 0)
  1593.     CALL bodyCreate(body(), index, shape)
  1594.     CALL trapCreate(p(), body(), index, xs, ys, yoff1, yoff2)
  1595.     CALL polygonInitialize(body(), p(), index)
  1596.     body(index).c = _RGB32(255, 255, 255)
  1597.  
  1598. SUB createTrapBodyEx (p() AS tPOLY, body() AS tBODY, objName AS STRING, xs AS _FLOAT, ys AS _FLOAT, yoff1 AS _FLOAT, yoff2 AS _FLOAT)
  1599.     DIM shape AS tSHAPE
  1600.     DIM index AS INTEGER
  1601.     CALL shapeCreate(shape, cSHAPE_POLYGON, 0)
  1602.     CALL bodyCreateEx(body(), objName, shape, index)
  1603.     CALL trapCreate(p(), body(), index, xs, ys, yoff1, yoff2)
  1604.     CALL polygonInitialize(body(), p(), index)
  1605.     body(index).c = _RGB32(255, 255, 255)
  1606.  
  1607.  
  1608. FUNCTION objectManagerAdd (body() AS tBODY)
  1609.     objectManagerAdd = objectManager.objectCount
  1610.     objectManager.objectCount = objectManager.objectCount + 1
  1611.     IF objectManager.objectCount > sNUMBEROFBODIES THEN
  1612.         sNUMBEROFBODIES = sNUMBEROFBODIES * 1.5
  1613.         REDIM _PRESERVE body(sNUMBEROFBODIES) AS tBODY
  1614.     END IF
  1615.  
  1616. FUNCTION objectManagerID (body() AS tBODY, objName AS STRING)
  1617.     DIM i AS INTEGER
  1618.     DIM uID AS _INTEGER64
  1619.     uID = computeHash(RTRIM$(LTRIM$(objName)))
  1620.     objectManagerID = -1
  1621.  
  1622.     FOR i = 0 TO objectManager.objectCount
  1623.         IF body(i).objectHash = uID THEN
  1624.             objectManagerID = i
  1625.             EXIT FUNCTION
  1626.         END IF
  1627.     NEXT
  1628.  
  1629. SUB bodyCreateEx (body() AS tBODY, objName AS STRING, shape AS tSHAPE, index AS INTEGER)
  1630.     index = objectManagerAdd(body())
  1631.     body(index).objectName = objName
  1632.     body(index).objectHash = computeHash(objName)
  1633.     CALL bodyCreate(body(), index, shape)
  1634.  
  1635.  
  1636. SUB bodyCreate (body() AS tBODY, index AS INTEGER, shape AS tSHAPE)
  1637.     CALL vectorSet(body(index).position, 0, 0)
  1638.     CALL vectorSet(body(index).velocity, 0, 0)
  1639.     body(index).angularVelocity = 0.0
  1640.     body(index).torque = 0.0
  1641.     body(index).orient = 0.0
  1642.  
  1643.     CALL vectorSet(body(index).force, 0, 0)
  1644.     body(index).staticFriction = 0.5
  1645.     body(index).dynamicFriction = 0.3
  1646.     body(index).restitution = 0.2
  1647.     body(index).shape = shape
  1648.     body(index).collisionMask = 255
  1649.     body(index).enable = 1
  1650.  
  1651. SUB boxCreate (p() AS tPOLY, body() AS tBODY, index AS INTEGER, sizex AS _FLOAT, sizey AS _FLOAT)
  1652.     DIM vertlength AS INTEGER: vertlength = 3
  1653.     DIM verts(vertlength) AS tVECTOR2d
  1654.  
  1655.     CALL vectorSet(verts(0), -sizex, -sizey)
  1656.     CALL vectorSet(verts(1), sizex, -sizey)
  1657.     CALL vectorSet(verts(2), sizex, sizey)
  1658.     CALL vectorSet(verts(3), -sizex, sizey)
  1659.  
  1660.     CALL vertexSet(p(), body(), index, verts(), vertlength)
  1661.  
  1662. SUB trapCreate (p() AS tPOLY, body() AS tBODY, index AS INTEGER, sizex AS _FLOAT, sizey AS _FLOAT, yOff1 AS _FLOAT, yOff2 AS _FLOAT)
  1663.     DIM vertlength AS INTEGER: vertlength = 3
  1664.     DIM verts(vertlength) AS tVECTOR2d
  1665.  
  1666.     CALL vectorSet(verts(0), -sizex, -sizey - yOff2)
  1667.     CALL vectorSet(verts(1), sizex, -sizey - yOff1)
  1668.     CALL vectorSet(verts(2), sizex, sizey)
  1669.     CALL vectorSet(verts(3), -sizex, sizey)
  1670.  
  1671.     CALL vertexSet(p(), body(), index, verts(), vertlength)
  1672.  
  1673. SUB createTerrianBody (p() AS tPOLY, body() AS tBODY, index AS INTEGER, slices AS INTEGER, sliceWidth AS _FLOAT, nominalHeight AS _FLOAT)
  1674.     DIM shape AS tSHAPE
  1675.     DIM elevation(slices) AS _FLOAT
  1676.  
  1677.     DIM AS INTEGER i, j
  1678.     FOR j = 0 TO slices
  1679.         elevation(j) = RND * 500
  1680.     NEXT
  1681.  
  1682.     CALL shapeCreate(shape, cSHAPE_POLYGON, 0)
  1683.  
  1684.     FOR i = 0 TO slices - 1
  1685.         CALL bodyCreate(body(), index + i, shape)
  1686.         CALL terrainCreate(p(), body(), index + i, elevation(i), elevation(i + 1), sliceWidth, nominalHeight)
  1687.         CALL polygonInitialize(body(), p(), index + i)
  1688.         body(index + i).c = _RGB32(255, 255, 255)
  1689.         CALL bodySetStatic(body(index + i))
  1690.     NEXT i
  1691.  
  1692.  
  1693. SUB createTerrianBodyEx (p() AS tPOLY, body() AS tBODY, objName AS STRING, elevation() AS _FLOAT, slices AS INTEGER, sliceWidth AS _FLOAT, nominalHeight AS _FLOAT)
  1694.     DIM shape AS tSHAPE
  1695.  
  1696.     DIM AS INTEGER i, index
  1697.  
  1698.     CALL shapeCreate(shape, cSHAPE_POLYGON, 0)
  1699.  
  1700.     FOR i = 0 TO slices - 1
  1701.         CALL bodyCreateEx(body(), objName + "_" + LTRIM$(STR$(i)), shape, index)
  1702.         CALL terrainCreate(p(), body(), index, elevation(i), elevation(i + 1), sliceWidth, nominalHeight)
  1703.         CALL polygonInitialize(body(), p(), index)
  1704.         body(index).c = _RGB32(255, 255, 255)
  1705.         CALL bodySetStatic(body(index))
  1706.     NEXT i
  1707.  
  1708.     DIM AS _FLOAT p1, p2
  1709.     DIM start AS _INTEGER64
  1710.  
  1711.     FOR i = 0 TO slices - 1
  1712.         start = objectManagerID(body(), objName + "_" + LTRIM$(STR$(i)))
  1713.         p1 = (sliceWidth / 2) - p(body(start).pa.start).vert.x
  1714.         p2 = nominalHeight - p(body(start).pa.start + 1).vert.y
  1715.         CALL setBody(body(), cPARAMETER_POSITION, start, world.terrainPosition.x + p1 + (sliceWidth * i), world.terrainPosition.y + p2)
  1716.     NEXT
  1717.  
  1718.  
  1719. SUB terrainCreate (p() AS tPOLY, body() AS tBODY, index AS INTEGER, ele1 AS _FLOAT, ele2 AS _FLOAT, sliceWidth AS _FLOAT, nominalHeight AS _FLOAT)
  1720.     DIM AS INTEGER vertLength
  1721.     vertLength = 3 ' numOfslices + 1
  1722.     DIM verts(vertLength) AS tVECTOR2d
  1723.  
  1724.     CALL vectorSet(verts(0), 0, nominalHeight)
  1725.     CALL vectorSet(verts(1), (0) * sliceWidth, -nominalHeight - ele1)
  1726.     CALL vectorSet(verts(2), (1) * sliceWidth, -nominalHeight - ele2)
  1727.     CALL vectorSet(verts(3), (1) * sliceWidth, nominalHeight)
  1728.     CALL vertexSet(p(), body(), index, verts(), vertLength)
  1729.  
  1730.  
  1731.  
  1732. SUB vShapeCreate (p() AS tPOLY, body() AS tBODY, index AS INTEGER, sizex AS _FLOAT, sizey AS _FLOAT)
  1733.     DIM vertlength AS INTEGER: vertlength = 7
  1734.     DIM verts(vertlength) AS tVECTOR2d
  1735.  
  1736.     CALL vectorSet(verts(0), -sizex, -sizey)
  1737.     CALL vectorSet(verts(1), sizex, -sizey)
  1738.     CALL vectorSet(verts(2), sizex, sizey)
  1739.     CALL vectorSet(verts(3), -sizex, sizey)
  1740.     CALL vectorSet(verts(4), -sizex, sizey / 2)
  1741.     CALL vectorSet(verts(5), sizex / 2, sizey / 2)
  1742.     CALL vectorSet(verts(6), sizex / 2, -sizey / 2)
  1743.     CALL vectorSet(verts(7), -sizex, sizey / 2)
  1744.  
  1745.     CALL vertexSet(p(), body(), index, verts(), vertlength)
  1746.  
  1747. SUB vertexSet (p() AS tPOLY, body() AS tBODY, index AS INTEGER, verts() AS tVECTOR2d, vertLength AS INTEGER)
  1748.     DIM rightMost AS INTEGER: rightMost = 0
  1749.     DIM highestXCoord AS _FLOAT: highestXCoord = verts(0).x
  1750.     DIM i AS INTEGER
  1751.     DIM x AS _FLOAT
  1752.     FOR i = 1 TO vertLength
  1753.         x = verts(i).x
  1754.         IF x > highestXCoord THEN
  1755.             highestXCoord = x
  1756.             rightMost = i
  1757.         ELSE
  1758.             IF x = highestXCoord THEN
  1759.                 IF verts(i).y < verts(rightMost).y THEN
  1760.                     rightMost = i
  1761.                 END IF
  1762.             END IF
  1763.         END IF
  1764.     NEXT
  1765.     DIM hull(cMAXNUMBEROFPOLYGONS) AS INTEGER
  1766.     DIM outCount AS INTEGER: outCount = 0
  1767.     DIM indexHull AS INTEGER: indexHull = rightMost
  1768.     DIM nextHullIndex AS INTEGER
  1769.     DIM e1 AS tVECTOR2d
  1770.     DIM e2 AS tVECTOR2d
  1771.     DIM c AS _FLOAT
  1772.     DO
  1773.         hull(outCount) = indexHull
  1774.         nextHullIndex = 0
  1775.         FOR i = 1 TO vertLength
  1776.             IF nextHullIndex = indexHull THEN
  1777.                 nextHullIndex = i
  1778.                 _CONTINUE
  1779.             END IF
  1780.             CALL vectorSubVectorND(e1, verts(nextHullIndex), verts(hull(outCount)))
  1781.             CALL vectorSubVectorND(e2, verts(i), verts(hull(outCount)))
  1782.             c = vectorCross(e1, e2)
  1783.             IF c < 0.0 THEN nextHullIndex = i
  1784.             IF c = 0.0 AND (vectorLengthSq(e2) > vectorLengthSq(e1)) THEN
  1785.                 nextHullIndex = i
  1786.             END IF
  1787.         NEXT
  1788.         outCount = outCount + 1
  1789.         indexHull = nextHullIndex
  1790.         IF nextHullIndex = rightMost THEN
  1791.             body(index).pa.count = outCount - 1
  1792.             EXIT DO
  1793.         END IF
  1794.     LOOP
  1795.  
  1796.     IF index = 0 THEN
  1797.         body(index).pa.start = 0
  1798.     ELSE
  1799.         body(index).pa.start = body(index - 1).pa.start + body(index - 1).pa.count + 1
  1800.     END IF
  1801.  
  1802.     FOR i = 0 TO vertLength
  1803.         p(body(index).pa.start + i).vert = verts(hull(i))
  1804.     NEXT
  1805.  
  1806.     DIM face AS tVECTOR2d
  1807.     FOR i = 0 TO vertLength
  1808.         CALL vectorSubVectorND(face, p(body(index).pa.start + arrayNextIndex(i, body(index).pa.count)).vert, p(body(index).pa.start + i).vert)
  1809.         CALL vectorSet(p(body(index).pa.start + i).norm, face.y, -face.x)
  1810.         CALL vectorNormalize(p(body(index).pa.start + i).norm)
  1811.     NEXT
  1812.  
  1813. FUNCTION arrayNextIndex (i AS INTEGER, count AS INTEGER)
  1814.     arrayNextIndex = ((i + 1) MOD (count + 1))
  1815.  
  1816. '**********************************************************************************************
  1817. '   Rendering Stuff Ahead
  1818. '**********************************************************************************************
  1819.  
  1820. SUB renderFps
  1821.     fpsLast = fps
  1822.     fps = 0
  1823.  
  1824. FUNCTION AABBOverlap (Ax AS _FLOAT, Ay AS _FLOAT, Aw AS _FLOAT, Ah AS _FLOAT, Bx AS _FLOAT, By AS _FLOAT, Bw AS _FLOAT, Bh AS _FLOAT)
  1825.     AABBOverlap = Ax < Bx + Bw AND Ax + Aw > Bx AND Ay < By + Bh AND Ay + Ah > By
  1826.  
  1827. FUNCTION AABBOverlapVector (A AS tVECTOR2d, Aw AS _FLOAT, Ah AS _FLOAT, B AS tVECTOR2d, Bw AS _FLOAT, Bh AS _FLOAT)
  1828.     AABBOverlapVector = AABBOverlap(A.x, A.y, Aw, Ah, B.x, B.y, Bw, Bh)
  1829.  
  1830.  
  1831. SUB renderBodies (p() AS tPOLY, body() AS tBODY, j() AS tJOINT, hits() AS tHIT, camera AS tCAMERA)
  1832.     DIM i AS INTEGER
  1833.     DIM hitcount AS INTEGER
  1834.     DIM camoffset AS tVECTOR2d
  1835.     DIM AS _FLOAT cx, cy, cwx, cwy, ox, oy, owx, owy, ccw, cch
  1836.     ccw = _WIDTH / 2
  1837.     cch = _HEIGHT / 2
  1838.     FOR i = 0 TO objectManager.objectCount
  1839.         IF body(i).enable THEN
  1840.             'AABB to cut down on rendering objects out of camera view
  1841.             cx = camera.position.x - ccw
  1842.             cy = camera.position.y - cch
  1843.             cwx = _WIDTH
  1844.             cwy = _HEIGHT
  1845.             ox = body(i).position.x - 2000
  1846.             oy = body(i).position.y - 2000
  1847.             owx = 4000
  1848.             owy = 4000
  1849.             IF AABBOverlap(cx, cy, cwx, cwy, ox, oy, owx, owy) THEN
  1850.                 IF body(i).shape.ty = cSHAPE_CIRCLE THEN
  1851.                     IF body(i).shape.texture = 0 THEN
  1852.                         CALL renderWireframeCircle(body(), i, camera)
  1853.                     ELSE
  1854.                         CALL renderTexturedCircle(body(), i, camera)
  1855.                     END IF
  1856.                 ELSE IF body(i).shape.ty = cSHAPE_POLYGON THEN
  1857.                         IF body(i).shape.texture = 0 THEN
  1858.                             CALL renderWireframePoly(p(), body(), i, camera)
  1859.                         ELSE
  1860.                             CALL renderTexturedBox(p(), body(), i, camera)
  1861.                         END IF
  1862.                     END IF
  1863.                 END IF
  1864.             END IF
  1865.         END IF
  1866.     NEXT
  1867.     IF cRENDER_JOINTS THEN
  1868.         FOR i = 1 TO sNUMBEROFJOINTS
  1869.             CALL renderJoints(j(i), body(), camera)
  1870.         NEXT
  1871.     END IF
  1872.     IF cRENDER_COLLISIONS THEN
  1873.         hitcount = 0
  1874.         DO WHILE hits(hitcount).A <> hits(hitcount).B
  1875.             CALL vectorSubVectorND(camoffset, hits(hitcount).position, camera.position)
  1876.             CIRCLE (camoffset.x, camoffset.y), 5, _RGB(255, 6, 11)
  1877.             hitcount = hitcount + 1
  1878.         LOOP
  1879.     END IF
  1880.  
  1881.  
  1882. SUB renderJoints (j AS tJOINT, b() AS tBODY, camera AS tCAMERA)
  1883.     DIM b1 AS tBODY: b1 = b(j.body1)
  1884.     DIM b2 AS tBODY: b2 = b(j.body2)
  1885.     DIM R1 AS tMATRIX2d: R1 = b1.shape.u
  1886.     DIM R2 AS tMATRIX2d: R2 = b2.shape.u
  1887.  
  1888.     DIM x1 AS tVECTOR2d: x1 = b1.position
  1889.     DIM p1 AS tVECTOR2d: CALL matrixMultiplyVector(R1, j.localAnchor1, p1)
  1890.  
  1891.     CALL vectorAddVectorND(p1, p1, x1)
  1892.  
  1893.     DIM x2 AS tVECTOR2d: x2 = b2.position
  1894.     DIM p2 AS tVECTOR2d: CALL matrixMultiplyVector(R2, j.localAnchor2, p2)
  1895.  
  1896.     CALL vectorAddVectorND(p2, p2, x2)
  1897.  
  1898.     CALL vectorSubVector(p1, camera.position)
  1899.     CALL vectorSubVector(x2, camera.position)
  1900.  
  1901.     LINE (p1.x, p1.y)-(x2.x, x2.y), _RGB(255, 255, 127) 'yellow
  1902.  
  1903. SUB renderWireframePoly (p() AS tPOLY, b() AS tBODY, index AS INTEGER, camera AS tCAMERA)
  1904.     DIM a AS tVECTOR2d ' dummy vertices
  1905.     DIM b AS tVECTOR2d
  1906.     DIM tv AS tVECTOR2d
  1907.     CALL vectorSet(tv, _WIDTH / 2, _HEIGHT / 2)
  1908.  
  1909.     DIM i, element, element_next AS INTEGER
  1910.     FOR i = 0 TO b(index).pa.count
  1911.         element = b(index).pa.start + i
  1912.         element_next = b(index).pa.start + arrayNextIndex(i, b(index).pa.count)
  1913.         a = p(element).vert
  1914.         b = p(element_next).vert
  1915.         CALL worldToCamera(b(), camera, index, a)
  1916.         CALL worldToCamera(b(), camera, index, b)
  1917.         LINE (a.x, a.y)-(b.x, b.y), b(index).c
  1918.     NEXT
  1919.  
  1920. SUB renderTexturedBox (p() AS tPOLY, b() AS tBODY, index AS INTEGER, camera AS tCAMERA)
  1921.     DIM vert(3) AS tVECTOR2d
  1922.  
  1923.     DIM AS INTEGER W, H
  1924.     DIM bm AS LONG ' Texture map
  1925.     bm = b(index).shape.texture
  1926.     W = _WIDTH(bm): H = _HEIGHT(bm)
  1927.  
  1928.     DIM i AS INTEGER
  1929.     FOR i = 0 TO 3
  1930.         vert(i) = p(b(index).pa.start + i).vert
  1931.         vert(i).x = vert(i).x + b(index).shape.offsetTextureX
  1932.         vert(i).y = vert(i).y + b(index).shape.offsetTextureY
  1933.         vert(i).x = vert(i).x * b(index).shape.scaleTextureX
  1934.         vert(i).y = vert(i).y * b(index).shape.scaleTextureY
  1935.         CALL worldToCamera(b(), camera, index, vert(i))
  1936.     NEXT
  1937.     IF b(index).velocity.x > 1 OR b(index).shape.flipTexture = 0 THEN
  1938.         _MAPTRIANGLE (0, 0)-(W - 1, 0)-(W - 1, H - 1), bm TO(vert(3).x, vert(3).y)-(vert(0).x, vert(0).y)-(vert(1).x, vert(1).y)
  1939.         _MAPTRIANGLE (0, 0)-(0, H - 1)-(W - 1, H - 1), bm TO(vert(3).x, vert(3).y)-(vert(2).x, vert(2).y)-(vert(1).x, vert(1).y)
  1940.     ELSE
  1941.         _MAPTRIANGLE (0, 0)-(W - 1, 0)-(W - 1, H - 1), bm TO(vert(0).x, vert(0).y)-(vert(3).x, vert(3).y)-(vert(2).x, vert(2).y)
  1942.         _MAPTRIANGLE (0, 0)-(0, H - 1)-(W - 1, H - 1), bm TO(vert(0).x, vert(0).y)-(vert(1).x, vert(1).y)-(vert(2).x, vert(2).y)
  1943.     END IF
  1944.  
  1945.  
  1946. SUB renderWireframeCircle (b() AS tBODY, index AS INTEGER, camera AS tCAMERA)
  1947.     DIM tv AS tVECTOR2d: tv = b(index).position
  1948.     tv.x = tv.x * camera.zoom
  1949.     tv.y = tv.y * camera.zoom
  1950.     CALL worldToCameraNR(b(), camera, index, tv)
  1951.     CIRCLE (tv.x, tv.y), b(index).shape.radius * camera.zoom, b(index).c
  1952.     LINE (tv.x, tv.y)-(tv.x + COS(b(index).orient) * (b(index).shape.radius * camera.zoom), tv.y + SIN(b(index).orient) * (b(index).shape.radius) * camera.zoom), b(index).c
  1953.  
  1954. SUB renderTexturedCircle (b() AS tBODY, index AS INTEGER, camera AS tCAMERA)
  1955.     DIM vert(3) AS tVECTOR2d
  1956.     DIM W, H AS INTEGER
  1957.     DIM bm AS LONG
  1958.     'DIM tv AS tVECTOR2d
  1959.     ' CALL vectorSet(tv, _WIDTH / 2 * (1 / camera.zoom), _HEIGHT / 2 * (1 / camera.zoom))
  1960.  
  1961.     bm = b(index).shape.texture
  1962.     W = _WIDTH(bm): H = _HEIGHT(bm)
  1963.     CALL vectorSet(vert(0), -b(index).shape.radius, -b(index).shape.radius)
  1964.     CALL vectorSet(vert(1), -b(index).shape.radius, b(index).shape.radius)
  1965.     CALL vectorSet(vert(2), b(index).shape.radius, b(index).shape.radius)
  1966.     CALL vectorSet(vert(3), b(index).shape.radius, -b(index).shape.radius)
  1967.     DIM i AS INTEGER
  1968.     FOR i = 0 TO 3
  1969.         CALL worldToCamera(b(), camera, index, vert(i))
  1970.     NEXT
  1971.     _MAPTRIANGLE (0, 0)-(0, H - 1)-(W - 1, H - 1), bm TO(vert(0).x, vert(0).y)-(vert(1).x, vert(1).y)-(vert(2).x, vert(2).y)
  1972.     _MAPTRIANGLE (0, 0)-(W - 1, 0)-(W - 1, H - 1), bm TO(vert(0).x, vert(0).y)-(vert(3).x, vert(3).y)-(vert(2).x, vert(2).y)
  1973.  
  1974.  
  1975. SUB worldToCamera (body() AS tBODY, camera AS tCAMERA, index AS INTEGER, vert AS tVECTOR2d)
  1976.     DIM tv AS tVECTOR2d
  1977.     CALL vectorSet(tv, _WIDTH / 2 * (1 / camera.zoom), _HEIGHT / 2 * (1 / camera.zoom)) ' Camera Center
  1978.     CALL matrixMultiplyVector(body(index).shape.u, vert, vert) ' Rotate body
  1979.     CALL vectorAddVector(vert, body(index).position) ' Add Position
  1980.     CALL vectorSubVector(vert, camera.position) 'Sub Camera Position
  1981.     CALL vectorAddVector(vert, tv) ' Add to camera Center
  1982.     CALL vectorMultiplyScalar(vert, camera.zoom) 'Zoom everything
  1983.  
  1984. SUB worldToCameraNR (body() AS tBODY, camera AS tCAMERA, index AS INTEGER, vert AS tVECTOR2d)
  1985.     DIM tv AS tVECTOR2d
  1986.     CALL vectorSet(tv, _WIDTH / 2 * (1 / camera.zoom), _HEIGHT / 2 * (1 / camera.zoom)) ' Camera Center
  1987.     CALL vectorSetVector(vert, body(index).position) ' Add Position
  1988.     CALL vectorSubVector(vert, camera.position) 'Sub Camera Position
  1989.     CALL vectorAddVector(vert, tv) ' Add to camera Center
  1990.     CALL vectorMultiplyScalar(vert, camera.zoom) 'Zoom everything
  1991.  
  1992.  
  1993. SUB polygonSetOrient (b AS tBODY, radians AS _FLOAT)
  1994.     CALL matrixSetRadians(b.shape.u, radians)
  1995.  
  1996. '**********************************************************************************************
  1997. '   Object initialization Ahead
  1998. '**********************************************************************************************
  1999.  
  2000. SUB circleInitialize (b() AS tBODY, index AS INTEGER)
  2001.     CALL circleComputeMass(b(), index, .10)
  2002.  
  2003. SUB circleComputeMass (b() AS tBODY, index AS INTEGER, density AS _FLOAT)
  2004.     b(index).mass = cPI * b(index).shape.radius * b(index).shape.radius * density
  2005.     IF b(index).mass <> 0 THEN
  2006.         b(index).invMass = 1.0 / b(index).mass
  2007.     ELSE
  2008.         b(index).invMass = 0.0
  2009.     END IF
  2010.  
  2011.     b(index).inertia = b(index).mass * b(index).shape.radius * b(index).shape.radius
  2012.  
  2013.     IF b(index).inertia <> 0 THEN
  2014.         b(index).invInertia = 1.0 / b(index).inertia
  2015.     ELSE
  2016.         b(index).invInertia = 0.0
  2017.     END IF
  2018.  
  2019. SUB polygonInitialize (body() AS tBODY, p() AS tPOLY, index AS INTEGER)
  2020.     CALL polygonComputeMass(body(), p(), index, .10)
  2021.  
  2022. SUB polygonComputeMass (b() AS tBODY, p() AS tPOLY, index AS INTEGER, density AS _FLOAT)
  2023.     DIM c AS tVECTOR2d ' centroid
  2024.     DIM p1 AS tVECTOR2d
  2025.     DIM p2 AS tVECTOR2d
  2026.     DIM area AS _FLOAT
  2027.     DIM I AS _FLOAT
  2028.     DIM k_inv3 AS _FLOAT
  2029.     DIM D AS _FLOAT
  2030.     DIM triangleArea AS _FLOAT
  2031.     DIM weight AS _FLOAT
  2032.     DIM intx2 AS _FLOAT
  2033.     DIM inty2 AS _FLOAT
  2034.     DIM ii AS INTEGER
  2035.  
  2036.     k_inv3 = 1.0 / 3.0
  2037.  
  2038.     FOR ii = 0 TO b(index).pa.count
  2039.         p1 = p(b(index).pa.start + ii).vert
  2040.         p2 = p(b(index).pa.start + arrayNextIndex(ii, b(index).pa.count)).vert
  2041.         D = vectorCross(p1, p2)
  2042.         triangleArea = .5 * D
  2043.         area = area + triangleArea
  2044.         weight = triangleArea * k_inv3
  2045.         CALL vectorAddVectorScalar(c, p1, weight)
  2046.         CALL vectorAddVectorScalar(c, p2, weight)
  2047.         intx2 = p1.x * p1.x + p2.x * p1.x + p2.x * p2.x
  2048.         inty2 = p1.y * p1.y + p2.y * p1.y + p2.y * p2.y
  2049.         I = I + (0.25 * k_inv3 * D) * (intx2 + inty2)
  2050.     NEXT ii
  2051.  
  2052.     CALL vectorMultiplyScalar(c, 1.0 / area)
  2053.  
  2054.     FOR ii = 0 TO b(index).pa.count
  2055.         CALL vectorSubVector(p(b(index).pa.start + ii).vert, c)
  2056.     NEXT
  2057.  
  2058.     b(index).mass = density * area
  2059.     IF b(index).mass <> 0.0 THEN
  2060.         b(index).invMass = 1.0 / b(index).mass
  2061.     ELSE
  2062.         b(index).invMass = 0.0
  2063.     END IF
  2064.  
  2065.     b(index).inertia = I * density
  2066.     IF b(index).inertia <> 0 THEN
  2067.         b(index).invInertia = 1.0 / b(index).inertia
  2068.     ELSE
  2069.         b(index).invInertia = 0.0
  2070.     END IF
  2071. '**********************************************************************************************
  2072. '   Joint Stuff Ahead
  2073. '**********************************************************************************************
  2074. SUB jointSet (j AS tJOINT, body() AS tBODY, b1 AS INTEGER, b2 AS INTEGER, x AS _FLOAT, y AS _FLOAT)
  2075.     DIM anchor AS tVECTOR2d
  2076.     CALL vectorSet(anchor, x, y)
  2077.     DIM Rot1 AS tMATRIX2d: Rot1 = body(b1).shape.u
  2078.     DIM Rot2 AS tMATRIX2d: Rot2 = body(b2).shape.u
  2079.     DIM Rot1T AS tMATRIX2d: CALL matrixTranspose(Rot1, Rot1T)
  2080.     DIM Rot2T AS tMATRIX2d: CALL matrixTranspose(Rot2, Rot2T)
  2081.     DIM tv AS tVECTOR2d
  2082.  
  2083.     j.body1 = b1
  2084.     j.body2 = b2
  2085.  
  2086.     CALL vectorSubVectorND(tv, anchor, body(b1).position)
  2087.     CALL matrixMultiplyVector(Rot1T, tv, j.localAnchor1)
  2088.  
  2089.     CALL vectorSubVectorND(tv, anchor, body(b2).position)
  2090.     CALL matrixMultiplyVector(Rot2T, tv, j.localAnchor2)
  2091.  
  2092.     CALL vectorSet(j.P, 0, 0)
  2093.  
  2094.     j.softness = 0.001
  2095.     j.biasFactor = 100
  2096.  
  2097.  
  2098. SUB jointPrestep (j AS tJOINT, body() AS tBODY, inv_dt AS _FLOAT)
  2099.     DIM Rot1 AS tMATRIX2d: Rot1 = body(j.body1).shape.u
  2100.     DIM Rot2 AS tMATRIX2d: Rot2 = body(j.body2).shape.u
  2101.     DIM b1invMass AS _FLOAT
  2102.     DIM b2invMass AS _FLOAT
  2103.  
  2104.     DIM b1invInertia AS _FLOAT
  2105.     DIM b2invInertia AS _FLOAT
  2106.  
  2107.     CALL matrixMultiplyVector(Rot1, j.localAnchor1, j.r1)
  2108.     CALL matrixMultiplyVector(Rot2, j.localAnchor2, j.r2)
  2109.  
  2110.     b1invMass = body(j.body1).invMass
  2111.     b2invMass = body(j.body2).invMass
  2112.  
  2113.     b1invInertia = body(j.body1).invInertia
  2114.     b2invInertia = body(j.body2).invInertia
  2115.  
  2116.     DIM K1 AS tMATRIX2d
  2117.     Call matrixSetScalar(K1, b1invMass + b2invMass, 0,_
  2118.                                                 0, b1invMass + b2invMass)
  2119.     DIM K2 AS tMATRIX2d
  2120.     Call matrixSetScalar(K2, b1invInertia * j.r1.y * j.r1.y, -b1invInertia * j.r1.x * j.r1.y,_
  2121.                             -b1invInertia * j.r1.x * j.r1.y,  b1invInertia * j.r1.x * j.r1.x)
  2122.  
  2123.     DIM K3 AS tMATRIX2d
  2124.     Call matrixSetScalar(K3,  b2invInertia * j.r2.y * j.r2.y, - b2invInertia * j.r2.x * j.r2.y,_
  2125.                              -b2invInertia * j.r2.x * j.r2.y,   b2invInertia * j.r2.x * j.r2.x)
  2126.  
  2127.     DIM K AS tMATRIX2d
  2128.     CALL matrixAddMatrix(K1, K2, K)
  2129.     CALL matrixAddMatrix(K3, K, K)
  2130.     K.m00 = K.m00 + j.softness
  2131.     K.m11 = K.m11 + j.softness
  2132.     CALL matrixInvert(K, j.M)
  2133.  
  2134.     DIM p1 AS tVECTOR2d: CALL vectorAddVectorND(p1, body(j.body1).position, j.r1)
  2135.     DIM p2 AS tVECTOR2d: CALL vectorAddVectorND(p2, body(j.body2).position, j.r2)
  2136.     DIM dp AS tVECTOR2d: CALL vectorSubVectorND(dp, p2, p1)
  2137.  
  2138.     CALL vectorMultiplyScalarND(j.bias, dp, -j.biasFactor * inv_dt)
  2139.     'Call vectorSet(j.bias, 0, 0)
  2140.     CALL vectorSet(j.P, 0, 0)
  2141.  
  2142. SUB jointApplyImpulse (j AS tJOINT, body() AS tBODY)
  2143.     DIM dv AS tVECTOR2d
  2144.     DIM impulse AS tVECTOR2d
  2145.     DIM cross1 AS tVECTOR2d
  2146.     DIM cross2 AS tVECTOR2d
  2147.     DIM tv AS tVECTOR2d
  2148.  
  2149.  
  2150.     'Vec2 dv = body2->velocity + Cross(body2->angularVelocity, r2) - body1->velocity - Cross(body1->angularVelocity, r1);
  2151.     CALL vectorCrossScalar(cross2, j.r2, body(j.body2).angularVelocity)
  2152.     CALL vectorCrossScalar(cross1, j.r1, body(j.body1).angularVelocity)
  2153.     CALL vectorAddVectorND(dv, body(j.body2).velocity, cross2)
  2154.     CALL vectorSubVectorND(dv, dv, body(j.body1).velocity)
  2155.     CALL vectorSubVectorND(dv, dv, cross1)
  2156.  
  2157.     ' impulse = M * (bias - dv - softness * P);
  2158.     CALL vectorMultiplyScalarND(tv, j.P, j.softness)
  2159.     CALL vectorSubVectorND(impulse, j.bias, dv)
  2160.     CALL vectorSubVectorND(impulse, impulse, tv)
  2161.     CALL matrixMultiplyVector(j.M, impulse, impulse)
  2162.  
  2163.     ' body1->velocity -= body1->invMass * impulse;
  2164.  
  2165.     CALL vectorMultiplyScalarND(tv, impulse, body(j.body1).invMass)
  2166.     CALL vectorSubVectorND(body(j.body1).velocity, body(j.body1).velocity, tv)
  2167.  
  2168.     ' body1->angularVelocity -= body1->invI * Cross(r1, impulse);
  2169.     DIM crossScalar AS _FLOAT
  2170.     crossScalar = vectorCross(j.r1, impulse)
  2171.     body(j.body1).angularVelocity = body(j.body1).angularVelocity - body(j.body1).invInertia * crossScalar
  2172.  
  2173.     CALL vectorMultiplyScalarND(tv, impulse, body(j.body2).invMass)
  2174.     CALL vectorAddVectorND(body(j.body2).velocity, body(j.body2).velocity, tv)
  2175.  
  2176.     crossScalar = vectorCross(j.r2, impulse)
  2177.     body(j.body2).angularVelocity = body(j.body2).angularVelocity + body(j.body2).invInertia * crossScalar
  2178.  
  2179.     CALL vectorAddVectorND(j.P, j.P, impulse)
  2180.  
  2181.  
  2182. '**********************************************************************************************
  2183. '   Vector Math Ahead
  2184. '**********************************************************************************************
  2185.  
  2186. SUB vectorSet (v AS tVECTOR2d, x AS _FLOAT, y AS _FLOAT)
  2187.     v.x = x
  2188.     v.y = y
  2189.  
  2190. SUB vectorSetVector (o AS tVECTOR2d, v AS tVECTOR2d)
  2191.     o.x = v.x
  2192.     o.y = v.y
  2193.  
  2194. SUB vectorNeg (v AS tVECTOR2d)
  2195.     v.x = -v.x
  2196.     v.y = -v.y
  2197.  
  2198. SUB vectorNegND (o AS tVECTOR2d, v AS tVECTOR2d)
  2199.     o.x = -v.x
  2200.     o.y = -v.y
  2201.  
  2202. SUB vectorMultiplyScalar (v AS tVECTOR2d, s AS _FLOAT)
  2203.     v.x = v.x * s
  2204.     v.y = v.y * s
  2205.  
  2206. SUB vectorMultiplyScalarND (o AS tVECTOR2d, v AS tVECTOR2d, s AS _FLOAT)
  2207.     o.x = v.x * s
  2208.     o.y = v.y * s
  2209.  
  2210. SUB vectorDivideScalar (v AS tVECTOR2d, s AS _FLOAT)
  2211.     v.x = v.x / s
  2212.     v.y = v.y / s
  2213.  
  2214. SUB vectorDivideScalarND (o AS tVECTOR2d, v AS tVECTOR2d, s AS _FLOAT)
  2215.     o.x = v.x / s
  2216.     o.y = v.y / s
  2217.  
  2218. SUB vectorAddScalar (v AS tVECTOR2d, s AS _FLOAT)
  2219.     v.x = v.x + s
  2220.     v.y = v.y + s
  2221.  
  2222. SUB vectorAddScalarND (o AS tVECTOR2d, v AS tVECTOR2d, s AS _FLOAT)
  2223.     o.x = v.x + s
  2224.     o.y = v.y + s
  2225.  
  2226. SUB vectorMultiplyVector (v AS tVECTOR2d, m AS tVECTOR2d)
  2227.     v.x = v.x * m.x
  2228.     v.y = v.y * m.y
  2229.  
  2230. SUB vectorMultiplyVectorND (o AS tVECTOR2d, v AS tVECTOR2d, m AS tVECTOR2d)
  2231.     o.x = v.x * m.x
  2232.     o.y = v.y * m.y
  2233.  
  2234. SUB vectorDivideVector (v AS tVECTOR2d, m AS tVECTOR2d)
  2235.     v.x = v.x / m.x
  2236.     v.y = v.y / m.y
  2237.  
  2238. SUB vectorAddVector (v AS tVECTOR2d, m AS tVECTOR2d)
  2239.     v.x = v.x + m.x
  2240.     v.y = v.y + m.y
  2241.  
  2242. SUB vectorAddVectorND (o AS tVECTOR2d, v AS tVECTOR2d, m AS tVECTOR2d)
  2243.     o.x = v.x + m.x
  2244.     o.y = v.y + m.y
  2245.  
  2246. SUB vectorAddVectorScalar (v AS tVECTOR2d, m AS tVECTOR2d, s AS _FLOAT)
  2247.     v.x = v.x + m.x * s
  2248.     v.y = v.y + m.y * s
  2249.  
  2250. SUB vectorAddVectorScalarND (o AS tVECTOR2d, v AS tVECTOR2d, m AS tVECTOR2d, s AS _FLOAT)
  2251.     o.x = v.x + m.x * s
  2252.     o.y = v.y + m.y * s
  2253.  
  2254. SUB vectorSubVector (v AS tVECTOR2d, m AS tVECTOR2d)
  2255.     v.x = v.x - m.x
  2256.     v.y = v.y - m.y
  2257.  
  2258. SUB vectorSubVectorND (o AS tVECTOR2d, v AS tVECTOR2d, m AS tVECTOR2d)
  2259.     o.x = v.x - m.x
  2260.     o.y = v.y - m.y
  2261.  
  2262. FUNCTION vectorLengthSq (v AS tVECTOR2d)
  2263.     vectorLengthSq = v.x * v.x + v.y * v.y
  2264.  
  2265. FUNCTION vectorLength (v AS tVECTOR2d)
  2266.     vectorLength = SQR(vectorLengthSq(v))
  2267.  
  2268. SUB vectorRotate (v AS tVECTOR2d, radians AS _FLOAT)
  2269.     DIM c, s, xp, yp AS _FLOAT
  2270.     c = COS(radians)
  2271.     s = SIN(radians)
  2272.     xp = v.x * c - v.y * s
  2273.     yp = v.x * s + v.y * c
  2274.     v.x = xp
  2275.     v.y = yp
  2276.  
  2277. SUB vectorNormalize (v AS tVECTOR2d)
  2278.     DIM lenSQ, invLen AS _FLOAT
  2279.     lenSQ = vectorLengthSq(v)
  2280.     IF lenSQ > cEPSILON_SQ THEN
  2281.         invLen = 1.0 / SQR(lenSQ)
  2282.         v.x = v.x * invLen
  2283.         v.y = v.y * invLen
  2284.     END IF
  2285.  
  2286. SUB vectorMin (a AS tVECTOR2d, b AS tVECTOR2d, o AS tVECTOR2d)
  2287.     o.x = scalarMin(a.x, b.x)
  2288.     o.y = scalarMin(a.y, b.y)
  2289.  
  2290. SUB vectorMax (a AS tVECTOR2d, b AS tVECTOR2d, o AS tVECTOR2d)
  2291.     o.x = scalarMax(a.x, b.x)
  2292.     o.y = scalarMax(a.y, b.y)
  2293.  
  2294. FUNCTION vectorDot (a AS tVECTOR2d, b AS tVECTOR2d)
  2295.     vectorDot = a.x * b.x + a.y * b.y
  2296.  
  2297. FUNCTION vectorSqDist (a AS tVECTOR2d, b AS tVECTOR2d)
  2298.     DIM dx, dy AS _FLOAT
  2299.     dx = b.x - a.x
  2300.     dy = b.y - a.y
  2301.     vectorSqDist = dx * dx + dy * dy
  2302.  
  2303. FUNCTION vectorDistance (a AS tVECTOR2d, b AS tVECTOR2d)
  2304.     vectorDistance = SQR(vectorSqDist(a, b))
  2305.  
  2306. FUNCTION vectorCross (a AS tVECTOR2d, b AS tVECTOR2d)
  2307.     vectorCross = a.x * b.y - a.y * b.x
  2308.  
  2309. SUB vectorCrossScalar (o AS tVECTOR2d, v AS tVECTOR2d, a AS _FLOAT)
  2310.     o.x = v.y * -a
  2311.     o.y = v.x * a
  2312.  
  2313. FUNCTION vectorArea (a AS tVECTOR2d, b AS tVECTOR2d, c AS tVECTOR2d)
  2314.     vectorArea = (((b.x - a.x) * (c.y - a.y)) - ((c.x - a.x) * (b.y - a.y)))
  2315.  
  2316. FUNCTION vectorLeft (a AS tVECTOR2d, b AS tVECTOR2d, c AS tVECTOR2d)
  2317.     vectorLeft = vectorArea(a, b, c) > 0
  2318.  
  2319. FUNCTION vectorLeftOn (a AS tVECTOR2d, b AS tVECTOR2d, c AS tVECTOR2d)
  2320.     vectorLeftOn = vectorArea(a, b, c) >= 0
  2321.  
  2322. FUNCTION vectorRight (a AS tVECTOR2d, b AS tVECTOR2d, c AS tVECTOR2d)
  2323.     vectorRight = vectorArea(a, b, c) < 0
  2324.  
  2325. FUNCTION vectorRightOn (a AS tVECTOR2d, b AS tVECTOR2d, c AS tVECTOR2d)
  2326.     vectorRightOn = vectorArea(a, b, c) <= 0
  2327.  
  2328. FUNCTION vectorCollinear (a AS tVECTOR2d, b AS tVECTOR2d, c AS tVECTOR2d, thresholdAngle AS _FLOAT)
  2329.     IF (thresholdAngle = 0) THEN
  2330.         vectorCollinear = (vectorArea(a, b, c) = 0)
  2331.     ELSE
  2332.         DIM ab AS tVECTOR2d
  2333.         DIM bc AS tVECTOR2d
  2334.         DIM dot AS _FLOAT
  2335.         DIM magA AS _FLOAT
  2336.         DIM magB AS _FLOAT
  2337.         DIM angle AS _FLOAT
  2338.  
  2339.         ab.x = b.x - a.x
  2340.         ab.y = b.y - a.y
  2341.         bc.x = c.x - b.x
  2342.         bc.y = c.y - b.y
  2343.  
  2344.         dot = ab.x * bc.x + ab.y * bc.y
  2345.         magA = SQR(ab.x * ab.x + ab.y * ab.y)
  2346.         magB = SQR(bc.x * bc.x + bc.y * bc.y)
  2347.         angle = _ACOS(dot / (magA * magB))
  2348.         vectorCollinear = angle < thresholdAngle
  2349.     END IF
  2350.  
  2351. SUB vectorGetSupport (p() AS tPOLY, body() AS tBODY, index AS INTEGER, dir AS tVECTOR2d, bestVertex AS tVECTOR2d)
  2352.     DIM bestProjection AS _FLOAT
  2353.     DIM v AS tVECTOR2d
  2354.     DIM projection AS _FLOAT
  2355.     DIM i AS INTEGER
  2356.     bestVertex.x = -9999999
  2357.     bestVertex.y = -9999999
  2358.     bestProjection = -9999999
  2359.  
  2360.     FOR i = 0 TO body(index).pa.count
  2361.         v = p(i + body(index).pa.start).vert
  2362.         projection = vectorDot(v, dir)
  2363.         IF projection > bestProjection THEN
  2364.             bestVertex = v
  2365.             bestProjection = projection
  2366.         END IF
  2367.     NEXT
  2368.  
  2369. '**********************************************************************************************
  2370. '   Matrix Stuff Ahead
  2371. '**********************************************************************************************
  2372.  
  2373. SUB matrixSetRadians (m AS tMATRIX2d, radians AS _FLOAT)
  2374.     DIM c AS _FLOAT
  2375.     DIM s AS _FLOAT
  2376.     c = COS(radians)
  2377.     s = SIN(radians)
  2378.     m.m00 = c
  2379.     m.m01 = -s
  2380.     m.m10 = s
  2381.     m.m11 = c
  2382.  
  2383. SUB matrixSetScalar (m AS tMATRIX2d, a AS _FLOAT, b AS _FLOAT, c AS _FLOAT, d AS _FLOAT)
  2384.     m.m00 = a
  2385.     m.m01 = b
  2386.     m.m10 = c
  2387.     m.m11 = d
  2388.  
  2389. SUB matrixAbs (m AS tMATRIX2d, o AS tMATRIX2d)
  2390.     o.m00 = ABS(m.m00)
  2391.     o.m01 = ABS(m.m01)
  2392.     o.m10 = ABS(m.m10)
  2393.     o.m11 = ABS(m.m11)
  2394.  
  2395. SUB matrixGetAxisX (m AS tMATRIX2d, o AS tVECTOR2d)
  2396.     o.x = m.m00
  2397.     o.y = m.m10
  2398.  
  2399. SUB matrixGetAxisY (m AS tMATRIX2d, o AS tVECTOR2d)
  2400.     o.x = m.m01
  2401.     o.y = m.m11
  2402.  
  2403. SUB matrixTransposeI (m AS tMATRIX2d)
  2404.     SWAP m.m01, m.m10
  2405.  
  2406. SUB matrixTranspose (m AS tMATRIX2d, o AS tMATRIX2d)
  2407.     DIM tm AS tMATRIX2d
  2408.     tm.m00 = m.m00
  2409.     tm.m01 = m.m10
  2410.     tm.m10 = m.m01
  2411.     tm.m11 = m.m11
  2412.     o = tm
  2413.  
  2414. SUB matrixInvert (m AS tMATRIX2d, o AS tMATRIX2d)
  2415.     DIM a, b, c, d, det AS _FLOAT
  2416.     DIM tm AS tMATRIX2d
  2417.  
  2418.     a = m.m00: b = m.m01: c = m.m10: d = m.m11
  2419.     det = a * d - b * c
  2420.     IF det = 0 THEN EXIT SUB
  2421.  
  2422.     det = 1 / det
  2423.     tm.m00 = det * d: tm.m01 = -det * b
  2424.     tm.m10 = -det * c: tm.m11 = det * a
  2425.     o = tm
  2426.  
  2427. SUB matrixMultiplyVector (m AS tMATRIX2d, v AS tVECTOR2d, o AS tVECTOR2d)
  2428.     DIM t AS tVECTOR2d
  2429.     t.x = m.m00 * v.x + m.m01 * v.y
  2430.     t.y = m.m10 * v.x + m.m11 * v.y
  2431.     o = t
  2432.  
  2433. SUB matrixAddMatrix (m AS tMATRIX2d, x AS tMATRIX2d, o AS tMATRIX2d)
  2434.     o.m00 = m.m00 + x.m00
  2435.     o.m01 = m.m01 + x.m01
  2436.     o.m10 = m.m10 + x.m10
  2437.     o.m11 = m.m11 + x.m11
  2438.  
  2439. SUB matrixMultiplyMatrix (m AS tMATRIX2d, x AS tMATRIX2d, o AS tMATRIX2d)
  2440.     o.m00 = m.m00 * x.m00 + m.m01 * x.m10
  2441.     o.m01 = m.m00 * x.m01 + m.m01 * x.m11
  2442.     o.m10 = m.m10 * x.m00 + m.m11 * x.m10
  2443.     o.m11 = m.m10 * x.m01 + m.m11 * x.m11
  2444.  
  2445. '**********************************************************************************************
  2446. '   Mostly Unused Stuff Ahead
  2447. '**********************************************************************************************
  2448.  
  2449. SUB polygonMakeCCW (obj AS tTRIANGLE)
  2450.     IF vectorLeft(obj.a, obj.b, obj.c) = 0 THEN
  2451.         SWAP obj.a, obj.c
  2452.     END IF
  2453.  
  2454. FUNCTION polygonIsReflex (t AS tTRIANGLE)
  2455.     polygonIsReflex = vectorRight(t.a, t.b, t.c)
  2456.  
  2457. FUNCTION scalarMin (a AS _FLOAT, b AS _FLOAT)
  2458.     IF a < b THEN
  2459.         scalarMin = a
  2460.     ELSE
  2461.         scalarMin = b
  2462.     END IF
  2463.  
  2464. FUNCTION scalarMax (a AS _FLOAT, b AS _FLOAT)
  2465.     IF a > b THEN
  2466.         scalarMax = a
  2467.     ELSE
  2468.         scalarMax = b
  2469.     END IF
  2470.  
  2471. SUB lineIntersection (l1 AS tLINE2d, l2 AS tLINE2d, o AS tVECTOR2d)
  2472.     DIM a1, b1, c1, a2, b2, c2, det AS _FLOAT
  2473.     o.x = 0
  2474.     o.y = 0
  2475.     a1 = l1.b.y - l1.a.y
  2476.     b1 = l1.a.x - l1.b.x
  2477.     c1 = a1 * l1.a.x + b1 * l1.a.y
  2478.     a2 = l2.b.y - l2.a.y
  2479.     b2 = l2.a.x - l2.b.x
  2480.     c2 = a2 * l2.a.x + b2 * l2.a.y
  2481.     det = a1 * b2 - a2 * b1
  2482.  
  2483.     IF INT(det * cPRECISION) <> 0 THEN
  2484.         o.x = (b2 * c1 - b1 * c2) / det
  2485.         o.y = (a1 * c2 - a2 * c1) / det
  2486.     END IF
  2487.  
  2488. FUNCTION lineSegmentsIntersect (l1 AS tLINE2d, l2 AS tLINE2d)
  2489.     DIM dx, dy, da, db, s, t AS _FLOAT
  2490.     dx = l1.b.x - l1.a.x
  2491.     dy = l1.b.y - l1.a.y
  2492.     da = l2.b.x - l2.a.x
  2493.     db = l2.b.y - l2.a.y
  2494.     IF da * dy - db * dx = 0 THEN
  2495.         lineSegmentsIntersect = 0
  2496.     ELSE
  2497.         s = (dx * (l2.a.y - l1.a.y) + dy * (l1.a.x - l2.a.x)) / (da * dy - db * dx)
  2498.         t = (da * (l1.a.y - l2.a.y) + db * (l2.a.x - l1.a.x)) / (db * dx - da * dy)
  2499.         lineSegmentsIntersect = (s >= 0 AND s <= 1 AND t >= 0 AND t <= 1)
  2500.     END IF
  2501.  
  2502. '**********************************************************************************************
  2503. '   Impulse Specific Math Ahead
  2504. '**********************************************************************************************
  2505.  
  2506. FUNCTION impulseEqual (a AS _FLOAT, b AS _FLOAT)
  2507.     impulseEqual = ABS(a - b) <= cEPSILON
  2508.  
  2509. FUNCTION impulseClamp (min AS _FLOAT, max AS _FLOAT, a AS _FLOAT)
  2510.     IF a < min THEN
  2511.         impulseClamp = min
  2512.     ELSE IF a > max THEN
  2513.             impulseClamp = max
  2514.         ELSE
  2515.             impulseClamp = a
  2516.         END IF
  2517.     END IF
  2518.  
  2519. FUNCTION impulseRound (a AS _FLOAT)
  2520.     impulseRound = INT(a + 0.5)
  2521.  
  2522. FUNCTION impulseRandom_float (min AS _FLOAT, max AS _FLOAT)
  2523.     impulseRandom_float = ((max - min) * RND + min)
  2524.  
  2525. FUNCTION impulseRandomInteger (min AS INTEGER, max AS INTEGER)
  2526.     impulseRandomInteger = INT((max - min) * RND + min)
  2527.  
  2528. FUNCTION impulseGT (a AS _FLOAT, b AS _FLOAT)
  2529.     impulseGT = (a >= b * cBIAS_RELATIVE + a * cBIAS_ABSOLUTE)
  2530.  
  2531. '**********************************************************************************************
  2532. '   Troubleshooting Tools
  2533. '**********************************************************************************************
  2534.  
  2535. SUB printMatrix (u AS tMATRIX2d, n AS INTEGER)
  2536.     PRINT "---------------------------"
  2537.     PRINT n; " u:"; u.m00; "|"; u.m10
  2538.     PRINT "       "; u.m10; "|"; u.m11
  2539.     DO
  2540.     LOOP UNTIL _KEYHIT = 27
  2541.  
  2542. '**********************************************************************************************
  2543. '   String Hash
  2544. '**********************************************************************************************
  2545.  
  2546. FUNCTION computeHash&& (s AS STRING)
  2547.     DIM p, i AS INTEGER: p = 31
  2548.     DIM m AS _INTEGER64: m = 1E9 + 9
  2549.     DIM AS _INTEGER64 hash_value, p_pow
  2550.     p_pow = 1
  2551.     FOR i = 1 TO LEN(s)
  2552.         hash_value = (hash_value + (ASC(MID$(s, i)) - 97 + 1) * p_pow)
  2553.         p_pow = (p_pow * p) MOD m
  2554.     NEXT
  2555.     computeHash = hash_value
  2556. '**********************************************************************************************
  2557. '   Load Bitmap
  2558. '**********************************************************************************************
  2559. SUB loadBitmap (textureMap() AS LONG)
  2560.     DIM AS INTEGER index
  2561.     DIM AS STRING fl
  2562.     FOR index = 1 TO 16
  2563.         fl = _CWD$ + "\Assets\ball_" + LTRIM$(STR$(index)) + ".png"
  2564.         textureMap(index) = _LOADIMAGE(fl)
  2565.         IF textureMap(index) > -2 THEN
  2566.             PRINT "Unable to load image "; fl; " with return of "; textureMap(index)
  2567.             END
  2568.         END IF
  2569.     NEXT
  2570.     textureMap(index + 1) = _LOADIMAGE(_CWD$ + "\Assets\cue.png")
  2571.     textureMap(index + 2) = _LOADIMAGE(_CWD$ + "\Assets\table.png")
  2572.     textureMap(index + 3) = _LOADIMAGE(_CWD$ + "\Assets\triangle.png")
  2573.     textureMap(index + 4) = _LOADIMAGE(_CWD$ + "\Assets\ballreturn.png")
  2574.  
* QBPOOL.zip (Filesize: 1.08 MB, Downloads: 167)
screenshot.png
* screenshot.png (Filesize: 67.48 KB, Dimensions: 1019x806, Views: 134)

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #1 on: May 15, 2021, 02:26:48 pm »
Really nice!

Lots of handy subs and functions in there too!

You solved the problem of having enough room to stroke the cue all around the room, just move the table LOL!

Oh mouse wheel zooms, more nice!
« Last Edit: May 15, 2021, 02:34:10 pm by bplus »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #2 on: May 15, 2021, 05:12:10 pm »
Amazing work, if that's considered simple then I should just stop working on mine now. I'm particularly intrigued by the vast library of vector functions. A lot of stuff there that I hadn't thought of doing.

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #3 on: May 15, 2021, 05:22:51 pm »
Quote
if that's considered simple then I should just stop working on mine now

@OldMoses I apologize about calling it simple. I meant that it was, by far, not a complete implementation of a pool game. You should not quit on your project, in fact you are more than welcome to use anything you can make use of, if it will help you.


Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #4 on: May 15, 2021, 06:23:38 pm »
No need to apologize, that is a wonderfully executed program. In my own program, I didn't intend to go beyond the simple physics of the thing, but yours has a very realistic look to it. For mine, the closest I get to realism is to make use of a particular exploit of Steve's circle fill routine that made it easy to do striped and solid balls, but I never thought about getting them to rotate. They just sort of sled around. ;)

Now that I think of it Bplus' Rotozoom3 would probably handle ball rotations, but my first task is to tame the little buggers not to plow through each other and teleport around the table.

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #5 on: May 15, 2021, 06:33:47 pm »
Quote
Now that I think of it Bplus' Rotozoom3 would probably handle ball rotations
I used Rotozoom for the texture rotations.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #6 on: May 15, 2021, 08:50:42 pm »
Well for the record bplus Rotozoom3 gives you 2 independent scales for x and y not always needed.

@justsomeguy And I liked the .zip package, everything I need is right there for me to Run and compile as soon as extracted, thanks for that!

I am having a hard time pocketing any ball, I am wondering if you could project the path of cue and first ball hit with tracer lines when aiming the cue ball. I've seen this in an on-line Pool app. Next best thing would be to project cue to kiss point (if cue would roll that far). This might be asking too much but you did ask for feedback.

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #7 on: May 15, 2021, 09:16:34 pm »
@bplus Hold down the TAB key and see what happens.
« Last Edit: May 15, 2021, 09:17:35 pm by justsomeguy »

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #8 on: May 15, 2021, 10:06:52 pm »
This game is pretty cool... Well done!

Can I assume that the table cloth colour can be changed? Say... to blue... lol (just for you bplus... lol)
Logic is the beginning of wisdom.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #9 on: May 15, 2021, 10:29:45 pm »
@bplus Hold down the TAB key and see what happens.

Fabulous! 3 in a row! I needed that to see what is off with my aiming.

I tried the tab but missed the part about holding it down. Thanks this gets better and better!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #10 on: May 15, 2021, 10:32:25 pm »
This game is pretty cool... Well done!

Can I assume that the table cloth colour can be changed? Say... to blue... lol (just for you bplus... lol)

I have something that could change the color of an image but I bet that green has many shades. Colour ha! so many letters ;-))

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #11 on: May 15, 2021, 10:55:27 pm »
Quote
Can I assume that the table cloth colour can be changed? Say... to blue... lol (just for you bplus... lol)

Can do! Just insert this in 'SUB animateScene ()'

Code: QB64: [Select]
  1.     IF _KEYDOWN(ASC("B")) OR _KEYDOWN(ASC("b")) THEN
  2.         _SOURCE textureMap(19)
  3.         _DEST textureMap(19)
  4.         DIM AS INTEGER x, y, r, g, b
  5.         DIM AS LONG c
  6.         FOR x = 0 TO _WIDTH
  7.             FOR y = 0 TO _HEIGHT
  8.                 c = POINT(x, y): r = _RED(c): g = _GREEN(c): b = _BLUE(c)
  9.                 IF g > 96 THEN
  10.                     PSET (x, y), _RGB32(r, b, g)
  11.                 END IF
  12.             NEXT
  13.         NEXT
  14.         _SOURCE 0
  15.         _DEST 0
  16.     END IF

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #12 on: May 16, 2021, 02:30:34 am »
Well you have to Shared Texture and drop "Map" ending plus if you prefer red...

Code: QB64: [Select]
  1.     If _KeyDown(Asc("B")) Or _KeyDown(Asc("b")) Then
  2.         _Source texture(19)
  3.         _Dest texture(19)
  4.         Dim As Integer x, y, r, g, b
  5.         Dim As Long c
  6.         For x = 0 To _Width
  7.             For y = 0 To _Height
  8.                 c = Point(x, y): r = _Red(c): g = _Green(c): b = _Blue(c)
  9.                 If g > 96 Then
  10.                     PSet (x, y), _RGB32(g, r, b)
  11.                 End If
  12.             Next
  13.         Next
  14.         _Source 0
  15.         _Dest 0
  16.     End If
  17.  

b is fine for key though

 
Red table.PNG


Except it screws up Tab scene.

PS that code is masterfully put together @justsomeguy
« Last Edit: May 16, 2021, 02:42:35 am by bplus »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #13 on: May 16, 2021, 01:39:10 pm »
We need to fine tune the mouse setting on the cue. It might be OK to quickly narrow down to +- 10 degrees but I am changing the pixels by 1 and getting 10 degrees too much then 10 degrees too short, and moving mouse a pixel is for watch makers and jewelers.

So move mouse for gross angle and power then key in for way more accurate angle and power is my next suggestion.

PS always shoot with spacebar, in case you accidently click mouse.

PPS the left right English works to tune shot but really English is best used to set cue ball for next shot, in that reguard, I sure do miss forward and reverse.

« Last Edit: May 16, 2021, 01:44:26 pm by bplus »

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: Simple Pool Game in QBasic
« Reply #14 on: May 16, 2021, 02:35:46 pm »
@bplus You are absolutely correct about about the mouse control being too difficult get that precise shot. I end up zooming out to get a little more fine control on the stick, but end up hitting way harder than intend. However, I was playing it on my Microsoft Surface and playing with a touchscreen is awesome, I might give the users a choice on how to aim.

The space bar does shoot, but it is about twice as hard. Per your suggestion this will probably change.

The left and right spin 'English' is simulated by putting angular velocity on the cue ball like you do in real pool. Because the engine is 2D, I'm not sure the best way to simulate angular velocity in a dimension it does not have (top and bottom). I have had a little success with playing with restitution of the cue ball, but it can only approximate top spin and not draw. Over all not a good solution. I will have to ponder this one.

Right now I'm going to see if using InForm would be a good choice for a GUI and, that would give access to real time tweaking of parameters and some options for varying the play style.

I had planned on circling around back to the aiming issue, so expect an up date in the near future.