Post by Emma on Mar 6, 2010 16:15:02 GMT
Originally posted by Grumpy
I put this up over at TheLys' site some time ago and it kind of got lost.
Emma asked me to put it up here.
This is a variation of the original warping I and Devlor with great assistance from Reznod developed over two years ago (from the 1.3 companion mod). This is just intended as kind of a tutorial for those who are intrested in scripting. Lot of things you can do in script. This is just one facet of the thing.
This is the simple version used for standard companion "enhanced" following:
-------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
Line for line explanation:
This line tells the game engine that this companion has to be in follow mode before the rest of the script processes. You don't want to tell them to wait somewhere just to see them start warping behind you once you walk away from them. Basically, you want them to be in AIFollow mode, and ONLY in AIFollow mode for the warping to work.
This function defines whether the object that it's attached to (companion in this instance) has their weapon out or not.
"GetWeaponDrawn" is a boolean function, meaning it is either true or false (1=true 0=false). A script (unless defined differently) runs once every frame of the game, and the checks that you use in the script are in-turn made once every frame, so (for example), this script runs at the beginning of the frame (script runs from top to bottom), and the first "check" encountered is (after CurrentAIPackage) "GetWeaponDrawn". If the check returns "1" here, then the next line is processed...
Next line, and subsequently the rest of the script are dependent on this first "check".
This line stops further script processing. None of the rest of this script will be processed as long as the "GetWeaponDrawn" function returns true. Reason we do this is to keep a companion from warping while in combat. They only draw their weapons when in combat, so we can assume that if their weapon IS drawn, that combat is occuring.
Side note: When friend Devlor suggested this as an appropriate qualifier to keep companions from warping during combat, we thought we had it whipped at that point. However, there is a game bug that happens on occasion, and they fail to sheathe after combat is over. This presents a problem because as long as they have their weapons drawn, none of the rest of the script processes, and warping is effectively "broken" at that point. Generally why you'll see some kind of mechanisim used in dialog to force a sheathe.
Always bear in mind when scripting that not all is black and white. Other factors can and do interfere sometimes.
If first condition exists (GetWeaponDrawn == 1), do this. elseif first condition does NOT exist we move to the next check; second condition exists (GetDistance Player > 800), do this.
Basically, this is the "trip wire" that activates warping. Companion is too far away. Normal following parameters as defined by the game engine itself are between 180gu near distance ("game units", or the measure of distance used by the game itself (1gu = .57 virtual inches)), and about 480gu far distance. Given that you are on flat terrain with no obstacles, the game engine defines that a companion should always be within this 180gu to 480gu range.
What we are doing with this line of code is making an assumption that if they are beyond this outside distance (480gu), that they are probably stuck.
Now the reason I used 800gu here, is that on occasion, the AI works and they (companions) will figure it out on their own. By using 800gu, we minimize warping to some degree to make it more asthetically pleasing. This is, however, a relatively arbitrary number, and I've seen it set shorter, so it's pretty much "user taste".
OK... Companion is STILL outside of normal parameters (480gu), and is presumed "stuck" at this point (remember that this script is processing 30 times per second on a computer that runs the game at 30fps).
This section actually serves two purposes. One reason is, again, as the 800gu distance above, used for reasons of asthetics, and allows the companion an additional 8 seconds (another arbitrary number BTW) to get back within normal following distance without warping. The second is a bit more complicated, but easy enough to understand if you consider that "GetWeaponDrawn" acts as a dynamic timer in the sense that as long as the companion has their weapon out, none of the rest of the script processes. So we actually have two different timers working here: "GetWeaponDrawn" acts as the first, but only during combat, in that it keeps the companion from warping during combat. It covers the amount of time from the onset of combat through the end of combat. Now once combat is over, if we didn't have a timer running, companion would instantly warp back to the player. The "real" timer, and the second purpose of it, is just to allow them enough time to return to the player without warping... IF THEY DON'T GET STUCK. If they do get stuck, and the 8 seconds elapses, then we move to the next area of the script.
lines defined:
-this initiates the timer
-defines the length of time, so if the timer is greater than (>) 8 seconds...
Remember? If condition exists, do this...
-resets the timer to 0 for the next go-round
(Script is processing 30 times per second from top to bottom. Defined conditions HAVE to be met for the script to contine processing. Distance is now exceeded, and time has run out. The two conditions we have defined for warping to occur are now true.)
-we use our variables here (floats in this case because these numbers are not simple as in 1.2,3 (short variables), but coordinates as in -167.312)
-set variable myx to the player's "x" coordinate
-same with these two, except that one is for the player's "y" coordinate, and the other is for the player's "z" coordinate
All objects in the game are mapped, and have coordinates that are in relation to the 0,0 coordinate. I'll probably screw this up, but I think "x" is east/west, "y" is north/south, and "z" is up/down (height).
-we now have the player's coordinates, so to complete the sequence, we mearly move the object (our companion) from his/her current location to the player's location
-set companion's x position (SetPos x) to myx (myx being the player's current x coordinate)
-same with these two, except that one is for the player's "y" coordinate, and the other is for the player's "z" coordinate
...and the "endif" lines to terminate the sequence...
A rub: What if the player levitates? Obviously without some kind of qualifier, once the distance is reached, and the timer times out, the companion will warp. Not good...
A simple qualification (I'm not going to get into companion levitation abilities here, the following addition simply keeps them from warping if the player does levitate, and is one of the simplest fixes):
-------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
Tells the game engine that if the player is levitating, then companion is to stop right where they are. Once the player has ended the levitation sequence, then he/she will need to return to the companion and initiate follow mode again.
Remember that this script is running once every frame. First check is made; if player IS levitating, AIPackage gets changed to something other than 3 (-1 maybe (not sure)), so the next frame, the script processes again; the first line is checked, and AIPackage is NOT 3, so none of the rest of the script gets processed. No warping.
Critters:
-------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
Rub with a critter is that they don't use weapons, so we can't use "GetWeponDrawn" here. In this case, the best that can be done is to just eliminate that line altogether and just use a timer.
Emma asked me to put it up here.
This is a variation of the original warping I and Devlor with great assistance from Reznod developed over two years ago (from the 1.3 companion mod). This is just intended as kind of a tutorial for those who are intrested in scripting. Lot of things you can do in script. This is just one facet of the thing.
This is the simple version used for standard companion "enhanced" following:
-------------------------------------------------------------------------------------
float myx
float myy
float myz
float timer
if ( GetCurrentAIPackage == 3 )
if ( GetWeaponDrawn == 1 )
return
elseif ( GetDistance Player > 800 )
set timer to timer + GetSecondsPassed
if ( timer > 8 )
set timer to 0
set myx to ( Player->GetPos x )
set myy to ( Player->GetPos y )
set myz to ( Player->GetPos z )
SetPos x myx
SetPos y myy
SetPos z myz
endif
endif
endif
endif
-------------------------------------------------------------------------------------
Line for line explanation:
if ( GetCurrentAIPackage == 3 )
This line tells the game engine that this companion has to be in follow mode before the rest of the script processes. You don't want to tell them to wait somewhere just to see them start warping behind you once you walk away from them. Basically, you want them to be in AIFollow mode, and ONLY in AIFollow mode for the warping to work.
if ( GetWeaponDrawn == 1 )
This function defines whether the object that it's attached to (companion in this instance) has their weapon out or not.
"GetWeaponDrawn" is a boolean function, meaning it is either true or false (1=true 0=false). A script (unless defined differently) runs once every frame of the game, and the checks that you use in the script are in-turn made once every frame, so (for example), this script runs at the beginning of the frame (script runs from top to bottom), and the first "check" encountered is (after CurrentAIPackage) "GetWeaponDrawn". If the check returns "1" here, then the next line is processed...
Next line, and subsequently the rest of the script are dependent on this first "check".
return
This line stops further script processing. None of the rest of this script will be processed as long as the "GetWeaponDrawn" function returns true. Reason we do this is to keep a companion from warping while in combat. They only draw their weapons when in combat, so we can assume that if their weapon IS drawn, that combat is occuring.
Side note: When friend Devlor suggested this as an appropriate qualifier to keep companions from warping during combat, we thought we had it whipped at that point. However, there is a game bug that happens on occasion, and they fail to sheathe after combat is over. This presents a problem because as long as they have their weapons drawn, none of the rest of the script processes, and warping is effectively "broken" at that point. Generally why you'll see some kind of mechanisim used in dialog to force a sheathe.
Always bear in mind when scripting that not all is black and white. Other factors can and do interfere sometimes.
elseif ( GetDistance Player > 800 )
If first condition exists (GetWeaponDrawn == 1), do this. elseif first condition does NOT exist we move to the next check; second condition exists (GetDistance Player > 800), do this.
Basically, this is the "trip wire" that activates warping. Companion is too far away. Normal following parameters as defined by the game engine itself are between 180gu near distance ("game units", or the measure of distance used by the game itself (1gu = .57 virtual inches)), and about 480gu far distance. Given that you are on flat terrain with no obstacles, the game engine defines that a companion should always be within this 180gu to 480gu range.
What we are doing with this line of code is making an assumption that if they are beyond this outside distance (480gu), that they are probably stuck.
Now the reason I used 800gu here, is that on occasion, the AI works and they (companions) will figure it out on their own. By using 800gu, we minimize warping to some degree to make it more asthetically pleasing. This is, however, a relatively arbitrary number, and I've seen it set shorter, so it's pretty much "user taste".
set timer to timer + GetSecondsPassed
if ( timer > 8 )
set timer to 0
OK... Companion is STILL outside of normal parameters (480gu), and is presumed "stuck" at this point (remember that this script is processing 30 times per second on a computer that runs the game at 30fps).
This section actually serves two purposes. One reason is, again, as the 800gu distance above, used for reasons of asthetics, and allows the companion an additional 8 seconds (another arbitrary number BTW) to get back within normal following distance without warping. The second is a bit more complicated, but easy enough to understand if you consider that "GetWeaponDrawn" acts as a dynamic timer in the sense that as long as the companion has their weapon out, none of the rest of the script processes. So we actually have two different timers working here: "GetWeaponDrawn" acts as the first, but only during combat, in that it keeps the companion from warping during combat. It covers the amount of time from the onset of combat through the end of combat. Now once combat is over, if we didn't have a timer running, companion would instantly warp back to the player. The "real" timer, and the second purpose of it, is just to allow them enough time to return to the player without warping... IF THEY DON'T GET STUCK. If they do get stuck, and the 8 seconds elapses, then we move to the next area of the script.
lines defined:
set timer to timer + GetSecondsPassed
-this initiates the timer
if ( timer > 8 )
-defines the length of time, so if the timer is greater than (>) 8 seconds...
Remember? If condition exists, do this...
set timer to 0
-resets the timer to 0 for the next go-round
(Script is processing 30 times per second from top to bottom. Defined conditions HAVE to be met for the script to contine processing. Distance is now exceeded, and time has run out. The two conditions we have defined for warping to occur are now true.)
set myx to ( Player->GetPos x )
-we use our variables here (floats in this case because these numbers are not simple as in 1.2,3 (short variables), but coordinates as in -167.312)
-set variable myx to the player's "x" coordinate
set myy to ( Player->GetPos y )
set myz to ( Player->GetPos z )
-same with these two, except that one is for the player's "y" coordinate, and the other is for the player's "z" coordinate
All objects in the game are mapped, and have coordinates that are in relation to the 0,0 coordinate. I'll probably screw this up, but I think "x" is east/west, "y" is north/south, and "z" is up/down (height).
SetPos x myx
-we now have the player's coordinates, so to complete the sequence, we mearly move the object (our companion) from his/her current location to the player's location
-set companion's x position (SetPos x) to myx (myx being the player's current x coordinate)
SetPos y myy
SetPos z myz
-same with these two, except that one is for the player's "y" coordinate, and the other is for the player's "z" coordinate
...and the "endif" lines to terminate the sequence...
A rub: What if the player levitates? Obviously without some kind of qualifier, once the distance is reached, and the timer times out, the companion will warp. Not good...
A simple qualification (I'm not going to get into companion levitation abilities here, the following addition simply keeps them from warping if the player does levitate, and is one of the simplest fixes):
-------------------------------------------------------------------------------------
float myx
float myy
float myz
float timer
if ( GetCurrentAIPackage == 3 )
if ( Player->GetEffect sEffectLevitate == 1 )
AIWander 0 0 0 0
endif
if ( GetWeaponDrawn == 1 )
return
elseif ( GetDistance Player > 800 )
set timer to timer + GetSecondsPassed
if ( timer > 8 )
set timer to 0
set myx to ( Player->GetPos x )
set myy to ( Player->GetPos y )
set myz to ( Player->GetPos z )
SetPos x myx
SetPos y myy
SetPos z myz
endif
endif
endif
endif
-------------------------------------------------------------------------------------
if ( Player->GetEffect sEffectLevitate == 1 )
AIWander 0 0 0 0
endif
Tells the game engine that if the player is levitating, then companion is to stop right where they are. Once the player has ended the levitation sequence, then he/she will need to return to the companion and initiate follow mode again.
Remember that this script is running once every frame. First check is made; if player IS levitating, AIPackage gets changed to something other than 3 (-1 maybe (not sure)), so the next frame, the script processes again; the first line is checked, and AIPackage is NOT 3, so none of the rest of the script gets processed. No warping.
Critters:
-------------------------------------------------------------------------------------
float myx
float myy
float myz
float timer
if ( GetCurrentAIPackage == 3 )
if ( Player->GetEffect sEffectLevitate == 1 )
AIWander 0 0 0 0
endif
if ( GetDistance Player > 800 )
set timer to timer + GetSecondsPassed
if ( timer > 8 )
set timer to 0
set myx to ( Player->GetPos x )
set myy to ( Player->GetPos y )
set myz to ( Player->GetPos z )
SetPos x myx
SetPos y myy
SetPos z myz
endif
endif
endif
endif
-------------------------------------------------------------------------------------
Rub with a critter is that they don't use weapons, so we can't use "GetWeponDrawn" here. In this case, the best that can be done is to just eliminate that line altogether and just use a timer.