· 6 months ago · Mar 19, 2025, 11:15 PM
1--[[
2 ProjectileSystem Module
3 -------------------------
4 This module implements a projectile system.
5 Instead of using built-in physics components (like BodyVelocity) for flight,
6 we simulate projectile motion using Euler integration and raycasting.
7
8 When a projectile hits a target (a character with a Humanoid), the target is pushed back
9 in the direction of the projectile's travel (preserving the impact angle),
10 and the target is temporarily highlighted in red to indicate a hit before reverting to its original colors.
11
12 Additional features include homing projectiles, trail effects, explosion effects, and more.
13--]]
14
15-- Create a table to hold all projectile-related functions and data.
16local ProjectileSystem = {}
17ProjectileSystem.__index = ProjectileSystem
18
19-- Get required Roblox services.
20local Players = game:GetService("Players") -- Manages players.
21local Debris = game:GetService("Debris") -- Automatically removes objects after a set time.
22local RunService = game:GetService("RunService") -- Provides per-frame update events.
23local Workspace = game:GetService("Workspace") -- The game world.
24
25-- Define the gravity vector (pointing down).
26local GRAVITY_VECTOR = Vector3.new(0, -Workspace.Gravity, 0)
27-- Table to hold all active projectile objects.
28local activeProjectiles = {}
29
30--------------------------------------------------------------------------------
31-- Function: CreateProjectilePart
32-- Purpose: Creates and returns a new visual part representing the projectile.
33-- Parameter:
34-- origin (Vector3) - The starting position for the projectile.
35--------------------------------------------------------------------------------
36function ProjectileSystem.CreateProjectilePart(origin)
37 -- Create a new Part instance.
38 local part = Instance.new("Part")
39 -- Set a small size (0.5 studs cube).
40 part.Size = Vector3.new(0.5, 0.5, 0.5)
41 -- Use a red color for the projectile.
42 part.Color = Color3.new(1, 0, 0)
43 -- Set material to Neon for a glowing appearance.
44 part.Material = Enum.Material.Neon
45 -- Make the part spherical.
46 part.Shape = Enum.PartType.Ball
47 -- Position the part at the provided origin.
48 part.CFrame = CFrame.new(origin)
49 -- Allow physics to move the part.
50 part.Anchored = false
51 -- Disable collisions to avoid interfering with other objects.
52 part.CanCollide = false
53 -- Parent the part to the Workspace so it appears in the game.
54 part.Parent = Workspace
55 -- Name the part "Projectile" for easy identification.
56 part.Name = "Projectile"
57 return part
58end
59
60--------------------------------------------------------------------------------
61-- Function: ApplyHitEffect
62-- Purpose: When a projectile hits a damageable target, this function applies a knockback
63-- effect and temporarily changes the target's color to red.
64-- Parameters:
65-- character (Model) - The target character model.
66-- hitPosition (Vector3) - The position where the projectile hit.
67-- proj (table) - The projectile object (used to determine impact direction).
68--------------------------------------------------------------------------------
69function ProjectileSystem.ApplyHitEffect(character, hitPosition, proj)
70 -- Attempt to find the target's HumanoidRootPart.
71 local hrp = character:FindFirstChild("HumanoidRootPart")
72 if hrp then
73 -- Calculate the push direction as the exact opposite of the projectile's velocity.
74 -- This ensures that if the projectile hits at a 45° angle, the target is pushed back along that same angle.
75 local pushDir = (proj.Velocity).Unit
76 -- Create a BodyVelocity to apply the knockback force.
77 local bv = Instance.new("BodyVelocity")
78 -- Multiply the push direction by a constant for a weighty push effect.
79 bv.Velocity = pushDir * 10
80 -- Set MaxForce high in all directions to ensure the force is applied properly (including vertical).
81 bv.MaxForce = Vector3.new(1e5, 1e5, 1e5)
82 -- Parent the BodyVelocity to the target's HumanoidRootPart.
83 bv.Parent = hrp
84 -- Remove the BodyVelocity after 0.5 seconds to allow natural physics to resume.
85 Debris:AddItem(bv, 0.5)
86 end
87
88 -- Create a table to store original colors for all BaseParts in the character.
89 local originalColors = {}
90 -- Iterate over all descendants of the character.
91 for _, part in pairs(character:GetDescendants()) do
92 -- Check if the object is a BasePart (i.e., an actual physical part).
93 if part:IsA("BasePart") then
94 -- Store the original color.
95 originalColors[part] = part.Color
96 -- Set the part's color to bright red to indicate the hit.
97 part.Color = Color3.new(1, 0, 0)
98 end
99 end
100
101 -- After 1 second, revert all parts back to their original colors.
102 delay(1, function()
103 for part, origColor in pairs(originalColors) do
104 -- Ensure the part still exists before reverting its color.
105 if part and part.Parent then
106 part.Color = origColor
107 end
108 end
109 end)
110end
111
112--------------------------------------------------------------------------------
113-- Function: FireProjectile
114-- Purpose: Fires a projectile with the given parameters and adds it to the active list.
115-- Parameters:
116-- origin (Vector3) - The starting position of the projectile.
117-- direction (Vector3) - The firing direction (should be a unit vector).
118-- speed (number) - The initial speed in studs per second.
119-- damage (number) - The damage to apply upon impact.
120-- lifetime (number) - Time in seconds before the projectile expires.
121-- shooter (Player) - The player who fired the projectile (to prevent self-damage).
122--------------------------------------------------------------------------------
123function ProjectileSystem.FireProjectile(origin, direction, speed, damage, lifetime, shooter)
124 -- Create a new projectile object represented as a table.
125 local proj = {}
126 -- Set the starting logical position.
127 proj.Position = origin
128 -- Compute the initial velocity based on the normalized direction and the given speed.
129 proj.Velocity = direction.Unit * (speed or 100)
130 -- Set the damage value (default is 10).
131 proj.Damage = damage or 10
132 -- Set the projectile's lifetime (default is 5 seconds).
133 proj.Lifetime = lifetime or 5
134 -- Record the time the projectile was fired.
135 proj.StartTime = tick()
136 -- Store the shooter to avoid self-damage.
137 proj.Shooter = shooter
138 -- Create the visual projectile part at the origin.
139 proj.Part = ProjectileSystem.CreateProjectilePart(origin)
140 -- Initialize the curve factor for curved trajectories to 0 by default.
141 proj.CurveFactor = 0
142 -- Set the bounce count to zero.
143 proj.BounceCount = 0
144 -- Allow up to 2 bounces before the projectile is destroyed.
145 proj.MaxBounces = 2
146
147 -- Define the OnHit function to handle collision events.
148 proj.OnHit = function(self, hit, hitPosition)
149 -- Get the parent of the hit object (typically a character model).
150 local character = hit.Parent
151 if character then
152 -- Check for a Humanoid to determine if the target is damageable.
153 local humanoid = character:FindFirstChildOfClass("Humanoid")
154 if humanoid then
155 -- If the target is the shooter, do not apply damage.
156 if self.Shooter and character == self.Shooter.Character then
157 -- Skip self-damage.
158 else
159 -- Apply the specified damage to the target.
160 humanoid:TakeDamage(self.Damage)
161 -- Apply the custom hit effect (knockback and red highlight).
162 ProjectileSystem.ApplyHitEffect(character, hitPosition, self)
163 -- Return true to indicate that a damageable target was hit.
164 return true
165 end
166 end
167 end
168 -- Return false if the target is not damageable.
169 return false
170 end
171
172 -- Add this projectile to the active projectile list.
173 activeProjectiles[#activeProjectiles + 1] = proj
174 return proj
175end
176
177--------------------------------------------------------------------------------
178-- Function: DestroyProjectile
179-- Purpose: Removes a projectile from the game and cleans up its references.
180-- Parameter:
181-- proj (table) - The projectile object to be destroyed.
182--------------------------------------------------------------------------------
183function ProjectileSystem.DestroyProjectile(proj)
184 -- If the visual part exists, destroy it.
185 if proj.Part then
186 proj.Part:Destroy()
187 end
188 -- Iterate through the active projectile list in reverse order for safe removal.
189 for i = #activeProjectiles, 1, -1 do
190 if activeProjectiles[i] == proj then
191 table.remove(activeProjectiles, i)
192 break
193 end
194 end
195end
196
197--------------------------------------------------------------------------------
198-- Update Loop: Called every frame to update active projectiles.
199-- This loop uses Euler integration to update positions, applies gravity and curve forces,
200-- performs raycasts for collision detection, applies bounce logic, and updates projectile appearance.
201-- Projectiles that exceed their lifetime are removed.
202--------------------------------------------------------------------------------
203RunService.Heartbeat:Connect(function(deltaTime)
204 for i = #activeProjectiles, 1, -1 do
205 local proj = activeProjectiles[i]
206 local timeElapsed = tick() - proj.StartTime
207 if timeElapsed >= proj.Lifetime then
208 ProjectileSystem.DestroyProjectile(proj)
209 else
210 local oldPos = proj.Position
211 -- Calculate new position: newPos = oldPos + velocity * deltaTime + 0.5 * gravity * (deltaTime^2)
212 local newPos = oldPos + proj.Velocity * deltaTime + 0.5 * GRAVITY_VECTOR * (deltaTime^2)
213 -- Update velocity by adding gravity's effect.
214 proj.Velocity = proj.Velocity + GRAVITY_VECTOR * deltaTime
215 -- If a curve factor is set, add a lateral force to curve the trajectory.
216 if proj.CurveFactor ~= 0 then
217 local lateralForce = Vector3.new(-proj.Velocity.Z, 0, proj.Velocity.X).Unit * proj.CurveFactor * deltaTime
218 proj.Velocity = proj.Velocity + lateralForce
219 end
220 -- Determine movement direction for raycasting.
221 local rayDir = newPos - oldPos
222 -- Set up raycast parameters to ignore the shooter and the projectile itself.
223 local rayParams = RaycastParams.new()
224 rayParams.FilterType = Enum.RaycastFilterType.Exclude
225 local filters = {}
226 if proj.Shooter and proj.Shooter.Character then
227 table.insert(filters, proj.Shooter.Character)
228 end
229 table.insert(filters, proj.Part)
230 rayParams.FilterDescendantsInstances = filters
231 -- Perform the raycast from oldPos in the direction of rayDir.
232 local rayResult = Workspace:Raycast(oldPos, rayDir, rayParams)
233 if rayResult then
234 -- Collision detected: update position to the hit location.
235 proj.Position = rayResult.Position
236 proj.Part.CFrame = CFrame.new(rayResult.Position)
237 -- Call OnHit; if a damageable target was hit, destroy the projectile immediately.
238 local hitDamageable = proj:OnHit(rayResult.Instance, rayResult.Position)
239 if hitDamageable then
240 ProjectileSystem.DestroyProjectile(proj)
241 elseif proj.BounceCount < proj.MaxBounces then
242 -- Otherwise, if bounces remain, increment the bounce count.
243 proj.BounceCount = proj.BounceCount + 1
244 -- Get the surface normal from the raycast result.
245 local normal = rayResult.Normal
246 -- Reflect the velocity using the standard reflection formula.
247 local reflectedVel = proj.Velocity - 2 * proj.Velocity:Dot(normal) * normal
248 -- Apply a bounce factor (reducing speed on bounce).
249 local newVel = reflectedVel * 0.8
250 -- Clamp new velocity to prevent unnatural gains (especially on vertical surfaces).
251 if newVel.Magnitude > proj.Velocity.Magnitude then
252 newVel = newVel.Unit * proj.Velocity.Magnitude
253 end
254 proj.Velocity = newVel
255 -- Offset the position slightly away from the surface.
256 proj.Position = rayResult.Position + normal * 0.5
257 else
258 -- If maximum bounces have been reached, destroy the projectile.
259 ProjectileSystem.DestroyProjectile(proj)
260 end
261 else
262 -- No collision: update the projectile's position normally.
263 proj.Position = newPos
264 proj.Part.CFrame = CFrame.new(newPos)
265 end
266 -- Update the projectile's appearance (e.g., shrink as it travels).
267 ProjectileSystem.UpdateProjectileAppearance(proj)
268 end
269 end
270end)
271
272--------------------------------------------------------------------------------
273-- Function: CalculateImpactPoint
274-- Purpose: Computes the theoretical impact point using a simple ballistic formula.
275-- Parameters:
276-- origin (Vector3) - Starting position.
277-- direction (Vector3) - Firing direction (unit vector).
278-- speed (number) - Initial speed.
279-- gravity (number) - (Optional) Gravity value; defaults to Workspace.Gravity.
280-- Returns:
281-- A Vector3 representing the predicted impact point.
282--------------------------------------------------------------------------------
283function ProjectileSystem.CalculateImpactPoint(origin, direction, speed, gravity)
284 gravity = gravity or Workspace.Gravity
285 local t = (2 * speed * direction.Y) / gravity
286 local horizontalVelocity = Vector3.new(direction.X, 0, direction.Z) * speed
287 local horizontalDistance = horizontalVelocity * t
288 return origin + horizontalDistance
289end
290
291--------------------------------------------------------------------------------
292-- Function: FireCurvedProjectile
293-- Purpose: Fires a projectile with a curved trajectory by setting a constant curve factor.
294-- Parameters: Same as FireProjectile, with an extra curveFactor parameter.
295--------------------------------------------------------------------------------
296function ProjectileSystem.FireCurvedProjectile(origin, direction, speed, damage, curveFactor, lifetime, shooter)
297 local proj = ProjectileSystem.FireProjectile(origin, direction, speed, damage, lifetime, shooter)
298 proj.CurveFactor = curveFactor or 5
299 return proj
300end
301
302--------------------------------------------------------------------------------
303-- Function: SetProjectileColor
304-- Purpose: Changes the color of the projectile's visual part.
305-- Parameters:
306-- proj (table) - The projectile object.
307-- color (Color3) - The desired color.
308--------------------------------------------------------------------------------
309function ProjectileSystem.SetProjectileColor(proj, color)
310 if proj and proj.Part then
311 proj.Part.Color = color
312 end
313end
314
315--------------------------------------------------------------------------------
316-- Function: FadeProjectile
317-- Purpose: Gradually fades the projectile out over a given duration.
318-- Parameters:
319-- proj (table) - The projectile object.
320-- fadeDuration (number) - Duration of the fade (defaults to projectile's lifetime).
321--------------------------------------------------------------------------------
322function ProjectileSystem.FadeProjectile(proj, fadeDuration)
323 fadeDuration = fadeDuration or proj.Lifetime
324 local startTime = tick()
325 local connection
326 connection = RunService.Heartbeat:Connect(function()
327 local elapsed = tick() - startTime
328 if proj and proj.Part and proj.Part.Parent then
329 proj.Part.Transparency = math.clamp(elapsed / fadeDuration, 0, 1)
330 if elapsed >= fadeDuration then
331 connection:Disconnect()
332 end
333 else
334 connection:Disconnect()
335 end
336 end)
337end
338
339--------------------------------------------------------------------------------
340-- Function: UpdateProjectileAppearance
341-- Purpose: Updates the projectile's visual appearance based on distance traveled.
342-- For example, it gradually shrinks the projectile.
343-- Parameter:
344-- proj (table) - The projectile object.
345--------------------------------------------------------------------------------
346function ProjectileSystem.UpdateProjectileAppearance(proj)
347 if proj and proj.Part then
348 local distance = (proj.Position - proj.Part.Position).Magnitude
349 local newSize = Vector3.new(0.5, 0.5, 0.5) * math.clamp(1 - distance / 100, 0.5, 1)
350 proj.Part.Size = newSize
351 end
352end
353
354--------------------------------------------------------------------------------
355-- Function: ResetProjectile
356-- Purpose: Resets a projectile's state to its initial values.
357-- Parameter:
358-- proj (table) - The projectile object.
359--------------------------------------------------------------------------------
360function ProjectileSystem.ResetProjectile(proj)
361 if proj and proj.Part then
362 proj.Position = proj.Origin or proj.Position
363 proj.Velocity = Vector3.new(0, 0, 0)
364 proj.StartTime = tick()
365 proj.Part.CFrame = CFrame.new(proj.Position)
366 end
367end
368
369--------------------------------------------------------------------------------
370-- Function: BounceProjectile
371-- Purpose: Simulates a bounce by reflecting the projectile's velocity.
372-- Parameters:
373-- proj (table) - The projectile object.
374-- bounceFactor (number) - Multiplier for the reflected velocity (default 0.8).
375--------------------------------------------------------------------------------
376function ProjectileSystem.BounceProjectile(proj, bounceFactor)
377 bounceFactor = bounceFactor or 0.8
378 if proj and proj.Part then
379 local normal = Vector3.new(0, 1, 0)
380 proj.Velocity = (proj.Velocity - 2 * proj.Velocity:Dot(normal) * normal) * bounceFactor
381 end
382end
383
384--------------------------------------------------------------------------------
385-- Function: ExtendLifetime
386-- Purpose: Increases the lifetime of a projectile.
387-- Parameters:
388-- proj (table) - The projectile object.
389-- extraTime (number)- Additional seconds to add.
390--------------------------------------------------------------------------------
391function ProjectileSystem.ExtendLifetime(proj, extraTime)
392 if proj then
393 proj.Lifetime = proj.Lifetime + extraTime
394 end
395end
396
397--------------------------------------------------------------------------------
398-- Function: SlowProjectile
399-- Purpose: Reduces the projectile's speed by applying a slow factor.
400-- Parameters:
401-- proj (table) - The projectile object.
402-- slowFactor (number)- Factor to multiply the velocity (default 0.5).
403--------------------------------------------------------------------------------
404function ProjectileSystem.SlowProjectile(proj, slowFactor)
405 slowFactor = slowFactor or 0.5
406 if proj then
407 proj.Velocity = proj.Velocity * slowFactor
408 end
409end
410
411--------------------------------------------------------------------------------
412-- Function: ApplyAirResistance
413-- Purpose: Simulates drag by gradually reducing the projectile's velocity.
414-- Parameters:
415-- proj (table) - The projectile object.
416-- resistanceFactor (number)- Factor to multiply the velocity (default 0.99).
417--------------------------------------------------------------------------------
418function ProjectileSystem.ApplyAirResistance(proj, resistanceFactor)
419 resistanceFactor = resistanceFactor or 0.99
420 if proj then
421 proj.Velocity = proj.Velocity * resistanceFactor
422 end
423end
424
425--------------------------------------------------------------------------------
426-- Function: DistanceTravelled
427-- Purpose: Calculates the distance the projectile has traveled.
428-- Parameter:
429-- proj (table) - The projectile object.
430-- Returns:
431-- A number representing the travel distance.
432--------------------------------------------------------------------------------
433function ProjectileSystem.DistanceTravelled(proj)
434 if proj and proj.Part then
435 return (proj.Part.Position - proj.Position).Magnitude
436 end
437 return 0
438end
439
440--------------------------------------------------------------------------------
441-- Function: GetProjectileTrajectory
442-- Purpose: Computes a predicted path for the projectile using Euler integration.
443-- Parameters:
444-- proj (table) - The projectile object.
445-- steps (number) - Number of simulation steps (default 20).
446-- deltaTime (number) - Time interval per step (default 0.1 seconds).
447-- Returns:
448-- A table of Vector3 positions along the predicted trajectory.
449--------------------------------------------------------------------------------
450function ProjectileSystem.GetProjectileTrajectory(proj, steps, deltaTime)
451 steps = steps or 20
452 deltaTime = deltaTime or 0.1
453 local trajectory = {}
454 local pos = proj.Position
455 local vel = proj.Velocity
456 for i = 1, steps do
457 pos = pos + vel * deltaTime + 0.5 * GRAVITY_VECTOR * (deltaTime^2)
458 vel = vel + GRAVITY_VECTOR * deltaTime
459 table.insert(trajectory, pos)
460 end
461 return trajectory
462end
463
464--------------------------------------------------------------------------------
465-- Function: PrintProjectileInfo
466-- Purpose: Outputs detailed debug info for a projectile to the output console.
467-- Parameter:
468-- proj (table) - The projectile object.
469--------------------------------------------------------------------------------
470function ProjectileSystem.PrintProjectileInfo(proj)
471 if proj and proj.Part then
472 print("Projectile Info:")
473 print(" Position: ", proj.Position)
474 print(" Velocity: ", proj.Velocity)
475 print(" Damage: ", proj.Damage)
476 print(" Time Elapsed: ", tick() - proj.StartTime)
477 print(" Bounce Count: ", proj.BounceCount)
478 end
479end
480
481--------------------------------------------------------------------------------
482-- Function: DebugProjectiles
483-- Purpose: Prints debug info for all active projectiles.
484--------------------------------------------------------------------------------
485function ProjectileSystem.DebugProjectiles()
486 print("Active Projectiles: " .. #activeProjectiles)
487 for i, proj in ipairs(activeProjectiles) do
488 ProjectileSystem.PrintProjectileInfo(proj)
489 end
490end
491
492--------------------------------------------------------------------------------
493-- Function: GetActiveProjectiles
494-- Purpose: Returns the list of all active projectiles.
495-- Returns:
496-- The activeProjectiles table.
497--------------------------------------------------------------------------------
498function ProjectileSystem.GetActiveProjectiles()
499 return activeProjectiles
500end
501
502--------------------------------------------------------------------------------
503-- Function: ClearAllProjectiles
504-- Purpose: Destroys all active projectiles and clears the active list.
505--------------------------------------------------------------------------------
506function ProjectileSystem.ClearAllProjectiles()
507 for i = #activeProjectiles, 1, -1 do
508 ProjectileSystem.DestroyProjectile(activeProjectiles[i])
509 end
510end
511
512--------------------------------------------------------------------------------
513-- Function: DrawTrajectory
514-- Purpose: Creates visual markers along the predicted projectile path for debugging.
515-- Parameters:
516-- proj (table) - The projectile object.
517-- steps (number) - Number of points to compute (default 20).
518-- deltaTime (number) - Time interval per step (default 0.1 seconds).
519--------------------------------------------------------------------------------
520function ProjectileSystem.DrawTrajectory(proj, steps, deltaTime)
521 local trajectory = ProjectileSystem.GetProjectileTrajectory(proj, steps, deltaTime)
522 for i, pos in ipairs(trajectory) do
523 local marker = Instance.new("Part")
524 marker.Size = Vector3.new(0.2, 0.2, 0.2)
525 marker.Shape = Enum.PartType.Ball
526 marker.CFrame = CFrame.new(pos)
527 marker.Anchored = true
528 marker.CanCollide = false
529 marker.Transparency = 0.5
530 marker.Parent = Workspace
531 Debris:AddItem(marker, 3)
532 end
533end
534
535--------------------------------------------------------------------------------
536-- Additional Complex Methods for the Projectile System
537--------------------------------------------------------------------------------
538
539--------------------------------------------------------------------------------
540-- Function: HomingProjectile
541-- Purpose: Fires a projectile that homes in on a target by adjusting its velocity over time.
542-- Parameters:
543-- origin (Vector3) - Starting position.
544-- direction (Vector3) - Initial firing direction (unit vector).
545-- speed (number) - Initial speed.
546-- damage (number) - Damage to apply upon impact.
547-- lifetime (number) - Projectile lifetime.
548-- shooter (Player) - The player who fired the projectile.
549-- target (Model) - The target character model.
550--------------------------------------------------------------------------------
551function ProjectileSystem.HomingProjectile(origin, direction, speed, damage, lifetime, shooter, target)
552 local proj = ProjectileSystem.FireProjectile(origin, direction, speed, damage, lifetime, shooter)
553 local homingFactor = 5
554 local connection
555 connection = RunService.Heartbeat:Connect(function(deltaTime)
556 if not proj.Part or not proj.Part.Parent then
557 connection:Disconnect()
558 return
559 end
560 if target and target.Character and target.Character:FindFirstChild("HumanoidRootPart") then
561 local targetPos = target.Character.HumanoidRootPart.Position
562 local desiredDir = (targetPos - proj.Position).Unit
563 proj.Velocity = proj.Velocity:Lerp(desiredDir * proj.Velocity.Magnitude, homingFactor * deltaTime)
564 else
565 connection:Disconnect()
566 end
567 end)
568 return proj
569end
570
571--------------------------------------------------------------------------------
572-- Function: SetTrailEffect
573-- Purpose: Attaches a particle trail to the projectile.
574-- Parameters:
575-- proj (table) - The projectile object.
576-- color (Color3) - The trail color (default white).
577-- lifetime (number)- Particle lifetime (default 1 second).
578--------------------------------------------------------------------------------
579function ProjectileSystem.SetTrailEffect(proj, color, lifetime)
580 if proj and proj.Part then
581 local emitter = Instance.new("ParticleEmitter")
582 emitter.Color = ColorSequence.new(color or Color3.new(1,1,1))
583 emitter.Lifetime = NumberRange.new(lifetime or 1)
584 emitter.Rate = 50
585 emitter.Speed = NumberRange.new(2, 5)
586 emitter.Parent = proj.Part
587 proj.TrailEmitter = emitter
588 end
589end
590
591--------------------------------------------------------------------------------
592-- Function: ExplodeProjectile
593-- Purpose: Forces a projectile to explode, triggering an explosion effect and area damage.
594-- Parameters:
595-- proj (table) - The projectile object.
596-- blastRadius (number) - Explosion radius (default 5 studs).
597-- blastPressure (number)- Explosion pressure (default 50000).
598--------------------------------------------------------------------------------
599function ProjectileSystem.ExplodeProjectile(proj, blastRadius, blastPressure)
600 local explosion = Instance.new("Explosion")
601 explosion.Position = proj.Position
602 explosion.BlastRadius = blastRadius or 5
603 explosion.BlastPressure = blastPressure or 50000
604 explosion.Parent = Workspace
605 -- Instead of traditional particle effects, trigger our custom hit effect.
606 if proj and proj.Part then
607 -- If the shooter has a character, apply the hit effect to it (for demonstration).
608 if proj.Shooter and proj.Shooter.Character then
609 ProjectileSystem.ApplyHitEffect(proj.Shooter.Character, proj.Position, proj)
610 end
611 end
612 ProjectileSystem.DestroyProjectile(proj)
613end
614
615--------------------------------------------------------------------------------
616-- Return the ProjectileSystem table so it can be required in other scripts.
617--------------------------------------------------------------------------------
618return ProjectileSystem
619