Difference between revisions of "Running Scripts On Arrows"

4,764 bytes added ,  13:55, 9 October 2006
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.
Anonymous user