Project Shuttle-D Development version 1.2

Okay, RCS thrusters are up, and I have exhausts working for those too. I finished modelling a nice little support vessel that will carry 2 UCGO cargoes when finished. Big thanks to Jedida for the help identifying the dumb mistake that was causing that to CTD.

Anyways, I believe Ive been able to integrate UCGO, however the HUD displays, which were working previously with UMMU have dissappeared. Does anyone see what the problem is?

Header File

Code:
#pragma once
#include "Orbitersdk.h"
#include "UMmuSDK.h"
#include "UCGOCargoSDK.h" //UCGO 2.0 copy past this line

// Vessel Parameters
const double EXP_SIZE = 37.4; // mean radius in meters
const VECTOR3 EXP_CS = {95,82,20}; //Shuttle-D cross section in m^2
const VECTOR3 EXP_PMI = {14.00,16.00,2.20}; //Principal Moments of Inertia, normalized, m^2
const double EXP_EMPTYMASS = 16970; //empty vessel mass in kg
const double EXP_FUELMASS =  18000; //max fuel mass in kg
const double EXP_RCS1FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS2FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS3FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS4FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS5FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS6FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS7FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS8FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS9FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS10FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS11FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS12FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS13FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS14FUELMASS =  120; //max fuel mass in kg
const double EXP_RCSRESFUELMASS =  1000; //max fuel mass in kg
const double VACSHD_ISP = 30000; //fuel-specific impulse in m/s
const double NMLSHD_ISP = 25000; //fuel-specific impulse in m/s
const double P_NML = 101.4e3;
const double EXP_MAXMAINTH = 250000; 
const double EXP_MAXHOVERTH = 94000; 
const double EXP_RCSTH0 = 1200; 
const double EXP_RCSTH1 = 1200;
const double EXP_RCSTH2 = 1200; 
const double EXP_RCSTH3 = 1200;
const double EXP_RCSTH4 = 1200; 
const double EXP_RCSTH5 = 1200;
const double EXP_RCSTH6 = 1200; 
const double EXP_RCSTH7 = 1200;
const double EXP_RCSTH8 = 1200; 
const double EXP_RCSTH9 = 1200;
const double EXP_RCSTH10 = 1200; 
const double EXP_RCSTH11 = 1200;
const double EXP_RCSTH12 = 1200; 
const double EXP_RCSTH13 = 1200;
const double EXP_RCSTH14 = 1200; 
const double EXP_RCSTH15 = 1200;
const double GEAR_OPERATING_SPEED = 0.10;
const double PLBAYA_OPERATING_SPEED = 0.07;
const double PLBAYB_OPERATING_SPEED = 0.18;

class ShuttleD :public VESSEL3
{
public:
    ShuttleD (OBJHANDLE hObj, int fmodel);

	MESHHANDLE DVCInterior;
void DefineAnimations();
	void clbkSetClassCaps (FILEHANDLE cfg);
	void clbkLoadStateEx (FILEHANDLE scn, void *status);
//	void clbkDrawHUD (int mode, const HUDPAINTSPEC *hps, HDC hDC, oapi::Sketchpad *skp );
    bool clbkDrawHUD (int mode, const HUDPAINTSPEC *hps, HDC hDC, oapi::Sketchpad *skp);
	void clbkSaveState (FILEHANDLE scn);
		void clbkPostStep (double simtt, double simdt, double mjd);
void RevertGEAR (void);
void RevertPLBAYA (void);
void RevertPLBAYB (void);

	void clbkPreStep (double simt, double simdt, double mjd);
	void clbkPostCreation(void);

	enum GEARStatus { GEAR_UP, GEAR_DOWN, GEAR_RAISING, GEAR_LOWERING } GEAR_status;
	enum PLBAYAStatus { PLBAYA_UP, PLBAYA_DOWN, PLBAYA_CLOSING, PLBAYA_OPENING } PLBAYA_status;
	enum PLBAYBStatus { PLBAYB_UP, PLBAYB_DOWN, PLBAYB_CLOSING, PLBAYB_OPENING } PLBAYB_status;
	enum {CAM_VCPILOT, CAM_VCPSNGR1, CAM_VCPSNGR2, CAM_VCPSNGR3, CAM_VCPSNGR4} campos;
	int SHD;	// this is your unique Id, it will identify your ship in OrbiterSound.



//	enum DoorStatus { DOOR_CLOSED, DOOR_OPEN, DOOR_CLOSING, DOOR_OPENING } gear_status;
	

	int  clbkConsumeBufferedKey (DWORD key, bool down, char *kstate);
	void clbkVisualCreated (VISHANDLE vis, int refcount);
	void clbkMFDMode (int mfd, int mode);
	bool clbkLoadVC (int id);
	bool clbkVCRedrawEvent (int id, int event, SURFHANDLE surf);
	bool clbkVCMouseEvent (int id, int event, VECTOR3 &p);
	VCMFDSPEC mfds_left;

		// 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);

		// UCGO SPECIFIC CODE OF CLASS HEADER **********************************

	void clbkSetClassCapsUCgo (void); // a custom UCGO clbkSetClassCap just to keep code clear

	// UCGO 2.0 CLASS HANDLE FUNCTION AND VARIABLES
	UCGO	hUcgo;						// Cargo class handle
	char   *SendCargHudMessage(void);	// Cargo hud display function
	char	cCargoHudDisplay[255];		// Cargo hud display char variable
	double	dCargHudMessageDelay;		// Cargo hud display delay
	int		iSelectedCargo;				// for the selection of cargos -1 by default



private:
	double myparam;
	UINT anim_gear;
	UINT anim_PLBAYA;
	UINT anim_PLBAYB;
	UINT anim_airlock_switch;
	double GEAR_proc,PLBAYA_proc,PLBAYB_proc;
	//double LIFT_SPEED;
};

HINSTANCE hDLL;
HFONT hFont;
HPEN hPen;
HBRUSH hBrush;

#define AID_MFD1_LBUTTONS		0
#define AID_MFD1_RBUTTONS		1
#define MFD1_LBUTTON1			2
#define MFD1_LBUTTON2			3
#define MFD1_LBUTTON3			4
#define MFD1_LBUTTON4			5
#define MFD1_LBUTTON5			6
#define MFD1_LBUTTON6			7
#define MFD1_RBUTTON1			8
#define MFD1_RBUTTON2			9
#define MFD1_RBUTTON3			10
#define MFD1_RBUTTON4			11
#define MFD1_RBUTTON5			12
#define MFD1_RBUTTON6			13
#define MFD1_BBUTTON1			14
#define MFD1_BBUTTON2			15
#define MFD1_BBUTTON3			16

and the CPP

Code:
// SHD.cpp : Defines the exported functions for the DLL application.
//

#define STRICT
#define ORBITER_MODULE
#include "D9base.h"
#include "orbitersdk.h"
#include <math.h>
#include <stdio.h>
#include "OrbiterSoundSDK35.h"
#define MY500METERSOUND				1		// those are definition so all function will be more clear to read..
#define MYRADARSOUND				2		// because we will know wich sound is concerned by function instead
#define MYEXTERNAL_UFO_FADEDSOUND	3		// of having simple number.
#define GEARUP						 4
#define GEARDOWN					 5
#define PLBAYAOPEN					 6
#define PLBAYACLOSE					 7
#define PLBAYBOPEN					 8
#define PLBAYBCLOSE					 9

//#include "ShuttleDUCGOCargo.h"

HINSTANCE g_hDLL;
VISHANDLE MainExternalMeshVisual = 0;

// ==============================================================
// Airfoil definition
// ==============================================================

void Shuttle_MomentCoeff (double aoa,double M,double Re,double *cl,double *cm,double *cd)
{
	int i;
	const int nabsc = 7;
	static const double AOA[nabsc] = {-180*RAD, -90*RAD,-30*RAD, 0*RAD, 60*RAD,90*RAD,180*RAD};
	static const double CL[nabsc]  = {       0,      0,   -0.004,     0,     0.008,     0,      0};
	static const double CM[nabsc]  = {       0,      0,   0.0014,  0,-0.0012,     0,      0};

	for (i = 0; i < nabsc-1 && AOA[i+1] < aoa; i++);
	double f = (aoa-AOA[i]) / (AOA[i+1]-AOA[i]);
	*cl = CL[i] + (CL[i+1]-CL[i]) * f;  // aoa-dependent lift coefficient
	*cm = CM[i] + (CM[i+1]-CM[i]) * f;  // aoa-dependent moment coefficient
	double saoa = sin(aoa);
	double pd = 0.045 + 0.4*saoa*saoa;  // profile drag
	*cd = pd + oapiGetInducedDrag (*cl, 0.1,0.7) + oapiGetWaveDrag (M, 0.75, 1.0, 1.1, 0.04);
	// profile drag + (lift-)induced drag + transonic/supersonic wave (compressibility) drag
}

ShuttleD::ShuttleD (OBJHANDLE hObj, int fmodel)
	: VESSEL3 (hObj, fmodel)
{
	GEAR_status = GEAR_UP;
	GEAR_proc = 0.0;
	PLBAYA_status = PLBAYA_UP;
	PLBAYA_proc = 0.0;
	PLBAYB_status = PLBAYB_UP;
	PLBAYB_proc = 0.0;

	DefineAnimations();
}

//=========================================================
// Vessel Animations
//=========================================================
void ShuttleD::DefineAnimations()
{
	//ANIMATIONCOMPONENT_HANDLE parent;
	static UINT gearback[1] = {26};
	static MGROUP_ROTATE gear (
		0,
		gearback,
		1,
		_V(0,-2.3,-22.9),
		_V(1,0,0),
		(float)(-90*RAD)
		);
	static UINT gearfront[1] = {27};
	static MGROUP_ROTATE gearf (
		0,
		gearfront,
		1,
		_V(0,-2.3,29.8),
		_V(1,0,0),
		(float)(90*RAD)
		);
	anim_gear = CreateAnimation (0);
	AddAnimationComponent (anim_gear, 0, 1, &gear);
	AddAnimationComponent (anim_gear, 0, 1, &gearf);

	static UINT PLBAYA[1] = {21};
	static MGROUP_ROTATE PLBAYA1 (
		0,
		PLBAYA,
		1,
		_V(0.858,-1.721666,0),
		_V(0,0,1),
		(float)(90*RAD)
		);
	anim_PLBAYA = CreateAnimation (0);
	AddAnimationComponent (anim_PLBAYA, 0, 1, &PLBAYA1);

	static UINT PLBAYB[1] = {17};
	static MGROUP_ROTATE PLBAYB1 (
		0,
		PLBAYB,
		1,
		_V(0,4.513,-23.72),
		_V(1,0,0),
		(float)(-90*RAD)
		);
	anim_PLBAYB = CreateAnimation (0);
	AddAnimationComponent (anim_PLBAYB, 0, 1, &PLBAYB1);
}

//void ShuttleD::Activategear(DoorStatus action)
//{
//	gear_status = action;
//}

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

void ShuttleD::RevertPLBAYA (void)
{
	PLBAYA_status = ((PLBAYA_status == PLBAYA_UP || PLBAYA_status == PLBAYA_CLOSING) ?
PLBAYA_OPENING : PLBAYA_CLOSING);
}

void ShuttleD::RevertPLBAYB (void)
{
	PLBAYB_status = ((PLBAYB_status == PLBAYB_UP || PLBAYB_status == PLBAYB_CLOSING) ?
PLBAYB_OPENING : PLBAYB_CLOSING);
}


//void ShuttleD::Activategear (DoorStatus action)


// --------------------------------------------------------------
// 
// --------------------------------------------------------------
//void ShuttleD::Revertgear ()
//{
//	Activategear (gear_status == DOOR_CLOSED || gear_status == DOOR_CLOSING ?
//		             DOOR_OPENING : DOOR_CLOSING);
//}

char *ShuttleD::SendHudMessage() //<---- Change the class name here
{
	dHudMessageDelay=15;
	return cUmmuHudDisplay;
}


//=========================================================
// Vessel Capabilities
//=========================================================
void ShuttleD::clbkSetClassCaps (FILEHANDLE cfg)
{

	// our call to our custom UCGO clbkSetClassCap
	// the only purpose is to keep code clear
	clbkSetClassCapsUCgo();

	Crew.InitUmmu(GetHandle());	
	float UMmuVersion=Crew.GetUserUMmuVersion();
	//	double UMmuVersion=Crew.GetUserUMmuVersion();
	Crew.DefineAirLockShape(TRUE,-1,1,-2.94,2.94, 29,32);
	Crew.SetMembersPosRotOnEVA(_V(0,-0.837,31.005),_V(0,-180,0));
	Crew.SetMaxSeatAvailableInShip(7);

	Crew.DeclareActionArea(0,_V(-5,0,0),2.5,TRUE,"action_activated.wav","You are on the left of the ship");
	Crew.DeclareActionArea(1,_V(5,0,0),2.5,TRUE, "action_repaired.wav","You are on the right of the ship");
	Crew.DeclareActionArea(2,_V(0,0,5),2.5,TRUE, "action_activated.wav","You are in front of the ship");
	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"

	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\n A=Open/Close airlock S=info M=add crew");

	// The Add mmu without scenery editor variable see PDF doc
	cAddUMmuToVessel[0]=0;

	THRUSTER_HANDLE th_main, th_hover, th_rcs[14], th_group[4];
	DOCKHANDLE Dock0, Dock1;

	// vessel caps definitions
	AddMesh ("Shuttle-D");

	SetMeshVisibilityMode (AddMesh (DVCInterior = oapiLoadMeshGlobal ("ShuttleDVC")), MESHVIS_VC);

	SetCameraOffset (_V(0,0.24,0.13));
	SetAlbedoRGB (_V(0.77,0.20,0.73));
	SetSize (EXP_SIZE);

	SetEmptyMass (EXP_EMPTYMASS);
	void UpdateEmptyMass(void);

	SetPMI (EXP_PMI);
	SetCrossSections (EXP_CS);
	SetSurfaceFrictionCoeff (0.55, 0.79);
	SetRotDrag (_V(0.9, 0.76, 0.2));

	EnableTransponder (true);
	InitNavRadios (4);

	// propellant resources
	PROPELLANT_HANDLE hpr = CreatePropellantResource (EXP_FUELMASS);
	PROPELLANT_HANDLE RCS1 = CreatePropellantResource (EXP_RCS1FUELMASS);
	PROPELLANT_HANDLE RCS2 = CreatePropellantResource (EXP_RCS2FUELMASS);
	PROPELLANT_HANDLE RCS3 = CreatePropellantResource (EXP_RCS3FUELMASS);
	PROPELLANT_HANDLE RCS4 = CreatePropellantResource (EXP_RCS4FUELMASS);
	PROPELLANT_HANDLE RCS5 = CreatePropellantResource (EXP_RCS5FUELMASS);
	PROPELLANT_HANDLE RCS6 = CreatePropellantResource (EXP_RCS6FUELMASS);
	PROPELLANT_HANDLE RCS7 = CreatePropellantResource (EXP_RCS7FUELMASS);
	PROPELLANT_HANDLE RCS8 = CreatePropellantResource (EXP_RCS8FUELMASS);
	PROPELLANT_HANDLE RCS9 = CreatePropellantResource (EXP_RCS9FUELMASS);
	PROPELLANT_HANDLE RCS10 = CreatePropellantResource (EXP_RCS10FUELMASS);
	PROPELLANT_HANDLE RCS11 = CreatePropellantResource (EXP_RCS11FUELMASS);
	PROPELLANT_HANDLE RCS12 = CreatePropellantResource (EXP_RCS12FUELMASS);
	PROPELLANT_HANDLE RCS13 = CreatePropellantResource (EXP_RCS13FUELMASS);
	PROPELLANT_HANDLE RCS14 = CreatePropellantResource (EXP_RCS14FUELMASS);
	PROPELLANT_HANDLE RCSRES = CreatePropellantResource (EXP_RCSRESFUELMASS);

	// main engine
	th_main = CreateThruster (_V(0,0,-31.45), _V(0,0,1), EXP_MAXMAINTH, hpr, VACSHD_ISP, NMLSHD_ISP, P_NML);
	CreateThrusterGroup (&th_main, 1, THGROUP_MAIN);
	AddExhaust (th_main, 8, 1, _V(0,0.23,-31.45), _V(0,0,-1));

	PARTICLESTREAMSPEC contrail_main = {
		0, 5.0, 16, 200, 0.15, 1.0, 5, 3.0, PARTICLESTREAMSPEC::DIFFUSE,
		PARTICLESTREAMSPEC::LVL_PSQRT, 0, 2,
		PARTICLESTREAMSPEC::ATM_PLOG, 1e-4, 1
	};
	PARTICLESTREAMSPEC exhaust_main = {
		0, 2.0, 20, 200, 0.05, 0.1, 8, 1.0, PARTICLESTREAMSPEC::EMISSIVE,
		PARTICLESTREAMSPEC::LVL_SQRT, 0, 1,
		PARTICLESTREAMSPEC::ATM_PLOG, 1e-5, 0.1
	};
	AddExhaustStream (th_main, _V(0,0.3,-10), &contrail_main);
	AddExhaustStream (th_main, _V(0,0.3,-5), &exhaust_main);

	// hover engine
	th_hover = CreateThruster (_V(0,-2.94,0), _V(0,1,0), EXP_MAXHOVERTH, hpr,  VACSHD_ISP, NMLSHD_ISP, P_NML);
	CreateThrusterGroup (&th_hover, 1, THGROUP_HOVER);
	AddExhaust (th_hover, 8, 1, _V(0.0,-2.94,-17.09), _V(0,-1,0));
	AddExhaust (th_hover, 8, 1, _V(0.0,-2.94,23.62), _V(0,-1,0));

	PARTICLESTREAMSPEC contrail_hover = {
		0, 5.0, 8, 200, 0.15, 1.0, 5, 3.0, PARTICLESTREAMSPEC::DIFFUSE,
		PARTICLESTREAMSPEC::LVL_PSQRT, 0, 2,
		PARTICLESTREAMSPEC::ATM_PLOG, 1e-4, 1
	};
	PARTICLESTREAMSPEC exhaust_hover = {
		0, 2.0, 10, 200, 0.05, 0.05, 8, 1.0, PARTICLESTREAMSPEC::EMISSIVE,
		PARTICLESTREAMSPEC::LVL_SQRT, 0, 1,
		PARTICLESTREAMSPEC::ATM_PLOG, 1e-5, 0.1
	};

	AddExhaustStream (th_hover, _V(0,-3, 1), &contrail_hover);
	AddExhaustStream (th_hover, _V(0,-3,-1), &contrail_hover);
	AddExhaustStream (th_hover, _V(0,-2, 1), &exhaust_hover);
	AddExhaustStream (th_hover, _V(0,-2,-1), &exhaust_hover);

	// RCS engines
	th_rcs[0] = CreateThruster (_V( 2,0, 30), _V(0,0,1), EXP_RCSTH0, RCS1,  VACSHD_ISP, NMLSHD_ISP, P_NML);//CM FRONT RIGHT SIDE FORWARD
	th_rcs[1] = CreateThruster (_V( -2,0,30), _V(0,0,1), EXP_RCSTH1, RCS2,  VACSHD_ISP, NMLSHD_ISP, P_NML);//CM FRONT LEFT SIDE FORWARD

	th_rcs[2] = CreateThruster (_V(3,0, 30), _V(-1, 0,0), EXP_RCSTH2, RCS3,  VACSHD_ISP, NMLSHD_ISP, P_NML);//CM FRONT RIGHT SIDE RIGHT
	th_rcs[3] = CreateThruster (_V(-3,0, 30), _V(1,0,0), EXP_RCSTH3, RCS4,  VACSHD_ISP, NMLSHD_ISP, P_NML);//CM FRONT LEFT SIDE LEFT

	th_rcs[4] = CreateThruster (_V( 2,2,22.20), _V(0, 1,0), EXP_RCSTH4, RCS5,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS FORWARD RIGHT SIDE UP
	th_rcs[5] = CreateThruster (_V( -2,2,22.20), _V(0,1,0), EXP_RCSTH5, RCS6,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS FORWARD LEFT SIDE UP
	th_rcs[6] = CreateThruster (_V(2,-2,22.20), _V(0,-1,0), EXP_RCSTH6, RCS7,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS FORWARD RIGHT SIDE DOWN
	th_rcs[7] = CreateThruster (_V(-2,-2,22.20), _V(0,-1,0), EXP_RCSTH7, RCS8,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS FORWARD LEFT SIDE DOWN

	th_rcs[8] = CreateThruster (_V( 2,2, -14), _V(0,1,0), EXP_RCSTH8, RCS9,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS AFT RIGHT SIDE UP
	th_rcs[9] = CreateThruster (_V( -2,2, -14), _V( 0,1,0), EXP_RCSTH9, RCS10,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS AFT LEFT SIDE UP
	th_rcs[10] = CreateThruster (_V(2,-2,-14), _V(0,-1,0), EXP_RCSTH10, RCS11,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS AFT RIGHT SIDE DOWN
	th_rcs[11] = CreateThruster (_V(-2,-2,-14), _V( 0,-1,0), EXP_RCSTH11, RCS12,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS AFT LEFT SIDE DOWN

	th_rcs[12] = CreateThruster (_V( 1.59,0.037,-18.595), _V(0,0, -1), EXP_RCSTH12, RCS13,  VACSHD_ISP, NMLSHD_ISP, P_NML);//SM BACK RIGHT SIDE BACKWARDS
	th_rcs[13] = CreateThruster (_V( -1.59,0.037,-18.595), _V(0,0,-1), EXP_RCSTH13, RCS14,  VACSHD_ISP, NMLSHD_ISP, P_NML);//SM BACK LEFT SIDE BACKWARDS

	th_rcs[14] = CreateThruster (_V(1.84,0.037,-18.193), _V( -1,0,0), EXP_RCSTH14, RCS12,  VACSHD_ISP, NMLSHD_ISP, P_NML);//SM BACK RIGHT SIDE RIGHT
	th_rcs[15] = CreateThruster (_V( -1.84,0.037,-18.193), _V(1,0, 0), EXP_RCSTH15, RCS13,  VACSHD_ISP, NMLSHD_ISP, P_NML);//SM BACK LEFT SIDE LEFT

	AddExhaust (th_rcs[12], 0.6,  0.078, _V( 1.601,0.100, 29.777), _V(0,0,1));
	AddExhaust (th_rcs[13], 0.6,  0.078, _V( -1.601,0.100,29.777), _V(0,0,1));

	AddExhaust (th_rcs[2], 0.79, 0.103, _V(1.8505,0.100, 29.265), _V(-1, 0,0));
	AddExhaust (th_rcs[3], 0.79, 0.103, _V(-1.8505,0.100, 29.265), _V(1,0,0));

	AddExhaust (th_rcs[6], 0.6,  0.078, _V( 1.101,2.04,21.208), _V(0, 1,0));
	AddExhaust (th_rcs[7], 0.6,  0.078, _V( -1.101,2.04,21.208), _V(0,1,0));
	AddExhaust (th_rcs[4], 0.79, 0.103, _V(1.101,-1.736,21.208), _V(0,-1,0));
	AddExhaust (th_rcs[5], 0.79, 0.103, _V(-1.101,-1.736,21.208), _V(0,-1,0));

	AddExhaust (th_rcs[10], 0.6,  0.078, _V( 1.101,2.04, -11.103), _V(0,1,0));
	AddExhaust (th_rcs[11], 0.6,  0.078, _V( -1.101,2.04, -11.103), _V( 0,1,0));
	AddExhaust (th_rcs[8], 0.79, 0.103, _V(1.101,-1.736,-11.103), _V(0,-1,0));
	AddExhaust (th_rcs[9], 0.79, 0.103, _V(-1.101,-1.736,-11.103), _V( 0,-1,0));

	AddExhaust (th_rcs[0], 0.6,  0.078, _V( 1.59,0.037,-18.595), _V(0,0, -1));
	AddExhaust (th_rcs[1], 0.6,  0.078, _V( -1.59,0.037,-18.595), _V(0,0,-1));

	AddExhaust (th_rcs[14], 0.79, 0.103, _V(1.84,0.037,-18.193), _V( -1,0,0));
	AddExhaust (th_rcs[15], 0.79, 0.103, _V( -1.84,0.037,-18.193), _V(1,0, 0));


	th_group[0] = th_rcs[4];
	th_group[1] = th_rcs[5];
	th_group[2] = th_rcs[10];
	th_group[3] = th_rcs[11];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_PITCHUP);

	th_group[0] = th_rcs[6];
	th_group[1] = th_rcs[7];
	th_group[2] = th_rcs[8];
	th_group[3] = th_rcs[9];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_PITCHDOWN);

	th_group[0] = th_rcs[4];
	th_group[1] = th_rcs[7];
	th_group[2] = th_rcs[8];
	th_group[3] = th_rcs[11];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_BANKLEFT);

	th_group[0] = th_rcs[5];
	th_group[1] = th_rcs[6];
	th_group[2] = th_rcs[9];
	th_group[3] = th_rcs[10];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_BANKRIGHT);

	th_group[0] = th_rcs[4];
	th_group[1] = th_rcs[5];
	th_group[2] = th_rcs[8];
	th_group[3] = th_rcs[9];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_UP);

	th_group[0] = th_rcs[6];
	th_group[1] = th_rcs[7];
	th_group[2] = th_rcs[10];
	th_group[3] = th_rcs[11];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_DOWN);

	th_group[0] = th_rcs[2];
	th_group[1] = th_rcs[15];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_YAWLEFT);

	th_group[0] = th_rcs[3];
	th_group[1] = th_rcs[14];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_YAWRIGHT);

	th_group[0] = th_rcs[2];
	th_group[1] = th_rcs[14];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_LEFT);

	th_group[0] = th_rcs[3];
	th_group[1] = th_rcs[15];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_RIGHT);

	th_group[0] = th_rcs[0];
	th_group[1] = th_rcs[1];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_FORWARD);

	th_group[0] = th_rcs[12];
	th_group[1] = th_rcs[13];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_BACK);

	Dock0 = CreateDock(_V(0,-0.637,30.455),_V(0,0,1),_V(0,1,0));
	Dock1 = CreateDock(_V(0,2.730,-17.498),_V(0,1,0),_V(0,0,1));

}

void ShuttleD::clbkSetClassCapsUCgo (void)
{
	//UCGO 2.0 Initialisation, cargo slot pos, rot declaration
	hUcgo.Init(GetHandle());
	// in contrary of the PDF code we declare 6 slots
	// because it's fun :)
	hUcgo.DeclareCargoSlot(0,_V(0,2.6,-3.50),_V(0,0,0)); // slot 0
	hUcgo.DeclareCargoSlot(1,_V(0,1.3,-3.50),_V(0,0,90)); // slot 1
	hUcgo.DeclareCargoSlot(2,_V(0,0,-3.50),_V(0,0,180)); // slot 2 
	hUcgo.DeclareCargoSlot(3,_V( 0,-1.3,-3.50),_V(0,0,270)); // slot 3 
	hUcgo.DeclareCargoSlot(4,_V(0,-2.6,-3.50),_V(0,0,0)); // slot 4 
	hUcgo.DeclareCargoSlot(5,_V( 0,-3.9,-3.50),_V(0,0,0)); // slot 5 

	// UCGO 2.0 Parameters settings
	hUcgo.SetReleaseSpeedInSpace(0.10f);	   // release speed of cargo in space in m/s
	hUcgo.SetMaxCargoMassAcceptable(10000.0);   // max cargo mass in kg that your vessel can carry
	hUcgo.SetGrappleDistance(50);		       // grapple distance radius in meter from center of ship
	hUcgo.SetSlotGroundReleasePos(0 ,_V(-4,2.6,-3.50));
	hUcgo.SetSlotGroundReleasePos(1 ,_V(-4,1.3,-3.50));
	hUcgo.SetSlotGroundReleasePos(2 ,_V(-4,0,-3.50));
	hUcgo.SetSlotGroundReleasePos(3 ,_V(-4,-1.3,-3.50));
	hUcgo.SetSlotGroundReleasePos(4 ,_V(-4,-2.6,-3.50));
	hUcgo.SetSlotGroundReleasePos(5 ,_V(-4,-3.9,-3.50));

	// UCGO Variables initialisation
	cCargoHudDisplay[0]=0;						// Cargo hud display char variable
	dCargHudMessageDelay=0;						// Cargo hud display delay
	iSelectedCargo=-1;							// for the selection of cargos (-1 mean "default" see header)
	// welcome message with keys for users
	strcpy(SendCargHudMessage(),"Cargo key: C/SHF+C = grapple/release, 9/SHF+9 = snc editor add cargo, 8=infos on cargos");
}

void ShuttleD::clbkMFDMode (int mfd, int mode)
{
	//	When an MFD changes mode (either in Panel or VC modes), this call back is
	//	invoked. Here, it is a general TriggerRedrawArea function used, which can
	//	be used to redraw MFD's for VC or Panel views. You can just as effectively
	//	use oapiTriggerVCRedrawArea for this ship, however, as there is no 2D panel.
	switch (mfd) {
	case MFD_LEFT:
		oapiTriggerRedrawArea (0, 0, AID_MFD1_LBUTTONS);
		oapiTriggerRedrawArea (0, 0, AID_MFD1_RBUTTONS);
		break;
	}
}
//-------------------------------------------------------------------------

bool ShuttleD::clbkLoadVC (int id)
{
	//	 VCHUDSPEC hud;
	VCMFDSPEC mfd;

	mfd.ngroup=15;
	mfd.nmesh=1;
	oapiVCRegisterMFD(0,&mfd);
	mfd.ngroup=16;
	mfd.nmesh=1;
	oapiVCRegisterMFD(1,&mfd);
	static VCHUDSPEC hud_pilot  = {1, 14,{0.55,2.04,28.93},1.02};
	static VCHUDSPEC hud_copilot  = {1, 14,{-0.55,2.04,28.93},1.02};
	static VCHUDSPEC hud_fdeckfloor  = {1, 14,{0,2.04,28.93},1.02};
	//	oapiVCRegisterHUD (&hud_pilot); // HUD parameters

	mfds_left.nmesh = 1;	//	The mesh number (the first one loaded, or 0, in this case).
	mfds_left.ngroup  = 15;	//	The mesh group that is the MFD screen in the above identified mesh.

	SetCameraDefaultDirection(_V(0, 0, 1)); // View angles down so you can see the
	//	MFD in VC view by default (it is the sine and cosine of 11º in Y and Z, respectively).

	SURFHANDLE MFDbuttons1 = oapiGetTextureHandle (DVCInterior,2); 
	//	Get the MFDButtons.dds D texture for redrawing purposes.

	int i;
	switch (id) {
	case 0:	//	The first VC cockpit view (id 0). Carefull with mixing up id.
		//	There are more references to id in the code, but what they identify is 
		//	according to the specific callback. For example, in clbkVCMouseEvent,
		//	id is the identity of the MFD buttons, not the cockpit view. 
		SetCameraOffset (_V(0.55,2.04,27.93));
		oapiVCSetNeighbours (1, -1, -1, 2);

		oapiVCRegisterMFD (MFD_LEFT, &mfds_left);
		oapiVCRegisterHUD (&hud_pilot); // HUD parameters


		oapiVCRegisterArea (AID_MFD1_LBUTTONS, _R( 0, 0, 32, 220), PANEL_REDRAW_USER, PANEL_MOUSE_IGNORE, PANEL_MAP_BACKGROUND, MFDbuttons1);		

		oapiVCRegisterArea (AID_MFD1_RBUTTONS, _R( 32, 0, 64, 220), PANEL_REDRAW_USER, PANEL_MOUSE_IGNORE, PANEL_MAP_BACKGROUND, MFDbuttons1);

		//	These area registrations are for redrawing purposes only, and are used by 
		//	the clbkVCRedrawEvent. The _R(X1, Y1, X2, Y2) coordinates pertain to the
		//	texture coordinates on the MFDButtons.dds texture (in PIXELS), which is the 
		//	dynamic texture that SURFHANDLE MFDbuttons1 gets from the mesh file.
		//	Note that as the bottom buttons are not ever redrawn, no registration of
		//	them for texture coords is needed.
		//	Have a look at the clbkMFDMode and clbkVCRedrawEvent. 
		//	The only areas triggered for redrawing and then redrawn are
		//	those two registered above (AID_MFD1_LBUTTONS and AID_MFD1_RBUTTONS.
		//	And have a look at the definitions in the AstroMatiz.h file. You will see their
		//	id's that are passed to the preprocessor during compile.

		//	For reference, here are the rectangular area 3D coordinates of the 
		//	left, right and bottom MFD buttons, in MESH coordinates. You WILL need to
		//	get this info, if you want to make your own VC work properly. 
		//	(Use Meshwizard, or go into the mesh file itself and find them).
		//				The mesh area of the 6 left buttons of MFD_LEFT
		//					_V(-0.32f, 0.067f, 0.654f),
		//					_V(-0.30f, 0.067f, 0.654f),
		//					_V(-0.32f, -0.080f, 0.643f),
		//					_V(-0.30f, -0.080f, 0.643f));
		//				The mesh area of the 6 right buttons of MFD_LEFT
		//					_V(-0.117f, 0.067f, 0.654f),
		//					_V(-0.097f, 0.067f, 0.654f),
		//					_V(-0.117f, -0.080f, 0.643f),
		//					_V(-0.097f, -0.080f, 0.643f));
		//				The mesh area of the three bottom buttons of MFD_LEFT
		//					_V(-0.267f, -0.095f, 0.642f),
		//					_V(-0.158f, -0.095f, 0.642f),
		//					_V(-0.267f, -0.114f, 0.641f),
		//					_V(-0.158f, -0.114f, 0.641f));

		for (i = 0; i < 6; i++) {
			oapiVCRegisterArea (MFD1_LBUTTON1+i, PANEL_REDRAW_NEVER, PANEL_MOUSE_LBDOWN);
			oapiVCSetAreaClickmode_Spherical(MFD1_LBUTTON1+i, _V(-0.31, 0.0547 - (i * 0.0245), 0.649), 0.0123);

			oapiVCRegisterArea (MFD1_RBUTTON1+i, PANEL_REDRAW_NEVER, PANEL_MOUSE_LBDOWN);
			oapiVCSetAreaClickmode_Spherical(MFD1_RBUTTON1+i, _V(-0.107, 0.0547-(i * 0.0245), 0.649), 0.0123);
		}
		//	Define the area for mouse events on each button of left and right columns.

		for (i = 0; i < 3; i++) {
			oapiVCRegisterArea (MFD1_BBUTTON1+i, PANEL_REDRAW_NEVER, PANEL_MOUSE_LBDOWN);
			oapiVCSetAreaClickmode_Spherical(MFD1_BBUTTON1+i, _V(-0.254 + (i * 0.042), -0.100, 0.641), 0.0123);
		}
		//	Define the area for mouse events on the bottom buttons. Coordinates are in
		//	mesh 3D coords (see why you needed to get this info?)

		campos = CAM_VCPILOT;
		break;



	case 1: // front left passenger
		SetCameraOffset (_V(-0.55,2.04,27.93));
		SetCameraMovement (_V(0.2,-0.05,0.3), -10*RAD, 10*RAD, _V(-0.3,0,0), 80*RAD, 0, _V(0.4,0,0), -90*RAD, 0);
		oapiVCSetNeighbours (-1, 0, -1, 2);
		oapiVCRegisterHUD (&hud_copilot); // HUD parameters
		campos = CAM_VCPSNGR1;
		break;

	case 2: // front right passenger
		SetCameraOffset (_V(0,-0.2,27.93));
		SetCameraMovement (_V(-0.2,-0.05,0.3), 10*RAD, 10*RAD, _V(-0.4,0,0), 90*RAD, 0, _V(0.3,0,0), -80*RAD, 0);
		oapiVCSetNeighbours (1, 0, -1, 3);
		oapiVCRegisterHUD (&hud_fdeckfloor); // HUD parameters
		campos = CAM_VCPSNGR2;
		break;

	case 3: // rear left passenger
		SetCameraOffset (_V(0.5, 0, 4.4));
		SetCameraMovement (_V(0.4,0,0), 0, 0, _V(-0.3,0,0), 70*RAD, 0, _V(0.4,0,0), -90*RAD, 0);
		oapiVCSetNeighbours (-1, -1, 2, 4);
		campos = CAM_VCPSNGR3;
		break;

	case 4: // rear right passenger
		SetCameraOffset (_V(0, 0.2, -18));
		SetCameraMovement (_V(-0.4,0,0), 0, 0, _V(-0.4,0,0), 90*RAD, 0, _V(0.3,0,0), -70*RAD, 0);
		oapiVCSetNeighbours (-1, -1, 3, -1);
		campos = CAM_VCPSNGR4;
		return true;

	};

	return true;
}
//void ShuttleD::clbkDrawHUD (int mode, const HUDPAINTSPEC *hps, HDC hDC, oapi::Sketchpad *skp)
bool ShuttleD::clbkDrawHUD(int mode, const HUDPAINTSPEC *hps, HDC hDC, oapi::Sketchpad *skp)
{
	// draw the default HUD
	VESSEL3::clbkDrawHUD (mode, hps, skp);

	// UMmu display messages
	if(dHudMessageDelay>0)
	{
		skp->Text(5,hps->H/60*15,cUmmuHudDisplay,strlen(cUmmuHudDisplay));
	//	dHudMessageDelay-=oapiGetSimStep();
	//	if(dHudMessageDelay<0)
	//		dHudMessageDelay=0;
	}

		// UCGO display messages
	if(dCargHudMessageDelay>0)
	{
		TextOut (hDC,5,hps->H/60*12,cCargoHudDisplay,strlen(cCargoHudDisplay));
		dCargHudMessageDelay-=oapiGetSimStep();
		if(dCargHudMessageDelay<0)
			dCargHudMessageDelay=0;
	}
    return true; 
}
//-------------------------------------------------------------------------

bool ShuttleD::clbkVCMouseEvent (int id, int event, VECTOR3 &p)
{
	if ((id) >= MFD1_LBUTTON1 && (id) < MFD1_LBUTTON1 + 12)
	{
		oapiProcessMFDButton (MFD_LEFT, id - MFD1_LBUTTON1, event);
		return true;
	}
	//	Processes the events for mouse clicks on VC view MFD buttons, left and
	//	right columns, by matching the id of the event to the #defined identifier of
	//	the button (see AstroMatiz.h file).

	if ((id) == MFD1_BBUTTON1)
	{
		oapiToggleMFD_on (MFD_LEFT);
		return true;
	}
	//	The ON / OFF button (left MFD button on the bottom).

	if ((id) == MFD1_BBUTTON2)
	{
		oapiSendMFDKey (MFD_LEFT, OAPI_KEY_F1);
		return true;
	}
	//	The MODE button (center MFD button on the bottom).

	if ((id) == MFD1_BBUTTON3)
	{
		oapiSendMFDKey (MFD_LEFT, OAPI_KEY_GRAVE);
		return true;
	}
	//	The MENU button (right MFD button on the bottom).

	return false;
}
//-------------------------------------------------------------------------


bool ShuttleD::clbkVCRedrawEvent (int id, int event, SURFHANDLE surf)
{
	int bt, side;
	switch (id) {
		const char *label;
	case AID_MFD1_LBUTTONS:
	case AID_MFD1_RBUTTONS:
		side = (id == AID_MFD1_LBUTTONS ? 0: 1);
		HDC hDC = oapiGetDC (surf);
		SelectObject (hDC, hFont);
		SetTextColor (hDC, RGB(250, 250, 100));
		SetTextAlign (hDC, TA_CENTER);
		SetBkMode (hDC, TRANSPARENT);
		for (bt = 0; bt < 6; bt++) {
			if (label = oapiMFDButtonLabel (MFD_LEFT, bt+side*6))
				TextOut (hDC, 16, 8+36*bt, label, strlen(label));
			else break;
		}
		oapiReleaseDC (surf, hDC);
		return true;
	}
	return false;

	//	This redraw business is the most complicated thing about making VC's. This is 
	//	where the registered areas AID_MFD1_LBUTTONS and AID_MFD1_RBUTTONS are used.
	//	This part has absolutely nothing to do with triggering mouse events. It is
	//	ONLY blitting your MFDButtons texture on the buttons again, but with text on it.
	//	Important points with this issue are;
	//	1.	Get your mesh UV coordinates right for the mesh. DON'T get is backwards.
	//	2.	Make sure your texture is dynamic (just D suffix it in the mesh file).
	//	3.	Get the texture in the program (in clbkLoadVC) as a SURFHANDLE.

};

//=========================================================
// Vessel Load and Save Parameters
//=========================================================
void ShuttleD::clbkLoadStateEx (FILEHANDLE scn, void *status)
{
	char *line;
	while (oapiReadScenario_nextline (scn, line)) 
	{

		if (!_strnicmp (line, "GEAR", 4)) {
			sscanf (line+4, "%d%lf", &GEAR_status, &GEAR_proc);
		}

		if (!_strnicmp (line, "PLBAYA", 5)) {
			sscanf (line+4, "%d%lf", &PLBAYA_status, &PLBAYA_proc);
		}

		if (!_strnicmp (line, "PLBAYB", 5)) {
			sscanf (line+4, "%d%lf", &PLBAYB_status, &PLBAYB_proc);
		}

		// Load UCGO 2.0 cargo from scenario
		if(hUcgo.LoadCargoFromScenario(line)==TRUE) // UCGO load cargo 
			continue;

		if(Crew.LoadAllMembersFromOrbiterScenario(line)==TRUE)
			continue;

				// Load UCGO 2.0 cargo from scenario
		if(hUcgo.LoadCargoFromScenario(line)==TRUE) // UCGO load cargo 
			continue;

		ParseScenarioLineEx (line, status);
	}



	SetAnimation (anim_gear, GEAR_proc);

}


void ShuttleD::clbkSaveState (FILEHANDLE scn)
{
	char cbuf[256];

	VESSEL3::clbkSaveState (scn);
	sprintf (cbuf, "%d %0.4f", GEAR_status, GEAR_proc);
	oapiWriteScenario_string (scn, "GEAR", cbuf);

	sprintf (cbuf, "%d %0.4f", PLBAYA_status, PLBAYA_proc);
	oapiWriteScenario_string (scn, "PLBAYA", cbuf);


	sprintf (cbuf, "%d %0.4f", PLBAYB_status, PLBAYB_proc);
	oapiWriteScenario_string (scn, "PLBAYB", cbuf);

	Crew.SaveAllMembersInOrbiterScenarios(scn);

	// Save UCGO 2.0 cargo in scenario
	hUcgo.SaveCargoToScenario(scn);	

	// Save UCGO 2.0 cargo in scenario
	hUcgo.SaveCargoToScenario(scn);	
}

//=========================================================
// Animation Template
//=========================================================
void ShuttleD::clbkPostStep(double simt, double simdt, double mjd)
{
	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 (GEAR_status >= GEAR_RAISING) { 
		double da = simdt * GEAR_OPERATING_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);

	}
	SetTouchdownPoints (_V(0,-4.89+GEAR_proc*0.99,1), _V(-1,-4.89+GEAR_proc*0.99,-1), _V(1,-4.89+GEAR_proc*0.99,-1));
	//sprintf(oapiDebugString(),"anim %2.2f", GEAR_proc );

	if (PLBAYA_status >= PLBAYA_CLOSING) {
		double da = simdt * PLBAYA_OPERATING_SPEED;
		if (PLBAYA_status == PLBAYA_CLOSING) {
			if (PLBAYA_proc > 0.0) PLBAYA_proc = max (0.0, PLBAYA_proc-da);
			else                PLBAYA_status = PLBAYA_UP;
		} else {
			if (PLBAYA_proc < 1.0) PLBAYA_proc = min (1.0, PLBAYA_proc+da);
			else                PLBAYA_status = PLBAYA_DOWN;
		}
		SetAnimation (anim_PLBAYA, PLBAYA_proc);



	}

	if (PLBAYB_status >= PLBAYB_CLOSING) {
		double da = simdt * PLBAYB_OPERATING_SPEED;
		if (PLBAYB_status == PLBAYB_CLOSING) {
			if (PLBAYB_proc > 0.0) PLBAYB_proc = max (0.0, PLBAYB_proc-da);
			else                PLBAYB_status = PLBAYB_UP;
		} else {
			if (PLBAYB_proc < 1.0) PLBAYB_proc = min (1.0, PLBAYB_proc+da);
			else                PLBAYB_status = PLBAYB_DOWN;
		}
		SetAnimation (anim_PLBAYB, PLBAYB_proc);



	}

	if (GEAR_status == GEAR_RAISING) {
		{if(GEAR_proc > 0.99)
			PlayVesselWave3(SHD,GEARDOWN);
		}
	}

	if (GEAR_status == GEAR_LOWERING) {
		{if(GEAR_proc < 0.01)
			PlayVesselWave3(SHD,GEARUP);
		}
	}
	if (PLBAYA_status == PLBAYA_CLOSING) {
		{if(PLBAYA_proc > 0.99)
			PlayVesselWave3(SHD,PLBAYACLOSE);
		}
	}

	if (PLBAYA_status == PLBAYA_OPENING) {
		{if(PLBAYA_proc < 0.01)
			PlayVesselWave3(SHD,PLBAYAOPEN);
		}
	}
	if (PLBAYB_status == PLBAYB_CLOSING) {
		{if(PLBAYB_proc > 0.9)
			PlayVesselWave3(SHD,PLBAYBCLOSE);
		}
	}

	if (PLBAYB_status == PLBAYB_OPENING) {
		{if(PLBAYB_proc < 0.1)
			PlayVesselWave3(SHD,PLBAYBOPEN);
		}
	}

	int ActionAreaReturnCode=Crew.DetectActionAreaActivated();
	if(ActionAreaReturnCode>-1)
	{
		// this is just an example, we have four area declared
		// action area ID 0 triggered 
		if(ActionAreaReturnCode==0)
		{
			// do something cool here
		}
		// action area ID 1 triggered
		else if(ActionAreaReturnCode==1)
		{
			// As you can edit action area in real time: relocation, sound, text, state
			// the possiblity are endless. you can for example order a UMMU to go to an external panel
			// and open it, then go to engine exhaust and "repair" it then back to panel to close it etc.
			if(iActionAreaDemoStep==0)
			{
				Crew.SetActionAreaText(1,"You are still on the right but this is 2nd keypress");
				iActionAreaDemoStep++;
				// do something cool here
			}
			else if(iActionAreaDemoStep==1)
			{
				// remember you change action area's parameter for the NEXT keypress of UMMU
				Crew.SetActionAreaText(1,"You are still on the right but this is 3rd keypress");
				iActionAreaDemoStep++;
				// do something cool here
			}
			else
			{
				Crew.SetActionAreaText(1,"You are still on the right but this is last keypress");
				// and of course you can change sound, location, state, text, here some examples:
				//Crew.SetActionAreaPos(1,_V(5,0,5)); // change location
				//Crew.SetActionAreaWav(1,"MyAddon\PlayRepaired.wav"); // change sound
				//Crew.SetActionAreaWav(1,"MyAddon\DoorClosed.wav"); // change sound
				//Crew.SetActionAreaText(1,NULL); // sound or text are not mandatory, here we set no text.
				// etc. etc.
			}
		}
		// action area ID 2 triggered
		else if(ActionAreaReturnCode==2)
		{
			// do something cool here
		}
		// action area ID 3 triggered
		else if(ActionAreaReturnCode==3)
		{
			// do something cool here
		}
	}

	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");
		}
	}

	AddUMmuToVessel();

	Crew.WarnUserUMMUNotInstalled("Shuttle-D");

	 // At the very end of clbkPostStep or clbkPreStep
     hUcgo.WarnUserUCGONotInstalled("Shuttle-D");
}

// --------------------------------------------------------------
// clbkVisualCreated
// --------------------------------------------------------------
void ShuttleD::clbkVisualCreated (VISHANDLE vis, int refcount)
{
	hUcgo.SetUcgoVisual(vis);	// must be called in clbkVisualCreated.
}
////////////////////////////////////////////////////////
// SendCargHudMessage
////////////////////////////////////////////////////////
char *ShuttleD::SendCargHudMessage(void)
{
	dCargHudMessageDelay=15; // 15 seconds display delay for msg
	return cCargoHudDisplay;
}
// --------------------------------------------------------------
// Respond to buffered keyboard events
// --------------------------------------------------------------
int ShuttleD::clbkConsumeBufferedKey (DWORD key, bool down, char *kstate)
{
	if (!down) return 0;       // only process keydown events

	if(key==OAPI_KEY_G)
	{ //Gear
		RevertGEAR();
		return 1;
	}   
	if(key==OAPI_KEY_K)
	{// Payload Bay B
		RevertPLBAYB();
		return 1;
	}  
	if(key==OAPI_KEY_O)
	{// hatch
		RevertPLBAYA();
		return 1;
	}     

	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_A&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		// switch state
		Crew.SetAirlockDoorState(!Crew.GetAirlockDoorState());
		// display state
		if(Crew.GetAirlockDoorState()==TRUE)
			strcpy(SendHudMessage(),"Airlock is now open");	
		else
			strcpy(SendHudMessage(),"Airlock is now closed");	
		return 1;
	}
	//---------------------------------------------------------------------------
	// 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(),4-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);
	}

		// 9 key "select" one cargo on disk (cycle)
	if(key==OAPI_KEY_9&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		sprintf(SendCargHudMessage(),"%s - selected",hUcgo.ScnEditor_SelectNextCargoAvailableOnDisk());
		return 1;
	}
	// SHIFT+9 key "add last cargo selected by key 9"
	// If iSelectedCargo=-1 (default) add to the first free slot found
	if(key==OAPI_KEY_9&&KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		if(hUcgo.ScnEditor_AddLastSelectedCargoToSlot(iSelectedCargo)==TRUE)
		{
			strcpy(SendCargHudMessage(),"Cargo added to ship");
		}
		else
		{
			if(iSelectedCargo<0)
			{
				strcpy(SendCargHudMessage(),"Cargo not added (ship full or weight excess ?)");
			}
			else
			{
				strcpy(SendCargHudMessage(),"Cargo not added (slot full ship full or weight excess ?)");
			}
		}
		return 1;
	}
	// "C" grapple cargo. If iSelectedCargo=-1 (default) add to the first free slot found
	if(key==OAPI_KEY_C&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		int ReturnedCode=hUcgo.GrappleOneCargo(iSelectedCargo);
		// for return code list see function "GrappleOneCargo" in the header
		switch(ReturnedCode)
		{
		case 1:
			strcpy(SendCargHudMessage(),"Cargo grappled");
			break;
		case 0:
			strcpy(SendCargHudMessage(),"No cargo in range");
			break;
		case -1:
			strcpy(SendCargHudMessage(),"cargo exceed maximum mass");
			break;
		case -2:
			strcpy(SendCargHudMessage(),"bad config, mesh not found or slot not declared");
			break;
		case -3:
			strcpy(SendCargHudMessage(),"Can't grapple cargo, slot not empty");
			break;
		case -4:
			strcpy(SendCargHudMessage(),"Can't grapple cargo,doors closed ");
			break;
		case -5:
			strcpy(SendCargHudMessage(),"Can't grapple cargo,Ship full");
			break;
		default:
			strcpy(SendCargHudMessage(),"Misc error Unable to grapple cargo");
		}
		return 1;
	}
	// SHIFT+C release cargo. If iSelectedCargo=-1 (default) release the first free slot found
	if(key==OAPI_KEY_C&&KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		if(hUcgo.ReleaseOneCargo(iSelectedCargo)!=FALSE)
		{
			strcpy(SendCargHudMessage(),"Cargo released");
		}
		else
		{
			strcpy(SendCargHudMessage(),"Cargo not released (empty slot, no cargo aboard?)");
		}
		return 1;
	}
	// "8" show some info on cargo
	if(key==OAPI_KEY_8&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		sprintf(SendCargHudMessage(),"%i cargos aboard. "
		"Total cargos weight: %.0fkg",hUcgo.GetNbrCargoLoaded(),hUcgo.GetCargoTotalMass());
		return 1;
	}

	return 0;
}

//-------------------------------------------------------------------------
// THIS IS FOR ADDING CREW SEE PDF doc "Allow user to add crew to your ship 
// without scenery editor"
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 ShuttleD::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?)");
		}
	}
}

//=========================================================
// Load and Delete Module Stuff
//=========================================================
DLLCLBK void InitModule (HINSTANCE hModule)
{
	g_hDLL = hModule;
	hFont = CreateFont (-20, 3, 0, 0, 150, 0, 0, 0, 0, 0, 0, 0, 0, "Arial");
	hPen = CreatePen (PS_SOLID, 3, RGB (120,220,120));
	hBrush = CreateSolidBrush (RGB(0,128,0));
	// perform global module initialisation here
}
DLLCLBK void ExitModule (HINSTANCE hModule)
{
	// perform module cleanup here
	DeleteObject (hFont);
	DeleteObject (hPen);
	DeleteObject (hBrush);
}

//=========================================================
// Load and Delete Vessel Stuff
//=========================================================
DLLCLBK VESSEL *ovcInit (OBJHANDLE hvessel, int flightmodel)
{
	return new ShuttleD (hvessel, flightmodel);
}
DLLCLBK void ovcExit (VESSEL *vessel)
{
	if (vessel)
		delete (ShuttleD*)vessel;
}

void ShuttleD::clbkPostCreation (void)
{

	////////////////////////////////////////////////////////////////
	// 3-ORBITERSOUND EXAMPLE - INIT AND LOADING OF WAV
	// THIS MUST BE CALLED ABSOLUTELY IN THE "POSTCREATION CALLBACK" 
	////////////////////////////////////////////////////////////////
	// here we connect to OrbiterSound and store the returned ID in your class
	// this is the first thing to do. You must call this in "clbkPostCreation" 
	// (new version of ovcPostCreation wich is now obsolet)
	SHD=ConnectToOrbiterSoundDLL3(GetHandle());

	RequestLoadVesselWave3(SHD,GEARUP,"Sound\\ShuttleD\\gearup.wav",INTERNAL_ONLY);
	RequestLoadVesselWave3(SHD,GEARDOWN,"Sound\\ShuttleD\\geardown.wav",INTERNAL_ONLY);
	RequestLoadVesselWave3(SHD,PLBAYAOPEN,"Sound\\ShuttleD\\plbayaopen.wav",INTERNAL_ONLY);
	RequestLoadVesselWave3(SHD,PLBAYACLOSE,"Sound\\ShuttleD\\plbayaclose.wav",INTERNAL_ONLY);
	RequestLoadVesselWave3(SHD,PLBAYBOPEN,"Sound\\ShuttleD\\plbaybopen.wav",INTERNAL_ONLY);
	RequestLoadVesselWave3(SHD,PLBAYBCLOSE,"Sound\\ShuttleD\\plbaybclose.wav",INTERNAL_ONLY);

	// now we are allowed for example to replace variable sound of OrbiterSound.
	// it will use them instead of the stock one when our vessel have the focus, see header file for parameter 
	// (you can replace more than the four below see parameters for "ReplaceStockSound3()")
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\mainext.wav",	REPLACE_MAIN_THRUST);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\attfire.wav",		REPLACE_RCS_THRUST_ATTACK);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\attsustain.wav",	REPLACE_RCS_THRUST_SUSTAIN);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_AIR_CONDITIONNING);

	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_1);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_2);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_3);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_4);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_5);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_6);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_7);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_8);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_9);

	// Now your ship have custom sound you can end here if you don't want to bother anymore
	// with sound, your ship is already customised. Anyway I'll show you more below.

	//...

	// here we will load a sound that will play when we pass 500 meter altitude
	// We will play it later ourself when we reach 500 meter (see opcPreStep below)
	RequestLoadVesselWave3(SHD,MY500METERSOUND,"Sound\\OrbiterSound_SDK\\sound\\my500meter.wav",RADIO_SOUND);
	// AS we will play it using the "radioexclusive function" and as we don't want to be disturbed
	// by OrbiterSound's ATC we set the ATC option of OrbiterSound to false (no more ATC will play)
	// it's not an obligation while the "playradio..." function stop any stock ATC sound that is already playing
	// but if you want to use radio sound you may not want to be disturbed by "off-topic" atc.
	SoundOptionOnOff3(SHD,PLAYRADIOATC,FALSE);

	// we disable also the stock countdown when we take-off.. it's annoying :)
	SoundOptionOnOff3(SHD,PLAYCOUNTDOWNWHENTAKEOFF,FALSE);

	// Here we will load a radar sound that will play in cockpit view only and after take-off below 500 meter
	// this sound will be looped and will see it's frequency varying as we approach ground
	RequestLoadVesselWave3(SHD,MYRADARSOUND,"Sound\\OrbiterSound_SDK\\sound\\myradar.wav",INTERNAL_ONLY);

	// Here is THE example of an external sound that must be also faded by distance and pressure 
	// as stated in the header of ObiterSoundSDK3.h it's IMPORTANT that you call this sound looped
	// and at each frame otherwise it will not be faded properly.
	// "EXTERNAL_ONLY_FADED_CLOSE" mean that it will be faded at close distance as the RCS sound in external view.
	RequestLoadVesselWave3(SHD,MYEXTERNAL_UFO_FADEDSOUND,"Sound\\OrbiterSound_SDK\\sound\\myUfoSound.wav",EXTERNAL_ONLY_FADED_CLOSE);
}

// --------------------------------------------------------------
// loop called each frame
// --------------------------------------------------------------
void ShuttleD::clbkPreStep (double simt, double simdt, double mjd) 
{
	double Altitude=0;
	static BOOL AltitudeSoundPlayed=FALSE;
	static BOOL RadarFlagForSound=FALSE;

	///////////////////////////////////////////////////////////////////
	// 4-ORBITERSOUND EXAMPLE - PLAYING THE SOUND WE LOADED DYNAMICALLY
	///////////////////////////////////////////////////////////////////

	// ----------------------------------------------------------
	// We play the "500 meter" sound we loaded previously 
	// we use the function "playradio" wich stop OrbiterSound atc
	// sound if one is playing.
	// ----------------------------------------------------------
	oapiGetFocusAltitude(&Altitude);
	if(Altitude>500&&Altitude<550&&AltitudeSoundPlayed==FALSE)
	{
		PlayVesselRadioExclusiveWave3(SHD,MY500METERSOUND,255);	// 255=volume maximum 
		AltitudeSoundPlayed=TRUE;
	}
	if(Altitude<500||Altitude>550)
		AltitudeSoundPlayed=FALSE;


	// ------------------------------------------------------
	// here we play our radar sound that slide with altitude
	// ------------------------------------------------------
	if(Altitude<500&&Altitude>2)
	{
		PlayVesselWave3(SHD,MYRADARSOUND,LOOP,255,(int)(11025+((500-Altitude)*40)));		// play looped and a frequency that increase with altitude
		RadarFlagForSound=TRUE;
	}
	else if(RadarFlagForSound=TRUE)
	{
		// we stop the sound only once (it would not harm else but it will save a bit of processor time)
		StopVesselWave3(SHD,MYRADARSOUND);
	}


	// ------------------------------------------------------
	// here we play our external "ufo" sound
	// ------------------------------------------------------
	// As stated a sound that play in external view must be called looped heach frame so it
	// fade properly with distance and pressure. Also we add a litle effect: if the ship is landed
	// the frequency is lower simulating an "iddle" engine.
	if(Altitude<2)
		PlayVesselWave3(SHD,MYEXTERNAL_UFO_FADEDSOUND,LOOP,255,11025); // original wav is 11025 hz
	else
		PlayVesselWave3(SHD,MYEXTERNAL_UFO_FADEDSOUND,LOOP,255,9000);  


	// -------------------------------------------------------------
	// another example with a different behaviour of the "Ufo" sound
	// -------------------------------------------------------------
	// here an example of the ufo sound that will play only beetween 0 and 500 meter high.
	// I show it here so you know how to stop/start an external sound.
	//if(Altitude<500)
	//	PlayVesselWave3(MyID,MYEXTERNAL_UFO_FADEDSOUND,LOOP,255); // original wav is 11025 hz
	//else
	//	StopVesselWave3(MyID,MYEXTERNAL_UFO_FADEDSOUND);		   // you can use a flag to avoid reapeated call (even the impact is very low, the function return immediately if the sound is already stoped)
}
 
Okay, tried to implement an Oxygen Tank named "O2Tank" with a 1000 kg capacity with a Propellant handle, designed to kill the crew when below 1. When I compile & test it however, Orbiter crashes, and I know that the Shuttle-D is the problem at this point. Anyone know what can be done to fix this?

Header File

Code:
#pragma once
#include "Orbitersdk.h"
#include "UMmuSDK.h"
#include "UCGOCargoSDK.h" //UCGO 2.0 copy past this line

// Vessel Parameters
const double EXP_SIZE = 37.4; // mean radius in meters
const VECTOR3 EXP_CS = {95,82,20}; //Shuttle-D cross section in m^2
const VECTOR3 EXP_PMI = {14.00,16.00,2.20}; //Principal Moments of Inertia, normalized, m^2
const double EXP_EMPTYMASS = 16970; //empty vessel mass in kg
const double EXP_FUELMASS =  18000; //max fuel mass in kg
const double EXP_RCS1FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS2FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS3FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS4FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS5FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS6FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS7FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS8FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS9FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS10FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS11FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS12FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS13FUELMASS =  120; //max fuel mass in kg
const double EXP_RCS14FUELMASS =  120; //max fuel mass in kg
const double EXP_RCSRESFUELMASS =  1000; //max fuel mass in kg
const double EXP_LOXMASS =  1000; //max Oxygen mass in kg
const double VACSHD_ISP = 30000; //fuel-specific impulse in m/s
const double NMLSHD_ISP = 25000; //fuel-specific impulse in m/s
const double P_NML = 101.4e3;
const double EXP_MAXMAINTH = 250000; 
const double EXP_MAXHOVERTH = 94000; 
const double RCSTH0 = 1200; 
const double RCSTH1 = 1200;
const double RCSTH2 = 720; 
const double RCSTH3 = 720;
const double RCSTH4 = 255; 
const double RCSTH5 = 255;
const double RCSTH6 = 255; 
const double RCSTH7 = 255;
const double RCSTH8 = 500; 
const double RCSTH9 = 500;
const double RCSTH10 = 500; 
const double RCSTH11 = 500;
const double RCSTH12 = 1200; 
const double RCSTH13 = 1200;
const double RCSTH14 = 1200; 
const double RCSTH15 = 1200;
const double GEAR_OPERATING_SPEED = 0.10;
const double PLBAYA_OPERATING_SPEED = 0.07;
const double PLBAYB_OPERATING_SPEED = 0.18;


class ShuttleD :public VESSEL3
{
public:
    ShuttleD (OBJHANDLE hObj, int fmodel);

	~ShuttleD();

	MESHHANDLE DVCInterior;
void DefineAnimations();
	void clbkSetClassCaps (FILEHANDLE cfg);
	void clbkLoadStateEx (FILEHANDLE scn, void *status);
	bool clbkDrawHUD (int mode, const HUDPAINTSPEC *hps, oapi::Sketchpad *skp);
	void clbkSaveState (FILEHANDLE scn);
	void Timestep (double simt);
	void clbkPostStep (double simtt, double simdt, double mjd);
	void RevertGEAR (void);
	void RevertPLBAYA (void);
	void RevertPLBAYB (void);
	void CheckOxygen (void);
	void CheckFuelState (void);
	void clbkPreStep (double simt, double simdt, double mjd);
	void clbkPostCreation(void);

	enum GEARStatus { GEAR_UP, GEAR_DOWN, GEAR_RAISING, GEAR_LOWERING } GEAR_status;
	enum PLBAYAStatus { PLBAYA_UP, PLBAYA_DOWN, PLBAYA_CLOSING, PLBAYA_OPENING } PLBAYA_status;
	enum PLBAYBStatus { PLBAYB_UP, PLBAYB_DOWN, PLBAYB_CLOSING, PLBAYB_OPENING } PLBAYB_status;
	enum {CAM_VCPILOT, CAM_VCPSNGR1, CAM_VCPSNGR2, CAM_VCPSNGR3, CAM_VCPSNGR4} campos;
	int SHD;	// this is your unique Id, it will identify your ship in OrbiterSound.

	int  clbkConsumeBufferedKey (DWORD key, bool down, char *kstate);
	void clbkVisualCreated (VISHANDLE vis, int refcount);
	void clbkMFDMode (int mfd, int mode);
	bool clbkLoadVC (int id);
	bool clbkVCRedrawEvent (int id, int event, SURFHANDLE surf);
	bool clbkVCMouseEvent (int id, int event, VECTOR3 &p);
	VCMFDSPEC mfds_left;

		// 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);

	// UCGO 2.0 CLASS HANDLE FUNCTION AND VARIABLES
	UCGO	hUcgo;						// Cargo class handle
	char   *SendCargHudMessage(void);	// Cargo hud display function
	char	cCargoHudDisplay[255];		// Cargo hud display char variable
	double	dCargHudMessageDelay;		// Cargo hud display delay
	int		iSelectedCargo;				// for the selection of cargos -1 by default

//	OBJHANDLE GetDockStatus (DOCKHANDLE hDock) const;

	/**
	 * \brief Returns a status flag for a docking port.
	 * \param port docking port index (>= 0)
	 * \return Docking status (0=free, 1=engaged)
	 * \note This method has the same functionality as 
	 *   \code (GetDockStatus (GetDockHandle(port)) ? 1:0) \endcode
	 * \sa GetDockStatus, GetDockHandle
	 */

//	void IncEngineLevel (ENGINETYPE eng, double dlevel) const;


private:
	UINT anim_gear;
	UINT anim_PLBAYA;
	UINT anim_PLBAYB;
	double GEAR_proc,PLBAYA_proc,PLBAYB_proc;
	ATTACHMENTHANDLE  payload_attachment[2];
	PROPELLANT_HANDLE *MainFuel, *RCSRES, *RCS1, *RCS2, *RCS3, *RCS4, *RCS5, *RCS6, *RCS7, *RCS8, *RCS9, *RCS10, *RCS11, *RCS12, *O2Tank;
	THRUSTER_HANDLE *th_rcs;
	//double LIFT_SPEED;
};


HINSTANCE hDLL;
HFONT hFont;
HPEN hPen;
HBRUSH hBrush;

#define AID_MFD1_LBUTTONS		0
#define AID_MFD1_RBUTTONS		1
#define MFD1_LBUTTON1			2
#define MFD1_LBUTTON2			3
#define MFD1_LBUTTON3			4
#define MFD1_LBUTTON4			5
#define MFD1_LBUTTON5			6
#define MFD1_LBUTTON6			7
#define MFD1_RBUTTON1			8
#define MFD1_RBUTTON2			9
#define MFD1_RBUTTON3			10
#define MFD1_RBUTTON4			11
#define MFD1_RBUTTON5			12
#define MFD1_RBUTTON6			13
#define MFD1_BBUTTON1			14
#define MFD1_BBUTTON2			15
#define MFD1_BBUTTON3			16

and the CPP,

Code:
// SHD.cpp : Defines the exported functions for the DLL application.
//

#define STRICT
#define ORBITER_MODULE
#include "D9base.h"
#include "UCGOCargoSDK.h"
#include "orbitersdk.h"
#include <math.h>
#include <stdio.h>
#include "OrbiterSoundSDK35.h"
#define MY500METERSOUND				1		// those are definition so all function will be more clear to read..
#define MYRADARSOUND				2		// because we will know wich sound is concerned by function instead
#define MYEXTERNAL_UFO_FADEDSOUND	3		// of having simple number.
#define GEARUP						 4
#define GEARDOWN					 5
#define PLBAYAOPEN					 6
#define PLBAYACLOSE					 7
#define PLBAYBOPEN					 8
#define PLBAYBCLOSE					 9

HINSTANCE g_hDLL;
VISHANDLE MainExternalMeshVisual = 0;

// ==============================================================
// Airfoil definition
// ==============================================================

void Shuttle_MomentCoeff (double aoa,double M,double Re,double *cl,double *cm,double *cd)
{
	int i;
	const int nabsc = 7;
	static const double AOA[nabsc] = {-180*RAD, -90*RAD,-30*RAD, 0*RAD, 60*RAD,90*RAD,180*RAD};
	static const double CL[nabsc]  = {       0,     -0.0005,   -0.001,     0,     0.0071,     0,      0};
	static const double CM[nabsc]  = {       0,      0,   0.0007,  0,-0.0010,     0,      0};

	for (i = 0; i < nabsc-1 && AOA[i+1] < aoa; i++);
	double f = (aoa-AOA[i]) / (AOA[i+1]-AOA[i]);
	*cl = CL[i] + (CL[i+1]-CL[i]) * f;  // aoa-dependent lift coefficient
	*cm = CM[i] + (CM[i+1]-CM[i]) * f;  // aoa-dependent moment coefficient
	double saoa = sin(aoa);
	double pd = 0.045 + 0.4*saoa*saoa;  // profile drag
	*cd = pd + oapiGetInducedDrag (*cl, 0.1,0.7) + oapiGetWaveDrag (M, 0.75, 1.0, 1.1, 0.04);
	// profile drag + (lift-)induced drag + transonic/supersonic wave (compressibility) drag
}

ShuttleD::ShuttleD (OBJHANDLE hObj, int fmodel)
	: VESSEL3 (hObj, fmodel)
{
	GEAR_status = GEAR_UP;
	GEAR_proc = 0.0;
	PLBAYA_status = PLBAYA_UP;
	PLBAYA_proc = 0.0;
	PLBAYB_status = PLBAYB_UP;
	PLBAYB_proc = 0.0;

	DefineAnimations();
}

//=========================================================
// Vessel Animations
//=========================================================
void ShuttleD::DefineAnimations()
{
	//ANIMATIONCOMPONENT_HANDLE parent;
	static UINT gearback[1] = {26};
	static MGROUP_ROTATE gear (
		0,
		gearback,
		1,
		_V(0,-2.3,-22.9),
		_V(1,0,0),
		(float)(-90*RAD)
		);
	static UINT gearfront[1] = {27};
	static MGROUP_ROTATE gearf (
		0,
		gearfront,
		1,
		_V(0,-2.3,29.8),
		_V(1,0,0),
		(float)(90*RAD)
		);
	anim_gear = CreateAnimation (0);
	AddAnimationComponent (anim_gear, 0, 1, &gear);
	AddAnimationComponent (anim_gear, 0, 1, &gearf);

	static UINT PLBAYA[1] = {21};
	static MGROUP_ROTATE PLBAYA1 (
		0,
		PLBAYA,
		1,
		_V(0.858,-1.721666,0),
		_V(0,0,1),
		(float)(90*RAD)
		);
	anim_PLBAYA = CreateAnimation (0);
	AddAnimationComponent (anim_PLBAYA, 0, 1, &PLBAYA1);

	static UINT PLBAYB[1] = {17};
	static MGROUP_ROTATE PLBAYB1 (
		0,
		PLBAYB,
		1,
		_V(0,4.513,-23.72),
		_V(1,0,0),
		(float)(-90*RAD)
		);
	anim_PLBAYB = CreateAnimation (0);
	AddAnimationComponent (anim_PLBAYB, 0, 1, &PLBAYB1);
}

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

void ShuttleD::RevertPLBAYA (void)
{
	PLBAYA_status = ((PLBAYA_status == PLBAYA_UP || PLBAYA_status == PLBAYA_CLOSING) ?
PLBAYA_OPENING : PLBAYA_CLOSING);
}

void ShuttleD::RevertPLBAYB (void)
{
	PLBAYB_status = ((PLBAYB_status == PLBAYB_UP || PLBAYB_status == PLBAYB_CLOSING) ?
PLBAYB_OPENING : PLBAYB_CLOSING);
}

void ShuttleD::CheckOxygen (void)
{
GetPropellantMaxMass (O2Tank);
}

//=========================================================
// Vessel Capabilities
//=========================================================
void ShuttleD::clbkSetClassCaps (FILEHANDLE cfg)
{
	Crew.InitUmmu(GetHandle());	
	float UMmuVersion=Crew.GetUserUMmuVersion();
	//	double UMmuVersion=Crew.GetUserUMmuVersion();
	Crew.DefineAirLockShape(TRUE,-1,1,-2.94,2.94, 29,32);
	Crew.SetMembersPosRotOnEVA(_V(0,-0.837,31.005),_V(0,-180,0));
	Crew.SetMaxSeatAvailableInShip(7);

	Crew.DeclareActionArea(0,_V(-5,0,0),2.5,TRUE,"action_activated.wav","You are on the left of the ship");
	Crew.DeclareActionArea(1,_V(5,0,0),2.5,TRUE, "action_repaired.wav","You are on the right of the ship");
	Crew.DeclareActionArea(2,_V(0,0,5),2.5,TRUE, "action_activated.wav","You are in front of the ship");
	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"

	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\n A=Open/Close airlock S=info M=add crew");

	// The Add mmu without scenery editor variable see PDF doc
	cAddUMmuToVessel[0]=0;

	//UCGO 2.0 Initialisation, cargo slot pos, rot declaration
	hUcgo.Init(GetHandle());
	// in contrary of the PDF code we declare 6 slots
	// because it's fun :)
	hUcgo.DeclareCargoSlot(0,_V(-0.65,-3.2,16.6),_V(0,0,270)); // slot 0
	hUcgo.DeclareCargoSlot(1,_V(-0.65,-3.2,15.3),_V(0,0,270)); // slot 1
	hUcgo.DeclareCargoSlot(2,_V(-0.65,-3.2,14),_V(0,0,270)); // slot 2
	hUcgo.DeclareCargoSlot(3,_V(-0.65,-3.2,12.7),_V(0,0,270)); // slot 3
	hUcgo.DeclareCargoSlot(4,_V(-0.65,-3.2,11.4),_V(0,0,270)); // slot 4
	hUcgo.DeclareCargoSlot(5,_V(-0.65,-3.2,10.1),_V(0,0,270)); // slot 5
	hUcgo.DeclareCargoSlot(6,_V(-0.65,-3.2,8.8),_V(0,0,270)); // slot 6
	hUcgo.DeclareCargoSlot(7,_V(-0.65,-3.2,7.5),_V(0,0,270)); // slot 7
	hUcgo.DeclareCargoSlot(8,_V(-0.65,-3.2,6.2),_V(0,0,270)); // slot 8
	hUcgo.DeclareCargoSlot(9,_V(-0.65,-3.2,4.9),_V(0,0,270)); // slot 9
	hUcgo.DeclareCargoSlot(10,_V(-0.65,-3.2,3.6),_V(0,0,270)); // slot 10
	hUcgo.DeclareCargoSlot(11,_V(-0.65,-3.2,2.3),_V(0,0,270)); // slot 11
	hUcgo.DeclareCargoSlot(12,_V(-0.65,-3.2,1),_V(0,0,270)); // slot 12
	hUcgo.DeclareCargoSlot(13,_V(-0.65,-3.2,-0.3),_V(0,0,270)); // slot 13
	hUcgo.DeclareCargoSlot(14,_V(-0.65,-3.2,-1.6),_V(0,0,270)); // slot 14
	hUcgo.DeclareCargoSlot(15,_V(-0.65,-3.2,-2.9),_V(0,0,270)); // slot 15
	hUcgo.DeclareCargoSlot(16,_V(-0.65,-3.2,-4.2),_V(0,0,270)); // slot 16
	hUcgo.DeclareCargoSlot(17,_V(-0.65,-3.2,-5.5),_V(0,0,270)); // slot 17

	hUcgo.SetSlotGroundReleasePos(0,_V(2,-3.5,16.6)); // slot 0
	hUcgo.SetSlotGroundReleasePos(1,_V(2,-3.5,15.3)); // slot 1
	hUcgo.SetSlotGroundReleasePos(2,_V(2,-3.5,14)); // slot 2
	hUcgo.SetSlotGroundReleasePos(3,_V(2,-3.5,12.7)); // slot 3
	hUcgo.SetSlotGroundReleasePos(4,_V(2,-3.5,11.4)); // slot 4
	hUcgo.SetSlotGroundReleasePos(5,_V(2,-3.5,10.1)); // slot 5
	hUcgo.SetSlotGroundReleasePos(6,_V(2,-3.5,8.8)); // slot 6
	hUcgo.SetSlotGroundReleasePos(7,_V(2,-3.5,7.5)); // slot 7
	hUcgo.SetSlotGroundReleasePos(8,_V(2,-3.5,6.2)); // slot 8
	hUcgo.SetSlotGroundReleasePos(9,_V(2,-3.5,4.9)); // slot 9
	hUcgo.SetSlotGroundReleasePos(10,_V(2,-3.5,3.6)); // slot 10
	hUcgo.SetSlotGroundReleasePos(11,_V(2,-3.5,2.3)); // slot 11
	hUcgo.SetSlotGroundReleasePos(12,_V(2,-3.5,1)); // slot 12
	hUcgo.SetSlotGroundReleasePos(13,_V(2,-3.5,-0.3)); // slot 13
	hUcgo.SetSlotGroundReleasePos(14,_V(2,-3.5,-1.6)); // slot 14
	hUcgo.SetSlotGroundReleasePos(15,_V(2,-3.5,-2.9)); // slot 15
	hUcgo.SetSlotGroundReleasePos(16,_V(2,-3.5,-4.2)); // slot 16
	hUcgo.SetSlotGroundReleasePos(17,_V(2,-3.5,-5.5)); // slot 17

	// UCGO 2.0 Parameters settings
	hUcgo.SetReleaseSpeedInSpace(0.001f);	   // release speed of cargo in space in m/s
	hUcgo.SetMaxCargoMassAcceptable(50000.0);   // max cargo mass in kg that your vessel can carry
	hUcgo.SetGrappleDistance(60);		       // grapple distance radius in meter from center of ship


	// UCGO Variables initialisation
	cCargoHudDisplay[0]=0;						// Cargo hud display char variable
	dCargHudMessageDelay=0;						// Cargo hud display delay
	iSelectedCargo=-1;							// for the selection of cargos (-1 mean "default" see header)
	// welcome message with keys for users
	strcpy(SendCargHudMessage(),"Cargo key: C/SHF+C = grapple/release, 9/SHF+9 = snc editor add cargo, 8=infos on cargos");

	THRUSTER_HANDLE th_main, th_hover, th_rcs[14], th_group[4];
	DOCKHANDLE Dock0, Dock1;


	// ************************ Airfoil  ****************************
	ClearAirfoilDefinitions();
	CreateAirfoil (LIFT_VERTICAL, _V(0,0,0), Shuttle_MomentCoeff,  8, 140, 0.1);


	// vessel caps definitions
	AddMesh ("Shuttle-D");

	SetMeshVisibilityMode (AddMesh (DVCInterior = oapiLoadMeshGlobal ("ShuttleDVC")), MESHVIS_VC);

	SetCameraOffset (_V(0,0.24,0.13));
	SetAlbedoRGB (_V(0.77,0.20,0.73));
	SetSize (EXP_SIZE);

	SetEmptyMass (EXP_EMPTYMASS);
	void UpdateEmptyMass(void);

	SetPMI (EXP_PMI);
	SetCrossSections (EXP_CS);
	SetSurfaceFrictionCoeff (0.55, 0.79);
	SetRotDrag (_V(0.9, 0.76, 0.2));

	EnableTransponder (true);
	InitNavRadios (4);

	// propellant resources
	PROPELLANT_HANDLE MainFuel = CreatePropellantResource (EXP_FUELMASS);
	PROPELLANT_HANDLE RCS1 = CreatePropellantResource (EXP_RCS1FUELMASS);
	PROPELLANT_HANDLE RCS2 = CreatePropellantResource (EXP_RCS2FUELMASS);
	PROPELLANT_HANDLE RCS3 = CreatePropellantResource (EXP_RCS3FUELMASS);
	PROPELLANT_HANDLE RCS4 = CreatePropellantResource (EXP_RCS4FUELMASS);
	PROPELLANT_HANDLE RCS5 = CreatePropellantResource (EXP_RCS5FUELMASS);
	PROPELLANT_HANDLE RCS6 = CreatePropellantResource (EXP_RCS6FUELMASS);
	PROPELLANT_HANDLE RCS7 = CreatePropellantResource (EXP_RCS7FUELMASS);
	PROPELLANT_HANDLE RCS8 = CreatePropellantResource (EXP_RCS8FUELMASS);
	PROPELLANT_HANDLE RCS9 = CreatePropellantResource (EXP_RCS9FUELMASS);
	PROPELLANT_HANDLE RCS10 = CreatePropellantResource (EXP_RCS10FUELMASS);
	PROPELLANT_HANDLE RCS11 = CreatePropellantResource (EXP_RCS11FUELMASS);
	PROPELLANT_HANDLE RCS12 = CreatePropellantResource (EXP_RCS12FUELMASS);
	PROPELLANT_HANDLE RCSRES = CreatePropellantResource (EXP_RCSRESFUELMASS);
	PROPELLANT_HANDLE O2Tank = CreatePropellantResource (EXP_LOXMASS);

	// main engine
	th_main = CreateThruster (_V(0,0,-31.45), _V(0,0,1), EXP_MAXMAINTH, MainFuel, VACSHD_ISP, NMLSHD_ISP, P_NML);
	CreateThrusterGroup (&th_main, 1, THGROUP_MAIN);
	AddExhaust (th_main, 8, 1, _V(0,0.23,-31.45), _V(0,0,-1));

	PARTICLESTREAMSPEC contrail_main = {
		0, 5.0, 16, 200, 0.15, 1.0, 5, 3.0, PARTICLESTREAMSPEC::DIFFUSE,
		PARTICLESTREAMSPEC::LVL_PSQRT, 0, 2,
		PARTICLESTREAMSPEC::ATM_PLOG, 1e-4, 1
	};
	PARTICLESTREAMSPEC exhaust_main = {
		0, 2.0, 20, 200, 0.05, 0.1, 8, 1.0, PARTICLESTREAMSPEC::EMISSIVE,
		PARTICLESTREAMSPEC::LVL_SQRT, 0, 1,
		PARTICLESTREAMSPEC::ATM_PLOG, 1e-5, 0.1
	};
	AddExhaustStream (th_main, _V(0,0.3,-10), &contrail_main);
	AddExhaustStream (th_main, _V(0,0.3,-5), &exhaust_main);

	// hover engine
	th_hover = CreateThruster (_V(0,-2.94,0), _V(0,1,0), EXP_MAXHOVERTH, MainFuel,  VACSHD_ISP, NMLSHD_ISP, P_NML);
	CreateThrusterGroup (&th_hover, 1, THGROUP_HOVER);
	AddExhaust (th_hover, 8, 1, _V(0.0,-2.94,-17.09), _V(0,-1,0));
	AddExhaust (th_hover, 11.05675834, 1, _V(0.0,-2.94,23.62), _V(0,-1,0));

	PARTICLESTREAMSPEC contrail_hover = {
		0, 5.0, 8, 200, 0.15, 1.0, 5, 3.0, PARTICLESTREAMSPEC::DIFFUSE,
		PARTICLESTREAMSPEC::LVL_PSQRT, 0, 2,
		PARTICLESTREAMSPEC::ATM_PLOG, 1e-4, 1
	};
	PARTICLESTREAMSPEC exhaust_hover = {
		0, 2.0, 10, 200, 0.05, 0.05, 8, 1.0, PARTICLESTREAMSPEC::EMISSIVE,
		PARTICLESTREAMSPEC::LVL_SQRT, 0, 1,
		PARTICLESTREAMSPEC::ATM_PLOG, 1e-5, 0.1
	};

	AddExhaustStream (th_hover, _V(0,-3, 1), &contrail_hover);
	AddExhaustStream (th_hover, _V(0,-3,-1), &contrail_hover);
	AddExhaustStream (th_hover, _V(0,-2, 1), &exhaust_hover);
	AddExhaustStream (th_hover, _V(0,-2,-1), &exhaust_hover);

	// RCS engines
	th_rcs[0] = CreateThruster (_V( 1.601,0.100, 29.777), _V(0,0,1), RCSTH0, RCS1,  VACSHD_ISP, NMLSHD_ISP, P_NML);//CM FRONT RIGHT SIDE FORWARD
	th_rcs[1] = CreateThruster (_V( -1.601,0.100, 29.777), _V(0,0,1), RCSTH1, RCS2,  VACSHD_ISP, NMLSHD_ISP, P_NML);//CM FRONT LEFT SIDE FORWARD

	th_rcs[2] = CreateThruster (_V(1.8505,0.100, 29.369), _V(-1, 0,0), RCSTH2, RCS1,  VACSHD_ISP, NMLSHD_ISP, P_NML);//CM FRONT RIGHT SIDE RIGHT
	th_rcs[3] = CreateThruster (_V(-1.8505,0.100, 29.369), _V(1,0,0), RCSTH3, RCS2,  VACSHD_ISP, NMLSHD_ISP, P_NML);//CM FRONT LEFT SIDE LEFT

	th_rcs[4] = CreateThruster (_V( 1.101,2.04,21.208), _V(0, 1,0), RCSTH4, RCS3,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS FORWARD RIGHT SIDE UP
	th_rcs[5] = CreateThruster (_V( -1.101,2.04,21.208), _V(0,1,0), RCSTH5, RCS4,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS FORWARD LEFT SIDE UP
	th_rcs[6] = CreateThruster (_V(1.101,-1.736,21.208), _V(0,-1,0), RCSTH6, RCS5,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS FORWARD RIGHT SIDE DOWN
	th_rcs[7] = CreateThruster (_V(-1.101,-1.736,21.208), _V(0,-1,0), RCSTH7, RCS6,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS FORWARD LEFT SIDE DOWN

	th_rcs[8] = CreateThruster (_V( 1.101,2.04, -11.055), _V(0,1,0), RCSTH8, RCS7,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS AFT RIGHT SIDE UP
	th_rcs[9] = CreateThruster (_V( -1.101,2.04, -11.055), _V( 0,1,0), RCSTH9, RCS8,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS AFT LEFT SIDE UP
	th_rcs[10] = CreateThruster (_V(1.101,-1.736,-11.055), _V(0,-1,0), RCSTH10, RCS9,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS AFT RIGHT SIDE DOWN
	th_rcs[11] = CreateThruster (_V(-1.101,-1.736,-11.055), _V( 0,-1,0), RCSTH11, RCS10,  VACSHD_ISP, NMLSHD_ISP, P_NML);//TRUSS AFT LEFT SIDE DOWN

	th_rcs[12] = CreateThruster (_V( 1.59,0.097,-18.595), _V(0,0, -1), RCSTH12, RCS11,  VACSHD_ISP, NMLSHD_ISP, P_NML);//SM BACK RIGHT SIDE BACKWARDS
	th_rcs[13] = CreateThruster (_V( -1.59,0.097,-18.595), _V(0,0,-1), RCSTH13, RCS12,  VACSHD_ISP, NMLSHD_ISP, P_NML);//SM BACK LEFT SIDE BACKWARDS

	th_rcs[14] = CreateThruster (_V(1.84,0.097,-18.193), _V( -1,0,0), RCSTH14, RCS11,  VACSHD_ISP, NMLSHD_ISP, P_NML);//SM BACK RIGHT SIDE RIGHT
	th_rcs[15] = CreateThruster (_V( -1.84,0.097,-18.193), _V(1,0, 0), RCSTH15, RCS12,  VACSHD_ISP, NMLSHD_ISP, P_NML);//SM BACK LEFT SIDE LEFT

	AddExhaust (th_rcs[12], 0.6,  0.078, _V( 1.601,0.100, 29.777), _V(0,0,1));
	AddExhaust (th_rcs[13], 0.6,  0.078, _V( -1.601,0.100,29.777), _V(0,0,1));

	AddExhaust (th_rcs[2], 0.79, 0.103, _V(1.8505,0.100, 29.369), _V(-1, 0,0));
	AddExhaust (th_rcs[3], 0.79, 0.103, _V(-1.8505,0.100, 29.369), _V(1,0,0));

	AddExhaust (th_rcs[6], 0.6,  0.078, _V( 1.101,2.04,21.208), _V(0, 1,0));
	AddExhaust (th_rcs[7], 0.6,  0.078, _V( -1.101,2.04,21.208), _V(0,1,0));
	AddExhaust (th_rcs[4], 0.79, 0.103, _V(1.101,-1.736,21.208), _V(0,-1,0));
	AddExhaust (th_rcs[5], 0.79, 0.103, _V(-1.101,-1.736,21.208), _V(0,-1,0));

	AddExhaust (th_rcs[10], 0.6,  0.078, _V( 1.101,2.04, -11.055), _V(0,1,0));
	AddExhaust (th_rcs[11], 0.6,  0.078, _V( -1.101,2.04, -11.055), _V( 0,1,0));
	AddExhaust (th_rcs[8], 0.79, 0.103, _V(1.101,-1.736,-11.055), _V(0,-1,0));
	AddExhaust (th_rcs[9], 0.79, 0.103, _V(-1.101,-1.736,-11.055), _V( 0,-1,0));

	AddExhaust (th_rcs[0], 0.6,  0.078, _V( 1.59,0.097,-18.595), _V(0,0, -1));
	AddExhaust (th_rcs[1], 0.6,  0.078, _V( -1.59,0.097,-18.595), _V(0,0,-1));

	AddExhaust (th_rcs[14], 0.79, 0.103, _V(1.84,0.097,-18.193), _V( -1,0,0));
	AddExhaust (th_rcs[15], 0.79, 0.103, _V( -1.84,0.097,-18.193), _V(1,0, 0));


	th_group[0] = th_rcs[4];
	th_group[1] = th_rcs[5];
	th_group[2] = th_rcs[10];
	th_group[3] = th_rcs[11];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_PITCHUP);

	th_group[0] = th_rcs[6];
	th_group[1] = th_rcs[7];
	th_group[2] = th_rcs[8];
	th_group[3] = th_rcs[9];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_PITCHDOWN);

	th_group[0] = th_rcs[4];
	th_group[1] = th_rcs[7];
	th_group[2] = th_rcs[8];
	th_group[3] = th_rcs[11];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_BANKLEFT);

	th_group[0] = th_rcs[5];
	th_group[1] = th_rcs[6];
	th_group[2] = th_rcs[9];
	th_group[3] = th_rcs[10];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_BANKRIGHT);

	th_group[0] = th_rcs[4];
	th_group[1] = th_rcs[5];
	th_group[2] = th_rcs[8];
	th_group[3] = th_rcs[9];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_UP);

	th_group[0] = th_rcs[6];
	th_group[1] = th_rcs[7];
	th_group[2] = th_rcs[10];
	th_group[3] = th_rcs[11];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_DOWN);

	th_group[0] = th_rcs[2];
	th_group[1] = th_rcs[15];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_YAWLEFT);

	th_group[0] = th_rcs[3];
	th_group[1] = th_rcs[14];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_YAWRIGHT);

	th_group[0] = th_rcs[2];
	th_group[1] = th_rcs[14];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_LEFT);

	th_group[0] = th_rcs[3];
	th_group[1] = th_rcs[15];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_RIGHT);

	th_group[0] = th_rcs[0];
	th_group[1] = th_rcs[1];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_FORWARD);

	th_group[0] = th_rcs[12];
	th_group[1] = th_rcs[13];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_BACK);

	Dock0 = CreateDock(_V(0,-0.637,30.455),_V(0,0,1),_V(0,1,0));
	Dock1 = CreateDock(_V(0,2.730,-17.498),_V(0,1,0),_V(0,0,1));

	// ************************ Attachment points ****************************
	char attach_id[8]={"SH"};

	payload_attachment[0]  = CreateAttachment (false,_V(1.76f,0.0f,-7.000f),_V(0.0f,0.0f,-1.0f),_V(0.0f,1.0f,0.0f),attach_id);
	payload_attachment[1]  = CreateAttachment (false,_V(-1.76f,0.0f,-7.000f),_V(0.0f,0.0f,-1.0f),_V(0.0f,1.0f,0.0f),attach_id);
}

void ShuttleD::clbkMFDMode (int mfd, int mode)
{
	//	When an MFD changes mode (either in Panel or VC modes), this call back is
	//	invoked. Here, it is a general TriggerRedrawArea function used, which can
	//	be used to redraw MFD's for VC or Panel views. You can just as effectively
	//	use oapiTriggerVCRedrawArea for this ship, however, as there is no 2D panel.
	switch (mfd) {
	case MFD_LEFT:
		oapiTriggerRedrawArea (1, 2, AID_MFD1_LBUTTONS);
		oapiTriggerRedrawArea (1, 3, AID_MFD1_RBUTTONS);
		break;
	}
}
//-------------------------------------------------------------------------

bool ShuttleD::clbkLoadVC (int id)
{
	//	 VCHUDSPEC hud;
	VCMFDSPEC mfd;

	mfd.ngroup=15;
	mfd.nmesh=1;
	oapiVCRegisterMFD(0,&mfd);
	mfd.ngroup=16;
	mfd.nmesh=1;
	oapiVCRegisterMFD(1,&mfd);
	static VCHUDSPEC hud_pilot  = {1, 14,{0.55,2.04,28.93},1.02};
	static VCHUDSPEC hud_copilot  = {1, 14,{-0.55,2.04,28.93},1.02};
	static VCHUDSPEC hud_fdeckfloor  = {1, 14,{0,2.04,28.93},1.02};
	//	oapiVCRegisterHUD (&hud_pilot); // HUD parameters

	mfds_left.nmesh = 1;	//	The mesh number (the first one loaded, or 0, in this case).
	mfds_left.ngroup  = 15;	//	The mesh group that is the MFD screen in the above identified mesh.

	SetCameraDefaultDirection(_V(0, 0, 1)); // View angles down so you can see the
	//	MFD in VC view by default (it is the sine and cosine of 11º in Y and Z, respectively).

	SURFHANDLE MFDbuttons1 = oapiGetTextureHandle (DVCInterior,2); 
	//	Get the MFDButtons.dds D texture for redrawing purposes.

	int i;
	switch (id) {
	case 0:	//	The first VC cockpit view (id 0). Carefull with mixing up id.
		//	There are more references to id in the code, but what they identify is 
		//	according to the specific callback. For example, in clbkVCMouseEvent,
		//	id is the identity of the MFD buttons, not the cockpit view. 
		SetCameraOffset (_V(0.55,2.04,27.93));
		oapiVCSetNeighbours (1, -1, -1, 2);

		oapiVCRegisterMFD (MFD_LEFT, &mfds_left);
		oapiVCRegisterHUD (&hud_pilot); // HUD parameters
		//		oapiVCRegisterSYSTAT (&systat_pilot); // Sytem Display parameters


		oapiVCRegisterArea (AID_MFD1_LBUTTONS, _R( 0, 0, 32, 220), PANEL_REDRAW_USER, PANEL_MOUSE_IGNORE, PANEL_MAP_BACKGROUND, MFDbuttons1);		

		oapiVCRegisterArea (AID_MFD1_RBUTTONS, _R( 32, 0, 64, 220), PANEL_REDRAW_USER, PANEL_MOUSE_IGNORE, PANEL_MAP_BACKGROUND, MFDbuttons1);

		//	These area registrations are for redrawing purposes only, and are used by 
		//	the clbkVCRedrawEvent. The _R(X1, Y1, X2, Y2) coordinates pertain to the
		//	texture coordinates on the MFDButtons.dds texture (in PIXELS), which is the 
		//	dynamic texture that SURFHANDLE MFDbuttons1 gets from the mesh file.
		//	Note that as the bottom buttons are not ever redrawn, no registration of
		//	them for texture coords is needed.
		//	Have a look at the clbkMFDMode and clbkVCRedrawEvent. 
		//	The only areas triggered for redrawing and then redrawn are
		//	those two registered above (AID_MFD1_LBUTTONS and AID_MFD1_RBUTTONS.
		//	And have a look at the definitions in the AstroMatiz.h file. You will see their
		//	id's that are passed to the preprocessor during compile.

		//	For reference, here are the rectangular area 3D coordinates of the 
		//	left, right and bottom MFD buttons, in MESH coordinates. You WILL need to
		//	get this info, if you want to make your own VC work properly. 
		//	(Use Meshwizard, or go into the mesh file itself and find them).
		//				The mesh area of the 6 left buttons of MFD_LEFT
		//					_V(-0.32f, 0.067f, 0.654f),
		//					_V(-0.30f, 0.067f, 0.654f),
		//					_V(-0.32f, -0.080f, 0.643f),
		//					_V(-0.30f, -0.080f, 0.643f));
		//				The mesh area of the 6 right buttons of MFD_LEFT
		//					_V(-0.117f, 0.067f, 0.654f),
		//					_V(-0.097f, 0.067f, 0.654f),
		//					_V(-0.117f, -0.080f, 0.643f),
		//					_V(-0.097f, -0.080f, 0.643f));
		//				The mesh area of the three bottom buttons of MFD_LEFT
		//					_V(-0.267f, -0.095f, 0.642f),
		//					_V(-0.158f, -0.095f, 0.642f),
		//					_V(-0.267f, -0.114f, 0.641f),
		//					_V(-0.158f, -0.114f, 0.641f));

		for (i = 0; i < 6; i++) {
			oapiVCRegisterArea (MFD1_LBUTTON1+i, PANEL_REDRAW_NEVER, PANEL_MOUSE_LBDOWN);
			oapiVCSetAreaClickmode_Spherical(MFD1_LBUTTON1+i, _V(-0.31, 0.0547 - (i * 0.0245), 0.649), 0.0123);

			oapiVCRegisterArea (MFD1_RBUTTON1+i, PANEL_REDRAW_NEVER, PANEL_MOUSE_LBDOWN);
			oapiVCSetAreaClickmode_Spherical(MFD1_RBUTTON1+i, _V(-0.107, 0.0547-(i * 0.0245), 0.649), 0.0123);
		}
		//	Define the area for mouse events on each button of left and right columns.

		for (i = 0; i < 3; i++) {
			oapiVCRegisterArea (MFD1_BBUTTON1+i, PANEL_REDRAW_NEVER, PANEL_MOUSE_LBDOWN);
			oapiVCSetAreaClickmode_Spherical(MFD1_BBUTTON1+i, _V(-0.254 + (i * 0.042), -0.100, 0.641), 0.0123);
		}
		//	Define the area for mouse events on the bottom buttons. Coordinates are in
		//	mesh 3D coords (see why you needed to get this info?)

		campos = CAM_VCPILOT;
		break;



	case 1: // front left passenger
		SetCameraOffset (_V(-0.55,2.04,27.93));
		SetCameraMovement (_V(0.2,-0.05,0.3), -10*RAD, 10*RAD, _V(-0.3,0,0), 80*RAD, 0, _V(0.4,0,0), -90*RAD, 0);
		oapiVCSetNeighbours (-1, 0, -1, 2);
		oapiVCRegisterHUD (&hud_copilot); // HUD parameters
		//		oapiVCRegisterSYSTAT (&systat_copilot); // Sytem Display parameters
		campos = CAM_VCPSNGR1;
		break;

	case 2: // front right passenger
		SetCameraOffset (_V(0,-0.2,27.93));
		SetCameraMovement (_V(-0.2,-0.05,0.3), 10*RAD, 10*RAD, _V(-0.4,0,0), 90*RAD, 0, _V(0.3,0,0), -80*RAD, 0);
		oapiVCSetNeighbours (1, 0, -1, 3);
		oapiVCRegisterHUD (&hud_fdeckfloor); // HUD parameters
		//		oapiVCRegisterSYSTAT (&systat_fdeckfloor); // Sytem Display parameters
		campos = CAM_VCPSNGR2;
		break;

	case 3: // rear left passenger
		SetCameraOffset (_V(0.5, 0, 4.4));
		SetCameraMovement (_V(0.4,0,0), 0, 0, _V(-0.3,0,0), 70*RAD, 0, _V(0.4,0,0), -90*RAD, 0);
		oapiVCSetNeighbours (-1, -1, 2, 4);
		campos = CAM_VCPSNGR3;
		break;

	case 4: // rear right passenger
		SetCameraOffset (_V(0, 0.2, -18));
		SetCameraMovement (_V(-0.4,0,0), 0, 0, _V(-0.4,0,0), 90*RAD, 0, _V(0.3,0,0), -70*RAD, 0);
		oapiVCSetNeighbours (-1, -1, 3, -1);
		campos = CAM_VCPSNGR4;
		return true;

	};

	return true;
}
bool ShuttleD::clbkDrawHUD(int mode, const HUDPAINTSPEC *hps, oapi::Sketchpad *skp)
{
	// draw the default HUD
	VESSEL3::clbkDrawHUD (mode, hps, skp);

	// UMmu display messages
	if(dHudMessageDelay>0)
	{
		skp->Text(5,hps->H/60*25,cUmmuHudDisplay,strlen(cUmmuHudDisplay));
		dHudMessageDelay-=oapiGetSimStep();
		if(dHudMessageDelay<0)
			dHudMessageDelay=0;
	}
	// UCGO display messages
	if(dCargHudMessageDelay>0)
	{
		skp->Text(5,hps->H/60*15,cCargoHudDisplay,strlen(cCargoHudDisplay));
		dCargHudMessageDelay-=oapiGetSimStep();
		if(dCargHudMessageDelay<0)
			dCargHudMessageDelay=0;
	}
	return true; 
}
char *ShuttleD::SendHudMessage() //<---- Change the class name here
{
	dHudMessageDelay=15;
	return cUmmuHudDisplay;
}
//-------------------------------------------------------------------------

bool ShuttleD::clbkVCMouseEvent (int id, int event, VECTOR3 &p)
{
	if ((id) >= MFD1_LBUTTON1 && (id) < MFD1_LBUTTON1 + 12)
	{
		oapiProcessMFDButton (MFD_LEFT, id - MFD1_LBUTTON1, event);
		return true;
	}
	//	Processes the events for mouse clicks on VC view MFD buttons, left and
	//	right columns, by matching the id of the event to the #defined identifier of
	//	the button (see AstroMatiz.h file).

	if ((id) == MFD1_BBUTTON1)
	{
		oapiToggleMFD_on (MFD_LEFT);
		return true;
	}
	//	The ON / OFF button (left MFD button on the bottom).

	if ((id) == MFD1_BBUTTON2)
	{
		oapiSendMFDKey (MFD_LEFT, OAPI_KEY_F1);
		return true;
	}
	//	The MODE button (center MFD button on the bottom).

	if ((id) == MFD1_BBUTTON3)
	{
		oapiSendMFDKey (MFD_LEFT, OAPI_KEY_GRAVE);
		return true;
	}
	//	The MENU button (right MFD button on the bottom).

	return false;
}
//-------------------------------------------------------------------------


bool ShuttleD::clbkVCRedrawEvent (int id, int event, SURFHANDLE surf)
{
	int bt, side;
	switch (id) {
		const char *label;
	case AID_MFD1_LBUTTONS:
	case AID_MFD1_RBUTTONS:
		side = (id == AID_MFD1_LBUTTONS ? 0: 1);
		HDC hDC = oapiGetDC (surf);
		SelectObject (hDC, hFont);
		SetTextColor (hDC, RGB(250, 250, 100));
		SetTextAlign (hDC, TA_CENTER);
		SetBkMode (hDC, TRANSPARENT);
		for (bt = 0; bt < 6; bt++) {
			if (label = oapiMFDButtonLabel (MFD_LEFT, bt+side*6))
				TextOut (hDC, 16, 8+36*bt, label, strlen(label));
			else break;
		}
		oapiReleaseDC (surf, hDC);
		return true;
	}
	return false;

	//	This redraw business is the most complicated thing about making VC's. This is 
	//	where the registered areas AID_MFD1_LBUTTONS and AID_MFD1_RBUTTONS are used.
	//	This part has absolutely nothing to do with triggering mouse events. It is
	//	ONLY blitting your MFDButtons texture on the buttons again, but with text on it.
	//	Important points with this issue are;
	//	1.	Get your mesh UV coordinates right for the mesh. DON'T get is backwards.
	//	2.	Make sure your texture is dynamic (just D suffix it in the mesh file).
	//	3.	Get the texture in the program (in clbkLoadVC) as a SURFHANDLE.

};

//=========================================================
// Vessel Load and Save Parameters
//=========================================================
void ShuttleD::clbkLoadStateEx (FILEHANDLE scn, void *status)
{
	char *line;
	while (oapiReadScenario_nextline (scn, line)) 
	{

		if (!_strnicmp (line, "GEAR", 4)) {
			sscanf (line+4, "%d%lf", &GEAR_status, &GEAR_proc);
		}

		if (!_strnicmp (line, "PLBAYA", 5)) {
			sscanf (line+5, "%d%lf", &PLBAYA_status, &PLBAYA_proc);
		}

		if (!_strnicmp (line, "PLBAYB", 6)) {
			sscanf (line+6, "%d%lf", &PLBAYB_status, &PLBAYB_proc);
		}

				if (!strnicmp (line, "O2Tank", 7))
		{
			sscanf (line+7, "%lf", &O2Tank);
		}

		if(Crew.LoadAllMembersFromOrbiterScenario(line)==TRUE)
			continue;

		// Load UCGO 2.0 cargo from scenario
		if(hUcgo.LoadCargoFromScenario(line)==TRUE) // UCGO load cargo 
			continue;

		ParseScenarioLineEx (line, status);
	}



	SetAnimation (anim_gear, GEAR_proc);

}


void ShuttleD::clbkSaveState (FILEHANDLE scn)
{
	char cbuf[256];

	VESSEL3::clbkSaveState (scn);
	sprintf (cbuf, "%d %0.4f", GEAR_status, GEAR_proc);
	oapiWriteScenario_string (scn, "GEAR", cbuf);

	sprintf (cbuf, "%d %0.4f", PLBAYA_status, PLBAYA_proc);
	oapiWriteScenario_string (scn, "PLBAYA", cbuf);


	sprintf (cbuf, "%d %0.4f", PLBAYB_status, PLBAYB_proc);
	oapiWriteScenario_string (scn, "PLBAYB", cbuf);

	Crew.SaveAllMembersInOrbiterScenarios(scn);

	// Save UCGO 2.0 cargo in scenario
	hUcgo.SaveCargoToScenario(scn);	
}

// --------------------------------------------------------------
// clbkVisualCreated
// --------------------------------------------------------------
void ShuttleD::clbkVisualCreated (VISHANDLE vis, int refcount)
{
	hUcgo.SetUcgoVisual(vis);	// must be called in clbkVisualCreated.
}
////////////////////////////////////////////////////////
// SendCargHudMessage
////////////////////////////////////////////////////////
char *ShuttleD::SendCargHudMessage(void)
{
	dCargHudMessageDelay=15; // 15 seconds display delay for msg
	return cCargoHudDisplay;
}
//=========================================================
// Animation Template
//=========================================================
void ShuttleD::clbkPostStep(double simt, double simdt, double mjd)
{
	double OxygenLevel = GetPropellantMass(O2Tank);

	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 (GEAR_status >= GEAR_RAISING) { 
		double da = simdt * GEAR_OPERATING_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);

	}
	SetTouchdownPoints (_V(0,-4.89+GEAR_proc*0.99,1), _V(-1,-4.89+GEAR_proc*0.99,-1), _V(1,-4.89+GEAR_proc*0.99,-1));
	//sprintf(oapiDebugString(),"anim %2.2f", GEAR_proc );

	if (PLBAYA_status >= PLBAYA_CLOSING) {
		double da = simdt * PLBAYA_OPERATING_SPEED;
		if (PLBAYA_status == PLBAYA_CLOSING) {
			if (PLBAYA_proc > 0.0) PLBAYA_proc = max (0.0, PLBAYA_proc-da);
			else                PLBAYA_status = PLBAYA_UP;
		} else {
			if (PLBAYA_proc < 1.0) PLBAYA_proc = min (1.0, PLBAYA_proc+da);
			else                PLBAYA_status = PLBAYA_DOWN;
		}
		SetAnimation (anim_PLBAYA, PLBAYA_proc);



	}

	if (PLBAYB_status >= PLBAYB_CLOSING) {
		double da = simdt * PLBAYB_OPERATING_SPEED;
		if (PLBAYB_status == PLBAYB_CLOSING) {
			if (PLBAYB_proc > 0.0) PLBAYB_proc = max (0.0, PLBAYB_proc-da);
			else                PLBAYB_status = PLBAYB_UP;
		} else {
			if (PLBAYB_proc < 1.0) PLBAYB_proc = min (1.0, PLBAYB_proc+da);
			else                PLBAYB_status = PLBAYB_DOWN;
		}
		SetAnimation (anim_PLBAYB, PLBAYB_proc);



	}

	if (GEAR_status == GEAR_RAISING) {
		{if(GEAR_proc > 0.99)
			PlayVesselWave3(SHD,GEARDOWN);
		}
	}

	if (GEAR_status == GEAR_LOWERING) {
		{if(GEAR_proc < 0.01)
			PlayVesselWave3(SHD,GEARUP);
		}
	}
	if (PLBAYA_status == PLBAYA_CLOSING) {
		{if(PLBAYA_proc > 0.99)
			PlayVesselWave3(SHD,PLBAYACLOSE);
		}
	}

	if (PLBAYA_status == PLBAYA_OPENING) {
		{if(PLBAYA_proc < 0.01)
			PlayVesselWave3(SHD,PLBAYAOPEN);
		}
	}
	if (PLBAYB_status == PLBAYB_CLOSING) {
		{if(PLBAYB_proc > 0.9)
			PlayVesselWave3(SHD,PLBAYBCLOSE);
		}
	}

	if (PLBAYB_status == PLBAYB_OPENING) {
		{if(PLBAYB_proc < 0.1)
			PlayVesselWave3(SHD,PLBAYBOPEN);
		}
	}

	int ActionAreaReturnCode=Crew.DetectActionAreaActivated();
	if(ActionAreaReturnCode>-1)
	{
		// this is just an example, we have four area declared
		// action area ID 0 triggered 
		if(ActionAreaReturnCode==0)
		{
			// do something cool here
		}
		// action area ID 1 triggered
		else if(ActionAreaReturnCode==1)
		{
			// As you can edit action area in real time: relocation, sound, text, state
			// the possiblity are endless. you can for example order a UMMU to go to an external panel
			// and open it, then go to engine exhaust and "repair" it then back to panel to close it etc.
			if(iActionAreaDemoStep==0)
			{
				Crew.SetActionAreaText(1,"You are still on the right but this is 2nd keypress");
				iActionAreaDemoStep++;
				// do something cool here
			}
			else if(iActionAreaDemoStep==1)
			{
				// remember you change action area's parameter for the NEXT keypress of UMMU
				Crew.SetActionAreaText(1,"You are still on the right but this is 3rd keypress");
				iActionAreaDemoStep++;
				// do something cool here
			}
			else
			{
				Crew.SetActionAreaText(1,"You are still on the right but this is last keypress");
				// and of course you can change sound, location, state, text, here some examples:
				//Crew.SetActionAreaPos(1,_V(5,0,5)); // change location
				//Crew.SetActionAreaWav(1,"MyAddon\PlayRepaired.wav"); // change sound
				//Crew.SetActionAreaWav(1,"MyAddon\DoorClosed.wav"); // change sound
				//Crew.SetActionAreaText(1,NULL); // sound or text are not mandatory, here we set no text.
				// etc. etc.
			}
		}
		// action area ID 2 triggered
		else if(ActionAreaReturnCode==2)
		{
			// do something cool here
		}
		// action area ID 3 triggered
		else if(ActionAreaReturnCode==3)
		{
			// do something cool here
		}
	}

	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");
		}
	}


		if(OxygenLevel <= 1)
	{
			int I;
			// 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 ! Oxygen Depleted - All crew dead");
	}


	if (PLBAYA_status == PLBAYA_UP) {
		hUcgo.SetSlotDoorState(FALSE);
	}

	if (PLBAYA_status == PLBAYA_DOWN) {
		hUcgo.SetSlotDoorState(TRUE);
	}

	if (PLBAYB_status == PLBAYB_OPENING) {
		{if(PLBAYB_proc < 0.1)
			PlayVesselWave3(SHD,PLBAYBOPEN);
		}
	}

	double CrewNumber = Crew.GetCrewTotalNumber();
	double OxygenConsumption = 1.2*CrewNumber;
//	double OxygenLevel = GetPropellantMass(O2Tank);

	{
	if (OxygenLevel>0)
	{
	OxygenLevel = (OxygenLevel - (OxygenConsumption*(simdt)));
	}
}
	// Orientation mode switch


	// RCS Stack Optimization...
//	double RCSMode = GetAttitudeMode (); // Checks current RCS mode 1 = ROT, 2 = LIN
//	if (RCSMode==2)
//	{ 

		//Cancel Linear Torque.
//		IncThrusterLevel_SingleStep (th_rcs[2],456.6447615); 	
//		IncThrusterLevel_SingleStep (th_rcs[3],456.6447615);
//		IncThrusterLevel (th_rcs[2],-456.6447615);
//		IncThrusterLevel (th_rcs[3],-456.6447615);
//		IncThrusterLevel_SingleStep (th_rcs[12], VertThrust*Ratio*0.965925826289); 
//		IncThrusterLevel_SingleStep (th_rcs[13],-VertThrust*Ratio*0.965925826289); 



		//	if (GetAttitudeMode = RCS_LIN)
		//	{

		// RCS Stack Optimization...
//		double RCSMode = GetAttitudeMode (); // Checks current RCS mode 1 = ROT, 2 = LIN
//		if (RCSMode==2)
//		{ 
//			(RCSTH0 = EXP_LINRCSTH0);
//			(RCSTH1 = EXP_LINRCSTH1);
//			(RCSTH2 = EXP_LINRCSTH2);
//			(RCSTH3 = EXP_LINRCSTH3);
//			(RCSTH4 = EXP_LINRCSTH4);
//			(RCSTH5 = EXP_LINRCSTH5);
//			(RCSTH6 = EXP_LINRCSTH6);
//			(RCSTH7 = EXP_LINRCSTH7);
//			(RCSTH8 = EXP_LINRCSTH8);
//			(RCSTH9 = EXP_LINRCSTH9);
//			(RCSTH10 = EXP_LINRCSTH10);
//			(RCSTH11 = EXP_LINRCSTH11);
//			(RCSTH12 = EXP_LINRCSTH12);
//			(RCSTH13 = EXP_LINRCSTH13);
//			(RCSTH14 = EXP_LINRCSTH14);
//			(RCSTH15 = EXP_LINRCSTH15);
//		}

//		if (RCSMode==1)
//		{ 
//			(RCSTH0 = EXP_RCSTH0);
//			(RCSTH1 = EXP_RCSTH1);
//			(RCSTH2 = EXP_RCSTH2);
//			(RCSTH3 = EXP_RCSTH3);
//			(RCSTH4 = EXP_RCSTH4);
//			(RCSTH5 = EXP_RCSTH5);
//			(RCSTH6 = EXP_RCSTH6);
//			(RCSTH7 = EXP_RCSTH7);
//			(RCSTH8 = EXP_RCSTH8);
//			(RCSTH9 = EXP_RCSTH9);
//			(RCSTH10 = EXP_RCSTH10);
//			(RCSTH11 = EXP_RCSTH11);
//			(RCSTH12 = EXP_RCSTH12);
//			(RCSTH13 = EXP_RCSTH13);
//			(RCSTH14 = EXP_RCSTH14);
//			(RCSTH15 = EXP_RCSTH15);
//		}

		AddUMmuToVessel();

		Crew.WarnUserUMMUNotInstalled("Shuttle-D");
		// At the very end of clbkPostStep or clbkPreStep
		hUcgo.WarnUserUCGONotInstalled("Shuttle-D");
	}
// --------------------------------------------------------------
// Respond to buffered keyboard events
// --------------------------------------------------------------
int ShuttleD::clbkConsumeBufferedKey (DWORD key, bool down, char *kstate)
{
	if (!down) return 0;       // only process keydown events

	if(key==OAPI_KEY_G)
	{ //Gear
		RevertGEAR();
		return 1;
	}   
	if(key==OAPI_KEY_K)
	{// Payload Bay B
		RevertPLBAYB();
		return 1;
	}  
	if(key==OAPI_KEY_O)
	{// Payload Bay A
		RevertPLBAYA();
		return 1;
	} 

	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_A&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		// switch state
		Crew.SetAirlockDoorState(!Crew.GetAirlockDoorState());
		// display state
		if(Crew.GetAirlockDoorState()==TRUE)
			strcpy(SendHudMessage(),"Airlock is now open");	
		else
			strcpy(SendHudMessage(),"Airlock is now closed");	
		return 1;
	}
	//---------------------------------------------------------------------------
	// 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(),4-Crew.GetCrewTotalNumber());
		return 1;
	}

		if(key==OAPI_KEY_Y)
	{
		sprintf(SendHudMessage(),"%i souls onboard, %i kg O2 remaining",
			Crew.GetCrewTotalNumber(),&ShuttleD::CheckOxygen);
		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;
	}
	// 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);
	}
	// 9 key "select" one cargo on disk (cycle)
	if(key==OAPI_KEY_9&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		sprintf(SendCargHudMessage(),"%s - selected",hUcgo.ScnEditor_SelectNextCargoAvailableOnDisk());
		return 1;
	}
	// SHIFT+9 key "add last cargo selected by key 9"
	// If iSelectedCargo=-1 (default) add to the first free slot found
	if(key==OAPI_KEY_9&&KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		if(hUcgo.ScnEditor_AddLastSelectedCargoToSlot(iSelectedCargo)==TRUE)
		{
			strcpy(SendCargHudMessage(),"Cargo added to ship");
		}
		else
		{
			if(iSelectedCargo<0)
			{
				strcpy(SendCargHudMessage(),"Cargo not added (ship full or weight excess ?)");
			}
			else
			{
				strcpy(SendCargHudMessage(),"Cargo not added (slot full ship full or weight excess ?)");
			}
		}
		return 1;
	}
	// "C" grapple cargo. If iSelectedCargo=-1 (default) add to the first free slot found
	if(key==OAPI_KEY_C&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		int ReturnedCode=hUcgo.GrappleOneCargo(iSelectedCargo);
		// for return code list see function "GrappleOneCargo" in the header
		switch(ReturnedCode)
		{
		case 1:
			strcpy(SendCargHudMessage(),"Cargo grappled");
			break;
		case 0:
			strcpy(SendCargHudMessage(),"No cargo in range");
			break;
		case -1:
			strcpy(SendCargHudMessage(),"cargo exceed maximum mass");
			break;
		case -2:
			strcpy(SendCargHudMessage(),"bad config, mesh not found or slot not declared");
			break;
		case -3:
			strcpy(SendCargHudMessage(),"Can't grapple cargo, slot not empty");
			break;
		case -4:
			strcpy(SendCargHudMessage(),"Can't grapple cargo,doors closed ");
			break;
		case -5:
			strcpy(SendCargHudMessage(),"Can't grapple cargo,Ship full");
			break;
		default:
			strcpy(SendCargHudMessage(),"Misc error Unable to grapple cargo");
		}
		return 1;
	}
	// SHIFT+C release cargo. If iSelectedCargo=-1 (default) release the first free slot found
	if(key==OAPI_KEY_C&&KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		if(hUcgo.ReleaseOneCargo(iSelectedCargo)!=FALSE)
		{
			strcpy(SendCargHudMessage(),"Cargo released");
		}
		else
		{
			strcpy(SendCargHudMessage(),"Cargo not released (empty slot, no cargo aboard?)");
		}
		return 1;
	}
	// "8" show some info on cargo
	if(key==OAPI_KEY_8&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
	{
		sprintf(SendCargHudMessage(),"%i cargos aboard. "
			"Total cargos weight: %.0fkg",hUcgo.GetNbrCargoLoaded(),hUcgo.GetCargoTotalMass());
		return 1;
	}
	return 0;
}
//-------------------------------------------------------------------------
// THIS IS FOR ADDING CREW SEE PDF doc "Allow user to add crew to your ship 
// without scenery editor"
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 ShuttleD::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?)");
		}
	}
}

//	hFont = CreateFont (-20, 3, 0, 0, 150, 0, 0, 0, 0, 0, 0, 0, 0, "Arial");

//=========================================================
// Load and Delete Module Stuff
//=========================================================
DLLCLBK void InitModule (HINSTANCE hModule)
{
	g_hDLL = hModule;
	hFont = CreateFont (-20, 3, 0, 0, 150, 0, 0, 0, 0, 0, 0, 0, 0, "Haettenschweiler");
	hPen = CreatePen (PS_SOLID, 3, RGB (120,220,120));
	hBrush = CreateSolidBrush (RGB(0,128,0));

	// perform global module initialisation here
}
DLLCLBK void ExitModule (HINSTANCE hModule)
{
	// perform module cleanup here
	DeleteObject (hFont);
	DeleteObject (hPen);
	DeleteObject (hBrush);

}

//=========================================================
// Load and Delete Vessel Stuff
//=========================================================
DLLCLBK VESSEL *ovcInit (OBJHANDLE hvessel, int flightmodel)
{
	return new ShuttleD (hvessel, flightmodel);
}
DLLCLBK void ovcExit (VESSEL *vessel)
{
	if (vessel)
		delete (ShuttleD*)vessel;
}

void ShuttleD::clbkPostCreation (void)
{

	////////////////////////////////////////////////////////////////
	// 3-ORBITERSOUND EXAMPLE - INIT AND LOADING OF WAV
	// THIS MUST BE CALLED ABSOLUTELY IN THE "POSTCREATION CALLBACK" 
	////////////////////////////////////////////////////////////////
	// here we connect to OrbiterSound and store the returned ID in your class
	// this is the first thing to do. You must call this in "clbkPostCreation" 
	// (new version of ovcPostCreation wich is now obsolet)
	SHD=ConnectToOrbiterSoundDLL3(GetHandle());

	RequestLoadVesselWave3(SHD,GEARUP,"Sound\\ShuttleD\\gearup.wav",INTERNAL_ONLY);
	RequestLoadVesselWave3(SHD,GEARDOWN,"Sound\\ShuttleD\\geardown.wav",INTERNAL_ONLY);
	RequestLoadVesselWave3(SHD,PLBAYAOPEN,"Sound\\ShuttleD\\plbayaopen.wav",INTERNAL_ONLY);
	RequestLoadVesselWave3(SHD,PLBAYACLOSE,"Sound\\ShuttleD\\plbayaclose.wav",INTERNAL_ONLY);
	RequestLoadVesselWave3(SHD,PLBAYBOPEN,"Sound\\ShuttleD\\plbaybopen.wav",INTERNAL_ONLY);
	RequestLoadVesselWave3(SHD,PLBAYBCLOSE,"Sound\\ShuttleD\\plbaybclose.wav",INTERNAL_ONLY);

	// now we are allowed for example to replace variable sound of OrbiterSound.
	// it will use them instead of the stock one when our vessel have the focus, see header file for parameter 
	// (you can replace more than the four below see parameters for "ReplaceStockSound3()")
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\mainext.wav",	REPLACE_MAIN_THRUST);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\attfire.wav",		REPLACE_RCS_THRUST_ATTACK);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\attsustain.wav",	REPLACE_RCS_THRUST_SUSTAIN);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_AIR_CONDITIONNING);

	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_1);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_2);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_3);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_4);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_5);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_6);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_7);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_8);
	ReplaceStockSound3(SHD,"Sound\\ShuttleD\\aircond.wav",		REPLACE_COCKPIT_AMBIENCE_9);

	// Now your ship have custom sound you can end here if you don't want to bother anymore
	// with sound, your ship is already customised. Anyway I'll show you more below.

	//...

	// here we will load a sound that will play when we pass 500 meter altitude
	// We will play it later ourself when we reach 500 meter (see opcPreStep below)
	RequestLoadVesselWave3(SHD,MY500METERSOUND,"Sound\\OrbiterSound_SDK\\sound\\my500meter.wav",RADIO_SOUND);
	// AS we will play it using the "radioexclusive function" and as we don't want to be disturbed
	// by OrbiterSound's ATC we set the ATC option of OrbiterSound to false (no more ATC will play)
	// it's not an obligation while the "playradio..." function stop any stock ATC sound that is already playing
	// but if you want to use radio sound you may not want to be disturbed by "off-topic" atc.
	SoundOptionOnOff3(SHD,PLAYRADIOATC,FALSE);

	// we disable also the stock countdown when we take-off.. it's annoying :)
	SoundOptionOnOff3(SHD,PLAYCOUNTDOWNWHENTAKEOFF,FALSE);

	// Here we will load a radar sound that will play in cockpit view only and after take-off below 500 meter
	// this sound will be looped and will see it's frequency varying as we approach ground
	RequestLoadVesselWave3(SHD,MYRADARSOUND,"Sound\\OrbiterSound_SDK\\sound\\myradar.wav",INTERNAL_ONLY);

	// Here is THE example of an external sound that must be also faded by distance and pressure 
	// as stated in the header of ObiterSoundSDK3.h it's IMPORTANT that you call this sound looped
	// and at each frame otherwise it will not be faded properly.
	// "EXTERNAL_ONLY_FADED_CLOSE" mean that it will be faded at close distance as the RCS sound in external view.
	RequestLoadVesselWave3(SHD,MYEXTERNAL_UFO_FADEDSOUND,"Sound\\OrbiterSound_SDK\\sound\\myUfoSound.wav",EXTERNAL_ONLY_FADED_CLOSE);
}

// --------------------------------------------------------------
// loop called each frame
// --------------------------------------------------------------
void ShuttleD::clbkPreStep (double simt, double simdt, double mjd) 
{
	double Altitude=0;
	static BOOL AltitudeSoundPlayed=FALSE;
	static BOOL RadarFlagForSound=FALSE;

	///////////////////////////////////////////////////////////////////
	// 4-ORBITERSOUND EXAMPLE - PLAYING THE SOUND WE LOADED DYNAMICALLY
	///////////////////////////////////////////////////////////////////

	// ----------------------------------------------------------
	// We play the "500 meter" sound we loaded previously 
	// we use the function "playradio" wich stop OrbiterSound atc
	// sound if one is playing.
	// ----------------------------------------------------------
	oapiGetFocusAltitude(&Altitude);
	if(Altitude>500&&Altitude<550&&AltitudeSoundPlayed==FALSE)
	{
		PlayVesselRadioExclusiveWave3(SHD,MY500METERSOUND,255);	// 255=volume maximum 
		AltitudeSoundPlayed=TRUE;
	}
	if(Altitude<500||Altitude>550)
		AltitudeSoundPlayed=FALSE;


	// ------------------------------------------------------
	// here we play our radar sound that slide with altitude
	// ------------------------------------------------------
	if(Altitude<500&&Altitude>2)
	{
		PlayVesselWave3(SHD,MYRADARSOUND,LOOP,255,(int)(11025+((500-Altitude)*40)));		// play looped and a frequency that increase with altitude
		RadarFlagForSound=TRUE;
	}
	else if(RadarFlagForSound=TRUE)
	{
		// we stop the sound only once (it would not harm else but it will save a bit of processor time)
		StopVesselWave3(SHD,MYRADARSOUND);
	}


	// ------------------------------------------------------
	// here we play our external "ufo" sound
	// ------------------------------------------------------
	// As stated a sound that play in external view must be called looped heach frame so it
	// fade properly with distance and pressure. Also we add a litle effect: if the ship is landed
	// the frequency is lower simulating an "iddle" engine.
	if(Altitude<2)
		PlayVesselWave3(SHD,MYEXTERNAL_UFO_FADEDSOUND,LOOP,255,11025); // original wav is 11025 hz
	else
		PlayVesselWave3(SHD,MYEXTERNAL_UFO_FADEDSOUND,LOOP,255,9000);  


	// -------------------------------------------------------------
	// another example with a different behaviour of the "Ufo" sound
	// -------------------------------------------------------------
	// here an example of the ufo sound that will play only beetween 0 and 500 meter high.
	// I show it here so you know how to stop/start an external sound.
	//if(Altitude<500)
	//	PlayVesselWave3(MyID,MYEXTERNAL_UFO_FADEDSOUND,LOOP,255); // original wav is 11025 hz
	//else
	//	StopVesselWave3(MyID,MYEXTERNAL_UFO_FADEDSOUND);		   // you can use a flag to avoid reapeated call (even the impact is very low, the function return immediately if the sound is already stoped)
}

ShuttleD::~ShuttleD() 
{
     delete th_rcs;
}
 
Not really expecting any help at this point, but I figured I might as well post some shots of what Ive got so far. If anyone is interested in beta testing, Id be happy for the help. :thumbup:





















 
Bruce, I am not a developer by any means, but I would like to help with this project. Is there any capacity in which you could use me?

Simple beta testing if you have the time would be great :thumbup:.
I can start putting together a package of what Ive got so far this evening, and maybe you can put it through

-a reentry from LEO scenario (dont think youll be able to land safely, but I could use a check of the aerodynamics & general control feel)
-a Lunar deorbit and landing
-take any space station you like & do a couple of dock to dock hops, so as to iron out the bugs in the RCS control systems

and of course, anything else you happen to feel like. There are some UMMU action areas around the ship that you might like to try. Are you feeling up to some beta testing?
 
Simple beta testing if you have the time would be great :thumbup:.
I can start putting together a package of what Ive got so far this evening, and maybe you can put it through

-a reentry from LEO scenario (dont think youll be able to land safely, but I could use a check of the aerodynamics & general control feel)
-a Lunar deorbit and landing
-take any space station you like & do a couple of dock to dock hops, so as to iron out the bugs in the RCS control systems

and of course, anything else you happen to feel like. There are some UMMU action areas around the ship that you might like to try. Are you feeling up to some beta testing?

I'm up for beta testing if you like. Always looking for more UMMU ships, and action areas don't seem to be a common thing. Re-entry I'm rubbish at, but I'm capable of a lunar de-orbit and landing, and dock to dock hops. I'll also test out the UMMU action areas and anything else UMMU/UCGO related - let me know :)

Kind Regards,
NT
 
Simple beta testing if you have the time would be great :thumbup:.
I can start putting together a package of what Ive got so far this evening, and maybe you can put it through

-a reentry from LEO scenario (dont think youll be able to land safely, but I could use a check of the aerodynamics & general control feel)
-a Lunar deorbit and landing
-take any space station you like & do a couple of dock to dock hops, so as to iron out the bugs in the RCS control systems

and of course, anything else you happen to feel like. There are some UMMU action areas around the ship that you might like to try. Are you feeling up to some beta testing?
Awesome! Bring it!
 
Note: Beta testing is not simple! ;)

lol, this one shouldnt be that bad. I really just need some more eyes to pick out minir bugs, and to tell me what works from a piloting perspective

If youll excuse me a minute I just need to split the package into 2 zip files. My original one is too big to upload

---------- Post added at 01:56 AM ---------- Previous post was at 01:40 AM ----------

Attachment not working, Im going to try to upload it on OHM
 
Just wondering if I could start getting some tester feedback on the package I uploaded? I finished the MFD inputs last night, and am working on the velcro rockets currently, but Im counting down to about a week to must-release, no extensions! If anybody has a booster design that I could give a try, now is the time to speak up. :)
 
Okay, Bruce, I took the Shuttle-D out for a test drive to Brighton Beach this afternoon:) Here are my conclusions:
:)She doesn't seem designed to land or take off at Earth.
:)Best use in her current state would be short-haul tug and rescue operations. Are you going to add cargo capacity?
:)She handles very well. Balance and control responsiveness are great:thumbup:
:)Overall she's a lovely ship. I might order a couple of these after I make the IPO for Real Time Incorporated in February.

---------- Post added at 02:26 PM ---------- Previous post was at 02:20 PM ----------

:ninja:'d by BJJL
Just saw the pics of the ship with cargo loaded. What key do I hit to load cargos?
 
Okay, Bruce, I took the Shuttle-D out for a test drive to Brighton Beach this afternoon:) Here are my conclusions:
:)She doesn't seem designed to land or take off at Earth.
:)Best use in her current state would be short-haul tug and rescue operations. Are you going to add cargo capacity?
:)She handles very well. Balance and control responsiveness are great:thumbup:
:)Overall she's a lovely ship. I might order a couple of these after I make the IPO for Real Time Incorporated in February.

---------- Post added at 02:26 PM ---------- Previous post was at 02:20 PM ----------

:ninja:'d by BJJL
Just saw the pics of the ship with cargo loaded. What key do I hit to load cargos?

Hmm,

Well as far as earth goes, I dont think itll be able to land safely, but the launch stack is still under development, due to velcro rockets being not quite as flexible as I had hoped, but thats development... :rolleyes:

Yes, there is UCGO support, but the overall package is still lacking somewhat in polish. 9, and Shift-9 should allow for cylcling through cargoes, and selecting those to add, while keys 3 and 4 should allow you to select cargo slots. With regards to that, I was wondering if someone could check for me and see what happens when slot -1 is selected (press 3 the first instant the Scenario is loaded). I would imagine youll get a CTD when selecting a nonexistent cargo slot, but Im wrapped up in other matters at the moment.

Glad to hear that handling is good :thumbup:, can anyone elaborate on atmospheric vs vacuum, and on the RCS translational alignment? (not sure if the translation thrusters are still creating tiny amounts of yaw)

I am very happy to say that I now have working MFDs in the VC, although I had to take a somewhat unique route for this, given my inexperience with redraw events and modelling.

BTW Tmac, care to elaborate on that IPO thing? Im flattered to receive my first spacecraft order :)

Oh, and I suppose I should mention that the gear is, as usual key G, the main payload bay, for UCGO located under the hab tunnel, is key O, and the auxiliary payload bay on top of the aft Service module for the utility UCGO cargo ferries that I will be including (not sure if I should also do them as an individual release as well?), is opened with key K. Sorry, If anyone was frustrated not knowing how to open the doors lol
 
Last edited:
BTW Tmac, care to elaborate on that IPO thing? Im flattered to receive my first spacecraft order
IPO=Initial Public Offering. It has to do with putting a company up for trade on the stock market. But in this case, it just means announcing the existence of my planned virtual space agency, Real Time Inc. I can't announce that yet, as I won't officially found the company until the current Iron Hill crew returns to Earth in February;)
 
Tester feedback, anyone?

anyways, I just though Id mention that Ive gotten a pair of launch fairings coded to release with the Space key (KSP anyone? :lol:), Ive ironed out all of the bugs, save for that in Virtual cockpit view the fairings do appear, but are invisible behind my VC "windows"-areas where I simply set the texture to be transparent. I was wondering if anyone with experience in the MSH-Blender editing process would be able to fix this for me, as I dont think I have the knowledge to fix this without completely destroying the VC mesh. Alternately, is there a MESHVIS call that might be able to make the mesh render properly while the fairing is on?
 
HI Bruce,I took the Shuttle D for a test run last night,and just wanted to say that your ship handles great,I had no problems flying her.Any of the nit picks I had,were adressed by TMac,the only thing I would like is to be able to launch her into Earths orbit on a Rocket of some type.Thanks for creating this. :cheers:
 
Last edited:
HI Bruce,I took the Shuttle D for a test run last night,and just wanted to say that your ship handles great,I had no problems flying her.Any of the nit picks I had,were adressed by TMac,the only thing I would like is to be able to launch her into Earths orbit on a Rocket of some type.Thanks for creating this. :cheers:

Could you describe your flight for us? I think the SH-D should be okay in space, but I was hoping for a check on what she feels like in an atmosphere, specifically, Mars, Titan, and Venus, if you feel up to it ;). Any thoughts from a creativity perspective? (ie what the cockpit should look, feel like?)
 
Right now, the cockpit feels very small and tight, like it's meant for a crew of one. And that's okay:) If you intend for the ship to only have a flight crew of one.

Also, I didn't see any buttons on the VC MFDs. I had to use glass cockpit view:(
 
Right now, the cockpit feels very small and tight, like it's meant for a crew of one. And that's okay:) If you intend for the ship to only have a flight crew of one.

Also, I didn't see any buttons on the VC MFDs. I had to use glass cockpit view:(

Umm, there is 2 seats right? Im pretty sure I scaled it to something reasonable, but is it the way it looks (colour, lighting?). Yes, I do have working MFDs in the VC now, very excited about that :woohoo:

I had to just put digital lines pointing to the side buttons though, as I dont have the time or knowledge to figure out their redraw functions. I figure using the function button (far right on bottom) and looking at the options in that view should be flexible enough eh?
 
Back
Top