Author Topic: Setting Up Boundaries  (Read 444 times)

Legacy_redmokah

  • Newbie
  • *
  • Posts: 37
  • Karma: +0/-0
Setting Up Boundaries
« on: March 02, 2011, 12:31:40 am »


               I'm having some trouble with invisible border applications; hopefully someone can point me in the right direction.

I'm looking to limit PC access to a certain area until I say otherwise. If the PC tries to move beyond these invisible boundaries, text will popup and say, "That's lava and you can't step there."  

I'm pretty sure the same thing happens when the PC tries to access an area transition they've not been granted access to. The difference is, this isn't an area transition; rather a large trigger outline.

I tried to figure it out myself, and the only thing I could come up with was forcing the PC to move back to a waypoint. However, the outline is decently sized. Even if I place the waypoint directly in the center, the PC will still have to run all the way to the center. Plus, it's a hassle for the player; especially if they accidentally exited the border.

Is there a function I'm missing or something I can use to halt the player in their steps?
               
               

               
            

Legacy_GhostOfGod

  • Hero Member
  • *****
  • Posts: 1490
  • Karma: +0/-0
Setting Up Boundaries
« Reply #1 on: March 02, 2011, 12:54:42 am »


               I don't think you can really stop them. Once they enter a trigger they are in that trigger. That is how It's OnEnter script fires. Same gos for OnExit. So that leaves you with 2 options.
1 - Move the player away with functions like JumpToObject/Location or ActionMoveAwayFromObject/Location like you were already doing. There is another function found in "x0_i0_position", GetBehindLocation(object). that you could probably use in conjunction with one of the Jump/Move functions to move the player just back out of range instead of porting them back to a waypoint.
2 - Put up invisible objects blocking any way into the area. Once the player meets some criteria then the invisible objects are destroyed allowing the player to enter. If this is a multiplayer mod then this method has a new set of problems.
               
               

               
            

Legacy_kalbaern

  • Hero Member
  • *****
  • Posts: 1531
  • Karma: +0/-0
Setting Up Boundaries
« Reply #2 on: March 02, 2011, 03:09:55 am »


               Here's a script I use to keep PCs from walking through placeables that I've lowered below the walk mesh. It should be easilly adaptable for your use. (I use this to also make canals in cities unwalkable. Just add it to a trigger's OnEnter and outline the area to restrict access to.) Just add your message to it. You can also have it return if a variable is set or another condition is met by the PC.

void MoveBackAction(location locBackLocation)
{
SetCommandable(TRUE);
ClearAllActions();
ActionMoveToLocation(locBackLocation);
ActionWait(0.6);
ActionDoCommand(SetCommandable(TRUE));
SetCommandable(FALSE);
}
void HitForceFieldAndForceBack(object oCreature, vector vLastPostion)
{
vector vCurrentPosition= GetPosition(oCreature);
vector vNewPosition= Vector();
vNewPosition.x= vCurrentPosition.x - (vCurrentPosition.x - vLastPostion.x) * 1.75;
vNewPosition.y= vCurrentPosition.y - (vCurrentPosition.y - vLastPostion.y) * 1.75;
location locNewLocation= Location(GetArea(oCreature), vNewPosition, GetFacing(oCreature));
AssignCommand(oCreature, MoveBackAction(locNewLocation));
}
void main()
{
object oCreature= GetEnteringObject();
if( GetIsObjectValid(oCreature) == FALSE )
{
return;
}
AssignCommand(oCreature, ActionDoCommand(SetCommandable(FALSE)));
vector vCreature= GetPosition(oCreature);
DelayCommand(0.1, HitForceFieldAndForceBack(oCreature, vCreature));
}

               
               

               


                     Modifié par kalbaern, 02 mars 2011 - 03:11 .
                     
                  


            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Setting Up Boundaries
« Reply #3 on: March 02, 2011, 05:15:15 am »


               lol some one beat me to it.  Here is my version.  However I have not tested it.  It does compile though.


// returns the location that is fMetersTowards meters away from lSource location
// in the direction of lTowards
location GetLocTowrdsLoc (location lSource, location lTowrds, float fMetersTowards);
location GetLocTowrdsLoc (location lSource, location lTowrds, float fMetersTowards)
{
  vector vSource = GetPositionFromLocation(lSource);
  vector vVector = GetPositionFromLocation(lTowrds);
  float  fXYZAdjust = fMetersTowards/GetDistanceBetweenLocations (lSource, lTowrds);
 
vVector= Vector (vVector.x/fXYZAdjust,vVector.y/fXYZAdjust,vVector.z/fXYZAdjust);

  return Location(GetAreaFromLocation(lSource),vSource+vVector,VectorToAngle(vVector));
}

               
               

               
            

Legacy_redmokah

  • Newbie
  • *
  • Posts: 37
  • Karma: +0/-0
Setting Up Boundaries
« Reply #4 on: March 03, 2011, 06:58:19 am »


               I hate to be a nusance, but can you guys break down what's happening in the code. I think I get it, but I want to full comprehend what I'm using. Thanks (:

I actually thought of a simpler solution. Even though the PC can keep clicking outside the border and get across with enough effort, this script is just running for roleplaying purposes. It won't break anything if the PC gets outside the border since they won't be able to go anywhere.

void main()
{
    object oPC = GetExitingObject ();
    object oDoor = GetObjectByTag ("door_NalepInnEnter");

    int iPC = GetIsPC (oPC);
    int iBorderPatrol = GetLocalInt (OBJECT_SELF, "local_BorderPatrol");

    if (iPC == 1 && iBorderPatrol != 1)
    {
        AssignCommand (oPC, ClearAllActions ());
        AssignCommand (oPC, ActionSpeakString ("I should stay close, just in case she's late."));
        AssignCommand (oPC, ActionForceMoveToObject (oDoor));
        DelayCommand (2.0, AssignCommand (oPC, ClearAllActions ()));
    }
}
               
               

               


                     Modifié par redmokah, 03 mars 2011 - 07:46 .
                     
                  


            

Legacy_kalbaern

  • Hero Member
  • *****
  • Posts: 1531
  • Karma: +0/-0
Setting Up Boundaries
« Reply #5 on: March 03, 2011, 08:35:02 pm »


               Both examples would just bounce a PC back. I provided a OnEnter script while the second is just a similar function you could also use.
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Setting Up Boundaries
« Reply #6 on: March 03, 2011, 11:23:46 pm »


               The first step in explaining how it work, Is forst to make sure you understand the data types. 
Location
A  location has three components: the object ID of the area, a vector representing the position within the area, and a floating point number representing the facing.

It is the vector within that data type that we will be most concerned with.


vector
Now a vector  is a Struct  The code for defining the vector structure in NWN would look like this. 

struct vector
{
   float x;
   float y;
   float z; 


This allows each of the elements to be minipulated useing the   dot (".") operator 

Now how you treat a vector is all acording to what you are storing in it.   Like the use in the location data type above it is commonly used as just an (x,y,z) position within the map grid. 
Other Times it is used to represent, wait for it..., you would never guess..., a vector. 
Here are a couple links I just found on vectors.  I have not read either of thies they just looked interesting for the subject at hand.

Vector Math for 3D Computer Graphics
 

http://emweb.unl.edu...rs/vectors.html

Note: I am only going to use two dimentions (x,y) in this explanation, The logic still hold true regaurdless of the number of dimentions you use.


A vector being really nothing more then a direction and a magnitude(distance).
The direction of the vector can be look at as the slope of the line that passes through the orgin cords (0,0) and the point given by the vector. The Slope–intercept form of the equation for a stright lin is given as y=mx+b.    Where m = the rise in the y axsis divided by the run in the x axsis which defines the slop of the line.  b = to the point at which the line crosses the y axis.  since our vectors are definded by a single point with a line going through the orgin (0,0) we can simply ingnore the b since it will alway be 0 once we have your value for our vector. Our direction of our vector is simply defined by the slope of the line, the vector (x,y)  will head in the direction of a line with a slope of y/x.

The magnitude of the vector is nothing more then the distance form the orgian(0,0) to the point (x,y).
So the way you use your vector data type is highly dependent on what you currently have stored in it.  You do not want to treat a vector holding a position  the way you would treat a vector holding a vector. 

Explanation of the function
No this is not really as hard as the above may have made it sound.  Everything above is just good back gorund information to help you minipulate vevtors to you hearts content.  The best way to explain the function I gave to take my thought process step by step in writing the function.  

After reading your original post.  I deducted that you wanted to move a given distance from source location towards a target location. In order to script it. All I really had to do is figure out the direction(vector) between the two locations and give it the distance(magnitude) that you wanted to move. At this point all I have is a line segment defined by two locations.  The First thing I need to do is pull the x,y cords out of the locations to have my two segment ends defined.  This was done using the GetPositionFromLocation function.  another option would have been to use the GetPosition function to get it from an object.   

vector vSource = GetPositionFromLocation(lSource);
vector vTwards = GetPositionFromLocation(lTowards);

Since both  of thies are vector data types I can use the   dot (".") operator  to get the x, y information. so my points are.
(vSource.x,vSource.y) and (vTowards.x,vTowards.y)

'Image


Now in order to turn this segment into a vector, instead of two positions, What i need to do is move the segment to where the source point is at the orgin or cords (0,0).  I can do this by subtracting my source cords from both points.   NWN script will allow you to do this by simply subtracting the vectors. 

vector1 - vector2 = (vector1.x-vector2.x , vector1.y - vector2.y)  
Is the way that subtracting one vector from another works.   

So to move my segment so vSource is at (0.0) I am going to subtract vSource from both the Source and Towards points. 

// Since this is always moved to (0,0) it is implied  and  can  be ingnored. 
vector vMovedSource = vSource - vSource;

//Since vMovedSource of this segment is now at (0,0) This is now my Vector no longer a position with a magnitude
// equal to is length.
vMovedTowards = vTowards - vSource;  

'Image
   

We have now changed our segment into a vector.  This vector gives both the direction and the distance that lTowards is from lSource..  We still have a litle bit of work to do to it though.  It is really no good to us with the magnitude(length) that it currently has.  What we need to do is what is called mormalizing the vector.  All this means is to give the vector a length of one unit.  In our case 1 meter since NWN uses meters.  In order to normalize the vector we devide both the x and the y cords by the distance.  This will keep the same slope for the line it is on and at the same time decrease the size to 1 unit.   

Now you could use the Distance formula to figure out the magnitude. But bioware has already given us a function to find the distance between two locations So i used it:GetDistanceBetweenLocations
Bioware also has other built in functions to normlize vectors:VectorNormalize
There is also a function that I could have used to get the length of the vector: VectorMagnitude
 
For simplicity right now lets just use VectorNormalize

vector vNormilizedTowards = VectorNormize(vMovedTowards);

The only thing really left to do to our vector, so we can use it, is to change the magnitude to the length we want to move our source location.  We do that by simply legnthing the vector back out by multiplying both x and y of our cord by the length we want.

vector vVectorToMove =  Vector (vNormalizedTowards.x * fMeters , vNormalizedTowards.y * fMeters);   
'Image
Edit: Ok So I'm tired and didnt read the numbers on the grid lines right. The normilized and magnitude vectors ar ten times too big.  

In order to move our vSource position vector,   the distance and direction we want,  just add the new vVectorToMove to it. 

vMoveTo = vSource + vVectorToMove.;

'Image

The only thing lest after that is rebulding the location and returning it.

return Location( GetAreaFromLocation(lSource), vMoveTo,VectorToAngle(vMoveTo));

Hope this helps you understand what was done.  I did do a little combining of the instructions above.  and Reordered them to combine them  together better. but this is the main thought process behing the function. 

L8 
               
               

               


                     Modifié par Lightfoot8, 04 mars 2011 - 03:14 .
                     
                  


            

Legacy_redmokah

  • Newbie
  • *
  • Posts: 37
  • Karma: +0/-0
Setting Up Boundaries
« Reply #7 on: March 05, 2011, 11:19:39 pm »


               Thanks a lot. I definitely wouldn't have understood anything without that explanation. I guess I should start practicing to get a working knowledge of the process (:

Thanks again!