(************************************************************************************

:Program.       Mine

:Contents.      neue Implementation eines alten Computer-Spiels

:Remark.        benötigt KICK 2.00^


:Copyright.     "freely distributable copyrighted software"

:Author.        Thomas Ansorge

:Address.       Dinkelackerring 55, W-6730 Neustadt, Deutschland, Europa


:Language.      Modula-2

:Translator.    M2Amiga V4.096 (deutsch)


:Imports.       BeepSupport [Thomas Ansorge]
:Imports.       Images [Thomas Ansorge]
:Imports.       OptReqL [Thomas Ansorge]
:Imports.       OptReqToolsL [Thomas Ansorge]
:Imports.       ReqSupport [Thomas Ansorge]

:Support.       reqtools.library (Nico François) falls vorhanden, sonst
:Support.       req.library (Colin Fox, Bruce Dawson) falls vorhanden

:Support.       viele Tips von Michi

:Support.       Fridtjof half debuggen


:Version.       1.6 vom 21.09.1992

:History.       1.0 vom 08.08.1992: es läuft.

:History.       1.1 vom 23.08.1992:
:History.           - mit 0-Automatik
:History.           - ab spielfeldMax >= 8: Anzeige Anzahl Fahnen

:History.       1.2 vom 11.09.1992:
:History.           - "Zeiger ist NIL"-Bug unter KICK 39.x.
:History.           - Nil-Chk abgestellt (funktioniert oft, hier?)

:History.       1.3 vom 11.09.1992: Sicherheitsabfrage PropInfo beim Slider

:History.       1.4 vom 14.09.1992:
:History.           - Gadget-Abfrage sicherer
:History.           - Ruhmeshalle einfach per Mausklick verlaßbar
:History.           - Nil-Chk wieder drin.
:History.           - closeWindow beendet jetzt stets das Spiel, die
:History.             Ruhmeshalle wird per Klick ins Fenster verlassen
:History.           - Anzahl Minen in 2 verschiedenen Farben
:History.           - BANG-Requester raus
:History.           - Abfrage "IF gadgetPtr # NIL" im ersten Fenster bei der Abfrage
:History.             der Gadgets nach "IF gadgetUp IN flags". Ist das der Bug unter
:History.             OS 39.x (beta)?
:History.           - Mausklick beendet Minen-Fenster, falls kein Str-Requester

:History.       1.5 vom 17.09.1992:
:History.           - Bug in der Programmierung von GadTools behoben (es war eine
:History.             fehlende PropInfo-Struktur)

:History.       1.6 vom 21.09.1992:
:History.           - Abfrage der Msg-Ports jetzt gemäß RKM: Libraries
:History.           - explizite Liste der benötigten Lib.-Mindestversionen
:History.           - RefreshWindow jetzt in jedem Fenster

************************************************************************************)


(*$ DEFINE Deutsch := TRUE *)

MODULE Mine;

FROM Arts IMPORT Assert;

FROM ASCII IMPORT nul;

FROM BeepSupport IMPORT Beep, infoPri, Sound;

FROM Conversions IMPORT ValToStr;

FROM DiskFontL IMPORT diskfontVersion, OpenDiskFont;

FROM DosD IMPORT Date, DatePtr;

FROM DosL IMPORT dosVersion, DateStamp;

FROM ExecL IMPORT execVersion, GetMsg, ReplyMsg, WaitPort;

FROM FileSystem IMPORT Close, File, Lookup, ReadByteBlock, Response, WriteByteBlock;

FROM GadToolsD IMPORT buttonIDCMP, buttonKind, GtTags, NewGadget, NewGadgetFlags,
   NewGadgetFlagSet, sliderIDCMP, sliderKind;

FROM GadToolsL IMPORT CreateContext, CreateGadgetA, DrawBevelBoxA, FreeGadgets,
   FreeVisualInfo, gadtoolsVersion, GetVisualInfoA, GTBeginRefresh, GTEndRefresh, 
   GTGetIMsg, GTRefreshWindow, GTReplyIMsg;

FROM GraphicsD IMPORT FontFlagSet, FontStyleSet, jam2, TextAttr, TextFontPtr;

FROM GraphicsL IMPORT graphicsVersion, CloseFont;

IMPORT Images;

FROM InputEvent IMPORT Qualifiers, QualifierSet;

FROM IntuitionD IMPORT GadgetPtr, GaTags, IDCMPFlags, IDCMPFlagSet, Image,
   IntuiMessage, IntuiMessagePtr, IntuiText, Screen, ScreenPtr, WaTags, Window,
   WindowFlags, WindowFlagSet, WindowPtr;

FROM IntuitionL IMPORT AddGList, BeginRefresh, CloseWindow, DrawImage, EndRefresh,
   intuitionVersion, LockPubScreen, ModifyIDCMP, OpenWindowTagList, PrintIText, 
   RefreshGadgets, RemoveGList, UnlockPubScreen;

FROM OptReqL IMPORT reqBase;

FROM OptReqToolsL IMPORT reqtoolsBase;

IMPORT R;

FROM RandomNumber IMPORT PutSeed, RND;

FROM RequesterSupport IMPORT noError, ReqLib, TextRequest, StringRequest;

FROM String IMPORT Concat, ConcatChar, Copy, Insert;

FROM SYSTEM IMPORT ADDRESS, ADR, ASSEMBLE, CAST, TAG;

FROM UtilityD IMPORT tagEnd;

(* ------------------------------------------------------------------------------- *)

(* die Farbverteilung auf der Standard-Workbench *)

CONST grau    = 0;
      schwarz = 1;
      weiss   = 2;
      blau    = 3;

CONST mineStr = "Mine";

CONST sliderGadID = 1;
      weiterGadID = 2;
      infoGadID   = 3;
      ruhmGadID   = 4;

(* TAG-Listen *)

CONST maxTags = 9;

(* der Font *)

CONST fontHoehe = 8;
      fontName  = "topaz.font";

(*$ IF Deutsch *)

CONST info = "Infos und Hilfe";

      kick20 = "Mine benötigt KICK 2.00^!";

      openWindowError = "konnte Fenster nicht öffnen!";

      ruhmeshalleFensterName = "Mine Ruhmeshalle für n = ";

      ruhmeshalleGadgetName = "Ruhmeshalle ansehen";

      seitenlaenge = "Seitenlänge:";

      stringReqTitle = "Mine: Ihren Namen bitte:";

      userPortError = "leider kein IDCMP im Fenster!";

      weiter = "weiter";

      zeitS = "Zeit: ";

(*$ ELSE *)

CONST info = "infos and help";

      kick20 = "Mine needs KICK 2.00^!";

      openWindowError = "could not open window!";

      ruhmeshalleFensterName = "Mine Highscores for n = ";

      ruhmeshalleGadgetName = "show Highscores";

      seitenlaenge = "field size :";

      stringReqTitle = "Mine: Please enter your name:";

      userPortError = "Could not get IDCMP!";

      weiter = "continue";

      zeitS = "Time: ";

(*$ ENDIF *)

(* Das Spielfeld *)

CONST boxX         = 24;
      boxY         = 12;
      fahne        = -20; (* wird auf Wert addiert *)
      horoffset    = 1;
      mine         = -1;
      nummer       = -3;
      randBreite   = 2;

(* Das quadratische Spielfeld *)

TYPE Spielfeld = ARRAY [1..77], [1..77] OF SHORTINT;

TYPE Zeit = RECORD
               ticks    : [0..49]; (* 50 Ticks pro Sekunde von Intuition *)
               minuten  : LONGINT;
               sekunden : LONGINT;
               zeitStr  : ARRAY [0..11] OF CHAR;
               hilfeStr : ARRAY [0..2] OF CHAR;
               zeitIText: IntuiText;
            END (* RECORD Zeit *);

(* Die Ruhmeshalle *)

CONST ruhmeshalleName = "Mine-Highscores_";

TYPE StringRuhm = ARRAY [0..26] OF CHAR; (* der komplette String mit allem drin! *)

     RuhmeshalleEintrag = RECORD
                             anzM: LONGINT;
                             zeit: RECORD
                                      min,
                                      sec: LONGINT;
                                   END (* RECORD Zeit *);
                             name: StringRuhm;
                          END (* RECORD *);

     Ruhmeshalle = ARRAY [1..10] OF RuhmeshalleEintrag;

VAR anzFahnen       : LONGINT;
    anzMinen        : LONGINT;
    anzNummer       : LONGINT;
    bombeImage      : Image;
    clickedImage    : Image;
    clickmeImage    : Image;
    code            : CARDINAL;
    contextPtr      : GadgetPtr;
    date            : Date;
    eintrag         : RuhmeshalleEintrag;
    ende            : BOOLEAN;
    err             : BOOLEAN;
    explosionImage  : Image;
    fahneImage      : Image;
    fahneORImage    : Image;
    flags           : IDCMPFlagSet;
    fontAttr        : TextAttr;
    fontPtr         : TextFontPtr;
    gadgetPtr       : GadgetPtr;
    gList           : GadgetPtr;
    i               : INTEGER;
    innerWidth      : LONGINT;
    innerHeight     : LONGINT;
    intuiText       : IntuiText;
    j               : INTEGER;
    mausX, mausY    : INTEGER;
    nachricht       : IntuiMessagePtr;
    newGadget       : NewGadget;
    pos             : LONGINT; (* Platz in der Ruhmeshalle *)
    qualifiers      : QualifierSet;
    reqLib          : ReqLib;
    ruhmeshalle     : Ruhmeshalle;
    ruhmeshalleEintr: RuhmeshalleEintrag;
    screenPtr       : ScreenPtr;
    sliderZahlPos   : INTEGER;
    spielfeld       : Spielfeld;
    spielfeldMax    : LONGINT;
    spielFeldMax    : LONGINT;
    string2         : ARRAY [0..2] OF CHAR;
    string30        : ARRAY [0..30] OF CHAR;
    string80        : ARRAY [0..80] OF CHAR;
    tagList         : ARRAY [1..2 * maxTags] OF LONGINT;
    visualInfo      : ADDRESS;
    windowPtr       : WindowPtr;
    windowPtr2      : WindowPtr;
    zeit            : Zeit;

(* ------------------------------------------------------------------------------- *)

(* Die Prozeduren etc. sind alphabetisch sortiert, daher sind evt. FORWARDs nötig. *)

PROCEDURE Ziffer (ziffer: SHORTINT): CHAR; FORWARD;

(* ------------------------------------------------------------------------------- *)

PROCEDURE AnzMinen (spielfeldMax: LONGINT): LONGINT;

   BEGIN (* Funktion AnzMinen *)

   RETURN (spielfeldMax * spielfeldMax) DIV 6;
END AnzMinen (* Funktion *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE DrawClickedImage (VAR windowPtr: WindowPtr;
                            VAR spielfeld: Spielfeld;
                            spielfeldMax : INTEGER;
                            i, j         : INTEGER;
                            VAR anzNummer: LONGINT);

   (* spielfeld [i, j] # mine! *)

   (* zeichnet das Clicked-Image, schreibt die Anzahl an Minen hinein, falls 0: *)
   (* ruft rekursiv sich selbst auf, bis keine 0 mehr da                        *)

   (* i: Zeile, j: Spalte *)

   VAR
      intuiText: IntuiText;
      string2  : ARRAY [0..2] OF CHAR;

   (* ---------------------------------------------------------------------------- *)

   PROCEDURE Farbe (zeile, spalte, anzMinen, max: INTEGER): SHORTCARD;

      (* ------------------------------------------------------------------------- *)

      PROCEDURE Nachbarn (zeile, spalte, max: INTEGER): INTEGER;

         (* macht Gebrauch davon, daß das Spielfeld quadratisch ist *)

         BEGIN (* Funktion Nachbarn *)

         IF ((zeile = 1) AND (spalte = 1)) OR ((zeile = max) AND (spalte = max)) OR
            ((zeile = 1) AND (spalte = max)) OR ((zeile = max) AND (spalte = 1)) THEN
            RETURN 3;

         ELSE (* IF (zeile = spalte) *)
            IF (zeile = 1) OR (spalte = 1) OR (zeile = max) OR (spalte = max) THEN
               RETURN 5; (* Rand, aber keine Ecke *)

            ELSE (* IF (zeile = 1 *)
               RETURN 8; (* mittendrin *)
            END (* IF (zeile = 1 *);
         END (* IF (zeile = spalte *);
      END Nachbarn (* Funktion *);

      (* ------------------------------------------------------------------------- *)

      BEGIN (* Funktion Farbe *)

      IF anzMinen = 0 THEN
         RETURN grau;
      END (* IF anzMinen *);

      IF Nachbarn (zeile, spalte, max) > (2 * anzMinen) THEN
         RETURN schwarz;

      ELSE (* IF (Nachbarn *)
         RETURN blau;
      END (* IF (Nachbarn *);
   END Farbe (* Funktion *);

   (* ---------------------------------------------------------------------------- *)

   BEGIN (* Prozedur DrawClickedImage *)

   DrawImage (windowPtr^.rPort, ADR (clickedImage),
      2 * randBreite + (j - 1) * clickedImage.width + horoffset * (j - 1),
      randBreite + (i - 1) * clickedImage.height);

   intuiText.frontPen := Farbe (i, j, spielfeld [i, j], spielfeldMax);

   IF intuiText.frontPen # grau THEN
      WITH intuiText DO
         (* frontPen: s. o. *)
         backPen := grau;
         leftEdge := 8;
         topEdge := 2;
         drawMode := jam2;
         iText := ADR (string2);
         iTextFont := ADR (fontAttr);
         nextText := NIL;
      END (* WITH intuiText *);

      string2 [0] := Ziffer (spielfeld [i, j]);
      string2 [1] := nul;

      PrintIText (windowPtr^.rPort, ADR (intuiText),
         2 * randBreite + (j - 1) * clickedImage.width + horoffset * (j - 1),
         randBreite + (i - 1) * clickedImage.height);
   END (* IF intuiText.frontPen *);

   (* 0 soweit vorhanden abräumen *)

   IF spielfeld [i, j] = 0 THEN
      spielfeld [i, j] := nummer;
      INC (anzNummer);

      IF i # 1 THEN
         IF j # 1 THEN
            IF spielfeld [i - 1, j - 1] >= 0 THEN
               DrawClickedImage (windowPtr, spielfeld, spielfeldMax, i - 1, j - 1,
                  anzNummer);
            END (* IF spielfeld *);
         END (* IF j *);

         IF spielfeld [i - 1, j] >= 0 THEN
            DrawClickedImage (windowPtr, spielfeld, spielfeldMax, i - 1, j,
               anzNummer);
         END (* IF spielfeld *);

         IF j # spielfeldMax THEN
            IF spielfeld [i - 1, j + 1] >= 0 THEN
               DrawClickedImage (windowPtr, spielfeld, spielfeldMax, i - 1, j + 1,
                  anzNummer);
            END (* IF spielfeld *);
         END (* IF j *);
      END (* IF i # 1 *);

      IF j # 1 THEN
         IF spielfeld [i, j - 1] >= 0 THEN
            DrawClickedImage (windowPtr, spielfeld, spielfeldMax, i, j - 1,
               anzNummer);
         END (* IF spielfeld *);
      END (* IF j *);

      IF j # spielfeldMax THEN
         IF spielfeld [i, j + 1] >= 0 THEN
            DrawClickedImage (windowPtr, spielfeld, spielfeldMax, i, j + 1,
               anzNummer);
         END (* IF spielfeld *);
      END (* IF j *);

      IF i # spielfeldMax THEN
         IF j # 1 THEN
            IF spielfeld [i + 1, j - 1] >= 0 THEN
               DrawClickedImage (windowPtr, spielfeld, spielfeldMax, i + 1, j - 1,
                  anzNummer);
            END (* IF spielfeld *);
         END (* IF j *);

         IF spielfeld [i + 1, j] >= 0 THEN
            DrawClickedImage (windowPtr, spielfeld, spielfeldMax, i + 1, j,
               anzNummer);
         END (* IF spielfeld *);

         IF j # spielfeldMax THEN
            IF spielfeld [i + 1, j + 1] >= 0 THEN
               DrawClickedImage (windowPtr, spielfeld, spielfeldMax, i + 1, j + 1,
                  anzNummer);
            END (* IF spielfeld *);
         END (* IF j *);
      END (* IF i # spielfeldMax *);

   ELSE (* IF spielfeld [i, j] *)
      (* Das ist NICHT wegoptimierbar!!! Sonst dicker BUG!!! *)
      spielfeld [i, j] := nummer;
      INC (anzNummer);
   END (* IF spielfeld [i, j] *);
END DrawClickedImage (* Prozedur *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE Eor (z1{R.D0}, z2{R.D1}: LONGINT): LONGINT;

   BEGIN (* Funktion Eor *)

   ASSEMBLE (
      EOR.L D1, D0
   END);

   RETURN z1;
END Eor (* Funktion *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE FuegeEintragEin (VAR ruhmeshalle: Ruhmeshalle;
                           eintrag        : RuhmeshalleEintrag;
                           pos            : LONGINT);

   VAR err  : BOOLEAN;
       i    : [1..10];
       hilfe: ARRAY [0..2] OF CHAR;
       str  : ARRAY [0..SIZE (eintrag.name)] OF CHAR;

   (* ---------------------------------------------------------------------------- *)

   BEGIN (* Prozedur FuegeEintragEin *)

   (* Platz schaffen *)

   FOR i := 9 TO pos BY -1 DO
      ruhmeshalle [i + 1].anzM := ruhmeshalle [i].anzM;
      ruhmeshalle [i + 1].zeit.min := ruhmeshalle [i].zeit.min;
      ruhmeshalle [i + 1].zeit.sec := ruhmeshalle [i].zeit.sec;
      Copy (ruhmeshalle [i + 1].name, ruhmeshalle [i].name);
   END (* FOR i *);

   (* eintragen *)

   ValToStr (eintrag.anzM, FALSE, str, 10, 3, " ", err);
   ConcatChar (str, " ");
   ValToStr (eintrag.zeit.min, FALSE, hilfe, 10, 2, "0", err);
   Concat (str, hilfe);
   ConcatChar (str, ":");
   ValToStr (eintrag.zeit.sec, FALSE, hilfe, 10, 2, "0", err);
   Concat (str, hilfe);
   ConcatChar (str, " ");
   Concat (str, eintrag.name);

   WITH ruhmeshalle [pos] DO
      anzM := eintrag.anzM;
      zeit.min := eintrag.zeit.min;
      zeit.sec := eintrag.zeit.sec;
      Copy (name, str);
   END (* WITH *);
END FuegeEintragEin (* Prozedur *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE Info (windowPtr: WindowPtr);

   (* zeigt via Text-Requester Infos und Hilfen zum Spiel an *)

   (*$ IF Deutsch *)

   CONST quitGad = "Quit";

   CONST shareware = (* war mal Shareware, daher der Name *)
      "                          Mine © 1992 by\n" +
      "\n" +
      "     Thomas Ansorge, Dinkelackerring 55, W-6730 Neustadt/Weinstaße,\n" +
      "                        Deutschland, Europa\n" +
      "\n" +
      "\n" +
      "\"Mine\" besteht aus den Dateien Mine.deutsch, Mine.english, Mine.dok,\n" +
      "Mine.doc und Mine-Quelltexte.lha.\n" +
      "\n" +
      "Mine ist \"freely distributable copyrighted software\". Sie dürfen es nur\n" +
      "auf nicht-kommerzieller Basis und nur als Ganzes weiterverbreiten und\n" +
      "benutzen, aber alle anderen Rechte bleiben bei mir.\n" +
      "\n" +
      "Die (insgesamt 73) Dateien Mine-Highscores_?? sind von Mine verwaltete\n" +
      "Highscore-Listen, die zwar zu Mine gehören, aber beim Weiterkopieren\n" +
      "weggelassen werden dürfen. Sie sollten allerdings dabei sein.\n" +
      "\n" +
      "Mit Ausnahme der Highscore-Listen (dort ist es erwünscht) darf Mine nicht\n" +
      "verändert werden.\n" +
      "\n" +
      "Da ich für Mine kein Geld verlange, sehe ich auch nicht ein, weshalb ich\n" +
      "irgendeine Verantwortung für Schäden, die durch Mine direkt oder indirekt\n" +
      "verursacht werden, aufkommen soll. SIE BENUTZEN MINE AUF EIGENE GEFAHR!";

   CONST sharewareTitel = "Mine:";

   CONST spielregeln =
      "Das Spiel Mine funktioniert nach folgenden Regeln:\n" +
      "\n" +
      "Sie haben ein quadratisches Spielfeld mit n * n Feldern. Zufällig über\n" +
      "diese Felder verteilt liegen INTEGER (n * n / 6) Minen versteckt. Diese\n" +
      "gilt es möglichst schnell zu finden. Dazu können Sie die Felder mit der\n" +
      "linken Maustaste anklicken. Ist auf dem Feld eine Mine, explodiert sie\n" +
      "und das Spiel ist zu Ende. Ist keine Mine auf dem Feld, dann enthüllt es\n" +
      "die Anzahl an Minen, die diesem Feld direkt benachbart sind (waagrecht,\n" +
      "senkrecht und diagonal). Glauben Sie eine Mine lokalisiert zu haben, dann\n" +
      "können Sie das entsprechende Feld mit der rechten Maustaste markieren.\n" +
      "\n" +
      "Das Spiel ist zu Ende, wenn eine Mine explodiert oder wenn alle Felder\n" +
      "mit Minen markiert und alle anderen aufgedeckt sind.\n" +
      "\n" +
      "Am Ende entscheiden die Anzahl an markierten Minen und die benötigte\n" +
      "Zeit, ob Sie in die Ruhmeshalle kommen oder nicht.";

   CONST spielregelnGad = "Spielregeln";

   CONST spielregelnTitel = "Mine: Die Spielregeln";

   (*$ ELSE *)

   CONST quitGad = "Quit";

   CONST shareware =
      "                          Mine © 1992 by\n" +
      "\n" +
      "     Thomas Ansorge, Dinkelackerring 55, W-6730 Neustadt/Weinstraße,\n" +
      "                          Germany, Europe\n" +
      "\n" +
      "\n" +
      "Mine is freely distributable copyrighted software.  You have permission to\n" +
      "distribute Mine to anyone you want on a non-commercial base and as long as\n" +
      "Mine is complete.  All other rights remain at the author alone.\n" +
      "\n" +
      "You are not allowed to change any part of Mine except for the highscore\n" +
      "lists (where you are supposed to...).\n" +
      "\n" +
      "I do not ask for money if you like Mine and can not be made liable for any\n" +
      "damage Mine does to anyone or anything directly or indirectly.  YOU USE\n" +
      "MINE ON YOUR OWN RISK!";

   CONST sharewareTitel = "Mine:";

   CONST spielregeln =
      "To play mine, you have to follow these simple rules:\n" +
      "\n" +
      "Your field is a n * n square with INTEGER (n * n / 6) hidden mines and\n" +
      "all you have to do is to mark them with a nice flag so that nobody\n" +
      "stumbles over them. The problem: As I said, they are hidden, so you\n" +
      "don't see them. But there is help available: If you click with your\n" +
      "left mouse button on a field that does not contain a mine, it will tell\n" +
      "you the number of mines hidden in its direct neighbourhood. If you hit\n" +
      "a mine with the left mouse button, the game is over. With your right\n" +
      "mouse button you can mark any field with a flag or collect a flag from\n" +
      "a field. You have as many flags as there are mines.\n" +
      "\n" +
      "The game is over if you hit a mine with your left mouse button or if\n" +
      "there is no field without either a number or a flag in it left. Then\n" +
      "the program has a look at the number of mines you marked with a flag\n" +
      "and the time it took and then perhaps you enter the highscorelist.";

   CONST spielregelnGad = "how to play Mine";

   CONST spielregelnTitel = "Mine: The rules of the game:";

   (*$ ENDIF *)

   CONST sharewareGads = spielregelnGad + "|" + quitGad;

   VAR flags : IDCMPFlagSet;
       nummer: LONGINT;

   (* ---------------------------------------------------------------------------- *)

   BEGIN (* Prozedur Info *)

   flags := IDCMPFlagSet {};

   REPEAT
      nummer := TextRequest (shareware, NIL, sharewareGads, sharewareTitel, reqLib,
         flags, windowPtr);

      CASE nummer OF
         |1: nummer := TextRequest (spielregeln, NIL, quitGad, spielregelnTitel,
                reqLib, flags, windowPtr);

         |0: (* QUIT: nichts *);
      END (* CASE nummer *);
   UNTIL nummer = 0;
END Info (* Prozedur *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE InitImages;

   (* initialisiert die Images *)

   (* alles ist global *)

   BEGIN (* Prozedur InitImages *)

   WITH bombeImage DO
      leftEdge :=0;
      topEdge :=0;
      width := boxX;
      height := boxY;
      depth := 2;
      imageData := ADR (Images.BombePlane);
      planePick := 3;
      planeOnOff := 0;
      nextImage := NIL;
   END (* WITH bombeImage *);

   WITH clickedImage DO
      leftEdge :=0;
      topEdge :=0;
      width := boxX;
      height := boxY;
      depth := 2;
      imageData := ADR (Images.ClickedPlane);
      planePick := 3;
      planeOnOff := 0;
      nextImage := NIL;
   END (* WITH clickedImage *);

   WITH clickmeImage DO
      leftEdge :=0;
      topEdge :=0;
      width := boxX;
      height := boxY;
      depth := 2;
      imageData := ADR (Images.ClickMePlane);
      planePick := 3;
      planeOnOff := 0;
      nextImage := NIL;
   END (* WITH clickmeImage *);

   WITH explosionImage DO
      leftEdge :=0;
      topEdge :=0;
      width := boxX;
      height := boxY;
      depth := 2;
      imageData := ADR (Images.ExplosionPlane);
      planePick := 3;
      planeOnOff := 0;
      nextImage := NIL;
   END (* WITH explosionImage *);

   WITH fahneImage DO
      leftEdge :=0;
      topEdge := 0;
      width := boxX;
      height := boxY;
      depth := 2;
      imageData := ADR (Images.FahnePlane);
      planePick := 3;
      planeOnOff := 0;
      nextImage := NIL;
   END (* WITH fahneImage *);

   WITH fahneORImage DO
      leftEdge :=0;
      topEdge := 0;
      width := 12;
      height := 8;
      depth := 2;
      imageData := ADR (Images.FahneORPlane);
      planePick := 3;
      planeOnOff := 0;
      nextImage := NIL;
   END (* WITH fahneImage *);
END InitImages (* Prozedur *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE LadeRuhmeshalle (VAR ruhmeshalle: Ruhmeshalle;
                           ruhmeshalleName: ARRAY OF CHAR);

   (* lädt bzw initialisiert mit 0 die Variable ruhmeshalle *)

   VAR file: File;
       i   : [1..10];

   (* ---------------------------------------------------------------------------- *)

   BEGIN (* Prozedur LadeRuhmeshalle *)

   Lookup (file, ruhmeshalleName, SIZE (ruhmeshalle), FALSE);

   IF file.res = done THEN
      ReadByteBlock (file, ruhmeshalle);
      Close (file);

   ELSE (* IF file.res *);
      FOR i := 1 TO 10 DO
         WITH ruhmeshalle [i] DO
            anzM := 0;

            WITH zeit DO
               min := 99;
               sec := 59;
            END (* WITH zeit *);

            name := "";
         END (* WITH ruhmeshalle [i] *);
      END (* FOR i *);
   END (* IF file.res *);
END LadeRuhmeshalle (* Prozedur *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE MaxAnzahlFelder (screenPtr: ScreenPtr): LONGINT;

   (* stellt fest, wie groß das auf dem Screen screenPtr gezeichnete Feld maximal  *)
   (* sein kann, damit das ganze Fenster draufpaßt                                 *)

   VAR horiz, vert: LONGINT;

   (* ---------------------------------------------------------------------------- *)

   BEGIN (* Funktion MaxAnzahlFelder *)

   horiz := (screenPtr^.width - 2 * 3 - 2 * 4) DIV (boxX + 1);
   vert := (screenPtr^.height - 3 * randBreite - 8 -
      LONGINT (screenPtr^.font^.ySize + CARDINAL (screenPtr^.wBorTop) + 1)) DIV boxY;

   IF horiz <= vert THEN
      RETURN horiz;

   ELSE
      RETURN vert;
   END (* IF horiz *);
END MaxAnzahlFelder (* Funktion *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE Pos (ruhmeshalle: Ruhmeshalle;
               eintrag    : RuhmeshalleEintrag (* ohne name *)
              ): LONGINT;

   (* stellt den Platz des neuen eintrags in der Ruhmeshalle fest *)

   VAR pos: LONGINT;

   (* ---------------------------------------------------------------------------- *)

   BEGIN (* Prozedur Pos *)

   pos := 1;

   WHILE (eintrag.anzM < ruhmeshalle [pos].anzM) AND (pos < 10) DO
      INC (pos);
   END (* WHILE *);

   IF (pos = 10) AND (eintrag.anzM < ruhmeshalle [pos].anzM) THEN
      RETURN 11;
   END (* IF (pos *);

   IF eintrag.anzM > ruhmeshalle [pos].anzM THEN
      RETURN pos;
   END (* IF eintrag.anzM *);

   WHILE (eintrag.zeit.min > ruhmeshalle [pos].zeit.min) AND (pos < 10) AND
      (eintrag.anzM = ruhmeshalle [pos].anzM) DO
      INC (pos);
   END (* WHILE *);

   IF (pos = 10) AND (eintrag.zeit.min > ruhmeshalle [pos].zeit.min) THEN
      RETURN 11;
   END (* IF (pos *);

   IF eintrag.zeit.min < ruhmeshalle [pos].zeit.min THEN
      RETURN pos;
   END (* IF eintrag.zeit.min *);

   WHILE (eintrag.zeit.sec >= ruhmeshalle [pos].zeit.sec) AND (pos < 10) AND
      (eintrag.anzM = ruhmeshalle [pos].anzM) AND
      (eintrag.zeit.min = ruhmeshalle [pos].zeit.min) DO
      INC (pos);
   END (* WHILE *);

   IF (pos = 10) AND (eintrag.zeit.sec >= ruhmeshalle [pos].zeit.sec) THEN
      RETURN 11;
   END (* IF (pos *);

   RETURN pos;
END Pos (* Funktion *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE SaveRuhmeshalle (ruhmeshalle    : Ruhmeshalle;
                           ruhmeshalleName: ARRAY OF CHAR);

   (* speichert die Ruhmeshalle *)

   VAR file: File;

   (* ---------------------------------------------------------------------------- *)

   BEGIN (* Prozedur SaveRuhmeshalle *)

   Lookup (file, ruhmeshalleName, SIZE (ruhmeshalle), TRUE);

   WriteByteBlock (file, ruhmeshalle);

   Close (file);
END SaveRuhmeshalle (* Prozedur *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE VerteileMinen (VAR spielfeld: Spielfeld;
                         spielfeldMax : LONGINT;
                         anzMinen     : LONGINT);

   (* initialisiert das Spielfeld *)

   VAR i, j, k: LONGINT;

   (* ---------------------------------------------------------------------------- *)

   BEGIN (* Prozedur VerteileMinen *)

   (* Feld löschen <=> mit 0 füllen *)

   FOR i := 1 TO spielfeldMax DO
      FOR j := 1 TO spielfeldMax DO
         spielfeld [i, j] := 0;
      END (* FOR j *);
   END (* FOR i *);

   (* Minen nach Zufallsprinzip verteilen und Felder drumherum aktualisieren *)

   k := 0;

   REPEAT
      i := RND (spielfeldMax) + 1;
      j := RND (spielfeldMax) + 1;

      IF spielfeld [i, j] # mine THEN
         (* eine neue Mine *)
         spielfeld [i, j] := mine;
         INC (k);

         IF i # 1 THEN
            IF j # 1 THEN
               IF spielfeld [i - 1, j - 1] # mine THEN
                  INC (spielfeld [i - 1, j - 1]); (* links oben *)
               END;
            END;

            IF spielfeld [i - 1, j] # mine THEN
               INC (spielfeld [i - 1, j]); (* oben *)
            END;

            IF j # spielfeldMax THEN
               IF spielfeld [i - 1, j + 1] # mine THEN
                  INC (spielfeld [i - 1, j + 1]); (* rechts oben *)
               END;
            END;
         END (* IF i # 1 *);

         IF j # 1 THEN
            IF spielfeld [i, j - 1] # mine THEN
               INC (spielfeld [i, j - 1]); (* links *)
            END;
         END;

         IF j # spielfeldMax THEN
            IF spielfeld [i, j + 1] # mine THEN
               INC (spielfeld [i, j + 1]); (* rechts *)
            END;
         END;

         IF i # spielfeldMax THEN
            IF j # 1 THEN
               IF spielfeld [i + 1, j - 1] # mine THEN
                  INC (spielfeld [i + 1, j - 1]); (* links unten *)
               END;
            END;

            IF spielfeld [i + 1, j] # mine THEN
               INC (spielfeld [i + 1, j]); (* unten *)
            END;

            IF j # spielfeldMax THEN
               IF spielfeld [i + 1, j + 1] # mine THEN
                  INC (spielfeld [i + 1, j + 1]); (* rechts unten *)
               END;
            END;
         END (* IF i # spielfeldMax *);
      END (* IF spielfeld [i, j] # mine *);
   UNTIL k = anzMinen;
END VerteileMinen (* Prozedur *);

(* ------------------------------------------------------------------------------- *)

PROCEDURE Ziffer (ziffer: SHORTINT): CHAR;

   (* Anzahl der Minen um ein Feld *)

   BEGIN (* Funktion Ziffer *)

   CASE ziffer OF
      |0: RETURN "0";
      |1: RETURN "1";
      |2: RETURN "2";
      |3: RETURN "3";
      |4: RETURN "4";
      |5: RETURN "5";
      |6: RETURN "6";
      |7: RETURN "7";
      |8: RETURN "8";
   END (* CASE ziffer *);
END Ziffer (* Funktion *);

(* ------------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------------- *)

BEGIN (* MODULE Mine *)

(* okay, was genau brauchen wir: *)

Assert (diskfontVersion  >= 33, ADR ("DiskFont Library Version 33 Minimum!"));
Assert (dosVersion       >= 33, ADR ("Dos Library Version 33 Minimum!"));
Assert (execVersion      >= 33, ADR ("Exec Library Version 33 Minimum!"));
Assert (gadtoolsVersion  >= 36, ADR ("GadTools Library Version 36 Minimum!"));
Assert (graphicsVersion  >= 33, ADR ("Graphics Library Version 33 Minimum!"));
Assert (intuitionVersion >= 36, ADR ("Intuition Library Version 36 Minimum!"));

IF reqtoolsBase # NIL THEN
   reqLib := reqtools;

ELSE (* IF reqtoolsBase *)
   IF reqBase # NIL THEN
      reqLib := req;

   ELSE (* IF reqBase *)
      reqLib := system;
   END (* IF reqBase *);
END (* IF reqtoolsBase *);

WITH intuiText DO
   frontPen := schwarz;
   backPen := grau;
   drawMode := jam2;
   iTextFont := ADR (fontAttr);
   nextText := NIL;
END (* WITH intuiText *);

screenPtr := NIL;
windowPtr := NIL;
windowPtr2 := NIL;
visualInfo := NIL;

DateStamp (ADR (date));
PutSeed (Eor (date.days, Eor (date.minute, date.tick)));

(* mein Font *)

WITH fontAttr DO
   name := ADR (fontName);
   ySize := fontHoehe;
   style := FontStyleSet {};
   flags := FontFlagSet {};
END (* WITH fontAttr *);

fontPtr := OpenDiskFont (ADR (fontAttr));

spielfeldMax := 10;

screenPtr := LockPubScreen (NIL);

LOOP
   ende := FALSE;
   anzNummer := 0;

   WITH zeit DO
      ticks := 0;
      minuten := 0;
      sekunden := 0;

      WITH zeitIText DO
         frontPen := schwarz;
         backPen := grau;
         drawMode := jam2;
         leftEdge := 0;
         topEdge := -1;
         iTextFont := ADR (fontAttr);
         iText := ADR (zeitStr);
         nextText := NIL;
      END (* WITH zeitIText *);
   END (* WITH Zeit *);

   (* das Anfangs-Fenster *)

   spielFeldMax := MaxAnzahlFelder (screenPtr);

   IF spielFeldMax > 77 THEN
      spielFeldMax := 77;
   END (* IF spielFeldMax *);

   innerWidth := 180;

   windowPtr := OpenWindowTagList (NIL, TAG (tagList,
                   waInnerHeight, 90,
                   waInnerWidth , innerWidth,
                   waLeft       , (LONGINT (screenPtr^.width) - 204) DIV 2,
                   waTop        , (LONGINT (screenPtr^.height) - 70) DIV 2,
                   waTitle      , ADR (mineStr),
                   waFlags      , CAST (LONGINT, WindowFlagSet {windowDrag,
                                                                windowClose,
                                                                gimmeZeroZero,
                                                                reportMouse,
                                                                activate,
                                                                windowDepth}),
                   waIDCMP      , CAST (LONGINT, IDCMPFlagSet {closeWindow,
                                                               refreshWindow} +
                                                 buttonIDCMP +
                                                 sliderIDCMP),
                   waPubScreen  , screenPtr,
                   tagEnd       , 0));

   Assert (windowPtr # NIL, ADR (openWindowError));
   Assert (windowPtr^.userPort # NIL, ADR (userPortError));

   IF visualInfo = NIL THEN
      visualInfo := GetVisualInfoA (windowPtr^.wScreen, NIL);
   END (* IF visualInfo *);

   gList := NIL;

   contextPtr := CreateContext (gList); (* bereitet gList für GadTools vor *)

   (* contextPtr und gList zeigen jetzt auf den selben Speicherbereich *)

   string80 := "5                ";

   ValToStr (spielFeldMax, FALSE, string2, 10, 2, "0", err);
   Concat (string80, string2);

   IF gList # NIL THEN
      (* der Slider *)

      WITH newGadget DO
         width := 154;
         height := 12;
         leftEdge := (innerWidth - width) DIV 2;
         topEdge := 18;
         gadgetText := ADR (string80);
         textAttr := ADR (fontAttr);
         gadgetID := sliderGadID;
         flags := NewGadgetFlagSet {placetextBelow};
         userData := NIL;
      END (* WITH newGadget *);

      sliderZahlPos := (innerWidth - 160) DIV 2 + 104;

      newGadget.visualInfo := visualInfo;

      gadgetPtr := CreateGadgetA (sliderKind, gList^, newGadget, TAG (tagList,
                      gtslMin        , 5,
                      gtslMax        , spielFeldMax,
                      gtslLevel      , spielfeldMax,
                      gtslMaxLevelLen, 21,
                      gaRelVerify    , TRUE,
                      gaImmediate    , TRUE,
                      tagEnd         , 0));

      (* weiter *)

      WITH newGadget DO
         width := 80;
         height := 12;
         leftEdge := (innerWidth - width) DIV 2;
         topEdge := 45;
         gadgetText := ADR (weiter);
         textAttr := ADR (fontAttr);
         gadgetID := weiterGadID;
         flags := NewGadgetFlagSet {placetextIn};
         userData := NIL;
      END (* WITH newGadget *);

      newGadget.visualInfo := visualInfo;

      gadgetPtr := CreateGadgetA (buttonKind, gadgetPtr^, newGadget, NIL);

      (* Infos und Hilfe *)

      WITH newGadget DO
         width := 128;
         height := 12;
         leftEdge := (innerWidth - width) DIV 2;
         topEdge := 60;
         gadgetText := ADR (info);
         textAttr := ADR (fontAttr);
         gadgetID := infoGadID;
         flags := NewGadgetFlagSet {placetextIn};
         userData := NIL;
      END (* WITH newGadget *);

      newGadget.visualInfo := visualInfo;

      gadgetPtr := CreateGadgetA (buttonKind, gadgetPtr^, newGadget, NIL);

      (* Ruhmeshalle ansehen *)

      WITH newGadget DO
         width := 160;
         height := 12;
         leftEdge := (innerWidth - width) DIV 2;
         topEdge := 75;
         gadgetText := ADR (ruhmeshalleGadgetName);
         textAttr :=ADR (fontAttr);
         gadgetID := ruhmGadID;
         flags := NewGadgetFlagSet {placetextIn};
         userData := NIL;
      END (* WITH newGadget *);

      newGadget.visualInfo := visualInfo;

      gadgetPtr := CreateGadgetA (buttonKind, gadgetPtr^, newGadget, NIL);

      i := AddGList (windowPtr, gList, 0, -1, NIL);
      RefreshGadgets (gList, windowPtr, NIL);
      GTRefreshWindow (windowPtr, NIL); (* für GadTools *)
   END (* IF gList *);

   i := 0;

   (* "Seitenlänge" über Prop-Gadget schreiben *)
   intuiText.leftEdge := 17;
   intuiText.topEdge := 0;
   intuiText.iText := ADR (string80);
   string80 := seitenlaenge;
   PrintIText (windowPtr^.rPort, ADR (intuiText), sliderZahlPos - 104, 7);

   intuiText.iText := ADR (string2);
   ValToStr (spielfeldMax, FALSE, string2, 10, 2, " ", err);
   PrintIText (windowPtr^.rPort, ADR (intuiText), sliderZahlPos, 7);

   REPEAT
      nachricht := GTGetIMsg (windowPtr^.userPort);

      IF nachricht = NIL THEN
         WaitPort (windowPtr^.userPort);
         nachricht := GTGetIMsg (windowPtr^.userPort);
      END (* IF nachricht *);

      IF nachricht # NIL THEN
         flags := nachricht^.class;
         gadgetPtr := nachricht^.iAddress;
         code := nachricht^.code; (* nur bei GadTools UND GTGetIMsg *)
         GTReplyIMsg (nachricht);

         IF closeWindow IN flags THEN
            EXIT (* LOOP *);

         ELSIF refreshWindow IN flags THEN
            GTBeginRefresh (windowPtr);
            GTEndRefresh (windowPtr, TRUE);

         ELSIF gadgetUp IN flags THEN
            IF gadgetPtr # NIL THEN
               i := gadgetPtr^.gadgetID;

               IF i = infoGadID THEN
                  Info (windowPtr);
               END (* IF i *);

               IF i = ruhmGadID THEN
                  ValToStr (spielfeldMax, FALSE, string2, 10, 2, " ", err);

                  Copy (string30, ruhmeshalleName);
                  Concat (string30, string2);

                  LadeRuhmeshalle (ruhmeshalle, string30);

                  innerWidth := 276;
                  innerHeight := 124;

                  Copy (string30, ruhmeshalleFensterName);
                  Concat (string30, string2);

                  windowPtr2 := OpenWindowTagList (NIL, TAG (tagList,
                     waTitle      , ADR (string30),
                     waInnerWidth , innerWidth,
                     waInnerHeight, innerHeight,
                     waLeft       , (screenPtr^.width - innerWidth) DIV 2,
                     waTop        , (screenPtr^.height - innerHeight) DIV 2,
                     waFlags      , CAST (LONGINT, WindowFlagSet {windowClose,
                                                                  activate,
                                                                  windowDrag,
                                                                  windowDepth,
                                                                  gimmeZeroZero}),
                     waIDCMP      , CAST (LONGINT, IDCMPFlagSet {closeWindow,
                                                                 refreshWindow,
                                                                 mouseButtons}),
                     tagEnd       , 0));

                  Assert (windowPtr2 # NIL, ADR (openWindowError));
                  Assert (windowPtr2^.userPort # NIL, ADR (userPortError));

                  (* windowPtr^ und WindowPtr2^ sind auf dem selben Screen, also *)
                  (* darf ich auch das selbe visualInfo verwenden *)

                  DrawBevelBoxA (windowPtr2^.rPort, 20, 7, 234, 108, TAG (tagList,
                     gtbbRecessed, 0,
                     gtVisualInfo, visualInfo,
                     tagEnd      , 0));

                  WITH intuiText DO
                     leftEdge := 0;
                     topEdge := 12;
                  END (* WITH intuiText *);

                  FOR j := 1 TO 10 DO
                     IF ruhmeshalle [j].anzM = AnzMinen (spielfeldMax) THEN
                        intuiText.frontPen := blau;
                     END (* IF ruhmeshalle *);

                     intuiText.iText := ADR (ruhmeshalle [j].name);
                     PrintIText (windowPtr2^.rPort, ADR (intuiText), 34,
                        10 * (j - 1));

                     intuiText.frontPen := schwarz;
                  END (* FOR j *);

                  flags := IDCMPFlagSet {};

                  REPEAT
                     nachricht := GetMsg (windowPtr2^.userPort);

                     IF nachricht = NIL THEN
                        WaitPort (windowPtr2^.userPort);
                        nachricht := GetMsg (windowPtr2^.userPort);
                     END (* IF nachricht *);

                     IF nachricht # NIL THEN
                        flags := nachricht^.class;
                        ReplyMsg (nachricht);
                     END (* IF nachricht *);
                     
                     IF refreshWindow IN flags THEN
                        BeginRefresh (windowPtr2);
                        EndRefresh (windowPtr2, TRUE);
                     END (* IF refreshWindow *);

                     IF closeWindow IN flags THEN
                        EXIT (* LOOP *);
                     END (* IF closeWindows *);
                  UNTIL mouseButtons IN flags;

                  CloseWindow (windowPtr2);
                  windowPtr2 := NIL;
               END (* IF i *);
            END (* IF gadgetPtr # NIL *);

         ELSIF gadgetDown IN flags THEN
            IF gadgetPtr # NIL THEN
               IF gadgetPtr^.gadgetID = sliderGadID THEN
                  REPEAT
                     nachricht := GTGetIMsg (windowPtr^.userPort);

                     IF nachricht = NIL THEN
                        WaitPort (windowPtr^.userPort);
                        nachricht := GTGetIMsg (windowPtr^.userPort);
                     END (* IF nachricht *);

                     IF nachricht # NIL THEN
                        flags := nachricht^.class;
                        code := nachricht^.code;
                        GTReplyIMsg (nachricht);

                        IF (mouseMove IN flags) OR (gadgetUp IN flags) THEN
                           (* jemand bewegt den Slider *)
                           spielfeldMax := LONGINT (code);

                           intuiText.leftEdge := 17;
                           intuiText.topEdge := 0;
                           intuiText.iText := ADR (string2);

                           ValToStr (spielfeldMax, FALSE, string2, 10, 2, " ", err);
                           PrintIText (windowPtr^.rPort, ADR (intuiText),
                              sliderZahlPos, 7);
                        END (* IF mouseMove *);
                     END (* IF nachricht *);
                  UNTIL gadgetUp IN flags; (* losgelassen *)
               END (* IF gadgetPtr^. *);
            END (* IF gadgetPtr *);
         END (* IF closeWindow *);
      END (* IF nachricht *);
   UNTIL i = weiterGadID;

   FreeVisualInfo (visualInfo);
   visualInfo := NIL;

   i := RemoveGList (windowPtr, gList, 5);

   FreeGadgets (gList);
   gList := NIL;

   CloseWindow (windowPtr);
   windowPtr := NIL;

   anzMinen := AnzMinen (spielfeldMax);

   (* spielfeldMax <= 44 (PAL SuperHires-Interlace mit vollem Overscan) *)
   (* ==>  anzMinen <= 322 *)

   (* spielfeldMax <= 77 <==> anzMinen dreistellig *)

   anzFahnen := 0;

   (* Das eigentliche Spiel-Fenster *)

   string2 := "";

   VerteileMinen (spielfeld, spielfeldMax, anzMinen);

   innerWidth := LONGINT ((spielfeldMax * boxX) + (4 * randBreite) + spielfeldMax -
      1);
   innerHeight := LONGINT ((spielfeldMax * boxY) + (2 * randBreite) - 2 + 10);

   windowPtr := OpenWindowTagList (NIL, TAG (tagList,
      waTitle      , ADR (mineStr),
      waInnerWidth , innerWidth,
      waInnerHeight, innerHeight,
      waFlags      , CAST (LONGINT, WindowFlagSet {windowDrag, windowDepth,
                        windowClose, activate, gimmeZeroZero, rmbTrap}),
      waIDCMP      , CAST (LONGINT, IDCMPFlagSet {mouseButtons, closeWindow,
                        refreshWindow, intuiTicks}),
      waLeft       , (screenPtr^.width - innerWidth) DIV 2,
      waTop        , (screenPtr^.height - innerHeight) DIV 2,
      waPubScreen  , screenPtr,
      tagEnd       , 0));

   Assert (windowPtr # NIL, ADR (openWindowError));
   Assert (windowPtr^.userPort # NIL, ADR (userPortError));

   InitImages ();

   IF spielfeldMax < 8 THEN
      zeit.zeitIText.leftEdge := (windowPtr^.gzzWidth - 88) DIV 2;

   ELSE (* IF spielfeldMax *)
      zeit.zeitIText.leftEdge := (((windowPtr^.gzzWidth DIV 2) - 88 + randBreite)
         DIV 2) + randBreite;

      intuiText.topEdge := -1;
      (* leftEdge: rechts der Mitte, alles 44 Pixel breit, davon FahneORImage 12 *)
      intuiText.leftEdge := (windowPtr^.gzzWidth DIV 2) +
         (((windowPtr^.gzzWidth DIV 2) - 44 - randBreite) DIV 2) + 12;
      intuiText.iText := ADR (string30);
   END (* IF spielfeldMax *);

   (* Felder zeichnen *)

   FOR i := 1 TO spielfeldMax DO (* Zeilen *)
      FOR j := 1 TO spielfeldMax DO (* Spalten *)
         DrawImage (windowPtr^.rPort, ADR (clickmeImage),
            2 * randBreite + (j - 1) * clickmeImage.width + horoffset * (j - 1),
            randBreite + (i - 1) * clickmeImage.height);
      END (* FOR j *);
   END (* FOR i *);

   Copy (zeit.zeitStr, zeitS);
   Concat (zeit.zeitStr, "00:00");

   PrintIText (windowPtr^.rPort, ADR (zeit.zeitIText), 0,
      2 * randBreite + spielfeldMax * boxY);

   IF spielfeldMax >= 8 THEN
      DrawImage (windowPtr^.rPort, ADR (fahneORImage),
         ((windowPtr^.gzzWidth DIV 2) + ((windowPtr^.gzzWidth DIV 2) - 44) DIV 2) - 1,
         2 * randBreite + spielfeldMax * boxY - 1);

      ValToStr (anzMinen - anzFahnen, FALSE, string30, 10, 3, " ", err);
      Insert (string30, 0, ":\o");
      PrintIText (windowPtr^.rPort, ADR (intuiText), 0,
         2 * randBreite + spielfeldMax * boxY);
   END (* IF spielfeldMax *);

   (* Aktion des Spielers abwarten und reagieren *)

   flags := IDCMPFlagSet {};

   REPEAT
      nachricht := GetMsg (windowPtr^.userPort);

      IF nachricht = NIL THEN
         WaitPort (windowPtr^.userPort);
         nachricht := GetMsg (windowPtr^.userPort);
      END (* IF nachricht *);

      IF nachricht # NIL THEN
         flags := nachricht^.class;
         qualifiers := nachricht^.qualifier;

         mausX := windowPtr^.gzzMouseX;
         mausY := windowPtr^.gzzMouseY;

         ReplyMsg (nachricht);

         (* so, jetzt geht's ans Auswerten *)

         IF intuiTicks IN flags THEN
            IF zeit.ticks < 9 THEN
               INC (zeit.ticks);

            ELSE (* IF zeit.ticks = 9 *)
               zeit.ticks := 0;

               IF zeit.sekunden < 59 THEN
                  INC (zeit.sekunden);

               ELSE (* IF zeit.sekunden *)
                  zeit.sekunden := 0;

                  IF zeit.minuten < 99 THEN
                     INC (zeit.minuten);

                  ELSE (* IF zeit.minuten *)
                     ende := TRUE;
                  END (* IF zeit.minuten *);
               END (* IF zeit.sekunden *);

               IF NOT ende THEN
                  WITH zeit DO
                     Copy (zeitStr, zeitS);

                     ValToStr (minuten, FALSE, hilfeStr, 10, 2, "0", err);
                     Concat (zeitStr, hilfeStr);

                     ConcatChar (zeitStr, ":");

                     ValToStr (sekunden, FALSE, hilfeStr, 10, 2, "0", err);
                     Concat (zeitStr, hilfeStr);

                     PrintIText (windowPtr^.rPort, ADR (zeitIText), 0,
                        2 * randBreite + spielfeldMax * boxY);
                  END (* WITH Zeit *);
               END (* IF NOT ende *);
            END (* IF zeit.ticks *);
         END (* IF intuiTicks *);
         
         IF refreshWindow IN flags THEN
            BeginRefresh (windowPtr);
            EndRefresh (windowPtr, TRUE);
         END (* IF refreshWindow *);

         IF mouseButtons IN flags THEN
            i := ((mausY - randBreite) DIV boxY) + 1; (* Zeile *)
            j := ((mausX - 2 * randBreite) DIV (boxX + horoffset)) + 1;

            IF (i >= 1) AND (i <= spielfeldMax) AND (j >= 1) AND
               (j <= spielfeldMax) THEN
               (* das Feld [i, j] angeklickt *)
               IF rightButton IN qualifiers THEN
                  IF spielfeld [i, j] >= mine THEN
                     IF anzFahnen < anzMinen THEN
                        DrawImage (windowPtr^.rPort, ADR (fahneImage),
                           2 * randBreite + (j - 1) * fahneImage.width +
                              horoffset * (j - 1),
                           randBreite + (i - 1) * fahneImage.height);

                        spielfeld [i, j] := spielfeld [i, j] + fahne;

                        INC (anzFahnen);
                     END (* IF anzFahnen *);

                  ELSE (* IF spielfeld [i, j] >= mine *)
                     IF spielfeld [i, j] <= fahne + 8 THEN
                        DrawImage (windowPtr^.rPort, ADR (clickmeImage),
                           2 * randBreite + (j - 1) * clickmeImage.width +
                              horoffset * (j - 1),
                           randBreite + (i - 1) * clickmeImage.height);

                        spielfeld [i, j] := spielfeld [i, j] - fahne;

                        DEC (anzFahnen);
                     END (* IF spielfeld [i, j] = fahne *);
                  END (* IF spielfeld [i, j] >= mine *);

                  IF spielfeldMax >= 8 THEN
                     ValToStr (anzMinen - anzFahnen, FALSE, string30, 10, 3, " ",
                        err);
                     Insert (string30, 0, ":\o");
                     PrintIText (windowPtr^.rPort, ADR (intuiText), 0,
                        2 * randBreite + spielfeldMax * boxY);
                  END (* IF spielfeldMax *);

               ELSIF leftButton IN qualifiers THEN
                  IF spielfeld [i, j] >= mine THEN
                     IF spielfeld [i, j] # mine THEN
                        ModifyIDCMP (windowPtr, IDCMPFlagSet {});

                        DrawClickedImage (windowPtr, spielfeld, spielfeldMax, i, j,
                           anzNummer);

                        ModifyIDCMP (windowPtr,
                           IDCMPFlagSet {mouseButtons, closeWindow, refreshWindow,
                              intuiTicks});

                     ELSE (* IF # mine *)
                        DrawImage (windowPtr^.rPort, ADR (explosionImage),
                           2 * randBreite + (j - 1) * explosionImage.width +
                           horoffset * (j - 1),
                           randBreite + (i - 1) * explosionImage.height);

                        Beep (booob, infoPri);

                        ende := TRUE;
                     END (* IF # mine *);
                  END (*IF >= mine *);
               END (* IF rightButton *);
            END (* IF (i >= 1 *);
         END (* IF mouseButtons *);
      END (* IF nachricht # NIL *);
   UNTIL ende OR (closeWindow IN flags) OR
      ((spielfeldMax * spielfeldMax - anzFahnen - anzNummer) = 0);

   IF closeWindow IN flags THEN
      CloseWindow (windowPtr);
      windowPtr := NIL;

      EXIT (* LOOP *);
   END (* IF closeWindow *);

   ModifyIDCMP (windowPtr, IDCMPFlagSet {}); (* keine Nachrichten mehr! *)

   (* Anzahl der gefundenen Minen zählen *)

   anzMinen := 0;

   FOR i := 1 TO spielfeldMax DO
      FOR j := 1 TO spielfeldMax DO
         IF spielfeld [i, j] = fahne + mine THEN
            INC (anzMinen);

            (* Bombe zeichnen *)

            DrawImage (windowPtr^.rPort, ADR (bombeImage),
               2 * randBreite + (j - 1) * clickedImage.width + horoffset * (j - 1),
               randBreite + (i - 1) * clickedImage.height);
         END (* IF spielfeld *);
      END (* FOR j *);
   END (* FOR i *);

   IF ((spielfeldMax * spielfeldMax) - anzNummer - anzMinen) = 0 THEN
      (* alles abgeräumt! *)
      Beep (beeeb, infoPri);
   END (* IF ((spielfeldMax *);

   ValToStr (spielfeldMax, FALSE, string2, 10, 2, " ", err);

   Copy (string30, ruhmeshalleName);
   Concat (string30, string2);

   LadeRuhmeshalle (ruhmeshalle, string30);

   eintrag.anzM := anzMinen;
   eintrag.zeit.min := zeit.minuten;
   eintrag.zeit.sec := zeit.sekunden;

   pos := Pos (ruhmeshalle, eintrag);

   IF (pos <= 10) AND (reqLib # system) THEN (* drin -- String-Requester! *)
      j := StringRequest (eintrag.name, 17, 16, stringReqTitle, windowPtr,
              reqLib);

      IF j = noError THEN
         FuegeEintragEin (ruhmeshalle, eintrag, pos);
         SaveRuhmeshalle (ruhmeshalle, string30);
      END (* IF noError *);

   ELSE (* IF pos *)
      (* nicht in der Ruhmeshalle -- auf Mausklick warten *)
      ModifyIDCMP (windowPtr, IDCMPFlagSet {mouseButtons, closeWindow});

      REPEAT
         nachricht := GetMsg (windowPtr^.userPort);

         IF nachricht = NIL THEN
            WaitPort (windowPtr^.userPort);
            nachricht := GetMsg (windowPtr^.userPort);
         END (* IF nachricht *);

         flags := nachricht^.class;
         ReplyMsg (nachricht);

         IF closeWindow IN flags THEN
            EXIT (* LOOP *);
         END (* IF closeWindow *);
      UNTIL mouseButtons IN flags;
   END (* IF pos *);

   CloseWindow (windowPtr);
   windowPtr := NIL;

   (* Ruhmeshalle anzeigen *);

   Copy (string80, ruhmeshalleFensterName);
   Concat (string80, string2);

   innerWidth := 276;
   innerHeight := 124;

   windowPtr := OpenWindowTagList (NIL, TAG (tagList,
      waTitle      , ADR (string80),
      waInnerWidth , innerWidth,
      waInnerHeight, innerHeight,
      waLeft       , (screenPtr^.width - innerWidth) DIV 2,
      waTop        , (screenPtr^.height - innerHeight) DIV 2,
      waFlags      , CAST (LONGINT, WindowFlagSet {windowClose,
                                                   windowDrag,
                                                   activate,
                                                   windowDepth,
                                                   gimmeZeroZero}),
      waIDCMP      , CAST (LONGINT, IDCMPFlagSet {closeWindow, refreshWindow,
                                                  mouseButtons}),
      tagEnd       , 0));

   Assert (windowPtr # NIL, ADR (openWindowError));
   Assert (windowPtr^.userPort # NIL, ADR (userPortError));

   IF visualInfo = NIL THEN
      visualInfo := GetVisualInfoA (windowPtr^.wScreen, NIL);
   END (* IF visualInfo *);

   DrawBevelBoxA (windowPtr^.rPort, 20, 7, 234, 108, TAG (tagList,
      gtbbRecessed, 0,
      gtVisualInfo, visualInfo,
      tagEnd      , 0));

   FreeVisualInfo (visualInfo);
   visualInfo := NIL;

   WITH intuiText DO
      leftEdge := 0;
      topEdge := 12;
   END (* WITH intuiText *);

   FOR i := 1 TO 10 DO
      intuiText.iText := ADR (ruhmeshalle [i].name);

      IF ruhmeshalle [i].anzM = AnzMinen (spielfeldMax) THEN
         intuiText.frontPen := blau;
      END (* IF ruhmeshalle *);

      IF (i = pos) AND (j = noError) THEN
         intuiText.frontPen := weiss;
      END (* IF i *);

      PrintIText (windowPtr^.rPort, ADR (intuiText), 34, 10 * (i - 1));

      intuiText.frontPen := schwarz;
   END (* FOR i *);

   flags := IDCMPFlagSet {};

   REPEAT
      nachricht := GetMsg (windowPtr^.userPort);

      IF nachricht = NIL THEN
         WaitPort (windowPtr^.userPort);
         nachricht := GetMsg (windowPtr^.userPort);
      END (* IF nachricht *);

      IF nachricht # NIL THEN
         flags := nachricht^.class;
         ReplyMsg (nachricht);
      END (* IF nachricht *);
                     
      IF refreshWindow IN flags THEN
         BeginRefresh (windowPtr);
         EndRefresh (windowPtr, TRUE);
      END (* IF refreshWindow *);

      IF closeWindow IN flags THEN
         EXIT (* LOOP *);
      END (* IF closeWindow *);
   UNTIL mouseButtons IN flags;

   CloseWindow (windowPtr);
   windowPtr := NIL;
END (* LOOP *);

(* ------------------------------------------------------------------------------- *)

CLOSE;

IF screenPtr # NIL THEN
   UnlockPubScreen (NIL, screenPtr);
   screenPtr := NIL;
END (* IF screenPtr *);

IF visualInfo # NIL THEN
   FreeVisualInfo (visualInfo);
   visualInfo := NIL;
END (* IF visualInfo *);

IF windowPtr # NIL THEN
   IF gList # NIL THEN
      i := RemoveGList (windowPtr, gList, 5);
      FreeGadgets (gList);
      gList := NIL;
   END (* IF gList *);

   CloseWindow (windowPtr);
   windowPtr := NIL;
END (* IF windowPtr *);

IF windowPtr2 # NIL THEN
   CloseWindow (windowPtr2);
   windowPtr2 := NIL;
END (* IF windowPtr2 *);

IF fontPtr # NIL THEN
   CloseFont (fontPtr);
   fontPtr := NIL;
END (* IF fontPtr *);

END Mine (* MODUL *).