#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(curContro
l, 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;
               }
       }
}