Difference between revisions of "Running Scripts On Arrows"
Work in Progress
imported>Scruggs |
imported>Scruggs (Work in Progress) |
||
Line 111: | Line 111: | ||
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. | 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. | ||
Great, now we have a trigger zone glued to the player's location, capable of intercepting any arrows he fires. We can attach a demo script to the trigger zone itself to show how this works: | |||
<pre> | |||
scriptName acDemoTrigSCR | |||
; Causes any projectile fired by the player to disappear | |||
ref trigRef ; reference to the triggering object | |||
begin onTriggerMob ; a mobile object has collided with the trigger zone | |||
set trigRef to getActionRef | |||
if ( trigRef.IsActor == 0 ) ; it's not an NPC | |||
trigRef.disable ; so make it disappear | |||
endif | |||
end | |||
</pre> | |||
That's fun, but not entirely useful. Also, a serious problem arises: the [[onTriggerMob]] block will run when ''any'' projectile collides with the trigger zone, including arrows and spells fired at the player. What we need is a method to determine whether the projectile is an arrow, and whether it was fired by the player. | |||
If we know the ID of the arrows we want to run scripts on, this is easy. Just check [[GetIsID]] on the triggering reference: | |||
<pre> | |||
scriptName acTrigZoneSCR | |||
ref trigRef | |||
begin onTriggerMob | |||
set trigRef to getActionRef | |||
if ( trigRef.getIsID "myArrowID" ) | |||
trigRef.disable | |||
endif | |||
end | |||
</pre> | |||
This assumes that only the player has this type of arrow, so we know it wasn't fired at the player by an NPC. | |||
Detecting ''any'' arrow is more complicated. We will rely on [[OBSE]] functions for this: | |||
<pre> | |||
scriptName acTrigZoneSCR | |||
ref trigRef | |||
begin onTriggerMob | |||
set trigRef to getActionRef | |||
if ( trigRef.IsAmmo ) ; OBSE, returns 1 if ref is an arrow | |||
trigRef.disable | |||
endif | |||
end</pre> | |||
Another more useful example results in a bottomless quiver of arrows: | |||
<pre> | |||
scriptName acTrigZoneInfiniteArrowsSCR | |||
ref trigRef | |||
ref baseObject | |||
short triggered ; flag to avoid repeated triggering by the same object | |||
begin onTriggerMob | |||
if ( triggered ) | |||
if ( getActionRef == trigRef ) ; same reference as last frame | |||
return | |||
else | |||
set triggered to 0 | |||
endif | |||
endif | |||
set trigRef to getActionRef | |||
set baseObject to trigRef.getBaseObject | |||
player.addItem baseObject 1 | |||
end | |||
</pre> | |||
We still haven't eliminated the possibility that the arrow was fired by an NPC. We'll tackle that later. | |||
At this point, we have a means of obtaining a reference to an arrow, and we can run scripts on that reference. However, it would be useful to know what the arrow is doing. We can figure this out by tracking its position and rotation over the course of its flight: | |||
*If the arrow's rotation changes and it continues moving, it has bounced off of a surface; | |||
*If its rotation changes and then it stops moving, it is lying on a flat surface, or on the ground; | |||
*If it stops moving, but its rotation remains the same, ''and'' its coordinates do not return 0, it has stuck into a surface, such as a wooden door; | |||
*If its coordinates all return 0, it has hit an actor and no longer exists in the gameworld. | |||
Using these guidelines, we can write a slightly more complicated script. This script Simply reports on the state of the arrow over the course of its flight. We use a doOnce variable '''triggered''' to make sure we only track one arrow at a time: | |||
<pre> | |||
scriptName acTrigZoneSummonSCR | |||
ref trigRef | |||
float xp | |||
float yp ; the arrow's current coordinates | |||
float zp | |||
float ox | |||
float oy ; coordinates from previous frame | |||
float oz | |||
float ax | |||
float ay ; arrow's starting angles | |||
float az | |||
short triggered ; set to 1 while an arrow is being tracked | |||
begin onTriggerMob | |||
if ( triggered == 0 ) | |||
set trigRef to getActionRef | |||
if ( trigRef.IsAmmo ) | |||
set triggered to 1 | |||
set ax to trigRef.getAngle x | |||
set ay to trigRef.getAngle y | |||
set az to trigRef.getAngle z | |||
message "Arrow fired." | |||
endif | |||
endif | |||
end | |||
begin gameMode | |||
if ( triggered ) ; currently tracking an arrow | |||
set xp to trigRef.getPos x | |||
set yp to trigRef.getPos y ; get current coordinates of arrow | |||
set zp to trigRef.getPos z | |||
if ( ( xp + yp + zp ) == 0 ) ; arrow is not in gameworld | |||
message "Arrow has hit an actor!" | |||
set triggered to 0 | |||
return | |||
elseif ( trigRef.getAngle x != ax || trigRef.getAngle y != ay || trigRef.getAngle z != az ) | |||
message "Arrow has bounced off of something!" | |||
set triggered to -1 ; remember that the arrow bounced | |||
set ax to trigRef.getAngle x | |||
set ay to trigRef.getAngle y | |||
set az to trigRef.getAngle z | |||
elseif ( xp == ox && yp == oy && zp == oz ) ; arrow stopped moving | |||
if ( triggered == 1 ) ; arrow never bounced | |||
message "Arrow stuck in a surface!" | |||
else ; arrow bounced | |||
message "Arrow is lying on the ground!" | |||
endif | |||
set triggered to 0 | |||
endif | |||
endif | |||
end</pre> | |||
Admittedly, that script isn't terribly useful in itself, but it should give you a starting point in setting up your own scripts on arrows. |