bbosen - 2020-06-05

From time to time, sophisticated missions that manage moving objects like ships will receive "MissionChronoMilestone" updates from the MissionCommander. These missions need to check for arrival of these messages frequently. Here is some sample source code for that task:

  //
  // Now we will check to determine whether the value of "MissionChronoMilestone" has advanced
  // since the last time we checked 100 milliseconds ago. To do this, we will compare
  // "MissionChronoMilestone" with "PriorChronoMilestone" as follows:
  //
  if (MissionChronoMilestone != PriorChronoMilestone)
     { // Get here within 100 milliseconds of receipt of a new Chrono Milestone message
     sprintf (SystemMessageBufferA, "NEW MISSIONCHRONOMILESTONE = %d", MissionChronoMilestone);
     NewSystemMessageNeedsScrolling = true;
     if (!(NetworkMode & 16))
        {
        popen ("espeak -p 1  -s 140 \"Mission State change detected.\"", "r");
        }
     PriorChronoMilestone = MissionChronoMilestone; // Prevent repeating this change detection.
     //
     // At this point, a more sophisticated mission would move, orient, and configure
     // all of the mobile mission objects according to hard-coded data associated
     // with the received ChronoMilestone value in order to keep them synchronized
     // with the corresponding objects of the MissionCommander. It is as if the
     // MissionCommander message means: Everybody listen up! I am the MissionCommander.
     // I am about to send an index into to a hard-coded table of the positions of every
     // object that I have been moving according to my timers, because on my system
     // all of those objects have reached the referenced positions and orientations
     // according to plan.  You are ordered to look up the pre-planned positions and 
     // orientations  of all mobile mission objects corresponding with the index in my 
     // message, and to move all of the corresponding objects on your local maps 
     // accordingly, so everybody sees them in matching locations, orientations, etc.
     //
     switch (MissionChronoMilestone)
        {
        case 1:
           {
           //
           // Get here within 100 ms of receipt of a Morse Code message alerting us
           // of the need to immediately update the position, orientation, velocity,
           // durability, or any other attributes of any and all mission objects that
           // change as a result of the mission's design upon reaching
           // MissionChronoMilestone 1.
           //

           break;
           }
        case 2:
           {
           //
           // Get here within 100 ms of receipt of a Morse Code message alerting us
           // of the need to immediately update the position, orientation, velocity,
           // durability, or any other attributes of any and all mission objects that
           // change as a result of the mission's design upon reaching
           // MissionChronoMilestone 2.
           //

           break;
           }
        case 3:
           {
           //
           // Get here within 100 ms of receipt of a Morse Code message alerting us
           // of the need to immediately update the position, orientation, velocity,
           // durability, or any other attributes of any and all mission objects that
           // change as a result of the mission's design upon reaching
           // MissionChronoMilestone 3.
           //

           break;
           }
        case 4:
           {
           //
           // Get here within 100 ms of receipt of a Morse Code message alerting us
           // of the need to immediately update the position, orientation, velocity,
           // durability, or any other attributes of any and all mission objects that
           // change as a result of the mission's design upon reaching
           // MissionChronoMilestone 4.
           //

           break;
           }
        case 5:
           {
           //
           // Get here within 100 ms of receipt of a Morse Code message alerting us
           // of the need to immediately update the position, orientation, velocity,
           // durability, or any other attributes of any and all mission objects that
           // change as a result of the mission's design upon reaching
           // MissionChronoMilestone 5.
           //

           break;
           }
        case 6:
           {
           //
           // Get here within 100 ms of receipt of a Morse Code message alerting us
           // of the need to immediately update the position, orientation, velocity,
           // durability, or any other attributes of any and all mission objects that
           // change as a result of the mission's design upon reaching
           // MissionChronoMilestone 6.
           //

           break;
           }
        case 7:
           {
           //
           // Get here within 100 ms of receipt of a Morse Code message alerting us
           // of the need to immediately update the position, orientation, velocity,
           // durability, or any other attributes of any and all mission objects that
           // change as a result of the mission's design upon reaching
           // MissionChronoMilestone 7.
           //

           break;
           }
        case 8:
           {
           //
           // Get here within 100 ms of receipt of a Morse Code message alerting us
           // of the need to immediately update the position, orientation, velocity,
           // durability, or any other attributes of any and all mission objects that
           // change as a result of the mission's design upon reaching
           // MissionChronoMilestone 8.
           //

           break;
           }
        case 9:
           {
           //
           // Get here within 100 ms of receipt of a Morse Code message alerting us
           // of the need to immediately update the position, orientation, velocity,
           // durability, or any other attributes of any and all mission objects that
           // change as a result of the mission's design upon reaching
           // MissionChronoMilestone 9.
           //

           break;
           }
        case 0:
           {
           //
           // Get here within 100 ms of receipt of a Morse Code message alerting us
           // of the need to immediately update the position, orientation, velocity,
           // durability, or any other attributes of any and all mission objects that
           // change as a result of the mission's design upon reaching
           // MissionChronoMilestone 10.
           //

           break;
           }
        default:
           { // Only get here if the received value of MissionChronoMilestone is > 9.
           display ("Received invalid value for MissionChronoMilestone.", LOG_MOST);
           }
        }          
     }

Several of our new, sample missions include code like that. You will find it inserted into the block of mission code that gets executed 10 times per second (previously that little block of code had been used only to rotate the RADAR antenna).

As you can see, that code consults the externally managed "MissionChronoMilestone" variable and compares it with the locally updated "PriorChronoMilestone" variable. If they are not identical, then it knows that a new MissionChronoMilestone has been received by LAC's network logic external to our mission code, and it takes steps to display appropriate progress text on the cockpit's "SystemMessagePanel", it vocalizes "Mission State change detected", and it updates "PriorChronoMilestone" with the updated value of "MissionChronoMilestone" to prevent repeated triggering of this logic until a new value of "MissionChronoMilestone" is received.

More sophisticated missions will add more logic at that point to update any relevant timers and to move, re-orient, and reconfigure any affected mission objects according to hard-coded data associated with the value of MissionChronoMilestone. Take a look at the source code for MissionNetworkBattle07.cpp for a working example. Just search for this line of code to find the right area:

switch (MissionChronoMilestone)

All of this is done to ensure that remote players become synchronized with the corresponding objects of the MissionCommander, even if one or more of them enters the mission long after others have been advancing them forward through mission logic. Each of the ten cases of the big "switch" statement at the end of that listing handles receipt of one of the ten MissionChronoMilestone values, and, if Legacy Mission Mobile objects have moved since the prior MissionChronoMilestone, additional code must be inserted in each case, to update attributes or affecting any and all mission objects that are known to change at the corresponding MissionChronoMilestone. (Several of our sample missions already do all of this. See MissionNetworkBattle07.cpp for example.)

By way of illustration, let's assume that the mission design assigns a RedTeam Battleship as ThreeDObject[15] and determines that when the mission's timers advance to a point we define as "MissionChronoMilestone 1", its XZ Position is 420.0, 955.2, and that it is headed directly East (for a compass heading of 90 degrees). We would then be expected to insert code like this into "case 1" to correspond with receipt of a message declaring arrival at MissionChronoMilestone 1:

case 1:
   {
   //
   // Get here within 100 ms of receipt of a Morse Code message alerting us
   // of the need to immediately update the position, orientation, velocity,
   // durability, or any other attributes of any and all mission objects that
   // change as a result of the mission's design upon reaching
   // MissionChronoMilestone 1.
   //
  ThreeDObjects[15]->tl->x = 420.0; // Move object 15 to X 420.0
  ThreeDObjects[15]->tl->z = 955.2; // Move object 15 to Z 955.2
  ThreeDObjects[15]->phi = 90; // Change object 15 compass heading to 90.
   break;
   }

Within that same "case 1" block, the updated position of any other Mobile Mission Objects at MissionChronoMilestione 1 would be updated according to that same pattern. (Note that the "y" position of any ship is always unchanged, since ships are always at Sea Level. Same for gamma (climb angle), and theta, since surface ships don't climb or dive or roll very much unless under tremendous stress.)

Other "case" blocks would be coded according to that same pattern, but updating positions and compass headings according to the mission's plan for subsequent MissionChronoMilestones. The source code for MissionNetworkBattle07.cpp is a great resource for more sophisticated use of these concepts.

To make all of this work properly, new Mission Designers must put together a master logistics plan for each mission. This plan must determine three fundamental mission design parameters. They are:

1 of 3: The number of "MissionStates". All of our sample missions have timers that advance through 201 of these states, defined as a global variable named MissionStateNetworkBattle advances from "0" through "201". Most of the existing timers advance from state to state every ten seconds. That arrangement will probably work for most missions without any big changes.

2 of 3: The position of ten "MissionChronoMilestones" among those "MissionStates". As many as ten times during the mission, existing logic can be called upon to synchronize the position of all mission objects among all players, even if some of them have been participating in the mission for a long time but others have just joined. Unless the Mission Designer identifies disruptive events of an unusual nature, it generally works out well to separate MissionChronoMilestones by about twenty MissionStates. All of the sample missions activate MissionChronoMilestone #1 at MissionState 15, MissionChronoMilestone #2 at MissionState 20, MissionChronoMilestone #3 at MissionState 40, MissionChronoMilestone #4 at MissionState 60, and the remaining six MissionChronoMilestones according to that same advancing pattern, at MissionStates 60, 80, 100, 120, 140, 160, and 180. That arrangement will probably work for most missions without any big changes.

3 of 3: The XYZ Positions and orientations of all Mobile Mission Objects at each of the ten MissionChronoMilestones. For example, all of the sample missions that have a moving Aircraft carrier have code like this:

float RedTeamCarrier07XPositionAtMilestone1 =  272.105042;
float RedTeamCarrier07ZPositionAtMilestone1 =  -76.429771;
float RedTeamCarrier07PhiAtMilestone1 = 246.202545;
float RedTeamCarrier07XPositionAtMilestone2 =  260.318420;
float RedTeamCarrier07ZPositionAtMilestone2 =  -136.597427;
float RedTeamCarrier07PhiAtMilestone2 = 315.804138;
float RedTeamCarrier07XPositionAtMilestone3 =  266.714844;
float RedTeamCarrier07ZPositionAtMilestone3 =  -66.925232;
float RedTeamCarrier07PhiAtMilestone3 = 234.528992;
float RedTeamCarrier07XPositionAtMilestone4 =  198.952469;
float RedTeamCarrier07ZPositionAtMilestone4 =  -49.964832;
float RedTeamCarrier07PhiAtMilestone4 = 153.413849;
float RedTeamCarrier07XPositionAtMilestone5 =  171.683975;
float RedTeamCarrier07ZPositionAtMilestone5 =  -114.148750;
float RedTeamCarrier07PhiAtMilestone5 = 72.458717;
float RedTeamCarrier07XPositionAtMilestone6 =  231.078461;
float RedTeamCarrier07ZPositionAtMilestone6 =  -151.122269;
float RedTeamCarrier07PhiAtMilestone6 = 351.185089;
float RedTeamCarrier07XPositionAtMilestone7 =  276.633148;
float RedTeamCarrier07ZPositionAtMilestone7 =  -98.321991;
float RedTeamCarrier07PhiAtMilestone7 = 270.229950;
float RedTeamCarrier07XPositionAtMilestone8 =  230.909607;
float RedTeamCarrier07ZPositionAtMilestone8 =  -44.914185;
float RedTeamCarrier07PhiAtMilestone8 = 188.474792;
float RedTeamCarrier07XPositionAtMilestone9 =  171.672470;
float RedTeamCarrier07ZPositionAtMilestone9 =  -81.931099;
float RedTeamCarrier07PhiAtMilestone9 = 107.359657;
float RedTeamCarrier07XPositionAtMilestone10 = 198.961212;
float RedTeamCarrier07ZPositionAtMilestone10 = -146.105331;
float RedTeamCarrier07PhiAtMilestone10 = 26.405413;

float BlueTeamCarrier07XPositionAtMilestone1 =  -239.394958;
float BlueTeamCarrier07ZPositionAtMilestone1 =  -76.429771;
float BlueTeamCarrier07PhiAtMilestone1 = 246.202545;
float BlueTeamCarrier07XPositionAtMilestone2 =  -251.181580;
float BlueTeamCarrier07ZPositionAtMilestone2 =  -136.597427;
float BlueTeamCarrier07PhiAtMilestone2 = 315.804138;
float BlueTeamCarrier07XPositionAtMilestone3 =  -244.785156;
float BlueTeamCarrier07ZPositionAtMilestone3 =  -66.925232;
float BlueTeamCarrier07PhiAtMilestone3 = 234.528992;
float BlueTeamCarrier07XPositionAtMilestone4 =  -312.547516;
float BlueTeamCarrier07ZPositionAtMilestone4 =  -49.964832;
float BlueTeamCarrier07PhiAtMilestone4 = 153.413849;
float BlueTeamCarrier07XPositionAtMilestone5 =  -339.816040;
float BlueTeamCarrier07ZPositionAtMilestone5 =  -114.148750;
float BlueTeamCarrier07PhiAtMilestone5 = 72.458717;
float BlueTeamCarrier07XPositionAtMilestone6 =  -280.421539;
float BlueTeamCarrier07ZPositionAtMilestone6 =  -151.122269;
float BlueTeamCarrier07PhiAtMilestone6 = 351.185089;
float BlueTeamCarrier07XPositionAtMilestone7 =  -234.866852;
float BlueTeamCarrier07ZPositionAtMilestone7 =  -98.321991;
float BlueTeamCarrier07PhiAtMilestone7 = 270.229950;
float BlueTeamCarrier07XPositionAtMilestone8 =  -280.590393;
float BlueTeamCarrier07ZPositionAtMilestone8 =  -44.914185;
float BlueTeamCarrier07PhiAtMilestone8 = 188.474792;
float BlueTeamCarrier07XPositionAtMilestone9 =  -339.827545;
float BlueTeamCarrier07ZPositionAtMilestone9 =  -81.931099;
float BlueTeamCarrier07PhiAtMilestone9 = 107.359657;
float BlueTeamCarrier07XPositionAtMilestone10 = -312.538788;
float BlueTeamCarrier07ZPositionAtMilestone10 = -146.105331;
float BlueTeamCarrier07PhiAtMilestone10 = 26.405413;

As you can see, that code implements a big table of floating-point variables with descriptive names referencing the XZ position and "phi" orientation (compass heading) for aircraft carriers, in Mission 07, at each of ten MissionChronoMilestones.

The specific position and orientation values listed above assume that the aircraft carriers are cruising along, at medium speeds, in a big, lazy circle. At the time of this writing in early July of 2020, all of the sample missions with aircraft carriers follow that mission design, and simple code in every MissionState designates a new compass heading to perfect that circular course.

If your mission design requires more complex navigation, you will need to build your own table of floating-point numbers, like the one above, but with different XZ positions and different "Phi" compass headings.

All of the sample missions have ten nearly identical blocks of special code in them (one block in each of the ten MissionChronoMilestone handlers) to help you with that task, but all of that code is normally "commented out", because it is only used during mission development (in order to capture the floating-point data needed to build the table shown above). To find an example for your study, look for blocks of code like this in the sample missions:

//
// FOR EARLY DEVELOPMENT WORK, It is necessary to capture the exact position of moving
// aircraft carriers (and any other moving objects) as soon as the mission advances to
// MissionChronoMilestone 3. We do that during development here, and later on we will
// use the resulting XZ positions as hard-coded data upon receipt of MissionChronoMilestone
// #3 messages from a MissionCommander.
//

/
* The following eleven lines of code may or may not be commented out because they were only used during
* development use, to capture the referenced values at the moment of MissionChronoMilestone #3.
* We are retaining the commented-out code block for future reference, but NOT for ordinary
* use:
RedTeamCarrier07XPositionAtMilestone3 = ThreeDObjects[MissionObjectCarrierRed1]->tl->x;
RedTeamCarrier07ZPositionAtMilestone3 = ThreeDObjects[MissionObjectCarrierRed1]->tl->z;
RedTeamCarrier07PhiAtMilestone3 = ThreeDObjects[MissionObjectCarrierRed1]->phi;
sprintf (DebugBuf, "MissionNetworkBattle07::processtimer() RedTeamCarrier07XPositionAtMilestone3 = %f", RedTeamCarrier07XPositionAtMilestone3);
display (DebugBuf, LOG_MOST);
sprintf (DebugBuf, "MissionNetworkBattle07::processtimer() RedTeamCarrier07ZPositionAtMilestone3 = %f", RedTeamCarrier07ZPositionAtMilestone3);
display (DebugBuf, LOG_MOST);
sprintf (DebugBuf, "MissionNetworkBattle07::processtimer() RedTeamCarrier07PhiAtMilestone3 = %f", RedTeamCarrier07PhiAtMilestone3);
display (DebugBuf, LOG_MOST);
sprintf (DebugBuf, "MissionNetworkBattle07::processtimer()b: MissionStateNetworkBattle = %d", MissionStateNetworkBattle);
display (DebugBuf,LOG_MOST);
/

At some point during your work to perfect a new mission involving more complex navigation, after you hard-code logic that moves your ships or other Mobile Mission Objects around the way you want them, you will activate all ten blocks of that code, causing it to calculate the XZ position and orientation of your ship(s) at each of the ten MissionChronoMilestones. The resulting data is stored in LAC's log file, which you can always find at ~home/.LAC/logfile.txt. After taking that step you must copy the resulting data from the log file into your source code according to the pattern shown in the table above. Then you can return those ten code blocks to their de-activated state, since you won't be needing them anymore.

If the remaining code of your new mission follows the existing patterns as documented in our samples, your ships will move around on the map according to your mission design and will synchronize their positions with one another. It shouldn't be necessary for you to change the logic of mission flow at all. Just follow the existing pattern that we have used for our moving aircraft carriers. If you decide to add other Mobile Mission Objects, you will need to add corresponding, additional logic and expand the sample data tables, but you can simply duplicate the existing pattern and make up your own descriptive names for any new variables that your new objects may demand.

 

Last edit: bbosen 2020-07-13