'\\ Unseen Game Deveopment Kit (GDK) - Version 1.8 - DEVELOPMENT VERSION - NOT FINISHED!!!
'// Updated 30/7/2019
'// Created By John Onyon a.k.a Unseen Machine
'// Optimistation by CodeGuy and DarthWho
'// ScanKeys% function by Cypherium
'// Image rotation function by Galleon
'// Rotated Rectangle Collision Detection By Richard Notley

'##########################################################################################################################
'// Typdef's
'##########################################################################################################################

SUB GDK_Types

  TYPE Sprite
    File AS LONG
    Width AS INTEGER
    Height AS INTEGER
    Alpha AS _UNSIGNED LONG
    IsVisible AS _BYTE
    XFrameCount AS _BYTE
    YFrameCount AS _BYTE
    TotalFrameCount AS INTEGER
    RotationX AS SINGLE
    RotationY AS SINGLE
    Scale AS SINGLE
    BitMask AS _MEM '// For pixel perfect collisions
  END TYPE

  TYPE Vector
    X AS SINGLE
    Y AS SINGLE
    Speed AS SINGLE
    Rotation AS DOUBLE
    Accel AS SINGLE
    Decel AS SINGLE
  END TYPE

  TYPE BoundingCircle
    X AS INTEGER
    Y AS INTEGER
    Radius AS INTEGER
  END TYPE

  TYPE MouseState
    X AS INTEGER
    Y AS INTEGER
    LB AS _BYTE
    RB AS _BYTE
    MB AS _BYTE
    MW AS _BYTE
  END TYPE

  TYPE KeyBoardState
    Left AS LONG
    Right AS LONG
    Down AS LONG
    Up AS LONG
    CTRL AS LONG
    SHIFT AS LONG
    ALT AS LONG
    SPACE AS LONG
    ENTER AS LONG
    ESC AS LONG
    Num1 AS LONG
    Num2 AS LONG
    Num3 AS LONG
    Num4 AS LONG
    Num5 AS LONG
    Num6 AS LONG
    Num7 AS LONG
    Num8 AS LONG
    Num9 AS LONG
    Num0 AS LONG
    PLUS AS LONG
    MINUS AS LONG
    BACKSPACE AS LONG
    TAB AS LONG
    A AS LONG
    B AS LONG
    C AS LONG
    D AS LONG
    E AS LONG
    F AS LONG
    G AS LONG
    H AS LONG
    I AS LONG
    J AS LONG
    K AS LONG
    L AS LONG
    M AS LONG
    N AS LONG
    O AS LONG
    P AS LONG
    Q AS LONG
    R AS LONG
    S AS LONG
    T AS LONG
    U AS LONG
    V AS LONG
    W AS LONG
    X AS LONG
    Y AS LONG
    Z AS LONG
  END TYPE


  TYPE ANIMATION
    Frame AS INTEGER
    StartFrame AS INTEGER
    ResetFrame AS INTEGER
    Timer AS DOUBLE
    Time AS DOUBLE
    Dir AS _BYTE
  END TYPE

  TYPE Emitter
    X AS INTEGER
    Y AS INTEGER
    SpawnRate AS INTEGER
    SpawnAngleMin AS SINGLE
    SpawnAngleMax AS SINGLE
    SpawnMaxVelocity AS SINGLE
    SpawnMinVelocity AS SINGLE
    MaxLifeTime AS DOUBLE
    MinLifeTime AS DOUBLE
    AccelMax AS SINGLE
    DecelMax AS SINGLE
    AccelMin AS SINGLE
    DecelMIn AS SINGLE
    Gravity AS SINGLE
  END TYPE

  TYPE Particle
    InitialCLR AS _UNSIGNED LONG
    CurrentCLR AS _UNSIGNED LONG
    ClrChange AS _UNSIGNED LONG
    ClrChangeMode AS INTEGER
    Vector AS Vector
    BirthTime AS DOUBLE
    LifeTime AS DOUBLE
    Exists AS _BYTE
    Accel AS SINGLE
    Decel AS SINGLE
    Gravity AS SINGLE
  END TYPE

  TYPE Tileset
    Sheet AS Sprite
    THeight AS INTEGER
    TWidth AS INTEGER
  END TYPE

  TYPE XY
    X AS INTEGER
    Y AS INTEGER
  END TYPE

  TYPE Rectangle
    XY AS XY
    WH AS XY
    Rotation AS SINGLE
    RotationPoint AS XY
    C1 AS XY
    C2 AS XY
    C3 AS XY
    C4 AS XY
  END TYPE

  TYPE GameObject
    Sprite AS Sprite
    Vector AS Vector
    Rect AS Rectangle
  END TYPE

  TYPE Tile
    RECT AS Rectangle
    Tile AS INTEGER
  END TYPE

  TYPE Path
    Vector AS Vector
    LastVector AS Vector
    TargetVector AS Vector
    NumPoints AS INTEGER
    CurrentPoint AS INTEGER
    HitTargetX AS INTEGER
    HitTargetY AS INTEGER
    Repeat AS INTEGER
    Relative AS INTEGER
    Reverse AS INTEGER
    IsReversing AS INTEGER
  END TYPE

  TYPE PathPoint
    X AS INTEGER
    Y AS INTEGER
    Speed AS INTEGER
  END TYPE


END SUB

'##########################################################################################################################
'// Collision detection
'##########################################################################################################################

SUB GDK_Rectangle_New (RectRef AS Rectangle, x%, y%, Width%, Height%)
  RectRef.XY.X = x%
  RectRef.XY.Y = y%
  RectRef.WH.X = Width%
  RectRef.WH.Y = Height%
END SUB

SUB GDK_Rectangle_Autosize (Sprite AS Sprite, Vector AS Vector, Rect AS Rectangle)
  Rect.XY.X = Vector.X - (Sprite.RotationX * Sprite.Scale)
  Rect.XY.Y = Vector.Y - (Sprite.RotationY * Sprite.Scale)
  Rect.WH.X = (Sprite.Width / Sprite.XFrameCount) * Sprite.Scale
  Rect.WH.Y = (Sprite.Height / Sprite.YFrameCount) * Sprite.Scale
  Rect.Rotation = Vector.Rotation
  Rect.RotationPoint.X = Sprite.RotationX * Sprite.Scale
  Rect.RotationPoint.Y = Sprite.RotationY * Sprite.Scale
  DIM pX(3) AS SINGLE, pY(3) AS SINGLE, W AS LONG, H AS LONG, I AS LONG, X AS LONG, Y AS LONG, X2 AS LONG, Y2 AS LONG, PointX AS LONG, PointY AS LONG
  X = Vector.X
  Y = Vector.Y
  W = (Sprite.Width / Sprite.XFrameCount)
  H = (Sprite.Height / Sprite.YFrameCount)
  PointX = Sprite.RotationX
  PointY = Sprite.RotationY
  pX(0) = -PointX
  pY(0) = -PointY
  pX(1) = -PointX
  pY(1) = -PointY + H - 1
  pX(2) = -PointX + W - 1
  pY(2) = -PointY + H - 1
  pX(3) = -PointX + W - 1
  pY(3) = -PointY 'Set dest screen points
  SINr = SIN(Vector.Rotation)
  COSr = COS(Vector.Rotation) 'Precalculate SIN & COS of angle
  FOR I = 0 TO 3
    pX(I) = pX(I) * Sprite.Scale
    pY(I) = pY(I) * Sprite.Scale 'Scale
    X2 = pX(I) * COSr + SINr * pY(I)
    pY(I) = pY(I) * COSr - pX(I) * SINr
    pX(I) = X2 'Rotate Dest Points
    pX(I) = pX(I) + X
    pY(I) = pY(I) + Y 'Translate Dest Points
    SELECT CASE I
      CASE 0
        Rect.C1.X = pX(I)
        Rect.C1.Y = pY(I)
      CASE 1
        Rect.C2.X = pX(I)
        Rect.C2.Y = pY(I)
      CASE 2
        Rect.C3.X = pX(I)
        Rect.C3.Y = pY(I)
      CASE 3
        Rect.C4.X = pX(I)
        Rect.C4.Y = pY(I)
    END SELECT
  NEXT
END SUB


FUNCTION GDK_Rectangle_Intersect (Rect1 AS Rectangle, Rect2 AS Rectangle)
  'IF Rect1.Rotation = 0 AND Rect2.Rotation = 0 THEN '// No rotation do standard collision check'''
  '
  '    IF Rect1.XY.X <= Rect2.XY.X + Rect2.WH.X THEN
  '      IF Rect1.XY.X + Rect1.WH.X >= Rect2.XY.X THEN
  '        IF Rect1.XY.Y <= Rect2.XY.Y + Rect2.WH.Y THEN
  '          IF Rect1.XY.Y + Rect1.WH.Y >= Rect2.XY.Y THEN
  '            GDK_Rectangle_Intersect = -1
  '            EXIT FUNCTION
  '          END IF
  '        END IF
  '      END IF
  '    END IF'

  '  ELSE '// Rotated rectangles

  '// Adapted from code created by Richard Notley
  '// http://www.qb64.net/forum/index.php?topic=12564.0
  REDIM sngCorners1!(3, 1), sngCorners2!(3, 1), sngCorners2A!(3, 1), sngCentre!(1, 1)

  '// Put rect data into arrays
  sngCorners1!(0, 0) = Rect1.C1.X
  sngCorners1!(0, 1) = Rect1.C1.Y
  sngCorners1!(1, 0) = Rect1.C2.X
  sngCorners1!(1, 1) = Rect1.C2.Y
  sngCorners1!(2, 0) = Rect1.C3.X
  sngCorners1!(2, 1) = Rect1.C3.Y
  sngCorners1!(3, 0) = Rect1.C4.X
  sngCorners1!(3, 1) = Rect1.C4.Y

  sngCorners2!(0, 0) = Rect2.C1.X
  sngCorners2!(0, 1) = Rect2.C1.Y
  sngCorners2!(1, 0) = Rect2.C2.X
  sngCorners2!(1, 1) = Rect2.C2.Y
  sngCorners2!(2, 0) = Rect2.C3.X
  sngCorners2!(2, 1) = Rect2.C3.Y
  sngCorners2!(3, 0) = Rect2.C4.X
  sngCorners2!(3, 1) = Rect2.C4.Y

  'Extra copy of sngCorners2! used for second poke-through condition check
  FOR bytN%% = 0 TO 3
    FOR bytM%% = 0 TO 1
      sngCorners2A!(bytN%%, bytM%%) = sngCorners2!(bytN%%, bytM%%)
    NEXT bytM%%
  NEXT bytN%%

  'Find Centres of Rectangles
  sngCentre!(0, 0) = (sngCorners1!(0, 0) + sngCorners1!(1, 0) + sngCorners1!(2, 0) + sngCorners1!(3, 0)) / 4
  sngCentre!(0, 1) = (sngCorners1!(0, 1) + sngCorners1!(1, 1) + sngCorners1!(2, 1) + sngCorners1!(3, 1)) / 4
  sngCentre!(1, 0) = (sngCorners2!(0, 0) + sngCorners2!(1, 0) + sngCorners2!(2, 0) + sngCorners2!(3, 0)) / 4
  sngCentre!(1, 1) = (sngCorners2!(0, 1) + sngCorners2!(1, 1) + sngCorners2!(2, 1) + sngCorners2!(3, 1)) / 4

  'Find lengths of rectangle diagonals
  sngDiag1! = (SQR((sngCorners1!(2, 0) - sngCorners1!(0, 0)) * (sngCorners1!(2, 0) - sngCorners1!(0, 0)) + (sngCorners1!(2, 1) - sngCorners1!(0, 1)) * (sngCorners1!(2, 1) - sngCorners1!(0, 1)))) / 2
  sngDiag2! = (SQR((sngCorners2!(2, 0) - sngCorners2!(0, 0)) * (sngCorners2!(2, 0) - sngCorners2!(0, 0)) + (sngCorners2!(2, 1) - sngCorners2!(0, 1)) * (sngCorners2!(2, 1) - sngCorners2!(0, 1)))) / 2
  sngSepn! = SQR((sngCentre!(1, 0) - sngCentre!(0, 0)) * (sngCentre!(1, 0) - sngCentre!(0, 0)) + (sngCentre!(1, 1) - sngCentre!(0, 1)) * (sngCentre!(1, 1) - sngCentre!(0, 1)))

  IF sngSepn! <= sngDiag1! + sngDiag2! THEN
    'Only need to do collision check if rectangles are closer than sum of half-diagonals

    'Check for intersect of diagonals
    IF HF_CrossOver%%(sngCorners1!(0, 1), sngCorners1!(0, 0), sngCorners1!(2, 1), sngCorners1!(2, 0), sngCorners2!(0, 1), sngCorners2!(0, 0), sngCorners2!(2, 1), sngCorners2!(2, 0)) = -1 THEN
      GDK_Rectangle_Intersect = -1
      EXIT FUNCTION
    ELSEIF HF_CrossOver%%(sngCorners1!(0, 1), sngCorners1!(0, 0), sngCorners1!(2, 1), sngCorners1!(2, 0), sngCorners2!(1, 1), sngCorners2!(1, 0), sngCorners2!(3, 1), sngCorners2!(3, 0)) = -1 THEN
      GDK_Rectangle_Intersect = -1
      EXIT FUNCTION
    ELSEIF HF_CrossOver%%(sngCorners1!(1, 1), sngCorners1!(1, 0), sngCorners1!(3, 1), sngCorners1!(3, 0), sngCorners2!(0, 1), sngCorners2!(0, 0), sngCorners2!(2, 1), sngCorners2!(2, 0)) = -1 THEN
      GDK_Rectangle_Intersect = -1
      EXIT FUNCTION
    ELSEIF HF_CrossOver%%(sngCorners1!(1, 1), sngCorners1!(1, 0), sngCorners1!(3, 1), sngCorners1!(3, 0), sngCorners2!(1, 1), sngCorners2!(1, 0), sngCorners2!(3, 1), sngCorners2!(3, 0)) = -1 THEN
      GDK_Rectangle_Intersect = -1
      EXIT FUNCTION
    ELSE
      'Check for poke-throughs

      'adjust sngCorners2! relative to sngArr1! centre

      FOR bytN%% = 0 TO 3
        FOR bytM%% = 0 TO 1
          sngCorners2!(bytN%%, bytM%%) = sngCorners2!(bytN%%, bytM%%) - sngCentre!(0, bytM%%)
        NEXT bytM%%
      NEXT bytN%%

      'rotate to sngArr1!

      'find Angle of sngCorners1!
      sngAngRot! = HF_Angle!(sngCorners1!(3, 1) - sngCorners1!(0, 1), sngCorners1!(3, 0) - sngCorners1!(0, 0))

      FOR bytN%% = 0 TO 3
        sngR! = SQR(sngCorners2!(bytN%%, 0) * sngCorners2!(bytN%%, 0) + sngCorners2!(bytN%%, 1) * sngCorners2!(bytN%%, 1)): sngPhi! = HF_Angle!(sngCorners2!(bytN%%, 1), sngCorners2!(bytN%%, 0))
        sngCorners2!(bytN%%, 1) = sngR! * COS(sngPhi! - sngAngRot!)
        sngCorners2!(bytN%%, 0) = sngR! * SIN(sngPhi! - sngAngRot!)
      NEXT bytN%%

      'Find height and width of sngCorners1!
      sngRWidth! = SQR((sngCorners1!(0, 0) - sngCorners1!(1, 0)) * (sngCorners1!(0, 0) - sngCorners1!(1, 0)) + (sngCorners1!(0, 1) - sngCorners1!(1, 1)) * (sngCorners1!(0, 1) - sngCorners1!(1, 1)))
      sngRHeight! = SQR((sngCorners1!(0, 0) - sngCorners1!(3, 0)) * (sngCorners1!(0, 0) - sngCorners1!(3, 0)) + (sngCorners1!(0, 1) - sngCorners1!(3, 1)) * (sngCorners1!(0, 1) - sngCorners1!(3, 1)))

      FOR bytN%% = 0 TO 3
        IF sngCorners2!(bytN%%, 1) <= sngRHeight! / 2 THEN
          IF sngCorners2!(bytN%%, 1) >= -sngRHeight! / 2 THEN
            IF sngCorners2!(bytN%%, 0) <= sngRWidth! / 2 THEN
              IF sngCorners2!(bytN%%, 0) >= -sngRWidth! / 2 THEN
                GDK_Rectangle_Intersect = -1
                EXIT FUNCTION
              END IF
            END IF
          END IF
        END IF
      NEXT

      'adjust sngCorners1! relative to sngArr2! centre

      FOR bytN%% = 0 TO 3
        FOR bytM%% = 0 TO 1
          sngCorners1!(bytN%%, bytM%%) = sngCorners1!(bytN%%, bytM%%) - sngCentre!(1, bytM%%)
        NEXT bytM%%
      NEXT bytN%%

      'rotate to sngArr2!

      'find Angle of sngCorners2A!
      sngAngRot! = HF_Angle!(sngCorners2A!(3, 1) - sngCorners2A!(0, 1), sngCorners2A!(3, 0) - sngCorners2A!(0, 0))

      FOR bytN%% = 0 TO 3
        sngR! = SQR(sngCorners1!(bytN%%, 0) * sngCorners1!(bytN%%, 0) + sngCorners1!(bytN%%, 1) * sngCorners1!(bytN%%, 1)): sngPhi! = HF_Angle!(sngCorners1!(bytN%%, 1), sngCorners1!(bytN%%, 0))
        sngCorners1!(bytN%%, 1) = sngR! * COS(sngPhi! - sngAngRot!)
        sngCorners1!(bytN%%, 0) = sngR! * SIN(sngPhi! - sngAngRot!)
      NEXT bytN%%

      'Find height and width of sngCorners2!
      sngRWidth! = SQR((sngCorners2A!(0, 0) - sngCorners2A!(1, 0)) * (sngCorners2A!(0, 0) - sngCorners2A!(1, 0)) + (sngCorners2A!(0, 1) - sngCorners2A!(1, 1)) * (sngCorners2A!(0, 1) - sngCorners2A!(1, 1)))
      sngRHeight! = SQR((sngCorners2A!(0, 0) - sngCorners2A!(3, 0)) * (sngCorners2A!(0, 0) - sngCorners2A!(3, 0)) + (sngCorners2A!(0, 1) - sngCorners2A!(3, 1)) * (sngCorners2A!(0, 1) - sngCorners2A!(3, 1)))

      FOR bytN%% = 0 TO 3
        IF sngCorners1!(bytN%%, 1) <= sngRHeight! / 2 THEN
          IF sngCorners1!(bytN%%, 1) >= -sngRHeight! / 2 THEN
            IF sngCorners1!(bytN%%, 0) <= sngRWidth! / 2 THEN
              IF sngCorners1!(bytN%%, 0) >= -sngRWidth! / 2 THEN
                GDK_Rectangle_Intersect = -1
                EXIT FUNCTION
              END IF
            END IF
          END IF
        END IF
      NEXT

    END IF
  END IF
  '  END IF
END FUNCTION

FUNCTION HF_Angle! (X!, Y!) '// Helper function for collisions
  IF X! = 0 THEN
    IF Y! < 0 THEN
      HF_Angle! = -_PI / 2
    ELSE
      HF_Angle! = _PI / 2
    END IF
  ELSE
    HF_Angle! = ATN(Y! / X!)
  END IF
  IF X! < 0 THEN
    HF_Angle! = HF_Angle! - _PI
    IF HF_Angle! < -_PI THEN HF_Angle! = HF_Angle! + 2 * _PI
  END IF
END FUNCTION

FUNCTION HF_CrossOver%% (sngXT1!, sngYT1!, sngXS1!, sngYS1!, sngXT2!, sngYT2!, sngXS2!, sngYS2!) '// Helper function
  sngM1! = (sngYT1! - sngYS1!) / (sngXT1! - sngXS1!)
  sngC1! = ((sngXT1! * sngYS1!) - (sngXS1! * sngYT1!)) / (sngXT1! - sngXS1!)
  sngM2! = (sngYT2! - sngYS2!) / (sngXT2! - sngXS2!)
  sngC2! = ((sngXT2! * sngYS2!) - (sngXS2! * sngYT2!)) / (sngXT2! - sngXS2!)
  sngXCross! = (sngC1! - sngC2!) / (sngM2! - sngM1!) '!!! <- Possible Divide by zero
  sngYCross! = (sngM1! * sngXCross!) + sngC1!
  sngX1H! = sngXT1!: sngY1H! = sngYT1!
  sngX1L! = sngXS1!: sngY1L! = sngYS1!
  sngX2H! = sngXT2!: sngY2H! = sngYT2!
  sngX2L! = sngXS2!: sngY2L! = sngYS2!
  IF sngX1L! > sngX1H! THEN SWAP sngX1L!, sngX1H!
  IF sngY1L! > sngY1H! THEN SWAP sngY1L!, sngY1H!
  IF sngX2L! > sngX2H! THEN SWAP sngX2L!, sngX2H!
  IF sngY2L! > sngY2H! THEN SWAP sngY2L!, sngY2H!
  IF sngXCross! >= sngX1L! THEN
    IF sngXCross! <= sngX1H! THEN
      IF sngYCross! >= sngY1L! THEN
        IF sngYCross! <= sngY1H! THEN
          IF sngXCross! >= sngX2L! THEN
            IF sngXCross! <= sngX2H! THEN
              IF sngYCross! >= sngY2L! THEN
                IF sngYCross! <= sngY2H! THEN
                  HF_CrossOver%% = -1
                END IF
              END IF
            END IF
          END IF
        END IF
      END IF
    END IF
  END IF
END FUNCTION



FUNCTION GDK_BoundingCircle_Intersect (Circ1 AS BoundingCircle, Circ2 AS BoundingCircle)
  LRAD% = Circ1.Radius + Circ2.Radius
  CDist! = GDK_Distance(Circ1.X, Circ1.Y, Circ2.X, Circ2.Y)
  IF CDist! > LRAD% THEN
    GDK_BoundingCircle_Intersect = 0
  ELSE
    GDK_BoundingCircle_Intersect = -1
  END IF
END FUNCTION

SUB GDK_BoundingCircle_New (Circ1 AS BoundingCircle, X%, Y%, Radius%)
  Circ1.X = X%
  Circ1.Y = Y%
  Circ1.Radius = Radius%
END SUB


'##########################################################################################################################
'//Vector stuff
'##########################################################################################################################

SUB GDK_Vector_New (Vector AS Vector, X%, Y%, Speed!, Rotation#)
  Vector.X = X%
  Vector.Y = Y%
  Vector.Speed = Speed!
  Vector.Rotation = Rotation#
END SUB

SUB GDK_Vector_SetPosition (Vector AS Vector, X%, Y%)
  Vector.X = X%
  Vector.Y = Y%
END SUB

SUB GDK_Vector_SetRotation (Vector AS Vector, Rotation#)
  Vector.Rotation = Rotation#
END SUB

SUB GDK_Vector_SetSpeed (Vector AS Vector, Speed!)
  Vector.Speed = Speed!
END SUB

SUB GDK_Vector_SetAccelDecel (Vector AS Vector, Accel!, Decel!)
  Vector.Accel = Accel!
  Vector.Decel = Decel!
END SUB

SUB GDK_Vector_Update (Vector AS Vector)
  IF Vector.Decel > 0 THEN
    IF Vector.Accel > 0 THEN
      Vector.Accel = Vector.Accel - Vector.Decel
    ELSE
      IF Vector.Speed > (Vector.Decel) THEN Vector.Speed = Vector.Speed - Vector.Decel ELSE Vector.Speed = 0
    END IF
  END IF
  IF Vector.Accel > 0 THEN Vector.Speed = Vector.Speed + Vector.Accel
  GDK_Vector_Move Vector
END SUB

SUB GDK_Vector_Move (Vector AS Vector)
  Vector.X = Vector.X - SIN(Vector.Rotation) * Vector.Speed
  Vector.Y = Vector.Y - COS(Vector.Rotation) * Vector.Speed
END SUB

'##########################################################################################################################
'// Sprite stuff
'##########################################################################################################################

SUB GDK_Sprite_New (Sprite AS Sprite, FileSource$, XFrameCount%, YFrameCount%, TotalFrameCount%, Alpha&)
  Sprite.File = _LOADIMAGE(FileSource$, 32)
  Sprite.Width = _WIDTH(Sprite.File)
  Sprite.Height = _HEIGHT(Sprite.File)
  Sprite.XFrameCount = XFrameCount%
  Sprite.YFrameCount = YFrameCount%
  Sprite.TotalFrameCount = TotalFrameCount%
  Sprite.Scale = 1
  Sprite.IsVisible = -1
  Sprite.Alpha = Alpha&
END SUB

SUB GDK_Sprite_SetRotationPoint (Sprite AS Sprite, X!, Y!)
  Sprite.RotationX = X!
  Sprite.RotationY = Y!
END SUB

SUB GDK_Sprite_SetVisibility (Sprite AS Sprite, OnOff AS _BYTE)
  Sprite.IsVisible = OnOff
END SUB

SUB GDK_Sprite_SetAlpha (Sprite AS Sprite, Alpha&)
  Sprite.Alpha = Alpha&
END SUB

SUB GDK_Sprite_DrawXY (Sprite AS Sprite, X%, Y%, Rotation!, AnimationFrame%)
  DIM tempvect AS Vector
  GDK_Vector_New tempvect, X%, Y%, 0, Rotation!
  GDK_Sprite_Draw Sprite, tempvect, AnimationFrame%
END SUB

SUB GDK_Sprite_Hide (Handle AS Sprite)
  Handle.IsVisible = 0
END SUB

SUB GDK_Sprite_Show (Handle AS Sprite)
  Handle.IsVisible = -1
END SUB

SUB GDK_Sprite_Free (Sprite AS Sprite)
  _FREEIMAGE (Sprite.File)
  Sprite.IsVisible = 0
END SUB

SUB GDK_Sprite_Clone (SourceSprite AS Sprite, DestSprite AS Sprite)
  DestSprite = SourceSprite
END SUB

SUB GDK_Sprite_Clone_X (SourceSprite AS Sprite, DestSprite() AS Sprite, FirstClone%, NumberOfClones%)
  FOR CloneCount% = FirstClone% TO FirstClone% + NumberOfClones% - 1
    DestSprite(CloneCount%) = SourceSprite
  NEXT
END SUB

SUB GDK_Sprite_SetRotationAsCenter (Sprite AS Sprite)
  GDK_Sprite_SetRotationPoint Sprite, Sprite.Width / (Sprite.XFrameCount * 2), Sprite.Height / (Sprite.YFrameCount * 2)
END SUB

'// UPDATED VERSION!!
SUB GDK_Sprite_Draw (Sprite AS Sprite, DestVector AS Vector, Frame%)
  IF Frame% = 0 THEN Frame% = 1
  _CLEARCOLOR Sprite.Alpha, Sprite.File
  IF Sprite.IsVisible THEN
    FrameXSize% = Sprite.Width / Sprite.XFrameCount
    FrameYSize% = Sprite.Height / Sprite.YFrameCount
    FrameXStart% = ((INT((Frame% - 1) MOD Sprite.XFrameCount)) * FrameXSize%)
    FrameYStart% = ((INT((Frame% - 1) / Sprite.XFrameCount)) * FrameYSize%)
    TmpImage& = _NEWIMAGE(FrameXSize%, FrameYSize%, 32)
    _PUTIMAGE (0, 0), Sprite.File, TmpImage&, (FrameXStart%, FrameYStart%)-(FrameXStart% + FrameXSize%, FrameYStart% + FrameYSize%)
    Rotate_Scale_AroundPoint DestVector.X, DestVector.Y, TmpImage&, -GDK_RadianToDegree(DestVector.Rotation), Sprite.Scale, Sprite.Scale, Sprite.RotationX, Sprite.RotationY
    _FREEIMAGE TmpImage&
  END IF
END SUB


'##########################################################################################################################
'// SCREEN Stuff!
'##########################################################################################################################

SUB GDK_Screen_SetRes (ScreenHandle&, width%, height%)
  ScreenHandle& = _NEWIMAGE(width%, height%, 32)
  SCREEN ScreenHandle&
END SUB


'##########################################################################################################################
'// User input subs and functions.
'##########################################################################################################################

SUB GDK_Keyboard_GetState (KeyboardRef AS KeyBoardState)
  FOR x% = 1 TO 88 'read each key scancode from array
    IF x% = 1 THEN KeyboardRef.ESC = ScanKey%(x%)
    IF x% = 2 THEN KeyboardRef.Num1 = ScanKey%(x%)
    IF x% = 3 THEN KeyboardRef.Num2 = ScanKey%(x%)
    IF x% = 4 THEN KeyboardRef.Num3 = ScanKey%(x%)
    IF x% = 5 THEN KeyboardRef.Num4 = ScanKey%(x%)
    IF x% = 6 THEN KeyboardRef.Num5 = ScanKey%(x%)
    IF x% = 7 THEN KeyboardRef.Num6 = ScanKey%(x%)
    IF x% = 8 THEN KeyboardRef.Num7 = ScanKey%(x%)
    IF x% = 9 THEN KeyboardRef.Num8 = ScanKey%(x%)
    IF x% = 10 THEN KeyboardRef.Num9 = ScanKey%(x%)
    IF x% = 11 THEN KeyboardRef.Num0 = ScanKey%(x%)
    IF x% = 12 THEN KeyboardRef.MINUS = ScanKey%(x%)
    IF x% = 13 THEN KeyboardRef.PLUS = ScanKey%(x%)
    IF x% = 14 THEN KeyboardRef.BACKSPACE = ScanKey%(x%)
    IF x% = 15 THEN KeyboardRef.TAB = ScanKey%(x%)
    IF x% = 30 THEN KeyboardRef.A = ScanKey%(x%)
    IF x% = 48 THEN KeyboardRef.B = ScanKey%(x%)
    IF x% = 46 THEN KeyboardRef.C = ScanKey%(x%)
    IF x% = 32 THEN KeyboardRef.D = ScanKey%(x%)
    IF x% = 18 THEN KeyboardRef.E = ScanKey%(x%)
    IF x% = 33 THEN KeyboardRef.F = ScanKey%(x%)
    IF x% = 34 THEN KeyboardRef.G = ScanKey%(x%)
    IF x% = 35 THEN KeyboardRef.H = ScanKey%(x%)
    IF x% = 23 THEN KeyboardRef.I = ScanKey%(x%)
    IF x% = 36 THEN KeyboardRef.J = ScanKey%(x%)
    IF x% = 37 THEN KeyboardRef.K = ScanKey%(x%)
    IF x% = 38 THEN KeyboardRef.L = ScanKey%(x%)
    IF x% = 50 THEN KeyboardRef.M = ScanKey%(x%)
    IF x% = 49 THEN KeyboardRef.N = ScanKey%(x%)
    IF x% = 24 THEN KeyboardRef.O = ScanKey%(x%)
    IF x% = 25 THEN KeyboardRef.P = ScanKey%(x%)
    IF x% = 16 THEN KeyboardRef.Q = ScanKey%(x%)
    IF x% = 19 THEN KeyboardRef.R = ScanKey%(x%)
    IF x% = 31 THEN KeyboardRef.S = ScanKey%(x%)
    IF x% = 20 THEN KeyboardRef.T = ScanKey%(x%)
    IF x% = 22 THEN KeyboardRef.U = ScanKey%(x%)
    IF x% = 47 THEN KeyboardRef.V = ScanKey%(x%)
    IF x% = 17 THEN KeyboardRef.W = ScanKey%(x%)
    IF x% = 45 THEN KeyboardRef.X = ScanKey%(x%)
    IF x% = 21 THEN KeyboardRef.Y = ScanKey%(x%)
    IF x% = 44 THEN KeyboardRef.Z = ScanKey%(x%)
    IF x% = 72 THEN KeyboardRef.Up = ScanKey%(x%)
    IF x% = 75 THEN KeyboardRef.Left = ScanKey%(x%)
    IF x% = 77 THEN KeyboardRef.Right = ScanKey%(x%)
    IF x% = 80 THEN KeyboardRef.Down = ScanKey%(x%)
    IF x% = 28 THEN KeyboardRef.ENTER = ScanKey%(x%)
    IF x% = 57 THEN KeyboardRef.SPACE = ScanKey%(x%)
    IF x% = 29 THEN KeyboardRef.CTRL = ScanKey%(x%)
    IF x% = 56 THEN KeyboardRef.ALT = ScanKey%(x%)
    IF x% = 42 THEN KeyboardRef.SHIFT = ScanKey%(x%)
  NEXT
END SUB

FUNCTION ScanKey% (scancode%)
  STATIC Ready%, keyflags%() 'retain values on procedure exit
  IF NOT Ready% THEN REDIM keyflags%(0 TO 127): Ready% = -1 'create array on first use
  i% = INP(&H60) 'read keyboard states
  IF (i% AND 128) THEN keyflags%(i% XOR 128) = 0
  IF (i% AND 128) = 0 THEN keyflags%(i%) = -1
  K$ = INKEY$ 'clears key buffer to prevent beeps
  ScanKey% = keyflags%(scancode%)
  IF scancode% = 0 THEN Ready% = 0 'allows program to reset all values to 0 with a REDIM
END FUNCTION

SUB GDK_Mouse_GetState (MouseRef AS MouseState)
  DO: LOOP WHILE _MOUSEINPUT
  MouseRef.X = _MOUSEX
  MouseRef.Y = _MOUSEY
  MouseRef.LB = _MOUSEBUTTON(1)
  MouseRef.RB = _MOUSEBUTTON(2)
  MouseRef.MB = _MOUSEBUTTON(3)
  MouseRef.MW = _MOUSEWHEEL
END SUB
'##########################################################################################################################
'//Particle engine v.03
'##########################################################################################################################

SUB GDK_Emitter_New (EmitRef AS Emitter, x%, y%, SpawnRate%, AngleMin!, AngleMax!, VelMin!, VelMax!, MinLifeTime#, MaxLifeTime#)
  EmitRef.X = x%
  EmitRef.Y = y%
  EmitRef.SpawnRate = SpawnRate%
  EmitRef.SpawnAngleMin = AngleMin!
  EmitRef.SpawnAngleMax = AngleMax!
  EmitRef.SpawnMaxVelocity = VelMax!
  EmitRef.SpawnMinVelocity = VelMin!
  EmitRef.MaxLifeTime = MaxLifeTime#
  EmitRef.MinLifeTime = MinLifeTime#
END SUB

SUB GDK_Emitter_SetGravity (EmitRef AS Emitter, Gravity!)
  EmitRef.Gravity = Gravity!
END SUB

SUB GDK_Emitter_SetAccelDecel (EmitRef AS Emitter, AccelMin!, AccelMax!, DecelMin!, DecelMax!)
  EmitRef.AccelMin = AccelMin!
  EmitRef.AccelMax = AccelMax!
  EmitRef.DecelMIn = DecelMin!
  EmitRef.DecelMax! = DecelMax!
END SUB

SUB GDK_Particles_Spawn (EmitRef AS Emitter, PartRef() AS Particle, ParticleMax&, InitialColor&, ColorChange&, ChangeMode%)
  PBTime# = TIMER(.001)
  FOR i& = 0 TO ParticleMax&
    IF NOT PartRef(i&).Exists THEN
      PartRef(i&).Vector.X = EmitRef.X
      PartRef(i&).Vector.Y = EmitRef.Y
      PartRef(i&).Exists = -1
      PartRef(i&).Vector.Rotation = (RND * EmitRef.SpawnAngleMax) + EmitRef.SpawnAngleMin
      PartRef(i&).Vector.Speed = (RND * EmitRef.SpawnMaxVelocity) + EmitRef.SpawnMinVelocity
      PartRef(i&).LifeTime = (RND * EmitRef.MaxLifeTime) + EmitRef.MinLifeTime
      PartRef(i&).InitialCLR = InitialColor&
      PartRef(i&).CurrentCLR = InitialColor&
      PartRef(i&).ClrChange = ColorChange&
      PartRef(i&).BirthTime = PBTime#
      PartRef(i&).ClrChangeMode = ChangeMode%
      IF EmitRef.AccelMax > 0 THEN
        PartRef(i&).Accel = (RND * EmitRef.AccelMax) + EmitRef.AccelMin
      END IF
      IF EmitRef.DecelMax > 0 THEN
        PartRef(i&).Decel = (RND * EmitRef.DecelMax) + EmitRef.DecelMIn
      END IF
      IF SuccessfulSpawns& = EmitRef.SpawnRate THEN EXIT FOR ELSE SuccessfulSpawns& = SuccessfulSpawns& + 1
    END IF
  NEXT
END SUB

SUB GDK_Particles_Update (PartRef() AS Particle, ParticleMax&)
  FOR i& = 0 TO ParticleMax&
    IF PartRef(i&).Exists THEN
      IF TIMER(.001) - PartRef(i&).BirthTime >= PartRef(i&).LifeTime THEN
        PartRef(i&).Exists = 0
      ELSE
        IF PartRef(i&).Vector.Speed > 0 THEN
          IF PartRef(i&).Decel > 0 THEN
            IF PartRef(i&).Accel > 0 THEN
              PartRef(i&).Accel = PartRef(i&).Accel - PartRef(i&).Decel
            ELSE
              PartRef(i&).Vector.Speed = PartRef(i&).Vector.Speed - PartRef(i&).Decel
            END IF
          END IF
          IF PartRef(i&).Accel > 0 THEN PartRef(i&).Vector.Speed = PartRef(i&).Vector.Speed + PartRef(i&).Accel
          IF PartRef(i&).ClrChangeMode = 1 THEN
            PartRef(i&).CurrentCLR = _RGB32(_RED(PartRef(i&).CurrentCLR) - _RED(PartRef(i&).ClrChange), _GREEN(PartRef(i&).CurrentCLR) - _GREEN(PartRef(i&).ClrChange), _BLUE(PartRef(i&).CurrentCLR) - _BLUE(PartRef(i&).ClrChange))
          ELSE
            PartRef(i&).CurrentCLR = _RGB32(_RED(PartRef(i&).CurrentCLR) + _RED(PartRef(i&).ClrChange), _GREEN(PartRef(i&).CurrentCLR) + _GREEN(PartRef(i&).ClrChange), _BLUE(PartRef(i&).CurrentCLR) + _BLUE(PartRef(i&).ClrChange))
          END IF
          GDK_Vector_Move PartRef(i&).Vector
        ELSE
          PartRef(i&).Exists = 0
        END IF
      END IF
    END IF
  NEXT
END SUB

SUB GDK_Particles_Draw (PartRef() AS Particle, ParticleMax&)
  FOR i& = 0 TO ParticleMax&
    IF PartRef(i&).Exists THEN
      PSET (PartRef(i&).Vector.X, PartRef(i&).Vector.Y), PartRef(i&).CurrentCLR
    END IF
  NEXT
END SUB

'##########################################################################################################################
'// MATH FUNCTIONS
'##########################################################################################################################

FUNCTION GDK_Distance! (X%, Y%, XX%, YY%)
  xd% = XX% - X%
  yd% = YY% - Y%
  GDK_Distance! = SQR((xd% * xd%) + (yd% * yd%))
END FUNCTION

FUNCTION GDK_RadianToDegree! (Radians!)
  GDK_RadianToDegree! = Radians! * (180 / (4 * ATN(1)))
END FUNCTION

FUNCTION GDK_DegreeToRadian! (Degrees!)
  GDK_DegreeToRadian! = Degrees! * ((4 * ATN(1)) / 180)
END FUNCTION

SUB GDK_MathX
  DECLARE LIBRARY
    FUNCTION atan2# (BYVAL y#, BYVAL x#)
    FUNCTION acos# (BYVAL x#)
    FUNCTION asin# (BYVAL x#)
    FUNCTION cosh# (BYVAL x#)
    FUNCTION sinh# (BYVAL x#)
    FUNCTION tanh# (BYVAL x#)
    FUNCTION pow# (BYVAL base#, BYVAL exponent#)
  END DECLARE
END SUB

'##########################################################################################################################
'// Tile engine
'##########################################################################################################################

SUB GDK_TileSet_New (TileRef AS Tileset, File$, XTiles%, YTiles%, Alpha&)
  GDK_Sprite_New TileRef.Sheet, File$, XTiles%, YTiles%, XTiles% * YTiles%, Alpha&
  TileRef.TWidth = (TileRef.Sheet.Width / XTiles%)
  TileRef.THeight = (TileRef.Sheet.Height / YTiles%)
END SUB

SUB GDK_TileMap_Load (Level() AS Tile, XTiles%, YTiles%)
  FOR j% = 0 TO YTiles% - 1
    FOR i% = 0 TO XTiles% - 1
      READ tile%
      Level(i%, j%).Tile = tile%
    NEXT
  NEXT
END SUB

SUB GDK_TileMap_Draw (TileRef AS Tileset, Level() AS Tile, X%, Y%, XCnt%, YCnt%, XMax%, YMax%)
  Tx% = X%
  Ty% = Y%
  sx% = _WIDTH(_DEST)
  sy% = _HEIGHT(_DEST)
  FOR j% = YCnt% TO YMax% - 1
    FOR i% = XCnt% TO XMax% - 1
      IF Tx% < sx% THEN
        IF Level(i%, j%).Tile > 0 THEN
          GDK_Sprite_DrawXY TileRef.Sheet, Tx%, Ty%, 0, Level(i%, j%).Tile
        END IF
        Tx% = Tx% + (TileRef.TWidth * TileRef.Sheet.Scale)
      ELSE
        EXIT FOR
      END IF
    NEXT
    Tx% = X%
    IF Ty% < sy% THEN Ty% = Ty% + (TileRef.THeight * TileRef.Sheet.Scale) ELSE EXIT FOR
  NEXT
END SUB


SUB GDK_TileMap_Load_FromFile (GameFile$, Level() AS Tile, YTiles%)
  X& = FREEFILE
  OPEN GameFile$ FOR INPUT AS #X&
  FOR i% = 0 TO YTiles% - 1
    LINE INPUT #X&, DataLine$
    Cnt% = 0
    FOR j% = 0 TO LEN(DataLine$)
      IF INSTR(j%, DataLine$, ",") > 0 THEN
        Level(Cnt%, i%).Tile = VAL(MID$(DataLine$, j%, INSTR(j%, DataLine$, ",") - j%))
        j% = INSTR(j%, DataLine$, ",")
        Cnt% = Cnt% + 1
      ELSE
        Level(Cnt%, i%).Tile = VAL(MID$(DataLine$, j%))
      END IF
    NEXT
  NEXT
  CLOSE #X&
END SUB

SUB GDK_Tileset_SetScale (TileRef AS Tileset, Scale!)
  TileRef.Sheet.Scale = Scale!
END SUB

'SUB GDK_Tiles_Draw_ISO (TileRef AS Tileset, XYRef() AS XY, Level() AS Tile, X%, Y%, XCnt%, YCnt%, XMax%, YMax%)
'  Tx% = X%
'  Ty% = Y%
'  YCntr% = 0
'  FOR j% = YCnt% TO YMax% - 1
'    FOR i% = XCnt% TO XMax% - 1
'            IF Level(i%, j%).Tile > 0 THEN GDK_Tile_Draw TileRef, Level(i%, j%).Tile, XYRef(), Tx%, Ty%
'            Tx% = Tx% + (TileRef.TWidth / 2)
'            Ty% = Ty% + (TileRef.THeight / 4)
'        NEXT
'        YCntr% = YCntr% + 1
'        Ty% = Y% + YCntr% * (TileRef.THeight / 4)
'        Tx% = X% - (YCntr% * (TileRef.TWidth / 2))
'    NEXT
'END SUB


'##########################################################################################################################
'// GameObject Subs and Functions - SUper fast prototyping commands for games
'##########################################################################################################################

SUB GDK_GameObject_New (Handle AS GameObject, File$, XframeCount%, YFrameCount%, X%, Y%, Speed!, Rotation!)
  GDK_Sprite_New Handle.Sprite, File$, XframeCount%, YFrameCount%, XframeCount% * YFrameCount%, 1
  GDK_Vector_New Handle.Vector, X%, Y%, Speed!, Rotation!
  GDK_Sprite_SetVisibility Handle.Sprite, -1
  GDK_Sprite_SetAlpha Handle.Sprite, _RGB(255, 0, 255)
END SUB

SUB GDK_GameObject_Draw (GameObject AS GameObject, AnimationFrame%)
  GDK_Sprite_Draw GameObject.Sprite, GameObject.Vector, AnimationFrame%
END SUB

SUB GDK_GameObject_Update (Handle AS GameObject)
  GDK_Vector_Update Handle.Vector
  GDK_Rectangle_Autosize Handle.Sprite, Handle.Vector, Handle.Rect
END SUB

SUB GDK_GameObject_Rotate (Handle AS GameObject, Rot!)
  Handle.Vector.Rotation = Handle.Vector.Rotation + Rot!
END SUB

SUB GDK_GameObject_Hide (Handle AS GameObject)
  Handle.Sprite.IsVisible = 0
END SUB

SUB GDK_GameObject_Show (Handle AS GameObject)
  Handle.Sprite.IsVisible = -1
END SUB

FUNCTION GDK_GameObject_Intersect (Handle AS GameObject, Handle2 AS GameObject)
  IF GDK_Rectangle_Intersect(Handle.Rect, Handle2.Rect) THEN
    GDK_GameObject_Intersect = -1
  ELSE
    GDK_GameObject_Intersect = 0
  END IF
END SUB

'##########################################################################################################################
'// Animation routines - Taken from GDK_GL
'##########################################################################################################################

SUB GDK_ANIMATION_NEW (Anim AS ANIMATION, Frame%, StartFrame%, ResetFrame%, AnimTime#, AnimTimer#)
  Anim.Frame = Frame%
  Anim.StartFrame = StartFrame%
  Anim.ResetFrame = ResetFrame%
  Anim.Time = AnimTime#
  Anim.Timer = AnimTimer#
  IF ResetFrame% < StartFrame% THEN Anim.Dir = -1 ELSE Anim.Dir = 0
END SUB

SUB GDK_ANIMATION_UPDATE (Anim AS ANIMATION)
  IF Anim.Dir = -1 THEN
    IF Anim.Frame > Anim.StartFrame OR Anim.Frame < Anim.ResetFrame THEN
      Anim.Frame = Anim.StartFrame
      Anim.Timer = TIMER(.001)
    ELSE
      IF TIMER(.001) - Anim.Timer >= Anim.Time THEN
        IF Anim.Frame = Anim.ResetFrame THEN
          Anim.Frame = Anim.StartFrame
        ELSE
          Anim.Frame = Anim.Frame - 1
        END IF
        Anim.Timer = TIMER(.001)
      END IF
    END IF
  ELSE
    IF Anim.Frame < Anim.StartFrame OR Anim.Frame > Anim.ResetFrame THEN
      Anim.Frame = Anim.StartFrame
      Anim.Timer = TIMER(.001)
    ELSE
      IF TIMER(.001) - Anim.Timer >= Anim.Time THEN
        IF Anim.Frame = Anim.ResetFrame THEN
          Anim.Frame = Anim.StartFrame
        ELSE
          Anim.Frame = Anim.Frame + 1
        END IF
        Anim.Timer = TIMER(.001)
      END IF
    END IF
  END IF
END SUB

'##########################################################################################################################
'// Image ROTATION/ZOOM FUNCTION BY GALLEON
'##########################################################################################################################

SUB Rotate_Scale_AroundPoint (X AS LONG, Y AS LONG, Image AS LONG, Rotation AS SINGLE, ScaleX AS SINGLE, ScaleY AS SINGLE, PointX AS LONG, PointY AS LONG)
  DEFSNG A-Z
  DIM pX(3) AS SINGLE: DIM pY(3) AS SINGLE
  DIM W AS LONG, H AS LONG, I AS LONG, X2 AS LONG, Y2 AS LONG
  W = _WIDTH(Image): H = _HEIGHT(Image)
  pX(0) = -PointX: pY(0) = -PointY: pX(1) = -PointX: pY(1) = -PointY + H - 1: pX(2) = -PointX + W - 1: pY(2) = -PointY + H - 1: pX(3) = -PointX + W - 1: pY(3) = -PointY 'Set dest screen points
  Radians = -Rotation / 57.29578: SINr = SIN(Radians): COSr = COS(Radians) 'Precalculate SIN & COS of angle
  FOR I = 0 TO 3
    pX(I) = pX(I) * ScaleX: pY(I) = pY(I) * ScaleY 'Scale
    X2 = pX(I) * COSr + SINr * pY(I): pY(I) = pY(I) * COSr - pX(I) * SINr: pX(I) = X2 'Rotate Dest Points
    pX(I) = pX(I) + X: pY(I) = pY(I) + Y 'Translate Dest Points
  NEXT
  _MAPTRIANGLE (0, 0)-(0, H - 1)-(W - 1, H - 1), Image TO(pX(0), pY(0))-(pX(1), pY(1))-(pX(2), pY(2))
  _MAPTRIANGLE (0, 0)-(W - 1, 0)-(W - 1, H - 1), Image TO(pX(0), pY(0))-(pX(3), pY(3))-(pX(2), pY(2))
END SUB

'##########################################################################################################################
'// Path based movement v.02
'##########################################################################################################################

SUB GDK_Path_New (Path AS Path, PathPoint() AS PathPoint, NumPoints%, Reverse%, Repeat%, Relative%)
  IF Relative% THEN READ Path.Vector.X, Path.Vector.Y, Path.Vector.Speed
  FOR i% = 0 TO NumPoints% - 1
    READ PathPoint(i%).X, PathPoint(i%).Y, PathPoint(i%).Speed
  NEXT
  IF Relative% THEN
    Path.TargetVector.X = Path.Vector.X + PathPoint(0).X
    Path.TargetVector.Y = Path.Vector.Y + PathPoint(0).Y
    Path.Vector.Rotation = -atan2(Path.Vector.Y - Path.TargetVector.Y, Path.Vector.X - Path.TargetVector.X) + (2 * ATN(1))
  ELSE
    Path.Vector.X = PathPoint(0).X
    Path.Vector.Y = PathPoint(0).Y
    Path.Vector.Speed = PathPoint(0).Speed
    Path.Vector.Rotation = -atan2(PathPoint(0).Y - PathPoint(1).Y, PathPoint(0).X - PathPoint(1).X) + (2 * ATN(1))
    Path.TargetVector.X = PathPoint(1).X
    Path.TargetVector.Y = PathPoint(1).Y
  END IF
  Path.LastVector = Path.Vector
  Path.NumPoints = NumPoints%
  Path.Reverse = Reverse%
  Path.Repeat = Repeat%
  Path.Relative = Relative%
END SUB


SUB GDK_Path_Update (Path AS Path, PathPoint() AS PathPoint)
  IF Path.Relative THEN '// Relative movement
    IF NOT Path.HitTargetX THEN IF IsWithinRange(Path.TargetVector.X, Path.LastVector.X, Path.Vector.X) THEN Path.HitTargetX = -1
    IF NOT Path.HitTargetY THEN IF IsWithinRange(Path.TargetVector.Y, Path.LastVector.Y, Path.Vector.Y) THEN Path.HitTargetY = -1
    IF Path.HitTargetX AND Path.HitTargetY THEN
      IF Path.CurrentPoint < Path.NumPoints - 1 THEN
        Path.TargetVector.X = Path.Vector.X + PathPoint(Path.CurrentPoint + 1).X
        Path.TargetVector.Y = Path.Vector.Y + PathPoint(Path.CurrentPoint + 1).Y
        Path.Vector.Rotation = -atan2(Path.Vector.Y - Path.TargetVector.Y, Path.Vector.X - Path.TargetVector.X) + (2 * ATN(1))
        Path.Vector.Speed = PathPoint(Path.CurrentPoint).Speed
        Path.CurrentPoint = Path.CurrentPoint + 1
      ELSE '// Last point
        IF Path.Repeat THEN
          Path.CurrentPoint = 0
          Path.TargetVector.X = Path.Vector.X + PathPoint(0).X
          Path.TargetVector.Y = Path.Vector.Y + PathPoint(0).Y
          Path.Vector.Rotation = -atan2(Path.Vector.Y - Path.TargetVector.Y, Path.Vector.X - Path.TargetVector.X) + (2 * ATN(1))
          Path.Vector.Speed = PathPoint(0).Speed
        ELSE
          Path.Vector.Speed = 0
        END IF
      END IF
      Path.HitTargetX = 0
      Path.HitTargetY = 0
    END IF
  ELSE '// Co-ordinate based movement
    IF Path.IsReversing THEN
      IF Path.CurrentPoint > 0 THEN
        IF NOT Path.HitTargetX THEN IF IsWithinRange(PathPoint(Path.CurrentPoint - 1).X, Path.LastVector.X, Path.Vector.X) THEN Path.HitTargetX = -1
        IF NOT Path.HitTargetY THEN IF IsWithinRange(PathPoint(Path.CurrentPoint - 1).Y, Path.LastVector.Y, Path.Vector.Y) THEN Path.HitTargetY = -1
        IF Path.HitTargetX AND Path.HitTargetY THEN
          IF Path.CurrentPoint > 1 THEN
            Path.CurrentPoint = Path.CurrentPoint - 1
            Path.Vector.Rotation = -atan2(Path.Vector.Y - PathPoint(Path.CurrentPoint - 1).Y, Path.Vector.X - PathPoint(Path.CurrentPoint - 1).X) + (2 * ATN(1))
          ELSE '// First point
            Path.CurrentPoint = 0
            IF Path.Reverse THEN
              Path.IsReversing = 0
              Path.Vector.Rotation = -atan2(Path.Vector.Y - PathPoint(Path.CurrentPoint + 1).Y, Path.Vector.X - PathPoint(Path.CurrentPoint + 1).X) + (2 * ATN(1))
            END IF
          END IF
          Path.HitTargetX = 0
          Path.HitTargetY = 0
          Path.Vector.Speed = PathPoint(Path.CurrentPoint).Speed
        END IF
      END IF
    ELSE
      IF Path.CurrentPoint <= Path.NumPoints - 1 THEN
        IF NOT Path.HitTargetX THEN IF IsWithinRange(PathPoint(Path.CurrentPoint + 1).X, Path.LastVector.X, Path.Vector.X) THEN Path.HitTargetX = -1
        IF NOT Path.HitTargetY THEN IF IsWithinRange(PathPoint(Path.CurrentPoint + 1).Y, Path.LastVector.Y, Path.Vector.Y) THEN Path.HitTargetY = -1
        IF Path.HitTargetX AND Path.HitTargetY THEN
          IF Path.CurrentPoint < Path.NumPoints - 2 THEN
            Path.CurrentPoint = Path.CurrentPoint + 1
            Path.Vector.Rotation = -atan2(Path.Vector.Y - PathPoint(Path.CurrentPoint + 1).Y, Path.Vector.X - PathPoint(Path.CurrentPoint + 1).X) + (2 * ATN(1))
          ELSE '// Last point
            Path.CurrentPoint = Path.NumPoints - 1
            IF Path.Reverse THEN
              Path.IsReversing = -1
              Path.Vector.Rotation = -atan2(Path.Vector.Y - PathPoint(Path.CurrentPoint - 1).Y, Path.Vector.X - PathPoint(Path.CurrentPoint - 1).X) + (2 * ATN(1))
            ELSEIF Path.Repeat THEN
              Path.CurrentPoint = 0
              IF Path.Repeat THEN
                FOR i% = 1 TO Path.NumPoints - 1
                  XDif% = PathPoint(i%).X - PathPoint(i% - 1).X
                  YDif% = PathPoint(i%).Y - PathPoint(i% - 1).Y
                  IF i% = 1 THEN
                    PathPoint(0).X = Path.Vector.X
                    PathPoint(0).Y = Path.Vector.Y
                  END IF
                  PathPoint(i%).X = PathPoint(i% - 1).X + XDif%
                  PathPoint(i%).Y = PathPoint(i% - 1).Y + YDif%
                NEXT
              END IF
              Path.Vector.Rotation = -atan2(Path.Vector.Y - PathPoint(Path.CurrentPoint + 1).Y, Path.Vector.X - PathPoint(Path.CurrentPoint + 1).X) + (2 * ATN(1))
            ELSE '// Stop at end
              PathPoint(Path.CurrentPoint).Speed = 0
            END IF
          END IF
          Path.Vector.Speed = PathPoint(Path.CurrentPoint).Speed
          Path.HitTargetX = 0
          Path.HitTargetY = 0
        END IF
      END IF
    END IF
  END IF
  Path.LastVector = Path.Vector
  GDK_Vector_Update Path.Vector
END SUB

'// GDK_Path_Update helper function
FUNCTION IsWithinRange% (Target%, MinRange%, MaxRange%)
  IF Target% >= MinRange% AND Target% <= MaxRange% THEN
    IsWithinRange% = -1
  ELSEIF Target% <= MinRange% AND Target% >= MaxRange% THEN
    IsWithinRange% = -1
  ELSE
    IsWithinRange% = 0
  END IF
END FUNCTION


'##########################################################################################################################
'// END OF LIBRARY!!!
'##########################################################################################################################

