BulletContext
The public-facing object that weapon code interacts with for every in-flight projectile.
Create one before firing, pass it to Vetra:Fire, then use the solver's signals to react to events. The context is passed as the first argument on every signal so you can identify the bullet and read its current state.
local context = BulletContext.new({
Origin = muzzlePosition,
Direction = direction,
Speed = 200,
})
Solver:Fire(context, Behavior)
UserData
Attach weapon-specific metadata (shooter UserId, damage value, hit-group
flags, etc.) via context.UserData before calling Fire. This table is
passed unchanged on every signal emission.
Properties
Id
This item is read only and cannot be modified. Read OnlyBulletContext.Id: numberAuto-incrementing unique identifier assigned at construction time. Use this to distinguish bullets in signal handlers without storing separate references.
Origin
This item is read only and cannot be modified. Read OnlyBulletContext.Origin: Vector3World-space muzzle position at the moment the bullet was fired. Never changes.
Direction
This item is read only and cannot be modified. Read OnlyBulletContext.Direction: Vector3Unit direction vector the bullet was fired along. Never changes.
Speed
This item is read only and cannot be modified. Read OnlyBulletContext.Speed: number
Initial speed in studs per second. The bullet's actual speed decreases
over time due to drag, bounces (restitution), and pierces (speed retention).
Read the magnitude of Velocity for the current speed.
StartTime
This item is read only and cannot be modified. Read OnlyBulletContext.StartTime: number
os.clock() timestamp at construction. Use with BulletContext:GetLifetime
to compute bullet age without storing the time yourself.
RaycastParams
This item is read only and cannot be modified. Read OnlyBulletContext.RaycastParams: RaycastParams?
Optional per-bullet raycast filter. When set, used as the raycast params
for this bullet if the Behavior does not have its own RaycastParams set.
Priority order in [Vetra:Fire]:
BulletContext.RaycastParams, this field (highest priority).Behavior.RaycastParams, if the user called:RaycastParams()on the BehaviorBuilder.- Default empty
RaycastParams.new().
Useful when you share a single Behavior across many bullet types but need per-bullet filtering (e.g. different ignore lists per shooter).
Position
This item is read only and cannot be modified. Read OnlyBulletContext.Position: Vector3?
Current world-space position. Updated every frame by the solver.
nil until the first simulation step.
Velocity
This item is read only and cannot be modified. Read OnlyBulletContext.Velocity: Vector3Current velocity vector in studs per second. Updated every frame by the solver. The magnitude of this vector is the current speed.
Alive
This item is read only and cannot be modified. Read OnlyBulletContext.Alive: boolean
true while the cast is being simulated. Set to false by
BulletContext:Terminate or automatically by the solver on hit,
distance expiry, speed expiry, or corner-trap detection.
Length
This item is read only and cannot be modified. Read OnlyBulletContext.Length: number
True accumulated path distance in studs, the sum of every frame
displacement since firing. This diverges from straight-line
(Position - Origin).Magnitude for bullets that bounce or follow
homing curves. See also BulletContext:GetDistanceTraveled.
SimulationTime
This item is read only and cannot be modified. Read OnlyBulletContext.SimulationTime: numberTotal seconds this bullet has been simulated, as tracked by the solver. Updated every frame. Use BulletContext:GetLifetime to read this. Does not advance while the cast is paused.
CosmeticBulletObject
This item is read only and cannot be modified. Read OnlyBulletContext.CosmeticBulletObject: Instance?
Set by the solver after the cosmetic bullet is created. Readable from
signal handlers (OnSegmentOpen, OnBounce, etc.) via the context argument.
nil when no cosmetic is configured or after the bullet terminates.
UserData
BulletContext.UserData: {[any]: any}Free-form table for attaching cast-specific metadata (shooter UserId, weapon type, damage value, etc.). Surfaced on every signal emission — read it in your handlers to route events without maintaining a parallel lookup.
context.UserData.Damage = 45
context.UserData.ShooterId = Players.LocalPlayer.UserId
Signals.OnHit:Connect(function(context, result, velocity)
local damage = context.UserData.Damage
local shooterId = context.UserData.ShooterId
end)
FireTravelEvents
BulletContext.FireTravelEvents: boolean?
Controls whether OnTravel and OnTravelBatch fire for this bullet.
Only relevant for the parallel solver — FF casts (no callbacks, no homing)
default to false and skip travel signal emission entirely for performance.
Set to true to opt in when you need per-frame position updates on a
parallel cast.
Has no effect on the serial solver (Vetra.new()), which always fires
travel signals.
Functions
new
Creates a new BulletContext.
Origin, Direction, and Speed are required.
Optional config fields:
UserData— pre-populate the context's UserData table at construction.RaycastParams— per-bullet raycast filter (highest priority over Behavior params).FireTravelEvents— setfalseto suppressOnTravel/OnTravelBatchfor this bullet (parallel solver only, defaults tofalsefor FF casts).SolverData— reserved for internal solver use, do not supply from weapon code.
IsAlive
BulletContext:IsAlive() → booleanReturns whether the bullet is still alive and being simulated.
GetLifetime
BulletContext:GetLifetime() → number--
Simulated age in seconds.
Returns how many seconds this bullet has been simulated.
Tracks SimulationTime accumulated by the solver, not real-world clock
time. Does not advance while the cast is paused.
GetDistanceTraveled
BulletContext:GetDistanceTraveled() → number--
Path length in studs.
Returns the true accumulated path length of the bullet in studs.
Unlike (Position - Origin).Magnitude, this correctly accounts for
bounces and homing curves by accumulating the actual frame-by-frame
displacement. Equivalent to reading context.Length.
GetSnapshot
Returns a read-only snapshot of the bullet's current state.
Useful for logging, replication, or passing state to systems that should not hold a live reference to the context.
Terminate
BulletContext:Terminate() → ()
Terminates the bullet immediately, notifying the solver to clean up
resources, destroy the cosmetic object, and fire OnTerminated.
Calling Terminate on an already-dead bullet is a no-op.
GetCast
BulletContext:GetCast() → Cast?Returns the internal Cast object associated with this bullet.
Available from OnFire onwards — nil before Fire() has been called.
Use this to call Cast methods (SetVelocity, Pause, Terminate, etc.)
directly from signal handlers.
Signals.OnPierce:Connect(function(context, result, velocity, pierceCount)
if pierceCount >= 3 then
context:GetCast():Terminate()
end
end)