Changing Empire The first thing you will notice about Amiga Empire is that it is written in a "nonstandard" language. That language is Draco, which is a shareware system available as part of the Fred Fish freely distributable software collection. Disk 76 in that collection contains the compiler, libraries, include files, some tools, etc. Disk 77 contains full documentation and several example programs, along with an early version of Amiga Empire. If you are used to programming in some other language, such as C, Pascal, Modula-II, Lisp or Forth, your reaction to this will be negative. Why then, did I chose to program Empire in Draco? The first reason is purely personal - I designed and programmed Draco, so it is only natural that I prefer to use it. The second reason is that by using a freely available compiler, the number of people who can work with Empire is considerably greater. The C compilers for the Amiga cost upwards of $200. For someone who has bought an Amiga 500 to play around with, spending that kind of money to work with a game is probably not an option. A third reason is that I (along with many other people) consider C a poor language for people learning to program, and I want to discourage people from trying to learn it as a first language. At work I program in C all the time, and I spent much of the past two years building a C compiler to meet the draft ANSI C specification, so I am quite conversant with the use of C. By providing the reasonably programmed source to a very popular game in Draco, I hope to encourage people to try the language out. No doubt someone will come along and translate Amiga Empire into C, thinking they are doing the world a big favour. I can't stop that, but I will say that I plan to support the Draco version, and that I am confident that the Draco version will tend to have fewer bugs than a C version would. The Data Structures There does not exist a design document for this version of Empire. Currently, the best source of inside information is the accompanying file, "Deity.doc", which explains some of the data structures that can be changed by the Deity. Empire doesn't have many complicated data structures. For example, there are no linked lists, and no storage is dynamically allocated. In brief, the data structures are as follows: Sector_t - this structure represents a single sector in the Empire world. The world itself is a square array of such sectors. Since Empire can handle large worlds (a 128 x 128 world has a couple of megabytes worth of sectors), the array is not kept in memory. Instead, sectors are kept on disk (in file 'empire.sectors') and accessed as needed. An LRU (least recently used) cache of sectors keeps the amount of actual disk I/O down. See file 'fileio.d'. World_t - this structure represents the global structure of the entire world. There is only one of these for each Empire game. It contains various counters, sizes, etc. as well as all of the Deity-tunable parameters. It is stored as the first part of 'empire.sectors'. Country_t - this structure represents the state of a country within Empire. It contains such things as the name of the country, where its capital is, how much money it has, which fleets it owns, etc. An array (size COUNTRY_MAX) of these is always kept in memory, and is stored on disk in 'empire.sectors' after the World_t and before the square array of Sector_t's. Thus, the format of file 'empire.sectors' is: one World_t COUNTRY_MAX Country_t's #rows x #columns Sector_t's The number of rows and columns is stored in the World_t. Note that Empire itself does not require a square world, but that is all that EmpCre can currently produce. The sectors are stored by row, that is, the first row comes first, followed by the second row, etc. The first sector in the file is the one with absolute (Deity) coordinates 0,0. Ship_t - this structure represents a ship. They are stored in file 'empire.ships', with no header, so ship number 0 is at the beginning of the file. The number of ships in the world is contained in the World_t, so the next free slot is always known. Currently, ships are not re-used when they are sunk. Fleet_t - this structure represents a fleet of ships. Each fleet can have FLEET_MAX ships in it, and is pointed to by the Country_t of the owning country. Fleets are stored in 'empire.fleets' analagous to ships in 'empire.ships'. Loan_t - this structure represents a loan. They are stored in file 'empire.loans'. Offer_t - this structure represents a sale offer, or lot. They are stored in file 'empire.offers'. Note that the delivery direction fields in an exchange sector are used to point at the offer which represents that exchange. News_t - this small structure represents a news item. They are automatically appended to the current day's news file as they are generated. Note: there are some major structural differences between this Empire and the old Peter Langston Empire. Fleets actually exist as structures. This makes it faster to navigate or examine a fleet of ships since the entire ship file does not have to be scanned. Items for sale also exist as distinct structures (Offer_t's). This makes it unneccessary to scan the ships and/or sectors to produce a trade report. It also made it possible to separate the functions of the trade report and the buy command. Third, each sector maintains a count of how many ships are in it. This considerably speeds up airplane flying by makeing it unneccessary to scan the ship file for each water sector flown over. The Code A number of conventions were followed in Empire, hopefully makeing it easier to understand. I sincerely hope those who modify it will follow the same conventions, thus allowing others that come after them the same chance. They are: - all command routines are named as 'cmd_' followed by the command. - the general parsing routines (in 'parse.d') are named as 'getXXX' or 'reqXXX' depending on whether they will prompt for a missing value. - all type names not built into the language end in '_t'. - all structure field names have a 'taglet' on the front which indicates which structure they belong to. - all constants are written entirely in upper case. - all global variables start with an upper-case letter. - all local variables start with a lower-case letter. - all routines in a given file which are called by routines in another file are declared in include file 'empfunc.g'. This allows the compiler to check all calls against that declaration, which is in turn checked against the actual definition. - global variables which are needed only in one file are declared in that file, instead of in 'empire.g'. The basic structure of the Empire code is quite straightforward. It spends most of its time in 'processCommands', looping around reading commands, looking them up and calling the appropriate 'cmd_' routine. Handling of parameters to commands is conventionalized. Nearly all input can be given on the command line. If it is not there it will be prompted for. This is handled by the 'reqXXX' routines - they all take a prompt, and return a boolean indicating whether or not they got a proper value. If there is something remaining on the input command line, they look there and do not print the prompt. If what they find is invalid, they return 'false', and the command will be aborted. If there is nothing left on the command line, they print their prompt, and read a value. If an invalid value is entered, they will reprint the prompt and try again. If no value is entered (just RETURN is hit), they will return 'false', and the command will be aborted. Several commands require that a range of sectors or ships be scanned. These ranges can have match conditions attached to them. These are handled by the code in 'scan.d'. The key routines there, 'scanSectors' and 'scanShips', are passed a procedure to call, and take their range from structures declared and built in 'scan.d'. For each matching sector or ship, they call the routine passed to them, giving it the sector or ship. A couple of hooks in there allow an empty specification on the 'ships' command to mean all ships, and allow a condition on the 'map' command, thus allowing the player to produce a map showing only those sectors which match the condition. The best way to find out for sure which file a routine is in is to look in 'empfunc.g'. Things To Do A couple of people have suggested that Empire should have graphics (after all, it's a game for the Amiga, right?). No one who has seriously played it has suggested it. Most players of Empire will be people who log in over the modem - they probably don't even HAVE an Amiga. The most useful thing I can think of is to have a split screen - the top portion displays the latest map or radar output, and the bottom portion is a normal, scrolling text area for all other output. Windowing in the top portion and history in the bottom portion would be useful. A number of people have built tools to use as front ends or batch processors for Empire. They have ranged from graphical front ends running on Sun workstations to tools which print maps on laser printers with borders around the owner's country, and delivery routes shown as arrows. By all means let's build some of these for the Amiga, and perhaps even combine them with a terminal program to build a nice front-end to Empire. A word of caution, however - such programs can change the nature of the game, by offloading a lot of work from the player to a computer. Make sure that all players in a game have similar advantages, or agree to the use of the tools by other players. A related advantage can be had by a player connecting at 2400 baud (or playing locally) while others are restricted to 300 baud. There should be a built-in facility for a Visitor country which can do power reports and read the news, but nothing else. Peter Langston Empire has treaties. Should these be implemented in Amiga Empire? How useful were they? Other versions of Empire have farms, bombers, tanks, nuclear weapons, "happiness" factors, etc. How much of this should be added? Are you volunteering? The Amiga is a multi-tasking computer, but Amiga Empire allows only one person at a time to play. It would be nice to fix this. Even on a normal Amiga, one player could be playing locally while another was coming in over the serial port. Serial line multiplexers are available which would let the Amiga have several people connected at once. Properly done, this would require locking/unlocking of each resource used (country, sector, ship, etc.) Going through the Amiga file system would clearly be too slow (the caches in 'fileio.d' would have to be removed). On the Amiga, a proper solution might be to have a separate task running as a resource server. It would contain the caches, and do all file I/O (except perhaps telegrams). It would be responsible for blocking Empire tasks which wanted access a resource which someone else was using (or of telling the task that the resource is unavailable, and have it try again later). Communication would probably be in the form of messages (Exec is a message passing operating system after all) - the message to the server would contain a request, and the reply would contain the locked data, or a notification that the data is locked by someone else. At first glance, this would appear to be easy to fit into Amiga Empire, since all requests for sectors, ships, fleets, loans, offers, news, and telegrams go through routines in 'fileio.d'. Deadlocks ("deadly embraces") would soon be a problem however. Many of the Empire routines need more than one resource at different times. For example, anything that can damage a ship (which is just about anything that looks at one, since 'updateShip' could sink the ship due to a hurricane) has to lock the sector it is in to be able to change the shipCount. All of the fighting commands involve at least two sectors or ships. Imagine the following: two enemy countries are logged in at the same time and are attacking each other. They happen to fire guns at the sector which the other is fireing from. Both lock the fireing sectors (they have to be prepared for defensive damage, and to decrement the shell count), verify that they can fire, and then try to lock their target sectors. If the timeing is just right, both of those will be locked by the other, and they will sit their forever, waiting for each other to release the sectors. Special code is needed to handle this (for example, requesting all needed resources in one message to the server, or being able to back out of all locks held if a needed lock cannot be gotten). Doing this in all neccessary places will not be easy. Identifying those places will also be hard.