home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
17 Bit Software 1: Collection A
/
17Bit_Collection_A.iso
/
files
/
23.dms
/
23.adf
/
Changing.doc
< prev
next >
Wrap
Text File
|
1988-07-25
|
13KB
|
230 lines
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.