home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Ken Long / MissileCmd2.3-p-c / Original / the snippet next >
Encoding:
Internet Message Format  |  1994-12-04  |  45.8 KB  |  [TEXT/EDIT]

  1. From: mrob@world.std.com (Robert P Munafo)
  2. Subject: "Missile" source from 1984
  3.  
  4. Recently in comp.sys.mac.programmer there has been some
  5. discussion of early Macintosh programs that still run on current
  6. machines, and my Missile Command game from mid-1984 was mentioned.
  7.  
  8. This program was written in Lisa Pascal, the cross-development
  9. system that everyone had to use until Stanford's sumacc came along,
  10. and has the rather charming property of continuing to run on all
  11. new machines that Apple comes out with. I ran it on my own
  12. Power Mac 6100/60 yesterday just for fun. It's just as playable
  13. as it was on the old 128K Mac (although the timing for the "Game
  14. Over" screen is now way off, as I didn't check TickCount for
  15. that).
  16.  
  17. I have not done anything to this code, like try to port it to
  18. any recent Pascal compiler or anything like that. I know that
  19. Lisa Pascal, Macintosh Pascal, and TML Language Systems Pascal
  20. were all reasonably compatible with each other; after that I
  21. switched over to Think C and lost track.
  22.  
  23. The "%title" and "%subtitle" directives were for the print spooler
  24. on the Dartmouth mainframe that I used to print out listings.
  25.  
  26. There seem to be a lot more options in the Game Options dialog
  27. than the actual Missile game provides. I'm not sure what's going
  28. on with that.
  29. ----------- Cut Here --- Pascal Source code -----------
  30.  
  31.  
  32. { %title 'Missile - a game for the Apple Macintosh.' }
  33. { %subtitle 'Overall program description.' }
  34. { $X- }  { Turns off A.R.S.E. }
  35.  
  36. PROGRAM Game;
  37. {
  38.  Missile -- A Missile Command game for the Macintosh
  39.             by Robert P. Munafo at Dartmouth College.
  40.  
  41.    I wrote this to learn a bit about Mac programming.  It has a
  42.  few 'standard user interface' bugs:
  43.  
  44.    It doesn't update the window during the 'End of round X' and
  45.      'GAME OVER' routines.  (It only updates while playing.)
  46.    It won't quit when you select "quit" while a desk accessory is
  47.      on screen.  Instead, it will wait until you close all the
  48.      desk accessories, then quit.
  49.    The cursor looks the same all the time.  It should change
  50.      shape to let the user know when he can and cannot fire missiles.
  51.  
  52.  It also has some missing features (which I hope to add soon):
  53.  
  54.    o Smart Bombs
  55.    o Add an "Ammunition: [xxx]% of normal" item to the dialog box.
  56.    o Sound
  57.    o User should be able to pick what round the game starts with
  58.    o High scores list saved on the disk
  59.    o Two-player option
  60.    o The animation needs to be made faster (in some manner)
  61. }
  62.  
  63. { %subtitle 'USES statements' }
  64. USES  {$U Obj/QuickDraw   } QuickDraw,
  65.       {$U Obj/OSIntf      } OSIntf,
  66.       {$U Obj/ToolIntf    } ToolIntf,
  67.       {$U Obj/PackIntf    } Packintf;
  68.  
  69. { %subtitle 'Constants' }
  70. CONST lastMenu = 4;      { number of menus }
  71.       appleMenu = 1;     { menu ID for desk accessory menu }
  72.       fileMenu = 256;    { menu ID for File menu }
  73.       EditMenu = 257;    { menu ID for the Edit menu. }
  74.       GameMenu = 258;    { menu ID for Game menu }
  75.       str_1_ID = 300;    {ID for my first string of text}
  76.       str_2_ID = 350;    {ID for my second string of text}
  77.       box_ID = 450;      {ID of "about" dialog box}
  78.       box2_id = 451;     {ID of options box}
  79.  
  80.       fbIdle = 0;
  81.       fbGrow = 1;
  82.       fbShrink = 2;
  83.       maxfb = 60;        { Maximum number of fireballs allowed. }
  84.       fbRad = 15;        { Radius of fireballs. }
  85.       fbRadRate = 2;     { Rate of expansion/contraction of fireballs. }
  86.       msIdle = 0;
  87.       msActive = 1;
  88.       msNormal = 1;
  89.       msMirv = 2;
  90.       msSmart = 3;
  91.       maxms = 20;        { Maximum number of missiles allowed. }
  92.       mWidth = 3;        { Width of missile tracks. }
  93.       CityHeight = 40;   { Distance from bottom up to cities. }
  94.       CityWidth = 50;    { Width of cities. }
  95.       BuildHeight = 30;  { Maximum height of buildings. }
  96.       BuildWidth = 4;    { Width of buildings. }
  97.       nCities = 6;       { Number of cities. }
  98.  
  99. TYPE  fireball = RECORD    { Data type to describe a fireball. }
  100.                    bounds: Rect;    { Rectanggle to copy bits into. }
  101.                    size: INTEGER;   { Current radius of fireball. }
  102.                    mode: INTEGER;   { Idle, Growing, or shrinking. }
  103.                  END;
  104.  
  105.       missile =  RECORD    { Data type to describe a missile. }
  106.                    start: Point;    { One endpoint of the missile's path. }
  107.                    pos: Point;      { Position within path - 0 to 1. }
  108.                    dh, dv: INTEGER; { Vertical and horizontal speed in pixels. }
  109.                    city: INTEGER;   { Number of the target city. }
  110.                    kind: INTEGER;   { MIRV or normal. }
  111.                    mode: INTEGER;   { Idle or Active. }
  112.                  END;
  113.  
  114. VAR myMenus: ARRAY [1..lastMenu] OF MenuHandle;
  115.     screenRect: Rect;
  116.     doneFlag: BOOLEAN;
  117.     myEvent: EventRecord;
  118.     code, refNum: INTEGER;
  119.     wRecord: WindowRecord;
  120.     myWindow, whichWindow: WindowPtr;
  121.     first_string, second_string : StringHandle; { Handles to strings of text for AboutMissile. }
  122.  
  123.     { My game variables are here. }
  124.     fbBitMap: BitMap;      { BitMap for fireball pictures. }
  125.     fbBits: ARRAY[1..3600] OF INTEGER;  { Actual bits for fireball pictures. }
  126.  
  127.     crosshairs: Cursor;    { Crosshairs cursor read in from resource. }
  128.     hCurs:  CursHandle;    { Handle to the cursor. }
  129.     CityPattern: PatHandle;{ Handle to pattern in which to draw cities. }
  130.     FieldWidth: INTEGER;   { Width of the playing field. }
  131.     FieldHeight: INTEGER;  { Height of the playing field. }
  132.     i: INTEGER;            { Loop variable. }
  133.     fbs: ARRAY [1..maxfb] OF fireball;     { Each entry describes one fireball. }
  134.     missiles: ARRAY [1..maxms] OF missile; { Each entry describes one missile. }
  135.     nMissiles: INTEGER;    { Number of currently active missiles. }
  136.     lastTick: LONGINT;     { System clock at time of previous update. }
  137.     gameOver: BOOLEAN;     { Set true when all cities are destroyed. }
  138.     PauseFlag: BOOLEAN;    { True when game is being paused. }
  139.     Playing: BOOLEAN;      { True except buring bonus points and GAME OVER screens. }
  140.     esFlag: BOOLEAN;       { True if "GAME OVER" is to be drawn after exiting main loop. }
  141.     enddelay: INTEGER;     { Used to wait a while after end of round. }
  142.     citiesLeft: INTEGER;   { Number of cities still alive. }
  143.     bcities: INTEGER;      { Number of bonus cities they have left. }
  144.     cities: ARRAY [1..nCities] OF BOOLEAN; { State of each city : false = destroyed. }
  145.     cities2: ARRAY [1..nCities] OF BOOLEAN; { State of the city before the round began. }
  146.     nukeHeight: INTEGER;   { Vertical position of fireball required to destroy city. }
  147.     endv: INTEGER;         { Y-coordinate of the end of a missile's path. }
  148.     MirvRate: INTEGER;
  149.     mirvHeight: INTEGER;   { Height at which Mirvs MIRV. }
  150.     mirvNasty: INTEGER;    { Percent chance of sub-missile on mirv for each city. }
  151.     msSpeed: INTEGER;      { Vertical speed of missiles, in 1/16ths of a pixel.}
  152.     msRate: INTEGER;       { Percent rate of missile production. }
  153.     RoundNumber: INTEGER;  { Number of the current round. }
  154.     Score: LONGINT;        { The player's current score. }
  155.     DisScore: LONGINT;     { Score currently displayed on the screen. }
  156.     HighScore: LONGINT;    { The current highest score. }
  157.     RoundH: INTEGER;
  158.     Scoreh: INTEGER;       { Horiz. position of text 'Score:' }
  159.     ScoreNumH: INTEGER;    { Horiz. position of score. }
  160.     statsH: INTEGER;       { Horiz. position of text 'Enemy left:' }
  161.     statsNumH: INTEGER;
  162.     statsRect: Rect;
  163.     eLeft: INTEGER;        { How many enemy missiles are left this round. }
  164.     yLeft: INTEGER;        { How many missiles you (the player) have left. }
  165.     eDestroyed: INTEGER;   { Number of enemy missiles destroyed. }
  166.     eDesH: INTEGER;        { Horiz. position of text 'Enemy Destroyed:' }
  167.     eDesNumH: INTEGER;     { Horiz. position of # of enemy missiles destroyed. }
  168.  
  169.     gameSpeed: INTEGER;    { Number of ticks between each AdvanFb call. }
  170.     StartRound: INTEGER;   { Round to start off with. }
  171.     mFlag1: BOOLEAN;       { Do missiles aim for dead cities? }
  172.     MFlag2: INTEGER;       { Extent to which enemy missiles blow up. }
  173.     MExists: ARRAY[1..3] OF BOOLEAN;  { Does this type of missile occur? }
  174.     MPBase: ARRAY[1..3] OF INTEGER;  { Value of missile at first round... }
  175.     MPExtra: ARRAY[1..3] OF INTEGER;  { ...plus this much each additional round... }
  176.     MPoints: ARRAY[1..3] OF INTEGER;  { ...is this much in all. }
  177.  
  178. { %subtitle 'Init routine for menus.' }
  179. PROCEDURE SetUpMenus;
  180. VAR i: INTEGER;
  181. BEGIN
  182.   myMenus[1] := GetMenu(appleMenu);             { Load menu from disk. }
  183.   MyMenus[1]^^.MenuData[1] := Chr(AppleSymbol); { Change title to the Apple symbol. }
  184.   AddResMenu(myMenus[1],'DRVR');                { Add items for desk accessories. }
  185.   myMenus[2] := GetMenu(fileMenu);
  186.   myMenus[3] := GetMenu(editMenu);
  187.   myMenus[4] := GetMenu(GameMenu);
  188.   FOR i:=1 TO lastMenu DO
  189.     InsertMenu(myMenus[i],0);
  190.   DrawMenuBar;
  191. END;    { of SetUpMenus }
  192.  
  193. { %subtitle 'Init routine for fireball BitMaps.' }
  194. {     This routine is called by the main Init routine, below.  It creates a
  195.     bunch of bit images of circles off-screen, which can later be drawn on
  196.     the screen much faster than FrameOval. }
  197. PROCEDURE InitFbs;
  198. VAR i: FIXED;
  199.     aRect: Rect;           { Rectangle for drawing FrameOvals. }
  200.     saveBits: BitMap;      { To save the current GrafPort while drawing off-screen. }
  201.     SaveRect: Rect;
  202. BEGIN
  203.   SaveBits := ThePort^.PortBits;
  204.   SaveRect := ThePort^.PortRect;
  205.   fbBitMap.rowBytes := 8;
  206.   SetRect(fbBitMap.bounds, 0, 0, 60, 60);
  207.   ThePort^.portRect := fbBitMap.Bounds;
  208.  
  209.   PenSize(fbRadRate,fbRadRate);
  210.   PenPat(black);
  211.   PenMode(PatCopy);
  212.   SetRect(aRect, 30, 30, 30, 30);  { Start with an empty rectangle in the center. }
  213.   FOR i := 0 TO 14 DO BEGIN
  214.     fbBitMap.baseAddr := @fbBits[240*i + 1];    { Pick starting address for this picture. }
  215.     insetRect(aRect, -2, -2);                   { Make rect a bit larger. }
  216.     SetPortBits(fbBitMap);
  217.     EraseRect(thePort^.portBits.bounds);        { Erase to all white. }
  218.     FrameOval(aRect);                           { Draw the circle. }
  219.   END;
  220.   SetPortBits(SaveBits);          { Return to normal screen drawing. }
  221.   ThePort^.PortRect := SaveRect;
  222. END;  { of InitFbs }
  223.  
  224. { %subtitle 'Init routine' }
  225. PROCEDURE Setup;
  226. VAR wRect: Rect;  { Window Rectangle. }
  227.   i: INTEGER;
  228. BEGIN
  229.                         {     Macintosh System initialization. }
  230.   InitGraf(@thePort);   { Quickdraw. }
  231.   InitFonts;            { Font Manager. }
  232.   InitWindows;          { Window Manager. }
  233.   InitMenus;            { Menu Manager. }
  234.   TEInit;               { TextEdit. }
  235.   InitDialogs(NIL);     { Dialog manager. }
  236.   InitCursor;           { Cursor handler. }
  237.   InitAllPacks;         { Package Manager. }
  238.  
  239.   SetUpMenus;           {   My routine to insert the menus. }
  240.   hCurs := POINTER(GetCursor(256));     { Load the Crosshairs cursor that I use in the game. }
  241.   crosshairs := hCurs^^;
  242.   SetCursor(crosshairs);
  243.   CityPattern := GetPattern(256);
  244.  
  245.   screenRect := screenBits.bounds;      { Don't actually use this, but might later. }
  246.  
  247.   doneFlag := FALSE;                    { This flag is set to False when user selects 'Quit'. }
  248.   HighScore := 0;
  249.  
  250.   GameSpeed := 8;                       { Set up all the user-settable options. }
  251.   StartRound := 1;
  252.   MFlag1 := True;
  253.   MFlag2 := 1;
  254.   FOR i := 1 TO 3 DO BEGIN
  255.     MExists[i] := True;
  256.     MPBase[i] := 10*i;
  257.     MPExtra[i] := 10*i;
  258.   END;
  259.  
  260.   { myWindow := GetNewWindow(256, @wRecord, POINTER(-1)); }
  261.   wRect := ScreenRect;
  262.   wRect.top := 20;
  263.   myWindow := NewWindow(@wRecord,
  264.                          wRect,                 { BoundsRect }
  265.                          'Missile Command   by Robert Munafo',
  266.                          True,                  { Visible }
  267.                          0,                     { ProcID }
  268.                          POINTER(-1),           { behind }
  269.                          False,                 { GoAwayFlag }
  270.                          0);                    { RefCon }
  271.   SetPort(myWindow);                    { Might as well play the game in a window... }
  272.  
  273.   FieldWidth := MyWindow^.portRect.right;   { These variables are used by the game. }
  274.   FieldHeight := MyWindow^.portRect.bottom;
  275.  
  276.   TextFont(0);
  277.   TextFace([]);
  278.  
  279.   InitFbs;              { Set up the fireball BitMaps. }
  280.  
  281.   second_string := GetString(str_2_ID);               { Get strings from resource. }
  282.   first_string := GetString(str_1_ID);
  283.  
  284.   FlushEvents(everyEvent,0);
  285. END;
  286.  
  287. { %Subtitle 'Gets a text string' }
  288.  
  289. {    This routine conducts a dialog to get a text string from the
  290.    user.  The function value returned indicates whether the user stopped by entering
  291.    OK or cancel.  The ID is the dialog ID number to use and Text is the
  292.    text they typed in.  To get the text, we display the dialog window,
  293.    get the handle of the edit text and execute a small event loop wich waits
  294.    until one of the two buttons is pressed.  When done, we dispose of the dialogue
  295.    window, set the flag depending on the means of exiting and return the text.
  296.      The predefined dialogue window has the following items in it:
  297.  
  298. Item Number              Item
  299.      1                   OK button, Enabled
  300.      2                   Cancel button, Enabled
  301.      3                   EditText area, Enabled
  302.      4                   StatText area, Disabled (Prompt)
  303.      5...                Anything else, Disabled
  304. *)
  305.  
  306. { the actual function has been deleted. }
  307.  
  308. { %subtitle '"About Missile" routine.' }
  309. { This routine is nearly identical to Jason Ansley's About_Windows routine. }
  310. PROCEDURE AboutMissile;
  311. VAR item_chosen : integer;   {which botton was hit (I only have one so it doesn't really matter)}
  312.     item_type : integer;     {type of item in res. def. file (needed to pass as parameter)}
  313.     first_handle, second_handle : Handle; {handles to my items}
  314.     the_dialog : DialogPtr;  {a pointer to my dialog box}
  315.     box : rect;              {the display rectangle of the item}
  316.  
  317. BEGIN
  318.     the_dialog := GetNewDialog(box_ID, NIL, pointer(-1)); {get dialog box from resource file}
  319.  
  320.     GetDItem(the_dialog, 2, item_type, first_handle, box);  {get text format info from file}
  321.     GetDItem(the_dialog, 3, item_type, second_handle, box);
  322.  
  323.     SetIText(first_handle, first_string^^); {set the lines of text into the box}
  324.     SetIText(second_handle, second_string^^);
  325.  
  326.     ModalDialog(NIL, item_chosen); {operate the box}
  327.     DisposDialog(the_dialog); {get rid of the box now that I'm done with it}
  328. END;
  329.  
  330. { %subtitle 'My own random number routine.' }
  331. FUNCTION Rnd (n: FIXED) : FIXED;  { Function to generate a random integer from
  332.                                       1 to N. }
  333. BEGIN
  334.   Rnd := (ABS(Random) MOD n) + 1;
  335. END;
  336.  
  337. { %subtitle 'DrawNumber - converts number to string and draws it on the screen.' }
  338. PROCEDURE DrawNumber (n: LONGINT);
  339. VAR s: Str255;
  340. BEGIN
  341.   NumToString(n, s);
  342.   DrawString(s);
  343. END;
  344.  
  345. { %subtitle 'Update the score at the bottom of the screen.' }
  346. PROCEDURE UpdScore;
  347. BEGIN
  348.   TextMode(SrcBic);
  349.   MoveTo(ScoreNumH, FieldHeight-5);
  350.   TextSize(24);
  351.   DrawNumber(disScore);
  352.  
  353.   TextMode(SrcOr);
  354.   MoveTo(ScoreNumH, FieldHeight-5);
  355.   DrawNumber(Score);
  356.   disScore := Score;
  357.  
  358.   EraseRect(statsRect);
  359.   TextMode(SrcOr);
  360.   MoveTo(statsNumH, FieldHeight-20);
  361.   TextSize(12);
  362.   DrawNumber(eLeft);
  363.   MoveTo(statsNumH, FieldHeight-5);
  364.   DrawNumber(yLeft);
  365.   TextMode(SrcCopy);
  366.   MoveTo(eDesNumH, FieldHeight-20);
  367.   DrawNumber(eDestroyed);
  368. END;
  369.  
  370. { %subtitle 'Print the score.' }
  371. PROCEDURE DrawScore;
  372. BEGIN
  373.   TextMode(srcCopy);
  374.   MoveTo(ScoreH, FieldHeight-14);
  375.   TextSize(12);
  376.   DrawString('Score: ');
  377.   MoveTo(ScoreNumH, FieldHeight-5);
  378.   TextSize(24);
  379.   DrawNumber(Score);
  380.   disScore := Score;
  381.  
  382.   TextSize(12);
  383.   MoveTo(statsH, FIeldHeight-20);
  384.   DrawString('Enemy left: ');
  385.   DrawNumber(eLeft);
  386.   MoveTo(statsH, FieldHeight-5);
  387.   DrawString('Yours left: ');
  388.   MoveTo(statsNumH, FieldHeight-5);
  389.   DrawNumber(yLeft);
  390.   MoveTo(eDesH, FieldHeight-20);
  391.   DrawString('Destroyed: ');
  392.   MoveTo(eDesNumH, FieldHeight-20);
  393.   DrawNumber(eDestroyed);
  394. END;
  395.  
  396. { %subtitle 'Print round #, score, and # killed.' }
  397. PROCEDURE BottomLine;
  398. BEGIN;
  399.   TextSize(12);
  400.   RoundH := 10 + StringWidth('Round: ');
  401.   ScoreH := RoundH + 2*StringWidth('00 ');
  402.   ScoreNumH := ScoreH + StringWidth('Score: ');
  403.   eDesH := FieldWidth - StringWidth('Destroyed: 0000 ');
  404.   eDesNumH := FieldWidth - StringWidth('0000 ');
  405.   statsH := eDesH - StringWidth('Enemy left: 000  ');
  406.   statsNumH := eDesH - StringWidth('000  ');
  407.   SetRect(statsRect, statsNumH, FieldHeight-30, eDesH-2, FieldHeight);
  408.  
  409.   TextMode(srcCopy);
  410.   MoveTo(10, FieldHeight-14);
  411.   DrawString('Round: ');
  412.   MoveTo(RoundH, FieldHeight-5);
  413.   TextSize(24);
  414.   DrawNumber(RoundNumber);
  415.  
  416.   DrawScore;
  417.   MoveTo(eDesH, FieldHeight-5);
  418.   DrawString('High Score: ');
  419.   DrawNumber(HighScore);
  420. END;
  421.  
  422. { %subtitle 'Create a new fireball.' }
  423. PROCEDURE AllocFb (location: Point);
  424. VAR i: INTEGER;
  425.     idlefb: INTEGER;
  426. BEGIN
  427.   idlefb := 0;
  428.   FOR i := 1 TO maxfb DO
  429.     IF (fbs[i].mode = fbIdle) AND (idlefb = 0)
  430.     THEN idlefb := i;
  431.   IF idlefb <> 0
  432.   THEN BEGIN
  433.     SetRect(fbs[idlefb].bounds, location.h-30, location.v-30, location.h+30, location.v+30);
  434.     fbs[idlefb].size := 0;
  435.     fbs[idlefb].mode := fbGrow;
  436.   END;
  437. END;  { of AllocFb }
  438.  
  439. { %subtitle 'Advance (animate) the fireballs.' }
  440. PROCEDURE AdvanFb;
  441. VAR i: INTEGER;
  442.     aRect: Rect;
  443.     fbrect: Rect;          { Rectangle for drawing fireballs. }
  444. BEGIN
  445.   PenSize(fbRadRate,fbRadRate);
  446.   PenPat(black);
  447.   SetRect(aRect, 0, 0, 60, 60);
  448.   FOR i := 1 TO maxfb DO BEGIN
  449.     IF fbs[i].mode <> fbidle
  450.     THEN
  451.       CASE fbs[i].mode OF
  452.  
  453.         fbGrow: BEGIN
  454.           fbs[i].size := fbs[i].size + 1;
  455.           fbBitMap.baseAddr := @fbBits[240*fbs[i].size - 239];  { Select a fireball picture. }
  456.           CopyBits(fbBitMap, thePort^.PortBits, aRect, fbs[i].bounds, SrcOr, NIL);
  457.  
  458.           IF fbs[i].size >= fbRad
  459.           THEN fbs[i].mode := fbShrink;
  460.         END;
  461.  
  462.         fbShrink: BEGIN
  463.           fbBitMap.baseAddr := @fbBits[240*fbs[i].size - 239];  { Select the correct fireball picture. }
  464.           CopyBits(fbBitMap, thePort^.PortBits, aRect, fbs[i].bounds, SrcBic, NIL);
  465.  
  466.           fbs[i].size := fbs[i].size - 1;
  467.           IF fbs[i].size = 0
  468.           THEN fbs[i].mode := fbIdle;
  469.         END;
  470.  
  471.       END;  { of mode case }
  472.   END;   { of loop }
  473. END;   { of AdvanFb }
  474.  
  475. { %subtitle 'Create a new enemy missile.' }
  476. PROCEDURE AllocMs(position: Point; city: INTEGER; kind: INTEGER);
  477. VAR i: INTEGER;
  478.     idleMs: INTEGER;
  479.     targetcity: integer;
  480.     targeth: integer;
  481. BEGIN
  482.   idlems := 0;
  483.   FOR i := 1 TO maxMs DO
  484.     IF (missiles[i].mode = msIdle) AND (idlems = 0)
  485.     THEN idlems := i;
  486.   IF idlems <> 0
  487.   THEN BEGIN
  488.     i := idlems;
  489.     missiles[i].start.v := position.v;    { Set coordinates for beginning of path. }
  490.     missiles[i].start.h := position.h;
  491.     missiles[i].pos.v := missiles[i].start.v * 16;  { Initial current position is }
  492.     missiles[i].pos.h := missiles[i].start.h * 16;  {   at start of path.o}
  493.     missiles[i].dv := msSpeed;        { Speed of missile. }
  494.  
  495.     missiles[i].city := city;
  496.     targeth := (((missiles[i].city*2 - 1)*FieldWidth) DIV (2*nCities))*16;
  497.     missiles[i].dh := (targeth-(missiles[i].start.h*16)) DIV
  498.                          (((endv-missiles[i].start.v)*16) DIV missiles[i].dv);
  499.     missiles[i].kind := kind;
  500.     missiles[i].mode := msActive;
  501.     nMissiles := nMissiles + 1;
  502.   END;
  503. END;
  504.  
  505.  
  506. FUNCTION BlackPixel(aPoint: Point) : BOOLEAN;
  507. BEGIN
  508.   aPoint.h := aPoint.h DIV 16;
  509.   aPoint.v := aPoint.v DIV 16;
  510.   BlackPixel := (GetPixel(aPoint.h, aPoint.v)
  511.     AND GetPixel(aPoint.h+1, aPoint.v));
  512. END;
  513.  
  514. { %subtitle 'Advance (animate) the enemy missiles.' }
  515. PROCEDURE AdvanMs;
  516. VAR i, j: INTEGER;
  517.     newpos, newpixel: Point;
  518.     detonate: BOOLEAN;
  519.     Points: INTEGER;
  520. BEGIN
  521.   penMode(PatCopy);
  522.   FOR i := 1 TO maxms DO BEGIN
  523.     IF missiles[i].mode <> msIdle
  524.     THEN BEGIN
  525.       newpos.v := missiles[i].pos.v + missiles[i].dv;
  526.       newpos.h := missiles[i].pos.h + missiles[i].dh;
  527.       newpixel.v := newpos.v DIV 16;
  528.       newpixel.h := newpos.h DIV 16;
  529.  
  530.       detonate := FALSE;
  531.       IF BlackPixel(newpos) OR BlackPixel(missiles[i].pos)
  532.       THEN detonate := TRUE;
  533.  
  534.       PenSize(mWidth, mWidth);
  535.       PenPat(gray);
  536.       MoveTo(missiles[i].pos.h DIV 16, missiles[i].pos.v DIV 16);
  537.       LineTo(newpixel.h, newpixel.v);
  538.       missiles[i].pos := newpos;
  539.  
  540.       IF (missiles[i].kind = msMirv)   { If it's a Mirv, }
  541.         AND (newpixel.v > mirvHeight)  {  and it's reached the right altitude, }
  542.       THEN BEGIN                       {  then make lots of little missiles... }
  543.         FOR j := 1 TO nCities DO
  544.           IF (j <> missiles[i].city) AND (Rnd(100) < mirvNasty)
  545.           THEN allocMs(newpixel, j, msNormal);
  546.         missiles[i].kind := msNormal;
  547.       END;
  548.  
  549.       IF (newpos.v DIV 16 >= endv)   { If the missile has reached its target }
  550.         OR detonate           {   or hit a fireball, }
  551.       THEN BEGIN
  552.         PenSize(mWidth+2, mWidth+2);
  553.         PenPat(white);
  554.         MoveTo(missiles[i].start.h-1, missiles[i].start.v-1);  { Erase the line. }
  555.         LineTo(newpixel.h-1, newpixel.v-1);
  556.  
  557.         missiles[i].mode := msIdle;  { turn off the missile, }
  558.         nMissiles := nMissiles-1;
  559.  
  560.         IF detonate THEN BEGIN
  561.           IF (MFlag2 = 2) OR ((MFlag2 = 1) AND (Rnd(25) > RoundNumber))
  562.           THEN allocFb(newpixel);           { Make a new fireball. }
  563.           eDestroyed := eDestroyed + 1;
  564.           Score := Score + MPoints[missiles[i].kind];
  565.           UpdScore;
  566.         END
  567.         ELSE
  568.           IF cities[missiles[i].city]
  569.           THEN BEGIN
  570.             allocfb(newpixel);
  571.             cities[missiles[i].city] := false;
  572.             citiesLeft := citiesLeft - 1;
  573.             IF citiesLeft+bCities = 0
  574.             THEN gameOver := true;
  575.           END;
  576.       END;  { Of detonate routine. }
  577.     END;  { Of THEN clause for this active missile. }
  578.  
  579.   END;  { of missile loop }
  580. END;  { of AdvanMs }
  581.  
  582. { %subtitle 'Print a string in the center of the window.' }
  583. PROCEDURE Centre (TheText: Str255; posV: INTEGER);
  584. BEGIN
  585.   MoveTo((FieldWidth-StringWidth(TheText))DIV 2, posV);
  586.   DrawString(TheText);
  587. END;
  588.  
  589. { %subtitle 'Clear the screen.' }
  590. PROCEDURE ClearScreen;
  591. VAR aRect: Rect;
  592. BEGIN
  593.   aRect.top := 0;
  594.   aRect.left := 0;
  595.   aRect.bottom := FieldHeight;
  596.   aRect.right := FieldWidth;
  597.   FillRect(aRect, White);
  598. END;
  599.  
  600. { %subtitle 'Draw a city.' }
  601. PROCEDURE DrawCity (city: INTEGER);
  602. VAR CityLoc: Point;
  603.     bOffset: INTEGER;
  604.     aRect: Rect;
  605. BEGIN;
  606.   RandSeed := 2;  { Make each city look the same. }
  607.   cityloc.v := FieldHeight - CityHeight;
  608.   cityloc.h := ((city*2-1) * FieldWidth) DIV (2*nCities);
  609.   bOffset := -(CityWidth DIV 2);
  610.   WHILE bOffset<(CityWidth DIV 2) DO BEGIN
  611.     aRect.left := cityloc.h + bOffset;
  612.     aRect.top := cityloc.v - Rnd(BuildHeight);
  613.     aRect.bottom := cityloc.v + 2;
  614.     aRect.right := aRect.left + BuildWidth;
  615.     FillRect(aRect, CityPattern^^);
  616.     bOffset := bOffset + BuildWidth;
  617.   END;
  618. END;
  619.  
  620. { %subtitle 'Draw cities and bottom line.' }
  621. PROCEDURE DrawStuff;
  622. VAR i: INTEGER;
  623.   aRect: Rect;
  624. BEGIN
  625.   ClearScreen;
  626.  
  627.   FOR i := 1 TO nCities DO
  628.   IF cities[i] THEN DrawCity(i);
  629.   PenPat(Black);
  630.   RandSeed := TickCount;  { Randomize }
  631.  
  632.   BottomLine;
  633. END;  { of DrawStuff }
  634.  
  635. { %subtitle 'MinMax - Convert Num to String with range checking. }
  636. FUNCTION MinMax(TheString: Str255; minimum: INTEGER; Maximum: INTEGER) : INTEGER;
  637. VAR
  638.   TheNumber: LONGINT;
  639. BEGIN
  640.   StringToNum(TheString, TheNumber);
  641.   IF TheNumber<Minimum THEN TheNumber := Minimum;
  642.   IF TheNumber>Maximum THEN TheNumber := Maximum;
  643.   MinMax := TheNumber;
  644. END;
  645.  
  646. { %subtitle 'DoGameOptions - Does the big dialog box.' }
  647. Procedure DoGameOptions;
  648. CONST           { Here are all the magic constants that go with that monster dialog box: }
  649.      OK = 1;            { OK button }
  650.      GSpeed = 5;        { Game Speed text box }
  651.      SRound = 7;        { Start Round text box }
  652.      MPB = 18;          { MPBase text boxes }
  653.      MPE = 24;          { MPExtra text boxes }
  654.      MF1 = 8;           { MFlag1 check box }
  655.      MF2 = 10;          { MFlag2 radio buttons }
  656.      MEX = 15;          { MExists check boxes }
  657.  
  658. VAR  savePort:grafptr;          { For saving the GrafPort and restoring later. }
  659.      DStorage: DialogRecord;    { Storage for my dialog box. }
  660.      DPtr: DialogPtr;           { Pointer to my dialog box. }
  661.      ItemHit: integer;          { Item that was just hit by the user. }
  662.      TheType: integer;          { not used }
  663.      TheHandle: Handle;         { Temp. handle }
  664.      TheRect: Rect;             { not used. }
  665.      TheString: str255;         { Temp. string }
  666.      TheValue: INTEGER;         { Temp. integer value. }
  667.      i: INTEGER;                { Loop Variable. }
  668.      Station: INTEGER;          { Current setting of radio buttons. }
  669. BEGIN
  670.    DPtr := getNewDialog(box2_id, @DStorage, pointer(-1));  { Load the data from disk . . . }
  671.  
  672.    GetDItem(Dptr, GSpeed, theType, theHandle, theRect);   { Set up all the EditText boxes }
  673.    NumToString(GameSpeed, TheString);
  674.    SetIText(theHandle, TheString);
  675.    GetDItem(Dptr, SRound, theType, theHandle, theRect);
  676.    NumToString(StartRound, TheString);
  677.    SetIText(theHandle, TheString);
  678.  
  679.    FOR i := 0 TO 2 DO BEGIN
  680.      GetDItem(Dptr, MPB+i, theType, theHandle, theRect);  { Text for points each missile is worth. }
  681.      NumToString(MPBase[i+1], TheString);
  682.      SetIText(theHandle, TheString);
  683.      GetDItem(Dptr, MPB+3+i, theType, theHandle, theRect);  { Text for plus sign. }
  684.      TheString := '+';
  685.      SetIText(theHandle, TheString);
  686.      GetDItem(Dptr, MPE+i, theType, theHandle, theRect);  { Text for additional points each round }
  687.      NumToString(MPExtra[i+1], TheString);
  688.      SetIText(theHandle, TheString);
  689.      GetDItem(Dptr, MPE+3+i, theType, theHandle, theRect);  { text : '* round' }
  690.      TheString := '* round';
  691.      SetIText(theHandle, TheString);
  692.      GetDItem(Dptr, MEX+i, theType, theHandle, theRect);  { Check boxes for types of missiles }
  693.      TheValue := 0;
  694.      IF MExists[i+1] THEN TheValue := 1;
  695.      SetCtlValue(TheHandle, TheValue);
  696.    END;
  697.  
  698.    FOR i := 0 to 2 DO BEGIN     { Program the radio buttons. }
  699.      GetDItem(Dptr, MF2+i, theType, theHandle, theRect);
  700.      TheValue := 0;
  701.      IF MFlag2 = i THEN TheValue := 1;
  702.      SetCtlValue(Pointer(TheHandle), TheValue);
  703.    END;
  704.    Station := MF2 + MFlag2;
  705.  
  706.    GetDItem(Dptr, MF1, theType, theHandle, theRect);  { Set check box for MFlag1. }
  707.    TheValue := 0;
  708.    IF MFlag1 THEN TheValue := 1;
  709.    SetCtlValue(Pointer(TheHandle), TheValue);
  710.  
  711.    SelIText(DPtr, sRound, 0, 1000);     { Initial selection is StartRound. }
  712.  
  713.    REPEAT
  714.      ModalDialog(NIL, ItemHit);  { Let them hit an item... }
  715.  
  716.      IF ItemHit IN [10,11,12]  { Was it one of the radio buttons? }
  717.      THEN BEGIN
  718.        GetDItem(Dptr, Station, theType, theHandle, theRect);  { Get the old one, and turn it off. }
  719.        SetCtlValue(Pointer(TheHandle), 0);
  720.        Station := ItemHit;
  721.        GetDItem(Dptr, Station, theType, theHandle, theRect);  { Turn this one on. }
  722.        SetCtlValue(Pointer(TheHandle), 1);
  723.      END;
  724.  
  725.      IF ItemHit IN [8,15,16,17]  { Was it a check box? }
  726.      THEN BEGIN
  727.        GetDItem(Dptr, ItemHit, TheType, TheHandle, TheRect);
  728.        TheValue := 1-GetCtlValue(Pointer(TheHandle));  { Find the value and invert it. }
  729.        SetCtlValue(Pointer(TheHandle), TheValue);
  730.      END;
  731.    UNTIL ItemHit in [1,2];
  732.  
  733.    if itemHit = OK then begin   { Have to get all the new values now! }
  734.      GetDItem(Dptr, GSpeed, theType, theHandle, theRect);   { Set up all the EditText boxes }
  735.      GetIText(theHandle, TheString);
  736.      GameSpeed := MinMax(TheString, 1, 99);
  737.  
  738.      GetDItem(Dptr, SRound, theType, theHandle, theRect);
  739.      GetIText(theHandle, TheString);
  740.      StartRound := MinMax(TheString, 1, 99);
  741.  
  742.      FOR i := 0 TO 2 DO BEGIN
  743.        GetDItem(Dptr, MPB+i, theType, theHandle, theRect);  { Text for points each missile is worth. }
  744.        GetIText(theHandle, TheString);
  745.        MPBase[i+1] := MinMax(TheString, -1000, 1000);
  746.  
  747.        GetDItem(Dptr, MPE+i, theType, theHandle, theRect);  { Text for additional points each round }
  748.        GetIText(theHandle, TheString);
  749.        MPExtra[i+1] := MinMax(TheString, -1000, 1000);
  750.  
  751.        GetDItem(Dptr, MEX+i, theType, theHandle, theRect);  { Check boxes for types of missiles }
  752.        IF GetCtlValue(Pointer(TheHandle)) = 0
  753.        THEN MExists[i+1] := False
  754.        ELSE MExists[i+1] := True;
  755.      END;
  756.  
  757.      FOR i := 0 to 2 DO BEGIN     { radio buttons. }
  758.        GetDItem(Dptr, MF2+i, theType, theHandle, theRect);
  759.        IF GetCtlValue(Pointer(TheHandle)) = 1
  760.        THEN MFlag2 := i;
  761.      END;
  762.  
  763.      GetDItem(Dptr, MF1, theType, theHandle, theRect);  { MFlag1. }
  764.      IF GetCtlValue(Pointer(TheHandle)) = 0
  765.      THEN MFlag1 := False
  766.      ELSE MFlag1 := True;
  767.    end;     {if itemHit = OK}
  768.  
  769.    DisposDialog(DPtr);
  770.    GetPort(savePort);           {save whatever port was current}
  771.    SetPort(MyWindow);
  772.    InvalRect(ScreenRect);       {force the entire screen, including frame, to be redrawn}
  773.    SetPort(savePort);
  774. end;        { of DoGameOptions }
  775.  
  776. { %subtitle 'DoCommand - Does all the menu commands.' }
  777. PROCEDURE DoCommand (mResult: LongInt);
  778. VAR name:  STR255;
  779.     theMenu, theItem: INTEGER;  { Menu and item numbers. }
  780.     dummy: BOOLEAN;
  781. BEGIN
  782.   theMenu := HiWord(mResult);
  783.   theItem := LoWord(mResult);
  784.   CASE theMenu OF
  785.  
  786.     appleMenu:                  { About Missile and Desk Accessories. }
  787.       IF theItem = 1
  788.       THEN AboutMissile
  789.       ELSE BEGIN
  790.         GetItem(myMenus[1],theItem,name);
  791.         refNum := OpenDeskAcc(name);
  792.       END;
  793.  
  794.     fileMenu:      { There is currently only one option in this menu. }
  795.       BEGIN
  796.         doneFlag := TRUE;
  797.         PauseFlag := False;
  798.         esFlag := False;
  799.       END;
  800.  
  801.     EditMenu:      { Cut, copy, paste, and undo. }
  802.       BEGIN
  803.         dummy := SystemEdit(theItem-1);  { Can't cut and paste in game window. }
  804.       END;    { of editMenu }
  805.  
  806.     GameMenu:
  807.       BEGIN
  808.         { SetPort(myWindow); }
  809.         CASE theItem OF
  810.           1: PauseFlag := True;   { pause game }
  811.           2: PauseFlag := False;  { resume game }
  812.           3: ;                    { Empty line in menu. }
  813.           4: DoGameOptions;
  814.           5: BEGIN                { New Game. }
  815.                GameOver := True;
  816.                EndDelay := 100;
  817.                esFlag := False;
  818.                PauseFlag := False;
  819.              END;
  820.          END;    { of item case }
  821.       END;    { of editMenu }
  822.  
  823.     END;    { of menu case }
  824.     HiliteMenu(0);
  825.  
  826. END;  { of DoCommand }
  827.  
  828. { %subtitle 'Routine to check for events.' }
  829. PROCEDURE CheckEvents;
  830. VAR MousePoint: Point;
  831.     MouseCode: INTEGER;
  832.     TheChar: CHAR;
  833.     i: FIXED;
  834.     fbrect: Rect;          { Rectangle for drawing fireballs. }
  835. BEGIN
  836.   REPEAT
  837.     SystemTask;
  838.  
  839.     GetMouse(MousePoint);
  840.     LocalToGlobal(MousePoint);
  841.     MouseCode := FindWindow(MousePoint,whichWindow);
  842.     IF FrontWindow = MyWindow  {  If my window is activated, set cursor: }
  843.     THEN IF (WhichWindow = MyWindow)  { If it's above my window, }
  844.         AND (Mousecode <> inMenuBar)  {  but not the scroll bar, }
  845.         AND (NOT PauseFlag)           {  the game is 'active',   }
  846.         AND (FrontWindow = MyWindow)  {  my window is in front,  }
  847.         AND (yLeft > 0)               {  and the user has missiles left, then: }
  848.       THEN SetCursor(crosshairs)  { Set the Missile Command cursor. }
  849.       ELSE SetCursor(Arrow);      { Otherwise, use an arrow. }
  850.  
  851.     WHILE GetNextEvent(everyEvent,myEvent) DO
  852.       CASE myEvent.what OF
  853.  
  854.         mouseDown:
  855.           BEGIN
  856.             code := FindWindow(myEvent.where,whichWindow);
  857.             CASE code OF
  858.  
  859.             inMenuBar:
  860.               DoCommand(MenuSelect(myEvent.where));
  861.  
  862.             inSysWindow:
  863.               SystemClick(myEvent,whichWindow);
  864.  
  865.             inDrag: ;  { Can't drag game window - it's too hard to
  866.                          figure out collisions off-screen. }
  867.  
  868.             inGrow, inContent:
  869.               BEGIN
  870.                 IF (whichWindow <> FrontWindow)
  871.                   AND (WhichWindow <> MyWindow)  { Can't select MyWindow - must close other windows. }
  872.                 THEN SelectWindow(whichWindow)
  873.                 ELSE
  874.                   IF (NOT PauseFlag)  { Can't make fireballs while paused. }
  875.                     AND (FrontWindow = MyWindow) { Game must be running }
  876.                     AND (yLeft > 0)     { Make sure they have some missiles left. }
  877.                   THEN BEGIN
  878.                     GlobalToLocal(myEvent.where);
  879.                     IF myEvent.where.v > NukeHeight      { If it's too low, }
  880.                     THEN myEvent.where.v := NukeHeight;  {   make it legal. }
  881.                     AllocFb(myEvent.where);      { Put a fireball in the list. }
  882.                     yLeft := yLeft - 1;       { They have less missiles now... }
  883.                     updScore;                 { let them know. }
  884.                   END;
  885.               END;
  886.  
  887.             END;    { of code case }
  888.           END;    { of mouseDown }
  889.  
  890.         keyDown:  { No Autokey - don't want to repeat Option-N. }
  891.             IF ((MyEvent.modifiers DIV 256)MOD 2<>0)  { If command key was held down }
  892.               THEN BEGIN
  893.                 TheChar := CHR(myEvent.message MOD 256);
  894.                 DoCommand(MenuKey(TheChar));
  895.               END;
  896.  
  897.         updateEvt:
  898.           BEGIN
  899.             SetPort(myWindow);
  900.             BeginUpdate(myWindow);
  901.  
  902.             IF Playing THEN BEGIN
  903.               FOR i:= 1 TO nCities DO
  904.                 IF cities[i]
  905.                 THEN BEGIN
  906.                   DrawCity(i);
  907.                 END;
  908.               RandSeed := TickCount;  { Randomize after drawing cities. }
  909.  
  910.               FOR i:= 1 TO maxMs DO
  911.                 IF missiles[i].mode = msActive
  912.                 THEN BEGIN
  913.                   PenSize(mWidth, mWidth);
  914.                   PenPat(Gray);
  915.                   MoveTo(missiles[i].start.h, missiles[i].start.v);
  916.                   LineTo(missiles[i].pos.h DIV 16, missiles[i].pos.v DIV 16);
  917.                 END;
  918.  
  919.               FOR i:= 1 TO maxFb DO
  920.                 IF fbs[i].mode <> fbIdle
  921.                 THEN BEGIN
  922.                   fbRect := fbs[i].bounds;
  923.                   insetRect(fbRect, 30-2*fbs[i].size, 30-2*fbs[i].size);
  924.                   PenMode(PatCopy);
  925.                   FillOval(fbRect, Black);
  926.                 END;
  927.             END;
  928.  
  929.             BottomLine;
  930.  
  931.             EndUpdate(myWindow);
  932.           END;    { of updateEvt }
  933.  
  934.       END;    { of event case }
  935.  
  936.   UNTIL (FrontWindow = MyWindow)   { If another window has been selected,      }
  937.       AND NOT PauseFlag;           {   don't do anything except process events }
  938.                                    {   until game window is re-selected.       }
  939. END;  { of CheckEvents }
  940.  
  941. { %subtitle 'Print the GAME OVER message.' }
  942. PROCEDURE EndScreen;
  943. VAR aRect: Rect;
  944.     i: INTEGER;
  945.     GameOffset, OverOffset: INTEGER;
  946.     Center: Point;
  947. BEGIN
  948.   TextSize(72);
  949.   TextMode(srcBic);
  950.   PenMode(patOr);
  951.   PenPat(Black);
  952.   PenSize(5,5);
  953.  
  954.   GameOffset := StringWidth('GAME') DIV 2;
  955.   OverOffset := StringWidth('OVER') DIV 2;
  956.   Center.v := FieldHeight DIV 2;
  957.   Center.h := FieldWidth DIV 2;
  958.  
  959.   SetRect(aRect, Center.h, Center.v, Center.h, Center.v);
  960.   FOR i := 1 TO 30 DO BEGIN
  961.     CheckEvents;
  962.  
  963.     InsetRect(aRect,-5,-5);
  964.     FrameOval(aRect);
  965.     TextSize(72);
  966.     TextMode(srcBic);
  967.     Centre('GAME', Center.v - 12);
  968.     Centre('OVER', Center.v + 60);
  969.   END;
  970.  
  971.   PenMode(patBic);
  972.   FOR i := 1 TO 30 DO BEGIN
  973.     CheckEvents;
  974.  
  975.     FrameOval(aRect);
  976.     InsetRect(aRect,5,5);
  977.   END;
  978.  
  979. END;  { of EndScreen }
  980.  
  981. { %subtitle 'Play a single game.' }
  982. PROCEDURE PlayGame;
  983.   VAR i: INTEGER;
  984.       s: Str255;
  985.       msPoint: Point;
  986.       msCity, msKind: INTEGER;
  987.       rMissiles: INTEGER;      { "round missiles" - number to set eLeft to each round. }
  988.       yMissiles: INTEGER;      { "your Missiles" - similar to rMissiles. }
  989.       RoundOver: BOOLEAN;
  990.       aString: Str255;
  991.       Points: INTEGER;
  992.  
  993.   PROCEDURE InitRound;
  994.   VAR
  995.     i: INTEGER;
  996.   BEGIN
  997.     RoundOver := False;
  998.     endDelay := 0;
  999.     Playing := True;
  1000.  
  1001.     DrawStuff;  { Redraw screen. }
  1002.  
  1003.     FOR i := 1 TO maxfb DO
  1004.       fbs[i].mode := fbIdle;
  1005.  
  1006.     FOR i := 1 TO maxms DO
  1007.       missiles[i].mode := msIdle;
  1008.  
  1009.     FOR i := 1 TO 3 DO
  1010.       MPoints[i] := MPBase[i] + MPExtra[i] * RoundNumber;
  1011.  
  1012.     FOR i := 1 TO nCities DO
  1013.       cities2[i] := cities[i];
  1014.  
  1015.     nMissiles := 0;
  1016.  
  1017.     MirvRate := 0;
  1018.     mirvHeight := FieldHeight DIV 4;
  1019.     mirvNasty := 30;
  1020.     msSpeed := 60;
  1021.     IF RoundNumber >2 THEN MirvRate := 10;
  1022.     IF RoundNumber >4 THEN msSpeed := 80;
  1023.     IF RoundNumber >6 THEN MirvRate := 20;
  1024.     IF RoundNumber >8 THEN MirvNasty := 50;
  1025.     IF RoundNumber >10 THEN MirvHeight := FieldHeight DIV 3;
  1026.     IF RoundNumber >12 THEN msSpeed := 90;
  1027.     IF RoundNumber >14 THEN MirvRate := 30;
  1028.     IF RoundNumber >19 THEN msSpeed := 100;
  1029.  
  1030.     msRate := 5 + RoundNumber;
  1031.     rMissiles := (RoundNumber*RoundNumber)DIV 6 + RoundNumber + 6;  { Compute # of enemy missiles }
  1032.     yMissiles := rMissiles + RoundNumber;            { Adjust yMissiles accordingly. }
  1033.     eLeft := rMissiles;
  1034.     yLeft := yMissiles;
  1035.     RoundNumber := RoundNumber + 1;
  1036.     IF (RoundNumber MOD 5) = 0
  1037.     THEN bCities := bCities + 1;
  1038.  
  1039.     BottomLine;
  1040.     FlushEvents(everyEvent,0);  { Ignore unprocessed events from previous round. }
  1041.   END;
  1042.  
  1043.   PROCEDURE EndBonus;
  1044.   VAR
  1045.     i: INTEGER;
  1046.   BEGIN;
  1047.     ClearScreen;
  1048.     BottomLine;
  1049.  
  1050.     TextSize(24);
  1051.     TextMode(srcCopy);
  1052.  
  1053.     aString := 'End of round   ';
  1054.     aString[15] := Chr(48+(RoundNumber Mod 10));
  1055.     IF RoundNumber>9
  1056.     THEN aString[14] := Chr(48+(RoundNumber DIV 10));
  1057.  
  1058.     IF CitiesLeft>0
  1059.     THEN BEGIN  { give bonus points for cities }
  1060.       Centre(aString, 100);
  1061.       Centre('Bonus:', 130);
  1062.       Points := 0;
  1063.       FOR i:= 1 TO nCities DO
  1064.         IF (cities[i] AND NOT DoneFlag) THEN BEGIN
  1065.           LastTick := TickCount;
  1066.  
  1067.           DrawCity(i);
  1068.           Points := Points + RoundNumber*20;
  1069.           Score := Score + RoundNumber*20;
  1070.           TextSize(24);
  1071.           NumtoString(Points, S);
  1072.           Centre(S, 160);
  1073.           updScore;
  1074.  
  1075.           REPEAT
  1076.              CheckEvents;
  1077.           UNTIL (TickCount >= LastTick + 30) OR DoneFlag;
  1078.         END
  1079.     END;  { of bonus for cities. }
  1080.     IF (bCities > 0) AND (CitiesLeft < 6)
  1081.     THEN BEGIN  { Give a bonus city. }
  1082.       LastTick := TickCount;
  1083.       TextSize(24);
  1084.       Centre('* Bonus City *', 190);
  1085.       CitiesLeft := CitiesLeft + 1;
  1086.       bCities := bCities - 1;
  1087.       REPEAT
  1088.         i := Rnd(6)
  1089.       UNTIL (NOT Cities[i]);
  1090.       Cities[i] := true;
  1091.       REPEAT
  1092.          CheckEvents;
  1093.       UNTIL (TickCount >= LastTick + 60) OR DoneFlag;
  1094.     END  { of bonus city routine. }
  1095.   END;  { of EndBonus }
  1096.  
  1097.   BEGIN
  1098.     RoundNumber := StartRound;  { Start out at this round. }
  1099.     Score := 0;
  1100.     eDestroyed := 0;
  1101.  
  1102.     FOR i := 1 TO nCities DO
  1103.       cities[i] := true;
  1104.     citiesLeft := nCities;
  1105.     bCities := 0;
  1106.  
  1107.     NukeHeight := FieldHeight - CityHeight - (BuildHeight DIV 2) - fbRad*fbRadRate;
  1108.     endv := FieldHeight - CityHeight - (BuildHeight DIV 2);
  1109.  
  1110.     GameOver := False;
  1111.     esFlag := True;
  1112.     lastTick := TickCount;
  1113.  
  1114.     REPEAT  { Repeat loop for playing rounds. }
  1115.       InitRound;
  1116.  
  1117.       REPEAT  { Repeat loop for animating objects in game. }
  1118.         CheckEvents;
  1119.  
  1120.         IF TickCount >= lastTick + gameSpeed  { If enough time has elapsed since the
  1121.                                                   last update, update objects on screen. }
  1122.         THEN BEGIN
  1123.           lastTick := TickCount;
  1124.           IF (eLeft <= 0) AND (nMissiles = 0) THEN RoundOver := True;
  1125.  
  1126.           IF RoundOver OR GameOver
  1127.           THEN endDelay := endDelay + 1
  1128.           ELSE
  1129.             IF (rnd(100) < msRate) AND (eLeft > 0)
  1130.             THEN BEGIN               { Launch an enemy missile every now and then. }
  1131.               eLeft := eLeft - 1;
  1132.               msPoint.v := 0;
  1133.               msPoint.h := Rnd(FieldWidth - 4);
  1134.               REPEAT
  1135.                 msCity := Rnd(nCities);      { Pick a city. }
  1136.               UNTIL ((Rnd(3) = 1) AND mFlag1)
  1137.                 OR cities2[msCity];  { Try again for most missiles if city was destroyed. }
  1138.  
  1139.               msKind := 0;
  1140.               IF MExists[msNormal]
  1141.               THEN msKind := msNormal;
  1142.               IF (rnd(100) <= MirvRate) AND MExists[msMirv]
  1143.               THEN msKind := msMirv;
  1144.               IF msKind = 0 THEN msKind := msNormal;
  1145.  
  1146.               AllocMs(msPoint, msCity, msKind);
  1147.             END;
  1148.  
  1149.           AdvanFb;        { Advance state of fireballs. }
  1150.           AdvanMs;        { Advance position of missiles. }
  1151.         END;
  1152.  
  1153.       UNTIL ((GameOver OR RoundOver) AND (endDelay > 30)) OR doneFlag;
  1154.                           { End of loop to move objects on screen. }
  1155.  
  1156.       Playing := False;
  1157.       IF esFlag AND (CitiesLeft+bCities>0) AND NOT DoneFlag
  1158.       THEN EndBonus;
  1159.  
  1160.     UNTIL GameOver OR DoneFlag;  { End of loop to play rounds. }
  1161.  
  1162.     IF Score > HighScore
  1163.     THEN HighScore := Score;
  1164.  
  1165.     IF esFlag
  1166.     THEN EndScreen;
  1167.  
  1168. END;  { of PlayGame }
  1169.  
  1170. { %subtitle 'Main program.' }
  1171. BEGIN    { main program }
  1172.   SetUp;
  1173.  
  1174.   REPEAT
  1175.     PlayGame;
  1176.   UNTIL doneFlag;
  1177. END.
  1178.  
  1179. *
  1180. ----------- Cut here --- Missile.r (resource source file) ------------
  1181.  
  1182.  
  1183. *  Missiler -- resource file for Missile
  1184. *               (Robert Munafo @ Dartmouth College)
  1185.  
  1186. RJUNK27.RSRC
  1187.  
  1188. * These are the menus for the program -
  1189. *   Menu ID
  1190. *   Menu title
  1191. *     Menu Item
  1192. *     Menu Item, etc.
  1193. Type MENU
  1194.   ,1
  1195.   @
  1196.     About Missile
  1197.     (-
  1198.  
  1199.   ,256
  1200.   File
  1201.     Quit
  1202.  
  1203.   ,257
  1204.   Edit
  1205.     Undo/Z
  1206.     (-
  1207.     Cut/X
  1208.     Copy/C
  1209.     Paste/V
  1210.     Clear
  1211.  
  1212.   ,258
  1213.   Game
  1214.     Pause/S
  1215.     Resume/Q
  1216.     (-
  1217.     Game Options/O
  1218.     New Game/N
  1219.  
  1220. * String information
  1221. *    ID
  1222. *    string
  1223. Type STR
  1224.   ,300
  1225. Missile Command by Robert P. Munafo
  1226.  
  1227.   ,350
  1228. Vers 2.3   August 18,1984
  1229.  
  1230. * Dialog Box for About Missile
  1231. *    ID
  1232. *    display rectangle
  1233. *    visible, standard box, no close box, RefCon
  1234. *    ID of content list
  1235. Type DLOG
  1236.   ,450
  1237.     100 110 190 402
  1238.     Visible 1 NoGoAway 0
  1239.     500
  1240.  
  1241. * Dialog Item List for About Missile
  1242. *   ID of list
  1243. *   number of items in list
  1244. *     a button that can be hit
  1245. *     display rectangle (coordinates local to box)
  1246. *     title of button
  1247. Type DITL
  1248.   ,500
  1249.   3
  1250.     BtnItem Enabled
  1251.     65 106 85 186
  1252. OK
  1253.  
  1254.     StatText Disabled
  1255.     10 10 30 300
  1256.  
  1257.     StatText Disabled
  1258.     35 10 55 300
  1259.  
  1260. * Dialog box for Game Options
  1261. Type DLOG
  1262.   ,451
  1263.     40 40 302 472
  1264.     Visible 1 NoGoAway 0
  1265.     501
  1266.  
  1267. * Dialog item list for Game Options
  1268. *   Lots and lots of little options for people to play around with.
  1269. *   Run the program if you want to see what the dialog box looks like...
  1270. Type DITL
  1271.   ,501
  1272.   29
  1273.     BtnItem Enabled
  1274.     200 320 220 400
  1275. OK
  1276.  
  1277.     BtnItem Enabled
  1278.     230 320 250 400
  1279. Cancel
  1280.  
  1281.     StatText Disabled
  1282.     8 130 24 400
  1283. Missile Command Options
  1284.  
  1285.     StatText Disabled
  1286.     40 4 56 124
  1287. Animation delay
  1288.  
  1289.     EditText Enabled
  1290.     40 140 56 170
  1291. num
  1292.  
  1293.     StatText Disabled
  1294.     40 200 56 350
  1295. Start off with round #
  1296.  
  1297.     EditText Enabled
  1298.     40 360 56 380
  1299. num
  1300.  
  1301.     ChkItem Enabled
  1302.     64 4 80 400
  1303. Missiles aim for dead cities
  1304.  
  1305.     StatText Disabled
  1306.     88 4 104 400
  1307. Missiles blow up when destroyed:
  1308.  
  1309.     RadioItem Enabled
  1310.     104 4 120 150
  1311. Always
  1312.  
  1313.     RadioItem Enabled
  1314.     104 160 120 400
  1315. Never
  1316.  
  1317.     RadioItem Enabled
  1318.     120 4 136 400
  1319. Less often with each round
  1320.  
  1321.     StatText disabled
  1322.     144 4 160 100
  1323. Type:
  1324.  
  1325.     StatText disabled
  1326.     144 160 160 400
  1327. Points for destroying:
  1328.  
  1329.     ChkItem enabled
  1330.     176 4 192 140
  1331. Missiles
  1332.  
  1333.     ChkItem enabled
  1334.     196 4 212 140
  1335. MIRVs
  1336.  
  1337.     ChkItem disabled
  1338.     216 4 232 140
  1339. Smart bombs
  1340.  
  1341.     EditText enabled
  1342.     176 160 192 190
  1343. num
  1344.  
  1345.     EditText enabled
  1346.     196 160 212 190
  1347. num
  1348.  
  1349.     EditText enabled
  1350.     216 160 232 190
  1351. num
  1352.  
  1353.     StatText disabled
  1354.     176 200 192 208
  1355. plus
  1356.  
  1357.     StatText disabled
  1358.     196 200 212 208
  1359. plus
  1360.  
  1361.     StatText disabled
  1362.     216 200 232 208
  1363. plus
  1364.  
  1365.     EditText enabled
  1366.     176 215 192 245
  1367. num
  1368.  
  1369.     EditText enabled
  1370.     196 215 212 245
  1371. num
  1372.  
  1373.     EditText enabled
  1374.     216 215 232 245
  1375. num
  1376.  
  1377.     StatText disabled
  1378.     176 255 192 300
  1379. x round
  1380.  
  1381.     StatText disabled
  1382.     196 255 212 300
  1383. x round
  1384.  
  1385.     StatText disabled
  1386.     216 255 232 300
  1387. x round
  1388.  
  1389. * Crosshairs cursor.  It has no mask, so it will look
  1390. * like a white crosshairs on a black background.
  1391. *   ID
  1392. *   hex data
  1393. *   hex mask
  1394. *   hotspot (v,h)
  1395. Type CURS
  1396.   ,256
  1397.   0180018001800180018001800180FFFFFFFF0180018001800180018001800180
  1398.   0000000000000000000000000000000000000000000000000000000000000000
  1399.   0008 0008
  1400.  
  1401. * Pattern of closely spaced vertical stripes, for drawing
  1402. * the cities.
  1403. Type PAT
  1404.   ,256
  1405.   5555555555555555
  1406.  
  1407. * This is the window the game graphics are drawn in.
  1408. * It is so big that all of its edges are off-screen -
  1409. * this makes it appear that the program is drawing on
  1410. * the entire screen without using a window.
  1411. Type WIND
  1412.   ,256
  1413.   Missile Command
  1414.   20 0 342 512
  1415.   Visible NoGoAway
  1416.   0
  1417.   0
  1418.  
  1419. * The Finder must be able to find this string -
  1420. * it identifies the version number of the program.
  1421. Type RJUN = STR
  1422. ,0
  1423. Game Vers 2.3   August 18,1984
  1424.  
  1425. * Part of the "Finder Info" :
  1426. *   Resource ID (purgable)
  1427. *   Hex for "APPL"
  1428. *   Local icon ID
  1429. *   name of file that must be transferred along with
  1430. *     the application when it is copied ("00" means none)
  1431. Type FREF = HEXA
  1432.   ,128(32)
  1433.   4150504C
  1434.   0000
  1435.   00
  1436.  
  1437. * Special icon and mask for application.
  1438. * The mask is a big solid rectangle to make it easy
  1439. * to select in the Finder.
  1440. Type ICN# = HEXA
  1441.   ,128 (32)
  1442.   00000000
  1443.   00000000
  1444.   00000000
  1445.   00000000
  1446.   00000000
  1447.   00000000
  1448.   01000100
  1449.   01000200
  1450.   00800400
  1451.   00800800
  1452.   00401000
  1453.   00402000
  1454.   00204000
  1455.   00008380
  1456.   000107C0
  1457.   000207C0
  1458.   000467C0
  1459.   0008F380
  1460.   0010F000
  1461.   00206000
  1462.   00400000
  1463.   00800000
  1464.   01000000
  1465.   00000000
  1466.   00000000
  1467.   00000000
  1468.   20202020
  1469.   28282828
  1470.   3A3A3A3A
  1471.   7E7E7E7E
  1472.   00000000
  1473.   00000000
  1474.   00000000
  1475.   00000000
  1476.   00000000
  1477.   00000000
  1478.   00000000
  1479.   FFFFFFFF
  1480.   FFFFFFFF
  1481.   FFFFFFFF
  1482.   FFFFFFFF
  1483.   FFFFFFFF
  1484.   FFFFFFFF
  1485.   FFFFFFFF
  1486.   FFFFFFFF
  1487.   FFFFFFFF
  1488.   FFFFFFFF
  1489.   FFFFFFFF
  1490.   FFFFFFFF
  1491.   FFFFFFFF
  1492.   FFFFFFFF
  1493.   FFFFFFFF
  1494.   FFFFFFFF
  1495.   FFFFFFFF
  1496.   FFFFFFFF
  1497.   FFFFFFFF
  1498.   FFFFFFFF
  1499.   FFFFFFFF
  1500.   FFFFFFFF
  1501.   FFFFFFFF
  1502.   FFFFFFFF
  1503.   FFFFFFFF
  1504.   FFFFFFFF
  1505.   00000000
  1506.  
  1507.  
  1508. * Bundle data for Missile:
  1509. *   Bundle ID
  1510. *   the owner ("RJUN" in hex), and ID of version data
  1511. *   # of types in the bundle (less one) - in this case,
  1512. *     there are two types.
  1513. *   "ICN#" in hex, and # of icons less one.
  1514. *   Local ID 0 maps to global ID 256.
  1515. *   "FREF" in hex, and # of FREF's less one.
  1516. *   Local ID 0 maps to global ID 256.
  1517. * (note that if the game created any documents, this
  1518. * bundle would need at least two icons and two FREF's.)
  1519. Type BNDL = HEXA
  1520.   ,128
  1521.   524A554E 0000
  1522.   0001
  1523.   49434E23 0000
  1524.   0000 0080
  1525.   46524546 0000
  1526.   0000 0080
  1527.  
  1528. * The code - my Exec file always saves the executable
  1529. * code with this name.
  1530. Type CODE
  1531.  RJUNK27L,0
  1532.  
  1533. *
  1534. ------------ Cut Here --- End of Missile Command Source -------------
  1535. -- 
  1536. mrob's .sig #4 |                                            '^`^` T_,       .-`
  1537. collect all 5! |    Internet: mrob@world.std.com                   "r"-, .---F
  1538.