SDK Question OrbiterAPI create vessel: setting custom parameters

Ok I missed a step are you proposing two seperate functions for stage seperation?

A version that spawns the SM and a version that doesn't?
 
No. Where did I propose that?

I proposed only a single function doing the common stuff instead of duplicating the same code into 2 functions.
 
so three functions then?

A stage seperation function, a spawn SM function, and a seperate SM common function.

ETA:
We'll need a seperate bollean to ensure seperateSMcommon isn't run more than once, Orbiter will CTD if it is. Seems to me that we are making the required logic more complex than it needs to be and for dubious benefit.

I mean how hard is it to debug 3 - 9 lines?

Is the effort you save worth the extra coding time?
 
Last edited:
Please elaborate, because I don't understand what you're trying to achieve.

I only proposed a single function that does the common stuff and not duplicate it into 2 callback functions, and not 3 functions doing different things.
 
3 things need to happen when the SM is jettisoned;
  1. A new "SM class" vessel needs to be spawned.
  2. The parent vessel's current SM propellant levels, and other variables, need to be applied to the new SM vessel.
  3. The SM's thrusters, propellant, mesh, etc... need to be deleted from the parent vessel.

Rather than scattered around the code, as Bruce currently has it, I suggested a single function that would consolidate the entire operation.

hense my comment about how...

Code:
Code:
	// Key "J" Jettison the service module
	if(key==OAPI_KEY_J&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))

	if (SMJett=1)
	{
	SMJettison();
	SMJett--;
	MeshControl ();
	ClassControl ();
	}

	else
	{
	MeshControl ();
	ClassControl ();
	}

Should look like

Code:
// Key "J" Jettison the service module
if(key==OAPI_KEY_J&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
{
  SeperateSM();
  return 1;
}

...and how he shouldn't be mucking about with vessel params in pre or poststep if he doesn't need to.

However this comes with the caveat that if he does consolidate the operation into a single function the last step, deleting the SM's thrusters, propellant, mesh, etc... will be needed to be called in clbkpostcreation behind an if statement to account for cases where the parent vessel is spawned without a SM. Such as on scenario load.

In the example I linked to this is as simple as...

Code:
	if (VesselStatus == ASCENT)
	{
		DelThruster (th_descent);						// Delete descent stage engine
		DelPropellantResource (ph_descentH2O);			// Delete descent stage water tank
		DelPropellantResource (ph_descentO2);			// Delete descent stage O2 tank
		DelPropellantResource (ph_descentFuel);			// Delete descent stage propellant tank
		DelMesh (mesh_Descent);							// Delete descent stage mesh
	}

I mean I suppose I could've put them in a seperate function but that seems a bit superfluous for only 5 lines.
 
Erm, this is all very interesting, but for once I figured things out on my own. After finally noticing the biggest :facepalm: imaginable inherent to how I have been writing my code up until know, I had a moment like this


Just substitute SM Jettison for "the switch" & capsule for "the non-laugher"

Anyways, most of the project is working at the moment, but I will need some advice on the LES system & the parachutes in particular. Heres the source code at present

Code:
#pragma once
#include "Orbitersdk.h"
#include "UMmuSDK.h"

// Vessel Parameters
// This is where data about the vessel class can be specified, then loaded later under a shorter name. For example,
// const double EXP_EMPTYMASS = 14770; allows me to put EXP_EMPTYMASS in place of 14770 when I specify the empty 
// mass of the vehicle later.

const double EXP_SIZE_BEFORE_SMJETT = 14; // mean radius in meters
const double EXP_SIZE_AFTER_SMJETT = 4; // mean radius in meters
const VECTOR3 EXP_CS_BEFORE_SMJETT = {57.99,58.69,17.33}; //Shuttle-D cross section in m^2
const VECTOR3 EXP_CS_AFTER_SMJETT = {10.13,10.25,17.11}; //Shuttle-D cross section in m^2
const VECTOR3 EXP_PMI_BEFORE_SMJETT = {14.48,14.49,2.16}; //Principal Moments of Inertia, normalized, m^2
const VECTOR3 EXP_PMI_AFTER_SMJETT = {1.50,1.50,1.93}; //Principal Moments of Inertia, normalized, m^2
const VECTOR3 CAPSULE_OFFSET = {0,0,6.214}; //Principal Moments of Inertia, normalized, m^2
const VECTOR3 SM_OFFSET = {0,0,-6.214}; //Principal Moments of Inertia, normalized, m^2
const VECTOR3 ZEROVECTOR = {0,0,0}; //Principal Moments of Inertia, normalized, m^2
const VECTOR3 FORWARD = {0,0,1}; //Principal Moments of Inertia, normalized, m^2
const VECTOR3 TENFORWARD = {0,0,10}; //Principal Moments of Inertia, normalized, m^2
const VECTOR3 TENTHOUSANDFORWARD = {0,0,10000}; //Principal Moments of Inertia, normalized, m^2
const double CAPSULEMASS = 6400; //empty vessel mass in kg
const double SMMASS = 11000; //empty vessel mass in kg
const double EXP_MAINFUELMASS =  29000; //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 VACRCS_ISP = 1600; //fuel-specific impulse in m/s
const double NMLRCS_ISP = 710; //fuel-specific impulse in m/s
const double P_NML = 101.4e3;
const double RCSTH0 = 1200; 
const double RCSTH1 = 1200;
const double RCSTH2 = 1200; 
const double RCSTH3 = 1200;
const double RCSTH4 = 1200; 
const double RCSTH5 = 1200;
const double RCSTH6 = 1200; 
const double RCSTH7 = 1200;
const double RCSTH8 = 1200; 
const double RCSTH9 = 1200;
const double RCSTH10 = 1200; 
const double RCSTH11 = 1200;


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

	~PhoenixASMN();

	// In this section functions to be called in the main body of the code are specified for use later. If a function placed in here is
	// never called later a "UNRESOLVED external" error will most likely pop up at compile-time. If a function is placed in the CPP but
	// not "created" here, it simply wont work.
	PROPELLANT_HANDLE RCS1, RCS2, RCS3, RCS4, RCS5, RCS6, RCS7, RCS8, MainFuel;
	DOCKHANDLE Dock0;
	THRUSTER_HANDLE th_rcsCM[12], th_group[4];



	MESHHANDLE PhoenixCapsule;
	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);
	double UpdateMass ();
	double O2Check ();
	double WaterCheck ();
	double AltitudeCheck ();
	double VelocityCheck ();
	double MeshControl ();
	double ClassControl ();
	double SMJettison ();
	double Checkint ();
	void clbkPostCreation(void);
	void SpawnObject(char* classname, char* ext, VECTOR3 ofs);

	// Bits of code used to give the gear & payload bay references to work with instead of 0,0.1,0.2...
	// The different VC camera positions are also identified here as well.

	enum {CAM_VCPILOT, CAM_VCPSNGR1, CAM_VCPSNGR2, CAM_VCPSNGR3, CAM_VCPSNGR4} campos;

	// This is a unique id, used to identify the ship in OrbiterSound.

	int PHXASMN;	

	bool clbkLoadVC (int id);
	int  clbkConsumeBufferedKey (DWORD key, bool down, char *kstate);
	VCMFDSPEC mfds_left;
	VCMFDSPEC mfds_right;

	//UMMU 2.0 Code
	//This section contains code which is used to add support for UMMU crew, created by Dansteph.

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

	// "Allow user to add crew to your ship 
	// without scenery editor"
	char cAddUMmuToVessel[255];
	void AddUMmuToVessel(BOOL bStartAdding=FALSE);

	char   *SendCargHudMessage(void);	// Cargo hud display function
	char	cCargoHudDisplay[255];		// Cargo hud display char variable
	double	dCargHudMessageDelay;		// Cargo hud display delay

	char   *SendCarg2HudMessage(void);	// Cargo hud display function
	char	cCargo2HudDisplay[255];		// Cargo hud display char variable
	double	dCarg2HudMessageDelay;		// Cargo hud display delay

	char   *SendCarg3HudMessage(void);	// Cargo hud display function
	char	cCargo3HudDisplay[255];		// Cargo hud display char variable
	double	dCarg3HudMessageDelay;		// Cargo hud display delay

	private:
	int iActiveDockNumber;

	// Vessel specific parameters are called here like the # of kilos of LOX in the onboard tanks, the positions of the gear & payload bay doors,
	//	a variable that Im hoping to use as a randomizer for this project in the future. Variables are used to store & keep track of various pieces
	// of information during a simulation session, but need to be saved & loaded properly in clbkLoadStateEx & clbkSaveState
	// if they are to be persistent. Oh hi Face... ;)



	double O2Tank;
	int SMJett;
	double Altitude;
	double Dv;
	double M0;
	double M1;
	double Airspeed;
	double Water;
	double MSStime;
	double Randomizer;
};


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

Code:
//=========================================================
// ShuttleDB 1.1 source code
// This is what I have been working on for the last few months. The Shuttle-D is currently designed as a NTR propelled cargo transport
// & utility vessel compatible with UMMU, UCGO, & Orbitersound. Ive made sure to heavily comment my source in order to give new addon devs
// a good resource for understanding many of the pitfalls I had to endure, and how to beat them. If youre planning on reading this as a tutorial,
// I would reccomend you start over in D9base.h, as I structured the tutorial starting from there.
//=========================================================

#define STRICT
#define ORBITER_MODULE
#include "PhoenixASMNbase.h"
#include "orbitersdk.h"
#include <math.h>
#include <stdio.h>
#include "OrbiterSoundSDK40.h"
#include "VesselAPI.h"

HINSTANCE g_hDLL;
VISHANDLE MainExternalMeshVisual = 0;

// ==============================================================
// Airfoil/Aerodynamics definition
// Looks painfuly complicated at first, but gets a lot simpler once you realize that each column corresponds to a particular AOA value,
// and the parameters underneath give the aerodynamic behaviour of the vessel at the given AOA. An excellent tutorial on what these values mean
// is available on the Orbiterwiki website.
// ==============================================================

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.0011,      0.00001};
	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)
// To the best of my knowledge, this is called when a Shuttle-D is first created & allows parameters to be set to specific values right away.
// For example 	O2Tank = 1000; indicates that the main oxygen tank parameter is set to 1000/1000; full, upon creation of a new Shuttle-D.
// ==============================================================

PhoenixASMN::PhoenixASMN (OBJHANDLE hObj, int fmodel)
	: VESSEL3 (hObj, fmodel)
{
	O2Tank = 70;
	Water = 230;
	SMJett = 1;
}

//=========================================================
// Mass Update Function
// A big prize to anyone who can guess what this does! Yes that is correct, it updates the mass of a Shuttle-D vessel based on its empty mass & the amount of O2 it is carrying!
// As a previous English teacher I once had was fond of saying, your prize is knoledge & undertanding (shows how much I learned :)
// This function does not fully complete the mass calculations, as the UCGO mass update function down in void clbkPostStep also has a hand in it, but this is the main place or things of that ilk, so...
//=========================================================

double PhoenixASMN::UpdateMass ()
{
double mass=(CAPSULEMASS+(SMJett*SMMASS));
double massand=(O2Tank);

double CrewComplement = Crew.GetCrewTotalNumber();

mass+=(O2Tank+(CrewComplement*85));

return mass;
}

void PhoenixASMN::SpawnObject(char* classname, char* ext, VECTOR3 ofs)
{
	VESSELSTATUS vs;
	char name[256];
	GetStatus(vs);
	Local2Rel (ofs, vs.rpos);
	vs.eng_main = vs.eng_hovr = 0.0;
	vs.status = 0;
	strcpy (name, GetName()); strcat (name, ext);
	oapiCreateVessel (name, classname, vs);
}


//=========================================================
// O2Check Function
// A very simple function here, that allows me to return the value of oxygen remaining in the tanks.
// I needed this in order for the hud check function that returns O2 levels to the pilot would work.
//=========================================================
double PhoenixASMN::O2Check ()
{
return O2Tank;
}


double PhoenixASMN::WaterCheck ()
{
return Water;
}

double PhoenixASMN::AltitudeCheck ()
{

Altitude = GetAltitude ();

return Altitude;
}

double PhoenixASMN::VelocityCheck ()
{

Airspeed = GetAirspeed ();

return Airspeed;
}

double PhoenixASMN::Checkint ()
{
return SMJett;
}

double PhoenixASMN::SMJettison ()
{
	//SpawnObject( "PhoenixSMN", ("%s_SM", GetName()), ZEROVECTOR);
	SpawnObject( "PhoenixSMN", "SM", ZEROVECTOR);
	AddForce((TENTHOUSANDFORWARD), ZEROVECTOR);
	ShiftCG(CAPSULE_OFFSET);
	SMJett--;
	MeshControl ();
	ClassControl ();

	DelPropellantResource (MainFuel);
	DelPropellantResource (RCS5);
	DelPropellantResource (RCS6);
	DelPropellantResource (RCS7);
	DelPropellantResource (RCS8);

	SetThrusterResource(th_rcsCM[0], RCS1);
	SetThrusterResource(th_rcsCM[1], RCS1);
	SetThrusterResource(th_rcsCM[2], RCS3);
	SetThrusterResource(th_rcsCM[3], RCS3);
	SetThrusterResource(th_rcsCM[4], RCS2);
	SetThrusterResource(th_rcsCM[5], RCS2);
	SetThrusterResource(th_rcsCM[6], RCS4);
	SetThrusterResource(th_rcsCM[7], RCS4);
	SetThrusterResource(th_rcsCM[8], RCS3);
	SetThrusterResource(th_rcsCM[9], RCS3);
	SetThrusterResource(th_rcsCM[10], RCS4);
	SetThrusterResource(th_rcsCM[11], RCS4);
return 1;
}

double PhoenixASMN::MeshControl ()
{

ClearMeshes();
	if (SMJett == 1)
	{
SetMeshVisibilityMode (AddMesh (PhoenixCapsule = oapiLoadMeshGlobal ("PhoenixAoffset")), MESHVIS_ALWAYS);
SetMeshVisibilityMode (AddMesh (PhoenixCapsule = oapiLoadMeshGlobal ("PhoenixSMN MKII")), MESHVIS_ALWAYS);
return 1;
	}

	else
	{
SetMeshVisibilityMode (AddMesh (PhoenixCapsule = oapiLoadMeshGlobal ("PhoenixA")), MESHVIS_ALWAYS);
return 1;
	}
return 1;
}

// PhoenixSMN MKII

// 6.214

double PhoenixASMN::ClassControl ()
{

	ClearDockDefinitions ();


	if (SMJett == 1)
	{
	SetPMI (EXP_PMI_BEFORE_SMJETT);
	SetSize (EXP_SIZE_BEFORE_SMJETT);
	SetCrossSections (EXP_CS_BEFORE_SMJETT);
	SetTouchdownPoints (_V(0,-2,5.064), _V(-2,2,5.064), _V(2,2,5.064));
	SetCameraOffset (_V(0,1.707,6.753));
	Crew.DefineAirLockShape(TRUE,-3,-1.222,-1.700,1.700, 5.464,8.714);
	Crew.SetMembersPosRotOnEVA(_V(-2.180,0,7.123),_V(0,-225,0));
	Dock0 = CreateDock(_V(0,0,8.241),_V(0,0,1),_V(0,1,0));

	//SetThrusterResource(th_rcs[0], NULL);
	//SetThrusterResource(th_rcs[1], NULL);
	//SetThrusterResource(th_rcs[2], NULL);
	//SetThrusterResource(th_rcs[3], NULL);
	//SetThrusterResource(th_rcs[4], NULL);
	//SetThrusterResource(th_rcs[5], NULL);
	//SetThrusterResource(th_rcs[6], NULL);
	//SetThrusterResource(th_rcs[7], NULL);
	//SetThrusterResource(th_rcs[8], NULL);
	//SetThrusterResource(th_rcs[9], NULL);
	//SetThrusterResource(th_rcs[10], NULL);
	//SetThrusterResource(th_rcs[11], NULL);
	return 1;
	}

	if (SMJett == 0)
	{
	SetPMI (EXP_PMI_AFTER_SMJETT);
	SetSize (EXP_SIZE_AFTER_SMJETT);
	SetCrossSections (EXP_CS_AFTER_SMJETT);
	SetTouchdownPoints (_V(0,-2,-1.145), _V(-2,2,-1.145), _V(2,2,-1.145));
	SetCameraOffset (_V(0,1.707,0.539));
	Crew.DefineAirLockShape(TRUE,-3,-1.222,-1.700,1.700, -0.75,2.50);
	Crew.SetMembersPosRotOnEVA(_V(-2.180,0,0.909),_V(0,-225,0));
	Dock0 = CreateDock(_V(0,0,2.027),_V(0,0,1),_V(0,1,0));

	DelPropellantResource (MainFuel);
	DelPropellantResource (RCS5);
	DelPropellantResource (RCS6);
	DelPropellantResource (RCS7);
	DelPropellantResource (RCS8);

	return 1;
	}

return 1;
}

//=========================================================
// Vessel Capabilities
// This is what you might consider the core of the addon. In clbkSetClassCaps, all of the core information about the way a vessel behaves
// as a physics object is declared. This includes empty mass, PMI (a measure of the dynamics involved in rotating the vessel), cross-sections,
// Albedo (the colour of the white dot when you zoom out), gear friction coefficients, thrusters & engines, the size of the vessel,
// & of course its mesh (what it looks like). Also included here in this addon are the UMMU & UCGO calls which specify how the vessel uses
// those libraries. They are fairly self-explanatory, so I wont go into detail on them here.
//=========================================================
void PhoenixASMN::clbkSetClassCaps (FILEHANDLE cfg)
{
	Crew.InitUmmu(GetHandle());	
	float UMmuVersion=Crew.GetUserUMmuVersion();
	Crew.SetMaxSeatAvailableInShip(3);

	Crew.DeclareActionArea(0,_V(-2.180,0,7.123),2.0,TRUE,"action_activated.wav","Airlock Activated");
	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. Press E to EVA, 1/2 to select crew, A to Open/Close airlock, 0 or 8 for info, and M to add crew.");

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

	cCargoHudDisplay[0]=0;						// Cargo hud display char variable
	dCargHudMessageDelay=0;						// Cargo hud display delay

	cCargo2HudDisplay[0]=0;						// Cargo hud display char variable
	dCarg2HudMessageDelay=0;						// Cargo hud display delay

	cCargo3HudDisplay[0]=0;						// Cargo hud display char variable
	dCarg3HudMessageDelay=0;						// Cargo hud display delay


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


	// vessel caps definitions

	SetCameraOffset (_V(0,1.707,6.753));
	SetAlbedoRGB (_V(1,1,1));

	MeshControl ();
	ClassControl ();

	SetSurfaceFrictionCoeff (0.88, 0.88);
	SetRotDrag (_V(0.9, 0.76, 0.2));

	EnableTransponder (true);
	InitNavRadios (4);


	// propellant resources
	MainFuel = CreatePropellantResource (EXP_MAINFUELMASS);
	RCS5 = CreatePropellantResource (EXP_RCS5FUELMASS);
	RCS6 = CreatePropellantResource (EXP_RCS6FUELMASS);
	RCS7 = CreatePropellantResource (EXP_RCS7FUELMASS);
	RCS8 = CreatePropellantResource (EXP_RCS8FUELMASS);

	RCS1 = CreatePropellantResource (EXP_RCS1FUELMASS);
	RCS2 = CreatePropellantResource (EXP_RCS2FUELMASS);
	RCS3 = CreatePropellantResource (EXP_RCS3FUELMASS);
	RCS4 = CreatePropellantResource (EXP_RCS4FUELMASS);

	// Service Module RCS engines

	// Capsule RCS engines
	th_rcsCM[0] = CreateThruster (_V( 0.065,2.146, -0.400), _V(0,-1,-1), RCSTH0, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//TOP SIDE FORWARD A
	th_rcsCM[1] = CreateThruster (_V( -0.065,2.146,-0.400), _V(0,-1,-1), RCSTH1, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//TOP SIDE FORWARD B
	th_rcsCM[2] = CreateThruster (_V( 0.220,2.200,-0.487), _V(-1,0,0), RCSTH4, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//TOP SIDE LEFT
	th_rcsCM[3] = CreateThruster (_V(-0.220,2.200,-0.487), _V(1,0,0), RCSTH5, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//TOP SIDE RIGHT

	th_rcsCM[4] = CreateThruster (_V( 0.065,-2.185, -0.407), _V(0,1,-1), RCSTH2, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//BOTTOM SIDE FORWARD A
	th_rcsCM[5] = CreateThruster (_V( -0.065,-2.185,-0.407), _V(0,1,-1), RCSTH3, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//BOTTOM SIDE FORWARD B
	th_rcsCM[6] = CreateThruster (_V( 0.220,-2.200,-0.487), _V(-1,0,0), RCSTH8, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//BOTTOM SIDE LEFT
	th_rcsCM[7] = CreateThruster (_V( -0.220,-2.200,-0.487), _V(1,0,0), RCSTH9, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//BOTTOM SIDE RIGHT


	th_rcsCM[8] = CreateThruster (_V(2.186,-0.221,-0.487), _V(-1,0,-1), RCSTH6, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//RIGHT SIDE FORWARD A
	th_rcsCM[9] = CreateThruster (_V(2.186,0.075,-0.487), _V(-1,0,-1), RCSTH7, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//RIGHT SIDE FORWARD B

	th_rcsCM[10] = CreateThruster (_V(-2.186,-0.221,-0.487), _V(1,0,-1), RCSTH10, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//LEFT SIDE FORWARD B
	th_rcsCM[11] = CreateThruster (_V(-2.186,0.075,-0.487), _V(1,0,-1), RCSTH11, NULL,  VACRCS_ISP, NMLRCS_ISP, P_NML);//LEFT SIDE FORWARD B

	SURFHANDLE texH2O2RCS = oapiRegisterExhaustTexture ("exhaust_atrcsShuttleD");

	AddExhaust (th_rcsCM[0], 1.9, 0.278, _V( 0.065,2.146, -0.400), _V(0,1,1), texH2O2RCS);
	AddExhaust (th_rcsCM[1], 1.9, 0.278, _V( -0.065,2.146,-0.400), _V(0,1,1), texH2O2RCS);
	AddExhaust (th_rcsCM[2], 1.9, 0.278, _V( 0.220,2.200,-0.487), _V(1,0,0), texH2O2RCS);
	AddExhaust (th_rcsCM[3], 1.9, 0.278, _V(-0.220,2.200,-0.487), _V(-1,0,0), texH2O2RCS);

	AddExhaust (th_rcsCM[4], 1.9,  0.278, _V( 0.065,-2.185, -0.407), _V(0,-1,1), texH2O2RCS);
	AddExhaust (th_rcsCM[5], 1.9,  0.278, _V( -0.065,-2.185,-0.407), _V(0,-1,1), texH2O2RCS);
	AddExhaust (th_rcsCM[6], 1.9, 0.278, _V( 0.220,-2.200,-0.487), _V(1,0,0), texH2O2RCS);
	AddExhaust (th_rcsCM[7], 1.9, 0.278, _V( -0.220,-2.200,-0.487), _V(-1,0,0), texH2O2RCS);

	AddExhaust (th_rcsCM[8], 1.9,  0.278, _V(2.186,-0.221,-0.487), _V(1,0,1), texH2O2RCS);
	AddExhaust (th_rcsCM[9], 1.9,  0.278, _V(2.186,0.075,-0.487), _V(1,0,1), texH2O2RCS);

	AddExhaust (th_rcsCM[10], 1.9,  0.278, _V(-2.186,-0.221,-0.487), _V(-1,0,1), texH2O2RCS);
	AddExhaust (th_rcsCM[11], 1.9,  0.278, _V(-2.186,0.075,-0.487), _V(-1,0,1), texH2O2RCS);


	th_group[0] = th_rcsCM[0];
	th_group[1] = th_rcsCM[1];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_PITCHUP);

	th_group[0] = th_rcsCM[4];
	th_group[1] = th_rcsCM[5];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_PITCHDOWN);

	th_group[0] = th_rcsCM[2];
	th_group[1] = th_rcsCM[7];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_BANKLEFT);

	th_group[0] = th_rcsCM[3];
	th_group[1] = th_rcsCM[6];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_BANKRIGHT);

	CreateThrusterGroup (th_group, 0, THGROUP_ATT_UP);

	CreateThrusterGroup (th_group, 0, THGROUP_ATT_DOWN);

	th_group[0] = th_rcsCM[10];
	th_group[1] = th_rcsCM[11];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_YAWLEFT);

	th_group[0] = th_rcsCM[8];
	th_group[1] = th_rcsCM[9];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_YAWRIGHT);

	th_group[0] = th_rcsCM[2];
	th_group[1] = th_rcsCM[6];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_LEFT);

	th_group[0] = th_rcsCM[3];
	th_group[1] = th_rcsCM[7];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_RIGHT);

	CreateThrusterGroup (th_group, 0, THGROUP_ATT_FORWARD);

	CreateThrusterGroup (th_group, 0, THGROUP_ATT_BACK);

}

//=========================================================
// clbkDrawHUD
// This code helps in drawing the custom hud displays that UMMU & UCGO use to send messages. All I really know about this at this point is that in
//   5,hps->H/60*25,cUmmuHudDisplay 
// 60 & 25 appear to change where the text appears on the display.
//=========================================================

bool PhoenixASMN::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*20,cCargoHudDisplay,strlen(cCargoHudDisplay));
		dCargHudMessageDelay-=oapiGetSimStep();
		if(dCargHudMessageDelay<0)
			dCargHudMessageDelay=0;
	}
		// UCGO display messages
	if(dCarg2HudMessageDelay>0)
	{
		skp->Text(5,hps->H/60*22.5,cCargo2HudDisplay,strlen(cCargo2HudDisplay));
		dCarg2HudMessageDelay-=oapiGetSimStep();
		if(dCarg2HudMessageDelay<0)
			dCarg2HudMessageDelay=0;
	}
		// UCGO display messages
	if(dCarg3HudMessageDelay>0)
	{
		skp->Text(5,hps->H/60*17.5,cCargo3HudDisplay,strlen(cCargo3HudDisplay));
		dCarg3HudMessageDelay-=oapiGetSimStep();
		if(dCarg3HudMessageDelay<0)
			dCarg3HudMessageDelay=0;
	}

	return true; 
}

//=========================================================
// clbkLoadVC
// This is where most code for the VC is added. oapiVCRegisterMFD & VCHUDSPEC are used to "create" the MFDs and the Heads-up display. After that,
// the code is subdivided into "case" sections, each one representing a different camera view in the virtual cockpit. The key to switching between these
// is the oapiVCSetNeighbours (a, b, c, d) function. All you need to do is remember a=left, b=right, c=up, d=down, and place the id # of the campos you
// want to switch to in place of that letter when the user its ctrl-directional arrow. -1 specifies a null value (camera wont move) and thats about all
// you need to know to have multiple VC camera points.
//=========================================================

bool PhoenixASMN::clbkLoadVC (int id)
{

	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).

		//SetCameraOffset (_V(0+(0.8*sin(Randomizer)),1.707+(0.8*sin(Randomizer)),6.753));
		SetCameraRotationRange (RAD*120, RAD*120, RAD*180, RAD*10); 
		SetCameraShiftRange (_V(0,0.1,0), _V(-0.7,0,0), _V(0.7,0,0));
		return true;
}


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


//=========================================================
// clbkLoadStateEx
// I would like to dedicate this as the Face function, lol. A big thanks to Face who helped me fix this part of the code when it wasnt written properly,
// and was causing me a lot of grief. The key thing to remember here and in clbkSaveState is that when custom variables (data about your ship) gets saved
// it gets a line in the SCN file. In say,
//
//		if (!_strnicmp (line, "O2Tank", 6)) {
//			sscanf (line+6, "%lf", &O2Tank);
//		}
//
// The number 6 refers to the number of characters in O2Tank - 6. This is the length of the id string, NOT how many lines it has to slip down in the
// scn file.
//=========================================================
void PhoenixASMN::clbkLoadStateEx (FILEHANDLE scn, void *status)
{
	char *line;
	while (oapiReadScenario_nextline (scn, line)) 
	{

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


		if (!_strnicmp (line, "Water", 5)) {
			sscanf (line+5, "%lf", &O2Tank);
		}

		if (!_strnicmp (line, "SMJett", 6)) {
			sscanf (line+6, "%d", &SMJett);
		}

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

		ParseScenarioLineEx (line, status);
	}

}

//=========================================================
// clbkSaveState
// Nothing too exciting here, just the same rules as above, but it is worth noting that in "%d %0.4f", the d & f appear to specify
// the data type being saved. In other words, the specifier "%d" or "%0.4f" will determine how many zeroes get written to the SCN, &
// as a result remembered.
//=========================================================

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

	VESSEL3::clbkSaveState (scn);

	sprintf (cbuf, "%0.4f", O2Tank);
	oapiWriteScenario_string (scn, "O2Tank", cbuf);

	sprintf (cbuf, "%0.4f", Water);
	oapiWriteScenario_string (scn, "Water", cbuf);

	sprintf (cbuf, "%d", SMJett);
	oapiWriteScenario_string (scn, "SMJett", cbuf);

	Crew.SaveAllMembersInOrbiterScenarios(scn);

}

//=========================================================
// SendCargHudMessage
// Another UCGO specific function, which you would think would be related to the hud.
//=========================================================

char *PhoenixASMN::SendCargHudMessage(void)
{
	dCargHudMessageDelay=15; // 15 seconds display delay for msg
	return cCargoHudDisplay;
}

char *PhoenixASMN::SendCarg2HudMessage(void)
{
	dCarg2HudMessageDelay=15; // 15 seconds display delay for msg
	return cCargo2HudDisplay;
}

char *PhoenixASMN::SendCarg3HudMessage(void)
{
	dCarg3HudMessageDelay=15; // 15 seconds display delay for msg
	return cCargo3HudDisplay;
}

//=========================================================
// clbkPostStep
// This is the step-to-step, always-in-motion part of the code during an Orbiter simulation. Orbiter does physics caculations every timestep,
// and as a result, interesting things can be done here like constant updating of variables, the nuts & bolts that drive the animations up & down,
// as well as functions to kill the crew when crashing or running out of air. I wont go into great detail on what I have in here, but this tends to be
// the exciting part of the code. This is where things happen!
//=========================================================

void PhoenixASMN::clbkPostStep(double simt, double simdt, double mjd)
{

SetEmptyMass(UpdateMass());

	int ReturnCode=Crew.ProcessUniversalMMu();
	switch(ReturnCode)
	{
	case UMMU_TRANSFERED_TO_OUR_SHIP: 
		sprintf(SendHudMessage(),"%s \"%s\" transfered to %s",
			Crew.GetCrewMiscIdByName(Crew.GetLastEnteredCrewName()),Crew.GetLastEnteredCrewName()
			,GetName());
		break;
	case UMMU_RETURNED_TO_OUR_SHIP:
		sprintf(SendHudMessage(),"%s \"%s\" ingressed %s",
			Crew.GetCrewMiscIdByName(Crew.GetLastEnteredCrewName()),
			Crew.GetLastEnteredCrewName(),GetName());
		break;
	}

		int ActionAreaReturnCode=Crew.DetectActionAreaActivated();
	if(ActionAreaReturnCode>-1)
	{
		if(ActionAreaReturnCode==0)
		{
		// switch state
		Crew.SetAirlockDoorState(!Crew.GetAirlockDoorState());
		// display state
		if(Crew.GetAirlockDoorState()==TRUE)
			strcpy(SendHudMessage(),"Airlock open");	
		else
			strcpy(SendHudMessage(),"Airlock closed");	
		}
	}



	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(),"Crashed into terrain");
		}
	}

		// Detect Atmospheric Entry-Function Courtesy of Hlynkacg
	if ( (GetDynPressure() > 300000) ) // It's about to get very hot in here...
	{
			int I;
			for(I=0;I<Crew.GetCrewTotalNumber();I++)
			{
				Crew.SetCrewMemberPulseBySlotNumber(I,0);	// set cardiac pulse to zero
			}
			strcpy(SendHudMessage(),"Hull Breach");
	}

//	MainTanks = GetPropellantMass(MainFuel);

	double CrewNumber = Crew.GetCrewTotalNumber();
	double OxygenConsumption = 1.2*1.157407E-5*CrewNumber;
	double WaterConsumption = 4*1.157407E-5*CrewNumber;

{
	if (O2Tank>0)
	{
	O2Tank = (O2Tank - OxygenConsumption*simdt);
	}
	else 
	{
	O2Tank=0;
	}
}

{
	int I;
	if(O2Tank == 0)
{
			// You ran out of oxygen, 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(),"O2 Main Tank empty-All crew dead");
}

}



{

{
	if (Water>0)
	{
	Water = (Water - WaterConsumption*simdt);
	}
	else 
	{
	Water=0;
	}
}
	int I;
	if(Water == 0)
{
			// You ran out of oxygen, 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(),"Water Reserves empty-All crew dead");
}

}

	{


			sprintf(SendCargHudMessage(),"Current altitude  %.0f meters",
				AltitudeCheck());

			sprintf(SendCarg2HudMessage(),"Current velocity  %.0f meters/second",
				VelocityCheck());

			sprintf(SendCarg3HudMessage(),"Water reserves %.0f L, %.0f kg Oxygen remaining",
				WaterCheck (),O2Check ());


	}

if (simt<1)
{
	MeshControl ();
	ClassControl ();
}


		Randomizer = sin(simdt*0.333);

		AddUMmuToVessel();

		Crew.WarnUserUMMUNotInstalled("Phoenix");
		// At the very end of clbkPostStep or clbkPreStep
}


//=========================================================
// clbkConsumeBufferedKey
// Another important function, this particular section is used to execute functions whenever the Orbiter application detects a given keystroke by a user.
// Really not very complicated (usually), very similar to clbkVCMouseEvent. The ony complicated part is getting the structure right, as calls specific
// to a ctrl-keypress or shift-keypress have to be grouped under a separate heading. Best example of that will be under the Hubble Space Telescope sample
// also in the OrbiterSDK directory.
//=========================================================

int PhoenixASMN::clbkConsumeBufferedKey (DWORD key, bool down, char *kstate)
{

	if (!down) return 0;       // only process keydown events

	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(),"%s transfered through main hatch",
				Crew.GetLastEvaedCrewName());SelectedUmmuMember=0;
			break;
		case EVA_OK:
			sprintf(SendHudMessage(),"%s on EVA",
				Crew.GetLastEvaedCrewName());SelectedUmmuMember=0;
			break;
		case ERROR_AIRLOCK_CLOSED:
			strcpy(SendHudMessage(),"Airlock closed. press A to open");
			break;
		case ERROR_DOCKED_SHIP_HAVE_AIRLOCK_CLOSED:
			strcpy(SendHudMessage(),"Docked vessel airlock closed");
			break;
		case ERROR_CREW_MEMBER_NOT_FOUND:
			strcpy(SendHudMessage(),"No crew by this name aboard");
			break;
		case ERROR_DOCKEDSHIP_DONOT_USE_UMMU:
			strcpy(SendHudMessage(),"Docked ship is not compatible with UMmu 2.0");
			break;
		case ERROR_MISC_ERROR_EVAFAILED:
			strcpy(SendHudMessage(),"Misc error with UMMU. Please reinstall");
			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(),"No crew aboard");	
			return 1;
		}

		// we test that we select existing member
		if(SelectedUmmuMember<Crew.GetCrewTotalNumber()-1)
			SelectedUmmuMember++;
		char * Name=Crew.GetCrewNameBySlotNumber(SelectedUmmuMember);
		sprintf(SendHudMessage(),"%i  %s \"%s\"Selected for EVA or Transfer",
			SelectedUmmuMember,Crew.GetCrewMiscIdBySlotNumber(SelectedUmmuMember),
			Name);
		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(),"No crew aboard");	
			return 1;
		}
		if(SelectedUmmuMember>0)
			SelectedUmmuMember--;
		char * Name=Crew.GetCrewNameBySlotNumber(SelectedUmmuMember);
		sprintf(SendHudMessage(),"Slot %i %s \"%s\" Selected for EVA or Transfer"
			", please press \"E\" to EVA",SelectedUmmuMember,
			Crew.GetCrewMiscIdBySlotNumber(SelectedUmmuMember),Name);
		return 1;
	}

	//---------------------------------------------------------------------------
	// Ummu Key "A" Open & Close the virtual UMMU airlock door
	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 open");	
		else
			strcpy(SendHudMessage(),"Airlock closed");	
		return 1;
	}

	//---------------------------------------------------------------------------
	// Get some infos Name of ship and total soul aboard
	if(key==OAPI_KEY_0)
	{
		sprintf(SendHudMessage(),"%i crew aboard %s",
			Crew.GetCrewTotalNumber(),GetName());
		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);
	}

	//---------------------------------------------------------------------------
	// Key "J" Jettison the service module
	if(key==OAPI_KEY_J&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))

	if (SMJett == 1)
	{
	SMJettison();
	}

	else
	{
	MeshControl ();
	ClassControl ();
	}

return 0;
}

//=========================================================
// UMmuCrewAddCallback & AddUMmuToVessel
// Again, a Dansteph creation, so I dont know a great deal about it, but its used in adding crew directly to the ship without entering through the main
// hatch. Fairly usefull, Id say.
//=========================================================

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 PhoenixASMN::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 member name (or hit escape to cancel)",UMmuCrewAddCallback,0,30,(void*)cAddUMmuToVessel);
	}
	else if(cAddUMmuToVessel[0]==3){
		cAddUMmuToVessel[0]=4;
		oapiOpenInputBox ("Enter age",UMmuCrewAddCallback,0,30,(void*)cAddUMmuToVessel);
	}
	else if(cAddUMmuToVessel[0]==5){
		cAddUMmuToVessel[0]=6;
		oapiOpenInputBox ("Enter Crew ID - 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(),"\"%s\" aged %i added to vessel",&cAddUMmuToVessel[2],Age);
		}
		else{
			strcpy(SendHudMessage(),"Unable to add crew");
		}
	}
}



//=========================================================
// Load and Delete Module Stuff
// Apparenty used to load & delete special classes in the module. If theyre not deleted properly in ExitModule, they cause what I believe is called a
// memory leak, not good. So always remember to clean up after yourself!
//=========================================================
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
// Appears similar to the part above, only I think its involved when creating or deleting vessels via the scenario editor.
//=========================================================
DLLCLBK VESSEL *ovcInit (OBJHANDLE hvessel, int flightmodel)
{
	return new PhoenixASMN (hvessel, flightmodel);
}

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

//=========================================================
// clbkPostCreation
// This is usually Orbitersound territory. Im not terribly familiar with how it works in 3.5 or 4.0, so I wont say much here. Better just to look
// up Dantephs tutoials in the Orbitersound docs. He can explain a lot of things better than me ;)
//=========================================================

void PhoenixASMN::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)
	PHXASMN=ConnectToOrbiterSoundDLL(GetHandle());

	SetMyDefaultWaveDirectory("Sound\\_CustomVesselsSounds\\PhoenixASMN\\");

	// 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()")
	ReplaceStockSound(PHXASMN,"mainext.wav",	REPLACE_MAIN_THRUST);
	ReplaceStockSound(PHXASMN,"attfire.wav",		REPLACE_RCS_THRUST_ATTACK);
	ReplaceStockSound(PHXASMN,"attsustain.wav",	REPLACE_RCS_THRUST_SUSTAIN);
	ReplaceStockSound(PHXASMN,"aircond.wav",		REPLACE_AIR_CONDITIONNING);

	ReplaceStockSound(PHXASMN,"VCamb1.wav",		REPLACE_COCKPIT_AMBIENCE_1);
	ReplaceStockSound(PHXASMN,"VCamb2.wav",		REPLACE_COCKPIT_AMBIENCE_2);
	ReplaceStockSound(PHXASMN,"VCamb3.wav",		REPLACE_COCKPIT_AMBIENCE_3);
	ReplaceStockSound(PHXASMN,"VCamb4.wav",		REPLACE_COCKPIT_AMBIENCE_4);
	ReplaceStockSound(PHXASMN,"VCamb5.wav",		REPLACE_COCKPIT_AMBIENCE_5);
	ReplaceStockSound(PHXASMN,"VCamb6.wav",		REPLACE_COCKPIT_AMBIENCE_6);
	ReplaceStockSound(PHXASMN,"VCamb7.wav",		REPLACE_COCKPIT_AMBIENCE_7);
	ReplaceStockSound(PHXASMN,"aircond.wav",		REPLACE_COCKPIT_AMBIENCE_8);
	ReplaceStockSound(PHXASMN,"aircond.wav",		REPLACE_COCKPIT_AMBIENCE_9);

	SoundOptionOnOff(PHXASMN,PLAYRADIOATC,FALSE);
}

//=========================================================
// ~ShuttleD
// This is what I believe is called the destructor. A better name for it might be the Terminator, because Ill be back!!!
//=========================================================

PhoenixASMN::~PhoenixASMN() 
{

}

//=========================================================
// Hope the work I did commenting this source helped, please let me know what you think via email at [email protected] or on the Orbiter Forums
// at BruceJohnJennerLawso (or even my Shuttle-D development thread). This project has been about 10 months or so in the making & I
// hope you enjoy flying the Shuttle-D, as much as I enjoyed creating it. Stay tuned for 1.1, and
// 
// Hail the Probe!
//
//=========================================================

Please ignore the comments, they arent relevant to this project at the moment.

I also need to do some research to find out ISP values for NTRs using LOX propellant, unless anyone around here knows...
 
Back
Top