Running Scripts On Arrows

Revision as of 13:26, 9 October 2006 by imported>Scruggs
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

One of the limitations of the Construction Set is that it won't allow you to attach scripts to arrows. This is an obstacle for many mod ideas. A partial workaround is to add a Script Effect enchantment to the arrow; the script will run when the arrow hits an actor. But there are times when you want to detect or control the behavior of an arrow while it is in flight, or trigger some action before the arrow hits an actor. This tutorial will show you how to do that.

The first step is to get a reference to the arrow. This is done by placing a trigger zone at the player's location. When an arrow collides with the trigger zone, an OnTriggerMob block will run. The reference to the triggering object can then be returned with GetActionRef.

We'll create a new trigger zone object using the TrigZone01.nif and place a reference to it somewhere in the gameworld. Make sure it is a persistent reference, and has a unique reference ID. We also want to scale it to 0.5 scale. We'll call the reference acTrigZoneREF.

Now, how do we keep the trigger zone glued to the player's location? SetPos and MoveTo will be useful here. We will use a quest script to handle the MoveTo calls for when the trigger zone is not in the same cell as the player, and update its position in the current cell with SetPos.

scriptName acQuestSCR

; attached to the control quest
; moves the trigger zone to the player when he changes cells
; updates trigger zone's position within the player's cell

float xp
float yp ; player coordinates
float zp
float fQuestDelayTime ; controls processing speed of this script
short relocate ; set this to 1 when we need to call moveTo

if ( fQuestDelayTime != 0.001 )
	set fQuestDelayTime to 0.001 ; process every frame
endif

if ( relocate == 1 )
	acTrigZoneREF.moveTo player
	set relocate to 2
	return
elseif ( relocate == 2 )
	acTrigZoneRef.disable
	set relocate to 3
	return
elseif ( relocate == 3 )
	acTrigZoneRef.enable
	set relocate to 4
	return
elseif ( relocate == 4 )
	set xp to player.getPos x
	acTrigZoneREF.setpos x xp
	set relocate to 0
endif

if ( player.getDistance taTrigNoisemakerRef < 200 ) ; trigZone is in same cell as player
	set xp to player.getPos x
	set yp to player.getPos y ; store player's coordinates
	set zp to player.getPos z
	acTrigZoneRef.setpos x xp
	acTrigZoneRef.setpos y yp ; and move trigZone to those coordinates
	acTrigZoneRef.setpos z zp
else ; player has changed cells
	set relocate to 1
endif

Notice that the use of the relocate variable splits the movement from one cell to the next into four stages. This is to make sure that the trigger zone retains its collision properties after it is moved.

That script works...sort of. The problem is that the trigger zone is always at the player's feet. We need to adjust the Z position of the trigger zone so that it will always be at the correct height to collide with an arrow fired by the player. Three factors are involved here: the player's height, which is defined by his race; the player's vertical rotation (X angle); and whether or not the player is sneaking. We'll add a section to the script to calculate the necessary adjustment and apply it to the trigger zone's coordinates:

scriptName acQuestSCR

; attached to the control quest
; moves the trigger zone to the player when he changes cells
; updates trigger zone's position within the player's cell

float xp
float yp ; player coordinates
float zp
float pxRot ; player's vertical rotation
float zOffset ; adjustment to zPos
float fQuestDelayTime ; controls processing speed of this script
short relocate ; set this to 1 when we need to call moveTo

if ( fQuestDelayTime != 0.001 )
	set fQuestDelayTime to 0.001 ; process every frame
endif

if ( relocate == 1 )
	acTrigZoneREF.moveTo player
	set relocate to 2
	return
elseif ( relocate == 2 )
	acTrigZoneRef.disable
	set relocate to 3
	return
elseif ( relocate == 3 )
	acTrigZoneRef.enable
	set relocate to 4
	return
elseif ( relocate == 4 )
	set xp to player.getPos x
	acTrigZoneREF.setpos x xp
	set relocate to 0
endif

set zOffset to player.getScale ; get player's height
set zOffset to ( zOffset * ( 115 - ( player.IsSneaking * 20 ) ) )
set pxRot to player.getAngle x ; get player's vertical facing
set pxRot to ( pxRot / -1.5 )
set zOffset to ( zOffset + pxRot )

if ( player.getDistance taTrigNoisemakerRef < 200 ) ; trigZone is in same cell as player
	set xp to player.getPos x
	set yp to player.getPos y ; store player's coordinates
	set zp to player.getPos z
        set zp to ( zp + zOffset ) ; adjust for zOffset
	acTrigZoneRef.setpos x xp
	acTrigZoneRef.setpos y yp ; and move trigZone to those coordinates
	acTrigZoneRef.setpos z zp
else ; player has changed cells
	set relocate to 1
endif

Here, GetScale returns the player's height as a proportion of the default racial height of 128. For the default height, we want the trigger zone placed 115 units above the player's feet when standing, and 95 units above when sneaking. Then we modify the offset to account for the player's vertical facing.