------ All content © 2008 DigiPen ( USA ) Corporation, all rights reserved. ------
-- Author: Hamza M. Hutchinson
-- Updated: 03/20/2009

dofile( "../../Assets/Scripts/key_name_mappings.lua" )
dofile( "../../Assets/Scripts/util_math.lua" )


--------------------------------------------------------------------------------
-- Fuctions!
-- This might look redundant, but avoids costly global lookup
--------------------------------------------------------------------------------

local PLAYER = Player
local ENTITY = Entity
local GAMEMODEFULL = Global:GameModeFULL()
local GAMEMODECAMERA = Global:GameModeCAMERA()

local PLAYER_gMaxLight               = Player.gMaxLight
local PLAYER_gMinLight               = Player.gMinLight
local PLAYER_gLightCount             = Player.gLightCount
local PLAYER_sLightCount             = Player.sLightCount
local PLAYER_gFFDist                 = Player.gFFDist
local PLAYER_gAster                  = Player.gAster
local PLAYER_gPSTime                 = Player.gPSTime

local VEC3_construct                 = vec3.construct
local VEC3_add                       = vec3.add
local VEC3_subtract                  = vec3.subtract
local VEC3_multiply                  = vec3.multiply
local VEC3_multiplyThis              = vec3.multiplyThis
local VEC3_divide                    = vec3.divide
local VEC3_dot                       = vec3.dot
local VEC3_cross                     = vec3.cross
local VEC3_magnitude                 = vec3.magnitude
local VEC3_magnitudeSq               = vec3.magnitudeSq
local VEC3_normalize                 = vec3.normalize

local UTIL_Approx                    = Approx
local UTIL_Clamp                     = Clamp

local MATH_acos                      = math.acos
local MATH_sqrt                      = math.sqrt
local MATH_max                       = math.max
local MATH_min                       = math.min

local GLOBAL_GetIslandInfo           = Global.GetIslandInfo
local GLOBAL_IsInGameMode            = Global.IsInGameMode
local GLOBAL_CreateEntity            = Global.CreateEntity
local GLOBAL_CreateEntityAt          = Global.CreateEntityAt
local GLOBAL_gVineID                 = Global.gVineID
local GLOBAL_gVineGrowDir            = Global.gVineGrowDir
local GLOBAL_gVineEndPoint           = Global.gVineEndPoint
local GLOBAL_gVinePattern            = Global.gVinePattern
local GLOBAL_sVineGrowSpeed          = Global.sVineGrowSpeed
local GLOBAL_GrowVine                = Global.GrowVine
local GLOBAL_UnGrowVine              = Global.UnGrowVine
local GLOBAL_IsDocking               = Global.IsDocking
local GLOBAL_AddToMaxVineLen         = Global.AddToMaxVineLen
local GLOBAL_FOVfromGrowth           = Global.FOVfromGrowth
local GLOBAL_gVineMaturity           = Global.gVineMaturity
local GLOBAL_LanternMessage          = Global.LanternMessage
local GLOBAL_StopVineGrowth          = Global.StopVineGrowth
local GLOBAL_PlayVineGrowth          = Global.PlayVineGrowth
local GLOBAL_SpiralSwerveSound       = Global.SpiralSwerveSound
local GLOBAL_PlaySound2D             = Global.PlaySound2D
local GLOBAL_IsGameButtonPressed     = Global.IsGameButtonPressed
local GLOBAL_IsGameButtonHeld        = Global.IsGameButtonHeld
local GLOBAL_IsGameButtonHeldFor     = Global.IsGameButtonHeldFor
local GLOBAL_gRangeState             = Global.gRangeState
local GLOBAL_gRangeIsActive          = Global.gRangeIsActive
local GLOBAL_ToggleRangeIsActive     = Global.ToggleRangeIsActive
local GLOBAL_sRangeIsActive          = Global.sRangeIsActive
local GLOBAL_gCameraBasis            = Global.gCameraBasis
local GLOBAL_gCameraPosition         = Global.gCameraPosition
local GLOBAL_gCameraDirection        = Global.gCameraDirection
local GLOBAL_gCameraViewVector       = Global.gCameraViewVector
local GLOBAL_ClampCameraZoomMinMax   = Global.ClampCameraZoomMinMax
local GLOBAL_ClampCameraPhiMinMax    = Global.ClampCameraPhiMinMax
local GLOBAL_SetCameraTargetObject   = Global.SetCameraTargetObject
local GLOBAL_SetCameraViewVector     = Global.SetCameraViewVector
local GLOBAL_SetShoulders            = Global.SetShoulders
local GLOBAL_SetLanternPoleEndpoints = Global.SetLanternPoleEndpoints
local GLOBAL_sAsterWalkState         = Global.sAsterWalkState
local GLOBAL_GameLanternPosition     = Global.GameLanternPosition
local GLOBAL_TimeElapsed             = Global.TimeElapsed
local GLOBAL_FireflySpawnMsg         = Global.FireflySpawnMsg
local GLOBAL_GetLanternBoost         = Global.GetLanternBoost
local GLOBAL_SetLanternBoost         = Global.SetLanternBoost
local GLOBAL_IncLanternBoost         = Global.IncLanternBoost
local GLOBAL_SetIslandHome           = Global.SetIslandHome
local GLOBAL_SetIslandRadius         = Global.SetIslandRadius
local GLOBAL_SetCameraMode           = Global.SetCameraMode

local ENTITY_GetMyPointer            = Entity.GetMyPointer
local ENTITY_SPT_RefreshNumbers      = Entity.SPT_RefreshNumbers
local ENTITY_SPT_RefreshEntities     = Entity.SPT_RefreshEntities
local ENTITY_SHR_Refresh             = Entity.SHR_Refresh
local ENTITY_SHR_gPosition           = Entity.SHR_gPosition
local ENTITY_SHR_sPosition           = Entity.SHR_sPosition
local ENTITY_SHR_sVelocity           = Entity.SHR_sVelocity
local ENTITY_SHR_sRotation           = Entity.SHR_sRotation
local ENTITY_SHR_sRotationMatrix     = Entity.SHR_sRotationMatrix
local ENTITY_PHY_SetAccelOnce        = Entity.PHY_SetAccelOnce
local ENTITY_PHY_GetAccel            = Entity.PHY_GetAccel
local ENTITY_PHY_GetAccelOnce        = Entity.PHY_GetAccelOnce
local ENTITY_PHY_Refresh             = Entity.PHY_Refresh
local ENTITY_VIS_SetLightColor       = Entity.VIS_SetLightColor
local ENTITY_SPT_GiveNumber          = Entity.SPT_GiveNumber


--------------------------------------------------------------------------------
-- Defines!
--------------------------------------------------------------------------------

local ASSETS_XML = "../../Assets/Xml/"
local SPAWNERFLY = "entities/e_SpawnerFly.xml"

local MAX_LIGHT     = PLAYER_gMaxLight( PLAYER ) -- Player's max light count
local MIN_LIGHT     = PLAYER_gMinLight( PLAYER ) -- Player's min light count
local MIN_SPEED     = MIN_LIGHT / 1.5            -- Speed when player has no light
local MAX_BOOST     = 3                          -- Firefly boost can be no more than
local UNGROW_SPEED  = 3                          -- How fast to ungrow
local MIN_VINEDIST  = 258
local MAX_VINEDIST  = 300
local MIN_CAM_DIST  = 18
local MAX_CAM_DIST  = 25
local BUBBLEPADDING = 7                          -- Without some extra room, the camera goes outside the bubble

local ASTER_SPEED   = 7                          -- Aster's movement speed
local ASTER_JUMP    = 10                         -- Aster's jump speed
local ASTER_STOP    = .95                        -- Multiplied by Aster's speed to slow him down
local AIR_CONTROL   = .6                         -- Limited air control
local LANTERN_SPEED = .08                        -- Lantern rotation speed
local LAND_FUDGE    = 1.2                        -- bias the landing comparison
local TIP_FUDGE     = 2.5                        -- tip radius squared
local JUMP_DELAY    = 500                        -- ms until next jump
local TIP_DELAY     = 1000                       -- ms before tip starts attractive field
local PAUSE_DELAY   = 100                        -- ms before controls come back


local FORWARD       = { x = 0, y = 0, z = -1 }
local SIDE          = { x = 1, y = 0, z =  0 }
local UP            = { x = 0, y = 1, z =  0 }
local lPosLocal     = { x = 1, y = 0, z =  0 } --lantern position when not on vine end
local lPosInterp    = { x = 0, y = 0, z =  0 }
local lVecFinal     = { x = 0, y = 0, z =  0 }

local PATTERN_TIME = PLAYER_gPSTime( PLAYER )
local OFFVECS = { 
                  [  0 ] = VEC3_construct(  1,  0,  0 ),
                  [  1 ] = VEC3_construct(  0,  1,  0 ),
                  [  2 ] = VEC3_construct(  0,  0,  1 ),
                  [  3 ] = VEC3_construct( -1,  0,  0 ),
                  [  4 ] = VEC3_construct(  0, -1,  0 ),
                  [  5 ] = VEC3_construct(  1,  0, -1 ),
                  [  6 ] = VEC3_construct(  1,  1,  0 ),
                  [  7 ] = VEC3_construct(  1,  0,  1 ),
                  [  8 ] = VEC3_construct(  0,  1,  1 ),
                  [  9 ] = VEC3_construct(  1, -1,  0 ),
                  [ 10 ] = VEC3_construct(  1,  0, -1 ),
                  [ 11 ] = VEC3_construct(  0,  1, -1 ),
                  max = 11
                }
local iter = 0
while( iter < OFFVECS.max ) do
  OFFVECS[ iter ] = VEC3_multiplyThis( OFFVECS[ iter ], 10 )
  iter = iter + 1
  
end

local CAMERA_MODE_FOLLOW = 0
local CAMERA_MODE_LOOK   = 1


--------------------------------------------------------------------------------
-- How to update the lantern when we're NOT on the vine
--------------------------------------------------------------------------------

local function UpdateLanternPosOff( input ) --not on vine end
  lPosLocal.y = 0
  lPosLocal = VEC3_add( lPosLocal, VEC3_multiply( input, LANTERN_SPEED ) )
  local magSqrd = VEC3_magnitudeSq( lPosLocal )
  if magSqrd > 1 then
    lPosLocal = VEC3_divide( lPosLocal, MATH_sqrt( magSqrd ) )
    
  else
    lPosLocal.y = MATH_sqrt( 1 - magSqrd )
    
  end
  
  return lPosLocal
end


--------------------------------------------------------------------------------
-- How to update the lantern when we're on the vine
--------------------------------------------------------------------------------

local function UpdateLanternPosOn( input ) --on vine end
  --using the grow direction
  local up = { x = 0, y = 1, z = 0 }
  local forward = GLOBAL_gVineGrowDir()
  local side = VEC3_cross( forward, up )
  side = VEC3_normalize( side )
  up = VEC3_cross( side, forward )

  forward = VEC3_add( forward, VEC3_multiply( side, input.x * LANTERN_SPEED ) )
  forward = VEC3_add( forward, VEC3_multiply(   up, input.z * LANTERN_SPEED ) )
  return forward
end


--------------------------------------------------------------------------------
-- Update island bubble position and radius
--------------------------------------------------------------------------------
local function UpdateIslandBubble(pos, rad)
  GLOBAL_SetIslandHome( pos.x, pos.y, pos.z )
  GLOBAL_SetIslandRadius( rad + BUBBLEPADDING )
end


--------------------------------------------------------------------------------
-- Vars
--------------------------------------------------------------------------------

local myData = {}
myData.fadeAmount = 0

local Aster     = PLAYER_gAster( PLAYER )
local AsterData = {}

local Camera = GLOBAL_CreateEntity( "../../Assets/Xml/entities/game_CameraInput.xml" )
ENTITY_SPT_GiveNumber( Camera, "Orient", 1.0 )

local CamHelper = GLOBAL_CreateEntity( "../../Assets/Xml/entities/e_Nentity.xml" )
local CamHelperData = {}

local grew      = false
local ungrew    = false
local lastGrew  = false

GLOBAL_SetCameraTargetObject( ENTITY_GetMyPointer( CamHelper ) )
myData.TakeCamera = 0.0

local elapsed = 0
local ffBoost = 0
local pattern = 0
local segments = 0
local curPattern = 0
local patternTimer = PATTERN_TIME
local patternIncr  = 0
local lastVine = GLOBAL_gVineID()
local curVineDSq = 0
local curVineDist = 0
local lastDockPos = GLOBAL_GetIslandInfo( 0 )

local onTip      = false
local onGround   = false

local moving     = false
local rangeMagSq = 0

local centerButtonFrameDelay = false

local pauseWait = 0
local jumpWait  = 0
local tipWait   = 0

local inLookAroundMode = false

local camPhiSpeed = 0
local camThetaSpeed = 0
local color = MAX_LIGHT

local vineVec = VEC3_construct( 0, 0, 0 )


--------------------------------------------------------------------------------
-- Im in ur loopz
--------------------------------------------------------------------------------

while( 1 ) 
do
  coroutine.yield()
  
  --for consistency
  --collectgarbage("collect")
  
  ENTITY = Entity
  camPhiSpeed   = 0
  camThetaSpeed = 0
  elapsed       = GLOBAL_TimeElapsed()


  ------------------------------------------------------------------------------
  -- Take camera?/fadeAmount/growing distance
  ------------------------------------------------------------------------------

  ENTITY_SPT_RefreshNumbers(  ENTITY, myData )
  ENTITY_SPT_RefreshEntities( ENTITY, myData )


  ----------------------------------------------------------------------------
  -- Update movement basis
  ----------------------------------------------------------------------------
  
  FORWARD, SIDE, UP = GLOBAL_gCameraBasis()


  ------------------------------------------------------------------------------
  -- Loop initialization
  ------------------------------------------------------------------------------
  
  GLOBAL_IncLanternBoost( -0.05 )
  ffBoost = GLOBAL_GetLanternBoost()
  
  color = PLAYER_gLightCount( PLAYER )
  PATTERN_TIME = PLAYER_gPSTime( PLAYER )
  
  ENTITY_SHR_Refresh( ENTITY, myData, true, true, false, true )
  ENTITY_SHR_Refresh( Aster, AsterData, true, true, false, true )
  ENTITY_SHR_Refresh( CamHelper, CamHelperData, true, true, false, true )
  
  local AsterPos = AsterData.pos
  local AsterVel = {} --AsterData.vel
  AsterVel.x =  AsterData.vel.x *    SIDE.x + AsterData.vel.y *    SIDE.y + AsterData.vel.z *    SIDE.z
  AsterVel.y =  AsterData.vel.x *      UP.x + AsterData.vel.y *      UP.y + AsterData.vel.z *      UP.z
  AsterVel.z = -AsterData.vel.x * FORWARD.x - AsterData.vel.y * FORWARD.y - AsterData.vel.z * FORWARD.z
  local rot = {}
  local pos = {x = lPosLocal.x, y = lPosLocal.y, z = lPosLocal.z}
  local vineEnd = GLOBAL_gVineEndPoint()
  
  ------------------------------------------------------------------------------
  -- Not playing; don't grow
  ------------------------------------------------------------------------------
  
  if( GLOBAL_IsInGameMode() <= GAMEMODECAMERA )
  then
    GLOBAL_StopVineGrowth()
    GLOBAL_SpiralSwerveSound( false )
    pauseWait = PAUSE_DELAY
    --local acc = ENTITY_PHY_GetAccel( Aster )
    --ENTITY_PHY_SetAccelOnce( Aster, -acc.x, -acc.y, -acc.z )
    
  elseif( pauseWait > 0 ) then
    pauseWait = MATH_max( pauseWait - elapsed, 0 )
    
    ------------------------------------------------------------------------------
    -- Look around?
    ------------------------------------------------------------------------------

  else


    ----------------------------------------------------------------------------
    -- Get input
    ----------------------------------------------------------------------------
    
    local range = GLOBAL_gRangeState( 0 )
    range.y = 0 --puts a value at y so that this table can be dealt with using vec3 functions
    rangeMagSq = VEC3_magnitudeSq( range )
    if( rangeMagSq < .00001 )
    then
      range = GLOBAL_gRangeState( 2 )
      range.y = 0 --puts a value at y so that this table can be dealt with using vec3 functions
      rangeMagSq = VEC3_magnitudeSq( range )
    end
    
    moving = false

    ----------------------------------------------------------------------------
    -- Toggles look mode
    if( Global:IsGameButtonTriggered( 7 ) ) -- button 8
    then
      GLOBAL_SetCameraMode(CAMERA_MODE_LOOK)
      inLookAroundMode = not inLookAroundMode
      --GLOBAL_ToggleRangeIsActive( 1 )
      if( inLookAroundMode == false )
      then
        GLOBAL_ClampCameraZoomMinMax( MIN_CAM_DIST, MAX_CAM_DIST )
        ENTITY_SPT_GiveNumber( Camera, "centerCamera", 1 )
      end
    end
    
    if( inLookAroundMode == true and
        ( not Approx( range.x, 0 ) or 
          not Approx( range.z, 0 ) or 
          Global:IsGameButtonPressed( 0 ) or 
          Global:IsGameButtonPressed( 1 ) or 
          Global:IsGameButtonPressed( 2 ) or 
          Global:IsGameButtonPressed( 3 ) or 
          Global:IsGameButtonPressed( 4 ) ) )
    then
      GLOBAL_SetCameraMode(CAMERA_MODE_FOLLOW)
      inLookAroundMode = false
      --GLOBAL_sRangeIsActive( 1, false )
      GLOBAL_ClampCameraZoomMinMax( MIN_CAM_DIST, MAX_CAM_DIST )
      ENTITY_SPT_GiveNumber( Camera, "centerCamera", 1 )
    end

    if( inLookAroundMode )
    then
      GLOBAL_ClampCameraZoomMinMax( 2.5, 2.5 )
      pos.x = 0
      pos.y = 0
      pos.z = -1
      lPosLocal.x = 0
      lPosLocal.y = 0
      lPosLocal.z = -1
      
      ENTITY_SHR_sVelocity( Aster, 0, 0, 0 )
      
      ------------------------------------------------------------------------------
      -- All the stuff we need to do
      ------------------------------------------------------------------------------
    
    else

      ----------------------------------------------------------------------------
      -- New vine?
      ----------------------------------------------------------------------------

      --local newestVine = GLOBAL_gVineID()
      --if( newestVine ~= lastVine )
      --then
        --lastVine = newestVine
        --segmentsTotal = 0

        --MIN_VINEDIST = myData.minVineDist
        --MAX_VINEDIST = myData.maxVineDist
        --lastDockPos = ENTITY_SHR_gPosition( myData.lastDock )

        --myData.minVineDist = nil
        --myData.maxVineDist = nil
        --myData.lastDock = nil     
      --end

      -- Update island bubble
      UpdateIslandBubble(lastDockPos, MIN_VINEDIST)

      ----------------------------------------------------------------------------
      -- Jumping
      ----------------------------------------------------------------------------

      -- have we landed? Cheeso?   
      ENTITY_PHY_Refresh( Aster, AsterData, true )
      if( not onGround and #AsterData.collisions > 0 ) then
        jumpWait = MATH_max( jumpWait - elapsed, 0 );
      end

      if( jumpWait == 0 ) then
        onGround = true
      end

      if( GLOBAL_IsGameButtonPressed( 0 ) ) and ( onGround ) and ( myData.forcedSequence == nil )
      then
        if( not Global:MoonDockOver() )
        then
          GLOBAL_PlaySound2D( "../../Assets/Sounds/aster_jump.mp3" )
        end
        AsterVel.y = ASTER_JUMP
        jumpWait = JUMP_DELAY
        onGround = false
        moving = true
      end

      ----------------------------------------------------------------------------
      -- Vine growth
      ----------------------------------------------------------------------------
      vineVec = VEC3_subtract( vineEnd, lastDockPos )
      curVineDist = VEC3_magnitude( vineVec )

      -- Button 0 is jump, Button 1 is grow
      -- press jump while growing to reverse
      local vGrowth = 0
      if ( GLOBAL_IsGameButtonPressed( 1 ) )
      then
        vGrowth = vGrowth + 1

      end
      if ( GLOBAL_IsGameButtonPressed( 4 ) )
      then
        vGrowth = vGrowth - 1
      end

      local playGrowing = grew
      if( vGrowth > 0 )
      then

        --------------------------------------------------------------------------
        -- On the vine end?
        if( onTip )
        then
          range.z = range.z * Global:gVineInversion()
          pos = UpdateLanternPosOn( range ) 
          -- inside or outside the bubble?
          --if( curVineDist < MIN_VINEDIST )
          --then
            --local growSpeed = ( ( color / MIN_SPEED ) * vGrowth ) + ffBoost
            local growSpeed = 4.0 * vGrowth + ffBoost --magic number about equal to 255.0/(100.0/1.5) which is what it was being set to before
            GLOBAL_sVineGrowSpeed( growSpeed )
            grew = GLOBAL_GrowVine( pos.x, pos.y, pos.z )
            GLOBAL_FOVfromGrowth( 6.25 * growSpeed )
          --else
          --  grew = false
          --  GLOBAL_FOVfromGrowth( 0 )
          --end

          --tell follow cam to move
          camPhiSpeed   = camPhiSpeed + .1
          camThetaSpeed = .4

        --------------------------------------------------------------------------
        -- Not on vine end
        else
          --this line grows the vine in the direction of Aster if he isn't on the end of the vine
          local dir = {}
          dir = VEC3_subtract( AsterPos, vineEnd )
          dir = VEC3_normalize( dir )


          local growSpeed = 4.0 * vGrowth --magic number about equal to 255.0/(100.0/1.5) which is what it was being set to before
          GLOBAL_sVineGrowSpeed( growSpeed )
          grew = GLOBAL_GrowVine( dir.x, dir.y, dir.z )

          ----------------------------------------------------------------------------
          --look at the vine
          local lookAtVine = vec3.subtract(vineEnd, CamHelperData.pos)
          lookAtVine = vec3.normalize(lookAtVine)
          lookAtVine = vec3.multiply(lookAtVine, 8)
          Global:SetCameraViewVector(lookAtVine.x, lookAtVine.y, lookAtVine.z)
        end

        color = MATH_max( color - vGrowth * 0.1, MIN_LIGHT )
      
      --the vine is growing  
      end 


      ----------------------------------------------------------------------------
      -- What if we grew?
      if( grew )
      then

        --------------------------------------------------------------------------
        -- Sound
        --were we already growing?
        if( playGrowing ) then
          --do nothing

        else
          GLOBAL_PlayVineGrowth( "../../Assets/Sounds/vine_growth.mp3",
                                 lastGrew == false )

        end

        lastGrew = true

        --------------------------------------------------------------------------
        -- Patterns
        pattern, segments = GLOBAL_gVinePattern()


		if (Global.FFCollected() > 0)
		then
		
			if( pattern > 0 )
			then
			  if( curPattern == 0 ) 
			  then
				GLOBAL_SpiralSwerveSound( true )
				curPattern = pattern

			  end
				  
			  if( patternTimer > ( MATH_max( 0, PATTERN_TIME - segments * .5 ) ) )
			  then
				patternTimer = 0
				patternIncr = ( patternIncr + 1 ) % OFFVECS.max

				local posPos = VEC3_add( vineEnd, OFFVECS[ patternIncr ] )
				local IS_SPAWNER = 1
				GLOBAL_FireflySpawnMsg( posPos.x, posPos.y, posPos.z, 0, 0, 0, 1, IS_SPAWNER )
				--GLOBAL_CreateEntityAt( ASSETS_XML .. SPAWNERFLY, 
				--                       posPos.x, posPos.y, posPos.z, 0, 0, 0 )
				--GLOBAL_CreateEntityAt( ASSETS_XML .. "entities/e_ParticleFlash.XML", 
				--                       posPos.x, posPos.y, posPos.z, 0, 0, 0 )

			  else
				patternTimer = patternTimer + 1

			  end

			else
			  if( curPattern ~= 0 ) 
			  then
				GLOBAL_SpiralSwerveSound( false )
				curPattern = 0

			  end

			end
			
		end -- if (Global.FFCollected() > 0)

      ----------------------------------------------------------------------------
      -- Or did we not grow?
      else

        --------------------------------------------------------------------------
        -- Sound
        --were we already growing?
        if( playGrowing ) then
          GLOBAL_StopVineGrowth()
          GLOBAL_PlaySound2D( "../../Assets/Sounds/vine_fail.mp3" )

        else
          --do nothing

        end

        if( curPattern ~= 0 ) 
        then
          GLOBAL_SpiralSwerveSound( false )
          curPattern = 0

        end

      end


      ----------------------------------------------------------------------------
      -- Vine ungrowth;
      ----------------------------------------------------------------------------

      local playUngrowing = ungrew
      if( vGrowth < 0 )
      then

        --------------------------------------------------------------------------
        -- On the vine end?
        if( onTip )
        then
          --tell follow cam to move
          camPhiSpeed   = camPhiSpeed + .1
          camThetaSpeed = .4
        end

        color = MATH_min( color - vGrowth * 0.1, MAX_LIGHT )
        ungrew = GLOBAL_UnGrowVine( vGrowth * UNGROW_SPEED )

      end

      ----------------------------------------------------------------------------
      -- Sounds; did we ungrow?
      if( ungrew )
      then
        --were we already ungrowing?
        if( playUngrowing ) then
          --do nothing

        else
          GLOBAL_PlayVineGrowth( "../../Assets/Sounds/vine_ungrowth.mp3",
                                 lastGrew == true )

        end

        lastGrew = false

      --did we not grow?
      else
        --were we already growing?
        if( playUngrowing ) then
          GLOBAL_StopVineGrowth()

        else
          --do nothing

        end

      end


      ----------------------------------------------------------------------------
      -- Vine no growth;
      ----------------------------------------------------------------------------
      
      if( vGrowth == 0 )
      then
        GLOBAL_FOVfromGrowth( 0 )

        -- if we were growing, stop the sound now
        if( grew == true ) or ( ungrew == true )
        then
          GLOBAL_StopVineGrowth()
          grew = false
          ungrew = false

        end

      end


      ----------------------------------------------------------------------------
      -- Aster's Position and Movement
      ----------------------------------------------------------------------------
      
      --[
      if( myData.endSequence and myData.forcedSequence ) then
        myData.forcedSequence = nil
        
      end
      --]]
      
      local velX = 0
      local velY = 0
      local velZ = 0
      if ( not UTIL_Approx( rangeMagSq, 0 ) ) and
         ( myData.forcedSequence == nil )
      then
        moving        = true
        camThetaSpeed = camThetaSpeed + .4
        range         = VEC3_multiply( range, ASTER_SPEED )

        if( onGround ) then
          AsterVel.x = range.x
          AsterVel.z = range.z
        else
          AsterVel.x = range.x * AIR_CONTROL
          AsterVel.z = range.z * AIR_CONTROL
        end
      else
        if( AsterVel.x * AsterVel.x + AsterVel.z * AsterVel.z > .01 )
        then
          moving = true
          AsterVel.x = AsterVel.x * ASTER_STOP
          AsterVel.z = AsterVel.z * ASTER_STOP
        end
      end

      if( moving ) then
        velX = AsterVel.x * SIDE.x + AsterVel.y * UP.x - AsterVel.z * FORWARD.x
        velY = AsterVel.x * SIDE.y + AsterVel.y * UP.y - AsterVel.z * FORWARD.y
        velZ = AsterVel.x * SIDE.z + AsterVel.y * UP.z - AsterVel.z * FORWARD.z
      end

      -------------------------------------------------------------
      -- Am I on the vine tip?
      tipWait = MATH_max( tipWait - elapsed, 0)
      if( onTip or (tipWait == 0 and VEC3_magnitudeSq( VEC3_subtract( vineEnd, AsterPos ) ) < TIP_FUDGE) ) then
        onTip     = true                         -- I'm on the tip now
        tipWait   = TIP_DELAY                    -- Do I want to get off?
        if(jumpWait == JUMP_DELAY) then          -- Yes, I just jumped
          onTip = false
          onGround = false                     
        elseif( moving and vGrowth == 0 ) then
          local vineDir = GLOBAL_gVineGrowDir()
          local dot = vineDir.x * velX + vineDir.y * velY + vineDir.z * velZ
          if( dot < -ASTER_SPEED * .8 ) then     -- Yes, I am walking down the vine
            onTip = false
          end
        end
      end

      if( onTip ) then                           -- I'm staying on the tip
        pos = UpdateLanternPosOn( range )
        ENTITY_SHR_sVelocity( Aster, 0, 0, 0 )
      else
        pos = UpdateLanternPosOff( range )       -- I'm not on the tip
        if( moving ) then
          ENTITY_SHR_sVelocity( Aster, velX, velY, velZ )
        end
      end


      ----------------------------------------------------------------------------
      -- If Aster is moving, prevent player from fucking up zoom
      ----------------------------------------------------------------------------

      if( moving or onTip )
      then
        GLOBAL_ClampCameraZoomMinMax( MIN_CAM_DIST, MAX_CAM_DIST )
        GLOBAL_ClampCameraPhiMinMax( -PI_OVER_SIX, PI_OVER_SIX )

      end


      ----------------------------------------------------------------------------
      -- Updates follow cam speed if we're moving
      ----------------------------------------------------------------------------

      if( not UTIL_Approx( rangeMagSq, 0 ) ) then
        camThetaSpeed = camThetaSpeed + .6

      end


      ----------------------------------------------------------------------------
      -- Check if the camera is being centered
      ----------------------------------------------------------------------------

      if( centerButtonFrameDelay ) -- button 4
      then
        centerButtonFrameDelay = false
        pos.x = 0
        pos.y = 0
        pos.z = -1
        lPosLocal.x = 0
        lPosLocal.y = 0
        lPosLocal.z = -1
      end

      if( Global:IsGameButtonPressed( 3 ) ) -- button 4
      then
        centerButtonFrameDelay = true
      end

    end -- inLookAroundMode


    ----------------------------------------------------------------------------
    -- Get the correctly oriented lantern vector
    ----------------------------------------------------------------------------

    if( onTip and not inLookAroundMode )
    then
      lVecFinal.x = pos.x
      lVecFinal.y = pos.y
      lVecFinal.z = pos.z
    else
      lVecFinal.x = pos.x * SIDE.x + pos.y * UP.x - pos.z * FORWARD.x
      lVecFinal.y = pos.x * SIDE.y + pos.y * UP.y - pos.z * FORWARD.y
      lVecFinal.z = pos.x * SIDE.z + pos.y * UP.z - pos.z * FORWARD.z
    end


    ----------------------------------------------------------------------------
    -- Aster's orientation
    ----------------------------------------------------------------------------

    local orient_side    = VEC3_normalize(VEC3_cross(lVecFinal, UP))
    local orient_forward = VEC3_cross(UP, orient_side)
    --I must be calculating the forward and side vectors backwards!?!?
    ENTITY_SHR_sRotationMatrix( Aster, -orient_side.x, -orient_forward.x, UP.x,
                                       -orient_side.y, -orient_forward.y, UP.y,
                                       -orient_side.z, -orient_forward.z, UP.z )


    ----------------------------------------------------------------------------
    -- Tell camera about our orientation
    ----------------------------------------------------------------------------
    
    local camOrientX = lVecFinal.x
    local camOrientY = lVecFinal.y
    local camOrientZ = lVecFinal.z
    ENTITY_SPT_GiveNumber( Camera, "x", camOrientX )
    ENTITY_SPT_GiveNumber( Camera, "y", camOrientY )
    ENTITY_SPT_GiveNumber( Camera, "z", camOrientZ )
    ENTITY_SPT_GiveNumber( Camera, "ThetaSpeed", camThetaSpeed )
    ENTITY_SPT_GiveNumber( Camera, "PhiSpeed", camPhiSpeed )
    if( onTip )
    then
      ENTITY_SPT_GiveNumber( Camera, "FollowThreshold", .25 * TWO_PI )
    else
      ENTITY_SPT_GiveNumber( Camera, "FollowThreshold", .01 * TWO_PI )
    end


    ------------------------------------------------------------------------------
    -- Set Aster's walk state
    ------------------------------------------------------------------------------

    if( ( moving ) and ( not onTip ) )
    then
      GLOBAL_sAsterWalkState(MATH_min(rangeMagSq, 1))
    else
      GLOBAL_sAsterWalkState(0)
    end

  end   --GLOBAL_IsInGameMode()


  ------------------------------------------------------------------------------
  -- Set the lantern position
  ------------------------------------------------------------------------------

  lPosInterp         = VEC3_add( lPosInterp, VEC3_multiply( VEC3_subtract( lVecFinal, lPosInterp ), .1 ) )
  local lPosEndFrame = VEC3_multiply( lPosInterp, 2 )
  lPosEndFrame       = VEC3_add( lPosEndFrame, AsterPos )
  lPosEndFrame.x     = lPosEndFrame.x + 2 * UP.x
  lPosEndFrame.y     = lPosEndFrame.y + 2 * UP.y
  lPosEndFrame.z     = lPosEndFrame.z + 2 * UP.z
  ENTITY_SHR_sPosition( ENTITY, lPosEndFrame.x, lPosEndFrame.y, lPosEndFrame.z )
  -- Send lantern position message
  GLOBAL_GameLanternPosition( lPosEndFrame.x, lPosEndFrame.y, lPosEndFrame.z )


  ------------------------------------------------------------------------------
  -- Cam helper; above Aster
  ------------------------------------------------------------------------------
    
  if( myData.TakeCamera and myData.TakeCamera ~= 0.0 )
  then
    GLOBAL_SetCameraTargetObject( ENTITY_GetMyPointer( CamHelper ) )
    myData.TakeCamera = nil
    
  end
  local camPos    = GLOBAL_gCameraPosition()
  local distAbove = VEC3_magnitudeSq( VEC3_subtract( camPos, CamHelperData.pos ) ) * .04 
  distAbove       = UTIL_Clamp( distAbove, 2, 7 )
  local distSide  = UTIL_Clamp( 5 - distAbove, 0, 3 ) * .4

  ENTITY_SHR_sPosition( CamHelper, AsterPos.x + distAbove * UP.x + distSide * SIDE.x,
                                   AsterPos.y + distAbove * UP.y + distSide * SIDE.y,
                                   AsterPos.z + distAbove * UP.z + distSide * SIDE.z )


  ------------------------------------------------------------------------------
  -- Tell Aster he's on the tip
  ------------------------------------------------------------------------------
    
  if( onTip == true ) then
    ENTITY_SPT_GiveNumber( Aster, "onVineEnd", 1.0 )
    if( not GLOBAL_IsDocking() )
    then
      ENTITY_SHR_sPosition( Aster, vineEnd.x, vineEnd.y, vineEnd.z )
    end
    
  else
    ENTITY_SPT_GiveNumber( Aster, "onVineEnd", 0.0 )
    
  end
  
  
  ------------------------------------------------------------------------------
  -- We just grew, so set the light count now
  ------------------------------------------------------------------------------
  
  PLAYER_sLightCount( PLAYER, color )
  color = color / MAX_LIGHT
  ENTITY_VIS_SetLightColor( ENTITY, color, color, color )
  GLOBAL_LanternMessage( MAX_VINEDIST / 100, MIN_VINEDIST / MAX_VINEDIST,
                         color, myData.fadeAmount )--0 )--
  

end
