Aller au contenu

Photo

Get*ObjectInShape - Half-Circles


  • Veuillez vous connecter pour répondre
22 réponses à ce sujet

#1
Vermain

Vermain
  • Members
  • 14 messages
I've got a reaaaallly weird question: Is there any way to do some kind of half-circle shape for the Get*ObjectInShape functions, or do something approximate to that? To explain: I'm trying to make an ability that takes flanking into account, so that if an ally and I are on opposite sides of an opponent, the ability does something in addition to what it does normally. I'm sure there's a mathematical solution, but I'm frankly an idiot when it comes to trigonometry!

Modifié par Vermain, 12 septembre 2013 - 10:32 .


#2
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Two potential answers, depending on how much you care about the precise shape.

Easy way:

// Get the first object in nShape
// - nShape: SHAPE_*
// - fSize:
//   -> If nShape == SHAPE_SPHERE, this is the radius of the sphere
//   -> If nShape == SHAPE_SPELLCYLINDER, this is the length of the cylinder
//      Spell Cylinder's always have a radius of 1.5m.
//   -> If nShape == SHAPE_CONE, this is the widest radius of the cone
//   -> If nShape == SHAPE_SPELLCONE, this is the length of the cone in the
//      direction of lTarget. Spell cones are always 60 degrees with the origin
//      at OBJECT_SELF.
//   -> If nShape == SHAPE_CUBE, this is half the length of one of the sides of
//      the cube
// - lTarget: This is the centre of the effect, usually GetSpellTargetLocation(),
//   or the end of a cylinder or cone.
// - bLineOfSight: This controls whether to do a line-of-sight check on the
//   object returned. Line of sight check is done from origin to target object
//   at a height 1m above the ground
//   (This can be used to ensure that spell effects do not go through walls.)
// - nObjectFilter: This allows you to filter out undesired object types, using
//   bitwise "or".
//   For example, to return only creatures and doors, the value for this
//   parameter would be OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR
// - vOrigin: This is only used for cylinders and cones, and specifies the
//   origin of the effect(normally the spell-caster's position).
// Return value on error: OBJECT_INVALID
object GetFirstObjectInShape(int nShape, float fSize, location lTarget, int bLineOfSight=FALSE, int nObjectFilter=OBJECT_TYPE_CREATURE, vector vOrigin=[0.0,0.0,0.0])


Use a SHAPE_CONE or SHAPE_SPELLCONE.

More complex answer:
Look at goaneng's Pentagrams and Summoning Circles. He has a hemisphere, which you can rotate in any direction on any axis (x, y, or z). While that code is only for vfx, the inc_draw include has all the vector math already done for you, and very elegantly. You should be able to adapt it pretty easily. As an added plus, he includes a demo module. When I was coding special effects for some of HG's portals, I simply added certain areas from HG to the demo mod and tinkered with the effects until I got exactly what I wanted. In any event, the testing room he provides should help you figure out rotation etc.

[Edit] Doh, just read the latter half of your post, where you do what I usually ask people to do: say what they're going to use it for. This recent thread was extremely helpful when it came to vector math, and had a very similar problem, though he wanted a deflection area in FRONT of the target in that case.

Funky

Modifié par FunkySwerve, 12 septembre 2013 - 11:04 .


#3
Vermain

Vermain
  • Members
  • 14 messages
Thanks for your assistance!! I was thinking about just using SHAPE_CONE, although I wasn't sure what I'd need to plug in to ensure it was a full half-circle. Is lSize used to establish the arc between the two radii? (God, I hope I'm using the right terminology. ._.)

#4
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Tell you what, I'll make both our lives simpler. Here's a minorly-tweaked function originally by Axe Murderer:

// Returns TRUE if object oBehind is located behind object oInFront as
// determined by the direction oInFront is facing.
int GetIsLocatedBehind( object oInFront, object oBehind){

    if( !GetIsObjectValid(oInFront) || !GetIsObjectValid( oBehind))
        return FALSE;

    int nFrontType = GetObjectType(oInFront), nRearType = GetObjectType(oBehind));

    if ((nFrontType != OBJECT_TYPE_PLACEABLE    &&
        nFrontType != OBJECT_TYPE_CREATURE)     ||
        (nRearType != OBJECT_TYPE_PLACEABLE     &&
        nRearType != OBJECT_TYPE_CREATURE)
        return FALSE;

    float fFacing = GetFacing(oInFront);
    float fBehind = VectorToAngle(GetPositionFromLocation(GetLocation(oBehind)) -GetPositionFromLocation(GetLocation( oInFront)));
    while(fFacing > 360.0)
        fFacing -= 360.0;
    while(fFacing <= 0.0)
        fFacing += 360.0;
    while(fBehind > 360.0)
        fBehind -= 360.0;
    while(fBehind <= 0.0)
        fBehind += 360.0;
    // "Behind" here is defined as 45 degrees left or right of directly 180
    // degrees from oInFront's facing direction. To narrow the "cone", change
    // the 45.0 to something smaller. Make it larger to widen it. It should
    // not be more than 90.0 or else your getting in front of the guy. 90.0
    // is directly off his left or right shoulder, 0.0 is directly behind him.
    return (fabs( fBehind -fFacing) >= (180.0 -45.0));
}

Funky

#5
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Found that using the Omnibus, by the way, so props to OldTimeRadio as well. The Krit offers another function in that thread, as well, which is titled 'Finding out if PC stands behind an NPC' - I just didn't feel like re-lineating and re-indenting a second function when that one appears to do exactly what you want.

Funky

#6
Vermain

Vermain
  • Members
  • 14 messages
Thank you a ton for your effort! I'll get a-working with this stuff right away.

#7
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
Why not just get the angle between your (attacker and target) and compare it to the angle between your (attacker and ally) if your ally is farther away then your target.

#8
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Two reasons: 1) I'm lazy 2) I wanted a generalizable solution. I rather like axe's solution, though you're right, it's not custom-tailored to OP's original question.

Also I think Axe's has some unnecessary checks, as GetFacing only returns between 0 and 360, according to the Lexicon, but hey. :P If you want to post the simplest vector math solution, I'd love to see it, as I'm still quite weak in that department.

Funky

#9
Vermain

Vermain
  • Members
  • 14 messages

Lightfoot8 wrote...

Why not just get the angle between your (attacker and target) and compare it to the angle between your (attacker and ally) if your ally is farther away then your target.


I think I might do something like this, yeah. Is it basically just:

-Get angles for myself/ally in comparison to target.
-Add 180 to ally's result.
-Subtract/add 45 degrees (or however large/small I want the flanking area to be) and see if I'm outside of that arc. (As well as making sure to apply sanity checks so I'm not at 480 degrees or something weird!)
-If so, and in range (distance check), I must be flanking.

Along those lines, anyways? I'm still working on streamlining my code, but I think I have the basic jist of it.

#10
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
int GetisFlanked( object oAttaker, object oTarget, object oAlly)
{
   vector posAttaker = GetPosition(oAttaker);
   vector posTarget = GetPosition(oTarget);
   vector posAlly = GetPosition(oAlly);

   vector VectorToTargetFromAttacker = posTarget- posAttaker ;
   float AngleToTargetFromAttacker = VectorToAngle( VectorToTargetFromAttacker);

   vector VectorToAllyFromTarget = posAlly - posTarget;
   float AngleToAllyFromTarget = VectorToAngle( VectorToAllyFromTarget);

   float DifferanceBetweenAngles = AngleToTargetFromAttacker - AngleToAllyFromTarget;

   //Adjust the angle so our results to compair will always be positive by adding
   //Half the the test range. This just allows us to be able to do a single
   //compairsion check instead of two.
   //EDIT: I Need AdjustedAngle to be an int so I can do my sanity check the way
   //I like to.
   int AdjustedAngle = FloatToInt(DifferanceBetweenAngles) + 45;

   // Sainty check. make sure the angle is in the 0 - 360 range.
   AdjustedAngle %= 360;
   if (AdjustedAngle < 0) AdjustedAngle += 360;

   // Check and return if the n adjusted Angle is +/- 45 deg
   // since we have already added 45 degrees we only need to check if it is less
   // then 90
   return AdjustedAngle <= 90;

}

Will hopefully be able to post more information reguarding the code later tonight.

EDIT: Pasting with Chrome is a pain.  

Modifié par Lightfoot8, 14 septembre 2013 - 02:43 .


#11
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

Lightfoot8 wrote...

Will hopefully be able to post more information reguarding the code later tonight.

I'd appreciate it, but thanks for posting what you did. :)

Funky

#12
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
  In Vermain's  question we have three points that we need to work with to figure out if two of them are flanking a target.   I will throw three points( A, B and C)  out and try to explain how to compare the relationships between them.  

Posted Image

  In NWN if point A, B and C where the locations of objects, we could get the position of that object by using the GetPosition function.    GetPosition returns it's data in the vector data type.   Now  many would say that is position is not a vector it is just a single point, since it is not defined in the traditional way, that being a direction and a magnitude.  Or lets just say a distance in a direction, the magnitude being the distance. 

  For that matter, the NWN data type for the vector, does not even store it in the traditional way. Instead of storing them in a structure that contains a distance and a direction element. they are stored in a structure that contains the Change in the x direction and change in the y direction. It is basically the same thing just stored in a different format. Given a direction and distance the change in x and y can be calculated and vice versa.  

  Stored in this format our position can be considered as a vector from the position (0,0). drawing the vectors for my positions would look like this.
Posted Image

so our positions are fundamentally vectors from the (0,0) position. It is important to note that vectors in themselves do not have a fixed starting location. they only represent a distance and direction that one thing is from another.

To find a vector between two points, subtract the position of your source point from the position of your destination point.
Posted Image

as shown above,  the order of the subtraction does not effect the magnitude of the vector.  both A-B and B-A have the same magnitude,   the only difference in the vectors is that they go in oppsit directions.    

For Vermain's problem, if we consider point A to the the position of the attacker and Point B to be the position of the target  it is easy to see that angle of vector B-A  will give us an angle that we can compare against.  

Posted Image

All we need to do for Vermin's problem is compare the differance between the angles for the B-A vector and the angle of the C-B vector.   

pA = GetPosition(A) 
pB = GetPosition(B) 
pC = GetPosition© 
fAngle= VectorToAngle(pB-pA)- VectortoAngle ( pC-pB)

Modifié par Lightfoot8, 22 septembre 2013 - 04:08 .


#13
WhiZard

WhiZard
  • Members
  • 1 204 messages

vector VectorToTargetFromAttacker = posTarget- posAttaker ;
float AngleToTargetFromAttacker = VectorToAngle( VectorToTargetFromAttacker);

vector VectorToAllyFromTarget = posAlly - posTarget;
float AngleToAllyFromTarget = VectorToAngle( VectorToAllyFromTarget);

float DifferanceBetweenAngles = AngleToTargetFromAttacker - AngleToAllyFromTarget;


Something doesn't look right about this. The AngleToTargetFromAttacker is supplementary to the AngleToAttackerFromTarget (new term). Thus the DifferenceBetweenAngles is supplementary to (AngleToAttackerFromTarget + AngleToAllyFromTarget) which can vary based on orientation (e.g. If both the attacker and ally are due east of the target the difference calculated would be 180.  If both the attacker and ally are due north of the target the difference calculated would be 0).

Also fabs (float absolute value) can be used without having to impose a sanity check (or go through the steps of adding half the total angle).

Modifié par WhiZard, 15 septembre 2013 - 11:11 .


#14
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages

WhiZard wrote...

vector VectorToTargetFromAttacker = posTarget- posAttaker ;
float AngleToTargetFromAttacker = VectorToAngle( VectorToTargetFromAttacker);

vector VectorToAllyFromTarget = posAlly - posTarget;
float AngleToAllyFromTarget = VectorToAngle( VectorToAllyFromTarget);

float DifferanceBetweenAngles = AngleToTargetFromAttacker - AngleToAllyFromTarget;


 Thus the DifferenceBetweenAngles is supplementary


 No the angles should not be supplementary They would be if I used VectorToAttackerfrom target. but I did not It is the vector to the target from the attacker. Hmm, perhaps I am missing what you are saying. Yes is both are the same direction from the target the difference in the angles will be 180 that is by design, since we want the difference to be 0 when they are directly opposing.


Also fabs (float absolute value) can be used without having to impose a sanity check (or go through the steps of adding half the total angle).


I have gotten burned to many times going that rout. the sanity checks are more for keeping the angles in the 360 range. Even the last script I posted for Shadow was flawed at certain angles due to omitting the sanity checks. As for the + 45, that is just the way I like to do it.

Edit:  
Posted Image


The code is using the B-A vector not the A-B vector. 

e.g. If both the attacker and ally are due east of the target the difference calculated would be 180. If both the attacker and ally are due north of the target the difference calculated would be 0).


No the difference would still be 180 in both cases, Or should be, I will double check my code later. Given I have not tested it at all.

Modifié par Lightfoot8, 16 septembre 2013 - 04:38 .


#15
WhiZard

WhiZard
  • Members
  • 1 204 messages
Rechecked and you are right. I used the term supplementary (180 - x) when I should have done opposite (180 + x). From that you get that your difference is the opposite (180 + x) of the angle formed from ally to target (vertex) to attacker which you are then limiting to the 90 degree back arc from the attacker to target by your inequality.

In this case fabs would work as there is no possibility of exceeding 360, but I understand your general dislike of it.

#16
WhiZard

WhiZard
  • Members
  • 1 204 messages
Here is a fabs rendition for those who are curious


int GetIsFlanked(object oAttacker, object oTarget, float fAngle = 90.0, object oAlly = OBJECT_INVALID)
{
float fToAttacker = VectorToAngle(GetPosition(oAttacker)- GetPosition(oTarget));
float fToFacing;
if(GetIsObjectValid(oAlly))
   fToFacing = VectorToAngle(GetPosition(oAlly) - GetPosition(oTarget));
else
   fToFacing = GetFacing(oTarget);
float fBackArc = fabs(fabs(fToAttacker - fToFacing) - 180.0);
return (fBackArc <= fAngle/2);
}

Modifié par WhiZard, 17 septembre 2013 - 03:29 .


#17
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
@WhiZ: Nice work on the double absolute.

#18
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Could one of you add a commented declaration to make clear precisely when it will return flanking/not flanking? I'm a little fuzzy on who's flanking what.

Thanks,
Funky

#19
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
Flanking is probably not the best word to use. It is more of an opposing or on opposite sides. we are checking to see if an ally is on the side of the target from the attacker.

Or in the gray shaded area below.

Posted Image

The facing of  the target does not matter.   the only check I am doing is to see if they are opposing.  

In WiZards version.   He accounted for just a single attacker with no ally  comming in from behind the target,     

note: both scripts would fail to return a TRUE result if both attacker and ally where coming in from behind the target. They are checking more for opposition then flanking.

Modifié par Lightfoot8, 22 septembre 2013 - 06:20 .


#20
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Ah, ok, I thought that's what I was reading. I guess in my mind, for a get is flanking function you would just want to know the facing of the target and the position of the attacker - not what the OP specifically requested, I know, but not all that different in the end.

Thanks,
Funky

#21
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
 I guess it would have helped if i stated I had finally finished my response above, (5 day later). 
 Link

#22
WhiZard

WhiZard
  • Members
  • 1 204 messages

FunkySwerve wrote...

Ah, ok, I thought that's what I was reading. I guess in my mind, for a get is flanking function you would just want to know the facing of the target and the position of the attacker - not what the OP specifically requested, I know, but not all that different in the end.

Thanks,
Funky


There are two approaches generally.

1) Backstab: give the target damage/penalties when attacked from behind.  For this approach the direction the target is facing is important.

2) Volleying: give the target incrementing damage/penalties as long as it remains between two creatures pushing it back and forth with their blows.  For this approach the position of the ally is important.

While my function can be used for both approaches (neglect the ally parameter if you are backstabbing), the OP seemed to describe a backstab model while asking for a function factoring in the position of the ally.  So I am not 100% sure if this addresses his concern.

#23
Vermain

Vermain
  • Members
  • 14 messages
Just wanted to say again: Thanks a whole bunch for all of this, everyone! It helps immensely. I finally think I'm starting to understand vectors (at long last!).