home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
mnth0109.zip
/
Timur
/
vol1n9.txt
< prev
next >
Wrap
Text File
|
1994-03-25
|
15KB
|
359 lines
A Turn for the Better
This issue marks the seventh installment of my column - it's
the perfect time to get some perspective on how far we've
come.
This column has two purposes. The first is to teach OS/2 2.x
PM programming, mainly by example. The second is to develop
the ultimate game for OS/2. All of the OS/2 games I have
seen so far are rather simple - they barely take advantage of
any of the features that OS/2 has over DOS.
The Ultimate OS/2 Game is a variation of the BattleTech and
MechWarrior from Fasa. BattleTech is a board game based upon
combat among large humanoid tanks called BattleMechs, and
MechWarrior is its role-playing counterpart. I have
permission from Fasa to use their trademarks with two
provisos: the copyright must be shared between Fasa and
myself, and the game must be distributed free of charge.
Although this game has been in development for over a year,
it is still in its infancy. Construction of a functional
combat system is the first goal. The combat system is
composed of four subsystems: terrain editing, movement,
weapons control, and damage control. At this point, the
first three are partially completed.
The terrain editor allows the player to create or alter a
combat field which is organized into a hexagonal grid. Each
hexagon represents an area thirty meters in diameter. The
current implementation allows the player to select from
eleven different terrain types.
The movement subsytem controls the navigation of all the
'Mechs on the playing field, whether they are piloted by the
player or by the computer. It keeps track of game time, it
determines initiative (a role-playing term that describes who
goes first in a given turn), and it regulates movement
points.
The weapons control subsytem handles all the details of
firing a weapon. It decides what targets are visible, which
weapons are available, and whether the shot was successful.
It knows all the stats of each weapon and attack type.
The damage subsystem determines the effect of any attack, and
it keeps track of each BattleMech's condition. It knows what
is damaged, how long it will take to repair the damage, and
how much the repair will cost.
Once these four are fully implemented, then the first half of
this project will be completed. A discussion of the second
half, the role-playing MechWarrior, will be saved for a later
date.
The text from the first installment of this column (volume 1,
number 3) is now available electronically, for those of you
who are interested in getting a better picture of what this
game is all about. Look for it wherever "OS/2 Monthly"
archives are available.
The compiler options have been changed to use the
single-threaded library. It is possible for a multi-threaded
program to function correctly with this library. The trick
is to ensure that only one thread calls the run-time library
at any given time. The function which handles hexagon
highlighting during targeting, Highlight() in module TARGET,
does not contain any library calls. Therefore, the
multi-threaded library is not necessary.
ERRORS
------
As the complexity of a piece of software increases, so will
its error handling capabilities. Unfortunately, most
languages have poor syntactical support for error handling,
and C is no exception. I am not going to present a detailed
analysis of error handling, since that topic deserves far
more attention than I can give it.
There are two errors handled by this program. API errors,
such as the failure to create a presentation space via
GpiCreatePS(), are unrecoverable and force the program to
terminate immediately, sometimes with a message box
appearing. I have never experienced such an error, and I
doubt that I ever will. Nevertheless, they are almost
exclusively caused by invalid parameters and need to be
checked.
The other condition is a user error, such as attempting to
load a map that does not exists. For these errors, a message
box always appears, informing the user of his mistake. The
operation is usually cancelled automatically, and the program
continues without a hitch.
Error messages are of type ERROR, an unsigned 32-bit integer
that uniquely describes the location where the error occured.
Function ErrorBox() takes an error code and displays a
message box that shows the module name, the function name,
and the error message. The names and messages and stored in
file ERRORMSG.H in look-up array amsg[].
Function ErrorBox() displays an error message box. Using the
information in the error code, it knows to which module and
function the error belongs.
For example, take the error code ERR_BITMAP_LOAD_HBM
(0x01020300). This error occurs when the bitmap ID passed to
function BitmapLoad() is invalid. A bitwise-AND with
0xFF000000 results in ERR_BITMAP (0x01000000), and another
AND with 0xFFFF00000 gives ERR_BITMAP_LOAD (0x01020000). The
look-up array translates these values to "BITMAP" and
"BitmapLoad", respectively. This is how ErrorBox() knows the
module and function names.
At this point, you might be wondering why the error messages
are not stored as string resources. The reason is quite
simple: the resource compiler that comes with the OS/2 2.0
toolkit does not accept ERRORS.H because it contains
arithmetic expressions for some of its definitions. Until an
improved resource compiler can be found, the error messages
stay where they are.
BITMAP
------
The first thing you will notice is that BitmapShutdown() will
only perform a shutdown if needed. Then it resets all the
handles (hdcMemory, etc.) so that the module can be
reinitialized. This feature is not used, but it is good
programming practice. If the shutdown procedure fails, then
the appropriate error code is returned.
All of the bitmaps are stored in a single memory presentation
space. When a particular bitmap needs to be drawn, it must
first be selected, and then copied from that PS to the screen
PS. GpiSetBitmap() is used to select the bitmap, and
GpiBitBlt() then draws it on the screen.
For reasons unknown to me, GpiSetBitmap() returns an error if
the desired bitmap is already selected. Just to make sure
that this "error condition" does not cause any problems, the
variable hbmCurrent has been added to keep track of the
currently selected bitmap.
FILES
-----
This module now focuses on the file dialog boxes only.
Previously, the functions in the module would not only prompt
the user for a filename, but they would also read or write
the data. To keep things modular, the responsibility of file
I/O (and file I/O errors) has been placed on the function
which calls this module.
Currently, the only module which performs any file I/O is
TERRAIN, and the routines for opening and saving maps are
there.
GAME
----
Thanks to the WM_MINMAXFRAME message, the info box is now
hidden/shown when the main window is minimized/restored. For
this message, the 'mp1' parameter is a pointer to a SWP
structure, which contains quite a bit of information about
the window's new size and position.
The Move and Target modes have been combined. The left mouse
button moves the 'Mech as before, but now the right button is
used to target. Edit mode works the same as always.
HEXES
-----
All of the targetting-specific functions have been moved out
of this module and placed in TARGET, where they belong. The
list includes function HexHighlight() and HexFirstSide().
In a previous issue, I mentioned that the presentation space
handles created with WinGetPS() are cached-micro spaces, and
therefore should only be used for a short-term drawing. To
ensure this restriction, all functions in this module that
perform any drawing now take an HPS parameter. OS/2
allocates a limited number of these presentation spaces in a
cache (hence the name). If the cache is fully allocated,
then WinGetPS() will return a NULLHANDLE.
The new function HexOutline() is used to highlight a hexagon
by drawing its outline. All hexagons have a one-pixel border
between them. The thickness of the border is determined by
the XLAG and YLAG constants defined in HEXES.H.
In the next installment, however, the hexagonal grid will be
removed. This is easily accomplished by setting XLAG and
YLAG to one, instead of the current value of two. It seems
that the hexagon borders are screwing up the targeting path
algorithm. Sometimes the targeting line squeezes between two
or three hexagons, and the routines which determine the path
get confused.
Unfortunately, without a grid, the player will not be able to
distunguish individual hexagons. As a remedy, the info box
will be expanded to always show the hex index under the mouse
pointer, and a ruler will be drawn around the combat map.
MECH
----
The only addition is function ShowPosition(), which shows the
BattleMech's current position.
MENU
----
The changes in this module reflect the changes in module
FILES. The funtions that actually load and save maps are in
module TERRAIN. The "File" menu now has the option to save
the current map and to quit the program. The "close" option
is also present but has not yet been implemented.
TARGET
------
This month introduces yet another attempt at the targetting
path algorithm. In most situations, the path is correctly
outlined, but there are still cases where breakdown occurs.
Strangely enough, the targeting path is not symmetric about a
column. For example, the targeting path from (7,7) to
(13,11) is not the same shape is the path from (7,7) to
(1,11). One possible cause for this aberration is that the
coordinate returned by HexMidpoint() may not be the exact
center of the hexagon.
The low-level functions for the targeting path have been
placed in submodule TARGET0. This module contains the hex
highlighting routines (formerly in HEXES) and all the
functions which calculate the targeting path.
The problem with the previous algorithm was explained in
issue seven: it's too accurate. Hexagons which were only
touched or briefly entered were being included, even though
they had no significant impact on the visibility. These
hexagons are called insignificant. Remember, the whole point
behind the targeting path is to determine the visibility of
the target.
To explain a solution to this problem, three new definitions
are introduced:
True exit side - the exact side through with the targeting
line exits.
Redirection - the act of selecting an alternate hexagon for a
better targeting path. The normal procedure would be to use
the true exit side. However, in certain cases, a neighboring
side might produce a better targeting path that more
accurately represents the true visibility of the target. If
a different exit side is chosen, then redirection is said to
have occured.
Vertex redirection - a redirection technique based on whether
the targeting line exits near a vertex of the hexagon. If it
does, then the slope of the targeting line is compared to the
slopes of the radii of the exit side and its neighbor. The
closest match determines which exit side is actually chosen.
Figure 1 shows a condition where this technique works.
Normally, the targeting path would jump from (0,2) to (1,1)
to (1,3) to (2,2). With vertex redirection, hexagon (1,1) is
skipped, and the resultant path is smoother.
Figure 2 shows a condition where this technique does not
work. The slope is so shallow that the targeting line never
enters hexagon (1,3). Once the algorithm jumps to that hex,
it no longer knows where to go. So it just gives up and
terminates, leaving the targeting path incompletely drawn on
the screen.
The only solution would be to backtrack and try a different
path. However, I will put the targeting path algorithm to
rest for now. Perhaps a more adventurous soul would want to
pursue this further?
I had the opportunity to test the game on an XGA-2 system.
Drawing the combat map is certainly much quicker than on my
VGA system. However, the background highlighting of the
source hexagon during targeting does not work properly.
Function Highlight(), which uses a color-cycling technique if
the screen supports more than sixteen colors, is too CPU
intensive.
The targeting path could not respond to mouse movements
quickly enough. Adding another DosSleep() call in function
DoHighlight() alleviated the problem. A better solution
would be to temporarily disable this thread while the main
thread is redrawing the targeting path. This enhancement can
be accomplished by using semaphores. Look for it in a future
issue.
TERRAIN
-------
This module now handles all combat map functions.
Previously, module HEXES handled map drawing, and module
FILES handled the file I/O.
All the functions in this module are self-sufficient. They
obtain the file name, if necessary. If an error occurs, they
display the proper error messages. After a successful "open"
or "save as" operation, the window title is automatically
changed. There is no need to return any error condition.
WINDOW
------
Function WindowSetTitle() sets the title that is displayed in
the title bar window. The parameter that is passed is the
name of the current map. A null string is used to represent
an unnamed map.
/* FLAME ON */
I am reserving the last few inches of my column for an open
discussion of OS/2 issues. Here is an opportunity for users
and developers alike to have their voices heard.
This past December, I noticed a bug in OS/2's keyboard
driver. IBM has a CompuServe E-mail address for reporting
bugs. The procedure is to obtain the bug-report form, fill
it out completely, and send it to the proper address. Since
I don't have a CompuServe account, I sent the form via
Internet. So far, so good.
The response I got was that IBM no longer supported Internet
customers, and that I should use other means to report the
bug. In effect, I was being forced to pay money to tell IBM
about a bug in their software.
Things are back to normal now. Apparently, there was enough
commotion, both inside and outside IBM, to reverse that
decision and allow Internet users to report bugs. About four
weeks after I initially reported the problem, a diskette with
the new keyboard driver arrived in my mailbox. My computer
is much happier now. And so am I.
The moral of this story is: users who are reporting bugs are
doing you a favor, so make it easy for them. Do not force
them to communicate with normal technical support channels,
since they are not asking for technical support.
Fortunately, IBM's support department is the best I have ever
seen, but there are plenty of other companies out there which
could use some improvement.
Next Month
----------
New window design, a cool opening screen, and maybe some
experimentation with IBM's new C++ compiler.