#include        <quickdraw.h>
#include        <menu.h>
#include        <window.h>
#include        <dialog.h>
#include        <textedit.h>
#include        <event.h>
#include        <desk.h>
#include        <control.h>
#include        <inits.h>
#include        <package.h>
#include        <pb.h>
#include        <syserr.h>
#include        <resource.h>
 
#define maxControls     10
#define sumControls     50
 
#define windowBase      200
#define dialogBase      300
#define menuBase        400
#define controlBase     500
 
 
#define numWindows      5
 
#define raceWind        0
#define classWind       1
#define statusWind      2
#define alignWind       3
#define statistWind     4
 
 
#define numRace         6
 
#define norace          0
#define human           1
#define elf             2
#define dwarf           3
#define gnome           4
#define hobbit          5
 
 
#define numClass        8
 
#define fighter         0
#define mage            1
#define priest          2
#define thief           3
#define bishop          4
#define samurai         5
#define lord            6
#define ninja           7
 
 
#define numStatus       8
 
#define healthy         0
#define afraid          1
#define asleep          2
#define paralysed       3
#define stoned          4
#define dead            5
#define ashes           6
#define lost            7
 
 
#define numAlign        4
 
#define noalign         0
#define nice            1
#define neutral         2
#define evil            3
 
 
#define numStatist      10
 
#define Strength        0
#define Intelligence    1
#define Piety           2
#define Vitality        3
#define Agility         4
#define Luck            5
#define Gold            6
#define Experience      7
#define ActHits         8
#define MaxHits         9
 
 
#define numDialogs      5
 
#define aboutDialog     0
#define saveDialog      1
#define charDialog      2
#define opWrDialog      3
#define ioErrDialog     4
 
 
#define saveItem        1
#define discardItem     2
#define cancelItem      3
 
 
#define numMenus        3
 
#define apMenu          0
#define fileMenu        1
#define charMenu        2
 
                                        /* Start of character storage info */
 
#define charBlock       186             /* size of storage in file for each character */
 
#define nameLength      0
#define charName        1
#define raceLoc         33
#define classLoc        34
#define statusLoc       38
#define alignLoc        39
#define strengthLoc     40
#define IQLoc           41
#define pietyLoc        42
#define vitalityLoc     43
#define agilityLoc      44
#define luckLoc         45
#define goldLoc         54
#define experienceLoc   106
#define actHitsLoc      114
#define maxHitsLoc      116
 
                                        /* Start of program info */
#define DlgTop          100
#define DlgLeft         85
 
MenuHandle      myMenu[numMenus];
EventRecord     curEvent;
WindowRecord    wRecord;
WindowPtr       curWindow, myWindow[numWindows];
ControlHandle   curControl, myControl[numWindows][maxControls];
Rect            limitRect;
 
short           numControls[numWindows] = { numRace, numClass, numStatus, numAlign, numStatist };
short           controlLoc[sumControls] = { raceLoc, classLoc, statusLoc, alignLoc, strengthLoc,
                                            IQLoc, pietyLoc, vitalityLoc, agilityLoc,luckLoc,
                                            goldLoc, experienceLoc, actHitsLoc, maxHitsLoc };
short           curFile ;
unsigned char   stat[charBlock];
Str255          * chName;
Boolean         Dirty, ErrorFlag, FOPENED = FALSE;
 
 
setmenus()                      /* set up menus */
{
        int     menu;
 
        myMenu[apMenu]  = GetMenu(1);
        AddResMenu(myMenu[apMenu], 'DRVR');
        InsertMenu(myMenu[apMenu], 0);
        for (menu = fileMenu; menu <= charMenu; menu++) {
                myMenu[menu]    = GetMenu(menuBase + menu);
                InsertMenu(myMenu[menu], 0);
        }
        for (menu = 1; menu <= 4; menu++)
                DisableItem(myMenu[charMenu], menu);
        DisableItem(myMenu[fileMenu], 2);
        DrawMenuBar();
}
 
 
initialize()
{
        InitGraf(&thePort);
        InitFonts();
        SetEventMask(everyEvent - keyUpMask);
        FlushEvents(everyEvent, 0);
        InitWindows();
        InitMenus();
        TEInit();
        InitDialogs(0L);
        SetCursor(&arrow);
 
        setmenus();
        SetRect(&limitRect, 4, 24, 508, 338);
        curWindow       = 0L;
}
 
 
IOCheck(resCode)                        /* check for IO errors */
        OSErr   resCode;
{
        int     alertDlg, ignore;
        long    longresult;
        Str255  * errString;
 
        switch (resCode)        {
                case noErr      : return;
                case opWrErr    : alertDlg      = opWrDialog;
                                  break;
                default         : alertDlg      = ioErrDialogg;
                                  longresult    = resCode;
                                  NumToString(longresult, errString);
                                  ParamText(errString, "\P ", "\P ", "\P ");
        }
        InitCursor();
        ignore  = StopAlert(dialogBase + alertDlg, 0L);
 
        ErrorFlag       = TRUE;
}
 
        
openfile()                              /* ask for file to open */
{
        Point           dlgOrigin;
        char            fType[30];
        SFReply         theReply;
        OSErr           resultCode;
 
        SetPt(&dlgOrigin, DlgTop, DlgLeft);
        strcpy(fType, "SAVE");
 
        SFGetFile(pass(dlgOrigin), "\P ", 0L, 1, fType, 0L, &theReply);
 
        if (theReply.good)      {
                resultCode      = FSOpen(theReply.fName, theReply.vRefNum, &curFile);
                IOCheck(resultCode);
                FOPENED = TRUE;
                EnableItem(myMenu[fileMenu], 2);
                DisableItem(myMenu[fileMenu], 1);
                EnableItem(myMenu[charMenu], 1);
        }
}
 
 
closeDA()                       /* close a desk accesory */
{
        WindowPeek      whichWindow;
        int             accNum;
 
        whichWindow     = (WindowPeek) FrontWindow();
        accNum          = whichWindow->windowKind;
        CloseDeskAcc(accNum);
}
 
 
closewindows()                          /* close all windows */
{
        int     window;
 
        for (window = raceWind; window <= statistWind; window++)
                DisposeWindow(myWindow[window]);
        curWindow       = 0L;
}
 
 
closefile()                             /* close a file */
{
        int     theItem;
        OSErr   resultCode;
 
        if ((curWindow != 0L) && (FrontWindow() == curWindow))
                closewindows();
        if (FrontWindow() != 0L)
                closeDA();
        resultCode      = FSClose(curFile);
        FOPENED = FALSE;
        DisableItem(myMenu[charMenu], 1);
        EnableItem(myMenu[fileMenu], 1);
        DisableItem(myMenu[fileMenu], 2);
}
 
 
getchname(theName)                      /* get character to edit */
        Str255  * theName;
{
        DialogPtr       charDlg;
        Handle          IHandle;
        Rect            IRect;
        int             itemNum, IType;
 
        charDlg = GetNewDialog(dialogBase + charDialog, 0L, -1L);
        do
                ModalDialog(0L, &itemNum);
        while ((itemNum != OK) && (itemNum != Cancel));
        if (itemNum == Cancel)  {
                        theName->length = 3;
                        strcpy(&(theName->text), "???");
        }
        else    {
                GetDItem(charDlg, 4, &IType, &IHandle, &IRect);
                GetIText(IHandle, theName);
        }
        DisposDialog(charDlg);
}
 
 
updateStats()                           /* draws statistics on window */
{
        static  char    * slabel[10]    = {     "strength  ", "intellect ", "piety     ", "vitality  ", "agility   ",
                                                "luck      ", "gold      ", "experience", "actual HPs", "max HPs   "    };
        
        static  int     hp[10]  = { 15, 15, 15, 260, 260, 260, 15, 15, 260, 260 },
                        vp[10]  = { 34, 59, 84, 34, 59, 84, 134, 159, 134, 159 };
        int             label;
 
        SetPort(myWindow[statistWind]);
        for (label = Strength; label <= MaxHits; label++)       {
                MoveTo(hp[label], vp[label]);
                DrawText(slabel[label], 0, 10);
        }
}
                
 
scale(ItoL, ip, lp)                     /* scales ints <-> longs */
        unsigned char   ItoL;
        unsigned int    * ip;
        unsigned long   * lp;
{
        float           tmp;
        
        if (ItoL)       {
                tmp     = ((float) *ip)/32000;
                *lp     = 2147483000*tmp;
        }
        else    {
                tmp     = ((float) *lp)/2147483000;
                *ip     = 32000*tmp;
        }
}
 
 
openwindows()                           /* opens all windows */
{
        int             wID, cID, * intPtr, tmp, ctlCount       = 0, localBase  = controlBase;
        
        for (wID = raceWind; wID <= statistWind; wID++) {
                myWindow[wID]   = GetNewWindow(windowBase + wID, 0L, -1L);
                for (cID = 0; cID < numControls[wID]; cID++)
                        myControl[wID][cID]     = GetNewControl(localBase + cID, myWindow[wID]);
                if (wID < statistWind)
                        SetCtlValue(myControl[wID][stat[controlLoc[wID]]], 1);
                else    {
                        for (cID = 0; cID < numControls[wID]; cID++)
                                if (cID < Gold)
                                        SetCtlValue(myControl[wID][cID], stat[controlLoc[wID + cID]]);
                                else if (cID != Experience)     {
                                        intPtr  = &(stat[controlLoc[wID + cID]]);
                                        SetCtlValue(myControl[wID][cID], *intPtr);
                                }
                                else    {
                                        scale(0, &tmp, &(stat[experienceLoc]));
                                        SetCtlValue(myControl[wID][cID], tmp);
                                }
                        updateStats();
                }
                localBase       += numControls[wID];
        }
        curWindow       = myWindow[wID - 1];
}
 
 
rdchar()                                /* reads character stats from file */
{
        long    blocklen        = charBlock;
        int     count, chindex;
        
        getchname(chName);
        if (chName->text[0] == '?')
                return;
        for (chindex = 0; chindex < 20; chindex++)      {
                SetFPos(curFile, fsFromStart, chindex*blocklen);
                FSRead(curFile, &blocklen, stat);
                for (count = 1; count <= stat[0]; count++)
                        if (stat[count] != chName->text[count - 1])
                                break;
                if (count > stat[0])    {
                        SetFPos(curFile, fsFromMark, -blocklen);
                        break;
                }
        }
        if (chindex < 20)       {
                Dirty   = FALSE;
                openwindows();
                DisableItem(myMenu[fileMenu], 2);
                DisableItem(myMenu[charMenu], 1);
                for (count = 2; count <= 4; count++)
                        EnableItem(myMenu[charMenu], count);
        }
}
 
 
 
wrchar()                                /* write character stats */
{
        long    blocklen        = charBlock;
        long    curPos;
 
        FSWrite(curFile, &blocklen, stat);
        SetFPos(curFile, fsFromMark, -blocklen);
        Dirty   = FALSE;
}
 
 
clchar()                                /* close a character */
{
        int     theItem;
 
        if (Dirty)      {
                ParamText(chName, "\P ", "\P ", "\P ");
                theItem = CautionAlert(dialogBase + saveDialog, 0L);
                switch  (theItem)       {
                        case saveItem           : wrchar();
                                                  break;
                        case discardItem        : break;
                        case cancelItem         : return;
                }
        }
        closewindows();
        EnableItem(myMenu[fileMenu], 2);
        EnableItem(myMenu[charMenu], 1);
        for (theItem = 2; theItem <= 4; theItem++)
                DisableItem(myMenu[charMenu], theItem);
}
 
 
brag()                                  /* do about stuff */
{
        int     ignore;
        
        ignore  = Alert(dialogBase + aboutDialog, 0L);
}
 
 
doCommand(eventloc)                     /* processes commands */
        Point   eventloc;
{
        unsigned long   mResult;
        char            name[30];
        short           theMenu, theItem;
        long            ticks;
        
        mResult = MenuSelect(pass(eventloc));
        theMenu = mResult >> 16;
        theItem = mResult;
        switch (theMenu - menuBase)     {
                case -399       : if (theItem == 1)
                                        brag();
                                  else  {
                                        GetItem(myMenu[apMenu], theItem, name);
                                        OpenDeskAcc(name);
                                  }
                                  break;
                case fileMenu   : switch (theItem)      {
                                        case 1  : openfile();
                                                  break;
                                        case 2  : closefile();
                                                  break;
                                        case 3  : if (Dirty)
                                                        clchar();
                                                  if (FOPENED)
                                                        closefile();
                                                  _exit();
                                  }
                                  break;
                case charMenu   : switch (theItem)      {
                                        case 1  : rdchar();
                                                  break;
                                        case 2  : wrchar();
                                                  break;
                                        case 3  : clchar();
                                                  break;
                                        case 4  : clchar();
                                                  break;
                                  }
                                  break;
                default         : break;
        }
        HiliteMenu(0);
}
 
 
pascal  void    scrollit(whichControl, whichPart)       /* pascal action procedure for scrolling */
        ControlHandle   whichControl;
        int             whichPart;
{
        int             change  = 0;
        
        switch (whichPart)      {
                case inUpButton         :;
                case inPageUp           : change--;
                                          break;
                case inDownButton       :;
                case inPageDown         : change++;
                                          break;
                default                 :;
        }
        if (whichPart)
                SetCtlValue(whichControl, GetCtlValue(whichControl) + change);
}
 
 
docontent()                             /* clicked in windows */
{
        int     controlPart, control, window, button, localBase = controlBase;
        int     * intPtr, tmpInt;
        
        if (curWindow != FrontWindow()) {
                SelectWindow(curWindow);
                return;
        }
        else    {
                GlobalToLocal(&curEvent.where);
                if (controlPart = FindControl(pass(curEvent.where), curWindow, &curControl))
                        if (curWindow == myWindow[statistWind]) {
                                Dirty   = TRUE;
                                if (controlPart == inThumb)
                                        controlPart     = TrackControl(curControl, pass(curEvent.where), -1L);
                                else
                                        controlPart     = TrackControl(curControl, pass(curEvent.where), scrollit);
                                for (control = Strength; control <= MaxHits; control++)
                                        if (curControl == myControl[statistWind][control])
                                                if (control < Gold)
                                                        stat[controlLoc[statistWind + control]] = GetCtlValue(curControl);
                                                else if (control != Experience) {
                                                        intPtr  = &(stat[controlLoc[statistWind + control]]);
                                                        *intPtr = GetCtlValue(curControl);
                                                }
                                                else    {
                                                        tmpInt  = GetCtlValue(curControl);
                                                        scale(1, &tmpInt, &(stat[experienceLoc]));
                                                }
                        }
                        else if (TrackControl(curControl, pass(curEvent.where), -1L) == inCheckBox)     {
                                Dirty   = TRUE;
                                SetCtlValue(curControl, 1);
                                for (window = raceWind; curWindow != myWindow[window]; window++)
                                        ;
                                for (button = 0; button < numControls[window]; button++)
                                        if (myControl[window][button] == curControl)
                                                stat[controlLoc[window]]        = button;
                                        else
                                                SetCtlValue(myControl[window][button], 0);
                        }
        }
}
 
 
DoActivate()                            /* Handle activate events */
{
        WindowPeek      whichWindow;
        WindowPtr       thisWindow;
        ControlHandle   whichControl;
        int             windnum;
        
        thisWindow = (WindowPtr) curEvent.message;
        for (windnum = 0; windnum < numWindows; windnum++)
                if (thisWindow == myWindow[windnum])
                        break;
        if (windnum < numWindows)       {
                whichWindow     = (WindowPeek) thisWindow;
                SetPort(thisWindow);
                if (curEvent.modifiers & 1)     {
                        curWindow       = thisWindow;
                        for (whichControl = whichWindow->controlList; whichControl != 0L; whichControl = (*whichControl)->nextControl)
                                HiliteControl(whichControl, 0);
                }
                else    {
                        for (whichControl = whichWindow->controlList; whichControl != 0L; whichControl = (*whichControl)->nextControl)
                                HiliteControl(whichControl, 255);
                }
        }
}
 
 
DoUpdate()
{
        WindowPtr       thisWindow;
        int             windnum;
 
        thisWindow = (WindowPtr) curEvent.message;
        for (windnum = 0; windnum < numWindows; windnum++)
                if (thisWindow == myWindow[windnum])
                        break;
        if (windnum < numWindows)       {
                curWindow       = thisWindow;
                BeginUpdate(curWindow);
                DrawControls(curWindow);
                if (windnum == statistWind)
                        updateStats();
                EndUpdate(curWindow);
        }
}
 
 
main()
{
        int     code;
 
        initialize();
        
        for (;;)        {
                SystemTask();
                GetNextEvent(everyEvent, &curEvent);
                
                switch  (curEvent.what) {
                        case mouseDown  :
                                code    = FindWindow(pass(curEvent.where), &curWindow);
                                switch  (code)  {
                                        case inDesk     : break;
                                        case inMenuBar  : doCommand(pass(curEvent.where));
                                                          break;
                                        case inSysWindow: SystemClick(&curEvent, curWindow);
                                                          break;
                                        case inDrag     : DragWindow(curWindow, pass(curEvent.where), &limitRect);
                                                          break;
                                        case inContent  : docontent();
                                                          break;
                                        default         : break;
                                }
                                break;
                        case activateEvt        :
                                DoActivate();
                                break;
                        case updateEvt  :
                                DoUpdate();
                                break;
                        default :
                                break;
                }
        }
}