OS-9/OSK Answers! by Joel Mathew Hegberg
May 1994 issue


This month should be an exciting one, for this is the month many OS-9 enthusiasts anxiously await each year. What's so great about May, you ask? (Other than the rain, mud, and high pollen counts.) It's the Annual Last Chicago CoCo/OS-9 Fest, sponsored by Glenside Color Computer Club. It truly is an event to look forward to, as everyone seems to have a wonderful time. Seminars to hear, new hard-/software to check out and buy, new OS-9 computer systems, old OS-9 computer systems, and many many OS-9 enthusiasts to chat/party all night with! If you haven't reserved your room or bought your tickets yet, I encourage you to read the advertisement in this issue!

This month, I'm going to start off with a letter I received recently and end with short program written by Ted Jaeger.


Joel,

Hi. You might not know me, but I met you at last year's CoCoFest in Chicago. I've been working with a C book, WorkoutC, and have started writing a program. It is an adventure game set in the ancient world. The problem I'm having is inventory. I can't find an easy way of doing inventory. The only way I can think of is a whole bunch of IF statements, but we think there has to be a better way. My dad hasn't any ideas, so he suggested I ask you.

Another question I have is: Is there any way to do "Real" random numbers. What I mean is so they don't come up in the same order each time the program is run. My book says nothing about either of these problems. Any help you could give would be greatly appreciated. Thank You.

Sincerely,
Jason Bauer.


Hi, Jason. I do remember you from the last CoCoFest, and hope we see each other again at this month's fest! (Maybe you'll let me play your adventure game then.)

Regarding the concept of 'inventory' for an adventure game (i.e. what items the player is currently carrying), there actually is an pretty elegant way of handling it. I would recommend setting up an array of bytes (of type "character"), one byte for each item you will have in the game. So, let's say we want to have a total of five items in our entire adventure game. You could define it the following way:

#define NUM_ITEMS 5
char items[NUM_ITEMS];

When the player begins his adventure game, you probably don't want him to start out with any items in his inventory, so let's set all items in the array to zero (0x00):

for (i = 0; i < NUM_ITEMS; i++) items[i] = 0x00;

What we're doing is using the items array as a boolean (ON or OFF) array, so this is a pretty simple set up. In C, any non-zero value is considered to be TRUE, while only zero is considered to be FALSE. This approach makes it easy to check whether or not the player has a needed item. For instance, lets say item #2 is 'a golden key' that the player needs in order to open a door. When the player tries to open the door, it's quite simple for your program to decide whether he can or can't open the door:

if (items[2])
{
    /* insert code to open door here */
}
else
    printf("Sorry, the door is locked.\n");

Okay, I know what you're thinking. "Yeah, this is really easy, but how about when the player requests an inventory list? There are no names associated with the game items." This really isn't hard to handle, either. I would suggest writing a subroutine to handle writing out the name of any given item number, like this:

print_item_name(item_number)
int item_number;
{
    switch(item_number)
    {
        case 0:
            printf("a book");
            break;
        case 1:
            printf("a rusty sword");
            break;
        case 2:
            printf("a golden key");
            break;
        case 3:
            printf("a small rock");
            break;
        case 4:
            printf("a magic ring");
            break;
    }
}

By making the above generic routine, you can use it whenever you need to print the name of an item, whether it be while listing the player's inventory, or while printing a room description with an item in it. Your inventory routine might look similar to this:

printf("Items you are carrying:\n");
for ( i = 0; i < NUM_ITEMS; i++)
{
    if (item[i])
    {
        print_item_name(i);
        printf("\n");
    }
}

Regarding random number generation, this is a very common problem with several obscure solutions. One method is to "seed" the random number generator with an arbitrary number, such as the system time. Seeding is typically done by passing a negative number to the random number generator, although this is not always the case.

What I tend to do is just call the random number generator an arbitrary number of times when my program starts up, or perhaps throughout the course of the program, and just throw away those values. This is analogous to "seeding" the random number generator. Under OS-9, you may use the current system time as the "arbitrary number" however you like. Let's say I decide to use "minutes * 5 + seconds" as the number of times to call my random number routine at startup:

#include <time.h>
struct sgtbuf systime;

main()
{
    int i, number;

    getime(&systime);
    number = systime.t_minute * 5 + systime.t_second;
    for (i = 0; i < number; i++) random();
    /* REST OF PROGRAM */
}

I hope that helps you out, Jason! I look forward to seeing you and your dad again, and if I can help you with something else, just let me know!

Ted Jaeger's program, 'tmd', will allow you to adjust the terminal's tmode parameters without resorting to 'shell "tmode etc."' I'm including a ported listing for C programmers as well (listing #2). The BASIC code (listing #1) will only run on OS-9 /68000 systems, due to the unavoidable use of the system-specific syscall function. The C source code, however, should work fine on any OS-9 system. Thanks for sharing your knowledge, Ted!

Listing #1: tmd for BASIC
-------------------------
PROCEDURE tmd
(* Oct 9, 1993
(* Controls screen echo and pause
(* without need of shell "tmode echo pause"
(* Should be merged with RUNB
(* to ensure it is in memory
(* Access from your BASIC programs using
(* something like RUN tmd("-e")
(* With tmd your BASIC programs do not
(* need access to tmode and run faster
(* since screen control no longer requires
(* forking a shell
(* As an extra goodie, you can disable
(* the END key to protect your programs
(* from mad-user-end-key-banging!

PARAM g:STRING[2]
TYPE registers=d(8),a(8),pc:INTEGER
DIM regs:registers
DIM c(128):BYTE

(* getstt call to look at path descriptor
(* the osk descriptor is 128 bytes
(* os9 descriptor is 32 bytes
regs.d(1)=0
regs.d(2)=0
regs.a(1)=ADDR(c)
RUN syscall($8d,regs)

(* turn off echo
IF g="-e" THEN
c(5)=0
ENDIF

(* turn on echo
IF g="e" THEN
c(5)=1
ENDIF

(* turn off pause
IF g="-p" THEN
c(8)=0
ENDIF

(* turn on screen pausing
IF g="p" THEN
c(8)=1
ENDIF

(* turn off BREAK key
IF g="-b" THEN
c(18)=0
ENDIF

(* turn on BREAK key
IF g="b" THEN
c(18)=5
ENDIF


(* now with a setstt we will change the descriptor
(* in accordance with user request
regs.d(1)=0
regs.d(2)=0
regs.a(1)=ADDR(c)
RUN syscall($8e,regs)

END


Listing #2: tmd for C
---------------------
#include <sgstat.h>

/* Same features as Listing #1, however you */
/* can pass multiple options to this C function. */
/* tmd("e -b -p"); will turn echo on, break off, pause off. */
#include <sgstat.h>

tmd(g)
char *g;
{
    char *cptr, neg;
    struct sgbuf tmode_buffer;
    
    getstat(0, 0, &tmode_buffer);
    neg = 0x00;
    for (cptr = g; *cptr; cptr++)
    {
        if (*cptr == '-') neg = 0x01;
        else
        {
            switch(*cptr)
            {
                case 'e':
                    if (neg) tmode_buffer.sg_echo = 0x00;
                    else tmode_buffer.sg_echo = 0x01;
                    break;
                case 'p':
                    if (neg) tmode_buffer.sg_pause = 0x00;
                    else tmode_buffer.sg_pause = 0x01;
                    break;
                case 'b':
                    if (neg) tmode_buffer.sg_kbach = 0x00;
                    else tmode_buffer.sg_kbach = 0x05;
                    break;
            }
            neg = 0x00;
        }
    }
    setstat(0, 0, &tmode_buffer);
}

Any comments, questions, or source code to be included in Joel's column may be sent in care of 68'Micros or, if you have e-mail, directly to Joel himself at "JoelHegberg@Delphi.com".

* THE END *