I'd like to submit a first draft of my generic projectile script from Elemental Magicka.  The functionality of it won't really change, but in the future I'd like to clean it up a bit more and document better.  Still, I wanted to submit something before it slips my mind again.
There are a number of adjustable constants/functions depending upon what type of motion you are trying to achieve, and what you want the forward sweep to be, but I tried to at least comment those lines.  This script would be a lot easier to execute if Morrowind scripting had a few low level math functions...
Begin NMZ_GenericProjectile_Script; -- -- Script Variablesfloat state					; Controls disablefloat timer1					; Removal timerfloat px					; Caster x position, now calculated rather than being tied to a referencefloat py					; Caster y position, now calculated rather than being tied to a referencefloat xpos					; New x positionfloat ypos					; New y positionfloat zpos					; New z positionfloat dx					; Delta xfloat dy					; Delya yfloat v0					; Initial velocityfloat vx					; Initial velocity x componentfloat vy					; Initial velocity y componentfloat hyp					; Unit vector from caster position to object positionfloat hypx					; Vector in x direction from caster position to object positionfloat hypy					; Vector in y direction from caster position to object positionfloat dx1					; New x positionfloat dy1					; New y positionfloat a0z					; Acceleration due to gravityfloat v0z					; Initial velocity z component (separate from v0, vx, and vy)float dz					; Position + Velocity term for the z direction motionfloat dz1					; Acceleration term for the z direction motionfloat xpos0					; Initial x positionfloat ypos0					; Initial y positionfloat zpos0					; Initial z positionfloat theta					; (d/dz)(dz + dz1)float ktheta					; Rotation speedfloat tfinal					; Final time after which the object will explode if it hasn't collided yet.  This is needed because we don't know the ground height.float pz					; Player z position (projectile will keep going past tfinal so long as it is above the player)float zangle					; Angle in the z direction of the objectfloat xtemp					; Temporary variable for calculating forward vectorfloat ytemp					; Temporary variable for calculating forward vectorfloat circleTimer				; Timer for circular motionfloat circleTimerB				; Timer b for circular motionfloat circle34th				; Unused 34th script variable, results in error on usagefloat circleTime				; Time to complete one revolution of the circular motion componentfloat circleRadius				; Radius function for the circular motion componentfloat circleDelta				; Temp variablefloat circleVec1Offset				; Circle vector 1, z directionfloat circleVec2Offset				; Circle vector 2, xy forward directionfloat circleVec2x				; Circle vector 2 x componentfloat circleVec2y				; Circle vector 2 y componentfloat circleVec2xAlt				; Circle vector 1 x componentfloat circleVec2yAlt				; Circle vector 1 y componentfloat circleNormalize				; Normalization factor for vector resolutionfloat circleOffsetX				; Circle x offsetfloat circleOffsetY				; Circle y offsetfloat circleTime2				; 0.75 *circleTimefloat circleTime3				; 0.50 *circleTimefloat circleTime4				; 0.25 *circleTimefloat circleSeedTime				; Randomized seed timer, 0 < circleSeedTime < circleTime to randomize start position	float circleDelta2				; circleDelta^2float circleDelta3				; circleDelta^3float circleDelta4				; circleDelta^4float circleDelta5				; circleDelta^5float circleDelta6				; circleDelta^6float circleDelta7				; circleDelta^7float zangle2					; zangle^2float zangle3					; zangle^3float zangle4					; zangle^4float zangle5					; zangle^5float zangle6					; zangle^6float zangle7					; zangle^7float HeightLimit				; Explosion limiter toggle, 0 = explode at tFinal, 1 = explode at tFinal only if not above player's z position (keeps arrows from looking funny)float angleOffset				; Optional z angle offset; -- -- Menumode Blockif ( MenuMode == 1 )	Returnendif; -- -- Disable Blockif ( GetDisabled == 1 )	if ( state <= 200 )		set state to ( state + 1 )		Return	else		setdelete 1		DontSaveObject		Return	endifendif; -- -- Spell Blockif ( state == 0 )	;Scale object	setscale 1.0	;Height-based explosion limiter	set HeightLimit to 0	;Figure out motion vector	set xpos to ( GetPos,x )	set ypos to ( GetPos,y )	set xpos0 to xpos	set ypos0 to ypos	;Optional angle offset line for the forward sweep range.  Comment out the following line to always move directly forward.	set angleOffset to ( Random, 121 )	;z angle calculations	set zangle to ( GetAngle, z )	set zangle to ( zangle + angleOffset )	set zangle to ( zangle - 60 )	setangle, z, zangle	;Convert to radians	set zangle to ( zangle * 3.1415 / 180 );	MessageBox "zangle %0.00f xpos %0.00f ypos %0.00f px %0.00f py %0.00f", zangle, xpos, ypos, px, py		;Perform some Taylor series approximations for sin(x) and cos(x), how hard would it have been to include these functions natively?	set zangle2 to ( zangle * zangle )	set zangle3 to ( zangle * zangle2 )	set zangle4 to ( zangle2 * zangle2 )	set zangle5 to ( zangle2 * zangle3 )	set zangle6 to ( zangle3 * zangle3 )	set zangle7 to ( zangle3 * zangle4 )	set xtemp to ( zangle - ( zangle3 / 6 ) )	set xtemp to ( xtemp + ( zangle5 / 120 ) )	set xtemp to ( xtemp - ( zangle7 / 5040 ) )		set ytemp to ( 1 - ( zangle2 / 2 ) )	set ytemp to ( ytemp + ( zangle4 / 24 ) )	set ytemp to ( ytemp - ( zangle6 / 720 ) )	set px to ( xpos - xtemp )	set py to ( ypos - ytemp )	set dx to ( xpos - px )	set dy to ( ypos - py )	;Resolve velocity components	;Default v0 value is 1000	set v0 to 1000	set hypx to ( ( GetPos,x ) - px )	set hypy to ( ( GetPos,y ) - py )	set hypx to ( hypx * hypx )	set hypy to ( hypy * hypy )	set hyp to ( hypx + hypy )	set hyp to ( GetSquareRoot, hyp )	set hyp to ( hyp + 0.01 )	set vx to ( v0 * dx / hyp )	set vy to ( v0 * dy / hyp )	;Raise off ground	set zpos to ( GetPos, z )	set zpos to ( zpos + 80 )	set zpos0 to zpos	; Default values	;set v0z to 200	;set ktheta to ( -75 / v0z )	set v0z to 200	set ktheta to ( -75 / v0z )	;Default tfinal value is 2.8	set tfinal to 2.8	setpos,z, zpos	;Next block	set state to 1	Returnelseif ( state == 1 )	set timer1 to ( timer1 + GetSecondsPassed )	if ( timer1 < tfinal )		; Collision check		if ( GetCollidingActor == 1 )			set state to 2			Return		elseif ( GetCollidingPC == 1 )			set state to 2			Return		endif		; The new x, y, and z positions of the object are the linear superposition		; of the forward linear motion (v0 controlled), gravitational motion (v0z controlled),		; and the circular motion around the point generated by the former functions.		; (1) Linear motion component in the forward direction		set dx1 to ( ( timer1 * vx  ) + xpos0 )		set dy1 to ( ( timer1 * vy  ) + ypos0 )		; (2) Gravity component of the motion in the z direction				;Default value for a0z is 478		set a0z to 478		set dz to ( zpos0 + ( v0z * timer1 ) )		set dz1 to ( -0.5 * a0z * timer1 * timer1 )		set dz1 to ( dz + dz1 )		; (3) Circular motion component		set circleRadius to 20		set circleTime to 1		set circleTime2 to ( 0.75 * circleTime )		set circleTime3 to ( 0.50 * circleTime )		set circleTime4 to ( 0.25 * circleTime )		; -- Set circle timer to reset after each revolution		if ( circleSeedTime == 0 )			set circleSeedTime to ( Random, 101 )			set circleSeedTime to ( circleSeedTime * circleTime / 100 )		endif		set circleTimer to ( timer1 + circleSeedTime )		while ( circleTimer > circleTime )			set circleTimer to ( circleTimer - circleTime )		endwhile		if ( circleTimer < circleTime4 )			set circleTimerB to ( circleTimer - 0 )		elseif ( circleTimer < circleTime3 )			set circleTimerB to ( circleTimer - circleTime4 )		elseif ( circleTimer < circleTime2 )			set circleTimerB to ( circleTimer - circleTime3 )		elseif ( circleTimer < circleTime )			set circleTimerB to ( circleTimer - circleTime2 )		endif		; -- Calculate offsets for the z vector and forward vector		set circleDelta to ( 2 * 3.1415 * circleTimerB / ( circleTime ) )		set circleDelta2 to ( circleDelta * circleDelta )		set circleDelta3 to ( circleDelta * circleDelta2 )		set circleDelta4 to ( circleDelta2 * circleDelta2 )		set circleDelta5 to ( circleDelta2 * circleDelta3 )		set circleDelta6 to ( circleDelta3 * circleDelta3 )		set circleDelta7 to ( circleDelta3 * circleDelta4 )		set circleVec1Offset to ( 1 - ( circleDelta2 / 2 ) )		set circleVec1Offset to ( circleVec1Offset + ( circleDelta4 / 24 ) )		set circleVec1Offset to ( circleVec1Offset - ( circleDelta6 / 720 ) )		set circleVec1Offset to ( circleVec1Offset * circleRadius )		set circleVec2Offset to ( circleDelta - ( circleDelta3 / 6 ) )		set circleVec2Offset to ( circleVec2Offset + ( circleDelta5 / 120 ) )		set circleVec2Offset to ( circleVec2Offset - ( circleDelta7 / 5040 ) )		set circleVec2Offset to ( circleVec2Offset * circleRadius )			; -- Resolve circleVec2Offset into x and y components		; Note that the circleVec2x/y calcs use the vy/x respectively, which looks reversed,		; but the vector we want is normal to the forward vector and the z vector rather than		; the forward vector itself.		set circleNormalize to ( ( vx * vx ) + ( vy * vy ) )		set circleNormalize to ( GetSquareRoot, circleNormalize )		set circleVec2x to ( circleVec2Offset * vy  / circleNormalize )		set circleVec2y to ( circleVec2Offset * vx  / circleNormalize )		set circleVec2xAlt to ( circleVec1Offset * vy  / circleNormalize )		set circleVec2yAlt to ( circleVec1Offset * vx  / circleNormalize )		if ( circleTimer < circleTime4 )			set dz1 to ( dz1 - circleVec1Offset )			set dx1 to ( dx1 + circleVec2x )			set dy1 to ( dy1 + circleVec2y )		elseif ( circleTimer < circleTime3 )			set dz1 to ( dz1 + circleVec2Offset )			set dx1 to ( dx1 + circleVec2xAlt )			set dy1 to ( dy1 + circleVec2yAlt )		elseif ( circleTimer < circleTime2 )			set dz1 to ( dz1 + circleVec1Offset )			set dx1 to ( dx1 - circleVec2x )			set dy1 to ( dy1 - circleVec2y )		elseif ( circleTimer < circleTime )			set dz1 to ( dz1 - circleVec2Offset )			set dx1 to ( dx1 - circleVec2xAlt )			set dy1 to ( dy1 - circleVec2yAlt )		endif		; (4) Rotation in x direction to accompany translation		set theta to ( v0z + ( -a0z * timer1 ) )		set theta to ( theta * ktheta )		; ---- Optional rotation limiter (keeps arrows from looking funny) ---		;if ( theta > 90 )		;	set theta to 90		;elseif ( theta < -90 )		;	set theta to -90		;endif		setangle,x,theta		; (5) Rotate in y direction to refresh collision		rotate,y,0.01		; (6) Set new coordinates for this frame		set xpos to dx1		set ypos to dy1		set zpos to dz1		setpos, x, xpos		setpos, y, ypos		setpos, z, zpos		Return	else		; This block will allow it to keep arcing if the need arises		set pz to ( Player->Getpos,z )		set zpos to ( GetPos, z )		if ( HeightLimit > 0 )			if ( zpos > pz )				set tfinal to ( tfinal + 0.1 )				return			endif		endif		set state to 2		return	endifelseif ( state == 2 )	set xpos to ( GetPos, x )	set ypos to ( GetPos, y )	set zpos to ( GetPos, z )	;Move the projectile downward a little bit.  if it just barely collided, then the spell might not hit without this adjustment.	set zpos to ( zpos - 100 )	setpos,z,zpos	;Replace the following spell with the desired effect upon impact	ExplodeSpell "NMZ_ProjectileSpell_Bomb"	DisableendifEnd