Part 26 Realms, space trading and how to do two things in sync.
When the Amiga and Atari ST arrived on the games scene we knew it was just a matter of time before they would dominate the market. The new machines were a bit expensive at the beginning so the market was not as big. We knew the cost of making a 16 bit game would be as many times that of an 8 bit games because of the many coloured high resolution graphics, and the increased size of the memory which meant people would expect more graphics and bigger games as the norm. Hewson was very pessimistic about the 16 bit market. They had tried a few 16 bit ports and were not getting sales figures to justify designing a game just for 16 bit. This was the time that budget games had revolutionised the 8 bit market and suggested cheaper games were the way to go.
I bought an Amiga for Andrew to play with but we could only start developing on the machine when Graftgold signed with Telecomsoft. The deal involved writing a few games for the Spectrum and C64 but crucially also included a game for the Amiga/Atari ST. Dominic Robinson and John Cumming had just joined Graftgold along with Gary Foreman and David O'Connor. This gave us the expertise and manpower needed to keep an income stream going and start developing systems ready for our first 16 bit games.
This required a change from writing individual games to working as a team. Dominic started work on a development system based around an Atari ST 1024 with a hard disk drive. Dominic also produced a series of stunning demonstrations to show the capabilities of the 16bit chip. Many of these were used in Simulcra and Realms. Our plan was to develop core systems that would allow us the write games that would work on both the Atari ST and Amiga with minimal code changes. David wrote a PC version which was first used for Iron Mans Offroad Racer.
John became the companies first graphic artist. We also took on Jason Page, a local lad , who started off as a YTS trainee but quickly became the Graftgold sound expert. We completed a couple of arcade conversions which paid for the development of the new systems and also got us working as a team ready to work on our own 16 bit games.
Having a graphic artist meant that we could up the ante when presenting ideas to publishers. John could mock up the look of a game fairly quickly which gave a publisher more than just words to read.
One of the early designs was an empire building game based on the Roman Empire. This evolved into a Battle Of Britain game as publishers showed little interest. The 50th anniversary of the battle was coming up so it was a topical choice. Meanwhile Microprose bought Telecomsoft and were interested in the new game and offered us a deal. The contract was terrible and I asked for several changes to make it more like the previous Telecomsoft contracts. They refused to negotiate at all saying it was their new standard contract so we decided to try other publishers. Activision were keen to work with Graftgold but already had a Battle Of Britain game being developed for them. We agreed to change the scenario . They liked the original empire building idea but suggested we set it in a fantasy world. So the idea for Realms was born. We drew up a revised design document and used one of Dominic's fractal landscape demos to show how we could display a 3d landscape. The game was signed quickly and development commenced on PC and Amiga/Atari simultaneously. The design allowed this to happen having several distinct screens. David programmed the battle screen on the PC while I worked on the main game. We then converted each others code. A university student Eldon Lewis was on work placement and he modelled population growth in BASIC and used this as the basis of the city code. We took on a talented local graphic artist, Michael Field to expand our graphics team. John Lilley also joined the art team as a YTS trainee.
The game was Graftgold's first original multiple platform game written as a team using the ways of working we had learned developing the arcade conversions. The game came together very quickly then disaster struck. We heard that Activision's UK company were to cease trading and that meant Realms would not be published. This also affected Paradroid 90 which Hewson was publishing via Activision . Hewson ceased trading in a domino effect and for a while it looked as though we would lose any income from both games. We also had to stop working on a nearly finished console version of Paradroid 90. These were pretty dire times for Graftgold. The publication of Rainbow Islands had also not happened due to the sale of Telecomsoft to Microprose. We were not on a royalty deal so that one did not affect us financially but it was another blow to morale. When you have sweated creating a game the last thing you want is it to be shelved. We had a string of disillusioned employees leave Graftgold soon after this.
Virgin Games were keen to work with us and had a look at Realms and agreed to buy the publishing rights from Activision. Thus we managed to complete the game and get it published. It was a shame as the game was designed to also appeal to the massive US market for strategy style games that Activision could have exploited. It was the first of our games that had a movie style start sequence. I really did not like them, I preferred spending the budget on the actual game. However it was all about the graphics with publishers so we put a small sequence at the beginning that combined pre_animated scenes with sprites moved and sequenced by game code.
I gave Jason a brief to create the soundtrack to Realms as a series of tune phrases which could be put together on the fly in the game depending on the action. Thus the music would be like a movie soundtrack that became more intense when things got going in the game. I had tried this technique successfully with Bushido and liked the way lack of music at times actually created suspense.
Deepest Blue Progress.
I always forgot how hard it is programming when the sun is streaming in through the windows beckoning you out in the fresh air. We used to close the curtains when Andrew and I programmed in my dining room. Then we could at least see what was happening on the screens. Nowadays I am more of a night programmer. I start programming in the evening and just don't stop. I hate switching off and leaving a problem unsolved although sometimes that is the best tactic.
This month I added a couple of trading screens so the player can buy and sell goods at a space station. That all went pretty smoothly as I have a pretty stable system that allows data entry style screens to be built quickly. It's very similar to the business programming I was doing at Eurobase. The forms involved making a grid of items to buy or sell, selecting a line and pressing a buy/sell button.
I had a system were the goods were sold in container quantities. The containers are loaded onto the ship hanging underneath like a Thunderbird 2 pod. I realised that precious things needed smaller quantities so added an on board storage system for smaller goods. This impacted missions and ship design but was only a smallish change.
I decided I wanted to see containers loading or unloading so coded this into the container behaviour routines. I then had to orchestrate this with the HUD docking and undocking sequence. I added a message system to help the player dock and tell them what is going on. I spent ages getting the containers to appear at the right place, move to under the ship then move to the docked position. You quickly forget how difficult thinking in 3d can be. I tell myself it is only arithmetic. You just need to take account of the containers dock point which could be front back top or bottom, its size and orientation, the location of the ships dock point and its orientation , rotate that all by the ships movement which is the movement of the base when it is docked to the base... You get the idea many a slip twixt cup and lip.
Finally the container goes to the right place on the ship but then flicks about. I find it is colliding with the ship and the base. So I added some code to ban collisions if the relative speeds are slow so docking can take place.
|A ship carrying a wing part standing in for a container graphic|
Almost done except I knew that it was no good putting game code in the HUD routine which is essentially a display routine. It meant that I could not use the same code for a remote player or for an AI player. Also if the AI wanted to buy/ sell that code had to be isolated outside the buy sell forms. Even harder if I wanted remote players the buy/sell form on one machine had to be reflected on all versions of the game running. It was complicated enough trying to get the ship/hud/container.base orchestration working without that to think about. Sometimes it is best to get something working in its simplest form then adding the complications.
So an intense cut and paste session was needed. The main ship routines had to own the docking sequence code from last month and the waiting for unloading/loading. The HUD had to work in parallel to this informing the player. Some critical HUD events had to be passed to the control data passed over the network, (and back to the originating game in sync). The buy/sell code was already abstracted , I have spent ten years writing distributed business systems so this was already split into the correct "front end2 and "back end" parts. It just needed a bit of tweaking so a buy/sell event goes via the players network control.
While doing this I thought it would be nice for containers to start loading and unloading while you are in the trade forms. That meant running the game code while in the form code. A few tweaks later and it almost worked. It blew up because I did not move a vital bit of code to set up the game camera. I had forgotten that I treat the camera as a game object that is attached to a player ship so it can follow the ships movement.
Now I wanted players to be able to refer to forms mid flight via the on board computer. For a multi player game it follows that the game should keep playing while a player refers to his computer system. So I need to switch ship control to auto rather like an AI ship while the player is not driving. Players can have several ships so they may switch between them. So ships need to switch in and out of manual mode effectively becoming AI ships. So the AI routines and player routines all need to interleave so the change can be made at any point. That was always the intention I must check that my code is following that plan. The AI routines have two levels, one that orchestrates the chosen mission or action and one that decided which mission/action locality. I like the idea of allowing the player to delegate all actions then just choose any that seem fun to do .
Next step is to get the ship to pick up a huge base part rather like the container process. I have anticipated this in the container code. A base part acts like a container until it is put in position then changes to a functioning base part.
Using a state switch to orchestrate two separated mechanisms.
I have previously shown how to use a state variable to orchestrate a process over several game cycles using a switch statement. I use a variable named State which is part of the objects structure and enumerate the states using an enum.
The ship stays in the same "state" until its code causes it to change states by simply setting pShip->State to a new state. I generally keep states as sequential as possible but they often loop round to a previous state or may skip states. This is a state oriented orchestration system. This pattern is very flexible but has the same downfall as unstructured programming in that in can become like spaghetti junction. The trick is to try to keep state changes as structured as possible, that is restrict them to
1 Sequences. One state follows another in strict sequence.
2. Conditions. An IF statement allows 2 or more alternate following states.
3. Loops. A condition decides whether a sequence is repeated. (note that a single state is repeated automatically as it will be executed every cycle until the state is altered)
4. Calls A new set of states can be called. I usually put these in a separate function. If you want several levels of calls you have to think about how to store the return states.
If 1 want to drive two pieces of code to isolate the processing I can do this by coding two switches and drive them with the same state variable. Say for example I want the HUD routine to respond to a player state by putting up a message, I simply code a switch similar to the original but put in blank stubs for the states where there are no HUD actions . When I want a message I simply put some code in that case. The HUD routine is thus kept in sync with the ship code. I could of course just put if statements in the ship code saying "if player then do hud message" but these quickly clutter up the ship code and mean that every ship takes longer to process rather than the one with the HUD.
The other use I have seen for this kind of state orchestration (or inversion) is for error processing during a huge nested sequence of initiation that may error at any level. You can put the error handling code in each nested level but it can get very messy. Each level that errors may have to clean up resources for all preceding code. This means lots of repeated clean up code. Instead you just assign each step of the initiation a separate state. Then if it goes wrong you just quit the initiation and enter a generic clean up process that has all the clean up steps as separate cases in a switch statement. The code thus branches to the first clean up operation that must be done. Each case has the break statement omitted so it just falls through to all the subsequent clean up steps for each state in turn. Note that the states in the clean up code are in the reverse order of the initialisation states.
Also if the whole thing bombs the state variable is a really good clue as to how far the process got. In my game I even have a mode where I print a short name for the state of each ship on the HUD radar so I can see what they think they are doing. With hundreds of objects it is really hard to isolate one doing something funny and find out which routine is the culprit.