-- Emitter
----------------------------------------------------------
-- Last update: April 21st, 2009 by Renaud

-- the emitter is now time based, the update call takes a time parameter which
-- is the last time since the update was called, if this time is not calculated, just pass in 33.33 to the update call
-- KNOWN ISSUES:
-- 
-- 
-- 
----------------------------------------------------------

-- PUBLIC FUNCTIONS
-- 

-- global gTime
-- property pTimeLastUpdate

property pCastLibName

property pEmitterData
property pNumEmmitions
property pShootTime

property pPause

property pParticles

property pPosition
property pScroll

property pMembers

property pSpriteManager

property pInfinit

property pLocZ

property pGravityPointLoc

property pLastUpdate

property pLoaded

property pName

property pIterateRotation

on new me, spriteManager --, fileName, directory, spriteManager, castLibName-- data, spriteManager, directory, castLibName -- where we store the particle images
  pSpriteManager = spriteManager
  
  pParticles = []
  
  --  pTimeLastUpdate = gTime.getTime()
  
  return me
end

on setEmitterData me, data, directory -- directory is used if we load them externaly ( this is so that we know where to get the images used with the file )
  
  if me.dataValid( data ) = false then
    me.debugAlert("Data Invalid:" && directory)
    return
  end if
  
  pEmitterData = data
  
  -- load the images that the emitter will use
  pMembers = []
  if directory = void then
    -- images must be in the cast already
    repeat with a = 1 to pEmitterData.frameLinks.count
      -- we assum the members have already been imported
      if member(pEmitterData.frameLinks[a], "particles").type = #bitmap then
        pMembers.append(member(pEmitterData.frameLinks[a], "particles"))
      else
        me.debugAlert(pEmitterData.frameLinks.count && "is not found in cast particles, use utility function to load them in.")
      end if
    end repeat
  else
    -- link to images that we need
    repeat with a = 1 to pEmitterData.frameLinks.count
      -- I assume the address is relative to the address of the .emi file
      imgAddress = macTheAddress( directory & pEmitterData.frameLinks[a] )
      if baFileExists(imgAddress) = false then
        me.debugAlert("Cannot find" && imgAddress)
      else
        
        -- if the image has already been imported use it (if you update the image, then you must also delete it from your particles cast so that it updates)
        if member(pEmitterData.frameLinks[a], "particles") = void then
          found = false
        else if member(pEmitterData.frameLinks[a], "particles").type = #empty then
          found = false
        else
          found = true
        end if
        if found then
          pMembers.append(member(pEmitterData.frameLinks[a], "particles"))
        else
          newMem = new(#bitmap, castlib("Particles"))
          newMem.importFileInto( imgAddress ,[ #trimWhiteSpace:False ,#remapImageToStage: FALSE ])
          newMem.name = pEmitterData.frameLinks[a]
          pMembers.append(newMem)
        end if
        
      end if
    end repeat
  end if
  if pMembers.count = 0 then
    return
  end if
  
  
  pLastUpdate = the milliseconds
  pShootTime = the milliseconds
  
  pNumEmmitions = pEmitterData.numEmmitions
  if pNumEmmitions = 0 then
    pInfinit = true
  else
    pInfinit = false
  end if
  
  pPosition = point(655, 465)
  pScroll = point(0,0)
  
  pLocZ = 500
  
  -- new data we add...so we dont get script errors
  if pEmitterData.getAProp(#gravityPointLoc)=void then
    pEmitterData.addprop(#gravityPointLoc,point(0.0,0.0))
  end if
  if pEmitterData.getAProp(#gravityPointStrenth)=void then
    pEmitterData.addprop(#gravityPointStrenth,0.0)
  end if
  if pEmitterData.getAProp(#terminateAtPoint)=void then
    pEmitterData.addprop(#terminateAtPoint,true)
  end if
  if pEmitterData.getAProp(#terminateTreshold)=void then
    pEmitterData.addprop(#terminateTreshold,20)
  end if
  if pEmitterData.getAProp(#IterateRotation)=void then
    pEmitterData.addprop(#IterateRotation,true)
  end if
  
  pGravityPointLoc = point(0,0)
  
  pLoaded = true
end

on loadFromFile me, fileName, directory
  pName = fileName
  if castLib("particles") = void then
    me.debugAlert("You need a particles cast to use the emitter class")
    return
  end if
  
  fileAddress = macTheAddress(directory & fileName & ".em3")
  if baFolderExists(directory) = false then
    me.debugAlert("Parent_Emitter, folder missing:" & directory)
    return
  else if baFileExists(fileAddress) = false then
    me.debugAlert("Parent_Emitter, file missing:" & fileAddress)
    return
  else
    me.setEmitterData(value(getFile(fileAddress)), directory)
  end if
end

on loadFromMem me, textMemName
  pName = textMemName & ".em3"
  
  if castLib("particles") = void then
    me.debugAlert("You need a particles cast to use the emitter class")
    return
  else if member(textMemName & ".em3", castlib("particles")) = void then
    me.debugAlert(textMemName & ".em3" && "is not a member or is not in the particles cast")
    return
  else if member(textMemName & ".em3", castlib("particles")).type <> #field then
    me.debugAlert(textMemName & ".em3" && "is not a field member")
    return
  end if
  textMEmber = member(textMemName & ".em3", castlib("particles"))
  
  me.setEmitterData(value(textMember.text))
end

on dataValid me, data
  
  if NOT(ilk(data)=#proplist) then
    return false
  end if
  
  -- dont have to check all the props...just make sure its the right type of data
  prop1 = getAProp(data,#blendcues)
  if prop1 = void then
    return false
  end if
  prop2 = getAProp(data,#gravityPointLoc)
  if prop2 = void then
    return false
  end if
  
  return true
end

on setGravityPointLoc me, theLoc
  pGravityPointLoc = theLoc
  
  pEmitterData.gravityPointLoc = theLoc
  
  repeat with a = 1 to pParticles.count
    pParticles[a].setGravityPointLoc(theLoc)
  end repeat
end

on reset me
  pNumEmmitions = pEmitterData.numEmmitions
end

on isPlaying me
  if pPause then
    return false
  end if
  if pInfinit then
    return true
  end if
  if pNumEmmitions > 0 then
    return true
  else
    return false
  end if
  return true
end

on play me
  if NOT(pLoaded) then
    me.debugAlert("Emitter object not loaded properly.")
    return
  end if
  pPause = false
end

on stop me
  pPause = true
end

on setPosition me, pos
  pPosition = pos
end
on setScroll me, scroll
  pScroll = scroll
  
  -- scroll particals
  repeat with a = 1 to pParticles.count
    pParticles[a].setScroll(pScroll)
  end repeat
end

on setLocZ me, locZ
  pLocZ = locZ
end


on shootParticle me, particleNumber, particlesPerEmmitions
  if NOT(pLoaded) then
    me.debugAlert("Emitter object not loaded properly.")
    return
  end if
  
  -- determin life time
  variance = integer(pEmitterData.lifeHi - pEmitterData.lifelo)
  if variance < 0 then
    lifeTime = pEmitterData.lifehi + random(variance*-1)
  else if variance = 0 then
    lifeTime = pEmitterData.lifeLo
  else
    lifeTime = pEmitterData.lifeLo + random(variance)
  end if
  
  
  -- determin blend cues
  blendCues = []
  repeat with a = 1 to pEmitterData.blendCues.count
    
    if pEmitterData.blendCues[a].hi = pEmitterData.blendCues[a].lo then
      blend = pEmitterData.blendCues[a].lo
    else if pEmitterData.blendCues[a].hi > pEmitterData.blendCues[a].lo then
      variance = integer(pEmitterData.blendCues[a].hi - pEmitterData.blendCues[a].lo + 1)
      blend = pEmitterData.blendCues[a].lo-1 + random(variance)
    else
      variance = integer(pEmitterData.blendCues[a].lo - pEmitterData.blendCues[a].hi + 1)
      blend = pEmitterData.blendCues[a].hi-1 + random(variance)
    end if
    
    -- get time
    time = lifeTime * pEmitterData.blendCues[a].lifescale
    -- add it to the blend cues
    blendCues.append([#time:time,#blend:blend])
  end repeat
  
  -- determin scale cues
  scaleCues = []
  repeat with a = 1 to pEmitterData.scaleCues.count
    -- get random scale
    
    if pEmitterData.scaleCues[a].hi = pEmitterData.scaleCues[a].lo then
      scale = pEmitterData.scaleCues[a].lo
    else if pEmitterData.scaleCues[a].hi > pEmitterData.scaleCues[a].lo then
      variance = pEmitterData.scaleCues[a].hi - pEmitterData.scaleCues[a].lo
      scale = pEmitterData.scaleCues[a].lo + random(integer(variance*100.0))/100.0
    else
      variance = pEmitterData.scaleCues[a].lo - pEmitterData.scaleCues[a].hi
      scale = pEmitterData.scaleCues[a].hi + random(integer(variance*100.0))/100.0
    end if
    
    -- get time
    time = lifeTime * pEmitterData.scaleCues[a].lifescale
    -- add it to the scale cues
    scaleCues.append([#time:time,#scale:scale])
  end repeat
  
  -- determin frame cues
  frameCues = []
  repeat with a = 1 to pEmitterData.frameCues.count
    if pEmitterData.frameCues[a].hi = pEmitterData.frameCues[a].lo then
      frame = pEmitterData.frameCues[a].lo
    else if pEmitterData.frameCues[a].hi > pEmitterData.frameCues[a].lo then
      variance = pEmitterData.frameCues[a].hi - pEmitterData.frameCues[a].lo + 1
      frame = pEmitterData.frameCues[a].lo-1 + random(variance)
    else
      variance = pEmitterData.frameCues[a].lo - pEmitterData.frameCues[a].hi + 1
      frame = pEmitterData.frameCues[a].hi-1 + random(variance)
    end if
    -- get time
    time = lifeTime * pEmitterData.frameCues[a].lifescale
    -- add it to the frame cues
    frameCues.append([#time:time,#frame:frame])
  end repeat
  
  -- determin velocity
  if pEmitterData.spread > 0 then
    if pEmitterData.IterateRotation AND particleNumber>0 AND particlesPerEmmitions>0 then
      -- particleNumber, particlesPerEmmitions
      angle = pEmitterData.angle + pEmitterData.spread * float(particleNumber/particlesPerEmmitions)--float(pEmitterData.angle + random(pEmitterData.spread) - pEmitterData.spread/2)
    else
      angle = float(pEmitterData.angle + random(pEmitterData.spread) - pEmitterData.spread/2)
    end if
  else
    angle = float(pEmitterData.angle)
  end if
  radAngle = angle * PI / 180.0
  variance = pEmitterData.speedhi - pEmitterData.speedlo
  if variance < 0 then
    speed = pEmitterData.speedhi + random(integer((variance*-1)*1000)) / 1000.0
  else if variance = 0 then
    speed = pEmitterData.speedLo
  else
    speed = pEmitterData.speedLo + random(integer(variance*1000)) / 1000.0
  end if
  oX = cos(radAngle) * float(speed)
  oY = -sin(radAngle) * float(speed)
  velocity = point(oX, oY)
  
  
  -- determin rotation force
  variance = integer(pEmitterData.rotationForceHi - pEmitterData.rotationForcelo)
  if variance < 0 then
    rotationForce = pEmitterData.rotationForcehi + random(variance*-1)
  else if variance = 0 then
    rotationForce = pEmitterData.rotationForceLo
  else
    rotationForce = pEmitterData.rotationForceLo + random(variance)
  end if
  
  
  particleData = [#lifeTime:lifeTime,\
  #members:pMembers,\
  #position:pPosition,\
  #scroll:pScroll,\
  #blendCues:blendCues,\
  #scaleCues:scaleCues,\
  #frameCues:frameCues,\
  #rotationForce:rotationForce,\
  #rotationFriction:pEmitterData.rotationFriction,\
  #velocity:velocity,\
  #friction:pEmitterData.friction,\
  #gravity:pEmitterData.gravity,\
  #ink:pEmitterData.ink,\
  #locZ:pLocZ,\
  #gravityPointLoc:pEmitterData.gravityPointLoc,\
  #gravityPointStrenth:pEmitterData.gravityPointStrenth,\
  #terminateAtPoint:pEmitterData.terminateAtPoint,\
  #terminateTreshold:pEmitterData.terminateTreshold,\
  #animSpeed:pEmitterData.animSpeed,\
  #loopFrames:pEmitterData.loopFrames,\
  #startRotation:pEmitterData.startRotation,\
  #rotationSpread:pEmitterData.rotationSpread]
  
  --  ATTENTION!!!     Check if the var=false statement is slow
  var = new(script"Parent_Particle_V3", particleData, pSpriteManager)
  if NOT(var=false) then
    pParticles.append( var )
    
    return var
  end if
end



on update me, timeLaps -- time since last update
  if NOT(pLoaded) then
    return true
  end if
  
  -- ADD NEW PARTICLES
  if NOT(pPause) then
    if pNumEmmitions > 0 OR pInfinit then
      if pShootTime+pEmitterData.emitterRate < the milliseconds then
        pShootTime = the milliseconds
        
        repeat with b = 1 to pEmitterData.particlePerEmmition
          me.shootParticle(b, pEmitterData.particlePerEmmition)
        end repeat
        
        pNumEmmitions = pNumEmmitions - 1
      end if
    end if
  end if
  
  -- UPDATE EMMITED PARTICLES
  repeat with a = 1 to pParticles.count
    var = pParticles[a].update(timeLaps)
    if var = false then
      pParticles[a].delete()
      pParticles.deleteAt(a)
    end if
  end repeat
  
  
  if pNumEmmitions = 0 and pInfinit = false then
    if pParticles.count = 0 then
      return false
    end if
  end if
  
  return true
  
end

on setEmitterRate me, rate
  pEmitterData.emitterRate = rate
end

on deleteParticles me
  if pLoaded then
    repeat with a = 1 to pParticles.count
      var = pParticles[a].delete()
    end repeat
    pParticles = []
  end if
end

on delete me
  if pLoaded then
    repeat with a = 1 to pParticles.count
      var = pParticles[a].delete()
    end repeat
    pParticles = []
  end if
end

on debugAlert me, str
  if getAuthor() = true then
    alert(str)
  end if
end

-- copy this function to a movie script and call it in the message window
on importParticleFile
  
  OFN_ALLOWMULTISELECT = 512
  OFN_RETURNASLIST = 256
  OFN_EXPLORER = 524288
  OFN_NOCHANGEDIR = 0 -- 8
  fileList = baGetFilename( "open", getMoviePath(), "", "Emitter3|*.em3", OFN_ALLOWMULTISELECT+OFN_RETURNASLIST+OFN_EXPLORER+OFN_NOCHANGEDIR, "Open an Emitter file", false, 100, 100 )
  
  -- need to import all of these one by one
  repeat with a = 1 to fileList.count
    data = value(getFile(fileList[a]))
    
    -- extract name of file
    fileName = fileList[a]
    -- remove extension
    offs = offset(fileName, "\")
    if offs > 0 then
      fileName = chars(fileName, 1, offs-1)
    end if
    -- get where path ends
    repeat with c = 1 to length(fileName)
      if chars(fileName, c, c) = "\" then
        theIndex = c
      end if
    end repeat
    -- get the path
    folderAddress = chars(fileName, 1, theIndex)
    -- extract path
    if theIndex > 0 then
      fileName = chars(fileName, theIndex+1, length(fileName))
    end if
    --
    if member(fileName, castLib("Particles")) <> void then
      member(fileName, castLib("Particles")).erase()
    end if
    newMem = new(#field, castLib("Particles"))
    newMem.text = string(data)
    newMem.name = fileName
    
    
    -- import the images the emitter uses
    -- PLZ NOTE, images with the relative path to the emiter overwrite each other
    frames = data.frameLinks
    repeat with d = 1 to frames.count
      if member(frames[d], castLib("Particles")) = void then
        memExists = false
      else if member(frames[d], castLib("Particles")).type <> #bitmap then
        alert("member:" && frames[d] && "already exists but it is not a member, you should delete it and run this again, because this member may get referenced by the emitter")
        memExists = false
      else
        memExists = true
      end if
      fileAddress = folderAddress & frames[d]
      if memExists then
        newMem = member(frames[d], castLib("Particles"))
      else
        newMem = new(#bitmap, castLib("Particles"))
      end if
      newMem.importFileInto( fileAddress ,[ #trimWhiteSpace:False ,#remapImageToStage: FALSE ])
      newMem.regPoint = point(newMem.width/2,newMem.height/2)
      newMem.name = frames[d]
    end repeat
    
  end repeat
end


-- this function loads the emi file and creates a em3 version of it in the same area
-- IMPORTANT, all this does is get rid of the path attached to the frame links, which are suppose to be relative paths
-- ask me about details, reno
on createEm3Files
  OFN_ALLOWMULTISELECT = 512
  OFN_RETURNASLIST = 256
  OFN_EXPLORER = 524288
  OFN_NOCHANGEDIR = 0 -- 8
  fileList = baGetFilename( "open", getMoviePath(), "", "Emitter|*.emi", OFN_ALLOWMULTISELECT+OFN_RETURNASLIST+OFN_EXPLORER+OFN_NOCHANGEDIR, "Open an Emitter file", false, 100, 100 )
  repeat with a = 1 to fileList.count
    
    -- 
    pos = offset(".emi", fileList[a])
    if pos > 0 then
      pathWithoutEx = chars(fileList[a], 1, pos-1)
    end if
    
    data = value(getFile(fileList[a]))
    
    repeat with b = 1 to data.frameLinks.count
      address = data.frameLinks[b]
      
      -- get the file name
      repeat with c = 1 to length(address)
        if chars(address, c, c) = "\" then
          theIndex = c
        end if
      end repeat
      if theIndex > 0 then
        fileName = chars(address, theIndex+1, length(address))
        data.frameLinks[b] = fileNAme
      end if
      
    end repeat
    
    setFile(pathWithoutEx & ".em3", string(data))
    
  end repeat
end
