Advertisement
XZTablets

Untitled

Aug 5th, 2020
152
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 37.92 KB | None | 0 0
  1.  
  2. --[[
  3.  
  4. Rain module v1.0 by buildthomas (July 2018)
  5.  
  6. This module is licensed under the APLv2:
  7. http://www.apache.org/licenses/LICENSE-2.0
  8.  
  9. In short, you may use this code only if you agree to the following:
  10. * This notice must always be present and may not be modified or removed in any copy of this code or derived code.
  11. * You may use this in commercial, closed source projects, and you may modify the source code itself.
  12.  
  13. Refer to the license for a full description.
  14.  
  15. For questions please reach out on the Developer Forum (@buildthomas)
  16. or via Twitter (https://www.twitter.com/buildthomasRBX)
  17.  
  18. ------
  19.  
  20.  
  21. Rain:Enable(<TweenInfo> tweenInfo)
  22. Enable the rain effects instantly, or over a given easing function if tweenInfo is given.
  23.  
  24. Rain:Disable(<TweenInfo> tweenInfo)
  25. Disable the rain effects instantly, or over a given easing function if tweenInfo is given.
  26.  
  27.  
  28. Rain:SetColor(<Color3> color, <TweenInfo> tweenInfo)
  29. Set the global color of all rain particles to a given Color3 value.
  30. Sets the color instantly, or over a given easing function if tweenInfo is given.
  31. Color sequences are not supported because this would lead to a messy effect.
  32. The starting value is RAIN_DEFAULT_COLOR.
  33.  
  34. Rain:SetTransparency(<number> transparency, <TweenInfo> tweenInfo)
  35. Set the global transparency of all rain effects. 0 = regular visibility, 1 = fully invisible.
  36. Sets the transparency instantly, or over a given easing function if tweenInfo is given.
  37. Clamped between 0 and 1, the starting value is RAIN_DEFAULT_TRANSPARENCY.
  38.  
  39. Rain:SetSpeedRatio(<number> ratio, <TweenInfo> tweenInfo)
  40. Set the vertical falling speed of the rain particles. 0 = still, 1 = max falling speed.
  41. Sets the speed instantly, or over a given easing function if tweenInfo is given.
  42. Clamped between 0 and 1, the starting value is RAIN_DEFAULT_SPEEDRATIO.
  43.  
  44. Rain:SetIntensityRatio(<number> ratio, <TweenInfo> tweenInfo)
  45. Set the intensity of the rain. 0 = no effects, 1 = full effects.
  46. Sets the intensity instantly, or over a given easing function if tweenInfo is given.
  47. Clamped between 0 and 1, the starting value is RAIN_DEFAULT_INTENSITYRATIO.
  48.  
  49. Rain:SetLightEmission(<number> ratio, <TweenInfo> tweenInfo)
  50. Set the global light emission of all rain effects.
  51. Sets the light emission instantly, or over a given easing function if tweenInfo is given.
  52. Clamped between 0 and 1, the starting value is RAIN_DEFAULT_LIGHTEMISSION.
  53.  
  54. Rain:SetLightInfluence(<number> transparency, <TweenInfo> tweenInfo)
  55. Set the global light influence of all rain effects.
  56. Sets the light influence instantly, or over a given easing function if tweenInfo is given.
  57. Clamped between 0 and 1, the starting value is RAIN_DEFAULT_LIGHTINFLUENCE.
  58.  
  59.  
  60. Rain:SetVolume(<number> volume, <TweenInfo> tweenInfo)
  61. Set the global max volume of rain instantly, or over a given easing function if tweenInfo is given.
  62. The initial volume of the rain's soundgroup is RAIN_SOUND_BASEVOLUME.
  63.  
  64.  
  65. Rain:SetCeiling(<Variant<number, nil>> ceiling)
  66. Set a Y coordinate that marks the ceiling of the world. Above this spot, rain will act as if it's indoors.
  67. Feed nil to remove any previously set ceiling.
  68.  
  69.  
  70. Rain:SetDirection(<Vector3> direction, <TweenInfo> tweenInfo)
  71. Set the direction that rain falls from. The direction parameter should be a unit direction.
  72. Sets the rain direction instantly, or over a given easing function if tweenInfo is given.
  73.  
  74. Rain:SetStraightTexture(<string> asset)
  75. Rain:SetTopDownTexture(<string> asset)
  76. Rain:SetSplashTexture(<string> asset)
  77. Adjust textures of the rain effect.
  78.  
  79. Rain:SetSoundId(<string> asset)
  80. Adjust sound effect of the rain effect.
  81.  
  82. Rain:SetCollisionMode(<Rain.CollisionMode> mode, ...)
  83. Sets the current way collisions are determined for the rain.
  84.  
  85. Rain.CollisionMode
  86. A table that should be seen as an enumerator for the collision mode.
  87. The following values are available:
  88. * Rain.CollisionMode.None - All parts in the default collision group will block the rain.
  89. * Rain.CollisionMode.Whitelist - Use the whitelist provided by Rain::SetCollisionWhitelist.
  90. * Rain.CollisionMode.Blacklist - Use the blacklist provided by Rain::SetCollisionBlacklist.
  91. * Rain.CollisionMode.Function - Use the test function provided by Rain::SetCollisionFunction and do deep-casts.
  92.  
  93. Rain:SetCollisionMode(Rain.CollisionMode.None)
  94. No parameters.
  95.  
  96. Rain:SetCollisionMode(Rain.CollisionMode.Whitelist, <Variant<Instance, table>> whitelist)
  97. The provided value can either be a hierarchy of objects or a table of objects to filter with.
  98.  
  99. Rain:SetCollisionMode(Rain.CollisionMode.Blacklist, <Variant<Instance, table>> blacklist)
  100. The provided value can either be a hierarchy of objects or a table of objects to filter out.
  101.  
  102. Rain:SetCollisionMode(Rain.CollisionMode.Function, <function<BasePart -> boolean>> f)
  103. If f(part) returns a value that lua evaluates to a true condition, that part can be hit by rain.
  104. If f(part) returns any other value, that part cannot be hit by the rain.
  105.  
  106.  
  107. --]]
  108. -- Options:
  109. local MIN_SIZE = Vector3.new(0.05,0.05,0.05) -- Size of main emitter part when rain inactive
  110. local RAIN_DEFAULT_COLOR = Color3.new(1,1,1) -- Default color3 of all rain elements
  111. local RAIN_DEFAULT_TRANSPARENCY = 0 -- Default transparency scale ratio of all rain elements
  112. local RAIN_DEFAULT_SPEEDRATIO = 1 -- Default speed scale ratio of falling rain effects
  113. local RAIN_DEFAULT_INTENSITYRATIO = 1 -- Default intensity ratio of all rain elements
  114. local RAIN_DEFAULT_LIGHTEMISSION = 0.05 -- Default LightEmission of all rain elements
  115. local RAIN_DEFAULT_LIGHTINFLUENCE = 0.9 -- Default LightInfluence of all rain elements
  116. local RAIN_DEFAULT_DIRECTION = Vector3.new(0,-1,0) -- Default direction for rain to fall into
  117. local RAIN_TRANSPARENCY_T1 = .25 -- Define the shape (time-wise) of the transparency curves for emitters
  118. local RAIN_TRANSPARENCY_T2 = .75
  119. local RAIN_SCANHEIGHT = 1000 -- How many studs to scan up from camera position to determine whether occluded
  120. local RAIN_EMITTER_DIM_DEFAULT = 40 -- Size of emitter block to the side/up
  121. local RAIN_EMITTER_DIM_MAXFORWARD = 100 -- Size of emitter block forwards when looking at the horizon
  122. local RAIN_EMITTER_UP_MODIFIER = 20 -- Maximum vertical displacement of emitter (when looking fully up/down)
  123. local RAIN_SOUND_ASSET = "rbxassetid://1516791621"
  124. local RAIN_SOUND_BASEVOLUME = 0.2 -- Starting volume of rain sound effect when not occluded
  125. local RAIN_SOUND_FADEIN_TIME = 1 -- Tween in/out times for sound volume
  126. local RAIN_SOUND_FADEOUT_TIME = 1
  127. local RAIN_STRAIGHT_ASSET = "rbxassetid://1822883048" -- Some properties of the straight rain particle effect
  128. local RAIN_STRAIGHT_ALPHA_LOW = 0.7 -- Minimum particle transparency for the straight rain emitter
  129. local RAIN_STRAIGHT_SIZE = NumberSequence.new(10)
  130. local RAIN_STRAIGHT_LIFETIME = NumberRange.new(0.8)
  131. local RAIN_STRAIGHT_MAX_RATE = 600 -- Maximum rate for the straight rain emitter
  132. local RAIN_STRAIGHT_MAX_SPEED = 60 -- Maximum speed for the straight rain emitter
  133. local RAIN_TOPDOWN_ASSET = "rbxassetid://1822856633" -- Some properties of the top-down rain particle effect
  134. local RAIN_TOPDOWN_ALPHA_LOW = 0.85 -- Minimum particle transparency for the top-down rain emitter
  135. local RAIN_TOPDOWN_SIZE = NumberSequence.new {
  136. NumberSequenceKeypoint.new(0, 5.33, 2.75);
  137. NumberSequenceKeypoint.new(1, 5.33, 2.75);
  138. }
  139. local RAIN_TOPDOWN_LIFETIME = NumberRange.new(0.8)
  140. local RAIN_TOPDOWN_ROTATION = NumberRange.new(0,360)
  141. local RAIN_TOPDOWN_MAX_RATE = 600 -- Maximum rate for the top-down rain emitter
  142. local RAIN_TOPDOWN_MAX_SPEED = 60 -- Maximum speed for the top-down rain emitter
  143. local RAIN_SPLASH_ASSET = "rbxassetid://1822856633" -- Some properties of the splash particle effect
  144. local RAIN_SPLASH_ALPHA_LOW = 0.6 -- Minimum particle transparency for the splash emitters
  145. local RAIN_SPLASH_SIZE = NumberSequence.new {
  146. NumberSequenceKeypoint.new(0, 0);
  147. NumberSequenceKeypoint.new(.4, 3);
  148. NumberSequenceKeypoint.new(1, 0);
  149. }
  150. local RAIN_SPLASH_LIFETIME = NumberRange.new(0.1, 0.15)
  151. local RAIN_SPLASH_ROTATION = NumberRange.new(0,360)
  152. local RAIN_SPLASH_NUM = 20 -- Amount of splashes per frame
  153. local RAIN_SPLASH_CORRECTION_Y = .5 -- Offset from impact position for visual reasons
  154. local RAIN_SPLASH_STRAIGHT_OFFSET_Y = 50 -- Offset against rain direction for straight rain particles from splash position
  155. local RAIN_NOSPLASH_STRAIGHT_OFFSET_Y_MIN = 20 -- Min/max vertical offset from camera height for straight rain particles
  156. local RAIN_NOSPLASH_STRAIGHT_OFFSET_Y_MAX = 100 -- when no splash position could be found (i.e. no floor at that XZ-column)
  157. local RAIN_OCCLUDED_MINSPEED = 70 -- Minimum speed for the occluded straight rain emitters
  158. local RAIN_OCCLUDED_MAXSPEED = 100 -- Maximum speed for the occluded straight rain emitters
  159. local RAIN_OCCLUDED_SPREAD = Vector2.new(10,10) -- Spread angle for the occluded straight rain emitters
  160. local RAIN_OCCLUDED_MAXINTENSITY = 2 -- How many occluded straight rain particles are emitted for every splash for max intensity
  161. local RAIN_OCCLUDECHECK_OFFSET_Y = 500 -- Vertical offset from camera height to start scanning downward from for splashes
  162. local RAIN_OCCLUDECHECK_OFFSET_XZ_MIN = -100 -- Range of possible XZ offset values from camera XZ position for the splashes
  163. local RAIN_OCCLUDECHECK_OFFSET_XZ_MAX = 100
  164. local RAIN_OCCLUDECHECK_SCAN_Y = 550 -- Scan magnitude along rain path
  165. local RAIN_UPDATE_PERIOD = 6 -- Update the transparency of the main emitters + volume of rain inside every X frames
  166. local RAIN_VOLUME_SCAN_RADIUS = 35 -- Defining grid for checking how far the camera is away from a spot exposed to rain
  167. local RAIN_VOLUME_SCAN_GRID = { -- Unit range grid for scanning how far away user is from rain space
  168. -- range 0.2, 4 pts
  169. Vector3.new(0.141421363, 0, 0.141421363);
  170. Vector3.new(-0.141421363, 0, 0.141421363);
  171. Vector3.new(-0.141421363, 0, -0.141421363);
  172. Vector3.new(0.141421363, 0, -0.141421363);
  173. -- range 0.4, 8 pts
  174. Vector3.new(0.400000006, 0, 0);
  175. Vector3.new(0.282842726, 0, 0.282842726);
  176. Vector3.new(2.44929371e-17, 0, 0.400000006);
  177. Vector3.new(-0.282842726, 0, 0.282842726);
  178. Vector3.new(-0.400000006, 0, 4.89858741e-17);
  179. Vector3.new(-0.282842726, 0, -0.282842726);
  180. Vector3.new(-7.34788045e-17, 0, -0.400000006);
  181. Vector3.new(0.282842726, 0, -0.282842726);
  182. -- range 0.6, 10 pts
  183. Vector3.new(0.600000024, 0, 0);
  184. Vector3.new(0.485410213, 0, 0.352671146);
  185. Vector3.new(0.185410202, 0, 0.570633948);
  186. Vector3.new(-0.185410202, 0, 0.570633948);
  187. Vector3.new(-0.485410213, 0, 0.352671146);
  188. Vector3.new(-0.600000024, 0, 7.34788112e-17);
  189. Vector3.new(-0.485410213, 0, -0.352671146);
  190. Vector3.new(-0.185410202, 0, -0.570633948);
  191. Vector3.new(0.185410202, 0, -0.570633948);
  192. Vector3.new(0.485410213, 0, -0.352671146);
  193. -- range 0.8, 12 pts
  194. Vector3.new(0.772740662, 0, 0.207055241);
  195. Vector3.new(0.565685451, 0, 0.565685451);
  196. Vector3.new(0.207055241, 0, 0.772740662);
  197. Vector3.new(-0.207055241, 0, 0.772740662);
  198. Vector3.new(-0.565685451, 0, 0.565685451);
  199. Vector3.new(-0.772740662, 0, 0.207055241);
  200. Vector3.new(-0.772740662, 0, -0.207055241);
  201. Vector3.new(-0.565685451, 0, -0.565685451);
  202. Vector3.new(-0.207055241, 0, -0.772740662);
  203. Vector3.new(0.207055241, 0, -0.772740662);
  204. Vector3.new(0.565685451, 0, -0.565685451);
  205. Vector3.new(0.772740662, 0, -0.207055241);
  206. }
  207. -- Enumerators:
  208. local CollisionMode = {
  209. None = 0;
  210. Whitelist = 1;
  211. Blacklist = 2;
  212. Function = 3;
  213. }
  214. -- Variables & setup:
  215. -- services
  216. local Players = game:GetService("Players")
  217. local TweenService = game:GetService("TweenService")
  218. local RunService = game:GetService("RunService")
  219. local GlobalModifier = Instance.new("NumberValue") -- modifier for rain visibility for disabling/enabling over time span
  220. GlobalModifier.Value = 1 -- 0 = fully visible, 1 = invisible
  221. local connections = {} -- Stores connections to RunService signals when enabled
  222. local disabled = true -- Value to figure out whether we are moving towards a disabled state (useful during tweens)
  223. local rainDirection = RAIN_DEFAULT_DIRECTION -- Direction that rain falls into
  224. local currentCeiling = nil -- Y coordinate of ceiling (if present)
  225. local collisionMode = CollisionMode.None -- Collision mode (from Rain.CollisionMode) for raycasting
  226. local collisionList = nil -- Blacklist/whitelist for raycasting
  227. local collisionFunc = nil -- Raycasting test function for when collisionMode == Rain.CollisionMode.Function
  228. local straightLowAlpha = 1 -- Current transparency for straight rain particles
  229. local topdownLowAlpha = 1 -- Current transparency for top-down rain particles
  230. local intensityOccludedRain = 0 -- Current intensity of occluded rain particles
  231. local numSplashes = 0 -- Current number of generated splashes per frame
  232. local volumeTarget = 0 -- Current (target of tween for) sound volume
  233. -- shorthands
  234. local v3 = Vector3.new
  235. local NSK010 = NumberSequenceKeypoint.new(0, 1, 0)
  236. local NSK110 = NumberSequenceKeypoint.new(1, 1, 0)
  237. local volumeScanGrid = {} -- Pre-generate grid used for raining area distance scanning
  238. for _,v in pairs(RAIN_VOLUME_SCAN_GRID) do
  239. table.insert(volumeScanGrid, v * RAIN_VOLUME_SCAN_RADIUS)
  240. end
  241. table.sort(volumeScanGrid, function(a,b) -- Optimization: sort from close to far away for fast evaluation if closeby
  242. return a.magnitude < b.magnitude
  243. end)
  244. -- sound group for easy main volume tweaking
  245. local SoundGroup = Instance.new("SoundGroup")
  246. SoundGroup.Name = "__RainSoundGroup"
  247. SoundGroup.Volume = RAIN_SOUND_BASEVOLUME
  248. SoundGroup.Archivable = false
  249. local Sound = Instance.new("Sound")
  250. Sound.Name = "RainSound"
  251. Sound.Volume = volumeTarget
  252. Sound.SoundId = RAIN_SOUND_ASSET
  253. Sound.Looped = true
  254. Sound.SoundGroup = SoundGroup
  255. Sound.Parent = SoundGroup
  256. Sound.Archivable = false
  257. -- emitter block around camera used when outside
  258. local Emitter do
  259.  
  260. Emitter = Instance.new("Part")
  261. Emitter.Transparency = 1
  262. Emitter.Anchored = true
  263. Emitter.CanCollide = false
  264. Emitter.Locked = false
  265. Emitter.Archivable = false
  266. Emitter.TopSurface = Enum.SurfaceType.Smooth
  267. Emitter.BottomSurface = Enum.SurfaceType.Smooth
  268. Emitter.Name = "__RainEmitter"
  269. Emitter.Size = MIN_SIZE
  270. Emitter.Archivable = false
  271.  
  272. local straight = Instance.new("ParticleEmitter")
  273. straight.Name = "RainStraight"
  274. straight.LightEmission = RAIN_DEFAULT_LIGHTEMISSION
  275. straight.LightInfluence = RAIN_DEFAULT_LIGHTINFLUENCE
  276. straight.Size = RAIN_STRAIGHT_SIZE
  277. straight.Texture = RAIN_STRAIGHT_ASSET
  278. straight.LockedToPart = true
  279. straight.Enabled = false
  280. straight.Lifetime = RAIN_STRAIGHT_LIFETIME
  281. straight.Rate = RAIN_STRAIGHT_MAX_RATE
  282. straight.Speed = NumberRange.new(RAIN_STRAIGHT_MAX_SPEED)
  283. straight.EmissionDirection = Enum.NormalId.Bottom
  284. straight.Parent = Emitter
  285.  
  286. local topdown = Instance.new("ParticleEmitter")
  287. topdown.Name = "RainTopDown"
  288. topdown.LightEmission = RAIN_DEFAULT_LIGHTEMISSION
  289. topdown.LightInfluence = RAIN_DEFAULT_LIGHTINFLUENCE
  290. topdown.Size = RAIN_TOPDOWN_SIZE
  291. topdown.Texture = RAIN_TOPDOWN_ASSET
  292. topdown.LockedToPart = true
  293. topdown.Enabled = false
  294. topdown.Rotation = RAIN_TOPDOWN_ROTATION
  295. topdown.Lifetime = RAIN_TOPDOWN_LIFETIME
  296. topdown.Rate = RAIN_TOPDOWN_MAX_RATE
  297. topdown.Speed = NumberRange.new(RAIN_TOPDOWN_MAX_SPEED)
  298. topdown.EmissionDirection = Enum.NormalId.Bottom
  299. topdown.Parent = Emitter
  300.  
  301. end
  302. local splashAttachments, rainAttachments do
  303.  
  304. splashAttachments = {}
  305. rainAttachments = {}
  306.  
  307. for i = 1, RAIN_SPLASH_NUM do
  308.  
  309. -- splashes on ground
  310. local splashAttachment = Instance.new("Attachment")
  311. splashAttachment.Name = "__RainSplashAttachment"
  312. local splash = Instance.new("ParticleEmitter")
  313. splash.LightEmission = RAIN_DEFAULT_LIGHTEMISSION
  314. splash.LightInfluence = RAIN_DEFAULT_LIGHTINFLUENCE
  315. splash.Size = RAIN_SPLASH_SIZE
  316. splash.Texture = RAIN_SPLASH_ASSET
  317. splash.Rotation = RAIN_SPLASH_ROTATION
  318. splash.Lifetime = RAIN_SPLASH_LIFETIME
  319. splash.Transparency = NumberSequence.new {
  320. NSK010;
  321. NumberSequenceKeypoint.new(RAIN_TRANSPARENCY_T1, RAIN_SPLASH_ALPHA_LOW, 0);
  322. NumberSequenceKeypoint.new(RAIN_TRANSPARENCY_T2, RAIN_SPLASH_ALPHA_LOW, 0);
  323. NSK110;
  324. }
  325. splash.Enabled = false
  326. splash.Rate = 0
  327. splash.Speed = NumberRange.new(0)
  328. splash.Name = "RainSplash"
  329. splash.Parent = splashAttachment
  330. splashAttachment.Archivable = false
  331. table.insert(splashAttachments, splashAttachment)
  332.  
  333. -- occluded rain particle generation
  334. local rainAttachment = Instance.new("Attachment")
  335. rainAttachment.Name = "__RainOccludedAttachment"
  336. local straightOccluded = Emitter.RainStraight:Clone()
  337. straightOccluded.Speed = NumberRange.new(RAIN_OCCLUDED_MINSPEED, RAIN_OCCLUDED_MAXSPEED)
  338. straightOccluded.SpreadAngle = RAIN_OCCLUDED_SPREAD
  339. straightOccluded.LockedToPart = false
  340. straightOccluded.Enabled = false
  341. straightOccluded.Parent = rainAttachment
  342. local topdownOccluded = Emitter.RainTopDown:Clone()
  343. topdownOccluded.Speed = NumberRange.new(RAIN_OCCLUDED_MINSPEED, RAIN_OCCLUDED_MAXSPEED)
  344. topdownOccluded.SpreadAngle = RAIN_OCCLUDED_SPREAD
  345. topdownOccluded.LockedToPart = false
  346. topdownOccluded.Enabled = false
  347. topdownOccluded.Parent = rainAttachment
  348. rainAttachment.Archivable = false
  349. table.insert(rainAttachments, rainAttachment)
  350.  
  351. end
  352.  
  353. end
  354. -- Helper methods:
  355. local ignoreEmitterList = { Emitter }
  356. local raycastFunctions = {
  357. [CollisionMode.None] = function(ray, ignoreCharacter)
  358. return workspace:FindPartOnRayWithIgnoreList(ray, ignoreCharacter and {Emitter, Players.LocalPlayer and Players.LocalPlayer.Character} or ignoreEmitterList)
  359. end;
  360. [CollisionMode.Blacklist] = function(ray)
  361. return workspace:FindPartOnRayWithIgnoreList(ray, collisionList)
  362. end;
  363. [CollisionMode.Whitelist] = function(ray)
  364. return workspace:FindPartOnRayWithWhitelist(ray, collisionList)
  365. end;
  366. [CollisionMode.Function] = function(ray)
  367. local destination = ray.Origin + ray.Direction
  368. -- draw multiple raycasts concatenated to each other until no hit / valid hit found
  369. while ray.Direction.magnitude > 0.001 do
  370. local part, pos, norm, mat = workspace:FindPartOnRayWithIgnoreList(ray, ignoreEmitterList)
  371. if not part or collisionFunc(part) then
  372. return part, pos, norm, mat
  373. end
  374. local start = pos + ray.Direction.Unit * 0.001
  375. ray = Ray.new(start, destination - start)
  376. end
  377. end;
  378. }
  379. local raycast = raycastFunctions[collisionMode]
  380. local function connectLoop()
  381.  
  382. local rand = Random.new()
  383.  
  384. local inside = true -- Whether camera is currently in a spot occluded from the sky
  385. local frame = RAIN_UPDATE_PERIOD -- Frame counter, and force update cycle right now
  386.  
  387. -- Update Emitter on RenderStepped since it needs to be synced to Camera
  388. table.insert(connections, RunService.RenderStepped:connect(function()
  389.  
  390. -- Check if camera is outside or inside
  391. local part, position = raycast(Ray.new(workspace.CurrentCamera.CFrame.p, -rainDirection * RAIN_SCANHEIGHT), true)
  392.  
  393. if (not currentCeiling or workspace.CurrentCamera.CFrame.p.y <= currentCeiling) and not part then
  394.  
  395. -- Camera is outside and under ceiling
  396.  
  397. if volumeTarget < 1 and not disabled then
  398. volumeTarget = 1
  399. TweenService:Create(Sound, TweenInfo.new(.5), {Volume = 1}):Play()
  400. end
  401.  
  402. frame = RAIN_UPDATE_PERIOD
  403.  
  404. local t = math.abs(workspace.CurrentCamera.CFrame.lookVector:Dot(rainDirection))
  405.  
  406. local center = workspace.CurrentCamera.CFrame.p
  407. local right = workspace.CurrentCamera.CFrame.lookVector:Cross(-rainDirection)
  408. right = right.magnitude > 0.001 and right.unit or -rainDirection
  409. local forward = rainDirection:Cross(right).unit
  410.  
  411. Emitter.Size = v3(
  412. RAIN_EMITTER_DIM_DEFAULT,
  413. RAIN_EMITTER_DIM_DEFAULT,
  414. RAIN_EMITTER_DIM_DEFAULT + (1 - t)*(RAIN_EMITTER_DIM_MAXFORWARD - RAIN_EMITTER_DIM_DEFAULT)
  415. )
  416.  
  417. Emitter.CFrame =
  418. CFrame.new(
  419. center.x, center.y, center.z,
  420. right.x, -rainDirection.x, forward.x,
  421. right.y, -rainDirection.y, forward.y,
  422. right.z, -rainDirection.z, forward.z
  423. )
  424. + (1 - t) * workspace.CurrentCamera.CFrame.lookVector * Emitter.Size.Z/3
  425. - t * rainDirection * RAIN_EMITTER_UP_MODIFIER
  426.  
  427. Emitter.RainStraight.Enabled = true
  428. Emitter.RainTopDown.Enabled = true
  429.  
  430. inside = false
  431.  
  432. else
  433.  
  434. -- Camera is inside / above ceiling
  435.  
  436. Emitter.RainStraight.Enabled = false
  437. Emitter.RainTopDown.Enabled = false
  438.  
  439. inside = true
  440.  
  441. end
  442.  
  443. end))
  444. -- Do the other effects on Stepped
  445. local signal = RunService:IsRunning() and RunService.Stepped or RunService.RenderStepped
  446. table.insert(connections, signal:connect(function()
  447.  
  448. frame = frame + 1
  449.  
  450. -- Only do some updates once every few frames
  451. if frame >= RAIN_UPDATE_PERIOD then
  452.  
  453. -- Measure of how much camera is facing down (0-1)
  454. local t = math.abs(workspace.CurrentCamera.CFrame.lookVector:Dot(rainDirection))
  455.  
  456. -- More looking down = see straight particles less and see top-down particles more
  457. local straightSequence = NumberSequence.new {
  458. NSK010;
  459. NumberSequenceKeypoint.new(RAIN_TRANSPARENCY_T1, (1 - t)*straightLowAlpha + t, 0);
  460. NumberSequenceKeypoint.new(RAIN_TRANSPARENCY_T2, (1 - t)*straightLowAlpha + t, 0);
  461. NSK110;
  462. }
  463. local topdownSequence = NumberSequence.new {
  464. NSK010;
  465. NumberSequenceKeypoint.new(RAIN_TRANSPARENCY_T1, t*topdownLowAlpha + (1 - t), 0);
  466. NumberSequenceKeypoint.new(RAIN_TRANSPARENCY_T2, t*topdownLowAlpha + (1 - t), 0);
  467. NSK110;
  468. }
  469.  
  470. -- Find desired rotation for the straight rain particles
  471. local mapped = workspace.Camera.CFrame:inverse() * (workspace.Camera.CFrame.p - rainDirection)
  472. local straightRotation = NumberRange.new(math.deg(math.atan2(-mapped.x, mapped.y)))
  473.  
  474. if inside then
  475.  
  476. -- Update emitter properties
  477. for _,v in pairs(rainAttachments) do
  478. v.RainStraight.Transparency = straightSequence
  479. v.RainStraight.Rotation = straightRotation
  480. v.RainTopDown.Transparency = topdownSequence
  481. end
  482.  
  483. if not disabled then
  484.  
  485. -- Only do occluded volume check if not moving towards disabled state
  486.  
  487. local volume = 0
  488.  
  489. if (not currentCeiling or workspace.CurrentCamera.CFrame.p.y <= currentCeiling) then
  490.  
  491. -- Check how far away camera is from a space open to the sky using volume scan grid
  492.  
  493. local minDistance = RAIN_VOLUME_SCAN_RADIUS
  494. local rayDirection = -rainDirection * RAIN_SCANHEIGHT
  495.  
  496. for i = 1, #volumeScanGrid do -- In order, so first hit is closest
  497. if not raycast(Ray.new(workspace.CurrentCamera.CFrame * volumeScanGrid[i], rayDirection), true) then
  498. minDistance = volumeScanGrid[i].magnitude
  499. break
  500. end
  501. end
  502.  
  503. -- Volume is inversely proportionate to minimum distance
  504. volume = 1 - minDistance / RAIN_VOLUME_SCAN_RADIUS
  505.  
  506. end
  507.  
  508. if math.abs(volume - volumeTarget) > .01 then
  509. -- Value is sufficiently different from previous target, overwrite it
  510. volumeTarget = volume
  511. TweenService:Create(Sound, TweenInfo.new(1), {Volume = volumeTarget}):Play()
  512. end
  513.  
  514. end
  515.  
  516. else
  517.  
  518. -- Update emitter properties
  519. Emitter.RainStraight.Transparency = straightSequence
  520. Emitter.RainStraight.Rotation = straightRotation
  521. Emitter.RainTopDown.Transparency = topdownSequence
  522.  
  523. end
  524.  
  525. -- Reset frame counter
  526. frame = 0
  527.  
  528. end
  529.  
  530. local center = workspace.CurrentCamera.CFrame.p
  531. local right = workspace.CurrentCamera.CFrame.lookVector:Cross(-rainDirection)
  532. right = right.magnitude > 0.001 and right.unit or -rainDirection
  533. local forward = rainDirection:Cross(right).unit
  534. local transform = CFrame.new(
  535. center.x, center.y, center.z,
  536. right.x, -rainDirection.x, forward.x,
  537. right.y, -rainDirection.y, forward.y,
  538. right.z, -rainDirection.z, forward.z
  539. )
  540. local rayDirection = rainDirection * RAIN_OCCLUDECHECK_SCAN_Y
  541.  
  542. -- Splash and occlusion effects
  543. for i = 1, numSplashes do
  544.  
  545. local splashAttachment = splashAttachments[i]
  546. local rainAttachment = rainAttachments[i]
  547.  
  548. -- Sample random splash position
  549. local x = rand:NextNumber(RAIN_OCCLUDECHECK_OFFSET_XZ_MIN, RAIN_OCCLUDECHECK_OFFSET_XZ_MAX)
  550. local z = rand:NextNumber(RAIN_OCCLUDECHECK_OFFSET_XZ_MIN, RAIN_OCCLUDECHECK_OFFSET_XZ_MAX)
  551. local part, position, normal = raycast(Ray.new(transform * v3(x, RAIN_OCCLUDECHECK_OFFSET_Y, z), rayDirection))
  552.  
  553. if part then
  554.  
  555. -- Draw a splash at hit
  556. splashAttachment.Position = position + normal * RAIN_SPLASH_CORRECTION_Y
  557. splashAttachment.RainSplash:Emit(1)
  558.  
  559. if inside then
  560.  
  561. -- Draw occlusion rain particles a little bit above the splash position
  562. local corrected = position - rainDirection * RAIN_SPLASH_STRAIGHT_OFFSET_Y
  563. if currentCeiling and corrected.Y > currentCeiling and rainDirection.Y < 0 then
  564. corrected = corrected + rainDirection * (currentCeiling - corrected.Y) / rainDirection.Y
  565. end
  566. rainAttachment.CFrame = transform - transform.p + corrected
  567. rainAttachment.RainStraight:Emit(intensityOccludedRain)
  568. rainAttachment.RainTopDown:Emit(intensityOccludedRain)
  569.  
  570. end
  571.  
  572. elseif inside then
  573.  
  574. -- Draw occlusion rain particles on the XZ-position at around the camera's height
  575. local corrected = transform * v3(x, rand:NextNumber(RAIN_NOSPLASH_STRAIGHT_OFFSET_Y_MIN, RAIN_NOSPLASH_STRAIGHT_OFFSET_Y_MAX), z)
  576. if currentCeiling and corrected.Y > currentCeiling and rainDirection.Y < 0 then
  577. corrected = corrected + rainDirection * (currentCeiling - corrected.Y) / rainDirection.Y
  578. end
  579. rainAttachment.CFrame = transform - transform.p + corrected
  580. rainAttachment.RainStraight:Emit(intensityOccludedRain)
  581. rainAttachment.RainTopDown:Emit(intensityOccludedRain)
  582.  
  583. end
  584.  
  585. end
  586.  
  587. end))
  588. end
  589. local function disconnectLoop()
  590. -- If present, disconnect all RunService connections
  591. if #connections > 0 then
  592. for _,v in pairs(connections) do
  593. v:disconnect()
  594. end
  595. connections = {}
  596. end
  597. end
  598. local function disableSound(tweenInfo)
  599.  
  600. -- Tween the rain sound to be mute over a given easing function
  601. volumeTarget = 0
  602. local tween = TweenService:Create(Sound, tweenInfo, {Volume = 0})
  603. tween.Completed:connect(function(state)
  604. if state == Enum.PlaybackState.Completed then
  605. Sound:Stop()
  606. end
  607. tween:Destroy()
  608. end)
  609. tween:Play()
  610.  
  611. end
  612. local function disable()
  613.  
  614. disconnectLoop()
  615.  
  616. -- Hide Emitter
  617. Emitter.RainStraight.Enabled = false
  618. Emitter.RainTopDown.Enabled = false
  619. Emitter.Size = MIN_SIZE
  620.  
  621. -- Disable sound now if not tweened into disabled state beforehand
  622. if not disabled then
  623. disableSound(TweenInfo.new(RAIN_SOUND_FADEOUT_TIME))
  624. end
  625.  
  626. end
  627. -- Shorthand for creating a tweenable "variable" using value object
  628. local function makeProperty(valueObjectClass, defaultValue, setter)
  629. local valueObject = Instance.new(valueObjectClass)
  630. if defaultValue then
  631. valueObject.Value = defaultValue
  632. end
  633. valueObject.Changed:connect(setter)
  634. setter(valueObject.Value)
  635. return valueObject
  636. end
  637. local Color = makeProperty("Color3Value", RAIN_DEFAULT_COLOR, function(value)
  638.  
  639. local value = ColorSequence.new(value)
  640.  
  641. Emitter.RainStraight.Color = value
  642. Emitter.RainTopDown.Color = value
  643.  
  644. for _,v in pairs(splashAttachments) do
  645. v.RainSplash.Color = value
  646. end
  647. for _,v in pairs(rainAttachments) do
  648. v.RainStraight.Color = value
  649. v.RainTopDown.Color = value
  650. end
  651.  
  652. end)
  653. local function updateTransparency(value)
  654.  
  655. local opacity = (1 - value) * (1 - GlobalModifier.Value)
  656. local transparency = 1 - opacity
  657.  
  658. straightLowAlpha = RAIN_STRAIGHT_ALPHA_LOW * opacity + transparency
  659. topdownLowAlpha = RAIN_TOPDOWN_ALPHA_LOW * opacity + transparency
  660.  
  661. local splashSequence = NumberSequence.new {
  662. NSK010;
  663. NumberSequenceKeypoint.new(RAIN_TRANSPARENCY_T1, opacity*RAIN_SPLASH_ALPHA_LOW + transparency, 0);
  664. NumberSequenceKeypoint.new(RAIN_TRANSPARENCY_T2, opacity*RAIN_SPLASH_ALPHA_LOW + transparency, 0);
  665. NSK110;
  666. }
  667.  
  668. for _,v in pairs(splashAttachments) do
  669. v.RainSplash.Transparency = splashSequence
  670. end
  671.  
  672. end
  673. local Transparency = makeProperty("NumberValue", RAIN_DEFAULT_TRANSPARENCY, updateTransparency)
  674. GlobalModifier.Changed:connect(updateTransparency)
  675. local SpeedRatio = makeProperty("NumberValue", RAIN_DEFAULT_SPEEDRATIO, function(value)
  676.  
  677. Emitter.RainStraight.Speed = NumberRange.new(value * RAIN_STRAIGHT_MAX_SPEED)
  678. Emitter.RainTopDown.Speed = NumberRange.new(value * RAIN_TOPDOWN_MAX_SPEED)
  679.  
  680. end)
  681. local IntensityRatio = makeProperty("NumberValue", RAIN_DEFAULT_INTENSITYRATIO, function(value)
  682.  
  683. Emitter.RainStraight.Rate = RAIN_STRAIGHT_MAX_RATE * value
  684. Emitter.RainTopDown.Rate = RAIN_TOPDOWN_MAX_RATE * value
  685.  
  686. intensityOccludedRain = math.ceil(RAIN_OCCLUDED_MAXINTENSITY * value)
  687. numSplashes = RAIN_SPLASH_NUM * value
  688.  
  689. end)
  690. local LightEmission = makeProperty("NumberValue", RAIN_DEFAULT_LIGHTEMISSION, function(value)
  691.  
  692. Emitter.RainStraight.LightEmission = value
  693. Emitter.RainTopDown.LightEmission = value
  694.  
  695. for _,v in pairs(rainAttachments) do
  696. v.RainStraight.LightEmission = value
  697. v.RainTopDown.LightEmission = value
  698. end
  699. for _,v in pairs(splashAttachments) do
  700. v.RainSplash.LightEmission = value
  701. end
  702.  
  703. end)
  704. local LightInfluence = makeProperty("NumberValue", RAIN_DEFAULT_LIGHTINFLUENCE, function(value)
  705.  
  706. Emitter.RainStraight.LightInfluence = value
  707. Emitter.RainTopDown.LightInfluence = value
  708.  
  709. for _,v in pairs(rainAttachments) do
  710. v.RainStraight.LightInfluence = value
  711. v.RainTopDown.LightInfluence = value
  712. end
  713. for _,v in pairs(splashAttachments) do
  714. v.RainSplash.LightInfluence = value
  715. end
  716.  
  717. end)
  718. local RainDirection = makeProperty("Vector3Value", RAIN_DEFAULT_DIRECTION, function(value)
  719. if value.magnitude > 0.001 then
  720. rainDirection = value.unit
  721. end
  722. end)
  723. -- Exposed API:
  724. local Rain = {}
  725. Rain.CollisionMode = CollisionMode
  726. function Rain:Enable(tweenInfo)
  727.  
  728. if tweenInfo ~= nil and typeof(tweenInfo) ~= "TweenInfo" then
  729. error("bad argument #1 to 'Enable' (TweenInfo expected, got " .. typeof(tweenInfo) .. ")", 2)
  730. end
  731.  
  732. disconnectLoop() -- Just in case :Enable(..) is called multiple times on accident
  733.  
  734. Emitter.RainStraight.Enabled = true
  735. Emitter.RainTopDown.Enabled = true
  736. Emitter.Parent = workspace.CurrentCamera
  737.  
  738. for i = 1, RAIN_SPLASH_NUM do
  739. splashAttachments[i].Parent = workspace.Terrain
  740. rainAttachments[i].Parent = workspace.Terrain
  741. end
  742.  
  743. if RunService:IsRunning() then -- don't need sound in studio preview, it won't work anyway
  744. SoundGroup.Parent = game:GetService("SoundService")
  745. end
  746.  
  747. connectLoop()
  748.  
  749. if tweenInfo then
  750. TweenService:Create(GlobalModifier, tweenInfo, {Value = 0}):Play()
  751. else
  752. GlobalModifier.Value = 0
  753. end
  754.  
  755. if not Sound.Playing then
  756. Sound:Play()
  757. Sound.TimePosition = math.random()*Sound.TimeLength
  758. end
  759.  
  760. disabled = false
  761.  
  762. end
  763. function Rain:Disable(tweenInfo)
  764.  
  765. if tweenInfo ~= nil and typeof(tweenInfo) ~= "TweenInfo" then
  766. error("bad argument #1 to 'Disable' (TweenInfo expected, got " .. typeof(tweenInfo) .. ")", 2)
  767. end
  768.  
  769. if tweenInfo then
  770. local tween = TweenService:Create(GlobalModifier, tweenInfo, {Value = 1})
  771. tween.Completed:connect(function(state)
  772. if state == Enum.PlaybackState.Completed then
  773. -- Only disable the rain completely once the visual effects are faded out
  774. disable()
  775. end
  776. tween:Destroy()
  777. end)
  778. tween:Play()
  779. -- Start tweening out sound now as well
  780. disableSound(tweenInfo)
  781. else
  782. GlobalModifier.Value = 1
  783. disable()
  784. end
  785.  
  786. disabled = true
  787.  
  788. end
  789. function Rain:SetColor(value, tweenInfo)
  790.  
  791. if typeof(value) ~= "Color3" then
  792. error("bad argument #1 to 'SetColor' (Color3 expected, got " .. typeof(value) .. ")", 2)
  793. elseif tweenInfo ~= nil and typeof(tweenInfo) ~= "TweenInfo" then
  794. error("bad argument #2 to 'SetColor' (TweenInfo expected, got " .. typeof(tweenInfo) .. ")", 2)
  795. end
  796.  
  797. if tweenInfo then
  798. TweenService:Create(Color, tweenInfo, {Value = value}):Play()
  799. else
  800. Color.Value = value
  801. end
  802.  
  803. end
  804. local function makeRatioSetter(methodName, valueObject)
  805. -- Shorthand because most of the remaining property setters are very similar
  806. return function(_, value, tweenInfo)
  807.  
  808. if typeof(value) ~= "number" then
  809. error("bad argument #1 to '" .. methodName .. "' (number expected, got " .. typeof(value) .. ")", 2)
  810. elseif tweenInfo ~= nil and typeof(tweenInfo) ~= "TweenInfo" then
  811. error("bad argument #2 to '" .. methodName .. "' (TweenInfo expected, got " .. typeof(tweenInfo) .. ")", 2)
  812. end
  813.  
  814. value = math.clamp(value, 0, 1)
  815.  
  816. if tweenInfo then
  817. TweenService:Create(valueObject, tweenInfo, {Value = value}):Play()
  818. else
  819. valueObject.Value = value
  820. end
  821.  
  822. end
  823. end
  824. Rain.SetTransparency = makeRatioSetter("SetTransparency", Transparency)
  825. Rain.SetSpeedRatio = makeRatioSetter("SetSpeedRatio", SpeedRatio)
  826. Rain.SetIntensityRatio = makeRatioSetter("SetIntensityRatio", IntensityRatio)
  827. Rain.SetLightEmission = makeRatioSetter("SetLightEmission", LightEmission)
  828. Rain.SetLightInfluence = makeRatioSetter("SetLightInfluence", LightInfluence)
  829. function Rain:SetVolume(volume, tweenInfo)
  830.  
  831. if typeof(volume) ~= "number" then
  832. error("bad argument #1 to 'SetVolume' (number expected, got " .. typeof(volume) .. ")", 2)
  833. elseif tweenInfo ~= nil and typeof(tweenInfo) ~= "TweenInfo" then
  834. error("bad argument #2 to 'SetVolume' (TweenInfo expected, got " .. typeof(tweenInfo) .. ")", 2)
  835. end
  836.  
  837. if tweenInfo then
  838. TweenService:Create(SoundGroup, tweenInfo, {Volume = volume}):Play()
  839. else
  840. SoundGroup.Volume = volume
  841. end
  842.  
  843. end
  844. function Rain:SetDirection(direction, tweenInfo)
  845.  
  846. if typeof(direction) ~= "Vector3" then
  847. error("bad argument #1 to 'SetDirection' (Vector3 expected, got " .. typeof(direction) .. ")", 2)
  848. elseif tweenInfo ~= nil and typeof(tweenInfo) ~= "TweenInfo" then
  849. error("bad argument #2 to 'SetDirection' (TweenInfo expected, got " .. typeof(tweenInfo) .. ")", 2)
  850. end
  851.  
  852. if not (direction.unit.magnitude > 0) then -- intentional statement formatting since NaN comparison
  853. warn("Attempt to set rain direction to a zero-length vector, falling back on default direction = (" .. tostring(RAIN_DEFAULT_DIRECTION) .. ")")
  854. direction = RAIN_DEFAULT_DIRECTION
  855. end
  856.  
  857. if tweenInfo then
  858. TweenService:Create(RainDirection, tweenInfo, {Value = direction}):Play()
  859. else
  860. RainDirection.Value = direction
  861. end
  862.  
  863. end
  864. function Rain:SetCeiling(ceiling)
  865.  
  866. if ceiling ~= nil and typeof(ceiling) ~= "number" then
  867. error("bad argument #1 to 'SetCeiling' (number expected, got " .. typeof(ceiling) .. ")", 2)
  868. end
  869.  
  870. currentCeiling = ceiling
  871.  
  872. end
  873. function Rain:SetStraightTexture(asset)
  874.  
  875. if typeof(asset) ~= "string" then
  876. error("bad argument #1 to 'SetStraightTexture' (string expected, got " .. typeof(asset) .. ")", 2)
  877. end
  878.  
  879. Emitter.RainStraight.Texture = asset
  880.  
  881. for _,v in pairs(rainAttachments) do
  882. v.RainStraight.Texture = asset
  883. end
  884.  
  885. end
  886. function Rain:SetTopDownTexture(asset)
  887.  
  888. if typeof(asset) ~= "string" then
  889. error("bad argument #1 to 'SetStraightTexture' (string expected, got " .. typeof(asset) .. ")", 2)
  890. end
  891.  
  892. Emitter.RainTopDown.Texture = asset
  893.  
  894. for _,v in pairs(rainAttachments) do
  895. v.RainTopDown.Texture = asset
  896. end
  897.  
  898. end
  899. function Rain:SetSplashTexture(asset)
  900.  
  901. if typeof(asset) ~= "string" then
  902. error("bad argument #1 to 'SetStraightTexture' (string expected, got " .. typeof(asset) .. ")", 2)
  903. end
  904.  
  905. for _,v in pairs(splashAttachments) do
  906. v.RainSplash.Texture = asset
  907. end
  908.  
  909. end
  910. function Rain:SetSoundId(asset)
  911.  
  912. if typeof(asset) ~= "string" then
  913. error("bad argument #1 to 'SetSoundId' (string expected, got " .. typeof(asset) .. ")", 2)
  914. end
  915.  
  916. Sound.SoundId = asset
  917.  
  918. end
  919. function Rain:SetCollisionMode(mode, param)
  920.  
  921. if mode == CollisionMode.None then
  922.  
  923. -- Regular mode needs no white/blacklist or test function
  924. collisionList = nil
  925. collisionFunc = nil
  926.  
  927. elseif mode == CollisionMode.Blacklist then
  928.  
  929. if typeof(param) == "Instance" then
  930. -- Add Emitter anyway, since users will probably not expect collisions with emitter block regardless
  931. collisionList = {param, Emitter}
  932. elseif typeof(param) == "table" then
  933. for i = 1, #param do
  934. if typeof(param[i]) ~= "Instance" then
  935. error("bad argument #2 to 'SetCollisionMode' (blacklist contained a " .. typeof(param[i]) .. " on index " .. tostring(i) .. " which is not an Instance)", 2)
  936. end
  937. end
  938. collisionList = {Emitter} -- see above
  939. for i = 1, #param do
  940. table.insert(collisionList, param[i])
  941. end
  942. else
  943. error("bad argument #2 to 'SetCollisionMode (Instance or array of Instance expected, got " .. typeof(param) .. ")'", 2)
  944. end
  945.  
  946. -- Blacklist does not need a test function
  947. collisionFunc = nil
  948.  
  949. elseif mode == CollisionMode.Whitelist then
  950.  
  951. if typeof(param) == "Instance" then
  952. collisionList = {param}
  953. elseif typeof(param) == "table" then
  954. for i = 1, #param do
  955. if typeof(param[i]) ~= "Instance" then
  956. error("bad argument #2 to 'SetCollisionMode' (whitelist contained a " .. typeof(param[i]) .. " on index " .. tostring(i) .. " which is not an Instance)", 2)
  957. end
  958. end
  959. collisionList = {}
  960. for i = 1, #param do
  961. table.insert(collisionList, param[i])
  962. end
  963. else
  964. error("bad argument #2 to 'SetCollisionMode (Instance or array of Instance expected, got " .. typeof(param) .. ")'", 2)
  965. end
  966.  
  967. -- Whitelist does not need a test function
  968. collisionFunc = nil
  969.  
  970. elseif mode == CollisionMode.Function then
  971.  
  972. if typeof(param) ~= "function" then
  973. error("bad argument #2 to 'SetCollisionMode' (function expected, got " .. typeof(param) .. ")", 2)
  974. end
  975.  
  976. -- Test function does not need a list
  977. collisionList = nil
  978.  
  979. collisionFunc = param
  980.  
  981. else
  982. error("bad argument #1 to 'SetCollisionMode (Rain.CollisionMode expected, got " .. typeof(param) .. ")'", 2)
  983. end
  984.  
  985. collisionMode = mode
  986. raycast = raycastFunctions[mode]
  987.  
  988. end
  989. return Rain
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement