Project Warhead module (collaboration project)

MJR

C++ developer in the mix
Addon Developer
Tutorial Publisher
Donator
Joined
Mar 19, 2008
Messages
2,460
Reaction score
5
Points
0
Location
United States
I was wondering if anyone is interested or at least willing to help me with the development of a custom .dll module of a warhead. I have seen others do it in SDK, but it way to confusing considering how much stuff their header file implies. I want something unique that could stand above other designs.

Also, how does Orbiter know how to "Explode" and things like that. Is it in the API?

All I really need help with is making that one module and animating an explosion with maybe some of effects. It is really lame seeing my mesh flying at 5k/s and hitting the ground uncontrollably doing nothing. It would be way more playable if it was as interactive as something like the Project R-7 warhead missions. Plus, I do not want to copy someones code and act like it is mine because there is no satisfaction in that.
 
Last edited:

Wishbone

Clueless developer
Addon Developer
Joined
Sep 12, 2010
Messages
2,421
Reaction score
1
Points
0
Location
Moscow
Two things any warhead (more or less) needs: zero-AoA re-entry and spin-stabilization. Small aft air control surface will suffice, but without zero-AoA the RV is kinda useless. I'm leafing through AeroBrake MFD source code to try to predict impact point before separation.
 

MJR

C++ developer in the mix
Addon Developer
Tutorial Publisher
Donator
Joined
Mar 19, 2008
Messages
2,460
Reaction score
5
Points
0
Location
United States
Most definitively. I need to add that to my code. My main thing as of now is the explosion animation. Maybe after that make the blast affect meshes around it and etc. If you got anymore tips like that do tell. :thumbup:
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,162
Reaction score
1,283
Points
203
Location
between the planets
To trigger the explosion, I think you'll be best off checking for altitude, and detonate it as soon as you've gone beneath a trigger altitude.
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
36,748
Reaction score
1,410
Points
203
Location
Langendernbach
To trigger the explosion, I think you'll be best off checking for altitude, and detonate it as soon as you've gone beneath a trigger altitude.

As well as arm the warhead on vehicle computer command.
 

RisingFury

OBSP developer
Addon Developer
Joined
Aug 15, 2008
Messages
6,377
Reaction score
384
Points
173
Location
Among bits and Bytes...
To trigger the explosion, I think you'll be best off checking for altitude, and detonate it as soon as you've gone beneath a trigger altitude.


That was how escapetomsfate did it in OBSP at first, but it's not 100% reliable. Now we check for ground contact of touchdown points or something like that...
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,162
Reaction score
1,283
Points
203
Location
between the planets
That was how escapetomsfate did it in OBSP at first, but it's not 100% reliable. Now we check for ground contact of touchdown points or something like that...

Interesting... I supposed that altitude checking should be more reliable, because it's a less momentual event and has greater chances of being detected, while ground contact at high speeds can be rather momentarily (with all that jumping back to solar orbit and stuff). Seems like I was wrong.
 

MJR

C++ developer in the mix
Addon Developer
Tutorial Publisher
Donator
Joined
Mar 19, 2008
Messages
2,460
Reaction score
5
Points
0
Location
United States
My question is though, how does Orbiter know how to do those types of commands? I understand a lot of the Orbiter related code, but do I just put something like void MIRV: Detonate()......
 

RisingFury

OBSP developer
Addon Developer
Joined
Aug 15, 2008
Messages
6,377
Reaction score
384
Points
173
Location
Among bits and Bytes...
Interesting... I supposed that altitude checking should be more reliable, because it's a less momentual event and has greater chances of being detected, while ground contact at high speeds can be rather momentarily (with all that jumping back to solar orbit and stuff). Seems like I was wrong.

It might work well for a single model, but in our case you need to consider that we're planning to allow weapons to be added through SC3 config style. That means you can have events where the ground contact points would always force the vessel to be above a detonation altitude.



My question is though, how does Orbiter know how to do those types of commands? I understand a lot of the Orbiter related code, but do I just put something like void MIRV: Detonate()......

Ok, let's say you have a vessel:
Code:
class MyVessel : public VESSEL3

That allows you to add your own function calls into MyVessel:

Code:
class MyVessel : public VESSEL3
{
public:
MyVessel() {Active = false};
~MyVessel();

void PreStep(double SimTime, double SimDeltaTime, double MJD);

private:
void Detonate();    // You don't want someone else to detonate you, right? ;)
bool Active;        // You want some sort of on-off switch, so you don't detonate before you get off the ground.

So, you've declared your functions.

Now, let's say you go for altitude based trigger for detonation, then you'd define your PreStep like this:

Code:
void MyVessel::PreStep(double SimTime, double SimDeltaTime, double MJD)
{
// First off you figure out if you've *ever* left the ground.
// You don't wanna detonate before you launch.

if (Active == false)
{
// You haven't activated your warhead yet.

if (GetAltitude() > 10)
{
// Alright, you've left the ground, time to activate your warhead.

Active = true;
}
}

// Now check if check if you've come under 10 meters, so you can detonate.

if ((Active == true) && (GetAltitude() < 10))
{
// Your warhead has been activated and you're now under 10 meters in altitude.
// Time to detonate.

Detonate();
}
}


That gives you a simple system that activates your warhead after launch, when it gets over 10 meters in altitude and then detonates it once it's active and under 10 meters. Once the conditions for detonation are met, the function Detonate() is called. Now you need to define that. I can't define the function in C++, but I will give you some comment hints on how I'd do it:

Code:
void MyVessel::Detonate()
{
// First thing you do is delete the existing mesh.
// Then define an exhaust stream with the exhaust that looks like an explosion. Delete it later, once the explosion is done.
// Also define an exhaust stream that acts as smoke from the crash site.
// Go into VESSELSTATUS and set status to inactive and latitude and longitute to where the vessel is right now, so your explosion won't move around...
// Play a sound...
// Anything you think would be cool...
}

I hope that gave you a general idea :)
 

MJR

C++ developer in the mix
Addon Developer
Tutorial Publisher
Donator
Joined
Mar 19, 2008
Messages
2,460
Reaction score
5
Points
0
Location
United States
It might work well for a single model, but in our case you need to consider that we're planning to allow weapons to be added through SC3 config style. That means you can have events where the ground contact points would always force the vessel to be above a detonation altitude.





Ok, let's say you have a vessel:
Code:
class MyVessel : public VESSEL3
That allows you to add your own function calls into MyVessel:

Code:
class MyVessel : public VESSEL3
{
public:
MyVessel() {Active = false};
~MyVessel();

void PreStep(double SimTime, double SimDeltaTime, double MJD);

private:
void Detonate();    // You don't want someone else to detonate you, right? ;)
bool Active;        // You want some sort of on-off switch, so you don't detonate before you get off the ground.
So, you've declared your functions.

Now, let's say you go for altitude based trigger for detonation, then you'd define your PreStep like this:

Code:
void MyVessel::PreStep(double SimTime, double SimDeltaTime, double MJD)
{
// First off you figure out if you've *ever* left the ground.
// You don't wanna detonate before you launch.

if (Active == false)
{
// You haven't activated your warhead yet.

if (GetAltitude() > 10)
{
// Alright, you've left the ground, time to activate your warhead.

Active = true;
}
}

// Now check if check if you've come under 10 meters, so you can detonate.

if ((Active == true) && (GetAltitude() < 10))
{
// Your warhead has been activated and you're now under 10 meters in altitude.
// Time to detonate.

Detonate();
}
}
That gives you a simple system that activates your warhead after launch, when it gets over 10 meters in altitude and then detonates it once it's active and under 10 meters. Once the conditions for detonation are met, the function Detonate() is called. Now you need to define that. I can't define the function in C++, but I will give you some comment hints on how I'd do it:

Code:
void MyVessel::Detonate()
{
// First thing you do is delete the existing mesh.
// Then define an exhaust stream with the exhaust that looks like an explosion. Delete it later, once the explosion is done.
// Also define an exhaust stream that acts as smoke from the crash site.
// Go into VESSELSTATUS and set status to inactive and latitude and longitute to where the vessel is right now, so your explosion won't move around...
// Play a sound...
// Anything you think would be cool...
}
I hope that gave you a general idea :)
That was probably one of the most helpful things ever. Thank you. I think I can get it from here now. :cheers:

---------- Post added at 10:47 AM ---------- Previous post was at 10:25 AM ----------

Ok. This is not my final code whatsoever. I am posting it to see if I am getting the concept. Here it is.

Code:
#include "Warhead.h"

class MIRV : public VESSEL3
{
public:
MIRV() {Active = false};
~MIRV();

void PreStep(double SimTime, double SimDeltaTime, double MJD);

private:
void Detonate();    // You don't want someone else to detonate you, right? ;)
bool Active;        // You want some sort of on-off switch, so you don't detonate before you get off the ground.

void MIRV::PreStep(double SimTime, double SimDeltaTime, double MJD)
{
// First off you figure out if you've *ever* left the ground.
// You don't wanna detonate before you launch.

if (Active == false)
{
// You haven't activated your warhead yet.

if (GetAltitude() > 10)
{
// Alright, you've left the ground, time to activate your warhead.

Active = true;
}
}

// Now check if check if you've come under 10 meters, so you can detonate.

if ((Active == true) && (GetAltitude() < 300))
{
// Your warhead has been activated and you're now under 10 meters in altitude.
// Time to detonate.

Detonate();
}
}

void MIRV::Detonate()
{
// lose the bomb mesh and load the fireball mesh
  ClearMeshes();
  AddMesh(Cloud);
////EXAMPLE OF CLOUD??? (From TRINITY code)
   uranium = CreatePropellantResource (1e6);
   PARTICLESTREAMSPEC radio_cloud = 
    {
        0, 1400.0, 10, 1.0, 0.3, 75.0, 40.0, 3.0, PARTICLESTREAMSPEC::DIFFUSE,
        PARTICLESTREAMSPEC::LVL_PSQRT, 0, 2,
        PARTICLESTREAMSPEC::ATM_PLOG, 1e-4, 1
    };

   PARTICLESTREAMSPEC glow_cloud = 
    {
        0, 1200.0, 60, 1.0, 0.3, 90.0, 40.0, 3.0, PARTICLESTREAMSPEC::EMISSIVE,
        PARTICLESTREAMSPEC::LVL_PSQRT, 0, 2,
        PARTICLESTREAMSPEC::ATM_FLAT, 1e-4, 1
    };
   thfission[0] = CreateThruster (_V(0.0,  0.0,  10.5), _V(0,0,1), 0.0001, uranium,4e4,4e4);
    thgfission = CreateThrusterGroup (thfission, 1, THGROUP_MAIN);

    diffuse_fire=AddExhaustStream (thfission[0], _V(0.0,  0.0,  10.5), &radio_cloud);
    emissive_fire=AddExhaustStream (thfission[0], _V(0.0,  0.0,  10.5), &glow_cloud);
    SetThrusterGroupLevel(THGROUP_MAIN,1.0);
}

// Go into VESSELSTATUS and set status to inactive and latitude and longitute to where the vessel is right now, so your explosion won't move around...
// Play a sound...
// Anything you think would be cool...
}


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

// Cleanup
DLLCLBK void ovcExit (VESSEL *vessel)
{
    if (vessel) 
        delete (MIRV*)vessel;
}

Of course I got add the vessel properties and define the meshes.
 
Last edited:

RisingFury

OBSP developer
Addon Developer
Joined
Aug 15, 2008
Messages
6,377
Reaction score
384
Points
173
Location
Among bits and Bytes...
Yea, as far as I can see, you're getting it right. :thumbup:

One thing you might wanna add, that I didn't think of before:
Currently you have it set up so that in every frame that you're under 10 meters and active warhead, it'll call detonation function. That's a bad thing, cos it'll add new thruster exhaust every frame. You might wanna add another private variable called bool Detonated, which you initialize as false and then before calling Detonate, you check if Detonated is false. Inside the Detonate function, you add Detonated = true.

That'll prevent multiple detonations :)
 

MJR

C++ developer in the mix
Addon Developer
Tutorial Publisher
Donator
Joined
Mar 19, 2008
Messages
2,460
Reaction score
5
Points
0
Location
United States
Yea, as far as I can see, you're getting it right. :thumbup:

One thing you might wanna add, that I didn't think of before:
Currently you have it set up so that in every frame that you're under 10 meters and active warhead, it'll call detonation function. That's a bad thing, cos it'll add new thruster exhaust every frame. You might wanna add another private variable called bool Detonated, which you initialize as false and then before calling Detonate, you check if Detonated is false. Inside the Detonate function, you add Detonated = true.

That'll prevent multiple detonations :)
Would I need a header file for this? I am thinking so. BTW, how would define 'ACTIVE'?
 
Last edited:

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
36,748
Reaction score
1,410
Points
203
Location
Langendernbach
If I may suggest something: Spawn a new vessel for the explosion, make it a separate effect. After creating the explosion, self-delete the warhead. This prevents the need to check for only one explosion.
 

RisingFury

OBSP developer
Addon Developer
Joined
Aug 15, 2008
Messages
6,377
Reaction score
384
Points
173
Location
Among bits and Bytes...
If I may suggest something: Spawn a new vessel for the explosion, make it a separate effect. After creating the explosion, self-delete the warhead. This prevents the need to check for only one explosion.

Is it cheaper? Spawning a new vessel can cause some momentary lag...


Would I need a header file for this? I am thinking so. BTW, how would define 'ACTIVE'?

You don't *need* a header file for it, but it does make things look better if you use it. You'd usually break up your code into two files:
MyVessel.h and MyVessel.cpp

MyVessel.h would look like this:

Code:
#ifndef MYVESSEL_H
#define MYVESSEL_H

#include "Orbitersdk.h"
// Put your includes here.

class MyVessel : public VESSEL3
{
public:
      MyVessel();
      ~MyVessel();

      void PreStep(double SimTime, double SimDeltaTime, double MJD);

private:
      bool Active;
      bool Detonated;
      
      void Detonate();
};

#endif

If you're using a VisualC++ compiler, you can use #pragma once instead of #ifndef structure, but I prefer it this way.

The header file is basically a declaration of code elements. As an analogy, think of it as a list of things you'll be using when making your dinner. You basically "reserve" the names. Although a header file is usually used to reserve names, it's generally split away from definitions. Definitions are done in the MyVessel.cpp file:

Code:
#include "MyVessel.h
// Need to include the header, of course ;)

MyVessel::MyVessel()
{
      // Constructor.

      Active = false;
      Detonated = false;
}

void MyVessel::PreStep(double SimTime, double SimDeltaTime, double MJD)
{
      if ((Active == false) && (GetAltitude() > 10))
      {
            // Set warhead to active only after it leaves the ground.
            // This will prevent detonation prior to liftoff.

            Active = true;
      }

      if ((Active == true) && (Detonated == false) && (GetAltitude() < 10))
      {
            // Check that the warhead is below 10 meters, active and undetonated.
            // Detonate if the conditions are met.

            Detonate();
      }
}

void MyVessel::Detonate()
{
      // Set Detonate to true, because the warhead just detonated.

      Detonated = true;

      // Clear mesh, define exhausts, play sound,...
}

MyVessel::~MyVessel()
{
      // Destructor.
}



The reason bool Active and bool Detonated are there is to prevent multiple detonations and to prevent detonations when the warhead is on the launchpad, waiting for liftoff (because it's below 10 meters, right? ;) )

You can think of the variable Active as having the warhead safety on or off. The other one just makes sure that the next time PreStep gets called, Detonation() won't get called all over again and executing the code inside it.
 

MJR

C++ developer in the mix
Addon Developer
Tutorial Publisher
Donator
Joined
Mar 19, 2008
Messages
2,460
Reaction score
5
Points
0
Location
United States
How does this look?
Code:
#include "Warhead.h"
#include "Orbitersdk.h"
// Need to include the header, of course ;)

Warhead::Warhead()
{
      // Constructor.

      Active = false;
      Detonated = false;
}

void Warhead::PreStep(double SimTime, double SimDeltaTime, double MJD)
{
      if ((Active == false) && (GetAltitude() > 10))
      {
            // Set warhead to active only after it leaves the ground.
            // This will prevent detonation prior to liftoff.

            Active = true;
      }

      if ((Active == true) && (Detonated == false) && (GetAltitude() < 10))
      {
            // Check that the warhead is below 10 meters, active and undetonated.
            // Detonate if the conditions are met.

            Detonate();
      }
}

void Warhead::Detonate()
{
      // Set Detonate to true, because the warhead just detonated.

      Detonated = true;

      // Clear mesh, define exhausts, play sound,...
    ClearMeshes();
    
}

void Warhead::clbkSetClassCaps(FILEHANDLE cfg)
{
    // physics and geometry
    SetSize(19.0);
    SetEmptyMass(3915);
    SetCrossSections(_V(36.0, 36.0, 6.0));                // S=pi*D^2/4. m^2
    SetPMI(_V(24.5, 24.5, 0.7));
    SetCW (0.5, 1.0, 2.0, 2.0);                            
    SetRotDrag (_V(6.7, 6.7, 0.1));                
    SetLiftCoeffFunc(0);
    SetPitchMomentScale(1e-4);
    SetBankMomentScale(1e-4);
    SetSurfaceFrictionCoeff(1e5, 1e5);
    SetTouchdownPoints (_V( 0, 0, 10), _V( 2, 1, -10), _V(-2, 2, -10));

    // fuel tank
    MainTank = CreatePropellantResource(38300);


Warhead::~Warhead()
{
      // Destructor.
    
}



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

// Cleanup
DLLCLBK void ovcExit (VESSEL *vessel)
{
    if (vessel) 
        delete (Warhead*)vessel;
}

The .h file....
Code:
#ifndef Warhead_H
#define Warhead_H

#include "Orbitersdk.h"
// Put your includes here.

class Warhead : public VESSEL3
{
public:
      Warhead(OBJHANDLE, int);
      

      void PreStep(double SimTime, double SimDeltaTime, double MJD);
      void clbkSetClassCaps(FILEHANDLE);
      void clbkPreStep(double, double, double);

private:
      bool Active;
      bool Detonated;
      
      void Detonate();

};

#endif
 

RisingFury

OBSP developer
Addon Developer
Joined
Aug 15, 2008
Messages
6,377
Reaction score
384
Points
173
Location
Among bits and Bytes...
Actually, I made a terrible mistake of calling clbkPreStep just PreStep. No need to have both of them in, just implement the functionality in clbkPreStep ;)

I'm just used to my version cos I defined it in a non-VESSEL3 derived class used for the OBSP AI...
 

Xyon

Puts the Fun in Dysfunctional
Administrator
Moderator
Addon Developer
Webmaster
GFX Staff
Donator
Beta Tester
Joined
Aug 9, 2009
Messages
6,804
Reaction score
505
Points
203
Location
10.0.0.1
Website
www.orbiter-radio.co.uk
Preferred Pronouns
she/her
Some confusion with that callback...
Code:
clbkPreStep (double simt, double simdt, double mjd);

Should work better than the "double, double, double" you have there.
 

RisingFury

OBSP developer
Addon Developer
Joined
Aug 15, 2008
Messages
6,377
Reaction score
384
Points
173
Location
Among bits and Bytes...
Some confusion with that callback...
Code:
clbkPreStep (double simt, double simdt, double mjd);
Should work better than the "double, double, double" you have there.


Just to explain Xyon's point (no offense there Xyon):

When declaring function members, you don't need to name the variables, but you do have to name them when defining. This code:
Code:
void oapiPreStep(double, double, double);
will compile and work fine, but it's generally a good idea to type out the names to avoid confusion.

Another trick when defining function members is the use of default values.

If you declare a function member like this:
Code:
void FooBar(double MyDouble, int MyInt = 1);
then you don't need to pass a value to the second parameter of FooBar when calling it. In it's place, the value of 1 will be assumed. Of course, if you want to override the default value, you can. Both of these will work:
Code:
FooBar(1.5);
FooBar(1.5, 2);
 

MJR

C++ developer in the mix
Addon Developer
Tutorial Publisher
Donator
Joined
Mar 19, 2008
Messages
2,460
Reaction score
5
Points
0
Location
United States
I am still tinkering with it although it still keeps saying that I didn't define Detonate, GetAltitude, and that stuff. Here are both files. The first one is the .h and the second is the .cpp.
Code:
#ifndef __WARHEAD_H
#define __WARHEAD_H

#define STRICT
#include "Orbitersdk.h"
// Put your includes here.

class Warhead : public VESSEL3
{
public:
      Warhead();
      ~Warhead();

      void clbkPreStep(double SimTime, double SimDeltaTime, double MJD);

private:
      bool Active;
      bool Detonated;
      
      void Detonate();
};

#endif

Code:
#include "Warhead.h"
#include "Orbitersdk.h"
// Need to include the header, of course ;)

Warhead::Warhead()
{
      // Constructor.

      Active = false;
      Detonated = false;
}

void Warhead::clbkPreStep(double SimTime, double SimDeltaTime, double MJD)
{
      if ((Active == false) && (GetAltitude() > 10))
      {
            // Set warhead to active only after it leaves the ground.
            // This will prevent detonation prior to liftoff.

            Active = true;
      }

      if ((Active == true) && (Detonated == false) && (GetAltitude() < 10))
      {
            // Check that the warhead is below 10 meters, active and undetonated.
            // Detonate if the conditions are met.

            Detonate();
      }
}

void Warhead::Detonate()
{
      // Set Detonate to true, because the warhead just detonated.

      Detonated = true;

      // Clear mesh, define exhausts, play sound,...
    ClearMeshes();
    
}

void Warhead::clbkSetClassCaps(FILEHANDLE cfg)
{
    // physics and geometry
    SetSize(19.0);
    SetEmptyMass(3915);
    SetCrossSections(_V(36.0, 36.0, 6.0));                // S=pi*D^2/4. m^2
    SetPMI(_V(24.5, 24.5, 0.7));
    SetCW (0.5, 1.0, 2.0, 2.0);                            
    SetRotDrag (_V(6.7, 6.7, 0.1));                
    SetLiftCoeffFunc(0);
    SetPitchMomentScale(1e-4);
    SetBankMomentScale(1e-4);
    SetSurfaceFrictionCoeff(1e5, 1e5);
    SetTouchdownPoints (_V( 0, 0, 10), _V( 2, 1, -10), _V(-2, 2, -10));

    // fuel tank
    MainTank = CreatePropellantResource(38300);


Warhead::~Warhead()
{
      // Destructor.
    
}



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

// Cleanup
DLLCLBK void ovcExit (VESSEL *vessel)
{
    if (vessel) 
        delete (Warhead*)vessel;
}
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,162
Reaction score
1,283
Points
203
Location
between the planets
Well, you haven't defined GetAltitude, or is that a function of the VESSEL3 class? haven't coded any vessels yet...

Also, you're linking Orbiter.h two times. That could lead to trouble, although I don't think your current problem is related.
 
Top