//=========================================================
// 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;
void Drogue_MomentCoeff (double aoa,double M,double Re,double *cl,double *cm,double *cd0)
{
int i;
const int nlift = 9;
static const double AOA[nlift] = {-90*RAD,-85*RAD,-75*RAD,0*RAD,90*RAD,180*RAD,255*RAD,265*RAD,270*RAD};
static const double CM[nlift] = { 0, -0.01, -0.1, -0.2, 0, 0.2, 0.1, 0.01, 0};
static const double SCM[nlift] = {(CM[1]-CM[0])/(AOA[1]-AOA[0]), (CM[2]-CM[1])/(AOA[2]-AOA[1]),
(CM[3]-CM[2])/(AOA[3]-AOA[2]), (CM[4]-CM[3])/(AOA[4]-AOA[3]),
(CM[5]-CM[4])/(AOA[5]-AOA[4]), (CM[6]-CM[5])/(AOA[6]-AOA[5]),
(CM[7]-CM[6])/(AOA[7]-AOA[6]), (CM[8]-CM[7])/(AOA[8]-AOA[7])};
for (i = 0; i < nlift-1 && AOA[i+1] < aoa; i++);
*cl= 0;
*cm=CM[i] + (aoa-AOA[i])*SCM[i];
*cd0 = 0.020 + oapiGetWaveDrag (M, 0.75, 1.0, 1.1, 0.04);
}
void Parachute_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, 30*RAD,90*RAD,180*RAD};
static const double CL[nabsc] = { 0, 0 ,0.5, 1, 0.5, 0 , 0 };
static const double CM[nabsc] = { 0, 0, 0.0010, 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;
Chutes = 0;
Para = 0;
}
//=========================================================
// 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 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 = 1.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::ParaCheck ()
{
return Para;
}
double PhoenixASMN::AltitudeCheck ()
{
Altitude = GetAltitude ();
return Altitude;
}
double PhoenixASMN::VelocityCheck ()
{
Airspeed = GetAirspeed ();
return Airspeed;
}
double PhoenixASMN::Checkint ()
{
return SMJett;
}
double PhoenixASMN::SMJettison ()
{
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);
SetThrusterResource(th_rcsSM[0], NULL);
SetThrusterResource(th_rcsSM[1], NULL);
SetThrusterResource(th_rcsSM[2], NULL);
SetThrusterResource(th_rcsSM[3], NULL);
SetThrusterResource(th_rcsSM[4], NULL);
SetThrusterResource(th_rcsSM[5], NULL);
SetThrusterResource(th_rcsSM[6], NULL);
SetThrusterResource(th_rcsSM[7], NULL);
SetThrusterResource(th_rcsSM[8], NULL);
SetThrusterResource(th_rcsSM[9], NULL);
SetThrusterResource(th_rcsSM[10], NULL);
SetThrusterResource(th_rcsSM[11], NULL);
SetThrusterResource(th_rcsSM[12], NULL);
SetThrusterResource(th_rcsSM[13], NULL);
SetThrusterResource(th_rcsSM[14], NULL);
SetThrusterResource(th_rcsSM[15], NULL);
SetThrusterResource(th_main, NULL);
return 1;
}
void PhoenixASMN::RevertChutes (void)
{
ChuteStatus = ((ChuteStatus == OPENED || ChuteStatus == OPENING) ?
CLOSING : OPENING);
}
double PhoenixASMN::Chutesclose ()
{
ClearAirfoilDefinitions();
if (Chutes == 1)
{
RevertChutes();
strcpy(SendHudMessage(),"Splashdown! Chutes stowing");
}
else
{
strcpy(SendHudMessage(),"Opening parachutes highly recommended when falling towards water at high velocity");
}
return true;
}
double PhoenixASMN::Chutesopen ()
{
RevertChutes();
strcpy(SendHudMessage(),"Chutes open");
ClearAirfoilDefinitions();
CreateAirfoil (LIFT_VERTICAL, _V(0.0,0.0,22.0), Parachute_MomentCoeff, 0.04, 2600, 0.05);
return true;
}
double PhoenixASMN::MeshControl ()
{
ClearMeshes();
if (SMJett == 1)
{
SetMeshVisibilityMode (AddMesh (PhoenixCapsule = oapiLoadMeshGlobal ("PhoenixAoffset")), MESHVIS_ALWAYS);
SetMeshVisibilityMode (AddMesh (PhoenixCapsule = oapiLoadMeshGlobal ("PhoenixSMN MKII")), MESHVIS_ALWAYS);
SetMeshVisibilityMode (AddMesh (PhoenixCapsule = oapiLoadMeshGlobal ("PhoenixLES")), MESHVIS_ALWAYS);
return 1;
}
else
{
SetMeshVisibilityMode (AddMesh (PhoenixCapsule = oapiLoadMeshGlobal ("PhoenixA")), MESHVIS_ALWAYS);
if (Chutes == 1)
{
SetMeshVisibilityMode (AddMesh (PhoenixCapsule = oapiLoadMeshGlobal ("PhoenixChutesMkI")), 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_rcsSM[0], RCS5);
//SetThrusterResource(th_rcsSM[1], RCS5);
//SetThrusterResource(th_rcsSM[2], RCS5);
//SetThrusterResource(th_rcsSM[3], RCS5);
//SetThrusterResource(th_rcsSM[4], RCS6);
//SetThrusterResource(th_rcsSM[5], RCS6);
//SetThrusterResource(th_rcsSM[6], RCS6);
//SetThrusterResource(th_rcsSM[7], RCS6);
//SetThrusterResource(th_rcsSM[8], RCS7);
//SetThrusterResource(th_rcsSM[9], RCS7);
//SetThrusterResource(th_rcsSM[10], RCS7);
//SetThrusterResource(th_rcsSM[11], RCS7);
//SetThrusterResource(th_rcsSM[12], RCS8);
//SetThrusterResource(th_rcsSM[13], RCS8);
//SetThrusterResource(th_rcsSM[14], RCS8);
//SetThrusterResource(th_rcsSM[15], RCS8);
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);
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);
if (Chutes == 1)
{
SetPMI (EXP_PMI_AFTER_CHUTESOPEN);
SetSize (EXP_SIZE_AFTER_CHUTESOPEN);
SetCrossSections (EXP_CS_AFTER_CHUTESOPEN);
SetRotDrag(_V(5.5,5.5,10.3));
Para == 1;
//ClearVariableDragElements ();
//CreateVariableDragElement (&Para, 2600,_V(0,0,22));
}
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
//CreateVariableDragElement (&Para, 2600,_V(0,0,22));
// 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
SetCameraOffset (_V(0,1.707,6.753));
SetAlbedoRGB (_V(1,1,1));
MeshControl ();
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
// main engine
th_main = CreateThruster (_V(0,0,-10.61), _V(0,0,1), MAINTH, MainFuel, VACNTR_ISP, NMLNTR_ISP, P_NML);
CreateThrusterGroup (&th_main, 1, THGROUP_MAIN);
SURFHANDLE texmain = oapiRegisterExhaustTexture ("ShuttleDMainExhaust");
AddExhaust (th_main, 1.99, 0.80, _V(0,0,-10.61), _V(0,0,-4.1), texmain);
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.13,-10.61), &contrail_main);
AddExhaustStream (th_main, _V(0,0.13,-10.61), &exhaust_main);
// RCS engines
th_rcsSM[0] = CreateThruster (_V(-2.353,0.325, 0.357), _V(0,0,-1), RCSSMTH0, RCS5, VACRCS_ISP, NMLRCS_ISP, P_NML);//LEFT QUAD FORWARD
th_rcsSM[1] = CreateThruster (_V(-2.353,0.672, 0.000), _V(0,-1,0), RCSSMTH1, RCS5, VACRCS_ISP, NMLRCS_ISP, P_NML);//LEFT QUAD UP
th_rcsSM[2] = CreateThruster (_V(-2.353,0.000, 0.000), _V(0,1,0), RCSSMTH2, RCS5, VACRCS_ISP, NMLRCS_ISP, P_NML);//LEFT QUAD DOWN
th_rcsSM[3] = CreateThruster (_V(-2.353,0.325,-0.365), _V(0,0,1), RCSSMTH3, RCS5, VACRCS_ISP, NMLRCS_ISP, P_NML);//LEFT QUAD AFT
th_rcsSM[4] = CreateThruster (_V( 2.353,0.325, 0.357), _V(0,0,-1), RCSSMTH4, RCS6, VACRCS_ISP, NMLRCS_ISP, P_NML);//RIGHT QUAD FORWARD
th_rcsSM[5] = CreateThruster (_V( 2.353,0.672, 0.000), _V(0,-1,0), RCSSMTH5, RCS6, VACRCS_ISP, NMLRCS_ISP, P_NML);//RIGHT QUAD UP
th_rcsSM[6] = CreateThruster (_V( 2.353,0.000, 0.000), _V(0,1,0), RCSSMTH6, RCS6, VACRCS_ISP, NMLRCS_ISP, P_NML);//RIGHT DOWN
th_rcsSM[7] = CreateThruster (_V( 2.353,0.325,-0.365), _V(0,0,1), RCSSMTH7, RCS6, VACRCS_ISP, NMLRCS_ISP, P_NML);//RIGHT AFT
th_rcsSM[8] = CreateThruster (_V( 0.000,-2.272, 0.357), _V(0,0,-1), RCSSMTH8, RCS7, VACRCS_ISP, NMLRCS_ISP, P_NML);//BOTTOM QUAD FORWARD
th_rcsSM[9] = CreateThruster (_V( 0.000,-2.272,-0.336), _V(1,0,0), RCSSMTH9, RCS7, VACRCS_ISP, NMLRCS_ISP, P_NML);//BOTTOM QUAD LEFT
th_rcsSM[10] = CreateThruster(_V(-0.311,-2.272, 0.000), _V(-1,0,0), RCSSMTH10, RCS7, VACRCS_ISP, NMLRCS_ISP, P_NML);//BOTTOM QUAD RIGHT
th_rcsSM[11] = CreateThruster(_V( 0.311,-2.272, 0.000), _V(0,0,1), RCSSMTH11, RCS7, VACRCS_ISP, NMLRCS_ISP, P_NML);//BOTTOM AFT
th_rcsSM[12] = CreateThruster (_V( 0.000,2.259, 0.357), _V(0,0,-1), RCSSMTH12, RCS8, VACRCS_ISP, NMLRCS_ISP, P_NML);//TOP QUAD FORWARD
th_rcsSM[13] = CreateThruster (_V(0.325,2.281, 0.000), _V(1,0,0), RCSSMTH13, RCS8, VACRCS_ISP, NMLRCS_ISP, P_NML);//TOP QUAD LEFT
th_rcsSM[14] = CreateThruster(_V(-0.325,2.281, 0.000), _V(-1,0,0), RCSSMTH14, RCS8, VACRCS_ISP, NMLRCS_ISP, P_NML);//TOP QUAD RIGHT
th_rcsSM[15] = CreateThruster(_V( 0.000,2.259,-0.336), _V(0,0,1), RCSSMTH15, RCS8, VACRCS_ISP, NMLRCS_ISP, P_NML);//TOP AFT
SURFHANDLE texH2O2RCS = oapiRegisterExhaustTexture ("exhaust_atrcsShuttleD");
AddExhaust (th_rcsSM[0], 1.9, 0.278, _V(-2.353,0.325, 0.357), _V(0,0,1), texH2O2RCS);
AddExhaust (th_rcsSM[1], 1.9, 0.278, _V(-2.353,0.672, 0.000), _V(0,1,0), texH2O2RCS);
AddExhaust (th_rcsSM[2], 1.9, 0.278, _V(-2.353,0.000, 0.000), _V(0,-1,0), texH2O2RCS);
AddExhaust (th_rcsSM[3], 1.9, 0.278, _V(-2.353,0.325,-0.365), _V(0,0,-1), texH2O2RCS);
AddExhaust (th_rcsSM[4], 1.9, 0.278, _V( 2.353,0.325, 0.357), _V(0,0,1), texH2O2RCS);
AddExhaust (th_rcsSM[5], 1.9, 0.278, _V( 2.353,0.672, 0.000), _V(0,1,0), texH2O2RCS);
AddExhaust (th_rcsSM[6], 1.9, 0.278, _V( 2.353,0.000, 0.000), _V(0,-1,0), texH2O2RCS);
AddExhaust (th_rcsSM[7], 1.9, 0.278, _V( 2.353,0.325,-0.365), _V(0,0,-1), texH2O2RCS);
AddExhaust (th_rcsSM[8], 1.9, 0.278, _V( 0.000,-2.272, 0.357), _V(0,0,1), texH2O2RCS);
AddExhaust (th_rcsSM[9], 1.9, 0.278, _V(-0.311,-2.272,0.000), _V(-1,0,0), texH2O2RCS);
AddExhaust (th_rcsSM[10], 1.9, 0.278, _V( 0.311,-2.272,0.000), _V(1,0,0), texH2O2RCS);
AddExhaust (th_rcsSM[11], 1.9, 0.278, _V( 0.000,-2.272,-0.336), _V(0,0,-1), texH2O2RCS);
AddExhaust (th_rcsSM[12], 1.9, 0.278, _V( 0.000,2.259, 0.357), _V(0,0,1), texH2O2RCS);
AddExhaust (th_rcsSM[13], 1.9, 0.278, _V( -0.325,2.281, 0.000), _V(-1,0,0), texH2O2RCS);
AddExhaust (th_rcsSM[14], 1.9, 0.278, _V( 0.325,2.281, 0.000), _V(1,0,0), texH2O2RCS);
AddExhaust (th_rcsSM[15], 1.9, 0.278, _V( 0.000,2.259,-0.336), _V(0,0,-1), texH2O2RCS);
// 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
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];
th_group[2] = th_rcsSM[11];
th_group[3] = th_rcsSM[12];
CreateThrusterGroup (th_group, 4, THGROUP_ATT_PITCHUP);
th_group[0] = th_rcsCM[4];
th_group[1] = th_rcsCM[5];
th_group[2] = th_rcsSM[8];
th_group[3] = th_rcsSM[15];
CreateThrusterGroup (th_group, 4, THGROUP_ATT_PITCHDOWN);
th_group[0] = th_rcsCM[2];
th_group[1] = th_rcsCM[7];
th_group[2] = th_rcsSM[1];
th_group[3] = th_rcsSM[6];
th_group[4] = th_rcsSM[9];
th_group[5] = th_rcsSM[14];
CreateThrusterGroup (th_group, 6, THGROUP_ATT_BANKLEFT);
th_group[0] = th_rcsCM[3];
th_group[1] = th_rcsCM[6];
th_group[2] = th_rcsSM[2];
th_group[3] = th_rcsSM[5];
th_group[4] = th_rcsSM[10];
th_group[5] = th_rcsSM[13];
CreateThrusterGroup (th_group, 6, THGROUP_ATT_BANKRIGHT);
th_group[0] = th_rcsSM[2];
th_group[1] = th_rcsSM[6];
CreateThrusterGroup (th_group, 2, THGROUP_ATT_UP);
th_group[0] = th_rcsSM[1];
th_group[1] = th_rcsSM[5];
CreateThrusterGroup (th_group, 2, THGROUP_ATT_DOWN);
th_group[0] = th_rcsCM[10];
th_group[1] = th_rcsCM[11];
th_group[2] = th_rcsSM[0];
th_group[3] = th_rcsSM[7];
CreateThrusterGroup (th_group, 4, THGROUP_ATT_YAWLEFT);
th_group[0] = th_rcsCM[8];
th_group[1] = th_rcsCM[9];
th_group[2] = th_rcsSM[3];
th_group[3] = th_rcsSM[4];
CreateThrusterGroup (th_group, 4, THGROUP_ATT_YAWRIGHT);
th_group[0] = th_rcsCM[2];
th_group[1] = th_rcsCM[6];
th_group[2] = th_rcsSM[10];
th_group[3] = th_rcsSM[14];
CreateThrusterGroup (th_group, 4, THGROUP_ATT_LEFT);
th_group[0] = th_rcsCM[3];
th_group[1] = th_rcsCM[7];
th_group[2] = th_rcsSM[9];
th_group[3] = th_rcsSM[13];
CreateThrusterGroup (th_group, 4, THGROUP_ATT_RIGHT);
th_group[0] = th_rcsSM[3];
th_group[1] = th_rcsSM[7];
th_group[2] = th_rcsSM[11];
th_group[3] = th_rcsSM[15];
CreateThrusterGroup (th_group, 4, THGROUP_ATT_FORWARD);
th_group[0] = th_rcsSM[0];
th_group[1] = th_rcsSM[4];
th_group[2] = th_rcsSM[8];
th_group[3] = th_rcsSM[12];
CreateThrusterGroup (th_group, 4, 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", &Water);
}
if (!_strnicmp (line, "SMJett", 6)) {
sscanf (line+6, "%d", &SMJett);
}
if (!_strnicmp (line, "Chutes", 6)) {
sscanf (line+6, "%d", &Chutes);
}
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);
sprintf (cbuf, "%d", Chutes);
oapiWriteScenario_string (scn, "Chutes", 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 (ChuteStatus >= OPENING) { // For all of the complexity involved in writing it, all this does is to make sure the animation variable
double da = simdt * PARACHUTE_SPEED; // slides back & forth when asked to.
if (ChuteStatus == OPENING) {
if (Para > 0.0) Para = max (0.0, Para-da);
else ChuteStatus = OPENED;
} else {
if (Para < 1.0) Para = min (1.0, Para+da);
else ChuteStatus = STORED;
}
}
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");
}
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(SendCarg2HudMessage(),"Current velocity %.0f meters/second",
ParaCheck());
sprintf(SendCarg3HudMessage(),"Water reserves %.0f L, %.0f kg Oxygen remaining",
WaterCheck (),O2Check ());
}
if (simt<1)
{
MeshControl ();
ClassControl ();
}
if (Chutes == 1)
{
if(GroundContact()==TRUE)
{
if (Chutes == 1);
Chutes--;
MeshControl ();
ClassControl ();
Chutesclose ();
}
}
//ClearVariableDragElements ();
CreateVariableDragElement (&Para, 2600,_V(0,0,22));
Randomizer = (log(cos(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);
}
// THIS IS FOR ADDING CREW SEE PDF doc "Allow user to add crew to your ship
// without scenery editor"
if(key==OAPI_KEY_N&&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))
{
RevertChutes ();
}
//---------------------------------------------------------------------------
// Key "K" Deploy the parachutes
if(key==OAPI_KEY_K)
{
if (SMJett == 0)
{
if (Chutes == 0)
{
Chutes++;
Chutesopen ();
MeshControl ();
ClassControl ();
}
}
return 0;
}
//---------------------------------------------------------------------------
// 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!
//
//=========================================================