Coding a Lunar Lander from the ground up: (Custom Functions and Sub-routines)

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
[/COLOR]PART 8: Custom Functions and Sub-routines

This is a subject that often intimidates newbies but once you realise just how easy it is to do there really is no going back.

Thus far we have been overloading functions orbiter's core functions. These functions all existed in Orbiter prior to our calling them. Now it's time to start creating entirely new functions. We'll start with something easy.

Go to your class interface in your header file and add the following lines.

Code:
class LM: public VESSEL3 {
public:
	LM (OBJHANDLE hVessel, int flightmodel);
	~LM ();

[COLOR="Red"]	// Custom vessel functions
	void	DefineAnimations (void);	[/COLOR]		
	
	// Overloaded callback functions
	void	clbkSetClassCaps (FILEHANDLE cfg);								// Set the capabilities of the vessel class
	void	clbkLoadStateEx (FILEHANDLE scn, void *status);					// Read status from scenario file

We have now declared a new (currently non-existant) function that will be unique to our vessel class.

It's keyword is "void" because, clbkConsumeBufferedKey, we don't expect or need it to return a value. It's input ( in perenthesis) ;) is also "void" because it's instructions will not be dependant on any external function or variables.

Now that the function has been declared we need to define it. Let's go back to our source file and add a new section to it below our destructor but above our overloaded callback functions.

Code:
LM::~LM ()
{
}

[COLOR="red"]// ==============================================================
// Custom Vessel Functions
// ==============================================================

// --------------------------------------------------------------
// Define meshgroup transformations
// --------------------------------------------------------------
void LM::DefineAnimations (void)
{

}[/COLOR]

Our function is now a "stub", meaning that it has been properly declared/defined but it doesn't do anything yet.

Let's change that. Go down and cut/paste all of our animation components from clbkPostCreation to our new animation function.

Code:
// --------------------------------------------------------------
// Define mesh animations
// --------------------------------------------------------------
void LM::DefineAnimations (void)
{
[COLOR="red"]	// EVA Hatch animation
	static UINT	meshgroup_Hatch		= AS_GRP_EvaHatch;				// participating groups
	static UINT	meshgroup_Handle	= AS_GRP_HatchHandle;

	static MGROUP_ROTATE	mgt_Hatch (mesh_Ascent, &meshgroup_Hatch, 1, _V( 0.394,-0.578, 1.661), _V( 0.0, 1.0, 0.0), (float)-90*RAD); 
	static MGROUP_ROTATE	mgt_HatchHandle (mesh_Ascent, &meshgroup_Handle, 1, _V(-0.40279,-0.55598, 1.69760), _V( 0.0, 0.0, 1.0), (float) 90*RAD);

	anim_Hatch		= CreateAnimation (0.0);
	ach_Hatch		= AddAnimationComponent (anim_Hatch, 0.3f, 1.0f, &mgt_Hatch);
	ach_HatchHandle	= AddAnimationComponent (anim_Hatch, 0.0f, 0.2f, &mgt_HatchHandle, ach_Hatch);

	// Landing Gear Animation
	static UINT meshgroup_Legs[4][3] = {
		{DS_GRP_LandingFoot_FWD,	DS_GRP_ShockStrut_FWD,	DS_GRP_PrimaryStrut_FWD}, 
		{DS_GRP_LandingFoot_AFT,	DS_GRP_ShockStrut_AFT,	DS_GRP_PrimaryStrut_AFT},
		{DS_GRP_LandingFoot_PORT,	DS_GRP_ShockStrut_PORT,	DS_GRP_PrimaryStrut_PORT}, 
		{DS_GRP_LandingFoot_STBD,	DS_GRP_ShockStrut_STBD,	DS_GRP_PrimaryStrut_STBD}};
	static UINT meshgroup_Struts[4] = { DS_GRP_SecondaryStruts_FWD, DS_GRP_SecondaryStruts_AFT, DS_GRP_SecondaryStruts_PORT, DS_GRP_SecondaryStruts_STBD};
	static UINT meshgroup_Locks[4] = { DS_GRP_Downlock_FWD, DS_GRP_Downlock_AFT, DS_GRP_Downlock_PORT, DS_GRP_Downlock_STBD};
	static UINT meshgroup_Ladder = DS_GRP_Ladder;

	anim_Gear = CreateAnimation (1.0);

	for (int i = 0; i < 4; i++)
	{
		// Animation components
		mgt_Leg[i]		= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Legs[i][0], 3, LM_LegPivot[i], LM_LegAxis[i], (float) 45*RAD);		// Animate landing legs
		mgt_Strut[i]	= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Struts[i], 1, LM_StrutPivot[i], LM_LegAxis[i], (float)-63*RAD);		// Animate Support Struts attatched to legs
		mgt_Downlock[i]	= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Locks[i], 1, LM_DownlockPivot[i], LM_LegAxis[i], (float) 150*RAD);	// Animate Locking mechanism joining support struts to body

		// Add individual components to 'anim_Gear' 
		ach_GearLeg[i]		= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Leg[i]);
		ach_GearStrut[i]	= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Strut[i], ach_GearLeg[i]);
		ach_GearLock[i]		= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Downlock[i], ach_GearStrut[i]);
	}

	static MGROUP_ROTATE mgt_Ladder (mesh_Descent, &meshgroup_Ladder, 1, LM_LegPivot[0], LM_LegAxis[0], (float) 45*RAD); // Apply front leg's animation state to ladder.
	AddAnimationComponent (anim_Gear, 0.0, 1, &mgt_Ladder);[/COLOR]}
Then in clbkPostCreation add a call to "DefineAnimations"

Code:
// --------------------------------------------------------------
// Finalise vessel creation
// --------------------------------------------------------------
void LM::clbkPostCreation ()
{
	[COLOR="red"]DefineAnimations ();[/COLOR]

} // End "LM::clbkPostCreation"

Compile your code and test it.

Functionally speaking you shouldn't notice anything different but under the hood this represents a signifigant change. It may not seem like much now but this will save us huge amounts of time and effort down the line.

In fact, lets plan ahead and add a few more stubs to our source file...

Code:
	static MGROUP_ROTATE mgt_Ladder (mesh_Descent, &meshgroup_Ladder, 1, LM_LegPivot[0], LM_LegAxis[0], (float) 45*RAD); // Apply front leg's animation state to ladder.
	AddAnimationComponent (anim_Gear, 0.0, 1, &mgt_Ladder);

} // End "LM::DefineAnimations"

[COLOR="red"]// --------------------------------------------------------------
// Define local light sources
// --------------------------------------------------------------
void LM::DefineLighting (void)
{

} // End "LM::DefineLighting"

// --------------------------------------------------------------
// Define exhaust and particle streams
// --------------------------------------------------------------
void LM::DefineParticleStreams (void)
{

} // End "LM::DefineParticleStreams"

// --------------------------------------------------------------
// Define sound effects
// --------------------------------------------------------------
void LM::DefineSounds (void)
{

} // End "LM::DefineSounds"[/COLOR]

Declare them in the header file the same way we declared "DefineAnimations" and add them to clbkPostCreation.

Code:
// --------------------------------------------------------------
// Finalise vessel creation
// --------------------------------------------------------------
void LM::clbkPostCreation ()
{
	DefineAnimations ();
[COLOR="red"]	DefineLighting ();
	DefineParticleStreams ();
	DefineSounds ();[/COLOR]

} // End "LM::clbkPostCreation"

Witness the power of this fully armed and operational object-oriented programming language!

The ability to break large blocks of our vessel's code into seperate sub-functions will be incredibly important moving forward.

---------- Post added at 13:58 ---------- Previous post was at 13:50 ----------

CURRENT STATE OF HEADER AND SOURCE

Header...
Code:
// ==============================================================
//                    ORBITER MODULE: LM
//                  Part of the ORBITER SDK
//               Copyright (C) 2012 Greg Hlynka
//                    All rights reserved
//
// LM.h
// Interface for LM vessel class
//
// Notes: This file exists
// ==============================================================

#define STRICT
#define ORBITER_MODULE

// Orbiter SDK files
#include "orbitersdk.h"

// Mesh resource files
#include "_ascentmesh.h"
#include "_descentmesh.h"
#include "_cockpitmesh.h"

// ==============================================================
// Vessel parameters
// ==============================================================

const VECTOR3	LM_ASC_OFFSET		= { 0.00, 1.44, 0.00};	// Offset of Ascent stage COG from combined COG
const VECTOR3	LM_DES_OFFSET		= { 0.00,-0.80, 0.00};	// Offset of Descent stage COG from combined COG
const double	LM_SIZE				= 5.5;					// Vessel's mean radius [m]
const double	LM_ASC_SIZE			= 2.5;					// Mean radius [m] after staging
const VECTOR3	LM_CS				= { 19.0, 19.2, 18.8};	// x,y,z cross sections [m^2]
const VECTOR3	LM_ASC_CS			= { 9.62, 12.6, 9.25};	// Cross sections [m^2] after staging
const VECTOR3	LM_PMI				= { 2.68, 2.10, 2.64};	// Principal moments of inertia (mass-normalised) [m^2]
const VECTOR3	LM_ASC_PMI			= {	1.31, 1.68, 1.25};	// Principal moments of inertia (mass-normalised) [m^2] after staging
const VECTOR3	LM_RD				= {	0.04, 0.05, 0.04};	// Rotation drag coefficients
const VECTOR3	LM_ASC_RD			= {	0.03, 0.07, 0.09};	// Rotation drag coefficients after staging
const double	LM_ASC_EMPTYMASS	= 1922.78;				// Ascent stage empty mass [kg] (4239 lbs, page 119)
const double	LM_DES_EMPTYMASS	= 1798.95;				// Descent stage empty mass [kg] (3,966 lbs, page 119)
const double	LM_ASC_FUELMASS		= 2353.0;				// Ascent stage fuel mass [kg] 
const double	LM_DES_FUELMASS		= 8480.81;				// Descent stage fuel mass [kg] (18,697 lbs, LMFM pg 58)
const double	LM_RCS_FUELMASS		= 750.0;				// RCS fuel mass [kg] (633 lbs, LMFM pg 72)
const double	LM_ASC_WATERMASS	= 19.55;				// max fuel mass [kg] (page 177)
const double	LM_DES_WATERMASS	= 135.64;				// Descent stage water storage mass [kg] (299 lbs, page 177)
const double	LM_ASC_O2MASS		= 1.102;				// max fuel mass [kg] (2.43 lbs)
const double	LM_DES_O2MASS		= 20.50;				// Descent stage O2 reserve mass [kg] (45.2 lbs)
const double	LM_ASC_ISP			= 3050.91;				// Ascent stage specific impulse [m/s] (311 sec, Astronautix.com)
const double	LM_DES_ISP			= 3075.44;				// Descent stage specific impulse [m/s] (313.5 sec, Astronautix.com)
const double	LM_RCS_ISP			= 2844.90;				// RCS specific impulse [m/s] (290 sec, Astronautix.com)
const VECTOR3	LM_TDP[3]			= {{ 0.0,-3.1, 4.4}, {-3.8,-3.1,-2.2}, { 3.8,-3.1,-2.2}};	// touchdown points [m]
const VECTOR3	LM_ASC_TDP[3]		= {{ 0.0,-1.4, 1.7}, {-0.9,-1.4,-1.5}, { 0.9,-1.4,-1.5}};	// touchdown points [m] after staging
const double	LM_ASC_MAXTHRUST	= 15568.8;				// Ascent stage max thrust [n] (3,500 lbf, LMFM pg 58)
const double	LM_DES_MAXTHRUST	= 44482.2;				// Descent stage max thrust	[n] (10,000 lbf, LMFM pg 58)
const double	LM_RCS_MAXTHRUST	= 440.0;				// RCS thruster max thrust [n] (100 lbf)
const VECTOR3	LM_ASC_ENGINEPOS	= {	0.00,-0.64, 0.00};	// Ascent stage engine position [m]
const VECTOR3	LM_DES_ENGINEPOS	= { 0.00,-1.18, 0.00};	// Descent stage  engine position [m]
const VECTOR3	LM_RCS_ENGINEPOS[8]	= {{-1.68, 1.44, 1.68}, {-1.562, 1.44, 1.562}, {-1.68, 1.44,-1.68}, {-1.562, 1.44,-1.562}, { 1.68, 1.44,-1.68}, { 1.562, 1.44,-1.562}, { 1.68, 1.44, 1.68}, { 1.562, 1.44, 1.562}}; // RCS engine positions [m]

const VECTOR3	LM_DOCK_POS			= { 0.00, 2.93, 0.00};	// docking port location [m]
const VECTOR3	LM_DOCK_DIR			= { 0, 1, 0};			// docking port approach direction
const VECTOR3	LM_DOCK_ROT			= { 0, 0, 1};			// docking port alignment direction

// ==============================================================
// Lunar Module class interface
// ==============================================================

class LM: public VESSEL3 {
public:
	LM (OBJHANDLE hVessel, int flightmodel);
	~LM ();

	// Custom vessel functions
	void	DefineAnimations (void);			// Define mesh animations
	void	DefineLighting (void);				// Define local light sources
	void	DefineParticleStreams (void);		// Define exhaust and particle streams
	void	DefineSounds (void);				// Define sound effects

	// Overloaded callback functions
	void	clbkSetClassCaps (FILEHANDLE cfg);								// Set the capabilities of the vessel class
	void	clbkLoadStateEx (FILEHANDLE scn, void *status);					// Read status from scenario file
	void	clbkSaveState (FILEHANDLE scn);									// Write status to scenario file
	void	clbkPostCreation ();											// Finalise vessel creation
	void	clbkPostStep (double simt, double simdt, double mjd);			// Manage Animations and Post-Step processes
	int		clbkConsumeBufferedKey (DWORD key, bool down, char *kstate);	// Process keyboard inputs

	// Thruster and Propellant handles
	PROPELLANT_HANDLE	ph_rcsA, ph_rcsB, ph_ascentFuel, ph_descentFuel;			// Functional Propellant tanks
	PROPELLANT_HANDLE	ph_ascentO2, ph_ascentH2O, ph_descentO2, ph_descentH2O;		// Represenative propellant tanks (consumables)
	
	THRUSTER_HANDLE		th_rcsA[8], th_rcsB[8], th_ascent, th_descent;				// Functional thrusters
	THRUSTER_HANDLE		th_dust, th_vent;											// Represenative thrusters (particle streams)

private:
	// Vessel status variables
	enum	vesselstate	{LAUNCH, DESCENT, ASCENT, WRECKED}	VesselStatus;	
	enum	doorstate	{CLOSED, OPEN, CLOSING, OPENING}	HatchStatus, GearStatus;
	
	double	thrust_mod, dv_mod, consumables_mod;

	// Animations
	UINT						anim_Hatch, anim_Gear;
	double						hatch_proc, gear_proc;
	ANIMATIONCOMPONENT_HANDLE	ach_GearLeg[4], ach_GearStrut[4], ach_GearLock[4], ach_Hatch, ach_HatchHandle;
	MGROUP_TRANSFORM			*mgt_Leg[4], *mgt_Strut[4], *mgt_Downlock[4];
	
	// Meshes
	MESHHANDLE	mh_descent, mh_ascent;	// Mesh handles
	UINT		mesh_Descent;			// Descent stage mesh index
	UINT		mesh_Ascent;			// Ascent stage mesh index

}; // End "class LM:"

Source...
Code:
// ==============================================================
//                    ORBITER MODULE: LM
//                  Part of the ORBITER SDK
//               Copyright (C) 2012 Greg Hlynka
//                    All rights reserved
//
// LM.cpp
// Control module for LM vessel class
//
// Notes: Writing Tutorials is hard work
// ==============================================================

#include "LM.h"

// ==============================================================
// Class Constructor and Destructor
// ==============================================================

LM::LM (OBJHANDLE hVessel, int flightmodel)
: VESSEL3 (hVessel, flightmodel)
{
	// Config file difficulty modifiers (1.0 = 100% of default value)
	thrust_mod		= 1.0;
	dv_mod			= 1.0;
	consumables_mod = 1.0;

	// Initial vessel state
	VesselStatus	= DESCENT;

	// Load Exterior meshes
	mh_descent		= oapiLoadMeshGlobal("UMMU_Apollo/LEM_DescentStage");
	mh_ascent		= oapiLoadMeshGlobal("UMMU_Apollo/LEM_AscentStage");
}

LM::~LM ()
{
}

// ==============================================================
// Custom Vessel Functions
// ==============================================================

// --------------------------------------------------------------
// Define mesh animations
// --------------------------------------------------------------
void LM::DefineAnimations (void)
{
	// EVA Hatch animation
	static UINT	meshgroup_Hatch		= AS_GRP_EvaHatch;				// participating groups
	static UINT	meshgroup_Handle	= AS_GRP_HatchHandle;

	static MGROUP_ROTATE	mgt_Hatch (mesh_Ascent, &meshgroup_Hatch, 1, _V( 0.394,-0.578, 1.661), _V( 0.0, 1.0, 0.0), (float)-90*RAD); 
	static MGROUP_ROTATE	mgt_HatchHandle (mesh_Ascent, &meshgroup_Handle, 1, _V(-0.40279,-0.55598, 1.69760), _V( 0.0, 0.0, 1.0), (float) 90*RAD);

	anim_Hatch		= CreateAnimation (0.0);
	ach_Hatch		= AddAnimationComponent (anim_Hatch, 0.3f, 1.0f, &mgt_Hatch);
	ach_HatchHandle	= AddAnimationComponent (anim_Hatch, 0.0f, 0.2f, &mgt_HatchHandle, ach_Hatch);

	// Landing Gear Animation
	static UINT meshgroup_Legs[4][3] = {
		{DS_GRP_LandingFoot_FWD,	DS_GRP_ShockStrut_FWD,	DS_GRP_PrimaryStrut_FWD}, 
		{DS_GRP_LandingFoot_AFT,	DS_GRP_ShockStrut_AFT,	DS_GRP_PrimaryStrut_AFT},
		{DS_GRP_LandingFoot_PORT,	DS_GRP_ShockStrut_PORT,	DS_GRP_PrimaryStrut_PORT}, 
		{DS_GRP_LandingFoot_STBD,	DS_GRP_ShockStrut_STBD,	DS_GRP_PrimaryStrut_STBD}};
	static UINT meshgroup_Struts[4] = { DS_GRP_SecondaryStruts_FWD, DS_GRP_SecondaryStruts_AFT, DS_GRP_SecondaryStruts_PORT, DS_GRP_SecondaryStruts_STBD};
	static UINT meshgroup_Locks[4] = { DS_GRP_Downlock_FWD, DS_GRP_Downlock_AFT, DS_GRP_Downlock_PORT, DS_GRP_Downlock_STBD};
	static UINT meshgroup_Ladder = DS_GRP_Ladder;

	anim_Gear = CreateAnimation (1.0);

	for (int i = 0; i < 4; i++)
	{
		// Animation components
		mgt_Leg[i]		= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Legs[i][0], 3, LM_LegPivot[i], LM_LegAxis[i], (float) 45*RAD);		// Animate landing legs
		mgt_Strut[i]	= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Struts[i], 1, LM_StrutPivot[i], LM_LegAxis[i], (float)-63*RAD);		// Animate Support Struts attatched to legs
		mgt_Downlock[i]	= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Locks[i], 1, LM_DownlockPivot[i], LM_LegAxis[i], (float) 150*RAD);	// Animate Locking mechanism joining support struts to body

		// Add individual components to 'anim_Gear' 
		ach_GearLeg[i]		= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Leg[i]);
		ach_GearStrut[i]	= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Strut[i], ach_GearLeg[i]);
		ach_GearLock[i]		= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Downlock[i], ach_GearStrut[i]);
	}

	static MGROUP_ROTATE mgt_Ladder (mesh_Descent, &meshgroup_Ladder, 1, LM_LegPivot[0], LM_LegAxis[0], (float) 45*RAD); // Apply front leg's animation state to ladder.
	AddAnimationComponent (anim_Gear, 0.0, 1, &mgt_Ladder);

} // End "LM::DefineAnimations"

// --------------------------------------------------------------
// Define local light sources
// --------------------------------------------------------------
void LM::DefineLighting (void)
{

} // End "LM::DefineLighting"

// --------------------------------------------------------------
// Define exhaust and particle streams
// --------------------------------------------------------------
void LM::DefineParticleStreams (void)
{

} // End "LM::DefineParticleStreams"

// --------------------------------------------------------------
// Define sound effects
// --------------------------------------------------------------
void LM::DefineSounds (void)
{

} // End "LM::DefineSounds"

// ==============================================================
// Overloaded callback functions
// ==============================================================

// --------------------------------------------------------------
// Set the capabilities of the vessel class
// --------------------------------------------------------------
void LM::clbkSetClassCaps (FILEHANDLE cfg)
{
	// Read dificulty modifiers from config file
	oapiReadItem_float (cfg, "THRUST_MODIFIER", thrust_mod);			// Max thrust multiplier 
	oapiReadItem_float (cfg, "DV_MODIFIER", dv_mod);					// Exhaust velocity multiplier
	oapiReadItem_float (cfg, "CONSUMABLES_MODIFIER", consumables_mod);	// Consumables consumption rate (1 = 100% of historical rate)

	// Physical vessel parameters
	SetSize (LM_SIZE);
	SetEmptyMass (LM_ASC_EMPTYMASS + LM_DES_EMPTYMASS);
	SetPMI (LM_PMI);
	SetCrossSections (LM_CS);
	SetRotDrag (LM_RD);
	SetTouchdownPoints (LM_TDP[0], LM_TDP[1], LM_TDP[2]);

	// Docking port definition
	SetDockParams (LM_DOCK_POS, LM_DOCK_DIR, LM_DOCK_ROT);
	
	// Attachment point definition
	CreateAttachment (true, _V( 0.0, 0.4, 0.0), _V( 0, 1, 0), _V( 0, 0, 1), "X");	// for attachment to launch vehicle

	// Propellant resources
	ph_rcsA			= CreatePropellantResource (LM_RCS_FUELMASS/2);		// RCS propellant tank 'A'
	ph_rcsB			= CreatePropellantResource (LM_RCS_FUELMASS/2);		// RCS propellant tank 'B'
	ph_ascentFuel	= CreatePropellantResource (LM_ASC_FUELMASS);		// Ascent stage propellant tank
	ph_ascentO2		= CreatePropellantResource (LM_ASC_O2MASS);			// Ascent stage O2 tank
	ph_ascentH2O	= CreatePropellantResource (LM_ASC_WATERMASS);		// Ascent stage water tank

	ph_descentFuel	= CreatePropellantResource (LM_DES_FUELMASS);		// Descent stage propellant tank
	ph_descentO2	= CreatePropellantResource (LM_DES_O2MASS);			// Descent stage O2 tank
	ph_descentH2O	= CreatePropellantResource (LM_DES_WATERMASS);		// Descent stage water tank

	// RCS engines: Quadrant 1 (Port Fwd)
	th_rcsA[0]	= CreateThruster (LM_RCS_ENGINEPOS[0], _V( 0,-1, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Down
	th_rcsA[1]	= CreateThruster (LM_RCS_ENGINEPOS[1], _V( 0, 0,-1), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Back
	th_rcsB[0]	= CreateThruster (LM_RCS_ENGINEPOS[0], _V( 0, 1, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Up
	th_rcsB[1]	= CreateThruster (LM_RCS_ENGINEPOS[1], _V( 1, 0, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Right

	// RCS engines: Quadrant 2 (Port Aft)
	th_rcsA[2]	= CreateThruster (LM_RCS_ENGINEPOS[2], _V( 0, 1, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Up
	th_rcsA[3]	= CreateThruster (LM_RCS_ENGINEPOS[3], _V( 0, 0, 1), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Fwd
	th_rcsB[2]	= CreateThruster (LM_RCS_ENGINEPOS[2], _V( 0,-1, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Down 
	th_rcsB[3]	= CreateThruster (LM_RCS_ENGINEPOS[3], _V( 1, 0, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Right

	// RCS engines: Quadrant 3 (Stbd Aft)
	th_rcsA[4]	= CreateThruster (LM_RCS_ENGINEPOS[4], _V( 0,-1, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Down
	th_rcsA[5]	= CreateThruster (LM_RCS_ENGINEPOS[5], _V(-1, 0, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Left
	th_rcsB[4]	= CreateThruster (LM_RCS_ENGINEPOS[4], _V( 0, 1, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Up
	th_rcsB[5]	= CreateThruster (LM_RCS_ENGINEPOS[5], _V( 0, 0, 1), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Fwd

	// RCS engines: Quadrant 4 (Stbd Fwd)
	th_rcsA[6]	= CreateThruster (LM_RCS_ENGINEPOS[6], _V( 0, 1, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Up
	th_rcsA[7]	= CreateThruster (LM_RCS_ENGINEPOS[7], _V(-1, 0, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Left
	th_rcsB[6]	= CreateThruster (LM_RCS_ENGINEPOS[6], _V( 0,-1, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Down
	th_rcsB[7]	= CreateThruster (LM_RCS_ENGINEPOS[7], _V( 0, 0,-1), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Back

	// Ascent engine
	th_ascent	= CreateThruster (LM_ASC_ENGINEPOS, _V( 0, 1, 0), LM_ASC_MAXTHRUST*thrust_mod, ph_ascentFuel, LM_ASC_ISP*dv_mod);

	// Descent engine
	th_descent	= CreateThruster (LM_DES_ENGINEPOS, _V( 0, 1, 0), LM_DES_MAXTHRUST*thrust_mod, ph_descentFuel, LM_DES_ISP*dv_mod);	
	
	// Camera parameters
	SetCameraOffset (_V( 0.0, 2.1, 0.0));

	// Associate meshes for the visual
	mesh_Descent	= AddMesh (mh_descent, &LM_DES_OFFSET);	// Descent stage mesh
	mesh_Ascent		= AddMesh (mh_ascent, &LM_ASC_OFFSET);	// Ascent stage mesh

	SetMeshVisibilityMode (mesh_Descent, MESHVIS_ALWAYS);	// I want the the descent stage's legs to be visible from the cockpit, so I've set the mesh_Descent to be always visible. By default a vessel's own meshes are only visible when in an external view.
} // End "LM::clbkSetClassCaps"

// --------------------------------------------------------------
// Read status from scenario file
// --------------------------------------------------------------
void LM::clbkLoadStateEx (FILEHANDLE scn, void *status)
{
	int i = 0;
	char *cbuf; 

	while (oapiReadScenario_nextline (scn, cbuf)) 
	{
		// Load Vessel State
		if (!_strnicmp( cbuf, "STATE", 5))  // find line labeled "STATE" in scn file
		{
			sscanf(cbuf+5, "%i", &VesselStatus);	// Read value stored there to VesselStatus
		}

		// Load animation states
        else if (!_strnicmp (cbuf, "HATCH", 5)) // find line labeled "HATCH" in scn file
		{
			sscanf (cbuf+5, "%i %lf", &HatchStatus, &hatch_proc);	// Read values stored there to HatchStatus and hatch_proc
			SetAnimation (anim_Hatch, hatch_proc);					// Apply process value to animation.
		}		
		else if (!_strnicmp (cbuf, "GEAR", 4)) // find line labeled "GEAR" in scn file
		{
			sscanf (cbuf+4, "%i %lf", &GearStatus, &gear_proc);		// Read values stored there to GearStatus and gear_proc
			SetAnimation (anim_Gear, gear_proc);					// Apply process value to animation.
		}

		// Load default parameters
		else ParseScenarioLineEx (cbuf, status);	

	} // End "while (oapiReadScenario_nextline (scn, cbuf))"

} // End "LM::clbkLoadStateEx"

// --------------------------------------------------------------
// Write status to scenario file
// --------------------------------------------------------------
void LM::clbkSaveState (FILEHANDLE scn)
{
	int i = 0;
	char cbuf[256];
	
	// Write default vessel parameters to scenario file
	VESSEL3::clbkSaveState (scn);

	// Write custom parameters to scenario file
	sprintf (cbuf, "%i", VesselStatus);						// Vessel status
	oapiWriteScenario_string (scn, "STATE", cbuf);

	sprintf (cbuf, "%i %0.4f", HatchStatus, hatch_proc);	// EVA Hatch status and animation state
	oapiWriteScenario_string (scn, "HATCH", cbuf);

	sprintf (cbuf, "%i %0.4f", GearStatus, gear_proc);		// Landing Gear status and animation state
	oapiWriteScenario_string (scn, "GEAR", cbuf);

} // End "LM::clbkSaveState"

// --------------------------------------------------------------
// Finalise vessel creation
// --------------------------------------------------------------
void LM::clbkPostCreation ()
{
	DefineAnimations ();
	DefineLighting ();
	DefineParticleStreams ();
	DefineSounds ();

} // End "LM::clbkPostCreation"

// --------------------------------------------------------------
// Manage Animations and Post-Step processes
// --------------------------------------------------------------
void LM::clbkPostStep (double simt, double simdt, double mjd)
{
	// Debuggery
	//sprintf (oapiDebugString(), "Vessel Status %0.0f", (float)VesselStatus);

	// EVA hatch control logic
	if (hatch_proc < 0)			// If process value is less than 0...
	{
		HatchStatus = CLOSED;	// ...set status to "CLOSED"
		hatch_proc = 0;			// and process value to 0
	}

	else if (hatch_proc > 1)	// If process value is greater than 1...
	{
		HatchStatus = OPEN;		// ...set status to "OPEN"
		hatch_proc = 1;			// and process value to 1
	}

	if (HatchStatus > CLOSED)	
	{
		double	delta = simdt / 3;	

		if (HatchStatus == OPENING)				// if Status equals "OPENING"...
		{
			hatch_proc += delta;				// ...add delta to process value
		}
		
		if (HatchStatus == CLOSING)				// if Status equals "CLOSING"...
		{
			hatch_proc -= delta;				// ...subtract it.
		}

		SetAnimation (anim_Hatch, hatch_proc);	// Apply process value to animation.
	}

	// Debuggery
	//sprintf (oapiDebugString (), "Hatch Status %0.0f, hatch_proc %0.3f", (float)HatchStatus, hatch_proc);

	// Landing Gear control logic
	if (gear_proc < 0)			// If process value is less than 0...
	{
		GearStatus = CLOSED;	// ...set status to "CLOSED"
		gear_proc = 0;			// and process value to 0
	}

	else if (gear_proc > 1)		// If process value is greater than 1...
	{
		GearStatus = OPEN;		// ...set status to "OPEN"
		gear_proc = 1;			// and process value to 1
	}

	if (GearStatus > CLOSED)	
	{
		double	delta = simdt / 0.3;	// delta = simdt divided by duration of animation in seconds

		if (GearStatus == OPENING)		// if Status equals "OPENING"...
		{
			gear_proc += delta;			// ...add delta to process value
		}
		
		if (GearStatus == CLOSING)		// if Status equals "CLOSING"...
		{
			gear_proc -= delta;			// ...subtract it.
		}

		SetAnimation( anim_Gear, gear_proc);	// apply process value to animation
	}

	// Debuggery
	sprintf (oapiDebugString (), "Gear Status %0.0f, gear_proc %0.3f", (float)GearStatus, gear_proc);

} // End "LM::clbkPostStep"

// --------------------------------------------------------------
// Process keyboard inputs
// --------------------------------------------------------------
int  LM::clbkConsumeBufferedKey (DWORD key, bool down, char *kstate)
{
	// Open hatch when [K] is pressed.
	if (key == OAPI_KEY_K  && down && !KEYMOD_SHIFT(kstate) && !KEYMOD_CONTROL (kstate) && !KEYMOD_ALT(kstate)) // [K] is down, no [shift], no [ctrl], no [alt]
	{
		if (HatchStatus == CLOSED) HatchStatus = OPENING;	// If the hatch is closed, open it
		else HatchStatus = CLOSING;							// If not, close it 
		return 1;											// return '1' to indicate that the input has been processed
	}

	// Deploy Ganding Gear when [G] is pressed.
	if (key == OAPI_KEY_G  && down && !KEYMOD_SHIFT(kstate) && !KEYMOD_CONTROL (kstate) && !KEYMOD_ALT(kstate)) // [K] is down, no [shift], no [ctrl], no [alt]
	{
		if (GearStatus != OPEN) GearStatus = OPENING;		// If landing gear have not been deployed, deploy them
		return 1;
	}

	return 0;	// if no keys are pressed the function returns '0' and nothing happens

} // End "LM::clbkConsumeBufferedKey"

// ==============================================================
// API callback interface
// ==============================================================

// --------------------------------------------------------------
// Vessel initialisation
// --------------------------------------------------------------
DLLCLBK VESSEL *ovcInit (OBJHANDLE hvessel, int flightmodel)
{
	return new LM (hvessel, flightmodel);
}

// --------------------------------------------------------------
// Vessel cleanup
// --------------------------------------------------------------
DLLCLBK void ovcExit (VESSEL *vessel)
{
	if (vessel) delete (LM*)vessel;
}

NOTE: VesselStatus enumerator will be subject of part 10.
 
Top