Why can't I call "GetDistance" from within my dll?

Post » Thu Apr 14, 2016 4:07 am

Everyone is really busy, so I don't want to bug anyone specifically. So if anyone sees this and has an answer, that would be swell.

I'm trying to utilize "GetDistance" from within my .dll. I'm using OBSE 21 source code.

I found this section of code in GameOjects.h (see third line from the bottom)

Spoiler

// 058
class TESObjectREFR : public TESForm
{
public:
#if OBLIVION
MEMBER_FN_PREFIX(TESObjectREFR);
DEFINE_MEMBER_FN(IsOffLimitsToPlayer, bool, kTESObjectREFR_IsOffLimitsToPlayerAddr);
#endif

enum
{
// TESForm flags
// if(!IsActor())
kChanged_IsEmpty = 0x00010000,
// CHANGE_OBJECT_EMPTY
// no data?

kChanged_Inventory = 0x08000000,
// CHANGE_REFR_INVENTORY
// ### todo: see 0048BA40

// if((changed & ((version < 0x43) ? 0x177577F0 : 0x177577E0))) || IsActor())
// {
// // this is all part of its own function
//
// }

// if(!IsActor())
kChanged_Animation = 0x02000000,
// CHANGE_REFR_ANIMATION
// UInt16 dataLen;
// UInt8 data[dataLen];
kChanged_Move = 0x00000004,
// TESObjectCELL cell
// float pos[3]
// float rot[3]
kChanged_HavokMove = 0x00000008,
// CHANGE_REFR_HAVOK_MOVE
kChanged_MapMarkerFlags = 0x00000400,
// CHANGE_MAPMARKER_EXTRA_FLAGS
kChanged_HadHavokMoveFlag = 0x00000800,
// CHANGEFLAG_REFR_HAD_HAVOK_MOVE_FLAG
// if either of these are set
// UInt16 dataLen;
// UInt8 data[dataLen];

// if(version > 0x43)
kChanged_Scale = 0x00000010,
// CHANGE_REFR_SCALE
// float scale;

kChanged_DoorOpenDefaultState = 0x00040000,
// CHANGE_DOOR_OPEN_DEFAULT_STATE
// no data

kChanged_DoorOpenState = 0x00080000,
// CHANGE_DOOR_OPEN_STATE
// no data

kChanged_DoorExtraTeleport = 0x00100000,
// CHANGE_DOOR_EXTRA_TELEPORT

kChanged_ExtraOwner = 0x00000080,
// CHANGE_OBJECT_EXTRA_OWNER
};

enum
{
kFlags_Persistent = 0x00000400, //shared bit with kFormFlags_QuestItem
kFlags_Disabled = 0x00000800,
kFlags_Unk00000002 = 0x00000002, // set when an item reference is picked up by an actor
kFlags_Deleted = 0x00000020, // refr removed from .esp or savegame
kFlags_Unk128 = 0x80000000,
kFlags_Temporary = 0x00004000,

// both flags are set when an item picked up from gameworld
// one or the other flag by itself is not sufficient
kFlags_Taken = kFlags_Deleted | kFlags_Unk00000002
};

TESObjectREFR();
~TESObjectREFR();

virtual void Unk_37(void) = 0;
virtual void Unk_38(void) = 0; // 38
virtual void Unk_39(void) = 0;
virtual void Unk_3A(void) = 0;
virtual float GetScale(void) = 0;
virtual void GetStartingAngle(float * pos) = 0;
virtual void GetStartingPos(float * pos) = 0;
virtual void Unk_3E(void) = 0;
virtual void Unk_3F(void) = 0;
virtual void RemoveItem(TESForm* toRemove, BaseExtraList* extraList, UInt32 unk2, UInt32 unk3, UInt32 unk4, TESObjectREFR* destRef,
UInt32 unk6, UInt32 unk7, UInt32 unk8, UInt8 unk9) = 0; // 40 unk2 quantity?
virtual void Unk_41(void) = 0;
virtual void Unk_42(void) = 0;
virtual void Unk_43(void) = 0;
virtual void Unk_44(void) = 0;
virtual void AddItem(TESForm* item, ExtraDataList* xDataList) = 0;
virtual void Unk_46(void) = 0;
virtual void Unk_47(void) = 0;
virtual void Unk_48(void) = 0;
virtual MagicTarget * GetMagicTarget(void) = 0;
virtual void Unk_4A(void) = 0;
virtual void Unk_4B(void) = 0;
virtual void Unk_4C(void) = 0;
virtual void Unk_4D(void) = 0;
virtual void Unk_4E(void) = 0;
virtual void Unk_4F(void) = 0;
virtual void Unk_50(void) = 0; // 50
virtual void Unk_51(void) = 0;
virtual void Unk_52(void) = 0; // inits animation-related data, and more
virtual NiNode* GenerateNiNode(void) = 0;
virtual void Set3D(NiNode* niNode);
virtual NiNode * GetNiNode(void) = 0;
virtual void Unk_56(void) = 0;
virtual void Unk_57(UInt32 arg0) = 0;
virtual void Unk_58(void) = 0;
virtual void Unk_59(void) = 0;
virtual void Unk_5A(void) = 0;
virtual void Unk_5B(void) = 0;
virtual TESForm * GetBaseForm(void) = 0; // returns type this object references
virtual float * GetPos(void) = 0;
virtual void Unk_5E(void) = 0;
virtual void Unk_5F(void) = 0;
virtual void Unk_60(void) = 0; // 60
virtual void Unk_61(void) = 0;
virtual void Unk_62(void) = 0;
virtual UInt8 GetSleepState(void) = 0;
virtual bool IsActor(void) = 0;
virtual void ChangeCell(TESObjectCELL * newCell) = 0;
virtual bool IsDead(bool arg0) = 0;
virtual void Unk_67(void) = 0;
virtual void Unk_68(void) = 0;
virtual void Unk_69(void) = 0;

TESChildCell childCell; // 018
TESForm * baseForm; // 01C
// U8(typeInfo + 4) == 0x23 is true if the object is a character
float rotX, rotY, rotZ; // 020 - either public or accessed via simple inline accessor common to all child classes
float posX, posY, posZ; // 02C - seems to be private
float scale; // 038
NiNode * niNode; // 03C
TESObjectCELL * parentCell; // 040
BaseExtraList baseExtraList; // 044

ScriptEventList* GetEventList() const;
bool IsTaken() const { return ((flags & kFlags_Taken) == kFlags_Taken) ? true : false; }
bool IsDeleted() const;
void SetTaken(bool bTaken) {
flags = (bTaken) ? (flags | kFlags_Taken) : (flags & ~kFlags_Taken);
}
bool IsDisabled() { return flags & kFlags_Disabled ? true : false; }
void SetDisabled(bool bDisabled) {
flags = bDisabled ? (flags | kFlags_Disabled) : (flags & ~kFlags_Disabled);
}
bool IsPersistent() { return (flags & kFlags_Persistent) ? true : false; }
bool IsTemporary() { return (flags & kFlags_Temporary) ? true : false; }
TESForm * GetInventoryItem(UInt32 itemIndex, bool bGetWares);
void Disable();
void Enable();
bool RunScripts(); // runs magic effect and object scripts plus any scripts on items in inventory

bool GetTeleportCellName(BSStringT* outName);
bool Update3D();

TESContainer* GetContainer();
bool IsMapMarker();
float GetDistance(TESObjectREFR* other, bool bIncludeDisabled);

ExtraTeleport::Data* GetExtraTeleportData();

static TESObjectREFR* Create(bool bTemp = false);
};



and in GameObjects.cpp

Spoiler

float TESObjectREFR::GetDistance(TESObjectREFR* other, bool bIncludeDisabled)
{
#if OBLIVION_VERSION == OBLIVION_VERSION_1_2_416
float result;
ThisStdCall(0x0065D270, this, other, bIncludeDisabled);
__asm { fstp [result] }
#else
#error unsupported Oblivion version
#endif
return result;
}



With everything else that I do, usually I can do Actor/Reference->k_value or Actor/Reference->SomeFunction() if the value or function is a member of the calling class.

So logically, you would think that the following would work:
bool GetLOSAlt(Script* scriptObj, Actor * dActor, Actor * tActor){
... snip ...
TESObjectREFR * dRef = (TESObjectREFR*)dActor;
TESObjectREFR * tRef = (TESObjectREFR*)tActor;

float distanceToTarget = dRef->GetDistance(tRef,1);
float fScale = dRef->GetScale();

if (distanceToTarget < dDetectLifeRange){bDetectLifeApplies = 1;}
Console_Print("SDR dll: DL Check | Dist: %.4f | Scale: %.4f | Det: %s (%08X) | Targ: %s (%08X)"
,distanceToTarget
,fScale
,GetFullName(dActor)
,dActor ? dActor->refID : 0
,GetFullName(tActor)
,tActor ? tActor->refID : 0
);
.... snip ...
I've tried it with just using the actor (because TESObjectREFR is a parent class) and also having the second argument be 0.
It compiles with no errors, however the result of distanceToTarget is always 0. But the fScale returns with an accurate result. Both functions are pulled from the exact same class structure.

Can someone please explain to me why?

Why?

Why the F does this NOT work??!!!

So bloody frustrating.
User avatar
Sophie Louise Edge
 
Posts: 3461
Joined: Sat Oct 21, 2006 7:09 pm

Post » Wed Apr 13, 2016 8:42 pm

Shot in the dark:



float distanceToTarget = dRef->GetDistance(*tRef,1);
User avatar
Rachael
 
Posts: 3412
Joined: Sat Feb 17, 2007 2:10 pm

Post » Thu Apr 14, 2016 3:11 am

Didn't work. :(
User avatar
Luna Lovegood
 
Posts: 3325
Joined: Thu Sep 14, 2006 6:45 pm

Post » Wed Apr 13, 2016 4:08 pm

Hmmm, Being a function it might want a double reference. Try &tref or **tref?

User avatar
Kortknee Bell
 
Posts: 3345
Joined: Tue Jan 30, 2007 5:05 pm

Post » Thu Apr 14, 2016 5:12 am

Unfortunately no.
With * I get: "No suitable conversion function from x to y exists" (where x is whatever form I'm inputting to the required form.)
With ** I get: "No operator "*" matches these operands"

However, I have discovered that TESObjectREFR->PosX (and PosY, and PosZ) works!
So I created a function that grabs the two positions of the two objects, uses some geometry, and calculates the distance between the two.

And it totally works. I'm getting the exact same distance as GetDistance.

Problem solved.

w00t!

P.S. What this means is that I now have an alternative version of GetLineOfSight which takes into account my various peripheral field of views per race/creature as well as impacts of helmets/hoods, and detect life.

So basically, if someone is looking in your direction, even if their vision is normally blocked, if they have an active detect life spell and you are within range, then they get LOS1. It's going to be much harder to escape detection from people that have detect life now.

:)
User avatar
glot
 
Posts: 3297
Joined: Mon Jul 17, 2006 1:41 pm

Post » Wed Apr 13, 2016 11:07 pm

float GetDistance(NiAVObject* Object) {


Vector3 v;


v.x = Object->m_worldTranslate.x - (*g_thePlayer)->posX;

v.y = Object->m_worldTranslate.y - (*g_thePlayer)->posY;

v.z = Object->m_worldTranslate.z - (*g_thePlayer)->posZ;

return sqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z));


}


The example gets the distance of an object from the player.
User avatar
Euan
 
Posts: 3376
Joined: Mon May 14, 2007 3:34 pm

Post » Wed Apr 13, 2016 2:15 pm

That's sort of what I ended up doing:


/*------------------------ Gets Distance between two sets of cooridnates ---------------------------- */
float AltGetDistancePos(float x1, float y1, float z1, float x2, float y2, float z2)
{
float squared = 2.0;
float xx1 = x1-x2;
float xx2 = pow(xx1,squared);
float yy1 = y1-y2;
float yy2 = pow(yy1,squared);
float zz1 = z1-z2;
float zz2 = pow(zz1,squared);
return sqrt( xx2 + yy2 + zz2 );
}

/*------------------------ Gets Distance between two references ---------------------------- */
float AltGetDistanceRefs(TESObjectREFR * ref1,TESObjectREFR * ref2)
{
float x1 = ref1->posX;
float y1 = ref1->posY;
float z1 = ref1->posZ;
float x2 = ref2->posX;
float y2 = ref2->posY;
float z2 = ref2->posZ;
return AltGetDistancePos(x1, y1, z1, x2, y2, z2);
}
I need more flexibility than just the player though. I don't know if mine is necessarily better in terms of performance though. It's going to be called a lot, so it's a concern of mine.
User avatar
Abel Vazquez
 
Posts: 3334
Joined: Tue Aug 14, 2007 12:25 am

Post » Thu Apr 14, 2016 3:44 am

This is a bug in OBSE code. If you check the implementation of GetDistance in GameObjects.cpp you'll find this:




float TESObjectREFR::GetDistance(TESObjectREFR* other, bool bIncludeDisabled)

{

#if OBLIVION_VERSION == OBLIVION_VERSION_1_2_416

float result;

ThisStdCall(0x0065D270, this, other, bIncludeDisabled);

__asm { fstp [result] }

#else

#error unsupported Oblivion version

#endif

return result;

}

It simply calls a member function at 0x0065D270, which is not in TESObjectREFR code range.

What it actually calls is PlayerCharacter::GetActorValueModifier.

Probably by sheer luck the two functions have compatible signatures.

Otherwise you'd sure get CTD when calling it.


The correct function is at 0x004D7E90.


User avatar
Avril Louise
 
Posts: 3408
Joined: Thu Jun 15, 2006 10:37 pm

Post » Wed Apr 13, 2016 9:38 pm

So would I be better off with the correct function or just keep my code as is? It works as is at the moment.
User avatar
Katie Louise Ingram
 
Posts: 3437
Joined: Sat Nov 18, 2006 2:10 am

Post » Thu Apr 14, 2016 4:33 am

@Tiawar: I have not the Oblivion database here, but IIRC, 0x0065D270 seems (as you say) a PlayerCharacter function.


Probably it is a copy&paste from an other function that has not been corrected.

User avatar
Andrew Perry
 
Posts: 3505
Joined: Sat Jul 07, 2007 5:40 am

Post » Wed Apr 13, 2016 4:44 pm

@saebel: if you are sure the refs are valid and in the same cell/worldspace, you can use your function. The getdistance vanilla function does only more checks before calculate the distance because it is made to be called from scripts or the console, so checks are required to ensure the refs and their state.

User avatar
mishionary
 
Posts: 3414
Joined: Tue Feb 20, 2007 6:19 am

Post » Thu Apr 14, 2016 1:05 am

At the moment, I'm only planning on using it in two scenarios.

The first is detection checks. Since detection calls are only made by valid high processing actors against other valid high processing actors, I don't think it will be a problem.
The second is still a work/concept in progress where it has to deal with being in combat and whether or not they will continue to pursue the player. So in that particular case, the other actor has to be in combat with the player before the check is made.

The check is an alternative method to get line of sight that allows for extension beyond peripheral point of view as well as the detect life effect. Of course the actor must be within the range of the detect life spell and be detectable by it, so that's why I needed to get the distance between the two actors.
It returns 0 if definitely no line of site, 1 if traditional line of sight, 2 if line of sight in extended peripheral range, and 3 if line of sight through Detect Life magic only.
It works pretty good so far. I still have to finesse the detect life conditions a bit.

I did run some tests getting distances on actors that weren't anywhere near the player. It returned some insanely high number (blah blah blah E+38), which was what I was expecting. And since it didn't crash, I'm okay with that.

One glitch I discovered about GetLOS (the regular one) is that after you have killed an actor, even if you are blocked from view, it returns 1 if you are facing their direction. So if they were alive, under the same conditions it would normally return 0. Very odd.

I will also probably have to add in some checks for death state too, since there is a reverse line of sight call which should normally fail if the target is already dead.
User avatar
Peter P Canning
 
Posts: 3531
Joined: Tue May 22, 2007 2:44 am

Post » Thu Apr 14, 2016 5:14 am

Here's a simple wrapper you can use to call the internal function:


inline float GetRefDistance(TESObjectREFR *me, TESObjectREFR *other, bool allowDisabled = false)
{
float distance;

ThisStdCall(0x004D7E90, me, other, allowDisabled);

__asm
{
fstp [distance]
}

return distance;
}



This will handle all cases of same cell/world or not.

As you have already discovered the game returns FLT_MAX if the distance between the two refs is undefined.

That's a constant you can test against.

float distance = GetRefDistance(ref1, ref2);

if (distance != FLT_MAX)
{
// ok: refs are valid and in same cell/world
}
else
{
// error: refs are not in same cell/world or ref2 is invalid (NULL, disabled or no parent cell/world)
}
User avatar
Ricky Meehan
 
Posts: 3364
Joined: Wed Jun 27, 2007 5:42 pm

Post » Wed Apr 13, 2016 8:28 pm

Ah yes, the functions under contention are all nicely listed https://github.com/JRoush/Common-Oblivion-Engine-Framework/blob/master/API/TESForms/TESObjectREFR.eed and https://github.com/JRoush/Common-Oblivion-Engine-Framework/blob/master/API/Actors/ActorValues.eed. Simple really. :P

User avatar
Guy Pearce
 
Posts: 3499
Joined: Sun May 20, 2007 3:08 pm

Post » Wed Apr 13, 2016 10:33 pm

I know it sounds strange, and I do not want to interfere with OBSE development,


but wouldnt it make sense to use this functions in a OBSE addon, like a obse_math.dll, extending those functionality.



I do use this math every frame, determing the distance between two positions, xy, and xyz.


plus determing the angle in z direction.



It would be nice to have something like this in a common library.


We might keep distance from having too much redundant code.



I think this could be much faster than scripting, all in all.


Though I do not know what steps compiled code takes,


in comparison to OBSE functions and their functionality in scripting,


and native c++ code in OBSE dll.

User avatar
JESSE
 
Posts: 3404
Joined: Mon Jul 16, 2007 4:55 am

Post » Wed Apr 13, 2016 4:23 pm

Well, I could split out my internal functions from my commands and make them available as common source code. That way all these bits that I have figured out or others have contributed can be used in other plugins.


Not all, of course, since some of the stuff that is specific to SDR would be useless. But there a number of functions that I have that either do math or replicate scripted functions that might be helpful.


If folks are interested, I'll see if I can't rearrange things a bit.


Perhaps call it open_toolbox.h and open_toolbox.cpp, indicating that those files could be dragged and dropped into any OBSE .dll solution and used.


Is that the sort of thing you mean?


Or are you talking about something that's more of an OBSEE. An extension of OBSE that is developed in common with commands that could be called from scripts?


I wouldn't be opposed to that either, but I wouldn't want to duplicate code in two places. Is it possible for one .dll to talk to another if there are multiple solutions? I'm still new at this.
User avatar
A Boy called Marilyn
 
Posts: 3391
Joined: Sat May 26, 2007 7:17 am


Return to IV - Oblivion