SDK Question read attachment id

gattispilot

Addon Developer
Addon Developer
Joined
Oct 17, 2007
Messages
10,563
Reaction score
4,386
Points
203
Location
Dallas, TX
Looking for an example how how read the attachment id of an attached vessel?

I have a parent attachment point named P1. I want to know what the id of the vessel attach to it if any.

I have this:
but I can't get stage to change when I know I have a vessel attached with the id of XS

Not sure if there is any easier way


Code:
            VECTOR3 aphpos, aphdir, aphrot, gaph;
             VECTOR3 shippos, shipdir, shiprot, gpship;
  
               GetAttachmentParams(LP1,aphpos, aphdir, aphrot);
                
  }
             // change the position to a global one rather then a local one. This will allow me to offset it from the local center.
             // the local position is stored in gaph
             Local2Global (aphpos, gaph);

             //I now know were the attach point is in the global world
             //Now lets find out if there is a skid in range
             // Search the complete vessel list for a grappling candidate.
            // Not very scalable ...
                for (DWORD i = 0; i < oapiGetVesselCount(); i++) {
                    OBJHANDLE hV = oapiGetVesselByIndex (i);    //get handle for ship
        //            if (strncmp (hV->GetClassName(), "QJFFS", 5)!=0  ){  //restrict to only QuadJ ships to make it easy.

                    // Look for any ship with a child attach point. This allows the Mule to grap any object
                    if (hV == GetHandle()) continue; // If self then continue onward because we don't want to grapple ourselves ...

                    oapiGetGlobalPos (hV, &gpship); // get global postion of ship and put into gpship


                       VESSEL *v = oapiGetVesselInterface (hV);
                       // Only look for child attach point
                       DWORD nAttach = v->AttachmentCount (true); //Get attachment count. I do this so that I can grap any child point.
                       if (nAttach > 0){ // Only continue if ship has a child attach point
                           for (DWORD j = 0; j < nAttach; j++) { // now scan all attachment points of the candidate and check how far away from the attach point it is
                               ATTACHMENTHANDLE hAtt = v->GetAttachmentHandle (true, j);
                               /* this would allow me to filter out all but QJ ships but I don't want to right now
                               const char *id = v->GetAttachmentId (hAtt);
                               if (strncmp (id, "GS", 2)) continue; // attachment point not compatible
                               */
                               v->GetAttachmentParams (hAtt, shippos, shipdir, shiprot); //lets get the attach point position
                               v->Local2Global (shippos, gpship); // and change it to a global position Recycle of gpship cause I can
                               if (dist (gpship, gaph) < MAX_GRAP_DIST) { // Is it close enough to grab?
                                   apd = dist(gpship, gaph);
                                   // Yes! Then lets grab the little bugger and get out of here
                                    const char *id = v->GetAttachmentId (hAtt);
								   
								    if(!strncmp (id, "XS", 2))
								   {stage=3;}
								}}}}
 
Looking for an example how how read the attachment id of an attached vessel?

I have a parent attachment point named P1. I want to know what the id of the vessel attach to it if any.

I have this:
but I can't get stage to change when I know I have a vessel attached with the id of XS

Not sure if there is any easier way
:facepalm:
The code you posted has nothing to do with determining the attachment id of an attached vessel, and is in fact a copy-paste of Jon Marcure's code for grappling QJ ships with some very minor (and poor) editing. That code is scanning all ships in the scenario to see if any are close enough to grab.

What you want to do is determine what ship is already attached. There's a simple function for that, which gets the handle of the ship attached to a given attachment point. You can then scan all attachments on that attached ship to figure out which one is the one that's attached to you, and then determine the id of that attachment.
 
Last edited:
Thanks Yes, it is Jon said that this might work.

Do you have an example of that simple function?


:facepalm:
The code you posted has nothing to do with determining the attachment id of an attached vessel, and is in fact a copy-paste of Jon Marcure's code for grappling QJ ships with some very minor (and poor) editing. That code is scanning all ships in the scenario to see if any are close enough to grab.

What you want to do is determine what ship is already attached. There's a simple function for that, which gets the handle of the ship attached to a given attachment point. You can then scan all attachments on that attached ship to figure out which one is the one that's attached to you, and then determine the id of that attachment.
 
Thanks Yes, it is Jon said that this might work.

Do you have an example of that simple function?
I don't have the SDK available to me right now--read through the SDK documentation for the attachment functions to find it, or wait till I get back to my apartment in eight hours and I'll let you know then.
 
Thanks. Is it this:
GetAttachmentId
Retrieve attachment identifier string.
Synopsis:
const char *GetAttachmentId (
ATTACHMENTHANDLE attachment) const
Parameters:
attachment attachment handle
Return value:
pointer to attachment string (8 characters)
 
Thanks. Is it this:
GetAttachmentId
Retrieve attachment identifier string.
Synopsis:
const char *GetAttachmentId (
ATTACHMENTHANDLE attachment) const
Parameters:
attachment attachment handle
Return value:
pointer to attachment string (8 characters)
Not quite--that will get the attachment string, yes, but it won't get the vessel. Isn't there a function that takes an attachment handle and returns a vessel handle?
 
Maybe this:
PHP:
GetAttachmentParams
Retrieve the parameters of an attachment point.
Synopsis:
void GetAttachmentParams (
ATTACHMENTHANDLE attachment,
VECTOR3 &pos,
VECTOR3 &dir,
VECTOR3 &rot) const
Parameters:
attachment attachment handle
pos attachment point position
dir attachment direction
rot longitudinal alignment vector

But the id is all I want.
 
Maybe this:
PHP:
GetAttachmentParams
Retrieve the parameters of an attachment point.
Synopsis:
void GetAttachmentParams (
ATTACHMENTHANDLE attachment,
VECTOR3 &pos,
VECTOR3 &dir,
VECTOR3 &rot) const
Parameters:
attachment attachment handle
pos attachment point position
dir attachment direction
rot longitudinal alignment vector

But the id is all I want.
:facepalm:

Right, but you need the ID of the attachment point that your attachment point is attached to.

You have ship A with attachment point P1 (your ship and your attachment point). It's attached to attachment point P2 on ship B.

If you have P2, you can get its ID via a function call, which I will represent by "->"--thus, getting the ID of P2 from the handle to P2 is "P2->P2ID".

But, you're not starting from P2--you have to get P2 somehow, and you're starting from P1. How do you do that? I'm pretty sure there's not a function that allows you to get P2 from P1 (get the attachment handle of the attach point that the passed attachment handle is attached to), but that might've been added in O2010. I'll check on that when I get to my apartment, but you won't have an answer to that for another five hours at least. If there was, you could do: P1->P2->P2ID.

What you *do* have is a function that tells you what ship is attached to P1. You can also iterate over all of the attach points on that ship to find the one that you're attached to. In effect, you can go P1->B->P2->P2ID.
 
Thanks. I need just to know what ID. Because for my Eagle3 If a pod is attach the pod's ID is XS. then stage =3. If stage =3 then I show additional thrust exhaust.
 
Thanks. I need just to know what ID. Because for my Eagle3 If a pod is attach the pod's ID is XS. then stage =3. If stage =3 then I show additional thrust exhaust.
GetAttachmentStatus
 
Thanks. This gives me the name aka handle of the attach vessel. But I need the ID.

I wonder once I get the handle then I can get the id?
PHP:
GetAttachmentStatus
Return the current status of an attachment point.
Synopsis:
OBJHANDLE GetAttachmentStatus (
ATTACHMENTHANDLE attachment) const
Parameters:
attachment attachment handle
Return value:
Handle of the attached vessel, or NULL if no vessel is attached to this point.



I got this from Jon
Code:
const char *id = GetAttachmentId (GetAttachmentStatus(P1));
if(!strncmp (id, "XS", 2))stage=3;

No CTD now to find out if a vessel attached at P1 with a id of XS makes stage =3.
 
Last edited:
I got this from Jon
Code:
const char *id = GetAttachmentId (GetAttachmentStatus(P1));
if(!strncmp (id, "XS", 2))stage=3;
That code won't actually work--GetAttachmentStatus returns the vessel handle, not the attachment handle. Once you have the vessel handle, you need to iterate over all attachment points to determine which one is attached to your vessel, then call GetAttachmentId on that one.
 
ok. Now at a lost. I guess I could go with the vessel name. It just limits it.
 
I've told you all the functions that you need and exactly what to do with them, and you still can't figure it out? :facepalm:

The following code assumes that P1 is the attachment handle for which we want to check the reciprocal attachment's ID, and that the attachment on the attached vessel is defined as a "to parent" attachment.

Code:
    // Get the handle and vessel pointer for the other ship
    OBJHANDLE hOtherShip = GetAttachmentStatus(P1);
    VESSEL * pOtherShip = oapiGetVesselInterface(hOtherShip);
 
    // loop over all to-parent attachments on the other ship
    for (int i = 0; i < pOtherShip->AttachmentCount(true); i++)
    {
        ATTACHMENTHANDLE hCurAttachment = pOtherShip->GetAttachmentHandle(true, i);
        // skip any attach point that isn't attached to us
        if (pOtherShip->GetAttachmentStatus(hCurAttachment) != GetHandle())
            continue;
        // check for the attachment ID and set stage = 3 if it starts with "XS"
        const char *id = pOtherShip->GetAttachmentId (hCurAttachment);
        if(!strncmp (id, "XS", 2))
            stage=3;
    }

I haven't actually tried this in a compiler, so there may be issues, but that's basically what you're looking to do.
 
Thanks. I have this:
it is put in in this:
void EAGLE3::clbkPostStep(double simt, double simdt, double mjd)

Code:
// Get the handle and vessel pointer for the other ship
    OBJHANDLE hOtherShip = GetAttachmentStatus(P1);
    VESSEL * pOtherShip = oapiGetVesselInterface(hOtherShip);
 
    // loop over all to-parent attachments on the other ship
    for (int i = 0; i < pOtherShip->AttachmentCount(true); i++)
    {
        ATTACHMENTHANDLE hCurAttachment = pOtherShip->GetAttachmentHandle(true, i);
        // skip any attach point that isn't attached to us
        if (pOtherShip->GetAttachmentStatus(hCurAttachment) != GetHandle())
            continue;
        // check for the attachment ID and set stage = 3 if it starts with "XS"
        const char *id = pOtherShip->GetAttachmentId (hCurAttachment);
        if(!strncmp (id, "XS", 2))
            stage=3;
    }

the only error I get is this
.\EAGLE3.cpp(847) : warning C4018: '<' : signed/unsigned mismatch

added this line because if no attachment it CTD
if (GetAttachmentStatus(P1)){


but I don't think it is working as stage is never set to 3.
I have this:
sprintf(oapiDebugString(),"anim %2.2f", stage);
and it reads 0.00
with the attachment id as XS
 
Last edited:
Thanks. I have this:
it is put in in this:
void EAGLE3::clbkPostStep(double simt, double simdt, double mjd)

Code:
// Get the handle and vessel pointer for the other ship
    OBJHANDLE hOtherShip = GetAttachmentStatus(P1);
    VESSEL * pOtherShip = oapiGetVesselInterface(hOtherShip);
 
    // loop over all to-parent attachments on the other ship
    for (int i = 0; i < pOtherShip->AttachmentCount(true); i++)
    {
        ATTACHMENTHANDLE hCurAttachment = pOtherShip->GetAttachmentHandle(true, i);
        // skip any attach point that isn't attached to us
        if (pOtherShip->GetAttachmentStatus(hCurAttachment) != GetHandle())
            continue;
        // check for the attachment ID and set stage = 3 if it starts with "XS"
        const char *id = pOtherShip->GetAttachmentId (hCurAttachment);
        if(!strncmp (id, "XS", 2))
            stage=3;
    }

the only error I get is this
.\EAGLE3.cpp(847) : warning C4018: '<' : signed/unsigned mismatch

added this line because if no attachment it CTD
if (GetAttachmentStatus(P1)){


but I don't think it is working as stage is never set to 3.
I have this:
sprintf(oapiDebugString(),"anim %2.2f", stage);
and it reads 0.00
with the attachment id as XS
What is the entirety of the code you have currently?
 
Thanks. here is the cpp
Code:
// ==============================================================
//                 ORBITER MODULE: EAGLE3
//                  Part of the ORBITER SDK
//          Copyright (C) 2002-2004 Martin Schweiger
//                   All rights reserved
//
// EAGLE3.cpp
// Control module for EAGLE3 vessel class
//

// ==============================================================
#define ORBITER_MODULE
#define ENG 				1	
#define START 				2	
#define STOP 				3
#define CANNON 				4
#include "orbitersdk.h"
#include "EAGLE3.h"
#include "EAGLE3MESH.h"
#include "OrbiterSoundSDK35.h"
VISHANDLE MainExternalMeshVisual = 0;

	static PARTICLESTREAMSPEC contrail_main = {
		0, 8.0, 5, 150, 0.3, 4.0, 4, 2.0, PARTICLESTREAMSPEC::DIFFUSE,
		PARTICLESTREAMSPEC::LVL_SQRT, 0, 1,
		PARTICLESTREAMSPEC::ATM_PLOG, 1e-4, 1
	};
	static PARTICLESTREAMSPEC exhaust_main = {
		0, 2.0, 20, 150, 0.1, 0.2, 16, 2.0, PARTICLESTREAMSPEC::EMISSIVE,
		PARTICLESTREAMSPEC::LVL_SQRT, 0, 1,
		PARTICLESTREAMSPEC::ATM_PLOG, 1e-5, 0.1
	};
	static PARTICLESTREAMSPEC exhaust_hover = {
		0, 1.0, 5, 50, 0.3, 2.0, 3, 2.0, PARTICLESTREAMSPEC::DIFFUSE,
		PARTICLESTREAMSPEC::LVL_SQRT, 0, 1,
		PARTICLESTREAMSPEC::ATM_PLOG, -0.1, 0.1
	};

void EAGLE3::SetUMMUAirlockPos(void)
{//{
int AirlockStatus=Crew.GetAirlockDoorState();
	Crew.SetActiveDockForTransfer(iActiveDockNumber);
	switch(iActiveDockNumber)
	{
	case 0:	//front
Crew.DefineAirLockShape(AirlockStatus,-3,3,-4,3,3,6);			
		Crew.SetMembersPosRotOnEVA(_V(0,-1.948,5),_V(0,3,0));	
	
			doorselected=0;
		break;
	case 1:	//rear
		Crew.DefineAirLockShape(AirlockStatus,-3,3,-4,3,-8,-4);		
	Crew.SetMembersPosRotOnEVA(_V(0,-1.948,-5),_V(0,0,0));
	doorselected=1;
		break;
		case 2:	//tpadl
					Crew.DefineAirLockShape(AirlockStatus,-45,-33,-10,13,-4,4);		
	Crew.SetMembersPosRotOnEVA(_V(-39,-3.85,-1),_V(0,0,0));
	doorselected=2;
		break;
				case 3:	//tpadright
		Crew.DefineAirLockShape(AirlockStatus,33,42,-10,13,-4,4);		
	Crew.SetMembersPosRotOnEVA(_V(39,-3.85,-1),_V(0,0,0));
	//				Crew.DefineAirLockShape(AirlockStatus,-45,-33,-10,13,-4,4);		
	//Crew.SetMembersPosRotOnEVA(_V(-39,-3.85,-1),_V(0,0,0));

	doorselected=3;
		break;
		case 4:	
		Crew.DefineAirLockShape(AirlockStatus,-20,20,-20,13,0,200);		
	Crew.SetMembersPosRotOnEVA(_V(12,-11.234,89),_V(0,0,0));
	doorselected=4;
		break;
				case 5:	
		Crew.DefineAirLockShape(AirlockStatus,-20,20,-20,13,-200,0);		
	Crew.SetMembersPosRotOnEVA(_V(-12,-11.234,-89),_V(0,0,0));
	doorselected=5;
		break;
	}
	}


void EAGLE3::clbkPostCreation (void)

{
JohnSoundID=ConnectToOrbiterSoundDLL3(GetHandle());
SoundOptionOnOff3(JohnSoundID,PLAYMAINTHRUST,TRUE);

SoundOptionOnOff3(JohnSoundID,PLAYHOVERTHRUST,TRUE);
SoundOptionOnOff3(JohnSoundID,PLAYATTITUDETHRUST,TRUE);

SoundOptionOnOff3(JohnSoundID,PLAYCOUNTDOWNWHENTAKEOFF,FALSE);

SoundOptionOnOff3(JohnSoundID,PLAYCABINAIRCONDITIONING,FALSE);

SoundOptionOnOff3(JohnSoundID,PLAYCABINRANDOMAMBIANCE,FALSE);

SoundOptionOnOff3(JohnSoundID,PLAYRADIOATC,FALSE);

SoundOptionOnOff3(JohnSoundID,DISPLAYTIMER,FALSE);
}// Constructor






EAGLE3::EAGLE3 (OBJHANDLE hObj, int fmodel)
: VESSEL2 (hObj, fmodel)


{
MAX_GRAP_DIST =10;
MAX_GRAP_DISTHEAD =30;
DOOR_status = DOOR_UP;
BUG_status = DOOR_UP;
BUGHATCH_status = DOOR_UP;


DOOR1_status = DOOR_UP;
DOOR2_status = DOOR_UP;
DOOR3_status = DOOR_UP;
GEAR_status = GEAR_UP;
IGNITION = 0;
GEAR_proc = 0;
BUG_proc = 0;
BUGHATCH_proc = 0;


LIFT_SPEED =.25;
light_SPEED =.5;
radar_speed=.5;
CAM=0;
	DefineAnimations ();
stage=1;

   BUG_pos.x =  0;
    BUG_pos.y = -.591;
    BUG_pos.z = -6.792;

	

	// Localización de los motores principales
	MainEngineOfs[0]=_V(    0,-0.76,-15.9);
	MainEngineOfs[1]=_V(    0, 1.89,-15.9);
	MainEngineOfs[2]=_V( 2.45, 0.57,-15.9);
	MainEngineOfs[3]=_V(-2.45, 0.57,-15.9);
	//Motores elevadores
	// Los de la estructura principal
	HoverEngineOfs[0]=_V( 0.64,-1.5,9.378);
	HoverEngineOfs[1]=_V(-0.64,-1.5,9.378);
	HoverEngineOfs[2]=_V(-0.64,-1.5,-9.399);
	HoverEngineOfs[3]=_V( 0.64,-1.5,-9.399);
	// Los del módulo de carga
	HoverEngineOfs[4]=_V( 1.077,-1.5, 4.122);
	HoverEngineOfs[5]=_V(-1.077,-1.5,4.122);
	HoverEngineOfs[6]=_V(-1.077,-1.5,-4.14);
	HoverEngineOfs[7]=_V( 1.077,-1.5,-4.14);
	// Impulsores de control de posición del cuerpo principal
	RCSTransporterOfs[0]=_V(6.236659,-0.3,7.873591);
	RCSTransporterOfs[1]=_V(-6.236659,-0.3,7.873591);
	RCSTransporterOfs[2]=_V(6.236659,-0.3,-7.955545);
	RCSTransporterOfs[3]=_V(-6.236659,-0.3,-7.955545);

	// Creación del tanque de combustible
	ph_main=CreatePropellantResource(MAX_FUEL);

}

////////////////////////////////////////////////////////
// SendHudMessage
////////////////////////////////////////////////////////
char *EAGLE3::SendHudMessage()
{
dHudMessageDelay=15;
return cUmmuHudDisplay;
}


void EAGLE3::SelectCockpitView (int CAM)
{
	
	      switch (CAM) {
       case 0:  //COCKPIT
           SetCameraDefaultDirection (_V(0,0,1));
SetCameraOffset (_V(2.186,2.9,13));
          //SetCameraOffset (_V(-1.362,1.633,11.752));
          break;
		  case 1:  //COCKPIT
           SetCameraDefaultDirection (_V(0,0,1));
SetCameraOffset (_V(-2.186,2.9,13));
          //SetCameraOffset (_V(-1.362,1.633,11.752));
          break;
		  case 2:  //ABOVE
        SetCameraDefaultDirection (_V(0,-1,0));

          SetCameraOffset (_V(0,7,-1.061));
          break;
		      
      }
            }



//void EAGLE3::clbkSetClassCaps (FILEHANDLE cfg)
void EAGLE3::SetTransporter()
{
	// physical specs
	
	SetSize(EAGLE_SIZE);
	SetPMI (PMI);
	SetCrossSections (CROSS_SECTION);
	SetCW (0.2, 0.2, 1.5, 1.5);
	SetRotDrag (ROT_DRAG);
	SetPitchMomentScale(PITCH_MOMENT_SCALE);
	SetBankMomentScale (BANK_MOMENT_SCALE);
	
	
	

	SetTrimScale (0.05);
	//SetCameraOffset (_V(-1.362,1.633,11.752));
	SetCameraOffset (_V(-2.18532,2.9,13));
	SetClipRadius(0.1);
	SetTouchdownPoints  (_V(0,-3.124,8.7), _V(-5.45,-3.124,-9.6), _V(5.45,-3.124,-9.6));; 
AddTransporterExhaust();
DOCKHANDLE main1=CreateDock (_V(0,0.8,15.8), _V(0,0,1), _V(0,1,0));
ATTACHMENTHANDLE MAIN;
MAIN = CreateAttachment (true, _V(0,-3.131,0),_V(0,-1,0),_V(0,0,1),"APR",false);
P1 = CreateAttachment (false, _V(0,1.68,0),_V(0,1,0),_V(1,0,0),"TOP",false);
BUG = CreateAttachment (false, _V(0,-.591,-6.792),_V(0,-1,0),_V(1,0,0),"EGLEB",false);
//TOP = CreateAttachment (true, _V(0,2.806,0),_V(0,-1,0),_V(0,0,1),"EGLETP",false);
EnableTransponder (true);
EnableIDS (main1, true);
SetIDSChannel(main1,8);

if (GetAltitude()<10)
		// Esta llamada fuerza los humos a activos
		HoverSmoke=PasarLosHumos(false);
	else
		// En caso contrario, se fuerzan los humos a inactivos
		HoverSmoke=PasarLosHumos(true);


    static VECTOR3 beaconpos[4] = {{-4.373,     1.07,   8}, //red
                                   {  -4.373,     1.07,   -8},  //red
                                   {4.434,     1.07,  8}, //red
                                   {  4.434,   1.07,       -8},   //blue
                                   
	                              };

    static VECTOR3 beaconcol[4] = {{1,0,0}, //red
	                               {1,0,0}, //red
	                               {0,1,0}, //Green
	                               {0, 1, 0},//Green
								  	                              };

    for (int i = 0; i < 4; i++) {
        beacon[i].shape = BEACONSHAPE_STAR;
        beacon[i].pos = beaconpos+i;
        beacon[i].col = beaconcol+i;
        beacon[i].size = 0.2;
        beacon[i].falloff =  0.3;
        beacon[i].period = 0.8;
        beacon[i].duration = 0.3;
        beacon[i].tofs = (6-i)*0.2;
        beacon[i].active = true;
        AddBeacon (beacon+i);
    }
// Call clbkSetClassCaps_UMMU and select a member
	SelectedUmmuMember=0;
	clbkSetClassCaps_UMMu();

	
// visual specs
SetMeshVisibilityMode (AddMesh (oapiLoadMeshGlobal ("EAGLEBUGGY4B")),  MESHVIS_EXTERNAL | MESHVIS_EXTPASS); //Main ship mesh  | MESHVIS_EXTPASS
SetMeshVisibilityMode (AddMesh (oapiLoadMeshGlobal ("EAGLEVC1")),  MESHVIS_COCKPIT ); //Main ship mesh
SetMeshVisibilityMode (AddMesh (oapiLoadMeshGlobal ("EAGLEVC1")),  MESHVIS_VC ); //Main ship mesh
  
	}
DLLCLBK void ovcTimestep (VESSEL *vessel, double simt)
{
	((EAGLE3*)vessel)->Estado();
}

void EAGLE3::AddTransporterExhaust()
{
	int i,NumHover;

	//Se crean los motores principales y sus escapes con humo
	for (i=0; i<4; i++)
	{
		th_main[i] = CreateThruster (_V(0,0,0), _V(0,0,1), MAX_MAIN_THRUST, ph_main, ISP_FUS);
		AddExhaust (th_main[i],2,0.5,_V(MainEngineOfs[i].x,MainEngineOfs[i].y,MainEngineOfs[i].z+1),_V(0,0,-1));
		AddExhaustStream (th_main[i], MainEngineOfs[i], &exhaust_main);
	};
	AddExhaustStream (th_main[1],_V(0,0,-24),&contrail_main);

	// Se crea el grupo motor principal
	thg_main = CreateThrusterGroup (th_main, 4, THGROUP_MAIN);

for (i=0; i<4; i++)
	{
	//	th_retro[i] = CreateThruster (_V(0,0,0), _V(0,0,1), MAX_MAIN_THRUST, ph_main, ISP_QUI);
	//	AddExhaust (th_retro[0],2,0.1,_V(6.234,-0.34,8.432),_V(0,0,1));
	//	AddExhaust (th_retro[1],2,0.1,_V(-6.234,-0.34,8.432),_V(0,0,1));
	//	AddExhaust (th_retro[2],2,0.1,_V(6.234,-0.34,-7.384),_V(0,0,1));
	//	AddExhaust (th_retro[3],2,0.1,_V(-6.234,-0.34,-7.384),_V(0,0,1));
	};
//thg_retro=CreateThrusterGroup(th_retro,4,THGROUP_RETRO);
//{
//		th_retro[4]=CreateThruster(_V(0,0,0),_V(0,0,-1), MAX_RETRO_THRUST, ph_main, ISP_QUI);
//		AddExhaust (th_retro[0],2,0.1,_V(6.234,-0.34,8.432),_V(0,0,1));
//		AddExhaust (th_retro[1],2,0.1,_V(-6.234,-0.34,8.432),_V(0,0,1));
//		AddExhaust (th_retro[2],2,0.1,_V(6.234,-0.34,-7.384),_V(0,0,1));
//		AddExhaust (th_retro[3],2,0.1,_V(-6.234,-0.34,-7.384),_V(0,0,1));
//	}

	// Se crea el grupo motor retropropulsor
//	thg_retro=CreateThrusterGroup(th_retro,4,THGROUP_RETRO);
	//Motores elevadores
	if (stage==1)
	{
		SetEngineLevel(ENGINE_HOVER,0.0);
		for (i=3; i<8; i++)
			DelThruster (th_hover[i]);
		NumHover=4;
	}

	else
		NumHover=8;
	for (i=0; i<NumHover; i++)
	{
		th_hover[i] = CreateThruster (_V(0,0,0), _V(0,1,0), MAX_HOVER_THRUST, ph_main, ISP_QUI);
		AddExhaust (th_hover[i], 0.3, 0.5,_V(HoverEngineOfs[i].x,HoverEngineOfs[i].y+1.3,HoverEngineOfs[i].z),_V(0,-1,1));
	}
	
	
	// Se crea el grupo motor elevador
	thg_hover = CreateThrusterGroup (th_hover, NumHover, THGROUP_HOVER);

	// Impulsores de control de posición
	// Para mejorar la navegabilidad, los motores están ubicados en posiciones diferentes a los escapes

	// Grupo delantero derecho

	// Delantero derecho superior
	th_rcs[0]=CreateThruster(_V(6,0,10),_V(0,-1,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[0],1,0.1,_V(6.253,.05,7.891),_V(0,1,0));
	// Delantero derecho inferior
	th_rcs[1]=CreateThruster(_V(6,0,10),_V(0,1,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[1],1,0.1,_V(6.253,-.713,7.891),_V(0,-1,0));
	// Delantero derecho proa
	th_rcs[2]=CreateThruster(_V(6,0,10),_V(0,0,-1),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[2],1,0.1,_V(6.234,-0.34,8.432),_V(0,0,1));
	// Delantero derecho popa
	th_rcs[3]=CreateThruster(_V(6,0,10),_V(0,0,1),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[3],1,0.1,_V(6.234,-0.34,7.375),_V(0,0,-1));
	// Delantero derecho exterior
	th_rcs[4]=CreateThruster(_V(6,0,10),_V(-1,0,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[4],1,0.1,_V(6.563,-.34,7.891),_V(1,0,0));

	// Grupo delantero izquierdo

	// Delantero izquierdo superior
	th_rcs[5]=CreateThruster(_V(-6,0,10),_V(0,-1,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[5],1,0.1,_V(-6.253,.05,7.891),_V(0,1,0));
	// Delantero izquierdo inferior
	th_rcs[6]=CreateThruster(_V(-6,0,10),_V(0,1,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[6],1,0.1,_V(-6.253,-.713,7.891),_V(0,-1,0));
	// Delantero izquierdo proa
	th_rcs[7]=CreateThruster(_V(-6,0,10),_V(0,0,-1),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[7],1,0.1,_V(-6.234,-0.34,8.432),_V(0,0,1));
	// Delantero izquierdo popa
	th_rcs[8]=CreateThruster(_V(-6,0,10),_V(0,0,1),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[8],1,0.1,_V(-6.234,-0.34,7.375),_V(0,0,-1));
	// Delantero izquierdo exterior
	th_rcs[9]=CreateThruster(_V(-6,0,10),_V(1,0,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[9],1,0.1,_V(-6.563,-.34,7.891),_V(-1,0,0));

	// Grupo trasero derecho

	// Trasero derecho superior
	th_rcs[10]=CreateThruster(_V(6,0,-10),_V(0,-1,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[10],1,0.1,_V(6.253,.05,-7.917),_V(0,1,0));
	// Trasero derecho inferior
	th_rcs[11]=CreateThruster(_V(6,0,-10),_V(0,1,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[11],1,0.1,_V(6.253,-.713,-7.917),_V(0,-1,0));
	// Trasero derecho proa
	th_rcs[12]=CreateThruster(_V(6,0,-10),_V(0,0,-1),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[12],1,0.1,_V(6.234,-0.34,-7.384),_V(0,0,1));
	// Trasero derecho popa
	th_rcs[13]=CreateThruster(_V(6,0,-10),_V(0,0,1),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[13],1,0.1,_V(6.234,-0.34,-8.45),_V(0,0,-1));
	// Trasero derecho exterior
	th_rcs[14]=CreateThruster(_V(6,0,-10),_V(-1,0,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[14],1,0.1,_V(6.563,-.34,-7.917),_V(1,0,0));

	// Grupo trasero izquierdo

	// Trasero izquierdo superior
	th_rcs[15]=CreateThruster(_V(-6,0,-10),_V(0,-1,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[15],1,0.1,_V(-6.253,.05,-7.917),_V(0,1,0));
	// Trasero izquierdo inferior
	th_rcs[16]=CreateThruster(_V(-6,0,-10),_V(0,1,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[16],1,0.1,_V(-6.253,-.713,-7.917),_V(0,-1,0));
	// Trasero izquierdo proa
	th_rcs[17]=CreateThruster(_V(-6,0,-10),_V(0,0,-1),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[17],1,0.1,_V(-6.234,-0.34,-7.384),_V(0,0,1));
	// Trasero izquierdo popa
	th_rcs[18]=CreateThruster(_V(-6,0,-10),_V(0,0,1),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[18],1,0.1,_V(-6.234,-0.34,-8.45),_V(0,0,-1));
	// Trasero izquierdo exterior
	th_rcs[19]=CreateThruster(_V(-6,0,-10),_V(1,0,0),MAX_RCS_THRUST,ph_main,ISP_QUI);
	AddExhaust (th_rcs[19],1,0.1,_V(-6.563,-.34,-7.917),_V(-1,0,0));

	// Se crean los grupos motores para los diferentes movimientos de la nave

	// 1-Movimientos rotacionales

	// Grupo motor de cabeceo hacia arriba "PITCH-UP"
	th_group[0]=th_rcs[1];
	th_group[1]=th_rcs[6];
	th_group[2]=th_rcs[10];
	th_group[3]=th_rcs[15];
	CreateThrusterGroup(th_group,4,THGROUP_ATT_PITCHUP);

	// Grupo motor de cabeceo hacia abajo "PITCH-DOWN"
	th_group[0]=th_rcs[0];
	th_group[1]=th_rcs[5];
	th_group[2]=th_rcs[11];
	th_group[3]=th_rcs[16];
	CreateThrusterGroup(th_group,4,THGROUP_ATT_PITCHDOWN);

	// Grupo motor de balanceo izquierda "YAW-RIGHT"
	th_group[0]=th_rcs[8];
	th_group[1]=th_rcs[18];
	th_group[2]=th_rcs[2];
	th_group[3]=th_rcs[12];
	CreateThrusterGroup(th_group,4,THGROUP_ATT_YAWRIGHT);

	// Grupo motor de balanceo derecha "YAW-LEFT"
	th_group[0]=th_rcs[3];
	th_group[1]=th_rcs[13];
	th_group[2]=th_rcs[7];
	th_group[3]=th_rcs[17];
	CreateThrusterGroup(th_group,4,THGROUP_ATT_YAWLEFT);

	// Grupo motor de alabeo derecho "BANK-RIGHT"
	th_group[0]=th_rcs[0];
	th_group[1]=th_rcs[6];
	th_group[2]=th_rcs[10];
	th_group[3]=th_rcs[16];
	CreateThrusterGroup(th_group,4,THGROUP_ATT_BANKRIGHT);

	// Grupo motor de alabeo izquierdo "BANK-LEFT"
	th_group[0]=th_rcs[1];
	th_group[1]=th_rcs[5];
	th_group[2]=th_rcs[11];
	th_group[3]=th_rcs[15];
	CreateThrusterGroup(th_group,4,THGROUP_ATT_BANKLEFT);

	// 2-Movimientos traslacionales

	// Grupo motor de traslación derecha "RIGHT"
	// Este grupo lo podían formar los impulsores izquierdos delantero de proa y trasero de popa
	th_group[0]=th_rcs[9];
	th_group[1]=th_rcs[19];
	CreateThrusterGroup(th_group,2,THGROUP_ATT_RIGHT);

	// Grupo motor de traslación izquierda "LEFT"
	// Este grupo lo podían formar los impulsores derechos delantero de proa y trasero de popa
	th_group[0]=th_rcs[4];
	th_group[1]=th_rcs[14];
	CreateThrusterGroup(th_group,2,THGROUP_ATT_LEFT);

	// Grupo motor de traslación arriba "DOWN"
	th_group[0]=th_rcs[0];
	th_group[1]=th_rcs[5];
	th_group[2]=th_rcs[10];
	th_group[3]=th_rcs[15];
	CreateThrusterGroup(th_group,4,THGROUP_ATT_DOWN);

	// Grupo motor de traslación abajo "UP"
	th_group[0]=th_rcs[1];
	th_group[1]=th_rcs[6];
	th_group[2]=th_rcs[11];
	th_group[3]=th_rcs[16];
	CreateThrusterGroup(th_group,4,THGROUP_ATT_UP);

	// Grupo motor de traslación adelante "FORWARD"
	th_group[0]=th_rcs[3];
	th_group[1]=th_rcs[8];
	th_group[2]=th_rcs[13];
	th_group[3]=th_rcs[18];
	CreateThrusterGroup(th_group,4,THGROUP_ATT_FORWARD);

	// Grupo motor de traslación atrás "BACK"
	th_group[0]=th_rcs[2];
	th_group[1]=th_rcs[7];
	th_group[2]=th_rcs[12];
	th_group[3]=th_rcs[17];
	CreateThrusterGroup(th_group,4,THGROUP_ATT_BACK);
}

void EAGLE3::SetRetros(bool ban)
{
	int i;
	double MAX_RETRO_THRUST;

	if (ban)
		MAX_RETRO_THRUST=160000.0;
	else
		MAX_RETRO_THRUST=0.0;
	//Se crean los motores retropropulsores
	for (i=0; i<4; i++)
	{
		th_retro[4]=CreateThruster(_V(0,0,0),_V(0,0,-1), MAX_RETRO_THRUST, ph_main, ISP_QUI);
		AddExhaust (th_retro[0],2,0.1,_V(6.234,-0.34,8.432),_V(0,0,1));
		AddExhaust (th_retro[1],2,0.1,_V(-6.234,-0.34,8.432),_V(0,0,1));
		AddExhaust (th_retro[2],2,0.1,_V(6.234,-0.34,-7.384),_V(0,0,1));
		AddExhaust (th_retro[3],2,0.1,_V(-6.234,-0.34,-7.384),_V(0,0,1));
	}

	// Se crea el grupo motor retropropulsor
	thg_retro=CreateThrusterGroup(th_retro,4,THGROUP_RETRO);
}

bool EAGLE3::PasarLosHumos(bool humo)
{
	//	PARTICLESTREAMSPEC contrail_hover = {
//		0, 1.0, 5, 50, 0.3, 2.0, 3, 2.0, PARTICLESTREAMSPEC::DIFFUSE,
//		PARTICLESTREAMSPEC::LVL_SQRT, 0, 1,
//		PARTICLESTREAMSPEC::ATM_PLOG, -0.1, 0.1
//	};
int NumHover, i;
	double level, alt;
	level=GetThrusterGroupLevel(THGROUP_HOVER);
	alt=GetAltitude();
	// If stage=1 only 4 hovers, here's no cargo module
	if (stage==1)
		NumHover=4;
	else
		NumHover=8;
	// Conditions to add smoke to hovers:
	// 1: Altitude below 10m
	// 2: Here's no previous defined smoke for hovers (humo flag in false condition)
	if ((alt <10) && (!humo))
	{
		for (i=0; i<NumHover; i++)
			hover_humo[i]=AddExhaustStream (th_hover[i], _V(HoverEngineOfs[i].x,HoverEngineOfs[i].y-7.5,HoverEngineOfs[i].z), &exhaust_hover);
		return true;
	};
	// Conditions to delete smoke to hovers:
	// 1: Altitude above 10m
	// 2: Previosly defined smoke for hovers (humo flag in true condition)
	if ((alt>10) && (humo))

	{
		SetThrusterGroupLevel(THGROUP_HOVER,0.0);
		for (i=0; i<NumHover; i++)
			DelExhaustStream (hover_humo[i]);
		SetThrusterGroupLevel(THGROUP_HOVER,level);
		return false;
	}
	return humo;
}


void EAGLE3::clbkSetClassCaps_UMMu(void)
{
	//--------------------------------------------------------------
	// InitUmmu
	// initialisation of Ummu must be done first ! return of 1 mean OK 
	// return of -999 mean Ummu addon is not installed in user's Orbiter, return of -1 
	// mean misc error. If error simply all function will fail silently 
	// and there will be no MMu. It's a good idea to warn the user. 
	// (see function "WarnUserUMMUNotInstalled" below)
	Crew.InitUmmu(GetHandle());	
	//--------------------------------------------------------------

	//--------------------------------------------------------------
	// GetUserUMmuVersion
	// this check wich version of UMmu addon the user have in his orbiter directory.
	// this may be usefull for future version with more function so you can check
	// if user installation support the new functions. Return -1 on error (not installed, version cannot be read)
	// or version number in float value (ie: 2.0="2.0" version etc etc)
	float UMmuVersion=Crew.GetUserUMmuVersion();

	//--------------------------------------------------------------
	// DefineAirLockShape
	// We set the airlock shape and state, this virtual box will be the one
	// into were a Ummu asking to reenter will be taken in account
	// first parameter is airlock door state (OPEN=TRUE) and other are X,X1,Y,Y1 and Z,Z1 
	// min and max coordinate for this virtual box (vessel local coordinate)
	// Pay attention to reenter when landed (if suitable) and make the box large enough so 
	// it's not a pain for the user to find airlock's entry. (See PDF doc)
	Crew.DefineAirLockShape(FALSE,-3,3,-4,3,3,6);			// Airlock open, 2 meter large 4 meter high 6 meter long (vessel local coordinate)

	//--------------------------------------------------------------
	// SetMembersPositionOrientationOnEVA
	// We define here were the Ummu will appear when they make an EVA
	// first parameter is position second is orientation, take care not to make
	// them appear to high on ground (will fall and die) or to low (Orbiter "light speed" bug)
	Crew.SetMembersPosRotOnEVA(_V(0,-1.948,5),_V(0,3,0));	// 3 meters in front of ship (z) facing direction of ship (vessel local coordinate)

	//---------------------------------------------------------------
	// finally set the maximum seat available in this ship, default is
	// 8 but it can go from 1 to 100
	// BAD IDEA: using this to "close" your ship, use "SetAirlockDoorState" instead.
	Crew.SetMaxSeatAvailableInShip(2);

	//----------------------------------------------------------------
	// OPTIONAL OPTIONAL OPTIONAL OPTIONAL OPTIONAL OPTIONAL OPTIONAL
	// DeclareActionArea
	// Ever wanted to be able to open a door, repair a system or trigger something in your vessel from an UMMU ?
	// Here come a new feature of UMMU 2.0: Action area. See UMmuSDK.h and PDF doc for complete explanation and see
	// "DetectActionAreaActivated" below.
	//Crew.DeclareActionArea(0,_V(-28,0,32.8),10,TRUE,"action_door.wav","Open/Close Travel Tube Doors");
	//Crew.DeclareActionArea(1,_V(54,0,0),5,TRUE, "action_door.wav","Open/Close Travel Tube Doors");
	//Crew.DeclareActionArea(2,_V(61,0,-11.75),2,TRUE, "action_door.wav","Inner AIrlock Active");
	//Crew.DeclareActionArea(3,_V(65.5,0,-11.75),2,TRUE, "action_door.wav","Outer AIrlock Active");

	//Crew.DeclareActionArea(3,_V(0,0,-5),2.5,TRUE,"action_activated.wav","You are behind the ship");
	//iActionAreaDemoStep=0;	// this is just to show a feature of action area, see below "DetectActionAreaActivated"

	//--------------------------------------------------------------
	// AddCrewMember
	// We'll add four default members for when the ship is spawned by Orbiter's scenario editor. 
	// parameters are name, age, cardiac pulse,weight (in kilogramm) and Function (Misc ID)
	// max four characters wich define the spacesuit used. (see UmmuMiscID in UMuSDK.h header)
	// Return 1 on success -1 on error (probably vessel full, name>25 char 
	// age, pulse or weight out of realistic range or MiscID>4 char)
	Crew.AddCrewMember("Peter Falcon",41,65,74,"Capt");		//(for name and id a-z A-Z 0-9 characters only)
	Crew.AddCrewMember("Fanny Gorgeous",27,67,55,"Eng");	//(for name and id a-z A-Z 0-9 characters only)
	Crew.AddCrewMember("George HealGood",15,70,45,"Doc");	//(for name and id a-z A-Z 0-9 characters only)
	Crew.AddCrewMember("Albert Jr Falcon",15,70,45);		//(for name and id a-z A-Z 0-9 characters only)
	SelectedUmmuMember =0;  // our current selected member


	// The HUD display method variables, see PDF doc
	cUmmuHudDisplay[0] =0;	// Initialisation of UMmu hud char variable
	dHudMessageDelay =0;	// Initialisation of UMmu delay variable
	strcpy(SendHudMessage(),"Welcome aboard ! E=EVA 1,2=select UMmu D=Open/Close airlock S=info M=add crew");

	// The Add mmu without scenery editor variable see PDF doc
	cAddUMmuToVessel[0]=0;
}
DLLCLBK void ovcSetClassCaps (VESSEL *vessel, FILEHANDLE cfg)
{
	((EAGLE3*)vessel)->SetTransporter();
}

void EAGLE3::clbkPostStep(double simt, double simdt, double mjd)

{

//---------------------------------------------------------------------------
	// ProcessUniversalMMu
	// Here the routine that detect if someone entered the ship (eva or transfer)
	// No need to manage anything here all is automatic, crew is added to your ship, ship's weight is updated,
	// and UMmu vessel is automatically deleted from Orbiter. You may just look the return
	// code to see if someone entered and display wathewer message on panel or other.
	// notice it's FPS friendly, function process only 4 time per second not each frame 
	// and return immediately if airlock closed or no Ummu is detected in vincinity. 
	int ReturnCode=Crew.ProcessUniversalMMu();
	switch(ReturnCode)
	{
	case UMMU_TRANSFERED_TO_OUR_SHIP: 
		sprintf(SendHudMessage(),"%s \"%s\" aged %i was transfered to our ship",
		Crew.GetCrewMiscIdByName(Crew.GetLastEnteredCrewName()),Crew.GetLastEnteredCrewName()
		,Crew.GetCrewAgeByName(Crew.GetLastEnteredCrewName()));
		break;
	case UMMU_RETURNED_TO_OUR_SHIP:
		sprintf(SendHudMessage(),"%s \"%s\" aged %i entered into our ship",
		Crew.GetCrewMiscIdByName(Crew.GetLastEnteredCrewName()),
		Crew.GetLastEnteredCrewName(),Crew.GetCrewAgeByName(Crew.GetLastEnteredCrewName()));
		break;
	}
	if(GroundContact()==TRUE)
	{
		// we check vertical speed
		//int I;
		VECTOR3 vHorizonAirspeedVector={0};
		GetHorizonAirspeedVector (vHorizonAirspeedVector);
		double VertSpeed		=vHorizonAirspeedVector.y;
		if(VertSpeed<-3)
		{
			// we touched ground with more than -3 m/s, sorry dude, time to kill you all :(
			//for(I=0;I<Crew.GetCrewTotalNumber();I++)
			//{
		//		Crew.SetCrewMemberPulseBySlotNumber(I,0);	// set cardiac pulse to zero
		//	}
		//	strcpy(SendHudMessage(),"Oooh no ! Crash - All crew aboard killed");
		}

		// TIPS: the vertical speed is often reset to zero when there is ground contact
		// this may bug somewhat the death of your crew.
		// to have an accurate VertSpeed at touchdown I recommand to record it at very END 
		// of timestep and use this "old" value. The next frame you'll have the vertspeed value 
		// of *last frame* just before the crash (GroundContact). This ensure an accurate 
		// verticalspeed value. (keep this value in your vessel class and don't forget to 
		// initialize it at zero in setclasscap)
	}

sprintf(oapiDebugString(),"anim %2.2f", stage);

	//---------------------------------------------------------------------------
	// WarnUserUMMUNotInstalled - IMPORTANT helper
	// Put here this function and users will be automatically warned if they don't have
	// UMMU installed or if it's outdated. Warning text duration is 20 seconds and text is:
	// [AddonName] require "Universal MMU" ver 2.0 or higher. Download at www.orbiter.dansteph.com (message countdown in seconds)
	// an additonal "your version of UMMU is outdated" is displayed the last 5 seconds if they have version lower than 2.0
	Crew.WarnUserUMMUNotInstalled("EAGLE3");



	// THIS IS FOR ADDING CREW SEE PDF doc "Allow user to add crew to your ship 
	// without scenery editor"
	AddUMmuToVessel();
	

if (GEAR_status >= GEAR_RAISING) {
		double da = simdt * LIFT_SPEED;
		if (GEAR_status == GEAR_RAISING) {
			if (GEAR_proc > 0.0) GEAR_proc = max (0.0, GEAR_proc-da);
			else                GEAR_status = GEAR_UP;
		} else {
			if (GEAR_proc < 1.0) GEAR_proc = min (1.0, GEAR_proc+da);
			else                GEAR_status = GEAR_DOWN;
		}
SetAnimation (anim_gear, GEAR_proc);
}

if (DOOR_status >= DOOR_RAISING) {
		double da = simdt * LIFT_SPEED;
		if (DOOR_status == DOOR_RAISING) {
			if (DOOR_proc > 0.0) DOOR_proc = max (0.0, DOOR_proc-da);
			else                DOOR_status = DOOR_UP;
		} else {
			if (DOOR_proc < 1.0) DOOR_proc = min (1.0, DOOR_proc+da);
			else                DOOR_status = DOOR_DOWN;
		}
SetAnimation (anim_adoor, DOOR_proc);
SetAnimation (anim_adoor1, DOOR_proc);


}

if (BUGHATCH_proc==1){
if (BUG_status >= DOOR_RAISING) {
		double da = simdt * LIFT_SPEED;
		if (BUG_status == DOOR_RAISING) {
			if (BUG_proc > 0.0) BUG_proc = max (0.0, BUG_proc-da);
			else                BUG_status = DOOR_UP;
		} else {
			if (BUG_proc < 1.0) BUG_proc = min (1.0, BUG_proc+da);
			else                BUG_status = DOOR_DOWN;
		}
SetAnimation (anim_BUG, BUG_proc);


}
}
if (BUG_proc==0){
if (BUGHATCH_status >= DOOR_RAISING) {
		double da = simdt * LIFT_SPEED;
		if (BUGHATCH_status == DOOR_RAISING) {
			if (BUGHATCH_proc > 0.0) BUGHATCH_proc = max (0.0, BUGHATCH_proc-da);
			else                BUGHATCH_status = DOOR_UP;
		} else {
			if (BUGHATCH_proc < 1.0) BUGHATCH_proc = min (1.0, BUGHATCH_proc+da);
			else                BUGHATCH_status = DOOR_DOWN;
		}
SetAnimation (anim_BUGdoor, BUGHATCH_proc);


}}

if (DOOR1_status >= DOOR_RAISING) {
		double da = simdt * LIFT_SPEED;
		if (DOOR1_status == DOOR_RAISING) {
			if (DOOR1_proc > 0.0) DOOR1_proc = max (0.0, DOOR1_proc-da);
			else                DOOR1_status = DOOR_UP;
		} else {
			if (DOOR1_proc < 1.0) DOOR1_proc = min (1.0, DOOR1_proc+da);
			else                DOOR1_status = DOOR_DOWN;
		}
SetAnimation (anim_adoor2, DOOR1_proc);
SetAnimation (anim_adoor3, DOOR1_proc);

}


//if (DOOR3_status >= DOOR_RAISING) {
//		double da = simdt * LIFT_SPEED;
//		if (DOOR3_status == DOOR_RAISING) {
//			if (DOOR3_proc > 0.0) DOOR3_proc = max (0.0, DOOR3_proc-da);
//			else                DOOR3_status = DOOR_UP;
//		} else {
//			if (DOOR3_proc < 1.0) DOOR3_proc = min (1.0, DOOR3_proc+da);
//			else                DOOR3_status = DOOR_DOWN;
//		}
//SetAnimation (anim_adoor6, DOOR3_proc);
//SetAnimation (anim_adoor7, DOOR3_proc);
//SetAnimation (anim_fdoor6, DOOR3_proc);
//SetAnimation (anim_fdoor7, DOOR3_proc);

//}
//
SetTouchdownPoints (_V(0,-3.124+GEAR_proc*1.5,8.7), _V(-2,-3.124+GEAR_proc*1.5,-9.6), _V(2,-3.124+GEAR_proc*1.5,-9.6));

//radar
double db = simdt * radar_speed;
            radar_proc += db;
            if( radar_proc > 1.0 ) 
                radar_proc = 0;
//        SetAnimation( anim_radar, radar_proc );

double dc = simdt * light_SPEED;
            light_proc += dc;
            if( light_proc > 1.0 ) 
                light_proc = 0;
        SetAnimation( anim_light, light_proc );



{BUG_pos.y=BUG_INT_POS-((BUG_proc)*2.533);
SetAttachmentParams(BUG,BUG_pos,_V(0,-1,0),_V(1,0,0));

}
//if  (GetAttachmentStatus(P1))
//{
//   const char *id = GetAttachmentId (GetAttachmentStatus(P1));
//if(!strcmp (id, "XS" ))POD=1;
//}
//else{
        //something already attached
						   
//}
if  (GetAttachmentStatus(P1)){
// Get the handle and vessel pointer for the other ship
    OBJHANDLE hOtherShip = GetAttachmentStatus(P1);
    VESSEL * pOtherShip = oapiGetVesselInterface(hOtherShip);
 
    // loop over all to-parent attachments on the other ship
    for (int i = 0; i < pOtherShip->AttachmentCount(true); i++)
    {
        ATTACHMENTHANDLE hCurAttachment = pOtherShip->GetAttachmentHandle(true, i);
        // skip any attach point that isn't attached to us
        if (pOtherShip->GetAttachmentStatus(hCurAttachment) != GetHandle())
            continue;
        // check for the attachment ID and set stage = 3 if it starts with "XS"
        const char *id = pOtherShip->GetAttachmentId (hCurAttachment);
        if(!strncmp (id, "XS", 2))
            stage=3;
    }



}
}
void EAGLE3::DefineAnimations(void)

{

   	static UINT EGrp33[3] = {GRP_elevpad1,GRP_elevpad2,GRP_eaglebugrods};//landing gear
 static MGROUP_TRANSLATE BUG(0,EGrp33, 3, _V(0,-2.5,0));
	anim_BUG = CreateAnimation (0);
	AddAnimationComponent  (anim_BUG, 0, 1, &BUG);    

	static UINT EGrp43[2] = {GRP_innerhatch,GRP_outerbuggyhatch};
   static MGROUP_ROTATE BUGH (0,EGrp43, 2, _V(-1.12,-.875,-7.599
	   ), _V(1,0,0), (float)(90*RAD));
   anim_BUGdoor = CreateAnimation (0);
   AddAnimationComponent  (anim_BUGdoor, 0, 1, &BUGH);



   	static UINT EGrp1[7] = {GRP_gear1,GRP_gear2,GRP_gear3,GRP_gear4,GRP_pad1a,GRP_pad2,GRP_gear7};//landing gear
 static MGROUP_TRANSLATE gear(0,EGrp1, 7, _V(0,1.52,0));
	anim_gear = CreateAnimation (0);
	AddAnimationComponent  (anim_gear, 0, 1, &gear);    

		static UINT EGrp2[2] = {GRP_door3a,GRP_door3b};//door3
   	 static MGROUP_TRANSLATE adoor(0,EGrp2, 2, _V(-.5,0,0));
	anim_adoor = CreateAnimation (0);
	AddAnimationComponent  (anim_adoor, 0, 1, &adoor); 

	static UINT EGrp3[2] = {GRP_door4a,GRP_door4b};//door4
	static MGROUP_TRANSLATE adoor1(0,EGrp3, 2, _V(.5,0,0));
	anim_adoor1 = CreateAnimation (0);
	AddAnimationComponent  (anim_adoor1, 0, 1, &adoor1);    

		static UINT EGrp6[2] = {GRP_door5a,GRP_door5b};//door5
   	 static MGROUP_TRANSLATE adoor2(0,EGrp6, 2, _V(.5,0,0));
	anim_adoor2 = CreateAnimation (0);
	AddAnimationComponent  (anim_adoor2, 0, 1, &adoor2); 

	static UINT EGrp7[2] = {GRP_door6a,GRP_door6b};//door6
	static MGROUP_TRANSLATE adoor3(0,EGrp7, 2, _V(-.5,0,0));
	anim_adoor3 = CreateAnimation (0);
	AddAnimationComponent  (anim_adoor3, 0, 1, &adoor3);    	
	
//static UINT EGrp10[4] = {GRP_door1,GRP_door1b,GRP_Edoor2a,GRP_Edoor2b};//door1
//   	 static MGROUP_TRANSLATE adoor6(4,EGrp10, 2, _V(.5,0,0));//
//	anim_adoor6 = CreateAnimation (0);
//	AddAnimationComponent  (anim_adoor6, 0, 1, &adoor6); 

//	static UINT EGrp11[4] = {GRP_door2,GRP_door2b,GRP_Edoor1a,GRP_Edoor1b};//door2
//	static MGROUP_TRANSLATE adoor7(4,EGrp11, 3, _V(-.5,0,0));
//	anim_adoor7 = CreateAnimation (0);
//	AddAnimationComponent  (anim_adoor7, 0, 1, &adoor7);    


	static UINT EGrp4[2] = {GRP_light1,GRP_light};
   static MGROUP_ROTATE light (0,EGrp4, 2, _V(.007,0,-.616), _V(0,1,0), (float)(360*RAD));
   anim_light = CreateAnimation (0);
   AddAnimationComponent  (anim_light, 0, 1, &light);

 
//   static UINT EGrp5[1] = {GRP_radar};
//   static MGROUP_ROTATE radar (0,EGrp5, 1, _V(0,0,6.661), _V(0,1,0), (float)(360*RAD));
         
//   anim_radar = CreateAnimation (0);
      
//   AddAnimationComponent  (anim_radar, 0, 1, &radar);


}
DLLCLBK VESSEL *ovcInit (OBJHANDLE hvessel, int flightmodel)
{
return new EAGLE3 (hvessel, flightmodel);
}

DLLCLBK void ovcExit (VESSEL *vessel)
{
if (vessel) delete (EAGLE3*)vessel;
}

// --------------------------------------------------------------
// Keyboard interface handler (buffered key events)
// --------------------------------------------------------------
int EAGLE3::clbkConsumeBufferedKey(DWORD key, bool down, char *kstate)
{
	// only process keydown events
	if (!down) 
		return 0; 

//---------------------------------------------------------------------------
	// Ummu Key "E" perform the EVA of the selected member
	//
	// ADD REALISM: It's your responsabilities also to set ship's control accordingly to crew aboard.
	//				If you want to disable control if no one is aboard have a look at "SetADCtrlMode()"
	//				and "SetAttitudeMode()" functions of Orbiter. To disable thrusters set their fuel 
	//				ressource to NULL.
	if(key==OAPI_KEY_E&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		// PERFORM THE EVA, first we get is name with "GetCrewNameBySlotNumber" then we perform EVA with "EvaCrewMember"
		int Returned=Crew.EvaCrewMember(Crew.GetCrewNameBySlotNumber(SelectedUmmuMember));
		// we provide feedback to user (You can display a message on panel or wathewer)
		// here below all the return code possible:
		switch(Returned)
		{
		case TRANSFER_TO_DOCKED_SHIP_OK:
			sprintf(SendHudMessage(),"Transfer to docked ship Ok - %s transfered",
			Crew.GetLastEvaedCrewName());SelectedUmmuMember=0;
			break;
		case EVA_OK:
			sprintf(SendHudMessage(),"EVA OK - %s left the ship",
			Crew.GetLastEvaedCrewName());SelectedUmmuMember=0;
			break;
		case ERROR_NO_ONE_ON_BOARD:
			strcpy(SendHudMessage(),"Error, no one on board, unable to EVA");
			break;
		case ERROR_AIRLOCK_CLOSED:
			strcpy(SendHudMessage(),"Error, airlock is closed, unable to EVA");
			break;
		case ERROR_DOCKED_SHIP_HAVE_AIRLOCK_CLOSED:
			strcpy(SendHudMessage(),"Error, docked ship's airlock is closed, unable to transfer");
			break;
		case ERROR_DOCKED_SHIP_IS_FULL:
			strcpy(SendHudMessage(),"Error, docked ship is already full transfer failed");
			break;
		case ERROR_CREW_MEMBER_NOT_FOUND:
			strcpy(SendHudMessage(),"Error, no crew by this name in ship");
			break;
		case ERROR_DOCKEDSHIP_DONOT_USE_UMMU:
			strcpy(SendHudMessage(),"Error, docked ship do not use UMmu 2.0, ask author to add it");
			break;
		case ERROR_MISC_ERROR_EVAFAILED:
			strcpy(SendHudMessage(),"Misc error with UMMU install it again");
			break;
		}
		return TRUE;
	}

	//---------------------------------------------------------------------------
	// Ummu Key "1" Select next member This is just internal to the demo
	// you may do your own selection system by panel button, name etc etc
	if(key==OAPI_KEY_1&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		// we test there is someone aboard
		if(Crew.GetCrewTotalNumber()==0)
		{
			strcpy(SendHudMessage(),"Sorry no one aboard unable to select");	
			return 1;
		}

		// we test that we select existing member
		if(SelectedUmmuMember<Crew.GetCrewTotalNumber()-1)
			SelectedUmmuMember++;
		char * Name=Crew.GetCrewNameBySlotNumber(SelectedUmmuMember);
		sprintf(SendHudMessage(),"Slot %i  %s \"%s\" aged %i Selected for EVA or Transfer, please press \"E\" to EVA",
		SelectedUmmuMember,Crew.GetCrewMiscIdBySlotNumber(SelectedUmmuMember),
		Name,Crew.GetCrewAgeBySlotNumber(SelectedUmmuMember));
		return 1;
	}

	//---------------------------------------------------------------------------
	// Ummu Key "2" Select previous member This is just internal to the demo
	// you may do your own selection system by panel button
	if(key==OAPI_KEY_2&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		// we test there is someone aboard
		if(Crew.GetCrewTotalNumber()==0)
		{
			strcpy(SendHudMessage(),"Sorry no one aboard unable to select");	
			return 1;
		}
		if(SelectedUmmuMember>0)
			SelectedUmmuMember--;
		char * Name=Crew.GetCrewNameBySlotNumber(SelectedUmmuMember);
		sprintf(SendHudMessage(),"Slot %i %s \"%s\" aged %i Selected for EVA or Transfer"
			", please press \"E\" to EVA",SelectedUmmuMember,
			Crew.GetCrewMiscIdBySlotNumber(SelectedUmmuMember),Name,
			Crew.GetCrewAgeBySlotNumber(SelectedUmmuMember));
		return 1;
	}
	//---------------------------------------------------------------------------
	// Ummu Key "A" Switch the virtual UMMU airlock door on/off
	if(key==OAPI_KEY_D&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{		
	//
		Crew.SetAirlockDoorState(!Crew.GetAirlockDoorState());
		// display state
		if (doorselected==0)
			{RevertDOOR ();
		if(Crew.GetAirlockDoorState()==TRUE)
			strcpy(SendHudMessage(),"Forward Airlock is now open");	
		else
			strcpy(SendHudMessage(),"Forward Airlock is now closed");
		}

	
if (doorselected==1)
{RevertDOOR1();
if(Crew.GetAirlockDoorState()==TRUE)
			strcpy(SendHudMessage(),"Rear Airlock is now open");	
		else
			strcpy(SendHudMessage(),"Rear Airlock is now closed");

	}
if (doorselected==2)
{
if(Crew.GetAirlockDoorState()==TRUE)
			strcpy(SendHudMessage(),"TPADL Airlock is now open");	
		else
			strcpy(SendHudMessage(),"TPADL Airlock is now closed");

}
if (doorselected==3)
{
if(Crew.GetAirlockDoorState()==TRUE)
			strcpy(SendHudMessage(),"TPADR Airlock is now open");	
		else
			strcpy(SendHudMessage(),"TPADR Airlock is now closed");

}
if (doorselected==4)
{
if(Crew.GetAirlockDoorState()==TRUE)
			strcpy(SendHudMessage(),"APADL Airlock is now open");	
		else
			strcpy(SendHudMessage(),"APADL Airlock is now closed");

}
if (doorselected==5)
{
if(Crew.GetAirlockDoorState()==TRUE)
			strcpy(SendHudMessage(),"APADR Airlock is now open");	
		else
			strcpy(SendHudMessage(),"APADR Airlock is now closed");

}


	}

	//---------------------------------------------------------------------------
	// Get some infos Name of ship and total soul aboard
	if(key==OAPI_KEY_S)
	{
		sprintf(SendHudMessage(),"%i souls aboard ship %s, %i seats available",
			Crew.GetCrewTotalNumber(),GetName(),2-Crew.GetCrewTotalNumber());
		return 1;
	}

	//---------------------------------------------------------------------------
	// ADD some Fun, Eject the guy, No check of all return code here to keep listing small and clear.
	// Notice eject function doesn't check airlock state at all.
	// GOOD IDEA: Get the Object's handle after ejection with function "GetObjHandleOfLastEVACrew"
	// and add to it one very small tank, thruster and smoke, then fire thruster (see pilot ejection of DGIV)
	// BAD IDEA: Not testing the handle returned by "GetObjHandleOfLastEVACrew" before using may
	// cause a CTD if by any bad luck the pointer is invalid (handle==NULL)
	if(key==OAPI_KEY_ESCAPE&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		if(Crew.EjectCrewMember(Crew.GetCrewNameBySlotNumber(SelectedUmmuMember))==EVA_OK)
			sprintf(SendHudMessage(),"%s EJECTED",Crew.GetLastEvaedCrewName());
		SelectedUmmuMember=0;
		return 1;
	}

	//---------------------------------------------------------------------------
	// Use a different Mesh (type "C" then EVA someone)
	// better idea is to use the new UMMU Id definition
	// look readme.txt in folder "config/UMMUIdConfig"
	if(key==OAPI_KEY_C)
	{
		Crew.SetAlternateMeshToUseForEVASpacesuit("mmu");	// the stock mmu of orbiter located in "meshes/mmu.msh"
		strcpy(SendHudMessage(),"Mesh changed");
		return 1;
	}

	// THIS IS FOR ADDING CREW SEE PDF doc "Allow user to add crew to your ship 
	// without scenery editor"
	if(key==OAPI_KEY_M&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		AddUMmuToVessel(TRUE);
	}
	//---------------------------------------------------------------------------
	

	//---------------------------------------------------------------------------
	// Get some infos Name of ship and total soul aboard
	
 // change active dock
    if(key==OAPI_KEY_9)
    {
        if(iActiveDockNumber>0)
        iActiveDockNumber--;
     //   sprintf(SendHudMessage(),"Active dock number changed to: %i",iActiveDockNumber);
		if(iActiveDockNumber==0) sprintf(SendHudMessage(),"Front Airlock Selected");
		if(iActiveDockNumber==1) sprintf(SendHudMessage(),"Rear Airlock Selected");
		if(iActiveDockNumber==2) sprintf(SendHudMessage(),"TPADL Airlock Selected");
		if(iActiveDockNumber==3) sprintf(SendHudMessage(),"TPADR Airlock Selected");
		if(iActiveDockNumber==4) sprintf(SendHudMessage(),"APADL Airlock Selected");
		if(iActiveDockNumber==5) sprintf(SendHudMessage(),"APADR Airlock Selected");

        SetUMMUAirlockPos();
        return 1;
    }
    //---------------------------------------------------------------------------
    // change active dock
    if(key==OAPI_KEY_0)
    {
        if(iActiveDockNumber<5)
        iActiveDockNumber++;
 //   sprintf(SendHudMessage(),"Active dock number changed to: %i",iActiveDockNumber);
		if(iActiveDockNumber==0) sprintf(SendHudMessage(),"Front Airlock Selected");
		if(iActiveDockNumber==1) sprintf(SendHudMessage(),"Rear Airlock Selected");
		if(iActiveDockNumber==2) sprintf(SendHudMessage(),"TPADL Airlock Selected");
		if(iActiveDockNumber==3) sprintf(SendHudMessage(),"TPADR Airlock Selected");
		if(iActiveDockNumber==4) sprintf(SendHudMessage(),"APADL Airlock Selected");
		if(iActiveDockNumber==5) sprintf(SendHudMessage(),"APADR Airlock Selected");

        SetUMMUAirlockPos();
        return 1;
    }
     if(key==OAPI_KEY_G)
	{// hatch
               RevertGEAR();
               return 1;
	}       
    if(key==OAPI_KEY_7)
	{// hatch
		//if (BUG_proc==0)
		{
               RevertBUGHATCH();
		}
               return 1;
	} 
	    if(key==OAPI_KEY_8)
	{
		//if (BUGHATCH_proc==1)
		{
               RevertBUGELEV();
		}
               return 1;
	}       
	if(key==OAPI_KEY_K)
	{do_attach = 1;
             AttachCargo();
             return 1;
	}
     if(key==OAPI_KEY_J)
		 // "Jettison"
	 {             DetachCargo();
             return 1;	
	 }
	 	if(key==OAPI_KEY_N)
	{do_attach = 1;
             AttachBUGGY();
             return 1;
	}
     if(key==OAPI_KEY_B)
		 // "Jettison"
	 {             DetachBUGGY();
             return 1;	
	 }
if(key==OAPI_KEY_V)
{
             {SelectCockpitView(CAM);
	            
	            CAM = CAM + 1;
            if(CAM > 2) CAM = 0;
        }
            return 1;
}
 
 

 
		return 0;
}

// ====================================================================
// clbkVisualCreated used to display UMMU initialisation message 
// because oapiDebugString() doesn't work in clbkSetClassCap
// ====================================================================
void EAGLE3::clbkVisualCreated (VISHANDLE vis, int refcount)
{   
	MainExternalMeshVisual = GetMesh(vis,0);
}
// ==============================================================
// Visual destroyed
// ==============================================================
void EAGLE3::clbkVisualDestroyed (VISHANDLE vis, int refcount)
{
	MainExternalMeshVisual = 0;	
}


void EAGLE3::RevertGEAR (void)
{
	GEAR_status = ((GEAR_status == GEAR_UP || GEAR_status == GEAR_RAISING) ?
		GEAR_LOWERING : GEAR_RAISING);
}

void EAGLE3::RevertDOOR (void)
{
	DOOR_status = ((DOOR_status == DOOR_UP || DOOR_status == DOOR_RAISING) ?
		DOOR_LOWERING : DOOR_RAISING);
}

void EAGLE3::RevertDOOR1 (void)
{
	DOOR1_status = ((DOOR1_status == DOOR_UP || DOOR1_status == DOOR_RAISING) ?
		DOOR_LOWERING : DOOR_RAISING);
}
void EAGLE3::RevertDOOR2 (void)
{
	DOOR2_status = ((DOOR2_status == DOOR_UP || DOOR2_status == DOOR_RAISING) ?
		DOOR_LOWERING : DOOR_RAISING);
}
void EAGLE3::RevertBUGHATCH (void)
{
	BUGHATCH_status = ((BUGHATCH_status == DOOR_UP || BUGHATCH_status == DOOR_RAISING) ?
		DOOR_LOWERING : DOOR_RAISING);
}
void EAGLE3::RevertBUGELEV (void)
{
	BUG_status = ((BUG_status == DOOR_UP || BUG_status == DOOR_RAISING) ?
		DOOR_LOWERING : DOOR_RAISING);
}
void EAGLE3::RevertDOOR3 (void)
{
	DOOR3_status = ((DOOR3_status == DOOR_UP || DOOR3_status == DOOR_RAISING) ?
		DOOR_LOWERING : DOOR_RAISING);
}


void EAGLE3::AttachCargo(){
             VECTOR3 aphpos, aphdir, aphrot, gaph;
             VECTOR3 shippos, shipdir, shiprot, gpship;

             // get attachment point positions based on selected point
             {
               
                    GetAttachmentParams(P1,aphpos, aphdir, aphrot);
                
              }  
             // change the position to a global one rather then a local one. This will allow me to offset it from the local center.
             // the local position is stored in gaph
             Local2Global (aphpos, gaph);

             //I now know were the attach point is in the global world
             //Now lets find out if there is a skid in range
             // Search the complete vessel list for a grappling candidate.
            // Not very scalable ...
                for (DWORD i = 0; i < oapiGetVesselCount(); i++) {
                    OBJHANDLE hV = oapiGetVesselByIndex (i);    //get handle for ship
        //            if (strncmp (hV->GetClassName(), "QJFFS", 5)!=0  ){  //restrict to only QuadJ ships to make it easy.

                    // Look for any ship with a child attach point. This allows the Mule to grap any object
                    if (hV == GetHandle()) continue; // If self then continue onward because we don't want to grapple ourselves ...

                    oapiGetGlobalPos (hV, &gpship); // get global postion of ship and put into gpship


                       VESSEL *v = oapiGetVesselInterface (hV);
                       // Only look for child attach point
                       DWORD nAttach = v->AttachmentCount (true); //Get attachment count. I do this so that I can grap any child point.
                       if (nAttach > 0){ // Only continue if ship has a child attach point
                           for (DWORD j = 0; j < nAttach; j++) { // now scan all attachment points of the candidate and check how far away from the attach point it is
                               ATTACHMENTHANDLE hAtt = v->GetAttachmentHandle (true, j);
                               /* this would allow me to filter out all but QJ ships but I don't want to right now
                               const char *id = v->GetAttachmentId (hAtt);
                               if (strncmp (id, "GS", 2)) continue; // attachment point not compatible
                               */
                               v->GetAttachmentParams (hAtt, shippos, shipdir, shiprot); //lets get the attach point position
                               v->Local2Global (shippos, gpship); // and change it to a global position Recycle of gpship cause I can
                               if (dist (gpship, gaph) < MAX_GRAP_DIST) { // Is it close enough to grab?
                                   apd = dist(gpship, gaph);
                                   // Yes! Then lets grab the little bugger and get out of here
                                    const char *id = v->GetAttachmentId (hAtt);
								   (do_attach=0);
								   if(!strncmp (id, "TOP", 3))
								   {do_attach=1;}
								   if(!strncmp (id, "XS", 2))
								   {do_attach=1;
								   
								   }
								   
								 
				   
								   if(do_attach > 0){
                                     
								 
								   
								 
                                     do_attach = 0;
                                   
                                      if ((v->GetAttachmentStatus(hAtt))||(GetAttachmentStatus(P1))) { 
 // do nothing;
      }else{
             AttachChild (hV, P1, hAtt);
                                               break;
                                        
                                   }
                                  return;
                                         }
                                 }// <-- removed return from here
                                   }}
                            } // End of for j
                       }
                              
          

void EAGLE3::DetachCargo ()
{
    
             DetachChild(P1,0);
           stage=1;
             
}

void EAGLE3::AttachBUGGY(){
             VECTOR3 aphpos, aphdir, aphrot, gaph;
             VECTOR3 shippos, shipdir, shiprot, gpship;

             // get attachment point positions based on selected point
             {
               
                    GetAttachmentParams(P1,aphpos, aphdir, aphrot);
                
              }  
             // change the position to a global one rather then a local one. This will allow me to offset it from the local center.
             // the local position is stored in gaph
             Local2Global (aphpos, gaph);

             //I now know were the attach point is in the global world
             //Now lets find out if there is a skid in range
             // Search the complete vessel list for a grappling candidate.
            // Not very scalable ...
                for (DWORD i = 0; i < oapiGetVesselCount(); i++) {
                    OBJHANDLE hV = oapiGetVesselByIndex (i);    //get handle for ship
        //            if (strncmp (hV->GetClassName(), "QJFFS", 5)!=0  ){  //restrict to only QuadJ ships to make it easy.

                    // Look for any ship with a child attach point. This allows the Mule to grap any object
                    if (hV == GetHandle()) continue; // If self then continue onward because we don't want to grapple ourselves ...

                    oapiGetGlobalPos (hV, &gpship); // get global postion of ship and put into gpship


                       VESSEL *v = oapiGetVesselInterface (hV);
                       // Only look for child attach point
                       DWORD nAttach = v->AttachmentCount (true); //Get attachment count. I do this so that I can grap any child point.
                       if (nAttach > 0){ // Only continue if ship has a child attach point
                           for (DWORD j = 0; j < nAttach; j++) { // now scan all attachment points of the candidate and check how far away from the attach point it is
                               ATTACHMENTHANDLE hAtt = v->GetAttachmentHandle (true, j);
                               /* this would allow me to filter out all but QJ ships but I don't want to right now
                               const char *id = v->GetAttachmentId (hAtt);
                               if (strncmp (id, "GS", 2)) continue; // attachment point not compatible
                               */
                               v->GetAttachmentParams (hAtt, shippos, shipdir, shiprot); //lets get the attach point position
                               v->Local2Global (shippos, gpship); // and change it to a global position Recycle of gpship cause I can
                               if (dist (gpship, gaph) < MAX_GRAP_DIST) { // Is it close enough to grab?
                                   apd = dist(gpship, gaph);
                                   // Yes! Then lets grab the little bugger and get out of here
                                    const char *id = v->GetAttachmentId (hAtt);
								   (do_attach=0);
								   if(!strncmp (id, "BUG", 3))
								   {do_attach=1;}
								 
								 
				   
								   if(do_attach > 0){
                                     
								 
								   
								 
                                     do_attach = 0;
                                   
                                      if ((v->GetAttachmentStatus(hAtt))||(GetAttachmentStatus(BUG))) { 
 // do nothing;
      }else{
             AttachChild (hV, BUG, hAtt);
                                               break;
                                        
                                   }
                                  return;
                                         }
                                 }// <-- removed return from here
                                   }}
                            } // End of for j
                       }
                              
          

void EAGLE3::DetachBUGGY ()
{
    
             DetachChild(BUG,0);
           
             
}
// --------------------------------------------------------------
// Orbiter's HUD callback
// used to display UMMU's message see PDF doc:
// "Example of feedback method by HUD Display"
// --------------------------------------------------------------
void EAGLE3::clbkDrawHUD (int mode, const HUDPAINTSPEC *hps, HDC hDC)
{
	// draw the default HUD
	VESSEL2::clbkDrawHUD (mode, hps, hDC);

	// UMmu display messages
	if(dHudMessageDelay>0)
	{
		TextOut (hDC,5,hps->H/60*15,cUmmuHudDisplay,strlen(cUmmuHudDisplay));
		dHudMessageDelay-=oapiGetSimStep();
		if(dHudMessageDelay<0)
			dHudMessageDelay=0;
	}
}
void EAGLE3::clbkSaveState(FILEHANDLE scn)
{
char cbuf[256];
	



	// ORBITER, default vessel parameters
	SaveDefaultState (scn);
 sprintf (cbuf, "%d %0.4f", GEAR_status, GEAR_proc);
		oapiWriteScenario_string (scn, "GEAR", cbuf);

	 sprintf (cbuf, "%d %0.4f", BUGHATCH_status, BUGHATCH_proc);
		oapiWriteScenario_string (scn, "BUGHATCH", cbuf);

		 sprintf (cbuf, "%d %0.4f", BUG_status, BUG_proc);
		oapiWriteScenario_string (scn, "BUG", cbuf);


	// UMmu, this will save all our members of crew in scenario
	// as airlock state depend of your ship (you may have a real animated airlock)
	// it's your responsabilites to save and reload UMmu's airlock state.
	Crew.SaveAllMembersInOrbiterScenarios(scn);
}
void EAGLE3::clbkLoadStateEx (FILEHANDLE scn, void *status)
{
	char *line;
	while (oapiReadScenario_nextline (scn, line)) 
	{
		

		// UMMU, Load all saved member from scenario. 
		// Return TRUE when a "UMMUCREW" line is encountered 
		// FALSE when it must pass this line to you or default parser
		if(Crew.LoadAllMembersFromOrbiterScenario(line)==TRUE)
			continue;

		      if (!_strnicmp (line, "GEAR", 4)) {
   sscanf (line+4, "%d%lf", &GEAR_status, &GEAR_proc);
			}
		        if (!_strnicmp (line, "BUGHATCH", 8)) {
   sscanf (line+8, "%d%lf", &BUGHATCH_status, &BUGHATCH_proc);
			}
			        if (!_strnicmp (line, "BUG", 3)) {
   sscanf (line+3, "%d%lf", &BUG_status, &BUG_proc);
			}
		// ORBITER, unrecognised option - pass to Orbiter's generic parser
		ParseScenarioLineEx (line, status);
	}
	SetAnimation (anim_gear, GEAR_proc);

SetAnimation (anim_BUG, BUG_proc);
SetAnimation (anim_BUGdoor, BUGHATCH_proc);
	// as airlock state depend of your ship (you may have a real animated airlock)
	// it's your responsabilites to save and reload UMmu airlock state and set it accordingly
	// to your airlock. IE: call here "SetAirlockDoorState" TRUE of FALSE depend
	// of your animated airlock
}
bool UMmuCrewAddCallback(void *id, char *str, void *data)
{
	if(strlen(str)<2||strlen(str)>38)
		return false;
	char *cPtr=(char*)data;	if(*cPtr==2){*cPtr=3;strcpy(cPtr+2,str);}
	else if(*cPtr==4){*cPtr=5;strcpy(cPtr+42,str);}
	else if(*cPtr==6){*cPtr=7;strcpy(cPtr+82,str);}return true;
}
void EAGLE3::AddUMmuToVessel(BOOL bStartAdding)
{
	if(bStartAdding==FALSE&&cAddUMmuToVessel[0]==0)
		return;
	if(bStartAdding==TRUE){
		int salut=sizeof(cAddUMmuToVessel);
		memset(cAddUMmuToVessel,0,sizeof(cAddUMmuToVessel));
		cAddUMmuToVessel[0]=1;
	}
	else if(cAddUMmuToVessel[0]==1){
		cAddUMmuToVessel[0]=2;
		oapiOpenInputBox ("Enter new crew's name (or escape)",UMmuCrewAddCallback,0,30,(void*)cAddUMmuToVessel);
	}
	else if(cAddUMmuToVessel[0]==3){
		cAddUMmuToVessel[0]=4;
		oapiOpenInputBox ("Enter crew's age",UMmuCrewAddCallback,0,30,(void*)cAddUMmuToVessel);
	}
	else if(cAddUMmuToVessel[0]==5){
		cAddUMmuToVessel[0]=6;
		oapiOpenInputBox ("Enter function (Capt,Sec,Vip,Sci,Doc,Tech,Crew,Pax)",UMmuCrewAddCallback,0,30,(void*)cAddUMmuToVessel);
	}
	else if(cAddUMmuToVessel[0]==7){
		cAddUMmuToVessel[0]=0;
		int Age=max(5,min(100,atoi(&cAddUMmuToVessel[42])));
		if(Crew.AddCrewMember(&cAddUMmuToVessel[2],Age,70,70,&cAddUMmuToVessel[82])==TRUE){
			sprintf(SendHudMessage(),"Crew \"%s\" aged %i added to vessel",&cAddUMmuToVessel[2],Age);
		}
		else{
			strcpy(SendHudMessage(),"ERROR: Crew not added (vessel full?)");
		}
	}
}
// END of  "Allow user to add crew to your ship without scenery editor"
//-------------------------------------------------------------------------
void EAGLE3::Estado()
{
	HoverSmoke=PasarLosHumos(HoverSmoke);
}

and the h
Code:
// ==============================================================
//                 ORBITER MODULE: EAGLE3
//                  Part of the ORBITER SDK
//          Copyright (C) 2002-2004 Martin Schweiger
//                   All rights reserved
//
// EAGLE3.cpp
// Control module for EAGLE3 vessel class
//
// Notes:
// This is an example for a "minimal" vessel implementation which
// only overloads the clbkSetClassCaps method to define vessel
// capabilities and otherwise uses the default VESSEL class
// behaviour.
// ==============================================================




#include "orbitersdk.h"
#include "UMmuSDK.h"

// ==============================================================
// Some vessel parameters
// ==============================================================
//const double FUELMASS = 10000;
//const double ISP = 2e5;
//const double MAXMAINTH = 2.5e5;
//const double MAXRETROTH = 2e4;
const double MAXRETROTH = 1600000.0;
//const double MAXRETROTH = 2e4
//const double MAXHOVERTH = 2e5;
//const double MAXRCSTH = 5000;
// Interface for derived vessel class: QJBStar
// ==========================================================
// Constantes de vuelo de la nave
// Constantes de vuelo de la nave
const double MAX_FUEL = 36000.0;
const double MAX_MAIN_THRUST = 1600000.0;
const double MAX_HOVER_THRUST = 410000.0;
const double MAX_RCS_THRUST = 12000.0;
const double MAX_RETRO_THRUST = 1600000.0;
// Impulso específico para los motores de fusión
const double ISP_FUS = 240000.0;
// Imuplso específico para los motores químicos
const double ISP_QUI = 105000.0;

const double EAGLE_SIZE = 17.0;
const double EAGLE_EMPTY_MASS = 173000.0;
//const double POD_MASS = 15000.0;

const double PITCH_MOMENT_SCALE = 0.0;
const double BANK_MOMENT_SCALE = 0.0;
const VECTOR3 PMI = {86.6, 89.8, 5.5};
const VECTOR3 CROSS_SECTION = {132.2, 237.9, 42.4};
const VECTOR3 ROT_DRAG = {0.7, 0.7, 0.3};

class EAGLE3: public VESSEL2 {
public:
    EAGLE3 (OBJHANDLE hObj, int fmodel);
	void clbkSaveState(FILEHANDLE scn);
       void clbkPostCreation (void);
//	void clbkSetClassCaps (FILEHANDLE cfg);
	void clbkLoadStateEx (FILEHANDLE scn, void *status);
	int clbkConsumeBufferedKey(DWORD key, bool down, char *kstate);
	void clbkVisualCreated (VISHANDLE vis, int refcount);
 	void DefineAnimations(void); 
	void clbkVisualDestroyed (VISHANDLE vis, int refcount);
	void clbkPostStep (double simtt, double simdt, double mjd);
	void clbkDrawHUD (int mode, const HUDPAINTSPEC *hps, HDC hDC);
bool PasarLosHumos(bool humo);
	enum GEARStatus { GEAR_UP, GEAR_DOWN, GEAR_RAISING, GEAR_LOWERING } GEAR_status;
enum DOORStatus { DOOR_UP, DOOR_DOWN, DOOR_RAISING, DOOR_LOWERING } DOOR_status,DOOR1_status,DOOR2_status,DOOR3_status,BUG_status,BUGHATCH_status;
void RevertAIRLOCKOUTER (void);
    void RevertGEAR (void);
    void RevertDOOR (void);
	 void RevertDOOR1 (void);
	 void RevertBUGHATCH (void);
	 void RevertBUGELEV (void);
	 void RevertDOOR2 (void);
	  void RevertDOOR3 (void);
		void AttachCargo(void);
	void DetachCargo (void);
			void AttachBUGGY(void);
	void DetachBUGGY (void);
	void SetUMMUAirlockPos (void);
void EAGLE3::SelectCockpitView (int CAM); 
void Estado();
void AddTransporterExhaust();
void SetRetros(bool ban);
void SetTransporter();

// UMMU 2.0 DECLARATION
	UMMUCREWMANAGMENT Crew;
	int SelectedUmmuMember;				// for the SDK demo, select the member to eva
	int iActionAreaDemoStep;			// this is just to show one feature of action area.
	void clbkSetClassCaps_UMMu(void);	// our special SetClassCap function just added for more readability

	// The HUD display method variable, see PDF doc
	char cUmmuHudDisplay[255];			// UMmu hud char variable
	double dHudMessageDelay;			// UMmu hud display delay
	char *SendHudMessage(void);			// UMmu hud display function

	// THIS IS FOR ADDING CREW SEE PDF doc "Allow user to add crew to your ship 
	// without scenery editor"
	char cAddUMmuToVessel[255];
	void AddUMmuToVessel(BOOL bStartAdding=FALSE);
private:

	// Posición relativa de los diferentes motores
	// Motores principales
	VECTOR3 MainEngineOfs[4];
	// Motores elevadores
	VECTOR3 HoverEngineOfs[8];
	// Impulsores de control de posición del cuerpo principal
	VECTOR3 RCSTransporterOfs[4];
	
	// Banderas numéricas de estado de la nave
	double stage_sep;
	int stage;

	// Definición de los motores y grupos motores
	THRUSTER_HANDLE th_main[4],th_retro[4],th_hover[8],th_rcs[20],th_group[4];
	THRUSTER_HANDLE thg_main,thg_retro,thg_hover;

	// Definición del tanque de combustible
	PROPELLANT_HANDLE ph_main;

	// Se referencian las columnas de humo para poder borrarlas luego
	PSTREAM_HANDLE hover_humo[8];

	bool HoverSmoke;

public:
ATTACHMENTHANDLE P1,BUG,TOP;
UINT anim_gear;
UINT anim_BUG;
int POD;
UINT anim_adoor;
UINT anim_adoor2;
UINT anim_adoor3;
UINT anim_adoor4;
UINT anim_adoor5;
UINT anim_adoor6;
UINT anim_adoor7;
UINT anim_adoor10;
UINT anim_adoor11;
UINT anim_adoor12;
UINT anim_adoor13;
UINT anim_adoor14;
UINT anim_adoor15;
UINT anim_adoor16;
UINT anim_adoor17;
UINT anim_radar;
UINT anim_adoor1;
UINT anim_light;
UINT anim_fdoor6;
UINT anim_fdoor7;
UINT anim_fdoor;
UINT anim_fdoor1;
UINT anim_fdoor2;
UINT anim_fdoor3;
UINT anim_BUGdoor;
int iActiveDockNumber,doorselected,CAM;
int JohnSoundID, do_attach, apd, do_attachh;
double GEAR_proc;
double LIFT_SPEED;
double radar_speed;
double light_SPEED;
double DOOR_proc;
double DOOR1_proc;
double DOOR2_proc;
double DOOR3_proc;
double db;
double i;
double i3;
double t1;
int I,i2,NumHover;
double radar_proc, MAX_GRAP_DIST,MAX_GRAP_DISTHEAD,BUGHATCH_proc;
double light_proc;
double adoor_proc, BUG_proc;
double IGNITION;
VECTOR3 beaconpos[4];
VECTOR3 beaconcol[4];
BEACONLIGHTSPEC beacon[4];
VECTOR3 BUG_pos;
double BUG_INT_POS,id;



};
 
How about if I could just see the name of the vessel attached
 
Hello. From what I read, you are trying to get the attachment ID of the child attachment used to attach to your parent attachment.

Code:
bool getpartnerid(ATTACHMENTHANDLE attach, bool toparent, char * buffer)
{
     // Check if any vessel is attached to the attachment point
     OBJHANDLE partner = GetAttachmentStatus(attach);
     if ( !oapiIsVessel(partner) ) return false;
  
     VESSEL * target = oapiGetVesselInterface(partner);
     // OK, some vessel is attached. Look for attachments of type 
     // [i]opposite[/i] of the specified attachment point
     DWORD count = target->AttachmentCount(!toparent);
     ATTACHMENTHANDLE ht;
     OBJHANDLE ov;
     for ( unsigned int i = 0; i < count; i++ )
     {
          ht = target->GetAttachmentHandle(!toparent, i);
          ov = target->GetAttachmentStatus(ht);
          // now check if the attached vessel equals this vessel...
          if ( ov == this->GetHandle() )
          {
                  // we have found the right attachment point
                  strcpy(buffer, target->GetAttachmentId(ht));     
                  return true;
          }
     }
     return false;
}

I have not compiled/tested the above code. Give it an attachment handle, the attachment type, and a buffer to store the ID as parameters. If a valid attachment point is found, the function returns true with the ID stored in the buffer. If false, the state of the buffer is unchanged.

Basically what you want to do is check your attachment point to see if a vessel is attached to it with GetAttachmentStatus. If it returns a valid vessel handle (check with oapiIsVessel) then a valid attachment point exists, otherwise no vessel is attached to the target attachment point. If a vessel is attached, iterate through all the attachment points on the attached vessel of the type opposite of the attachment point you specify. Check the status of each attachment. If the attached vessel of one of the attachment equals the handle to your vessel, you have found the right attachment point.
 
Last edited:
Thanks. Basically I have Parent attachment point P1. I want to check the id of any vessel attached to it. If the id =XS then stage =3 else it is 0

I put this at the end of my cpp.

I do get these errors.
.\EAGLE3.cpp(1560) : error C3861: 'GetAttachmentStatus': identifier not found
.\EAGLE3.cpp(1566) : error C2039: 'GetAttachmentCount' : is not a member of 'VESSEL'
C:\orbiter\Orbitersdk\include\VesselAPI.h(24) : see declaration of 'VESSEL'
.\EAGLE3.cpp(1574) : error C2673: 'getpartnerid' : global functions do not have 'this' pointers
.\EAGLE3.cpp(1574) : error C2227: left of '->GetHandle' must point to class/struct/union/generic type
 
Back
Top