program rpg; { A helper program for AD&D } { Original Turbo Pascal code by David Mar, } { Converted to THINK Pascal 3.0 and some color added by David Janes 6/27/91} const MenuCnt = 8; { total # of menus } ApplMenu = 1000; { resource ID of Apple Menu } FileMenu = 1001; { " File Menu } EditMenu = 1002; { " Edit Menu } DiceMenu = 1003; { " Dice Menu } TimeMenu = 1004; { " Time Menu } CastMenu = 1005; { " Cast Menu } UtilMenu = 1006; { " Util Menu } CharMenu = 1007; { " Char Menu } AM = 1; { index into MenuList for Apple Menu } FM = 2; { " File Menu } EM = 3; { " Edit Menu } DM = 4; { " Dice Menu } TM = 5; { " Time Menu } CM = 6; { " Cast Menu } UM = 7; { " Util Menu } HM = 8; { " Char Menu } TimeID = 1000; { resource ID for TimeWindow } DiceID = 1001; { " DiceWindow } TreaID = 1002; { " TreaWindow } CharID = 1003; { " CharWindow } InfoID = 1004; { " InfoWindow } AboutID = 1000; { " About box } m1 = 259200; { Random number constants } ia1 = 7141; ic1 = 54773; rm1 = 3.8580247e-6; m2 = 134456; ia2 = 8121; ic2 = 28411; rm2 = 7.4373773e-6; m3 = 243000; ia3 = 4561; ic3 = 51349; type yearrange = 0..2047; { Time types } dayrange = 0..364; hourrange = 0..23; minuterange = 0..59; secondrange = 0..59; timetype = record year: yearrange; day: dayrange; hour: hourrange; minute: minuterange; second: secondrange end; latrange = -90..90; terrainList = (plain, forest, jungle, hills, mountains, desert, broken, swamp, coast, ocean); terraintype = record lat: latrange; alt: Integer; ter: terrainList end; statrange = 0..99; { Character types } ACrange = -10..10; saverange = 1..20; statList = (str, int, wis, dex, con, cha, excep); stattype = array[statList] of statrange; saveList = (death, petpol, rsw, breath, magic); savetype = array[saveList] of saverange; heightrange = 0..127; weightrange = 0..511; HPrange = 0..511; nametype = string[63]; racetype = string[8]; classtype = array[1..3] of 'A'..'T'; leveltype = array[1..3] of 0..63; charactertype = record name: nametype; class: classtype; level: leveltype; race: racetype; height: heightrange; weight: weightrange; DOB: timetype; AC, AC2: ACrange; HP: HPrange; stats: stattype; saves: savetype end; partytype = array[1..10] of charactertype; modetype = (fix, add); padtype = (zero, blank); var Finished: Boolean; { used to terminate the program } theEvent: EventRecord; { event passed from operating system } { Screen stuff} DragArea: Rect; { defines area where window can be dragged in } GrowArea: Rect; { defines area to which a window's size can change } ScreenArea: Rect; { defines screen dimensions } Watch: Cursor; { used to hold watch cursor } { Menu stuff} MenuList: array[1..MenuCnt] of MenuHandle; { holds menu info } { Window stuff } TimePtr, DicePtr, TreaPtr, CharPtr, InfoPtr: WindowPtr; { pointers to windows } ScreenPort: GrafPtr; { pointer to entire screen } { program specific stuff} time: timetype; { Stores current game time } terrain: terraintype; { Stores current terrain } party: partytype; { Stores party info } NumChars, SelChar: 0..10; { Character indices } glix1, glix2, glix3: LongInt; { Random number variables } glr: array[1..97] of real; { *** utility functions and procedures *** } function ran1: real; { Returns uniform deviate from 0 to 1. Initialise with negative idum } var k: Integer; begin glix1 := (ia1 * glix1 + ic1) mod m1; glix2 := (ia2 * glix2 + ic2) mod m2; glix3 := (ia3 * glix3 + ic3) mod m3; k := 1 + (97 * glix3) div m3; ran1 := glr[k]; glr[k] := (glix1 + glix2 * rm2) * rm1 end; function die (dietype: integer): integer; { Returns a single die throw in the range 1..dietype } begin die := trunc(dietype * ran1) + 1 end; function dice (numdice, dietype: integer): integer; { Returns sum of numdice throws in range 1..dietype } var temp, i: integer; begin temp := 0; for i := 1 to numdice do temp := temp + die(dietype); dice := temp end; function Pad (padding, theNum: LongInt; leader: padtype): str255; { Outputs a string of padding characters containing theNum, padded } { with leading zeroes or blanks if necessary } var temp: str255; begin NumToString(theNum, temp); while (Length(temp) < padding) do if leader = zero then temp := concat('0', temp) else temp := concat(' ', temp); Pad := temp end; { func Pad } function Max (x, y: Integer): Integer; begin if x > y then Max := x else Max := y end; { func Max } function OKNumber (theString: Str255; Min, Max: Integer; var theNum: LongInt; theDialog: DialogPtr; theItem: Integer): Boolean; { decides if an input string is okay as a numerical input, returning the} { result in theNum if it is, beeping the dialog if not } var i: Integer; temp: Boolean; OKChars: set of char; begin OKChars := ['+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; temp := true; for i := 1 to Length(theString) do if not (theString[i] in OKChars) then temp := false; StringToNum(theString, theNum); if (theNum < Min) or (theNum > Max) then temp := false; if not (temp) then SelIText(theDialog, theItem, 0, 32767); OKNumber := temp end; { func OKNumber } function GetDayMonth (DayNum: dayrange): str255; {Returns day/month format string given day of year in Greyhawk calendar} var GoneMonths: 0..12; GoneFests: 0..3; Day, Month: str255; DaysLeft: dayrange; begin GoneMonths := 0; GoneFests := 0; DaysLeft := DayNum; while ((((GoneMonths div 3) = GoneFests) and (DaysLeft > 28)) or (((GoneMonths div 3) <> GoneFests) and (DaysLeft > 7))) do begin if ((GoneMonths div 3) = GoneFests) then begin DaysLeft := DaysLeft - 28; GoneMonths := GoneMonths + 1 end else begin DaysLeft := DaysLeft - 7; GoneFests := GoneFests + 1 end end; if (GoneMonths - GoneFests * 3 = 3) then case GoneFests of 0: Month := 'Gr'; 1: Month := 'Ri'; 2: Month := 'Br'; 3: Month := 'Ne' end else NumToString(GoneMonths + 1, Month); NumToString(DaysLeft, Day); if (Length(Month) = 1) then Month := concat('0', Month); if (Length(Day) = 1) then Day := concat('0', Day); GetDayMonth := concat(Day, '/', Month); end; { function GetDayMonth } procedure AddTime (var thetime, theinterval: timetype); var temp: Integer; begin temp := thetime.second + theinterval.second; thetime.second := temp mod 60; temp := thetime.minute + theinterval.minute + temp div 60; thetime.minute := temp mod 60; temp := thetime.hour + theinterval.hour + temp div 60; thetime.hour := temp mod 24; temp := thetime.day + theinterval.day + temp div 24; thetime.day := temp mod 364; if (thetime.day = 0) then thetime.day := 364; temp := thetime.year + theinterval.year + (temp - 1) div 364; thetime.year := temp end; { proc AddTime } procedure DrawMoon (x, y, rad: Integer; phase: Real); { Draws a moon at the given position with the given phase } var Invert: Boolean; Moon, Shadow: Rect; PartRad: Integer; begin Invert := false; with Moon do begin top := y - rad; bottom := y + rad; left := x - rad; right := x + rad end; FrameOval(Moon); if (phase < 0.25) then begin phase := phase + 0.5; Invert := true end; if (phase >= 0.75) then begin phase := phase - 0.5; Invert := true end; PartRad := Trunc(0.4 + Abs(cos(6.2831853072 * phase) * rad)); InsetRect(Moon, 1, 1); Shadow := Moon; Shadow.left := x - PartRad; Shadow.right := x + PartRad; PaintOval(Shadow); if (phase < 0.5) then PaintArc(Moon, 180, 180) else PaintArc(Moon, 0, 180); if Invert then InvertOval(Moon) end; {proc DrawMoon} procedure ClearWindow (WPtr: WindowPtr); begin EraseRect(WPtr^.portRect) end; { of proc ClearWindow } procedure WriteTime; { Writes time in Greyhawk Calendar following the format} { day/month/year} { hour:minute:second} { Festivals are denoted by the first two letters in place of a month} { number, viz:} { "Gr"owfest, "Ri"chfest, "Br"ewfest, "Ne"edfest. } var display: str255; SavePort: WindowPtr; MoonPhase: Real; begin GetPort(SavePort); SetPort(TimePtr); ClearWindow(TimePtr); NumToString(time.year, display); display := concat(GetDayMonth(time.day), '/', display); MoveTo(20, 20); DrawString(display); display := concat(Pad(2, time.hour, zero), ':', Pad(2, time.minute, zero), ':', Pad(2, time.second, zero)); MoveTo(20, 40); DrawString(display); case ((time.day) mod 7) of 0: display := 'Saturday'; 1: display := 'Sunday'; 2: display := 'Monday'; 3: display := 'Tuesday'; 4: display := 'Wednesday'; 5: display := 'Thursday'; 6: display := 'Friday' end; MoveTo(20, 60); DrawString(display); MoonPhase := ((time.day + 17) mod 28) / 28; DrawMoon(32, 92, 30, MoonPhase); MoonPhase := ((time.day + 3) mod 91) / 91; DrawMoon(80, 92, 17, MoonPhase); MoveTo(20, 130); display := 'Luna Celene'; DrawString(display); SetPort(SavePort) end; { proc WriteTime } procedure WriteChar; { Displays a character file } var display: str255; SavePort: WindowPtr; begin GetPort(SavePort); SetPort(CharPtr); ClearWindow(CharPtr); if (SelChar > 0) then with party[SelChar] do begin MoveTo(5, 10); DrawString(name); MoveTo(5, 20); DrawString(race); MoveTo(100, 20); display := concat(class[1], Pad(1, level[1], zero)); if (class[2] <> 'N') then display := concat(display, '/', class[2], Pad(1, level[2], zero)); if (class[3] <> 'N') then display := concat(display, '/', class[3], Pad(1, level[3], zero)); DrawString(display); MoveTo(5, 40); display := concat('Str:', Pad(3, stats[str], blank)); if ((stats[str] = 18) and (class[1] in ['F', 'P', 'R'])) then display := concat(display, '/', Pad(2, stats[excep], zero)); DrawString(display); MoveTo(5, 50); display := concat('Int:', Pad(3, stats[int], blank)); DrawString(display); MoveTo(5, 60); display := concat('Wis:', Pad(3, stats[wis], blank)); DrawString(display); MoveTo(5, 70); display := concat('Dex:', Pad(3, stats[dex], blank)); DrawString(display); MoveTo(5, 80); display := concat('Con:', Pad(3, stats[con], blank)); DrawString(display); MoveTo(5, 90); display := concat('Chr:', Pad(3, stats[cha], blank)); DrawString(display); MoveTo(100, 40); display := concat('AC: ', Pad(2, AC, blank), '/', Pad(1, AC2, blank)); DrawString(display); MoveTo(100, 50); display := concat('HP: ', Pad(2, HP, blank)); DrawString(display); MoveTo(100, 70); display := concat('Height: ', Pad(2, height, blank), '"'); DrawString(display); MoveTo(100, 80); display := concat('Weight: ', Pad(2, weight, blank), '#'); DrawString(display); MoveTo(5, 110); display := concat('Paral/Poison/Death:', Pad(3, saves[death], blank)); DrawString(display); MoveTo(5, 120); display := concat('Petrifac/Polymorph:', Pad(3, saves[petpol], blank)); DrawString(display); MoveTo(5, 130); display := concat('Rod/Staff/Wand :', Pad(3, saves[rsw], blank)); DrawString(display); MoveTo(5, 140); display := concat('Breath Weapon :', Pad(3, saves[breath], blank)); DrawString(display); MoveTo(5, 150); display := concat('Magic/Spells :', Pad(3, saves[magic], blank)); DrawString(display); end; SetPort(SavePort) end; { proc WriteChar } { ********* items in Char Menu *********** } procedure DoNoChar; { Selects no character to be displayed } begin if SelChar <> 0 then SetItemMark(MenuList[HM], SelChar + 2, Chr(NoMark)); SelChar := 0; SetItemMark(MenuList[HM], 1, Chr(CheckMark)); WriteChar end; procedure DoSelChar (n: integer); { Selects a specified character to be displayed } begin if SelChar <> 0 then SetItemMark(MenuList[HM], SelChar + 2, Chr(NoMark)) else SetItemMark(MenuList[HM], 1, Chr(NoMark)); SelChar := n; SetItemMark(MenuList[HM], SelChar + 2, Chr(CheckMark)); WriteChar end; {$S FileSeg} procedure DoCharDialog (mode: modetype); { Open, process, and close the Character dialog box. } { NOTE: saving throws follow a smooth linear progression with level,} { unlike official AD&D rules, but are close to the official values. } const humanBtn = 3; elfBtn = 4; halfelfBtn = 5; dwarfBtn = 6; gnomeBtn = 7; halflingBtn = 8; halforcBtn = 9; nameEdit = 20; fighterEdit = 21; muEdit = 22; clericEdit = 23; thiefEdit = 24; paladinEdit = 25; rangerEdit = 26; illusionistEdit = 27; druidEdit = 28; assassinEdit = 29; hpEdit = 30; acEdit = 31; ac2Edit = 32; heightEdit = 33; weightEdit = 34; yearEdit = 35; monthEdit = 36; dayEdit = 37; strEdit = 38; intEdit = 39; wisEdit = 40; dexEdit = 41; conEdit = 42; chaEdit = 43; excepEdit = 44; type typeOfChar = (C, F, MU, T, Z); slopeInt = (m, b, min); var theDialog: DialogPtr; itemHit: Integer; theType: Integer; r: Rect; radButton: array[humanBtn..halforcBtn] of ControlHandle; done, OKinput: Boolean; n, i: Integer; edits: array[nameEdit..excepEdit] of Handle; s: array[nameEdit..excepEdit] of Str255; vals: array[fighterEdit..excepEdit] of LongInt; theRace: string[8]; h: handle; SavePort: GrafPtr; num: Integer; classList: string[9]; saveData: array[C..T, saveList, slopeInt] of Real; theChar: typeOfChar; theSave: saveList; begin if mode = fix then begin num := NumChars + 1; if (num > 10) then begin if (Alert(512, nil) > 0) then {nothing} ; Exit(DoCharDialog); end end else begin if SelChar = 0 then begin SysBeep(10); Exit(DoCharDialog); end; num := SelChar end; classList := 'FMCTPRIDA'; saveData[C, death, m] := -4 / 9; saveData[C, death, b] := 34 / 3; saveData[C, death, min] := 2.1; saveData[C, petpol, m] := -4 / 9; saveData[C, petpol, b] := 43 / 3; saveData[C, petpol, min] := 5.1; saveData[C, rsw, m] := -3 / 7; saveData[C, rsw, b] := 107 / 7; saveData[C, rsw, min] := 6.1; saveData[C, breath, m] := -4 / 9; saveData[C, breath, b] := 52 / 3; saveData[C, breath, min] := 8.1; saveData[C, magic, m] := -4 / 9; saveData[C, magic, b] := 49 / 3; saveData[C, magic, min] := 7.1; saveData[F, death, m] := -0.75; saveData[F, death, b] := 16; saveData[F, death, min] := 3.1; saveData[F, petpol, m] := -0.75; saveData[F, petpol, b] := 17; saveData[F, petpol, min] := 4.1; saveData[F, rsw, m] := -0.75; saveData[F, rsw, b] := 18; saveData[F, rsw, min] := 5.1; saveData[F, breath, m] := -1; saveData[F, breath, b] := 19; saveData[F, breath, min] := 4.1; saveData[F, magic, m] := -0.75; saveData[F, magic, b] := 18.75; saveData[F, magic, min] := 6.1; saveData[MU, death, m] := -2 / 7; saveData[MU, death, b] := 106 / 7; saveData[MU, death, min] := 8.1; saveData[MU, petpol, m] := -0.4; saveData[MU, petpol, b] := 14.2; saveData[MU, petpol, min] := 5.1; saveData[MU, rsw, m] := -0.4; saveData[MU, rsw, b] := 12.2; saveData[MU, rsw, min] := 3.1; saveData[MU, breath, m] := -0.4; saveData[MU, breath, b] := 16.2; saveData[MU, breath, min] := 7.1; saveData[MU, magic, m] := -0.4; saveData[MU, magic, b] := 13.2; saveData[MU, magic, min] := 4.1; saveData[T, death, m] := -0.25; saveData[T, death, b] := 14; saveData[T, death, min] := 8.1; saveData[T, petpol, m] := -0.25; saveData[T, petpol, b] := 13; saveData[T, petpol, min] := 7.1; saveData[T, rsw, m] := -0.5; saveData[T, rsw, b] := 15.5; saveData[T, rsw, min] := 4.1; saveData[T, breath, m] := -0.25; saveData[T, breath, b] := 17; saveData[T, breath, min] := 11.1; saveData[T, magic, m] := -0.5; saveData[T, magic, b] := 16; saveData[T, magic, min] := 5.1; GetPort(SavePort); theDialog := GetNewDialog(259, nil, Pointer(-1)); SetPort(theDialog); GetDItem(theDialog, ok, theType, h, r); InsetRect(r, -4, -4); PenSize(3, 3); FrameRoundRect(r, 16, 16); PenSize(1, 1); SetPort(SavePort); for n := humanBtn to halforcBtn do GetDItem(theDialog, n, theType, Handle(radButton[n]), r); if mode = fix then begin SetCtlValue(radButton[humanBtn], 1); theRace := 'Human' end else begin theRace := party[SelChar].race; case theRace[Length(theRace) - 2] of 'm': SetCtlValue(radButton[humanBtn], 1); 'E': if theRace = 'Elf' then SetCtlValue(radButton[elfBtn], 1) else SetCtlValue(radButton[halfelfBtn], 1); 'a': SetCtlValue(radButton[dwarfBtn], 1); 'o': SetCtlValue(radButton[gnomeBtn], 1); 'i': SetCtlValue(radButton[halflingBtn], 1); 'O': SetCtlValue(radButton[halforcBtn], 1) end {case} end; for n := nameEdit to excepEdit do GetDItem(theDialog, n, theType, edits[n], r); for n := fighterEdit to excepEdit do s[n] := '0'; if mode = add then with party[SelChar] do begin s[nameEdit] := name; SetIText(edits[nameEdit], name); SelIText(theDialog, nameEdit, 0, 32767); NumToString(HP, s[hpEdit]); SetIText(edits[hpEdit], s[hpEdit]); NumToString(AC, s[acEdit]); SetIText(edits[acEdit], s[acEdit]); NumToString(AC2, s[ac2Edit]); SetIText(edits[ac2Edit], s[ac2Edit]); NumToString(height, s[heightEdit]); SetIText(edits[heightEdit], s[heightEdit]); NumToString(weight, s[weightEdit]); SetIText(edits[weightEdit], s[weightEdit]); NumToString(DOB.year, s[yearEdit]); SetIText(edits[yearEdit], s[yearEdit]); s[dayEdit] := GetDayMonth(DOB.day); s[monthEdit] := Copy(s[dayEdit], 4, 2); s[dayEdit] := Copy(s[dayEdit], 1, 2); SetIText(edits[monthEdit], s[monthEdit]); SetIText(edits[dayEdit], s[dayEdit]); for n := strEdit to excepEdit do begin NumToString(stats[statList(n - 38)], s[n]); SetIText(edits[n], s[n]) end; for n := 1 to 3 do if class[n] <> 'N' then begin case class[n] of 'F': begin NumToString(level[n], s[fighterEdit]); SetIText(edits[fighterEdit], s[fighterEdit]) end; 'M': begin NumToString(level[n], s[muEdit]); SetIText(edits[muEdit], s[muEdit]) end; 'C': begin NumToString(level[n], s[clericEdit]); SetIText(edits[clericEdit], s[clericEdit]) end; 'T': begin NumToString(level[n], s[thiefEdit]); SetIText(edits[thiefEdit], s[thiefEdit]) end; 'P': begin NumToString(level[n], s[paladinEdit]); SetIText(edits[paladinEdit], s[paladinEdit]) end; 'R': begin NumToString(level[n], s[rangerEdit]); SetIText(edits[rangerEdit], s[rangerEdit]) end; 'I': begin NumToString(level[n], s[illusionistEdit]); SetIText(edits[illusionistEdit], s[illusionistEdit]) end; 'D': begin NumToString(level[n], s[druidEdit]); SetIText(edits[druidEdit], s[druidEdit]) end; 'A': begin NumToString(level[n], s[assassinEdit]); SetIText(edits[assassinEdit], s[assassinEdit]) end end {case} end end; repeat done := False; OKinput := True; repeat ModalDialog(nil, itemHit); case itemHit of ok, cancel: done := True; humanBtn..halforcBtn: begin for n := humanBtn to halforcBtn do SetCtlValue(radButton[n], Ord(n = itemHit)); case itemHit of humanBtn: theRace := 'Human'; elfBtn: theRace := 'Elf'; halfelfBtn: theRace := 'Half-Elf'; dwarfBtn: theRace := 'Dwarf'; gnomeBtn: theRace := 'Gnome'; halflingBtn: theRace := 'Halfling'; halforcBtn: theRace := 'Half-Orc' end end; nameEdit..excepEdit: GetIText(edits[itemHit], s[itemHit]) end until done; if (itemHit = ok) then begin { Check for ok input } theType := 0; for n := fighterEdit to assassinEdit do begin if s[n] <> '' then OKInput := OKInput and OKNumber(s[n], 0, 63, vals[n], theDialog, n) else vals[n] := 0; if vals[n] > 0 then begin theType := theType + 1; if theType = 4 then begin OKInput := false; SelIText(theDialog, n, 0, 32767) end end end; if theType < 1 then begin OKInput := false; SelIText(theDialog, fighterEdit, 0, 32767) end; OKInput := OKInput and OKNumber(s[hpEdit], 1, 511, vals[hpEdit], theDialog, hpEdit); OKInput := OKInput and OKNumber(s[acEdit], -10, 10, vals[acEdit], theDialog, acEdit); OKInput := OKInput and OKNumber(s[ac2Edit], -10, 10, vals[ac2Edit], theDialog, ac2Edit); OKInput := OKInput and OKNumber(s[heightEdit], 1, 127, vals[heightEdit], theDialog, heightEdit); OKInput := OKInput and OKNumber(s[weightEdit], 1, 511, vals[weightEdit], theDialog, weightEdit); OKInput := OKInput and OKNumber(s[yearEdit], 0, 2047, vals[yearEdit], theDialog, yearEdit); OKInput := OKInput and OKNumber(s[dayEdit], 1, 28, vals[dayEdit], theDialog, dayEdit); for n := strEdit to chaEdit do OKInput := OKInput and OKNumber(s[n], 3, 25, vals[n], theDialog, n); OKInput := OKInput and OKNumber(s[excepEdit], 0, 99, vals[excepEdit], theDialog, excepEdit); case s[monthEdit][1] of 'G', 'g': vals[dayEdit] := vals[dayEdit] + 84; 'R', 'r': vals[dayEdit] := vals[dayEdit] + 175; 'B', 'b': vals[dayEdit] := vals[dayEdit] + 266; 'N', 'n': vals[dayEdit] := vals[dayEdit] + 357; otherwise if OKNumber(s[monthEdit], 1, 12, vals[monthEdit], theDialog, monthEdit) then vals[dayEdit] := vals[dayEdit] + (vals[monthEdit] - 1) * 28 + ((vals[monthEdit] - 1) div 3) * 7 else OKinput := False end; {case} if ((vals[dayEdit] > 364) or (vals[dayEdit] < 0)) then begin SelIText(theDialog, monthEdit, 0, 32767); OKinput := False end; if (not (OKinput)) then SysBeep(10) end until OKinput; if (itemHit = ok) then { Process input } with party[num] do begin name := s[nameEdit]; race := theRace; height := vals[heightEdit]; weight := vals[weightEdit]; AC := vals[acEdit]; AC2 := vals[ac2Edit]; HP := vals[hpEdit]; DOB.year := vals[yearEdit]; DOB.day := vals[dayEdit]; stats[str] := vals[strEdit]; stats[int] := vals[intEdit]; stats[wis] := vals[wisEdit]; stats[dex] := vals[dexEdit]; stats[con] := vals[conEdit]; stats[cha] := vals[chaEdit]; if s[excepEdit] = '' then stats[excep] := 0 else stats[excep] := vals[excepEdit]; class[1] := 'N'; class[2] := 'N'; class[3] := 'N'; for n := fighterEdit to assassinEdit do if (class[1] = 'N') then begin if (vals[n] > 0) then begin class[1] := classList[n - 20]; level[1] := vals[n] end end else if (class[2] = 'N') then begin if (vals[n] > 0) then begin class[2] := classList[n - 20]; level[2] := vals[n] end end else if (class[3] = 'N') then begin if (vals[n] > 0) then begin class[3] := classList[n - 20]; level[3] := vals[n] end end; for n := 1 to 3 do begin case class[n] of 'F', 'P', 'R': theChar := F; 'M', 'I': theChar := MU; 'C', 'D': theChar := C; 'T', 'A': theChar := T; 'N': theChar := Z end; {case} if n = 1 then for theSave := death to magic do saves[theSave] := Max(Trunc(level[n] * saveData[theChar, theSave, m] + saveData[theChar, theSave, b] + 0.01), Trunc(saveData[theChar, theSave, min])) else if theChar <> Z then for theSave := death to magic do begin i := Max(Trunc(level[n] * saveData[theChar, theSave, m] + saveData[theChar, theSave, b] + 0.01), Trunc(saveData[theChar, theSave, min])); if i < saves[theSave] then saves[theSave] := i end end; if mode = fix then begin NumChars := num; if (Length(s[nameEdit]) > 5) then { Get truncated name and } for n := 5 to Length(s[nameEdit]) do { insert in Char menu } if (s[nameEdit][n] = ' ') then s[nameEdit] := Copy(s[nameEdit], 1, n - 1); InsMenuItem(MenuList[HM], s[nameEdit], NumChars + 1) end else begin DelMenuItem(MenuList[HM], SelChar + 2); if (Length(s[nameEdit]) > 5) then { Get truncated name and } for n := 5 to Length(s[nameEdit]) do { insert in Char menu } if (s[nameEdit][n] = ' ') then s[nameEdit] := Copy(s[nameEdit], 1, n - 1); InsMenuItem(MenuList[HM], s[nameEdit], SelChar + 1) end end; DisposDialog(theDialog); SetItemMark(MenuList[HM], 1, Chr(NoMark)); DoSelChar(num) end; { proc DoCharDialog } { ********* items in File Menu *********** } procedure DoNew; { Define a new character } begin DoCharDialog(fix) end; procedure DoOpen; { Open a saved character file } var where: Point; prompt: Str255; reply: SFReply; refNum, n: Integer; errCode: Integer; byteCount: LongInt; typeList: SFTypeList; begin if (NumChars = 10) then begin if (Alert(512, nil) > 0) then {nothing} ; Exit(DoOpen); end; where.v := 50; where.h := 50; typeList[0] := 'ADCH'; SFGetFile(where, prompt, nil, 1, typeList, nil, reply); with reply do begin if not (good) then Exit(DoOpen); errCode := FSOpen(fName, vRefNum, refNum); if errCode <> noErr then Exit(DoOpen); byteCount := SizeOf(charactertype); errCode := FSRead(refNum, byteCount, @party[NumChars + 1]); if errCode <> noErr then Exit(DoOpen); errCode := FSClose(refNum) end; NumChars := NumChars + 1; prompt := party[NumChars].name; if (Length(prompt) > 5) then { Get truncated name and } for n := 5 to Length(prompt) do { insert in Character menu } if (prompt[n] = ' ') then prompt := Copy(prompt, 1, n - 1); InsMenuItem(MenuList[HM], prompt, NumChars + 1); DoSelChar(NumChars) end; procedure DoClose; { Close a character file } var Temp: charactertype; i: Integer; begin if (SelChar > 0) then begin if (SelChar <> NumChars) then for i := SelChar to NumChars - 1 do party[i] := party[i + 1]; i := SelChar + 2; DoNoChar; NumChars := NumChars - 1; DelMenuItem(MenuList[HM], i) end else SysBeep(10) end; procedure DoSave; { Saves an old character file } begin { unimplemented } end; procedure DoSaveAs; { Saves a new character file } var where: Point; prompt, theName: Str255; reply: SFReply; refNum: Integer; errCode: Integer; byteCount: LongInt; begin if SelChar = 0 then begin SysBeep(10); Exit(DoSaveAs); end; where.v := 50; where.h := 50; prompt := 'Save character as:'; theName := party[SelChar].name; SFPutFile(where, prompt, theName, nil, reply); with reply do begin if not (good) then Exit(DoSaveAs); errCode := Create(fName, vRefNum, 'ADAD', 'ADCH'); if errCode <> noErr then Exit(DoSaveAs); errCode := FSOpen(fName, vRefNum, refNum); if errCode <> noErr then Exit(DoSaveAs); byteCount := SizeOf(charactertype); errCode := FSWrite(refNum, byteCount, @party[SelChar]); if errCode <> noErr then Exit(DoSaveAs); errCode := FSClose(refNum); errCode := FlushVol(nil, 0) end end; { proc DoSaveAs } procedure DoModify; { Modify an existing character } begin DoCharDialog(add) end; {$S } procedure DoQuit; { Quit. Obviously } begin Finished := true end; { ********* items in Dice Menu *********** } procedure DoDice (TheDie: integer); var Throws, Totals: array[1..20] of Integer; Index: 1..20; display: str255; SavePort: WindowPtr; begin for Index := 1 to 20 do Totals[Index] := 0; for Index := 1 to 20 do begin Throws[Index] := die(TheDie); if Index = 1 then Totals[Index] := Throws[Index] else Totals[Index] := Totals[Index - 1] + Throws[Index]; end; {WriteDice} GetPort(SavePort); SetPort(DicePtr); ClearWindow(DicePtr); MoveTo(10, 10); NumToString(TheDie, display); display := concat('d', display, 's'); DrawString(display); MoveTo(5, 20); display := 'Num Die Tot'; DrawString(display); for Index := 1 to 20 do begin MoveTo(5, 20 + Index * 10); display := concat(' ', Pad(2, Index, blank), ' ', Pad(3, Throws[Index], blank), ' ', Pad(4, Totals[Index], blank)); DrawString(display) end; SetPort(SavePort) end; procedure DoOther; { Open, process, and close the Dice dialog box. } const DiceRef = 5; var theDialog: DialogPtr; itemHit: Integer; theType: Integer; r: Rect; edText: ControlHandle; done, OKinput: Boolean; h: Handle; newstr: Str255; new: Longint; SavePort: GrafPtr; begin GetPort(SavePort); theDialog := GetNewDialog(257, nil, Pointer(-1)); SetPort(theDialog); GetDItem(theDialog, ok, theType, h, r); InsetRect(r, -4, -4); PenSize(3, 3); FrameRoundRect(r, 16, 16); PenSize(1, 1); SetPort(SavePort); GetDItem(theDialog, DiceRef, theType, Handle(edText), r); { Set initial values in text boxes and storage array} SelIText(theDialog, DiceRef, 0, 1); newstr := '1'; repeat done := False; OKinput := True; repeat ModalDialog(nil, itemHit); case itemHit of ok, cancel: done := True; DiceRef: GetIText(Handle(edText), newstr) end; until done; if (itemHit = ok) then begin { Convert the input number to the die type } OKInput := OKNumber(newstr, 1, 1000, new, theDialog, DiceRef); if (not (OKinput)) then SysBeep(10) end until OKinput; DisposDialog(theDialog); if (itemHit = ok) then DoDice(new) end; { proc DoOther } { ********* items in Time Menu *********** } procedure DoTurn; { Add a turn to the time } var interval: timetype; begin with interval do begin year := 0; day := 0; hour := 0; minute := 10; second := 0 end; AddTime(time, interval); WriteTime end; procedure DoRound; { Add a round to the time } var interval: timetype; begin with interval do begin year := 0; day := 0; hour := 0; minute := 1; second := 0 end; AddTime(time, interval); WriteTime end; procedure DoEnd; { End a combat: round time up to the nearest turn } var interval: timetype; begin with interval do begin year := 0; day := 0; hour := 0; second := 0 end; time.second := 0; interval.minute := (60 - time.minute) mod 10; if (interval.minute = 0) then interval.minute := 10; AddTime(time, interval); WriteTime end; procedure DoDateDialog (mode: modetype); { Open, process, and close the Date dialog box. } const YearRef = 10; MonthRef = 11; DayRef = 12; HourRef = 13; MinuteRef = 14; SecondRef = 15; var theDialog: DialogPtr; itemHit: Integer; theType: Integer; r: Rect; edText: array[YearRef..SecondRef] of ControlHandle; done, OKinput: Boolean; n: Integer; h: Handle; s, t: Str255; newstr: array[YearRef..SecondRef] of Str255; new: array[YearRef..SecondRef] of Longint; interval: timetype; SavePort: GrafPtr; begin if (mode = fix) then ParamText('Set Time To:', 'Month:', '', '') else ParamText('Add Time Interval:', 'Weeks:', 's', ''); GetPort(SavePort); theDialog := GetNewDialog(256, nil, Pointer(-1)); SetPort(theDialog); GetDItem(theDialog, ok, theType, h, r); InsetRect(r, -4, -4); PenSize(3, 3); FrameRoundRect(r, 16, 16); PenSize(1, 1); SetPort(SavePort); for n := YearRef to SecondRef do GetDItem(theDialog, n, theType, Handle(edText[n]), r); { Set initial values in text boxes and storage array} if (mode = fix) then with time do begin s := GetDayMonth(day); t := Copy(s, 4, 2); SetIText(Handle(edText[MonthRef]), t); newstr[MonthRef] := t; t := Copy(s, 1, 2); SetIText(Handle(edText[DayRef]), t); newstr[DayRef] := t; NumToString(hour, s); SetIText(Handle(edText[HourRef]), s); newstr[HourRef] := s; NumToString(minute, s); SetIText(Handle(edText[MinuteRef]), s); newstr[MinuteRef] := s; NumToString(second, s); SetIText(Handle(edText[SecondRef]), s); newstr[SecondRef] := s; NumToString(year, s); SetIText(Handle(edText[YearRef]), s); newstr[YearRef] := s end else for n := YearRef to SecondRef do begin SetIText(Handle(edText[n]), '0'); newstr[n] := '0'; s := '0' end; SelIText(theDialog, YearRef, 0, 32767); repeat done := False; OKinput := True; repeat ModalDialog(nil, itemHit); case itemHit of ok, cancel: done := True; YearRef..SecondRef: GetIText(Handle(edText[itemHit]), newstr[itemHit]) end; until done; if (itemHit = ok) then begin { Convert the input numbers to the new time } OKInput := OKInput and OKNumber(newstr[YearRef], 0, 2047, new[YearRef], theDialog, YearRef); OKInput := OKInput and OKNumber(newstr[DayRef], 1, 28, new[DayRef], theDialog, DayRef); OKInput := OKInput and OKNumber(newstr[HourRef], 0, 23, new[HourRef], theDialog, HourRef); OKInput := OKInput and OKNumber(newstr[MinuteRef], 0, 59, new[MinuteRef], theDialog, MinuteRef); OKInput := OKInput and OKNumber(newstr[SecondRef], 0, 59, new[SecondRef], theDialog, SecondRef); if (mode = fix) then case newstr[MonthRef][1] of 'G', 'g': new[DayRef] := new[DayRef] + 84; 'R', 'r': new[DayRef] := new[DayRef] + 175; 'B', 'b': new[DayRef] := new[DayRef] + 266; 'N', 'n': new[DayRef] := new[DayRef] + 357; otherwise if OKNumber(newstr[MonthRef], 1, 12, new[MonthRef], theDialog, MonthRef) then new[DayRef] := new[DayRef] + (new[MonthRef] - 1) * 28 + ((new[MonthRef] - 1) div 3) * 7 else begin SelIText(theDialog, MonthRef, 0, 32767); OKinput := False end end {case} else if OKNumber(newstr[MonthRef], 1, 52, new[MonthRef], theDialog, MonthRef) then new[DayRef] := new[DayRef] + 7 * new[MonthRef] else OKinput := False; if ((new[DayRef] > 364) or (new[DayRef] < 0)) then begin SelIText(theDialog, MonthRef, 0, 32767); OKinput := False end; if (not (OKinput)) then SysBeep(10) end until OKinput; if (itemHit = ok) then begin if (mode = fix) then with time do begin year := new[YearRef]; day := new[DayRef]; hour := new[HourRef]; minute := new[MinuteRef]; second := new[SecondRef] end else begin with interval do begin year := new[YearRef]; day := new[DayRef]; hour := new[HourRef]; minute := new[MinuteRef]; second := new[SecondRef] end; AddTime(time, interval) end end; DisposDialog(theDialog); WriteTime end; { proc DoDateDialog } procedure DoDay; { Add a day to the time, and set time to 08:00:00 } var interval: timetype; begin with interval do begin year := 0; day := 1; hour := 0; minute := 0; second := 0 end; with time do begin hour := 8; minute := 0; second := 0 end; AddTime(time, interval); WriteTime end; {proc DoDay} procedure DoTerrainDialog; { Open, process, and close the Terrain dialog box. } { NOTE: for future use in weather generation! } const LatRef = 3; AltRef = 4; PlainRef = 5; ForestRef = 6; JungleRef = 7; HillsRef = 8; MountainsRef = 9; DesertRef = 10; BrokenRef = 11; SwampRef = 12; CoastRef = 13; OceanRef = 14; var theDialog: DialogPtr; itemHit: Integer; theType: Integer; r: Rect; radButton: array[PlainRef..OceanRef] of ControlHandle; edText: array[LatRef..AltRef] of ControlHandle; done, OKinput: Boolean; n: Integer; h: Handle; s: Str255; newstr: array[LatRef..AltRef] of Str255; new: array[LatRef..AltRef] of Longint; SavePort: GrafPtr; theTer: terrainList; begin GetPort(SavePort); theDialog := GetNewDialog(260, nil, Pointer(-1)); SetPort(theDialog); GetDItem(theDialog, ok, theType, h, r); InsetRect(r, -4, -4); PenSize(3, 3); FrameRoundRect(r, 16, 16); PenSize(1, 1); SetPort(SavePort); for n := LatRef to AltRef do GetDItem(theDialog, n, theType, Handle(edText[n]), r); for n := PlainRef to OceanRef do GetDItem(theDialog, n, theType, Handle(radButton[n]), r); { Set initial values in text boxes and storage array} with terrain do begin theTer := ter; new[LatRef] := lat; new[AltRef] := alt; NumToString(lat, s); SetIText(Handle(edText[LatRef]), s); newstr[LatRef] := s; NumToString(alt, s); SetIText(Handle(edText[AltRef]), s); newstr[AltRef] := s; case ter of plain: SetCtlValue(radButton[PlainRef], 1); forest: SetCtlValue(radButton[ForestRef], 1); jungle: SetCtlValue(radButton[JungleRef], 1); hills: SetCtlValue(radButton[HillsRef], 1); mountains: SetCtlValue(radButton[MountainsRef], 1); desert: SetCtlValue(radButton[DesertRef], 1); broken: SetCtlValue(radButton[BrokenRef], 1); swamp: SetCtlValue(radButton[SwampRef], 1); coast: SetCtlValue(radButton[CoastRef], 1); ocean: SetCtlValue(radButton[OceanRef], 1); end {case} end; SelIText(theDialog, LatRef, 0, 32767); repeat done := False; OKinput := True; repeat ModalDialog(nil, itemHit); case itemHit of ok, cancel: done := True; LatRef..AltRef: GetIText(Handle(edText[itemHit]), newstr[itemHit]); PlainRef..OceanRef: begin for n := PlainRef to OceanRef do SetCtlValue(radButton[n], Ord(n = itemHit)); theTer := terrainList(itemHit - 5) end end {case} until done; if (itemHit = ok) then begin OKInput := OKInput and OKNumber(newstr[LatRef], -90, 90, new[LatRef], theDialog, LatRef); OKInput := OKInput and OKNumber(newstr[AltRef], -1000, 10000, new[AltRef], theDialog, AltRef); if (not (OKinput)) then SysBeep(10) end until OKinput; if (itemHit = ok) then with terrain do begin lat := new[LatRef]; alt := new[AltRef]; ter := theTer end; DisposDialog(theDialog) end; { proc DoTerrainDialog } { ********* items in Cast Menu *********** } procedure DoMU; begin { unimplemented } end; procedure DoIllusionist; begin { unimplemented } end; procedure DoCleric; begin { unimplemented } end; procedure DoDruid; begin { unimplemented } end; { ********* items in Util Menu *********** } procedure DoSecret; { Check for secret doors } const found = ' found a secret door!'; lost = ' found nothing.'; var i: Integer; display: Str255; SavePort: WindowPtr; begin GetPort(SavePort); SetPort(InfoPtr); ClearWindow(InfoPtr); for i := 1 to NumChars do with party[i] do begin display := name; case Die(6) of 1: display := concat(display, found); 2: if race[Length(race) - 2] = 'E' then {if an elf or half-elf} display := concat(display, found) else display := concat(display, lost); 3..6: display := concat(display, lost); end; {case} MoveTo(5, 10 * i); DrawString(display) end; SetPort(SavePort) end; procedure DoHear; { Check for hear noise chances } begin { unimplemented } end; procedure DoSaveThrow; { Roll saving throws for the party } begin { unimplemented } end; procedure DoTreasure (var TreasType: str255; var NumMonst, Percent: LongInt); { Generate a random treasure } type TreasureType = record Cu, Ag, El, Au, Pt: LongInt; Gems, Jewelry: Integer end; SmallInt = 0..127; var count: Integer; theTreasure: TreasureType; Fract: Real; SavePort: WindowPtr; display: str255; Value: LongInt; procedure BigTreas (CuCh, CuNum, CuDie, AgCh, AgNum, AgDie, ElCh, ElNum, ElDie, AuCh, AuNum, AuDie, PtCh, PtNum, PtDie, GeCh, GeNum, GeDie, JeCh, JeNum, JeDie: SmallInt); begin with theTreasure do begin if (die(100) <= CuCh) then Cu := Cu + LongInt(1000) * dice(CuNum, CuDie); if (die(100) <= AgCh) then Ag := Ag + LongInt(1000) * dice(AgNum, AgDie); if (die(100) <= ElCh) then El := El + LongInt(1000) * dice(ElNum, ElDie); if (die(100) <= AuCh) then Au := Au + LongInt(1000) * dice(AuNum, AuDie); if (die(100) <= PtCh) then Pt := Pt + LongInt(100) * dice(PtNum, PtDie); if (die(100) <= GeCh) then Gems := Gems + dice(GeNum, GeDie); if (die(100) <= JeCh) then Jewelry := Jewelry + dice(JeNum, JeDie) end end; {proc BigTreas} procedure IndTreas (var Pieces: LongInt; Num, Die: SmallInt); var index: Integer; begin for index := 1 to NumMonst do Pieces := Pieces + dice(Num, Die) end; {proc IndTreas} begin SetCursor(Watch); with theTreasure do begin Cu := 0; Ag := 0; El := 0; Au := 0; Pt := 0; Gems := 0; Jewelry := 0 end; for count := 1 to Length(TreasType) do case TreasType[count] of 'a', 'A': BigTreas(25, 1, 6, 30, 1, 6, 35, 1, 6, 40, 1, 10, 25, 1, 4, 60, 4, 10, 50, 3, 10); 'b', 'B': BigTreas(50, 1, 8, 25, 1, 6, 25, 1, 4, 25, 1, 3, 0, 0, 0, 30, 1, 8, 20, 1, 4); 'c', 'C': BigTreas(20, 1, 12, 30, 1, 6, 10, 1, 4, 0, 0, 0, 0, 0, 0, 25, 1, 6, 20, 1, 3); 'd', 'D': BigTreas(10, 1, 8, 15, 1, 12, 15, 1, 8, 50, 1, 6, 0, 0, 0, 30, 1, 10, 25, 1, 6); 'e', 'E': BigTreas(5, 1, 10, 25, 1, 12, 25, 1, 6, 25, 1, 8, 0, 0, 0, 15, 1, 12, 10, 1, 8); 'f', 'F': BigTreas(0, 0, 0, 10, 1, 20, 15, 1, 12, 40, 1, 10, 35, 1, 8, 20, 3, 10, 10, 1, 10); 'g', 'G': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 10, 4, 50, 1, 20, 30, 5, 4, 25, 1, 10); 'h', 'H': BigTreas(25, 5, 6, 40, 1, 100, 40, 10, 4, 55, 10, 6, 25, 5, 10, 50, 1, 100, 50, 10, 4); 'i', 'I': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 3, 6, 55, 2, 10, 50, 1, 12); 'j', 'J': IndTreas(theTreasure.Cu, 3, 8); 'k', 'K': IndTreas(theTreasure.Ag, 3, 6); 'l', 'L': IndTreas(theTreasure.El, 2, 6); 'm', 'M': IndTreas(theTreasure.Au, 2, 4); 'n', 'N': IndTreas(theTreasure.Pt, 1, 6); 'o', 'O': BigTreas(25, 1, 4, 20, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 'p', 'P': BigTreas(0, 0, 0, 30, 1, 6, 25, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 'q', 'Q': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 1, 4, 0, 0, 0); 'r', 'R': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 2, 4, 50, 10, 6, 55, 4, 8, 45, 1, 12); 's', 'S': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 't', 'T': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 'u', 'U': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 10, 8, 80, 5, 6); 'v', 'V': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 'w', 'W': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 5, 6, 15, 1, 8, 60, 10, 8, 50, 5, 8); 'x', 'X': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 'y', 'Y': BigTreas(0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 2, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0); 'z', 'Z': BigTreas(20, 1, 3, 25, 1, 4, 25, 1, 4, 30, 1, 4, 30, 1, 6, 55, 10, 6, 50, 5, 6) end; if (Percent <> 100) then with theTreasure do begin Fract := Percent / 100; Cu := Trunc(Cu * Fract); Ag := Trunc(Ag * Fract); El := Trunc(El * Fract); Au := Trunc(Au * Fract); Pt := Trunc(Pt * Fract); Gems := Trunc(Gems * Fract); Jewelry := Trunc(Jewelry * Fract) end; {Write Treasure} with theTreasure do Value := Trunc(Pt * 5 + Au + El / 10 + Ag / 20 + Cu / 200); GetPort(SavePort); SetPort(TreaPtr); ClearWindow(TreaPtr); MoveTo(5, 10); display := concat(Pad(6, theTreasure.Cu, blank), ' Coppers'); DrawString(display); MoveTo(5, 20); display := concat(Pad(6, theTreasure.Ag, blank), ' Silvers'); DrawString(display); MoveTo(5, 30); display := concat(Pad(6, theTreasure.El, blank), ' Electrums'); DrawString(display); MoveTo(5, 40); display := concat(Pad(6, theTreasure.Au, blank), ' Golds'); DrawString(display); MoveTo(5, 50); display := concat(Pad(6, theTreasure.Pt, blank), ' Platinums'); DrawString(display); MoveTo(5, 60); display := concat(Pad(6, theTreasure.Gems, blank), ' Gems'); DrawString(display); MoveTo(5, 70); display := concat(Pad(6, theTreasure.Jewelry, blank), ' Jewellery'); DrawString(display); MoveTo(10, 85); display := 'Total value'; DrawString(display); MoveTo(10, 95); display := concat(Pad(8, Value, blank), ' gp'); DrawString(display); SetPort(SavePort); SetCursor(Arrow) end; {proc DoTreasure} procedure DoTreasDialog; { Open, process, and close the Treas dialog box. } const TypeRef = 7; NumbRef = 8; PercentRef = 9; var theDialog: DialogPtr; itemHit: Integer; theType: Integer; r: Rect; edText: array[TypeRef..PercentRef] of ControlHandle; done, OKinput: Boolean; n: Integer; h: Handle; newstr: array[TypeRef..PercentRef] of Str255; new: array[NumbRef..PercentRef] of Longint; SavePort: GrafPtr; begin GetPort(SavePort); theDialog := GetNewDialog(258, nil, Pointer(-1)); SetPort(theDialog); GetDItem(theDialog, ok, theType, h, r); InsetRect(r, -4, -4); PenSize(3, 3); FrameRoundRect(r, 16, 16); PenSize(1, 1); SetPort(SavePort); for n := TypeRef to PercentRef do GetDItem(theDialog, n, theType, Handle(edText[n]), r); { Set initial values in storage array} newstr[TypeRef] := ''; newstr[NumbRef] := '1'; newstr[PercentRef] := '100'; repeat done := False; OKinput := True; repeat ModalDialog(nil, itemHit); case itemHit of ok, cancel: done := True; TypeRef..PercentRef: GetIText(Handle(edText[itemHit]), newstr[itemHit]) end; until done; if (itemHit = ok) then begin { Convert the input numbers to numerical type } for n := NumbRef to PercentRef do begin StringToNum(newstr[n], new[n]); if (new[n] < 0) then OKinput := False end; OKInput := OKInput and OKNumber(newstr[NumbRef], 1, 1000, new[NumbRef], theDialog, NumbRef); OKInput := OKInput and OKNumber(newstr[PercentRef], 1, 1000, new[PercentRef], theDialog, PercentRef); if (newstr[TypeRef] = '') then OKinput := false; if (not (OKinput)) then SysBeep(10) end until OKinput; DisposDialog(theDialog); if (itemHit = ok) then DoTreasure(newstr[TypeRef], new[NumbRef], new[PercentRef]) end; { proc DoTreasDialog } { ********* items in Apple Menu *********** } procedure DoAbout; { bring up 'About...' box using a dialog box } var theItem: Integer; AboutPtr: DialogPtr; h: handle; theType: integer; r: rect; SavePort: GrafPtr; begin GetPort(SavePort); AboutPtr := getNewDialog(AboutID, nil, Pointer(-1)); SetPort(AboutPtr); GetDItem(AboutPtr, ok, theType, h, r); InsetRect(r, -4, -4); PenSize(3, 3); FrameRoundRect(r, 16, 16); PenSize(1, 1); SetPort(SavePort); ModalDialog(nil, theItem); DisposDialog(AboutPtr); end; { of proc DoAbout } procedure DoDeskAcc (Item: Integer); { start up desk accessory from Apple menu } var SavePort: GrafPtr; RefNum: Integer; DName: str255; begin GetPort(SavePort); GetItem(MenuList[AM], Item, DName); refNum := OpenDeskAcc(DName); SetPort(SavePort) end; { of proc DoDeskAcc } { ********* event handling routines *********** } procedure SetItemState (Mndx, Indx: Integer; Flag: Boolean); { if true, enables item Indx of menu Mndx; else disables } begin if Flag then EnableItem(MenuList[Mndx], Indx) else DisableItem(MenuList[Mndx], Indx) end; { of proc SetItemState } procedure HandleMenu (MenuInfo: LongInt); { decode MenuInfo and carry out command } var Menu: Integer; Item: Integer; B: Boolean; begin if MenuInfo <> 0 then begin Menu := HiWord(MenuInfo); Item := LoWord(MenuInfo); case Menu of ApplMenu: if Item = 1 then DoAbout else DoDeskAcc(Item); FileMenu: case Item of 1: DoNew; 2: DoOpen; 3: DoClose; 4: DoSave; 5: DoSaveAs; 6: DoModify; 8: DoQuit end; EditMenu: case Item of 1, 3..6: if not SystemEdit(Item - 1) then { pass to desk acc } { do nothing } end; DiceMenu: case Item of 1: DoDice(4); 2: DoDice(6); 3: DoDice(8); 4: DoDice(10); 5: DoDice(12); 6: DoDice(20); 7: DoDice(100); 9: DoOther end; TimeMenu: case Item of 1: DoTurn; 2: DoRound; 3: DoEnd; 5: DoDay; 7: DoDateDialog(add); 8: DoDateDialog(fix); 10: DoTerrainDialog; end; CastMenu: case Item of 1: DoMU; 2: DoIllusionist; 3: DoCleric; 4: DoDruid end; UtilMenu: case Item of 1: DoSecret; 2: DoHear; 3: DoSaveThrow; 5: DoTreasDialog end; CharMenu: case Item of 1: DoNoChar; 3..12: DoSelChar(Item - 2) end end;{case of Menu} HiliteMenu(0); end end; {of proc HandleMenu} procedure HandleClick (WPtr: WindowPtr; MLoc: Point); { handle mouse click within window } begin if WPtr <> FrontWindow then SelectWindow(WPtr) end; { of proc HandleClick } procedure HandleGoAway (WPtr: WindowPtr; MLoc: Point); { handle mouse click in go-away box } var WPeek: WindowPeek; begin if WPtr = FrontWindow then begin WPeek := WindowPeek(WPtr); if TrackGoAway(WPtr, MLoc) then begin if WPeek^.WindowKind = userKind then DisposeWindow(WPtr) else CloseDeskAcc(WPeek^.WindowKind) end end else SelectWindow(WPtr) end; { of proc HandleGoAway } procedure HandleGrow (WPtr: WindowPtr; MLoc: Point); { handle mouse click in grow box } type GrowRec = record case Integer of 0: ( Result: LongInt ); 1: ( Height, Width: Integer ) end; var GrowInfo: GrowRec; begin if WPtr = TimePtr then with GrowInfo do begin Result := GrowWindow(WPtr, MLoc, GrowArea); SizeWindow(WPtr, Width, Height, True); InvalRect(WPtr^.portRect) end end; { of proc HandleGrow } procedure DoMouseDown (theEvent: EventRecord); { identify where mouse was clicked and handle it } var Location: Integer; theWindow: WindowPtr; MLoc: Point; WLoc: Integer; begin MLoc := theEvent.Where; WLoc := FindWindow(MLoc, theWindow); case WLoc of InMenuBar: HandleMenu(MenuSelect(MLoc)); InContent: HandleClick(theWindow, MLoc); InGoAway: HandleGoAway(theWindow, MLoc); InGrow: HandleGrow(theWindow, MLoc); InDrag: DragWindow(theWindow, MLoc, DragArea); InSysWindow: SystemClick(theEvent, theWindow) end end; { of proc DoMouseDown } procedure DoKeypress (theEvent: EventRecord); { handles keypress (keyDown, autoKey) event } var KeyCh: Char; begin if BitAnd(theEvent.modifiers, cmdKey) <> 0 then begin KeyCh := Chr(BitAnd(theEvent.Message, charCodeMask)); HandleMenu(MenuKey(KeyCh)) end else SysBeep(1) end; { of proc DoKeypress } procedure DoUpdate (theEvent: EventRecord); { handles window update event } var SavePort, theWindow: WindowPtr; begin theWindow := WindowPtr(theEvent.Message); BeginUpdate(theWindow); if theWindow = TimePtr then begin SetCursor(Watch); WriteTime; SetCursor(Arrow) end; if theWindow = CharPtr then begin SetCursor(Watch); WriteChar; SetCursor(Arrow) end; EndUpdate(theWindow) end; { of proc DoUpdate } procedure DoActivate (theEvent: EventRecord); { handles window activation event } var I: Integer; AFlag: Boolean; theWindow: WindowPtr; begin with theEvent do begin theWindow := WindowPtr(Message); AFlag := Odd(Modifiers); if AFlag then SetPort(theWindow) else SetPort(ScreenPort); if (WindowPeek(theWindow)^.WindowKind = UserKind) then begin SetItemState(EM, 1, not AFlag); for I := 3 to 6 do SetItemState(EM, I, not AFlag); SetItemState(FM, 0, AFlag); for I := DM to HM do SetItemState(I, 0, AFlag); DrawMenuBar end end end; { of proc DoActivate } {$S InitSeg} procedure Initialize; var Indx: Integer; watchHandle: CursHandle; j: Integer; begin InitGraf(@thePort); InitFonts; InitWindows; InitMenus; TEInit; InitDialogs(nil); FlushEvents(everyEvent, 0); watchHandle := GetCursor(watchCursor); HLock(Handle(watchHandle)); Watch := watchHandle^^; SetCursor(Watch); MenuList[AM] := GetMenu(ApplMenu); MenuList[FM] := GetMenu(FileMenu); MenuList[EM] := GetMenu(EditMenu); MenuList[DM] := GetMenu(DiceMenu); MenuList[TM] := GetMenu(TimeMenu); MenuList[CM] := GetMenu(CastMenu); MenuList[UM] := GetMenu(UtilMenu); MenuList[HM] := GetMenu(CharMenu); AddResMenu(MenuList[AM], 'DRVR'); for Indx := 1 to MenuCnt do InsertMenu(MenuList[Indx], 0); DrawMenuBar; GetWMgrPort(ScreenPort); SetPort(ScreenPort); TimePtr := GetNewWindow(TimeID, nil, Pointer(-1)); SetPort(TimePtr); TextFont(monaco); TextSize(9); DicePtr := GetNewWindow(DiceID, nil, Pointer(-1)); SetPort(DicePtr); TextFont(monaco); TextSize(9); TreaPtr := GetNewWindow(TreaID, nil, Pointer(-1)); SetPort(TreaPtr); TextFont(monaco); TextSize(9); CharPtr := GetNewWindow(CharID, nil, Pointer(-1)); SetPort(CharPtr); TextFont(monaco); TextSize(9); InfoPtr := GetNewWindow(InfoID, nil, Pointer(-1)); SetPort(InfoPtr); TextFont(monaco); TextSize(9); ScreenArea := screenBits.Bounds; with ScreenArea do begin SetRect(DragArea, 5, 25, Right - 5, Bottom - 10); SetRect(GrowArea, 50, 20, Right - 5, Bottom - 10) end; { Random number generator initialisation } glix1 := (ic1 + TickCount) mod m1; glix1 := (ia1 * glix1 + ic1) mod m1; glix2 := glix1 mod m2; glix1 := (ia1 * glix1 + ic1) mod m1; glix3 := glix1 mod m3; for j := 1 to 97 do begin glix1 := (ia1 * glix1 + ic1) mod m1; glix2 := (ia2 * glix2 + ic2) mod m2; glr[j] := (glix1 + glix2 * rm2) * rm1 end; { Variable initialisation } with time do begin year := 1; day := 1; hour := 0; minute := 0; second := 0 end; with terrain do begin lat := 40; alt := 0; ter := plain end; NumChars := 0; DoNoChar; Finished := False; { set program terminator to false } SetCursor(Arrow) end; { of proc Initialize } procedure CleanUp; begin DisposeWindow(TimePtr); { get rid of the windows } DisposeWindow(DicePtr); DisposeWindow(TreaPtr); DisposeWindow(CharPtr); DisposeWindow(InfoPtr) end; { of proc CleanUp } {$S } procedure HandleEvent (theEvent: EventRecord); begin case theEvent.What of mouseDown: DoMouseDown(theEvent); keyDown: DoKeyPress(theEvent); autoKey: DoKeyPress(theEvent); updateEvt: DoUpdate(theEvent); activateEvt: DoActivate(theEvent) end end; { of proc HandleEvent } begin { main body of program AD&D } Initialize; UnloadSeg(@Initialize); repeat SystemTask; if GetNextEvent(everyEvent, theEvent) then HandleEvent(theEvent) until Finished; Cleanup end. { of program AD&D }