Click here to Skip to main content
15,879,474 members
Articles / Programming Languages / C#

Mars Mission (5) : the Chemistry of Fuel

Rate me:
Please Sign up or sign in to vote.
4.70/5 (10 votes)
18 Jul 2011CPOL37 min read 29.3K   6.2K   17   8
adding chemical elements the ships can use as fuel, and a new auto-pilot feature

Warning for Technocrats

After reading the last entry in my series of Mars Mission articles I noticed that an exorbitant amount of text and images had been used up in explaining how to play the game. Since this website is dedicated to promoting open-source software and they continue to deem this project worth posting I was surprised to discover that noone had made any mention of my negligence to explain more adequately the code involved in making this elaborate project. Therefore I decided to warn my readers in order to spare them having to needlessly read through entire segments of text which do not contain any references whatsoever to the inner workings of this coding enterprise. And so I hereby provide this caveat for any of you not interested in wasting time reading a senseless study full of dull digressions, petty passages, and aimless arguments in a mindless manuscript concerning the frivolities involved in playing a silly computer game : skip the following section "How to Play Mars Mission".

How to play Mars Mission

Press F1 for help menu.

Chemical Inventory

The chemistry involved in Mars Mission is relatively simple compared to real life but the code behind it has a few points you could find interesting. You may recall from your high school days these things called "atoms" that pair up and combine into molecules which we call matter in its various states of gas, liquid or solid, you probably realize that the world of chemistry is somewhat more complicated than what your high-school teacher told you. Doing some cursory research in the matter I discovered that there are billions of known molecules with chemists inventing new ones every day, and so, I came to the conclusion that trying to keep an up-to-date record of all known chemical molecules is unrealistic. Despite this obstacle, however, the list of known chemical elements has remained fixed for quite some time now and you can view these in any periodic table.

My objective here was to create an interface that would allow the user to combine input resources together in whatever order or number they wanted and do the same with the output resources to create balanced equations that didn't need to be preprogrammed into the computer. In other words, I didn't want to bound the chemistry set within a predefined list of known chemical reactions but keep the player free to decide how to break down input molecules and recombine their components into different output molecules. So making one list of chemical-elements and another list of chemical-molecules meant that water(a molecule) would be broken into a combination of two elements(oxygen and hydrogen), what I needed was one class that could define them both and that's what the classChemical is, and more.

Since the classChemical has all the requirements to be combined into a ternary tree for easy retrievability the class has a static pointer to the root of a ternary tree which it uses to quickly fetch whatever chemical the program requires. There is only one instance of classChemical per chemical in the list of known chemicals (known to Mars Mission, that is) and pointers throughout the rest of the code make reference to these instances when deciding the flow of their various algorithms.

But I still needed to keep a separate list of chemicals telling me which ones were elements. So I wound up making a separate list of elements... I know, I know what I just said about keeping them together in the same class but what I wound up doing is having a list of elements in a class called classChemical_Element(aptly named, I'm sure) and then included every element into the static ternary tree of classChemical so that when the user writes a chemical reaction decomposing table salt into its elements what actually happens is we have one molecule (chemical NaCl) breaking down into its two component "molecules" (chemicals Na & Cl) despite the fact that Na and Cl are not technically molecules at all but really chemical elements. A subtle difference most people wouldn't notice or care about but the hardcore chemists out there are all shaking their heads and tut-tutting about it already so let me ease the next drastic simplification involved in simulating a chemistry set in this space adventure...

Cold may be the absence of heat to some people but to chemists the difference between endothermic and exothermic types of reactions is like cold-fusion in a hot-tub(have you seen those Coors Light commercials!) Since the list of known chemical reactants is constantly growing you can be certain that the list of known chemical reactions is also unreasonably huge and forever growing as well, and that is what would be required in order to keep track of which reactions are endothermic(require heat in order to cause a reaction between input reactants) or exothermic(produce heat as a result of bringing input reactants together) and so as a consequence of a lack of a list of known chemical reactions that would keep track of which combinations of chemicals require heat and which combinations give off heat, heat is not being considered in this version of Mars Mission. Exothermic/endothermic, Buttle/Tuttle, its all the same. I chose to make this design decision because I wanted to allow the player to have greater flexibility in writing their own chemical reactions and make chemistry and balancing equations "fun", if at all possible.

The chemical and element definitions are stored in Xml files chemicals.xml & Chemical_Elements.xml. Although the list of elements should probably not be changed you can easily add/remove nodes from the xml file to modify the existing list and create your own "known" chemical molecules there.

Chemical Inventory

Although resource objects need to be containers which hold chemicals in order to make use of their chemical inventories, each resource object, ship and base has its own chemical inventory. This is easily done by creating a class that does the job in a similar but non-static way that classChemical keeps track of all known chemicals. classChemicalInventory holds a list of classChemicalInvetory_Item which is parsed out into a ternary tree which can be quickly searched. This class would not be anything special if it were not for the complications involved in updating a graphic display when required as opposed to every clock cycle. There are several gauges available in the player's ship/structure consoles that indicate the level of chemicals available in the structure(ship/building) inventory. The fuel gauge monitors the level of fuel & oxidizer which are two chemicals in the ship's inventory and the chemical inventory can be prompted by the user for display. These two displays are not always visible and don't require constant updating at every clock cycle, as that would cause needless strain on the smooth flow of the program.

For this reason then the classChemicalInventory_Item has an event for changes in its value, this event is called whenever the contents of the inventory is altered but C# does not(to my knowledge) allow a program to discontinue an event-handler once it has been created. So we have an inventory for every ship and one display panel & form on the screen and when we switch from one ship which has event handlers to tell the display to change the value of its inventory and the user then takes control of a different ship and that ship's inventory starts to throw events at the same display we start to have a problem because both ships have chemical inventories that are acting up and giving orders to the display panel which can't make the difference. Not only that but when the first ship is taken control of by the user a second, third and subsequent time, that first ship re-initializes its event-handlers every time and pretty soon we have dozens of events for the same change in content happening for every ship. The result is a disaster.

A solution to this was to make a copy of the list of chemicals in the inventory by rebuilding each item of classChemicalInventory_Item in the classChemicalInventory's list and in that way getting rid of all the old event handlers. Then nulling the ternary tree so that it requires rebuilding from the new list the next time a request is made of it. The interface allows the user to pop-up the chemical inventory by moving the mouse over a label and then discarding the inventory display when the mouse moves away so that new inventory item events are created when the display is summoned and then they are destroyed when the mouse moves away from the "chemical inventory" label but the fuel gauge still needs to show the percentage of fuel remaining even if the player has not moved the mouse cursor over that label to see the specific amount of fuel/oxidizer left in the inventory, so the event handlers for the fuel are recreated as soon as the old ones are destroyed if the ship involved is the one being displayed in the game's ship-navigation-console.

public void killEvents() 
{ 
  for (int intItemCounter = 0; intItemCounter < lcInventory.Count; intItemCounter++) 
  {
  lcInventory[intItemCounter] = lcInventory[intItemCounter].Copy(); 
  } 
  rebuildChemTree(); 
  if (SI != null) 
  { 
  if (SI.cShip != null) 
   { 
    if (SI.cShip.grbShipData != null) 
      { 
      SI.cShip.grbShipData.grbResources.lblwFuelGuage_Value.Ship = SI.cShip; 
      } 
    } 
  }
} 

Dynamically Generated Actions

Dynamically generated actions are actions that are not predefined in the list of actions which are set-up using the resourceEditor option of the program.cs file. Those actions are clearly spelled out with input, interrupt, and output resources, as well as specific skill requirements, hours of labor and a few other details that define everything the astronauts can and cannot do when they interact with their environment. Dynamically generated actions are invented during run-time by the players as they make decisions in the game to create more flexibility in what can and cannot be done. Clearly allowing the players to invent any action they want would make things far too easy and ruin the game but in the case of chemical reactions inputs and outputs can be managed with an interface that does not allow any unbalanced chemical equations produced by unqualified chemists without the necessary input resources such as a chemistry processing station.

As I mentioned above I wanted to allow the users to write their own chemical reactions. Since the game already has a classAction which takes in inputs and produces outputs the simplest thing to do was to allow chemicals to be used as inputs and outputs in these actions, have a generic type of action called "chemical reaction" in the enumerated list enuActions to make it easier to identify chemical reactions when they are completed in the actions schedule, and then treat all the inputs and output as they would be treated were it any other kind of non-dynamically generated action.

And that's what I did.

There weren't many things to change in the classAction in order to accomodate this new feature aside from being able to handle the inventories for the chemical resources. The only important thing that needed to change was the way a loop-action(the action that follows the completion of a current action) was stored in the xml file and the classAction. The old method used the enuActions enumerated type list but doing that does not identify any one of the countless possible chemical reactions which the players can define on their own so that had to be changed to a more flexible string definition which allows any action name, dynamically generated or otherwise. This way the loop-action of a chemical reaction can be identified as pointing to itself in the function

public static classAction fromXmlNode(ref XmlNode xNode) 

which loads an action event from the saved game GameInfo.xml file when it builds the schedule of action events and includes the line

if (cRetVal.cRequirements.strLoopActionName == cRetVal.Name)
  cRetVal.cRequirements.cLoopAction = cRetVal; 

allowing a dynamically generated action to loop within itself until the input resources or changing conditions discontinue the looping action. Alternate solutions to achieve the same thing could have been just as easily implemented but since this one didn't create any new issues and I got it working right away it turns out once again that, "the first solution that works is the right one". I love it when I'm right!

This means that although you can have five chemists each converting carbon dioxide into its component parts of carbon and dioxide each chemist has dynamically created her own instance of a classAction which does the same thing and has the exact same name as the others because none of these actions are kept in any list or array for other chemists to copy from. It may be worth considering making such a list but since the laws governing the validity of a chemical reaction is easily derived and applied, dynamically creating actions for all chemical reactions does not require much MIPs processing and therefore the option of burdening the game's already heavily ladened memory, which the graphics caching requires, with a list of previously dynamically generated chemical reactions seemed unnecessary.

Chemical Processing Station

To create their own chemical reactions the user interfaces at a chemical processing station. The formChemicalReactionInterface allows the players to interface with the game to program the chemical reactions they want their chemists to perform. The character's level of chemistry skill determines the amount of help, such as a chemistry intellisense, option to choose chemical molecules by name or formula, and the remaining imbalance of the reaction, to help the player invent the chemical reactions they need. To do this it relies heavily on the textbox_Chemical which interfaces with the player to help choose a chemical molecule. The limit of options available here depends on the chemist's skill. A very skilled chemist has the option to write the formula or name of a compound while an unskilled chemist can only choose one by name (making it difficult to balance equations when their component elements are not visible on the screen).

Writing the textbox_Chemical involved a few details which I hadn't thought of before. Since the need to have several different types of interfaces had to be met the combination of two textboxes seemed appropriate, one for the name of the chemical being selected by the user and the other for the formula of the chemical being selected. I wrote the class to function with both these textboxes visible and then denied the one textbox or the other depending on the skill level of the chemist doing the interfacing. Both of these textboxes needed an intellisense to let the player know if what they are writing is valid or if it isn't. Not only do these textboxes do a spell-check which is reflected in a yellow or red background depending on the result but the option of an intellisense appears whenever the chemist's skill level is high enough to justify giving the player that extra bit of help.

So a sub-tree search needed to be written in the classChemical which returns a list of all entries in the static ternary tree of chemicals given any "root" leaf to start the search at. The way a ternary tree is built and searched makes every branch of the tree branching off each leaf have the same first few search characters as the leaf off which it branches since there is only one search path that will lead you to that leaf from the root of the tree. What this means is that if we do a scan of a tree until we find the branch that meets with the exact spelling of what the player has written then all the leaves on the branch extending off that leaf will have the same first characters as the entire entry of the leaf. This means that if the player writes "carbo" the intellisense can search through the ternary tree containing all of Mars Mission's "known" molecules find the leaf with the search key "o" after going down the tree until it reaches the last character written by the player in the word "carbo" and then traverses that branch in-order to get a complete list of molecules which have names that start with the letters "carbo", such as "carbon", "carbon dioxide", "carbon monoxide" and so on. Once we have this list its printed up on a listbox in a borderless form that hovers near the textbox_Chemical and the player is free to choose any of the options on that list which sets the chemical the player has selected.

Because the chemical formula needs to be displayed graphically for esthetic reasons a picture box is drawn and positioned in front of the textboxes whenever the mouse cursor moves away from the textbox_chemical class and the chemist's skill level permits the formula to be seen.

Clicking the form on either the right side or left side adds an input or output textbox_Chemical depending on the side you clicked and pressing enter in the textbox of one of these while there is no text discards that <code><code>textbox_Chemical from the form. Whenever a change is made to any of the list of textbox_Chemicals then they are compared by counting the elements (not molecules) on either side of the equation and then telling the user what is imbalanced to help those savvy skilled chemist's and showing the "ok" button whenever a balanced equation is written.

Venting Gas

Venting gas is another example of a dynamically generated action. The interface allowing the user to select which gas to vent is made by drawing the contents of the ship or building's chemical inventory onto a bitmap which can be clicked to make the selection. Once the gas to be vented has been selected then a new action is dynamically generated that takes in a Gas_Exhaust_Controls resource object and an amount of gas selected determined by the chemistry skill level of the character doing the venting and then returns only the Gas_Exhaust_Controls resource object and loops on itself so long as the gas being vented and other conditions allow, or the character quits the task.

Auto-Pilot

The last time I wrote this game (in VB 6.1 while burning many little green bags of insight) the notion of an auto-pilot was beyond my befuddled brain, but this time around I decided that an auto-pilot feature was essential and I determined to plug away at a solution until I had something working. So I spent a week trying to control the ship's engines to redirect its course during runtime in the hopes of catching a solar body in its flight and entering into a circular orbit but the result was unsatisfactory.

I figured I should have the navigation controls actually plot out a course by figuring out where the solar body would be at a given point in time and have the ship appear there at the exact moment ready to enter into orbit so, with that in mind, I decided to write an interface to do the auto-pilot testing and you may want to have a look at this by changing a line in program.cs

public static enuPrograms eProgram = enuPrograms.MarsMission; 

to read

public static enuPrograms eProgram = enuPrograms.testAutoPilot; 

Its fairly user friendly so you should be able to load it up and have a look around. Once I had a decent testing-ground working it took a couple of weeks of daily plugging away at this problem to make any real progress. No matter how many ways I tried to break down the brain business of puzzling through this course-plot paradox, the angles and magnitudes of the depot and depart, the stop and start velocities made proceeding there promptly and at the right expedite the most formidable factor of this difficult function.

I started making some progress when I decided to divide the problem in two separate axes X & Y. That should have been a no-brainer but it actually took me a couple of days to decide to do this. Then, dealing with each axis separately, we have two much simpler linear problems that involve the start and stop velocities (Vi & Vf) as well as the start and stop positions (Xi & Xf, even if we're actually dealing with the Y-axis, I'm using the same function).

Here's the problem :

  1. we have N time segments in which to reach our destination.
  2. we have to travel the distance between Xf & Xi in N segments.
  3. the start segment must not vary from Vi by a value greater than MaxDeltaV (max acceleration)
  4. the end segment must not vary from Vf by a value greater than MaxDeltaV

So, at first, I started by reversing the input values of all problems that had the Xf to the left of Xi then I ran those reversed values back into the function and reversed the results so that the actual calculations were always dealing with a positive X motion(again, regardless of which axis the function was actually solving). Meaning that every calculation involved a motion from left to right but then there were still different cases

      • a. Vi > 0 & Vf > 0
      • b. Vi > 0 & Vf < 0
      • c. Vi < 0 & Vf > 0
      • d. Vi < 0 & Vf < 0

each one of these cases involved several separate calculations to either reverse initial velocity, gain speed towards destination, reduce speed towards destination, reverse speed after passing destination and gain speed to arrive at the correct velocity.

and if that wasn't enough...

I kept running into problems with the actual 1.2.3 & 4. criterias mentioned above. Even in (a) where Vi>0, Vf>0 and deltaX>0 finding a solution to the problem remained an obstacle as I hadn't yet found a way to resolve the mathematics involved in adding up a bunch of segments(vel at each time segment) whose step-to-step variance was limited by a constant (max acceleration) such that given a fixed criteria of first and last segment size(start and stop velocities) the sum of all segments equalled another input variable (total distance travelled).

It took some time before I reasoned out that none of the four different cases (a,b,c or d) could be easily resolved without first solving the mathematics involved in meeting the four mathematical problem criterias. And when I began to desperately try to match two half segments that totalled in the correct distance travelled but whose changes in velocities between them disallowed their combining together by redistributing the excessive segment portions among their neaghbours did I finally make a breakthrough.

At that point I was separating the (a) problem into three segments of

  • I. Accelerate to steady velocity from initial velocity
  • II. Maintain steady velocity
  • III. Accelerate to final velocity from steady velocity

After managing to get parts I & II together reaching the final velocity in the alotted time while keeping the total distance travelled and meeting the four criteria was problematic. What I was doing for part III of this three segmented solution was taking the remaining distance and dividing it up into the number of steps left in which to make the distance. To do that I divided the remaining distance into half the number of time segments remaining and calculated that the sum of the last segment with the first segment should equal this constant making the problem similar to Gauss's solution to the sum of numbers from 1 to 100.

      • 1 + 100 = 101
      • 2 + 99 = 101
      • 3 + 98 = 101
      • ...
      • 49 + 52 = 101
      • sum = 49 * 101 + 50

auto_pilot_-_calculating_a_distance_in_N_steps.png

except now the "start" of section III of this course had to match with the end of the steady velocity(section II) and the criteria of not allowing the acceleration (or change in velocity, size of neaghbouring segments) exceed the max acceleration.

So this is where I was having the computer test which combinations of divined steady velocities fit in with the estimated acceleration and randomly reckoned number of steps. It was looping through the possible solutions for a long time and usually never found a valid match.

Eventually I alighted on the idea of simply forcing segments to match by redistributing the values between adjacent steps that did not abide by the maxAcceleration rule.

and thus the breakthrough...

In only a few hours I had a ship meandering in a bizarre confused fashion to arrive at the exact time and place with the correct course and speed and without once breaking that great rule of the god of maximum acceleration.

It was 2am and I went for a walk to congratulate myself.

The next day I sparked up my computer, did a bit of latin after cooking breakfast and shaved then I got to work on my auto-pilot again and by noon I had it all solved.

There is no need to reverse the inputs and work with a specific type of problem and then further subdividing these into the four situations mentioned above using the algorithm I am about to describe. As I've explained in previous articles I do next to NO RESEARCH so its very likely that this algorithm was invented by someone in the fourteenth century and already has a name but I take pride in having solved it independently(and I thoroughly enjoyed the quest!)

  1. initialize an array with equal segment sizes that sum up to the total distance travelled, this will be the steady velocity where no change in velocity will be required and the ship will coast towards its destination.
  2. keep track of start and stop markers that record the index of the segments we are currently working with
  3. initialize start-less-one segment size and stop-plus-one segment size variables
  4. taper the values of the segments from start-less-one down to the steady velocity without exceeding maxAcceleration and keeping a sum of the amounts by which each segment was altered.
  5. move the start-less-one indicator to the right until it is over the first segment that is still at steady velocity
  6. taper the values of the segments from stop-plus-one down to the steady velocity(working backwards) without exceeding maxAcceleration and summing these changes with the sum kept in part 4 above.
  7. move the stop-plus-one indicator to the left to indicate the last steady velocity segment remaining
  8. distribute the sum of all differences between the two start/stop indicators and add the average to the segments that have not yet been altered
  9. if the difference between the new steady velocity and the start-less-one velocity exceeds maxAcceleration or if the difference between the new steady velocity and the stop-plus-one velocity exceeds maxAcceleration then loop back to #3 above

PlotCourse.gif

Entering Orbit

But that wasn't the end of it. The next issue was selecting a point of approach because simply picking the nearest point on the solar-body's final position relative to the ship's starting position and saying "wind up there at the right speed in N turns" doesn't guarantee that the plotted course won't make your ship fly right through the planet when it alters its speed during final approach. In certain cases, depending on the final velocity required relative to the planet's velocty when the ship gets there, it happens that the ship needs to fly beyond the final position and then return to arrive at the correct course and speed which often leads it to crash and splatter and burn and the crew dies in a flaming blast of debris. And that, of course, is a problem.

To resolve this then what we need to do is find a point somewhere near the planet where we are certain that the ship will never accidentally crash into the planet its trying to orbit. After several attempts to fumble with the trig involved I resolved the problem relatively easily by dividing the issue into two different situations : coming from the left (relative to the final path of the solar body) or coming from the right. And then I further divided those into clockwise or counter-wise orbit the solution was fairly easy to find.

To figure out whether we're flying to the left of or to the right of the solar body's final velocity the algorithm finds a point of intersection between two lines : one goes through the solar body's final position at an angle equal to the solar body's final velocity and the other is perpendicular to the first and runs through the ship's original position. Then a vector is drawn between the point of intersection and the ship's initial position and this vector's angle is compared with the planet or moon's angle of final velocity. If the angle of the vector is equal to the solar body's final velocity angle plus Pi/2 then we are to the right of the solar-body(I should explain that the angles in the following diagrams increase clockwise to agree with the graphic screen's negative y-axis) and a vector with an angle equal to the solar-body's final velocity angle minus Pi/2 indicates that we are flying to the left of the solar body.

Now, dividing the world in half along the solar body's final velocity at its final position, we know which half to place our point of approach.

Selecting_an_approach_point__determine_Type_.png

Next we need to consider whether we want to enter into a clockwise or counter-clockwise orbit and we position the point of approach either in front or in back of the solar object depending on the combination of left/right and clockwise/counter-clockwise.

Selecting_an_approach_point.png

Once the approach point has been selected, the course is plotted in two segments. The first segment gets us from our start position to the approach point with a given start velocity and a final velocity equal to the solar body's final velocity and in that way we can arrive a safe distance away and track the solar body before final approach. The second segment moves us from the approach point to the point of orbital entry where the ship will be placed into orbit.

When the auto-pilot is engaged the ship travels along these predefined "tracks" and neither the ship's engines nor any solar body's gravitational forces affect it(unless the player takes control and cancels the auto-pilot) until the course has been travelled and the ship is set into orbit by changing the enuLocation value in the classCollisionDetectionObject from "Space" to "Orbit" which means that its angle of orbit is altered every clock cycle and it is repositioned to the exact location given its altitude. This way gravity has no effect on it and it just keeps flying round and round until the player decides otherwise.

I realize that in the real world things are much more complicated and no NASA engineer would be foolish enough to propose so simple a solution but this is just a simple simulation so it'll just have to do.  For the moment entering orbit around the speedier moons is a real problem but I'll have the Auto-Pilot orbit the primary and approach the primary soon enough.

Making the Help Menu

The help menu was made using a class I originally made for a previous project that graphically displays multi-colored text for a GCIDE : A Complete English Dictionary dictionary classGraphicOutputPanel. Although I was forced to use RichTextBoxes to build the editor and these leave much to be desired (I went through the four stages of "Dealing with RichTextBoxes" including anger, denial, bargaining and eventually acceptance before finally reattaining a stable relationship with my computer, we're reconciled now after much bruising) the editor is functioning well enough and is mostly stable though I would suggest patience and much "saving of data" if you plan on using it in your own projects or editing the help-menu in this game.

It relies mostly on a second class which I use in making the various graphic display panels in Mars Mission, classGraphicText. To display text on a bitmap the classGraphicText uses a collection of smaller bitmaps which make up the words and collages them together to make a page of text. Since generating these word-images may slow things down they are stored in a dynamically generated binary tree WordImage_BinTree which can easily be appended or searched. Because a word can appear on the page in any combination of script, font, size or color this information is also kept stored with the word's image and each leaf in the binary-tree may have any number of similar leaves that have the same word displayed slightly differently stored as a linked-list pointed to by the tree's leaf. Although this design requires each word displayed onto the screen to have it's image drawn at least once every run-time, and the binary tree grows with every new variation of every new word, the speed achieved with this scheme and the memory requirements are suitable for a help-menu or any other closed environment that does not have an endless vocabulary or size/font/color variations which would eventually cause the program's memory to fail.

In order to make the editor, however, as mentioned above, I had to make use of the RichTextBoxes and anyone who has ever had the pleasure of dealing with these would know that they are woefully inadequate in meeting their proposed specs. They, in theory, should be easy to get along with and they should respond in a rational manner but sometimes it seems like dealing with a RichTextBox is like dealing with a crack-head who's cheating on his methadone program while stealing your wallet. These things are trash.

Nevertheless, I managed to get by using an RTF parser from the Code-Project article Writing your own RTF Converter to muddle me through. If it weren't for that article and its source-code you would be reading about how to fire your thrusters and the need to compress the planet's atmosphere to make fuel rather than the finer details of a frustrated programmer's vain attempts to make sense of an antiquated piece of MS BS that should soon be disposed of. So thanks go out to the guy who wrote the RTF parser while I still wait for Bill to get back to me about why RichTextBoxes aren't up to snuff compared with the rest of the Visual Suite gifts Microsoft has made to those of us who just love to write code.

An important aspect of the classGraphicText is its ability to generate images of chemical formulae which would otherwise be difficult to display adequately on the screen. Anyone who has had to display a chemical formula on the screen would probably attest to the trouble involved in super-scripting and sub-scripting but this is easily achieved with a single call to

static public Bitmap getImageChemicalFormula(string strFormula, classMyFont cFnt)
{
  classWordImage[] udrWordImage = new classWordImage[0];
  appendWordImage_ChemicalFormula(ref udrWordImage, strFormula, cFnt);
  return udrWordImage[0].bmp;
}

Which spits an image back at you displaying your chemical formula in the desired color, size and font. The appendWordImage_ChemicalFormula does all the work by breaking the string down into its different elements. It does this by scanning the text for open and close brackets as well as those little dot-like circles sometimes used in mathematics to mean multiply or in boolean logic to mean "and", then separates the different sub-formulae from the main formula and iterates through them all by breaking them down further into numbers and letters being certain to recognize two-character elements(e.g. Co, Mn or Al) from one-character elements(C, N, or O) then sub-scripting numbers that follow elements or brackets of a sub-formula and displaying in regular size those numbers that precede the formula. After creating bitmaps for each component of a formula, these bitmaps are individually assembled into a single image which is then stored into the binary-tree mentioned above so that should that exact formula be required again it no longer needs to be rebuilt but simply pulled off of RAM.

The Help-Menu can display regular text, link, pop-up text, flash pop-up text and images. To manage this the classGraphicText keeps track of where each word is and has a pointer to the image in the binary tree to recall the size of each word's image and can therefore figure out what word image is under the mouse cursor given where the mouse cursor is relative to the top-left corner of the text's output image. It does this in the function

public classWordImage getWordUnderMouse(Point ptMouse, classWordImage[] cWordArray)

Then the calling function, given the result, can look at the classWordImage it got back and sees what kind of word that is(or image) and the classGraphicOutputPanel responds accordingly. For example, whenever the mouse moves over a link the cursor changes to a crooked arrow which indicates that the viewer will link to a different text if that word is clicked.

This class is also used by the formReport class which also displays a graphic text of information on the screen.

The following line located in the formHelpMenu() function prevents the player from editing the help-menu unless they are running the game in debug-mode.

pnlOutput.bolEdit = (System.IO.Directory.GetCurrentDirectory().ToUpper().Contains("DEBUG"));

To edit the links, images and flashes you have to place the cursor on the visible text(the caption in the case of images) and then press Ctrl-E to summon a "link-editor". The link editor has several options including deciding whether or not you're dealing with a link, an image or a flash but in order to add a link to your page you must first search the page you want to link to and then select it from the list box of results or click the "new" link button which will generate a new one for you. Then, to control the visible text to this link, delete the text in the "visible text" textbox and press "save". The link-editor will disappear and your new link will be included in the page you're currently editing(which still needs to be saved!). If you've added a new link, do not add another one right away as the one you just 'created' hasn't actually be created yet(you've added a pointer to a file that doesn't exist in the file you're currently editing), so save the file you're editing and then click the "New Link" link in the viewer when it appears displaying the file you just saved. clicking the "New Link" link while in the viewer will force the editor to appear again but this time ready to edit the link you intended for the previous file. If you left the "visible text" textbox blank, then whatever text you write in your heading here will appear in your previous file's link reference. Just remember to save this file too.

Although the class is relatively stable all the trouble I had to go through to get it that way was definitely not worth it. There are so many issues with the RichTextBoxes and the general concept of building an editor that has links and other forms of non-visible-text like images and "flash" that display popup's of text not normally seen make building a stable editor difficult and since all this could probably be done using HTML and a professional HTML editor, I would suggest you go that route if you ever have a mind to do something like what I did because although it works it could seriously be improved.

Setting Up

You've probably already noticed the dozen-odd files ready for you to download. Many of the contents of these zip files are included in the download files of previous articles but since some major changes have been made to the way the game sets up and initializes on first launch you're better off just starting from scratch. Well, here we are, downloading files again. For you newbies in the Code world and the C# novitiates let me step you through what you'll have to do to get yourself inside a Raptor and fly your way into the interplanetary void.

  1. first you'll need a copy of C#2010 up and running on your computer. Microsoft provides this software free for anyone to download at Microsoft C# 2010.
  2. then you'll have to grab all the files listed at the top of this page. Starting with the source code. This is the actual program and it also contains the subdirectories in which all the other files are to be included. When you've extracted this file download all the remaining files and extract them into the last MarsMission subdirectory where you'll also find the application's reddish icon.Image 5
  3. when you've got all the files extracted into the correct directory you'll have to spark-up your C#2010 IDE (the MS software mentioned above) and load the project's solution file wherever it was that you extracted it on your computer.
  4. because the files this project needs to run are so big (100s of times bigger than what you've downloaded) they cannot be posted on this site but need to be generated the first time you run the application. this takes time. So what I suggest you do is, instead of running the application in the debugger like most people would do when downloading the code from a new CodeProject article, compile the application and then find the executable and run it. For a newbie this may a bit rough but its not too bad. Load the project and press F6. When the C# IDE has finished compiling there won't be any fanfare or lights or buzzers or anything but you'll notice the words "Build start", "Build Progress"(with a progress bar) and "Build Complete" written at the very bottom left of the screen. When that's done you can exit the C# program and go to your Windows Explorer a-huntin' for the executable which will be located in the project's newly created sub-directory \bin\release\. You can recognize it by its icon, except the file you're looking for this time will not be an icon file but an application.
  5. next, after you've double-clicked the file's name, a lot of things will start to happen but if you're in an elevator and you suddenly feel weightless you might have a problem and that I can't help you with. Primarily what you should expect is this: the application will start to generate all the files it needs and, as i promised in a previous article, will take some time. There'll be a bunch of caching involved but hopefully no real crashing while it builds file-streams for the different resources you might find laying around any untidy space-ship and then the screen will show a star-scape and soon you'll get a text-box asking you how many images you want per quarter rotation. If you've tried the previous installments of this project this is pretty much the same as before but I'll explain again. The number of images per quarter rotation refers to the number of angles the planets will be visible per quarter turn about their own axes. The higher the number the more fluid the planets will appear to rotate and the smaller the number the clunkier the whole machine will clang about(silently though, if that's possible). Either way, the orbits remain the same and it just looks cooler if you pick a number like 90 but if you're in a hurry you can go with 4. Essentially : better smoother graphics take time to initialize and you'll be sitting in front of that screen watching it work for ninety minutes or so (maybe more, depends... just be sure to run the executable or this will take 8-16 hours!).

what's next ?

since the auto-pilot's in its genesis phase I'll find ways to allow a fleet of ships to track each other and possibly a target point, time and speed, as opposed to "enter orbit", eventually, but for now I'd like to include some minerals on the soil for mining and start the engineers actually building and fixing things, which means stuff has to break down. maybe solar flares might be cool, i'm not sure.  communications needs work.  as it is you can reach any one any time but now that you can capture satellites in orbit I'll have to limit communications with the need for satellites and radios.  I'll have to chew on that for a bit...

giving astronauts orders like "report to Engineering" will be cool but that's a major step I'm not sure how I want to tackle yet so I'll have to ponder on it for a while before I do.

updates

  • no surprise that despite taking my time and debugging and testing for the last ten days I discover a bunch of crashing-bugs the day after I publish...  fixed those and a few details.
  • july 18, 2011 - uploaded all the extra files again because they were not accessible

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO unemployable
Canada Canada
Christ Kennedy grew up in the suburbs of Montreal and is a bilingual Quebecois with a bachelor’s degree in computer engineering from McGill University. He is unemployable and currently living in Moncton, N.B. writing his next novel.

Comments and Discussions

 
QuestionGreat work... but fix the bugs Pin
janfe17-Oct-11 14:56
janfe17-Oct-11 14:56 
AnswerRe: Great work... but fix the bugs Pin
Christ Kennedy12-Nov-18 5:31
Christ Kennedy12-Nov-18 5:31 
Questionextra files not downloadable? Pin
R. van Groenigen12-Jul-11 0:58
R. van Groenigen12-Jul-11 0:58 
AnswerRe: extra files not downloadable? Pin
R. Hoffmann13-Jul-11 9:31
professionalR. Hoffmann13-Jul-11 9:31 
GeneralRe: extra files not downloadable? Pin
Christ Kennedy18-Jul-11 7:02
Christ Kennedy18-Jul-11 7:02 
GeneralRe: extra files not downloadable? Pin
R. Hoffmann18-Jul-11 11:16
professionalR. Hoffmann18-Jul-11 11:16 
GeneralMy vote of 5 Pin
Slacker0077-Jul-11 0:40
professionalSlacker0077-Jul-11 0:40 
GeneralRe: My vote of 5 Pin
Christ Kennedy18-Jul-11 7:02
Christ Kennedy18-Jul-11 7:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.