[comp.sys.amiga] A Terrain Viewer with 3d relief

hull@hao.UCAR.EDU (Howard Hull) (07/28/87)

Ok for you, you turkeys!  I got ZERO, none, NO responses to my last plea
for devine GURU guidance, and ended up with only the essence of the GURU
for company.  So, having _partially_ solved the dynamically allocated array
subroutine access problem, I am going to post here again, with "improvements".

This program (an essay, really, which is why it's posted here instead of
the comp sources group) presents the Gray terrain generator _compleat_ with
3d relief, and with clarified graphics mode options.  Some day I will try,
what is it you call it, AllocMem? - and see if it does anything better than
calloc.

I will be brief this time.  If you are inclined to reply, read the code and
see what it is I am complaining about.  I am posting a .uue this time, too,
since the program does something that the Draco program doesn't (but easily
could be coaxed to do).  When you fire this hummer up, be prepared to wait
30 seconds for it to generate the terrain after opening the screen.  The
opening format will be 256 wide by 288 high (256 plus 32 for relief) and will
be on an interlaced screen.  It is easily possible to configure it for 256 by
400 (384 plus 16 for relief), but there are some degradations of scale that
seem to happen when you do that.  See instructions within the shar file.
Take apart the shar in the usual way.  The compiler instructions are (this
time no typos) using 16-bit int Manx:
	cc atv3d.c
	cc enq.c
	ln atv3d.o enq.o -lc
My circumstances are 512K Amiga 1000, Manx 3.40a patched to level c (if you
have any patches for your 3.40a at all, you are probably patched this far.

Personally, after finally being able to see what his program puts out, I think
Chris Gray is as much a genius as Slartibartfast.  Nonetheless, be warned, if
you don't have about 200Kbytes of chip mem just wastefully sprawling about,
this program will silently bail out and all you'll get is your next prompt.
The next article posted will be the .uue file.

Good Luck...
						Best Regards,   Howard Hull
[If yet unproven concepts are outlawed in the range of discussion...
                 ...Then only the deranged will discuss yet unproven concepts]
	{ucbvax!hplabs | decvax!noao | mcvax!seismo | ihnp4!seismo} !hao!hull
	for domain mailers: hull@hao.ucar.edu

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	atv3d.h
#	atv3d.c
#	enq.c
# This archive created: Mon Jul 27 23:30:39 1987
cat << \SHAR_EOF > atv3d.h

#include "exec/types.h"
#include "intuition/intuition.h"
#include "libraries/dos.h"
#include "functions.h"

#ifdef NULL
#undef NULL
#define NULL ((void *)0)
#endif

typedef unsigned UINT;

struct Screen *OpenScreen();                /* for terrain window */
struct Window *OpenWindow(),*OpenWindow();

struct Screen *Screen;                      /* for enquirer window */
struct Window *Window,*RWindow;

SHAR_EOF
cat << \SHAR_EOF > atv3d.c

#include "atv3d.h"

extern struct Requester req;           /* custom requester structure */ 
extern struct Border out_border;       /* they must have at least one */
extern struct Gadget ongad;            /* the on gadget is the TRUE one */
extern struct Gadget offgad;           /* the off gadget is the FALSE one */

extern short autoEnquire();            /* declare the trackable requester */

/*
 * sc.c - fractalish terrain generator.
 *
 *   Date:     March 5, 1987 (original version sometime in 1985)
 *   Author:   Chris Gray
 *   Language: C
 *   System:   Amiga
 */

/*
 * Badly hacked for 3d and Dynamic allocation under 16-bit integer Manx:
 *
 *   Date:        July 26, 1987
 *   Barbarian:   Howard Hull
 *   Implements:  (some) Dynamic allocation code from Ray Bovet, and
 *                (considerable) custom requester code from John Draper's
 *                 gadget tutorial.
 *                Devil's Post Pile relief generator whipped up by yours
 *                 truly on the spot.
 *
 *                 Structured design?  Structured design is for lords and
 *                 prima donas.  The rest of us have to make do, even the
 *                 geniuses among us:
 *                 "Hold up! Spare me that nonsense. It's dangerous. We're
 *                  set up for direct current in America. People like it,
 *                  and it's all I'll ever fool with. But maybe I could
 *                  give you a job. Can you fix a ship's lighting plant?"
 *                  Thomas Edison to Nikola Tesla, 1884.
 */

/*
 * Gray: (continued, but edited for current content)
 *
 * The nature of the terrain can be changed by playing with the numbers
 * in 'Range'. If you change SIZE, you must also change the number of
 * values given for 'Range'.  Also, if you use the graphics HIRES flag
 * to get 640 pixel width screens, it is advisable to use numbers that
 * are approximately half what give good results for 320 pixel width
 * screens.  The way this is set up, the created terrain for SIZE = 8
 * and XGEOM set to SINGLES is 256 pixels by 256 pixels, which doesn't
 * fit on a non-interlaced screen.  Only the top 200 pixels are shown.
 * The terrain is a torus, i.e. wraps around both horizontally as well as
 * vertically.
 *
 * In this modified program, to see the whole torus in a rectangular
 * format, use SIZE = 7 and set XGEOM to DOUBLES, YGEOM to SINGLES.
 * For other combinations of SIZE, X and Y GEOM, Range, SCREEN_HEIGHT
 * and SCREEN_WIDTH, DEPTH and ColourMap, see instructions
 * below.  One of the more outrageous combinations, 256 wide by 400 high,
 * (384 pixels plus 16 for terrain relief), with 16 colors, can be set up
 * by using SIZE = 7, XGEOM = DOUBLES, YGEOM = TRIPLES, 7 value Range table,
 * SCREEN_HEIGHT = 400,  SCREEN_WIDTH = 320, DEPTH = 4, use the sixteen
 * element ColourMap, and set NewWindow flag = LACE.  This is also the
 * fastest Devil's Post Pile configuration, since the posts are shorter.
 * The disadvantage is that the scale of the terrain is smaller.
 */

/*
 * Gray goes on to add:
 * Feel free to use this algorithm in any games you write, so long as you
 * give me [Gray] credit for it. (I THINK I invented it, since I've never
 * heard of anything similar, and other programs I've seen use much slower
 * methods.)
 */

/*
 * Of course, by adding the 3d algorithm to Gray's code (that has a familiar
 * ring, somehow) things did get somewhat slower, so it's a good thing that
 * Gray decided to make the code fast!
 */

#define SINGLES 1           /* Use to obtain normal pixel map              */
#define DOUBLES 2           /* Use to obtain wide rectangular pixel map    */
#define TRIPLES 3           /* Use to obtain wider rectangular pixel map   */
#define QUADRUS 4           /* Use to obtain widest rectangular pixel map  */
#define YGEOM  SINGLES      /* Selects picture Y geometry.  Use with LACE  */
#define XGEOM  SINGLES      /* Selects picture X geometry.  Use with HIRES */
                            /* Usually YGEOM is SINGLES, w XGEOM as below: */
#define SIZE 8              /* 128 X 128 SIZE 7 w SINGLES, or 256 X 128 at */
#define MAP (1 << SIZE)     /* 7 in DOUBLES.  MAP is edge base length in   */
#define MAPH (MAP*YGEOM)    /* pixels, 128 for SIZE = 7, 256 for SIZE = 8  */
#define MAPW (MAP*XGEOM)    /* configured as torus; GEOM's extend this.    */
#define CAMOD 4             /* SIZE 7 always 4, SIZE 8 w DOUBLES must be 8 */
#define MAPMH MAPH/CAMOD    /* sets calloc module size, 16-bit calloc      */
#define CSIZE 2             /* calloc needs to know short type is 2 bytes  */
#define SCREEN_WIDTH 320    /* 320X200: 7 & SINGLES 32K, looks 1/4 size    */
#define SCREEN_HEIGHT 400   /* 320X200: 7 & DOUBLES 65K, 8 & SINGLES 131K  */
                            /* 320X400: 7 & TRIPLES 98K, 8 & SINGLES 131K  */
                            /* 640X200: 8 & SINGLES 131K, 7 & QUADRUS 131K */
                            /* 640X200: 8 & DOUBLES 262K, 8 & QUADRUS 393K */
                            /* 640X400: 8 & DOUBLES 262K, 8 & TRIPLES 393K */
                            /* and so on.  The possiblities are endless... */
#define SCREEN_DEPTH 5L     /* use 4L max w HIRES flag, 5L max w LACE flag */
#define COLOURS (long)(1 << SCREEN_DEPTH)   /* set according to HIRES flag */
#define WINDOW_WIDTH (long)(MAPW < SCREEN_WIDTH ? MAPW : SCREEN_WIDTH)
#define WINDOW_LENGTH (long)(MAPH + COLOURS < SCREEN_HEIGHT ? MAPH + COLOURS : SCREEN_HEIGHT)
#define WINDOW_HEIGHT (long)(MAPH < SCREEN_HEIGHT ? MAPH : SCREEN_HEIGHT)
#define REQ_WIDTH 64L               /* Width of enquirer window            */
#define REQ_HEIGHT 72L              /* Length of enquirer window           */
#define REQ_LEFT_EDGE (long)(SCREEN_WIDTH - REQ_WIDTH) /* enquirer window  */
#define REQ_TOP_EDGE 12L            /* initial position on terrain screen  */

#define FREEPEN (-1)

/* Range for use with SIZE = 7  and 4 bit planes */
/* static unsigned short Range[SIZE] = {20, 18, 18, 5, 4, 3, 2}; */

/* Range for use with SIZE = 7  and 5 bit planes */
/* static unsigned short Range[SIZE] = {36, 34, 34, 8, 6, 4, 2}; */

/* Range for use with SIZE = 8  and 4 bit planes */
/* static unsigned short Range[SIZE] = {20, 18, 18, 18, 5, 4, 3, 2}; */

/* Range for use with SIZE = 8  and 5 bit planes */
   static unsigned short Range[SIZE] = {36, 34, 34, 34, 8, 6, 4, 2};

/* Colour table for non-HIRES screen */
   static unsigned short ColourMap[COLOURS] = {
    0x00f, 0x0ff, 0x765, 0x0b0, 0x0d0, 0x0f0, 0x4f4, 0x8fb,
    0xdfa, 0xff9, 0xbd8, 0x9c7, 0x6b5, 0x4a6, 0x297, 0x088,
    0x078, 0x177, 0x366, 0x465, 0x663, 0x762, 0x951, 0xa50,
    0xa62, 0xa74, 0xa86, 0xa98, 0xaaa, 0xccc, 0xeee, 0xfff
};

/* Colour table for HIRES screen */
/* static unsigned short ColourMap[COLOURS] = {
    0x00f, 0x0ff, 0x765, 0x0f0,
    0x8fb, 0xff9, 0x9c7, 0x4a6,
    0x088, 0x177, 0x465, 0x762,
    0xa50, 0xa74, 0xa98, 0xfff
}; */

/* AutoEnquire body and button texts */
struct IntuiText negtext =
    {COLOURS - 1L, 0L, JAM2, 5L, 2L, NULL, NULL, NULL};
struct IntuiText postext =
    {COLOURS - 1L, 0L, JAM2, 5L, 2L, NULL, NULL, NULL};
struct IntuiText text =
    {COLOURS - 1L, 0L, JAM2, 12L, 7L, NULL, NULL, NULL};

short wleftedge = -1; /* These are the variables in which we     */
short wtopedge = -1;  /* get the first moved enquirer position.  */

static unsigned short Seed;
static short altitude;
short interested;

short *Cell[MAPH];  /* define an array of pointers to modules from calloc */
char *calloc();     /* define the allocator subroutine call */

/*
 * random - return a random number 0 - passed range.
 */

unsigned short random(rang)
unsigned short rang;
{
    if (rang == 0)
        return 0;
    Seed = Seed * 17137 + 4287;
    Seed = (Seed >> 8) ^ (Seed << 8);
    return Seed % rang;
}

/*
 * set - set a given spot in Cell.
 */

void set(size, height)
unsigned short size;
short height;
{
    unsigned short rang;

    rang = Range[size];
    height = height + random(rang) - (rang + 1) / 2;
    altitude = height;
}

/*
 * bound - set colour 0 at image edge.
 */

void bound(l)
unsigned short l;
{
    unsigned short c;
    short height;

    height = 0;
    for (c=0; c<WINDOW_WIDTH; c++)
        {
        SetAPen(Window->RPort, (long)height);
        WritePixel(Window->RPort, (long)c, (long)l);
    }
}

/*
 * show - set a Devil's Post Pile (line of pixels) in the terrain window.
 */

void show(cc, l, Cell)
unsigned short cc, l;
short *Cell;
{
    unsigned short c, ll0;
    short ll, lll, height;
    short colour = 0;

    for (c=0; c<WINDOW_WIDTH; c++)
        {
        height = Cell[c];
        if (height < 0) height = 0;
        if (height >= COLOURS) height = COLOURS - 1;
        ll0 = l + COLOURS -1;
        ll = ll0 - height;
        colour = height;
        if (cc != 0 && height != 0)  colour = cc;
        for (lll = ll0; lll >= ll; lll -= 1) 
            {
            SetAPen(Window->RPort, (long)colour);
            WritePixel(Window->RPort, (long)c, (long)lll);
        }
    }
}

struct IntuitionBase *IntuitionBase;
struct GfxBase       *GfxBase;

/*
 * main program.
 */

void main()
{

    unsigned short i, j, k, l, m;
    unsigned short c, step, nextStep, l1, l2, c1, c2, cc;
    short height, ll, ll0, lll;

    struct DateStamp ds;

/* this is the terrain screen opening structure.  Configure as per RKM */
/* flags field may contain 0x0, LACE, or HIRES */
    static struct NewScreen newScreen =
            {
            0L, 0L, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, 0L, 1L,
            LACE, CUSTOMSCREEN, NULL, NULL, NULL, NULL
            };

/* this is the terrain display's window opening structure */
    static struct NewWindow newWindow =
            {
            0L, 0L, WINDOW_WIDTH, WINDOW_LENGTH,
            FREEPEN, FREEPEN,
            0x0, BORDERLESS | ACTIVATE | NOCAREREFRESH,
            NULL, NULL, NULL, NULL, NULL, 0L, 0L, 0L, 0L,
            CUSTOMSCREEN
            };

/* this is the AutoEnquire window structure */
    static struct NewWindow reqWindow =
            {
            REQ_LEFT_EDGE, REQ_TOP_EDGE, REQ_WIDTH, REQ_HEIGHT,
            FREEPEN, FREEPEN,
              CLOSEWINDOW            /*  IDCMP flags                      */
            | REFRESHWINDOW
            | MOUSEBUTTONS
            | REQCLEAR 
            | GADGETDOWN 
            | SELECTDOWN 
            | SELECTUP, 
                                     /*  Usual flags for gadgets and such */ 
              ACTIVATE
            | WINDOWDEPTH 
            | WINDOWDRAG 
            | SMART_REFRESH, 
            NULL, NULL, NULL, NULL, NULL, 0L, 0L, 0L, 0L,
            CUSTOMSCREEN
            };

/* 'main' assignments begin here */

/* **************************************************************************
*                                                                           *
*  :::::::::::::::::::::::::::::::::::::::::::::::::::::::::                *
*  ::  When you're up to your OS in allocators, it's hard ::                *
*  :: to remember that the task was to empty /dev/swap... ::                *
*  :::::::::::::::::::::::::::::::::::::::::::::::::::::::::                *
*                                                                           *
*  I ransacked some of Ray Bovet's Atari code to get the dynamic allocator. *
*  However, on my own I discovered Horror #1:                               *
*                                                                           *
*  Dynamic allocation for Manx uses either of two Unix compatible routines, *
*  malloc or calloc from the system independent library.  Since calloc      *
*  merely multiplies its two input arguments and uses them to call malloc,  *
*  with a 16-bit integer compiler the maximum request is 65,535 bytes.      *
*  Requests for 65,536 bytes get zero bytes allocated.  In order to obtain  *
*  262,144 bytes we would have to ask malloc for eight 32,768 byte modules. *
*                                                                           *
*  Just to be decent, this code was issued with #defines at the front that  *
*  will ask (as printed) for four modules.  Change the #define for CAMOD to *
*  experiment with this.                                                    *
*  Good Luck...                                                     H. Hull *
*                                                                           *
************************************************************************** */

/* Dynamic allocation routine for terrain growth arrays */

/* casts input arguments to their required types */
/* typedef for UINT 'unsigned integer' is at top of page */
/* cast the char pointer returned to type 'short *' for our use in Cell */
/* calloc returns arg1 items of size arg2, cleared to zeros */

k = 0;
do
    {
    if ((Cell[k] = (short *)calloc((UINT)(MAPMH*MAPW), (UINT)CSIZE)) == NULL)
        {
        exit(0);  /* Silently exits.  Anybody know a more graceful method? */
    }
}

/* MAPMH is module 'height' in the window; */
/* an even number of modules stack together like blind slats on the window */

/* Each module produces a pointer to its base that goes into Cell[0] so   */
/* we have to then fill the Cell array with window height-based pointers  */
/* one pointer for each line across the window.  These serve as pointers  */
/* to window-width based line data we want to put in the returned module. */

while ((k = k + MAPMH) < MAPH);

for (k = 1; k <= CAMOD; k++)
    {
    l = MAPMH*k;
    m = MAPMH*(k-1);
    for (j = m+1; j < l; j++)
        {
        Cell[j] = Cell[j-1] + MAPW;         /* makes an array of pointers */

    }
}

/* Back to the original program now... */

    IntuitionBase = (struct IntuitionBase *)
                            OpenLibrary("intuition.library", 0L);
    if (IntuitionBase != NULL)
        {
        GfxBase = (struct GfxBase *)
                          OpenLibrary("graphics.library", 0L);
        if (GfxBase != NULL)
            {
            Screen = OpenScreen(&newScreen);
            if (Screen != NULL)
                {
                LoadRGB4(&Screen->ViewPort, ColourMap, COLOURS);
                newWindow.Screen = Screen;
                Window = OpenWindow(&newWindow);
                if (Window != NULL)
                    {
                    DateStamp(&ds);
                    Seed = (ds.ds_Minute ^ ds.ds_Tick) | 1;

/*  Ok.  I give up.  I notice that Cell[y][x] in main() is a place to put */
/* a single data point, even though Cell is actually a singly subscripted */
/* array of pointers.  We certainly wouldn't want to smash the pointer by */
/* attempting an assignment of data to Cell[n].  The compiler would warn  */
/* about a short to pointer conversion, anyway.  We could use *Cell[n] to */
/* obtain the quantity in the base cell of any particular line of data,   */
/* and pointer arithmetic (*Cell[y]+x) might (?) get us the data we want  */
/* to access.  But when I try to use Cell[y][x] in a subroutine, I get an */
/* excess array subscript error.  No amount of fiddling with subroutine   */
/* argument definitions yielded any error free situation.  I can't use a  */
/* 'static' definition in main for a dynamically allocated data element,  */
/* either, can I?  So this program got hacked to where it looks like a    */
/* meat packing plant after a company holiday.  However, I have now used  */
/* Cell in the "show" subroutine.  Indentation is out the window in main. */

do
{

/*
 * grow - grow the basic scenery heights.
 */

    Cell[0][0] = 0;
    step = MAP;
    for (i=0; i<SIZE; i++)
        {
        nextStep = step / 2;
        for (l=0; l<MAPH; l+=step)
            {
            l1 = l + nextStep;
            l2 = l + step;
            if (l2 == MAPH)
                l2 = 0;
            for (c=0; c<MAPW; c+=step)
                {
                c1 = c + nextStep;
                c2 = c + step;
                if (c2 == MAPW)
                    c2 = 0;
                set(i, (Cell[l][c] + Cell[l][c2] + 1) / 2);
                Cell[l][c1] = altitude;
                set(i, (Cell[l][c] + Cell[l2][c] + 1) / 2);
                Cell[l1][c] = altitude;
                set(i, (Cell[l][c] + Cell[l][c2] +
                                 Cell[l2][c] + Cell[l2][c2] + 2) / 4);
                Cell[l1][c1] = altitude;
                }
            }
        step = nextStep;
        }

/*
 * display - display the resulting scenery.
 */

/* clear a band as tall as the highest relief color value */
    for (l=0; l<COLOURS; l++)
        {
        (void) bound(l);
    }
/* call the Devil's Post Pile routine once for each line in the display */
/* set cc to use height for colour */
    cc = 0;
    for (l=0; l<WINDOW_HEIGHT-1; l++)
        {
        (void) show(cc, l, Cell[l]);
    }
/* make a call for the the bottom line, but set the color to #2 */
    cc = 2;
    l = WINDOW_HEIGHT - 1;
        (void) show(cc, l, Cell[l]);

/*
 *  Now we go find out how the audience reacted.
 */

interested = FALSE;
reqWindow.Screen = Screen;
if ((wleftedge >= 0) || (wtopedge >= 0))
    {
    reqWindow.LeftEdge = wleftedge;  /* if the window has been opened, */
    reqWindow.TopEdge = wtopedge;    /* use the updated coordinates    */
}
RWindow = OpenWindow(&reqWindow);
if (RWindow != NULL)
    {
    (void) InitRequester(&req);  /* Rumor: clears req structure entirely */

    /* Init the fields in the Requester structure */ 
  
    req.LeftEdge  = 4; 
    req.TopEdge   = 12; 
    req.Width     = 56; 
    req.Height    = 59; 
    req.ReqGadget = &ongad;         /* First gadget */
    req.ReqText   = &text;          /* Text for enquirer */
    req.BackFill  = 1;              /* BackGnd colour to window */
    req.Flags     = 0; 
    req.ReqBorder = &out_border;    /* Must have at least one */

    /* initialize the enquirer text */

    text.IText = (unsigned char *)"DONE";
    postext.IText = (unsigned char *)"NEXT";
    negtext.IText = (unsigned char *)"QUIT";

    if (Request(&req, RWindow) == 1)  /* try to open the enquirer window */
        {
        interested = (autoEnquire());  /* if we're here it did open, so  */
                             /* now we can find out what they want to do */

/* To avoid some the above cumbersome initialization, the following is a
    model for the AutoEnquire function (for v1.3 anybody?) which allows us
    to specify WHERE the requester will go rather than HOW BIG it will be:

        interested = (AutoEnquire
        (
                    Window,
                    BodyText,
                    PositiveText,
                    NegativeText,
                    PositiveFlags,
                    NegativeFlags,
                    LeftEdge,
                    TopEdge
                    ));
   It should also be possible to set things up so that if the user moves
    the AutoEnquire window, Intuition can keep track of where it was put
    so that it will open up in the same place next time it's called.
    Would you like that, Bunky?  Since Intuition maintains the structure,
    it could be done with appropriate flags.                           */


        CloseWindow(RWindow);  /* if we're here we got their attention */
                               /* and we can close the enquirer window */
    }
}
}
while (interested == TRUE);

/* Ok.  We're back to normal now.  We're also all done with the program. */
/* Time to clean up the mess and get out while we're still ahead...      */

                    CloseWindow(Window);
                }
                CloseScreen(Screen);
            }
            CloseLibrary(GfxBase);
        }
        CloseLibrary(IntuitionBase);
    }
}

SHAR_EOF
cat << \SHAR_EOF > enq.c
/* *************************************************************************

       The modified code in the appended section was extracted from

                     MENUS, REQUESTORS, AND GADGETS 
                                  BY 
                            John T. Draper 
                 A service from the Programmers Network 
                       on the WELL in Sausalito. 
                     Article 408@well and 409@well
                           December 31, 1985
 
  Permission to post this on other networks is granted provided the 
source of this information is included.   The programmers network is 
a non-profit network exchange of programming information.   For more 
information,  mail your requests to: 
 
WELL: crunch orsomewellprogrammerwhosequipmenthasnotbeenseized
BIX:  crunch orsomewellprogrammerwhosequipmenthasnotbeenseized
USENET: ihnp4!ptsfa!well!crunch orsomewellprogrammerwhosequipmenthasnotbeenseized
DELPHI: crunch orsomewellprogrammerwhosequipmenthasnotbeenseized

 ************************************************************************* */

/*
 * Hacked to make autoEnquirer, a positionable position-tracking requester
 *
 *   Date:        July 11, 1987
 *   Barbarian:   Howard Hull
 *   Implements:  custom requester code from John Draper's gadget tutorial
 *                + blood, sweat, tears, & additions to the explative list...
 */

/* **********************************************************
 *
 * ::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 * :: When you're a tiger trainer, it's a bad deal when  ::
 * :: the tiger gets your attention by putting a paw in  ::
 * :: your face.  This is not only because tiger paws    ::
 * :: don't smell very good  - it's also because you'll  ::
 * :: shortly begin to wonder what the rest of the tiger ::
 * :: is up to.  With that paw in your face, you can't   ::
 * :: determine what you need to know to decide on what  ::
 * :: ought to be done next.  With tigers, it's much the ::
 * :: same as it is with anything else:  As Will Rogers  ::
 * :: once said, "It ain't what we know that gets us in  ::
 * :: trouble.  It's what we know that ain't so..."      ::
 * ::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 *
 *
 * I wrote this thing up because I got tired of moving the requester
 * out of the way so I could see what was going on, and that in turn
 * so that I could then decide what to do with the requester.  As all
 * of you by now know, and as is evident in the net programs, if you
 * make an AutoRequest, you get a requester at 0,0 with no programmable
 * options to reposition it anywhere that you'd rather it be put. (Correct?)
 *
 * Given that the Gadget Editor Boys have not yet released their product
 * (I think they are waiting for John Draper's life to get a little less
 * complicated) I thought I'd put this out in the mean time.  As I'm very
 * much a C programming novice, I'm putting this up for some presumably
 * critical guidance.  Rather than being an example of proper code, I'm
 * certain it contains a number of spectacular boo boos - maybe including
 * some assumptions that would make the whole program completely unnecessary.
 * Just as John said, requesters are a pain in the *ss.  But without a
 * gadget editor, this is the sort of thing that has to be done...
 *
 */

#include "atv3d.h"

extern short wleftedge;     /* These are the variables in which we     */
extern short wtopedge;      /* put the first received positions.       */

extern struct IntuiText negtext; /* Let the user keep the button names in */
extern struct IntuiText postext; /*  in his stuff, like for AutoRequest */

struct Requester req;            /* custom requester structure */ 

/*
 * Above is an instance of the (empty) Requester stucture.  It contains a
 * pointer to the host window through the RWindow structure.  (The empty
 * Requester structure was defined just above this comment.  It is not
 * declared, since it gets wiped clean by the InitRequester() call in the
 * user's program.  That call could be moved into this section if the user
 * didn't have to follow through by initializing the structure before it
 * can be used.
 */

/*
 * In the RWindow structure containing the window's LeftEdge - TopEdge
 * definition, we wish to keep track of the requester position by means
 * of these two numbers once they are initialized.  This structure is
 * housed in the user's section.
 */

short autoEnquire();    /* the enquirer subroutine is in this section */

/***************************************************************************
*              I M P O R T A N T         C O N S T A N T S
***************************************************************************/

#define NL 0

#define REDP 3
#define BLKP 2
#define WHTP 1
#define BLUP 0

/***************************************************************************
               CUSTOM REQUEST WITH TEXT, BORDERS, AND GADGETS
***************************************************************************/

/* Border for buttons */
SHORT Pairs[] = {
 0, 0,            /* Coordinates relative to button position definition */
 43, 0,
 43, 12,
 0, 12,
 0,  0
};

#define NUM_PAIRS 5    /* 5 pairs make a rectangle, start and stop incl */
struct Border butt_border = {
 -1,  -1,
 BLUP, 0, JAM1,
 NUM_PAIRS,
 (SHORT *) Pairs,
 NULL
};

/* FALSE BUTTON */
#define ONE_BUTT 1      /** GadgetID used to identify the action ***/
struct Gadget offgad = {
  NULL,
  7, 42,                  /* LeftEdge, TopEdge     */
  42, 11,                 /* Width,  Height        */
  GADGHCOMP,              /* Flag                  */
  RELVERIFY | ENDGADGET,  /* Activation            */
  BOOLGADGET | REQGADGET, /* GadgetType            */
  (APTR)&butt_border,     /* GadgetRender - Border */
  NULL,                   /* SelectRender          */
  &negtext,               /* "OK" text             */
  NL, NL, ONE_BUTT, NL    /* Mut Excl, Spec Info,  */

};

/* TRUE BUTTON */
#define TWO_BUTT 2
struct Gadget ongad = {
  &offgad,
  7, 22,                  /* LeftEdge, TopEdge     */
  42, 11,                 /* Width,  Height        */
  GADGHCOMP,              /* Flag                  */
  RELVERIFY | GADGIMMEDIATE,   /* Activation       */
  BOOLGADGET | REQGADGET, /* GadgetType            */
  (APTR)&butt_border,     /* GadgetRender - Border */
  NULL,                   /* SelectRender          */
  &postext,               /* "OK" text             */
  NL, NL, TWO_BUTT, NL    /* Mut Excl, Spec Info,  */
};


/* Outside Border of Requester */
SHORT ReqPairs[] = {
 3, 3,        /* Coordinates relative to requester sheet top left corner */
 53, 3,
 53, 57,
 3, 57,
 3, 3
};

struct Border out_border = {
 -1, -1,
 BLKP, 0, JAM1,
 NUM_PAIRS,
 (SHORT *) ReqPairs,
 NULL
};

/***************************************************************************
                   D O   C U S T O M    R E Q U E S T
***************************************************************************/

/*
 * Just as Enquiring minds want to know, Enquiring eyes want to see...
 */

autoEnquire()
{ 

    short looping = TRUE;
    short response = FALSE;
    struct IntuiMessage *mes;
    struct Gadget *gad;           /* Gadget chosen */
    unsigned long class; 

    while (looping) 
        { 
        if ((mes = (struct IntuiMessage *)GetMsg(RWindow->UserPort)) == 0L)
            { 
            (VOID) Wait(1L<<RWindow->UserPort->mp_SigBit); 
            continue; 
        } 
        class = mes->Class; 
        gad = (struct Gadget *)mes->IAddress; 
        (VOID) ReplyMsg(mes); 
        if (class == REQCLEAR)
            looping = FALSE; 
        if (class == GADGETDOWN)
            { 
            switch (gad->GadgetID)
                { 
                case TWO_BUTT:  response = (short)TWO_BUTT-1;
                                 EndRequest(&req, RWindow);
                                 break; 
                case ONE_BUTT:  response = (short)ONE_BUTT-1;
                                 EndRequest(&req, RWindow);
                                 break; 

                /* As you can see, adding more buttons would be easy */
            } 
        } 
    } 
    wleftedge = RWindow->LeftEdge;    /* This is where we track position  */
    wtopedge = RWindow->TopEdge;      /* of the requester if it was moved */
return (response);
} 

SHAR_EOF
#	End of shell archive
exit 0