Author Topic: Getting an angle between locations  (Read 464 times)

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Getting an angle between locations
« on: June 18, 2012, 12:15:48 am »


                 I was planning to write up a reaction time script, to add a delay in before DetermineCombatRound is called in the various AI functions, but ran into a snag in trying to determine if the attacker is in the front arc or not.

  I found a script Lightfoot8 had written here, that's in essence the opposite of what I need, getting a location in a certain arc, instead of checking if a location falls in that arc.

 
Essentially I just need it to check if location 2 is within +/- 90 degrees of the facing of location 1.


  I assume between VectorNormalize, VectorToAngle, and AngleToVector it must be possible to do, but I'm drawing a blank in how to go about it.  I can probably hammer something out based on trig comparisons, but that's not usually very efficient.

   Any ideas how to go about this?

  
               
               

               
            

Legacy_Rolo Kipp

  • Hero Member
  • *****
  • Posts: 4349
  • Karma: +0/-0
Getting an angle between locations
« Reply #1 on: June 18, 2012, 12:31:03 am »


               <pulling out his...>

I would GetAheadLocation and then GetAngleBetweenLocations and see if it's >90.

<...sextant>
               
               

               
            

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Getting an angle between locations
« Reply #2 on: June 18, 2012, 12:56:35 am »


               I missed that one, though it needs to be modified now that there's a fAbs command.  Thanks Rolo.
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Getting an angle between locations
« Reply #3 on: June 18, 2012, 01:08:02 am »


               Function Request  Will have the function you need.   

Only Percieve Player if Seen/ Deaf NPCs also covers the same topic and function.


As discused in the  Function Request thread GetAngleBetweenLocations Is somewhat flawed.
               
               

               


                     Modifié par Lightfoot8, 18 juin 2012 - 12:10 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Getting an angle between locations
« Reply #4 on: June 18, 2012, 01:14:41 am »


               If I understand correctly, you are trying to implement a sight of view? In past (after I finished Thief 3 and wanted to implements things from this game) I was trying the same but never finished it (probably because my scripting skills wasn't enough to make it work).

The biggest problem is that nwn engine calls OnPerceived only in certain cases. If you block the attack from OnPerceived because guard in fact shouldn't see the thief behind him, its a bit problematic to get him attack when he changes position. Heartbeat is each 6seconds which gives PC plenty of time to hide/overhaul him.

Probably some code could be added into the WalkWaypoints function to re-initiate sight of view everytime NPC reach new WP, but still far from perfection.

Anyway, searching in old modules, I found this piece of code in OnPerceived:

 if(!GetIsInShape(oPercept,SHAPE_SPELLCONE,35.0,GetLocation(OBJECT_SELF),TRUE)) return;


Might help you as you don't need to do anything with angles with this approach. If you finish it anytime, I would be very interested in such code.
               
               

               
            

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Getting an angle between locations
« Reply #5 on: June 18, 2012, 01:22:21 am »


               GetAngleBetweenLocations is heavily flawed, with the abs (or fabs) in, it returns 0-90, without it 0-180, in my testing just now.

 The spellcone idea is a good one though, I think those are 60 degree wedges, so I could check the front wedge, then the outer two, and apply different modifiers based on how directly in the line of sight the target is.

I should have an idea if that's workable or not in another 15 or 20 minutes.
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Getting an angle between locations
« Reply #6 on: June 18, 2012, 01:22:25 am »


               Might as well post bot of them here from the other thread

Here is six's version. 

// Determine if oSource is facing oTarget using tolerance of angle fRange
// * returns TRUE if facing, else FALSE
// _six
int GetIsFacingObject(object oSource, object oTarget, float fRange);
int GetIsFacingObject(object oSource, object oTarget, float fRange)
{
  // Create directional vector
  vector vDir = GetPosition(oTarget) - GetPosition(oSource);
  float fVLen = GetDistanceBetween(oTarget, oSource);
  vDir /= fVLen;

  // Calculate angle between two objects, and facing of source
  float angle = acos(vDir.y);
  float facing = GetFacing(oSource);

  // Make sure angle is signed equivalently to the dir vector
  if(vDir.x < 0.0f) angle = 0.0f - angle;

  // Deal with NWN's rotation offset - due north is actually 90 degrees
  if(angle > 90.0f) angle -= 360.0f;

  // If angle falls within range given, must be facing
  if(facing < 90.0f - angle + fRange/2.0f
       && facing > 90.0f - angle - fRange/2.0f)
  {
     return TRUE;
  }

  // ...otherwise can't be, return FALSE
  return FALSE;
}  



And here is My version.

int GetIsFacingTarget(object oSelf, object oTarget, int nViewArc);
int GetIsFacingTarget(object oSelf, object oTarget, int nViewArc)
{
  location lSelf = GetLocation(oSelf);
  location lTarget = GetLocation(oTarget);

  float AngleOffset = VectorToAngle( GetPosition(oTarget) - GetPosition(oSelf)) - GetFacing(oSelf) ;

  //float  fViewArc = 180.0;
  return (abs(FloatToInt(AngleOffset)) <  nViewArc/2);
}


               
               

               


                     Modifié par Lightfoot8, 18 juin 2012 - 12:26 .
                     
                  


            

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Getting an angle between locations
« Reply #7 on: June 18, 2012, 01:37:46 am »


                 I tried Lightfoot8's code first, works like a charm, though admittedly I have no idea why.   Vector manipulation still throws me for a loop. 

Here's what I've got at the moment for my reaction time finding script (with Lightfoots above it):

int GetIsFacingTarget(object oSelf, object oTarget, int nViewArc)
{
 location lSelf = GetLocation(oSelf);
 location lTarget = GetLocation(oTarget);

 float AngleOffset = VectorToAngle( GetPosition(oTarget) - GetPosition(oSelf)) - GetFacing(oSelf) ;

 //float  fViewArc = 180.0;
 return (abs(FloatToInt(AngleOffset)) <  nViewArc/2);
}

//// Gets the reaction time of oSource to the perceived threat (oTarget).
//// Returned time is 0.1 to 6.0, allowing for one full surprise round in certain conditions.
float GetReactionTime (object oTarget, object oSource = OBJECT_SELF)
{
float fTime;

location lTarget = GetLocation (oTarget);
location lSource = GetLocation (oSource);
float fDistance  = GetDistanceBetweenLocations(lTarget, lSource);

//// Is lTarget seen?
if (GetIsFacingTarget (oSource, oTarget, 180) && GetObjectSeen (oTarget, oSource))
   {
     fDistance -=   IntToFloat (GetSkillRank (SKILL_SPOT, oSource));
   }
//// or heard?
else if (GetObjectHeard (oTarget, oSource))
   {
     fDistance -= IntToFloat (GetSkillRank (SKILL_LISTEN, oSource));
   }
//// Distance, modified potentially by spot/listen, +/- 0.1 per meter.
if (fDistance > 0.0) fTime += (fDistance / 10.0);

//// Dexterity modifies 0.1 per point +/-
fTime -= (IntToFloat (GetAbilityModifier (ABILITY_DEXTERITY, oSource)) / 10.0);

if (GetHasFeat (FEAT_IMPROVED_INITIATIVE, oSource))      fTime -= 0.4;
if (GetHasFeat (FEAT_EPIC_SUPERIOR_INITIATIVE, oSource)) fTime -= 0.4;

if      (fTime < 0.1) fTime = 0.1;
else if (fTime > 6.0) fTime = 6.0;

return fTime;
}

  Admittedly, it's untested still, so the values might need tweaking.  I'm only using it in the events when the NPC is out of combat, to simulate surprise.  I even threw in some use for those seldom used initiative feats in it.

Edit:  I'm not sure why that even compiled, since I was subtracting an int from a float.  My version of the GetSkillRank checks for skill synergies as well, but I editted that out in case anyone wanted to try this script.
               
               

               


                     Modifié par Failed.Bard, 18 juin 2012 - 04:17 .
                     
                  


            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Getting an angle between locations
« Reply #8 on: June 18, 2012, 04:33:58 am »


               

Failed.Bard wrote...

  I tried Lightfoot8's code first, works like a charm, though admittedly I have no idea why.  Vector manipulation still throws me for a loop. 


I will try and give an explanation.  In truth once you get use to vectors they are easy to work with and open up a lot of things you can do in your code.  

A vector is nothing more then a set of coordinates that represent where one object is from another.   where a position is coordinates that represent where a point is from the  (0,0) point  on a graph.  The vector defines both a Magnitude ( length of the vector ) and an angle.  

As an example I will place two objects on a grid  the source PC at (4,4)  facing off at a 45 degree angle to the left of  due north( up).  and the target NPC at  (13,13) .

'Image

To get the vector that the NPC is from the PC you just subtract the cords of the source from the cords of the target.
in this case that will give us a vector of (9,9).      Our magnitude of the vector will be 12.7 the distance between the two points.    and the angle will be 45 degrees.

'Image

An important note here is that a lot of people make a big fuss about what angle north is at in the game and the effects it has on there calulations.  In some cases it might.  but it really has no effect when working with vectors.   Since the vector is reltive to the two objects it really maks no differance where the 0 angle points is ,   As long as both angles are calculated from the same refferance, the answer will be correct.   

in the Pic above my 0 degree refferance is the x axis giving me a 45 deg angle for my vector.  That would   give the PC a facing of 135 deg.    

If the refferance is the y axis, for our 0 deg,  That would make the vector a -45 deg angle and the PC's facing a 45 deg angle. 
 

it just does not really matter what system the engine uses to calculate the angles, the differance between them will be the same.  90 degree result. 

I believe it is easy to see from the pic above.  how to find the angle that oNPC is from the facing of oPC. broken down it is:   
1)  Get the position of oPC.          
2 ) Get the position of oNPC.
3 ) Get the Vector between the two.
4 ) convert the vector to an angle.     
5 ) Get the facing of oPC.
6 ) subtract the facing of the PC from the vector.

Postive angles will be to left of his facing, negtive angles to the right.  


//Get the position of oPC. 
vector  posPC = GetPosition( oPC);

//Get the position of oNPC.
 vector  posNPC = GetPosition( oNPC);

//Get the Vector between the two.
vector vPCtoNPC =  posNPC - posPC ;

//convert the vector to an angle.
float AngleOfVector = VectorToAngle(vPCtoNPC);

//Get the facing of oPC. 
float PCFacing = GetFacing(oPC);
 
//subtract the facing of the PC from the vector.
float AngleOfSight =  AngleOfVector - PCFacing;
               
               

               


                     Modifié par Lightfoot8, 18 juin 2012 - 03:47 .
                     
                  


            

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Getting an angle between locations
« Reply #9 on: June 18, 2012, 05:34:32 am »


               Thanks for the explanation.  That does actually make more sense now.  Not complete sense, but at least I've got a butter idea of why it works now at least.
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Getting an angle between locations
« Reply #10 on: June 18, 2012, 05:57:05 am »


               I guess I could add a some information on Normalized vectors.  

A Normalized vector is any vector with a magnitude (length) of 1.  

It is simple to normlize a vector in NWN using the VectorNormalize(vVector) function.   

It is also simple to get the magnitude of a vector using the VectorMagnitude( vVector) function. 

multiplying a Vector by a float  changes its magnitude, but leaves the angle the same. .   this make it simple to get any location between objects.  


If you wanted to get the position half way between two objects. you simply find the vector between the two objects, cut it in half  and added it to your source object.  

vector  vVec  = posTarget - posSource; 
vector  vBetween = ( vVec/2 ) + posSource;


if you want the distance 5m in front of oTarget  simply get his facing, turn it into a vector ( Already normlized),    multiply it by 5 and add it back to your target.  

vector posTarget = GetPosition (oTarget);
vector vFacing = AngleToVector(GetFacing(oTarget));
vector  posForwardFive = posTarget + vFacing * 5;  

Once you get the hang of it, it becomes really simple, even though the code can end up looking complex.   
 
               
               

               


                     Modifié par Lightfoot8, 18 juin 2012 - 04:58 .
                     
                  


            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Getting an angle between locations
« Reply #11 on: June 18, 2012, 07:54:35 am »


               Cleaned the code just a tad.  I have no Idea why I had the two location variables in my function, So I removed them.  I also removed the IntToFloat functions in your main body, They are not needed.

 
int GetIsFacingTarget(object oSelf, object oTarget, int nViewArc)
{
  float AngleOffset = VectorToAngle( GetPosition(oTarget) - GetPosition(oSelf)) - GetFacing(oSelf) ;
 
//float fViewArc = 180.0;
  return (abs(FloatToInt(AngleOffset)) < nViewArc/2);
}

//// Gets the reaction time of oSource to the perceived threat (oTarget).
//// Returned time is 0.1 to 6.0, allowing for one full surprise round in certain conditions.
float GetReactionTime (object oTarget, object oSource = OBJECT_SELF)
{
  float fTime;

  location lTarget = GetLocation (oTarget);
  location lSource = GetLocation (oSource);
  float fDistance = GetDistanceBetweenLocations(lTarget, lSource);

  //// Is lTarget seen?
  if (GetIsFacingTarget (oSource, oTarget, 180) && GetObjectSeen (oTarget, oSource))
  {
    fDistance -=   GetSkillRank (SKILL_SPOT, oSource);
  }
  //// or heard?
  else if (GetObjectHeard (oTarget, oSource))
  {
    fDistance -= GetSkillRank (SKILL_LISTEN, oSource);
  }
  //// Distance, modified potentially by spot/listen, +/- 0.1 per meter.
  if (fDistance > 0.0) fTime += (fDistance / 10.0);

  //// Dexterity modifies 0.1 per point +/-
  fTime -= GetAbilityModifier (ABILITY_DEXTERITY, oSource) / 10.0;

  if (GetHasFeat (FEAT_IMPROVED_INITIATIVE, oSource)) fTime -= 0.4;
  if (GetHasFeat (FEAT_EPIC_SUPERIOR_INITIATIVE, oSource)) fTime -= 0.4;

  if (fTime < 0.1) fTime = 0.1;
  else if (fTime > 6.0) fTime = 6.0;

  return fTime;


               
               

               
            

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Getting an angle between locations
« Reply #12 on: June 18, 2012, 08:24:33 am »


               

Lightfoot8 wrote...

Cleaned the code just a tad.  I have no Idea why I had the two location variables in my function, So I removed them.  I also removed the IntToFloat functions in your main body, They are not needed.

 
int GetIsFacingTarget(object oSelf, object oTarget, int nViewArc)
{
  float AngleOffset = VectorToAngle( GetPosition(oTarget) - GetPosition(oSelf)) - GetFacing(oSelf) ;
 
//float fViewArc = 180.0;
  return (abs(FloatToInt(AngleOffset)) < nViewArc/2);
}

//// Gets the reaction time of oSource to the perceived threat (oTarget).
//// Returned time is 0.1 to 6.0, allowing for one full surprise round in certain conditions.
float GetReactionTime (object oTarget, object oSource = OBJECT_SELF)
{
  float fTime;

  float fDistance  = GetDistanceBetween(oTarget, oSource);

  //// Is lTarget seen?
  if (GetIsFacingTarget (oSource, oTarget, 180) && GetObjectSeen (oTarget, oSource))
  {
    fDistance -=   GetSkillRank (SKILL_SPOT, oSource);
  }
  //// or heard?
  else if (GetObjectHeard (oTarget, oSource))
  {
    fDistance -= GetSkillRank (SKILL_LISTEN, oSource);
  }
  //// Distance, modified potentially by spot/listen, +/- 0.1 per meter.
  if (fDistance > 0.0) fTime += (fDistance / 10.0);

  //// Dexterity modifies 0.1 per point +/-
  fTime -= GetAbilityModifier (ABILITY_DEXTERITY, oSource) / 10.0;

  if (GetHasFeat (FEAT_IMPROVED_INITIATIVE, oSource)) fTime -= 0.4;
  if (GetHasFeat (FEAT_EPIC_SUPERIOR_INITIATIVE, oSource)) fTime -= 0.4;

  if (fTime < 0.1) fTime = 0.1;
  else if (fTime > 6.0) fTime = 6.0;

  return fTime;


I didn't really need the location checks in mine either anymore.  They'd been in there for trying to make the bioware script work.

Edit:  I should add, without using a wrapper for DetermineCombatRound, you have to add a GetObjectSeen check into the start of that to make sure someone didn't go invisible or use HiPS during the delay.  
               
               

               


                     Modifié par Failed.Bard, 18 juin 2012 - 07:26 .
                     
                  


            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Getting an angle between locations
« Reply #13 on: June 18, 2012, 06:47:04 pm »


               

Lightfoot8 wrote...
If you wanted to get the position half way between two objects. you simply find the vector between the two objects, cut it in half  and added it to your source object.  

vector  vVec  = posTarget - posSource; 
vector  vBetween = ( vVec/2 ) + posSource;


if you want the distance 5m in front of oTarget  simply get his facing, turn it into a vector ( Already normlized),    multiply it by 5 and add it back to your target.  

vector posTarget = GetPosition (oTarget);
vector vFacing = AngleToVector(GetFacing(oTarget));
vector  posForwardFive = posTarget + vFacing * 5;  

Once you get the hang of it, it becomes really simple, even though the code can end up looking complex.   
 


Make sure multiplication/division is done by floats not integers.  (e.g. vVec/2 should be vVec/2.0, and vFacing*5 should be vFacing*5.0).
               
               

               


                     Modifié par WhiZard, 18 juin 2012 - 05:47 .
                     
                  


            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Getting an angle between locations
« Reply #14 on: June 18, 2012, 09:11:30 pm »


               'Image *hanges head*