Guest User

SnowflakeAnimator.lua with snowflake spawn under screen

a guest
Dec 22nd, 2015
2,038
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.59 KB | None | 0 0
  1. --[[
  2.    |--------------------------------------------|
  3.    |       TheIcyStar's snowing script          |
  4.    |            Realistic  version              |
  5.    |--------------------------------------------|
  6.    
  7.             !NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!
  8.             !NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!
  9.                 This script has been modified to handle particles spawning at the bottom of the screen.
  10.                     Only use this if the particles are to flow UPWARD. (FallingSpeed is negative)
  11.                                                 Changes on lines 201, 202
  12.             !NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!
  13.             !NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!!NOTICE!
  14.    
  15.     BTC: 14KNUpohrrMH4sEhcUkBJ9iERjmKGGAHiJ
  16.    
  17.     Part of IcyStorm V2
  18.    
  19.     All variables are in Snow.ini
  20.     Does not support dynamic variables - Because of performance optimizations.  (well, if you make an UpdateVariables() function, I guess that could work. I'm not doing that though.)
  21.    
  22.     How this works:
  23.     On Initialize,
  24.         get all variables
  25.         make a table of meters availible to use
  26.     On update,
  27.         for every particle on screen:
  28.             Grab the particle's data (distance, wind speed, location, etc)
  29.             if Wind needs rerolling, reroll wind
  30.             calculate sway
  31.             SET X
  32.             SET Y
  33.             Update stored values
  34.             check for particle removal
  35.                 if the lifetime is about to end, start fading
  36.                 if the lifetime is reached, remove particle
  37.                 if the particle reached the screen's bounds, remove particle
  38.         When it's time to add a particle
  39.             calculate all variables based on one "Distance" variable (makes this entire thing feel WAAAAY less flat.)
  40.             Calculate value wind that will be used
  41.             store generated variables and put particle into "SnowflakesInUse"
  42.            
  43.     Things to note:
  44.     The spawning rate is based on a particle's lifetime. I can NOT get the spawnrate to be proportional to the time a particle gets to the bottom of the screen
  45.     because of the randomness involved of the particle's speeds.
  46.        
  47. --]]
  48. local SnowflakesAvailible = {}
  49. local SnowflakesInUse = {}
  50. local SnowflakeData = {}
  51. local Tints = {}
  52. local TintsActive = false
  53. local WindActive = false
  54. --Maybe this will fix some errors?
  55. --local SpawnRate = 1000
  56.  
  57. --[[ easing function
  58. -- t = time == how much time has to pass for the tweening to complete
  59. -- b = begin == starting value
  60. -- c = change == ending - beginning
  61. -- d = duration == running time. How much time has passed *right now*
  62. --Icy's note: function found on github, line 54-61: https://github.com/EmmanuelOga/easing/blob/master/lib/easing.lua
  63. --            swap Time and duration, or you'll be getting infinity whem all you need is a ten. took me a day of pain to figure out
  64. --]]
  65. function InOutQuad (Time,begin,change,duration)
  66.     Time = Time / duration * 2
  67.     if Time < 1 then return change / 2 * math.pow(Time, 2) + begin end
  68.     return -change / 2 * ((Time - 1) * (Time - 3) - 1) + begin
  69. end
  70. --]]
  71.  
  72. --Aquires variables and skins on refresh
  73. function Initialize()
  74.     UpdateRate = tonumber(SKIN:GetVariable("UpdateRate"))
  75.     NumSnowflakes = tonumber(SKIN:GetVariable("NumSnowflakes"))
  76.     Lifetime = tonumber(SKIN:GetVariable("Lifetime"))
  77.     WorkAreaX = tonumber(SKIN:GetVariable("TotalScreenAreaX"))
  78.     WorkAreaY = tonumber(SKIN:GetVariable("TotalScreenAreaY"))
  79.     NumImageTints = tonumber(SKIN:GetVariable("NumImageTints"))
  80.     if NumImageTints ~= 0 then
  81.         for i=1,NumImageTints do
  82.             Tints[i] = SKIN:GetVariable("ImageTint"..i)
  83.         end
  84.         TintsActive = true
  85.     end
  86.     MinRot = tonumber(SKIN:GetVariable("MinRot"))
  87.     MaxRot = tonumber(SKIN:GetVariable("MaxRot"))
  88.     StartX = tonumber(SKIN:GetVariable("StartX"))
  89.     EndX = tonumber(SKIN:GetVariable("EndX"))
  90.     StartY = tonumber(SKIN:GetVariable("StartY"))
  91.     EndY = tonumber(SKIN:GetVariable("EndY"))
  92.     MinTransparency = tonumber(SKIN:GetVariable("MinTransparency"))
  93.     MaxTransparency = tonumber(SKIN:GetVariable("MaxTransparency"))
  94.     MinSize = tonumber(SKIN:GetVariable("MinSize"))
  95.     MaxSize = tonumber(SKIN:GetVariable("MaxSize"))
  96.     MinFallSpeed = tonumber(SKIN:GetVariable("MinFallSpeed"))
  97.     MaxFallSpeed = tonumber(SKIN:GetVariable("MaxFallSpeed"))
  98.     MinSway = tonumber(SKIN:GetVariable("MinSway"))
  99.     MaxSway = tonumber(SKIN:GetVariable("MaxSway"))
  100.     MinSwayTime = tonumber(SKIN:GetVariable("MinSwayTime"))
  101.     MaxSwayTime = tonumber(SKIN:GetVariable("MaxSwayTime"))
  102.     DampSwayNum = tonumber(SKIN:GetVariable("DampSway"))
  103.     if DampSwayNum ~= 0 then
  104.         DampActive = true
  105.     end
  106.     MinWindSpeed = tonumber(SKIN:GetVariable("MinWindSpeed"))
  107.     MaxWindSpeed = tonumber(SKIN:GetVariable("MaxWindSpeed"))
  108.     if MinWindSpeed ~= MaxWindSpeed then
  109.         WindActive = true
  110.     elseif MinWindSpeed == MaxWindSpeed and MinWindSpeed ~= 0 then
  111.         WindActive = true
  112.         WindStable = true
  113.     end
  114.     WindType = tonumber(SKIN:GetVariable("WindType"))
  115.     RerollChance = tonumber(SKIN:GetVariable("RerollChance"))
  116.     DebugMode = tonumber(SKIN:GetVariable("DebugMode"))
  117.    
  118.     --Aquires Meters
  119.     for i=1,NumSnowflakes do
  120.         local meter = SKIN:GetMeter("MeterSnowflake"..i)
  121.         SnowflakesAvailible[i] = meter
  122.         if not SnowflakesAvailible[i] then
  123.             print("ERROR! Snowflake meter number "..i.." not found! Change names or adjust the variable NumSnowflakes")
  124.         end
  125.     end
  126.    
  127.     --Variables used for timing and other things
  128.     fps = 1000/UpdateRate
  129.     TotalLifetimeFrames = Lifetime*fps
  130.     SpawnRate = math.ceil(TotalLifetimeFrames/NumSnowflakes)--it's fine to set spawnrate to another value. However, this script can only spawn how many snowflakes are availible to use.
  131. end
  132.  
  133.  
  134. local timer = 1 --used for snowflake spawn delays
  135. function Update()
  136.     --================--
  137.     --snowflake moving--
  138.     --================--
  139.     for i=1,#SnowflakesInUse do
  140.         local meter = SnowflakesInUse[i]
  141.         if meter then
  142.             local meterName = meter:GetName()
  143.             local x = SnowflakeData[meterName.."X"]
  144.             local y = SnowflakeData[meterName.."Y"]
  145.             local StartingX = SnowflakeData[meterName.."StartingX"]
  146.             local LifeFrames = SnowflakeData[meterName.."LifeFrames"]
  147.             local FallSpeed = SnowflakeData[meterName.."FallSpeed"]
  148.             local Sway = SnowflakeData[meterName.."Sway"]
  149.             local SwayTime = SnowflakeData[meterName.."SwayTime"]
  150.             local SwayInterval = SnowflakeData[meterName.."SwayInterval"]
  151.             local WindSpeed = SnowflakeData[meterName.."WindSpeed"]
  152.             local TotalWind = SnowflakeData[meterName.."TotalWind"]
  153.            
  154.             --wind rerolls 10 times per second
  155.             if WindType == 1 then
  156.                 if timer%math.floor(fps/10) == 0 then
  157.                     local roll = math.random(0,100)
  158.                     if roll <= RerollChance then
  159.                         WindSpeed = math.random(MinWindSpeed,MaxWindSpeed)
  160.                     end
  161.                 end
  162.             end
  163.            
  164.             --sway
  165.             local SwayPos = 0 --temp
  166.             if MinSway ~= 0 and MaxSway ~= 0 then
  167.                 --OH MAN I AM NOT GOOD WITH MATHEMATIC PLS TO HELP
  168.                 if SwayInterval <= SwayTime/2 then
  169.                     SwayPos = InOutQuad(SwayInterval,StartingX+Sway,-Sway*2,SwayTime/2)
  170.                 else
  171.                     SwayPos = InOutQuad(SwayInterval-(SwayTime/2),StartingX-Sway,Sway*2,SwayTime/2)
  172.                 end
  173.                
  174.                 if SwayInterval < SwayTime then --resetting sway interval
  175.                     SwayInterval = SwayInterval + 1
  176.                 else
  177.                     SwayInterval = 0
  178.                 end
  179.                 SnowflakeData[meterName.."SwayInterval"] = SwayInterval
  180.             end
  181.             SwayPos = math.floor(SwayPos)
  182.             --wind
  183.             if WindActive then
  184.                 TotalWind = TotalWind + WindSpeed
  185.             end
  186.            
  187.             --(x value calculation)
  188.             x=math.floor(x+SwayPos+TotalWind)
  189.             meter:SetX(x)
  190.     --      SnowflakeData[meterName.."X"]
  191.            
  192.             --(y value calculation)
  193.             y = y + FallSpeed
  194.             meter:SetY(y)
  195.             SnowflakeData[meterName.."Y"] = y
  196.            
  197.             --stored value updating
  198.             SnowflakeData[meterName.."SwayInterval"] = SwayInterval
  199.             SnowflakeData[meterName.."TotalWind"] = TotalWind
  200.            
  201.             --removal based on screen size
  202.             if y > WorkAreaY or x > WorkAreaX*2 then
  203.         --      table.remove(SnowflakesInUse,i) -- <<< removed to allow particle spawning under the screen
  204.         --      SnowflakesAvailible[#SnowflakesAvailible+1] = meter -- <<< removed to allow particle spawning under the screen
  205.                
  206.             --[removal based on lifetime
  207.             --lifeframes = current duration of particle's life in frames
  208.             --TotalLifetimeFrames = when the particle dies
  209.             elseif LifeFrames >= TotalLifetimeFrames then
  210.                 table.remove(SnowflakesInUse,i)
  211.                 SnowflakesAvailible[#SnowflakesAvailible+1] = meter
  212.                
  213.             elseif LifeFrames > (TotalLifetimeFrames*0.85) then --fade out
  214.                 --anti mess
  215.                 local Life = (LifeFrames-(TotalLifetimeFrames*0.75))
  216.                 local Teim = (TotalLifetimeFrames-(TotalLifetimeFrames*0.75))
  217.                 --
  218.                 local transparency = ((Teim-Life)/Teim)*256-1
  219.                
  220.                 if TintsActive then
  221.                     SKIN:Bang("!SetOption",meterName,"ImageTint",SnowflakeData[meterName.."Tint"]..","..transparency)
  222.                 else
  223.                     SKIN:Bang("!SetOption",meterName,"ImageTint","255,255,255,"..transparency)
  224.                 end
  225.                
  226.                 SnowflakeData[meterName.."LifeFrames"] = LifeFrames + 1
  227.             else
  228.                 SnowflakeData[meterName.."LifeFrames"] = LifeFrames + 1
  229.             end --you know what? I really don't like how this removal system works. I can't really calculate the correct spawnrate based on the random intervals of snowflakes reaching
  230.             --]]--the bottom of the screen. I have to find some crappy ballance between having slow snowflakes fading out in the middle of the screen and the unused numebr of snowflakes.
  231.            
  232.             --[debug
  233.             if DebugMode ~= 0 then --3 less calculations from this. woooow so helpfulll /s
  234.                
  235.                 if DebugMode == 1 then
  236.                     print("OnScreen: "..#SnowflakesInUse.."/ Availible: "..NumSnowflakes)
  237.                 elseif DebugMode == 2 then
  238.                     if meterName == "MeterSnowflake1" then
  239.                         print("X:"..x.." Y:"..y.." LifeFrames:"..LifeFrames.." FallSpeed:"..FallSpeed.." Sway:"..Sway.." TimeToSway:"..SwayTime.." SwayInterval:"..SwayInterval.." WindSpeed:"..WindSpeed.." TotalWind:"..TotalWind)
  240.                     end
  241.                 elseif DebugMode == 3 then
  242.                     print("OnScreen: "..#SnowflakesInUse.."/ Availible: "..NumSnowflakes.." SpawnRateInFrames:"..SpawnRate.." TotalLifetimeFrames"..TotalLifetimeFrames.." FPS:"..1000/UpdateRate)
  243.                 end
  244.             end
  245.             --]]
  246.            
  247.         end
  248.     end
  249.    
  250.    
  251.    
  252.     --==================--
  253.     --Snowflake spawning--
  254.     --==================--
  255.     if timer%SpawnRate == 0 then
  256.         if #SnowflakesAvailible ~= 0 then
  257.             local meter = SnowflakesAvailible[1]
  258.             local meterName = meter:GetName()
  259.             table.remove(SnowflakesAvailible,1)
  260.             local x = 0 -- two temporary --
  261.             local y = 0 --    values     --
  262.            
  263.             --Calculating values based on distance
  264.             local Distance = (math.random(0,100))/100 --distance is linear, what would happen if these values were logarithmic?
  265.             local Size = MinSize+(Distance*(MaxSize-MinSize))
  266.             local FallSpeed = MinFallSpeed+(Distance*(MaxFallSpeed-MinFallSpeed))
  267.             local Sway = MinSway+(Distance*(MaxSway-MinSway))
  268.             local SwayTime = MinSwayTime+(Distance*(MaxSwayTime-MinSwayTime))
  269.             local Transparency = math.floor(MinTransparency+(Distance*(MaxTransparency-MinTransparency)))
  270.            
  271.             --wind speed logic
  272.             local WindSpeed = 0 --I should really learn how to not to use these temp values
  273.             if WindActive then
  274.                 if WindStable then
  275.                     WindSpeed = MinWindSpeed
  276.                 else
  277.                     if WindType == 0 then --default wind (variation only on distance)
  278.                         WindSpeed = MinWindSpeed+(Distance*(MaxWindSpeed-MinWindSpeed))
  279.                     elseif WindType == 2 then --wind 2 (random variation only calculated once)
  280.                         if MaxWindSpeed > MinWindSpeed then
  281.                             WindSpeed = math.random(MinWindSpeed*10,MaxWindSpeed*10)/10 --adding these tens because someone might like decimals.
  282.                         elseif MaxWindSpeed < MinWindSpeed then
  283.                             WindSpeed = math.random(MaxWindSpeed*10,MinWindSpeed*10)/10
  284.                         end
  285.                     end
  286.                 end --what if I have a proper random wind + distance formula? I wonder how that would look like
  287.             end
  288.            
  289.             --X and Y spawning position values
  290.             x = math.random(StartX,EndX)
  291.             y = math.random(StartY,EndY)
  292.            
  293.            
  294.             --store and apply variables
  295.             SnowflakeData[meterName.."Distance"] = Distance
  296.             SnowflakeData[meterName.."Size"] = Size
  297.             SKIN:Bang("!SetOption",meterName,"W",Size)
  298.             SKIN:Bang('[!SetOption "'..meterName..' "W" "'..Size..'"][!SetOption "'..meterName..'" "H" "'..Size..'"]')
  299.             SnowflakeData[meterName.."FallSpeed"] = FallSpeed
  300.             SnowflakeData[meterName.."Sway"] = Sway
  301.             SnowflakeData[meterName.."SwayTime"] = math.floor(SwayTime)
  302.             SnowflakeData[meterName.."SwayInterval"] = math.random(0,SwayTime)--random "position" in the sway cycle
  303.             SnowflakeData[meterName.."WindSpeed"] = WindSpeed
  304.             SnowflakeData[meterName.."TotalWind"] = 0
  305.             SnowflakeData[meterName.."LifeFrames"] = 0
  306.            
  307.             --transparency and image tints
  308.             if Transparency < 0 then
  309.                 Transparency = 0
  310.             elseif Transparency > 255 then
  311.                 Transparency = 255
  312.             end
  313.             SnowflakeData[meterName.."Transparency"] = Transparency
  314.             if TintsActive then
  315.                 local ActiveTint = Tints[math.random(1,#Tints)]
  316.                 SKIN:Bang("!SetOption",meterName,"ImageTint",ActiveTint..","..Transparency)
  317.                 SnowflakeData[meterName.."Tint"] = ActiveTint
  318.             else
  319.                 SKIN:Bang("!SetOption",meterName,"ImageTint","255,255,255,"..Transparency)
  320.             end
  321.            
  322.             --rest of the values
  323.             SnowflakeData[meterName.."X"] = x
  324.             SnowflakeData[meterName.."StartingX"] = x--used later for sway calculation because I can't think of any other way to NOT use this -_-
  325.             SnowflakeData[meterName.."Y"] = y
  326.             meter:SetX(x)
  327.             meter:SetY(y)
  328.             SnowflakesInUse[#SnowflakesInUse+1] = meter
  329.            
  330.         else
  331.             print("Tried to spawn a snowflake, but failed due to lack of SnowflakesAvailible ! Change either SpawnRate or add snowflake meters!")
  332.         end
  333.     end
  334.    
  335.     --timer stuff
  336.     timer = timer + 1
  337.     if timer == (SpawnRate*100000) then -- I don't want to eventually have an integer overflow (if that can happen... better safe than sorry.)
  338.         timer = 0
  339.     end
  340. end
Advertisement
Add Comment
Please, Sign In to add comment