Program UFO;

{$I "Include:Utils/Stringlib.i"}
{$I "Include:Utils/random.i"}
{$I "Include:Intuition/Intuition.i"}
{$I "include:Intuition/screens.i"  }
{$I "include:graphics/Pens.i"      }
{$I "include:graphics/Text.i"      }
{$I "include:graphics/Graphics.i"  }
{$I "include:Exec/libraries.i"  }
{$I "include:game.i"}                 { * Die Spiel-Routinen * }
{$I "prg/ufo/bilder"}                 { * Die Imagedaten     * }


CONST

        { Wir definieren einen Screen mit 3-Bitlanes und keiner Titel-
          leiste, Hires und 640 x 200 Punkte Auflösung }

        NewScr : NewScreen  =  ( 0, 0, 640, 200, 3, 1, 0, HIRES + SPRITES,
                                 CUSTOMSCREEN_f + SCREENQUIET_f,
                                 NIL, NIL, NIL, NIL);

        { Und jetzt ein Rahmenloses Fenster }

        NewWin : NewWindow  =  (0,0,640,200,0,0,0,
                                BORDERLESS + ACTIVATE,
                                NIL,NIL,"",
                                NIL,NIL,0,0,0,0,
                                CUSTOMSCREEN_F);

type
    { * Die Definition für die Highscore-Liste. * }
    spielerdef  = record
                  name   : array [1..21] of char;
                  punkte : integer;
                  lev    : integer; { * Enthält den erreichten Level. * }
    end;

    { * Die Definition der Level. * }
    Leveldef    = record
                  Objekte       : byte; { * Anzahl der Objekte. * }
                  Fallen        : byte; { * Wieviele Fallen gibt es
                                            zusätzlich ? * }
                  Fallenspeed   : byte; { * Max. Speed der Fallen. * }
                  Anmerkung     : String; { * Falls wir noch was sagen
                                              wollen. * }
    end;

Const

    { * Und jetzt unsere 10 Level. * }
    MaxLevel : byte =  10;

    Levels  : array[1..MaxLevel] of Leveldef =
(
    (   50, 4, 0, "Easy is the first way!"),
    (   50, 4, 1, "Look out at the Death!"),
    (   40, 8, 2, "A few more Death !    "),
    (   30, 8, 3, "Yeah, not enough Objekts!"),
    (   30, 10, 4, "They will run on you! "),   { * 5. Level. * }
    (   20, 10, 4, ""),
    (   20, 15, 6, ""),
    (   20, 20, 6, "The Death is around you!"),
    (   15, 25, 8, ""),
    (   10, 30, 8, "If you stand, you win!")
);


VAR
    Spieler : array [1..10] of spielerdef; { * Maximal 10 Einträge in der
                                               Highscore-Liste. * }

    mainflag,loop : boolean; { Hauptsteuerung, Spielsteuerung }

        Irspunkte,
        Irstank         : short;    { * Punkte- und Tankzähler * }
        MaxObjekts      : byte;     { * Wieviele Objekte sollen auf
                                        den Bildschirm? * }
        Key             : byte;     { * Enthält den aktuellen Tastencode. * }
        Level           : integer;  { * Enthält den aktuellen Level. * }

{ ********************************************************************* }
        MyVPort         : Address;
        Scr             : ScreenPtr;
        Win             : WindowPtr;

{ ********************************************************************* }
{ Irgendwie wollen wir PCQ ja nun mal verlassen, oder? }

PROCEDURE cleanexit(why : String ; rtcode : Integer);
VAR
    tt  : byte;
    tt2 : short;
BEGIN
        for tt := 1 to 100 do begin
            if Picture[tt] <> NIL then begin
                tt2 := (Picture[tt]^.Width + 15)/16; { wieviele Words? }
                tt2 := tt2 * Picture[tt]^.Height; { mal Höhe }
                tt2 := tt2 * Picture[tt]^.Depth; { mal BitPlanes }
                FreeRaster(Picture[tt]^.Imagedata,16,tt2);
            end;
        end;
        if BlittiSpeicher <> Nil then FreeRaster(BlittiSpeicher,80,200);
        if Win      <> NIL then CloseWindow(Win);
        IF Scr      <> NIL THEN CloseScreen(Scr);
        IF GfxBase  <> NIL THEN CloseLibrary(GfxBase);

                { ## Ausgabe ins CLI, warum das Program verlassen }
                { ## werden mußte, inkl.Returncode f. Batchfiles  }
        IF why<>NIL THEN writeln(why);
        exit(rtcode);
END;

{ ********************************************************************* }
{ Ist für die Eintragung des Highscore Namens. }

Procedure GetString(xpos, ypos : short; an : byte; str : string);
{* Liest in string str an Zeichen von der Position xpos, ypos. * }

var
    g   : GadgetPtr;
    gi  : StringInfoPtr;
    tt  : boolean;
    tt1 : short;
    Msg : IntuiMessagePtr;
    MsgClass    : integer;
    MsgCode     : short;

BEGIN
    str[0] := '\0';     { * Muß gesetzt werden, sonst klappts nicht ! * }

    { * Erst mal alles für das Gadget allokieren. * }

    New(gi);                            { * StringInfo allokieren. * }
    gi^.Buffer     :=str;               { * Initialisieren. * }
    gi^.UndoBuffer :=NIL;               { * Gibt keinen. * }
    gi^.BufferPos  :=0;
    gi^.MaxChars   :=an + 1;
    gi^.DispPos    :=0;
    gi^.AltKeyMap  :=NIL;

    New(g);                       { jetzt die Stringgadgets }
    g^.NextGadget := NIL;
    g^.LeftEdge   := xpos;
    g^.TopEdge    := ypos;
    g^.Width      := short(an * 8);
    g^.Height     := 10;
    g^.Flags      :=gadgHComp;    { auch ausfüllen }
    g^.Activation :=relVerify;
    g^.GadgetType :=strGadget;
    g^.GadgetRender  :=NIL;
    g^.SelectRender  :=NIL;
    g^.GadgetText    :=NIL;
    g^.MutualExclude :=0;
    g^.SpecialInfo   :=gi;
    g^.GadgetID      :=0;

    { * Noch ein Unterstrich * }

    Move(MyRPort,xpos,ypos+8);
    Draw(MyRPort,xpos + (an * 8), ypos+8);

    { * Jetzt das zugehörige Window aktualisieren. * }

    ModifyIDCMP(Win, GADGETUP_f); { * Gadgetaktivitäten zulassen. * }

    { * Gadget anmachen und zeichen holen. * }

    tt1 := AddGadget(win,g,-1);
    Refreshgadgets(g, win, Nil);
    tt := ActivateGadget(g,win,NIL);

    Msg := IntuiMessagePtr(WaitPort(Win^.UserPort));
    Msg := IntuiMessagePtr(GetMsg(Win^.UserPort));
    IF Msg <> NIL then begin
       MsgClass := Msg^.Class;
       MsgCode  := Msg^.Code;
       ReplyMsg(MessagePtr(Msg));
    end;

    { * Und jetzt alles wieder zurückgeben. * }

    ModifyIDCMP(win,0);
    SetRast(MyRPort,0);
    gi^.Buffer := Nil;
    tt1 := RemoveGadget(win,g);

end; {GetString}

{/***********************************************************************/}
procedure SetColour(tt1,tt2 : byte); { tt1 = Vordergrundfarbe,tt2 = Hinter-
                                     grundfarbe }
begin
    SetDrMd(MyRPort,JAM2);
    SetAPen(MyRPort,tt1);
    SetBPen(MyRPort,tt2);
end; {SetColour}


{/***********************************************************************/}

procedure init();      {/* Initialisiert alle Daten */}

var
    tt1 : short;
begin
    GfxBase := OpenLibrary("graphics.library",0);
    IF GfxBase = NIL THEN cleanexit("Can`t open Gfx.lib.",20);

{ Jetzt kommt der Screen, das Window und die Console dran }
    Scr := OpenScreen(Adr(NewScr));
    IF Scr = NIL THEN cleanexit("Can`t open Screen.",5);

    NewWin.Screen := Scr;
    Win := OpenWindow(Adr(NewWin));
    IF Win = NIL THEN cleanexit("Can`t open window.",5);

    MyRPort:=Win^.RPort;
    MyVPort:=Adr(Scr^.SViewPort);
    MyBitMap := MyRPort^.BitMap;

    LoadRGB4(MyVPort,ADR(Farbtabelle),8); { Die Farbtabelle kommt aus
                                            Bilder.p. }
{ * Jetzt die Picturedaten initialisieren. * }

    ImageAstronaut.Imagedata := ChipCopy(ADR(ImageDataAstronaut),30*2);
    ImageAtomium.Imagedata   := ChipCopy(ADR(ImageDataAtomium),66*2);
    ImageEieruhr.Imagedata   := ChipCopy(ADR(ImageDataEieruhr),24*2);
    ImageFragezeichen.Imagedata := ChipCopy(ADR(ImageDataFragezeichen),39*2);
    ImageTankstelle.Imagedata := ChipCopy(ADR(ImageDataTankstelle),20*2);
    ImageSchiff.Imagedata     := ChipCopy(ADR(ImageDataSchiff),48*2);
    ImageFalle.Imagedata      := ChipCopy(ADR(ImageDataFalle),40*2);
    ImageExplosion1.Imagedata := ChipCopy(ADR(ImageDataExplosion1),78*2);
    ImageExplosion2.Imagedata := ChipCopy(ADR(ImageDataExplosion2),78*2);
    ImageExplosion3.Imagedata := ChipCopy(ADR(ImageDataExplosion3),78*2);

    Picture[1] := ADR(ImageAstronaut);
    Picture[2] := ADR(ImageEieruhr);
    Picture[3] := ADR(ImageFragezeichen);
    Picture[4] := ADR(ImageAtomium);
    Picture[5] := ADR(ImageTankstelle);
    Picture[6] := ADR(ImageSchiff);
    Picture[7] := ADR(ImageFalle);
    Picture[8] := ADR(ImageExplosion1);
    Picture[9] := ADR(ImageExplosion2);
    Picture[10] := ADR(ImageExplosion3);

{ Und unseren Blitterspeicher. }
    BlittiSpeicher := AllocRaster(80,200);
    if BlittiSpeicher = Nil then cleanexit("Kein Chip-Mem für Blitterspeicher.",20);

{ Ein bischen Zufall.}
    SelfSeed();

end; { Init }

{/***********************************************************************/}
procedure Print(xpos,ypos : short; stext : string);
{ Zeichnet an der Stelle ypos, xpos den Text stext. }
var
    tt : short;
begin
    Move(MyRPort,xpos,ypos);
    tt := short(StrLen(stext));
    GText(MyRPort, stext, tt);
end; {Print}

{***********************************************************************}
function GetKey(): byte;
var
    tt : byte;
begin
    tt := GetJoy2();
    if tt <> 0 then begin
        if (tt and 1) = 1 then GetKey := 2; { * Rechts * }
        if (tt and 2) = 2 then GetKey := 1; { * Links  * }
        if (tt and 4) = 4 then GetKey := 4; { * runter * }
        if (tt and 8) = 8 then GetKey := 3; { * hoch   * }
    end;

    { * Abfrage der Tasten, wenn es nicht der Joystick war * }

    tt := GetChar();
    if tt = 97 then     {/*   <-   */ }
       GetKey := 1;
    if tt = 99 then     {/*   ->   */ }
       GetKey := 2;
    if tt = 103 then    {/*   hoch  */}
       GetKey := 3;
    if tt = 101 then    {/*  runter */}
       GetKey := 4;
    GetKey := 0;        { * War wohl alles nichts * }
end;{GetKey}

{ ********************************************************************* }
{ Auch das Highscore wollen wir laden und speichern. Und natürlich wol- }
{ len wir mit der Punktzahl eines Spielers auch seinen Platz ermitteln. }

procedure LoadHighscore;
var
    infile : file of spielerdef;
    flag : boolean;
    a : byte;
    tt, tt1  : byte;
begin
    flag:=true;
    flag := reopen(":Ufo.Highscore",infile);
    if flag = true then begin
        for a := 1 to 10 do
            read(infile,spieler[a]);
        close(infile);
    end;{if}
    if flag = false then begin
       for tt:=1 to 10 do begin
           for tt1 := 1 to 20 do
               spieler[tt].name[tt1] := ' ';
           spieler[tt].name[21] := '\0';
           spieler[tt].punkte := 0;
           spieler[tt].lev    := 0;
       end;{for}
    end;{if}
end;{LoadHighScore}

{***}

procedure SaveHighscore;
var
    outfile : file of spielerdef;
    flag : boolean;
    a : byte;
begin
    flag:=true;
    flag := open(":Ufo.Highscore",outfile);
    if flag = true then begin
        for a := 1 to 10 do
            write(outfile,spieler[a]);
        close(outfile);
    end;{if}
end;{SaveHighScore}

{***}

function Insertscore(Punkte : integer): byte;
{ Liefert den entsprechenden Tabellenplatz zurück und verschiebt
  entsprechend die Tabelle. An dem frei werdenden Platz wird alles
  auf Null gesetzt. Wenn der Tabplatz nicht gefunden werden kann
  gibt es -1 zurück. }

VAR
    tt, tt1 : byte;
    loop    : boolean;

begin
    tt := 1;
    loop := true;
    repeat
        if Spieler[tt].punkte > Punkte then begin { Punktzahl Tab größer? }
                         tt:=tt+1;         { Ja, also nächster Tab-Platz }
                         if tt>10 then begin
                            loop:=false; { Ende Tab ?}
                            InsertScore := -1;
                         end;
        end;
        if Spieler[tt].punkte <= Punkte then begin

    {* An der Position TT steht ein Spieler mit niedriger Punktzahl *}
    {* oder gleicher Punktzahl                                      *}
    {* Deshalb muß jetzt die ganze Tabelle verschoben werden.       *}

           loop := false;
           for tt1 := 10 downto tt+1 do begin
               strcpy(ADR(Spieler[tt1].name),ADR(Spieler[tt1-1].name));
               Spieler[tt1].punkte:=Spieler[tt1-1].punkte;
               Spieler[tt1].lev   :=Spieler[tt1-1].lev;
           end;
           strcpy(ADR(Spieler[tt].name),"                    ");
           Spieler[tt].punkte := 0;
           Spieler[tt].lev    := 0;
           InsertScore := tt;
        end;
    until loop = false;
end; {Insertscore}

{***********************************************************************}
Procedure ShowHighscore();
{ * Zeigt Alle High-Scores an. * }
var
    tt, tt1 : integer;
    tt2 : string;

begin
    LoadHighscore();
    tt2 := AllocString(10);
    SetRast(MyRPort,0);
    SetColour(0,1);
    for tt := 1 to 10 do begin
        Print(20,tt*10,"  .:                      -  Punkte:        -  Level:        ");
        tt1 :=IntToStr(tt2,tt);
        Print(20,tt*10,tt2);
        Print(60,tt*10,ADR(Spieler[tt].name));
        tt1 :=IntToStr(tt2,Spieler[tt].punkte);
        Print(316,tt*10,tt2);
        tt1 :=IntToStr(tt2,Spieler[tt].lev);
        Print(452,tt*10,tt2);
    end;
    FreeString(tt2);
    repeat
        tt := GetKey();
    until tt <> 0;
end; { ShowHighscore }

{***********************************************************************}
procedure PrintScore();
{ * Schreibt die Punktzahl auf den Bildschirm. * }
var
    tt1 : string;
begin
    tt1 := AllocString(10);
    SetColour(1,0);
    Move(MyRPort,240,7);
    GText(MyRPort,"Points: ", 8);
    IntToStr6(tt1,irspunkte);
    GText(MyRport,tt1,6);
    FreeString(tt1);
end; { *  Printscore * }

{***********************************************************************}
procedure PrintTank();
{ * Bringt den Tankinhalt auf den Bildschirm. * }
var
    tt1 : short;
begin
{ * Erstmal löschen * }
    SetColour(0,0);
    Rectfill(MyRPort,128,1,228,9);

    SetColour(1,0);
    Move(MyRPort,80,7);
    GText(MyRPort,"Fuel: ", 6);
    if irstank < 16 then
        SetColour(3,3);   { Rote Farbe  }
    if irstank > 15 then
        SetColour(7,7);   { Grüne Farbe }
    tt1 := irstank;
    if tt1 > 100 then tt1 := 100;
    tt1 := tt1 + 128;
    Rectfill(MyRPort,128,1,tt1,9);
end; { * PrintTank * }

{***********************************************************************}
Procedure InitObjekts(Wieviele : byte);
{ * Initialisiert die Objekte und zeichnet sie auf den Schirm. Die
    Objekte sind wie folgt aufgeteilt:  1 -> Ist das Raumschiff,
                                        100 - 199 -> die Zeichen,
                                        200 - 255 -> die Fallen. * }
var
    tt, tt1  : short;
begin
    { Erstmal alle als nicht belegt kennzeichnen. }
    for tt := 0 to 255 do
        Objekt[tt].Ox := -1;

    { Die Begrenzungslinien }
    SetColour(4,0);

    { Oben }
    Move(MyRPort, 0 ,  10);
    Draw(MyRPort, 639, 10);

    { rechts }
    Draw(MyRPort, 639, 189);

    { links }
    Move(MyRPort, 0,  10);
    Draw(MyRPort, 0, 189);

    { unten }
    Draw(MyRPort, 639, 189);

    { * Und die entsprechenden Objektdefinitionen *  }

    { Oben }
    Objekt[2].Ox := 0;
    Objekt[2].Oy := 10;
    Objekt[2].Sizex := 639;
    Objekt[2].Sizey := 0;

    { rechts }
    Objekt[3].Ox := 639;
    Objekt[3].Oy := 10;
    Objekt[3].Sizex := 0;
    Objekt[3].Sizey := 179;

    { links }
    Objekt[4].Ox := 0;
    Objekt[4].Oy := 10;
    Objekt[4].Sizex := 0;
    Objekt[4].Sizey := 179;

    { unten }
    Objekt[5].Ox := 0;
    Objekt[5].Oy := 189;
    Objekt[5].Sizex := 639;
    Objekt[5].Sizey := 0;


    { Und jetzt das Raumschiff. }
    Objekt[1].Ox := 2;
    Objekt[1].Oy := 100;
    Objekt[1].Sizex := Picture[6]^.width;
    Objekt[1].Sizey := Picture[6]^.height;
    Objekt[1].Speedx := 0;  { Raumschiff hat keine Fahrt! }
    Objekt[1].Speedy := 0;  { Raumschiff hat keine Fahrt! }
    Objekt[1].typ := 6;

    { Die Objekte }

    for tt := 101 to 100+wieviele do begin
        repeat
             Objekt[tt].typ := RangeRandom(4)+1; { * 1 - 5 * }
             Objekt[tt].Ox := 1  + Rangerandom(638 - Picture[Objekt[tt].typ]^.width);
             Objekt[tt].Oy := 11 + Rangerandom(178 - Picture[Objekt[tt].typ]^.height);
             Objekt[tt].Sizex := Picture[Objekt[tt].typ]^.width;
             Objekt[tt].Sizey := Picture[Objekt[tt].typ]^.height;

             { * Gibts an der Position schon ein Objekt? * }
             tt1 := CollObjekt(1, 100+wieviele, Objekt[tt].Ox,
                               Objekt[tt].Oy, Objekt[tt].Sizex,
                               Objekt[tt].Sizey);
             if tt1 = tt then begin   { * Selbst gefunden. * }
                if tt <> 100+wieviele then
                   tt1 := CollObjekt(tt1+1, 101+wieviele, Objekt[tt].Ox,
                                     Objekt[tt].Oy, Objekt[tt].Sizex,
                                     Objekt[tt].Sizey);
                if tt = 100+wieviele then tt1 := -1;
             end;
        until tt1 = -1;
    end;

    { * Und alles zeichnen * }

    DrawObjekt(101,100+wieviele);

end; { InitObjekt }

{***********************************************************************}
Procedure InitFallen(wieviele : byte);
{ * Initialisiert die Fallen und zeichnet sie auf den Schirm. * }
var
    tt, tt1  : short;
begin
    { * Die Fallen. *  }

    for tt := 201 to 200+wieviele do begin
        repeat
             Objekt[tt].typ := 7;   { * Immer eine Falle. * }
             Objekt[tt].Ox := 21  + Rangerandom(618 - Picture[Objekt[tt].typ]^.width);
             Objekt[tt].Oy := 11 + Rangerandom(178 - Picture[Objekt[tt].typ]^.height);
             Objekt[tt].Sizex  := Picture[7]^.width;
             Objekt[tt].Sizey  := Picture[7]^.height;
             if Level < 10 then begin
                Objekt[tt].Speedx := RangeRandom(Levels[Level].Fallenspeed);
                Objekt[tt].Speedy := RangeRandom(Levels[Level].Fallenspeed);
             end;
             if Level > 9 then begin
                Objekt[tt].Speedx := RangeRandom(Levels[10].Fallenspeed);
                Objekt[tt].Speedy := RangeRandom(Levels[10].Fallenspeed);
             end;

             { * Gibts an der Position schon ein Objekt? * }
             tt1 := CollObjekt(1, 255, Objekt[tt].Ox,
                               Objekt[tt].Oy, Objekt[tt].Sizex,
                               Objekt[tt].Sizey);
             if tt1 = tt then begin   { * Selbst gefunden. * }
                if tt <> 200+wieviele then
                   tt1 := CollObjekt(tt1+1, 200+wieviele, Objekt[tt].Ox,
                                     Objekt[tt].Oy, Objekt[tt].Sizex,
                                     Objekt[tt].Sizey);
                if tt = 200+wieviele then tt1 := -1;
             end;
        until tt1 = -1;
    end;

    { * Und alles zeichnen * }

    DrawObjekt(201,200+wieviele);

end; { InitFallen }

{***********************************************************************}
{ Selbstredend. }
procedure Anleitung;
var
    tt : byte;
begin
    SetRast(MyRPort,0);
    SetColour(1,0);
    Print(20,10,"Das Ziel des Spieles ist es, möglichst lange über den Bildschirm zu fliegen");
    Print(20,18,"Der Joystick oder die Cursor-Tasten helfen dir bei der Steurerung. Aber    ");
    Print(20,28,"denke daran: jede Joystick- oder Cursortastenbewegung kostet dich eine     ");
    Print(20,36,"Treibstoffeinheit. Von den Wänden wirst du zurückgeschleudert.             ");
    DrawImage(MyRport,Picture[1],10,45);
    Print(40,54," ---> 1 Punkt          ");
    DrawImage(MyRport,Picture[2],10,70);
    Print(40,78," ---> 3 Punkte         ");
    DrawImage(MyRport,Picture[3],10,100);
    Print(40,110," ---> 1 - 10 Punkte   ");
    DrawImage(MyRport,Picture[4],10,160);
    Print(40,168," ---> 5 Punkte        ");
    DrawImage(MyRport,Picture[5],10,130);
    Print(40,139," ---> 10 Tankeinheiten");
    DrawImage(MyRport,Picture[7],250,45);
    Print(280,54," ---> Der Tod !");
    Print(40,190,"Viel Spaß !!! (Bitte eine Taste drücken)");
    repeat
        tt := GetKey();
    until tt <> 0;
    SetRast(MyRPort,0);
end;{Anleitung}

{***********************************************************************}
{ * TitleScreen * }
Procedure Titlescreen();
{ * Bringt den Schriftzug auf den Schirm und fragt die Tasten nach
  weiteren Aktionen ab. Und noch ein bischen Augenwischerei. * }
const
        Titelpunkte : array[1..32] of short =
(      150, 50, 150,100,200,100,200, 50,    { U }
       250, 50, 300, 50,250, 50,250, 75,
       300, 75, 250, 75,250,100,            { F }
       350, 50, 400, 50,400,100,350,100,
       350, 50                              { O }
);

var
    tt, tt4  : short; { * Für ein bischen Augenwischerei. * }
begin
    repeat
        tt  := 230;
        tt4 := -5;
        SetRast(MyRPort,0);
        Move(MyRPort,150,50);
        SetColour(1,0);
        PolyDraw(MyRPort,4,ADR(Titelpunkte[1]));
        Move(MyRPort,250,50);
        SetColour(2,0);
        PolyDraw(MyRPort,7,ADR(Titelpunkte[9]));
        Move(MyRPort,350,50);
        SetColour(6,0);
        PolyDraw(MyRPort,5,ADR(Titelpunkte[23]));

        SetColour(1,0);
        Print(40,130,"Version 2.0  (c) 1982, 1992 by Jörg Wach ");
        Print(40,140,"This Game is Giftware ! If you like it,  ");
        Print(40,150,"then please tell it me.                  ");
        Print(40,170,"<Hoch>    = Anleitung, <Runter> = Spiel");
        Print(40,190,"<Links>   = Highscore, <Rechts> = Ende ");
        repeat
            key:=GetKey();      { * Was darfs sein, Fremder? * }
            tt := tt + tt4;
            if tt  > 350 then tt4 := -tt4;  { * und Rückwärts. * }
            if tt  < 0   then tt4 := -tt4;  { * Genauso. * }
            ScrollRaster(MyRPort,tt4, 0, 5, 45,635,105); { * Wisch, wisch ... * }
        until key <> 0;

        if key = 1 then begin   { * Highscore kommt und geht ... * }
           ShowHighscore();
           SetRast(MyRPort,0);
        end;

        if key = 2 then     { * Ab gehts ..... * }
           CleanExit("Bye Bye ...",0); { * Sauberer Abgang! * }
        if key = 3 then begin     { * Hoch gehts her ... * }
           Anleitung();
           SetRast(MyRPort,0);
        end;
    until key = 4;
end;

{***********************************************************************}
{ Level }
procedure SetLevel();
{ * Setzt alle erforderlichen Variablen und zeigt den Level an,
    in welchem man (Frau?) sich befindet. * }
var
    tt  : string;
    tt1 : byte;
begin
    Inc(Level);     { * Um eins erhöhen. * }
    tt1 := Level;
    if tt1 > 10 then tt1 := 10; { * Und kürzen, wenns sein muß. * }
    tt := AllocString(10);
    SetRast(MyRPort,0);
    SetColour(1,1);
    Rectfill(MyRPort,270,80,370,120);
    SetColour(0,1);
    Print(271,107,"Level ");
    IntToStr6(tt, level);
    GText(MyRPort, tt, 6);  { * Cursor steht richtig, also schreiben wir. * }

    FreeString(tt);
    SetColour(0,2);
    Print(219,132," ");
    Print(227,132,Levels[tt1].Anmerkung);
    GText(MyRPort, " ", 1);  { * Cursor steht richtig, also schreiben wir. * }

    repeat
        for tt1 := 1 to 7 do begin
            SetColour(tt1,0);
            Print(219,140," Bitte eine Taste Drücken! ");
        end;
        key:=GetKey();      { * Wir warten. * }
    until key <> 0;
    SetRast(MyRPort,0);

    tt1 := Level;
    if tt1 > 10 then tt1 := 10; { * Und kürzen, wenns sein muß. * }

    { * Und jetzt initialisieren wir den anderen Kram. * }

    InitObjekts(Levels[tt1].Objekte);

    { * Auch die Fallen wollen leben. * }

    if Levels[tt1].Fallen > 0 then InitFallen(Levels[tt1].Fallen);

    { * Und natürlich müssen wir unseren Zähler akzualisieren. * }

    MaxObjekts := Levels[tt1].Objekte;
end;

{***********************************************************************}
{ * Animation der restlichen Objekte, sprich Fallen und Explosionen. *}
{ * Dieses sind : 100 - 199 -> die Zeichen, 200 - 255 -> die Fallen. * }

Procedure AniObjekts();
var
    tt, tt1, tt2, tt4 : short;
begin
    for tt := 101 to 199 do begin
        if Objekt[tt].typ > 7 then begin { * Explosionen * }
           if Objekt[tt].Ox <> -1 then begin   { * Keine Toten. *}
              Objekt[tt].Phase1 := Objekt[tt].Phase1 - 1;
              if Objekt[tt].Phase1 < 1 then begin  { * Neue Explosion. * }
                 UndrawObjekt(tt,tt);              { * Löschen. * }
                 Objekt[tt].typ := Objekt[tt].typ + 1;

                 if Objekt[tt].typ > 10 then
                    Objekt[tt].Ox := -1;     { * Ist also Tot * }

                 if Objekt[tt].typ < 11 then begin
                    Objekt[tt].Phase1 := 5; { * Neu beginnen. * }
                    DrawObjekt(tt,tt);      { * Und neue Explosion. * }
                 end;
              end; { * wir brauchen nichts zu zeichnen. * }
           end; { * Tote Objekte. * }
        end; { * Keine Explosionen. * }
    end; { For - Schleife. * }

{ * Wenns eine Falle ist dann Positionsabfrage auf neue Position. * }
    for tt := 201 to 255 do begin
        if Objekt[tt].Ox <> -1 then begin
           UndrawObjekt(tt,tt);              { * Löschen. * }
           { * Ecken wir irgendwo an ? * }
           if (Objekt[tt].Ox + Objekt[tt].Speedx) < 1 then
               Objekt[tt].Speedx := -Objekt[tt].Speedx;
           if (Objekt[tt].Ox + Objekt[tt].Speedx) > 618 then
               Objekt[tt].Speedx := -Objekt[tt].Speedx;

           if (Objekt[tt].Oy + Objekt[tt].Speedy) < 11 then
               Objekt[tt].Speedy := -Objekt[tt].Speedy;
           if (Objekt[tt].Oy + Objekt[tt].Speedy) > 178 then
               Objekt[tt].Speedy := -Objekt[tt].Speedy;

           { * Neue Position ermitteln. * }
           Objekt[tt].Ox := Objekt[tt].Ox + Objekt[tt].Speedx;
           Objekt[tt].Oy := Objekt[tt].Oy + Objekt[tt].Speedy;

           { * Ist da irgendwas im Weg ? * }
           { * Als erstes das Raumschiff. * }

           tt4 := CollObjekt(1,1, Objekt[tt].Ox, Objekt[tt].Oy,
                             Objekt[tt].sizex, Objekt[tt].sizey);
           if tt4 = 1 then begin { * Raumschiff! * }
              loop := False;
              return;    { * Sofort raus hier. * }
           end;

           GraphCollision(Objekt[tt].Ox,Objekt[tt].Oy,Objekt[tt].sizex,Objekt[tt].sizey);
        if blitctrl = 1 then begin
           { * Jetzt die anderen Objekte. * }
           tt1 := 101;
           tt2 := 255;
           tt4 := 0;

           repeat
                tt4 := CollObjekt(tt1,tt2, Objekt[tt].Ox, Objekt[tt].Oy,
                                  Objekt[tt].sizex, Objekt[tt].sizey);

{ * Wenn da was ist, dann das Objekt löschen und die Daten für die
    Explosion initieren. * }

                if tt4 <> -1 then begin

                   if Objekt[tt4].typ > 6 then begin
                      tt1 := tt4+1;
                   end;

                   if Objekt[tt4].typ < 7 then begin
                      dec(MaxObjekts);  { * Wieder eins weniger. * }
                      UnDrawObjekt(tt4,tt4); { * Wech * }
                      Objekt[tt4].Typ := 8;  { * Explosion1 * }
                      Objekt[tt4].Phase1 := 5;  { * 5 mal auftauchen * }
                      Objekt[tt4].sizex := Picture[8]^.width;  { * 5 mal auftauchen * }
                      Objekt[tt4].sizey := Picture[8]^.height;  { * 5 mal auftauchen * }
                      DrawObjekt(tt4,tt4); { * Und wieder zeichnen * }
                   end;
                   if tt1 > 255 then tt4 := -1;
                end; { if }
           until tt4 = -1;
         end;
           DrawObjekt(tt,tt);              { * Falle zeichnen. * }
        end; { * Also auch kein Lebendes Objekt. * }
    end; { * For * }
end; { * AniObjekts * }

{/***********************************************************************/
/*******  Hauptprogramm  (MAIN)                                    *****/
/***********************************************************************/}

var
    i, itt : integer;

    tempstr : string;

    temp1,
    temp2,
    temp3,
    temp4,
    temp5       : short;  { Temporäre Zwischenspeicher. }

begin
    tempstr := AllocString(80);
    init();
    mainflag := true;

while mainflag = true do begin
    Titlescreen();
    Level := 0; { * Muß 0 sein, da er von SetLevel erhöht wird. * }
    SetLevel(); { * Initialisieren wir mal alles. * }
    loop:=true;
    irstank:=20;
    irspunkte:=0;

    { * Spieldaten schreiben * }
    PrintScore();
    PrintTank();

    while loop = true do begin
        { * Alte Position sichern * }
        UnDrawObjekt(1,1);  { * Raumschiff löschen. * }

        key:=GetKey();      { Wohin geht die Reise? }
        if key <> 0 then begin
           irstank := irstank - 1;
           PrintTank();
           if key = 3 then
              Objekt[1].Speedy := Objekt[1].Speedy - 1; { * Hoch * }

           if key = 4 then
              Objekt[1].Speedy := Objekt[1].Speedy + 1; { * runter * }

           if key = 1 then
              Objekt[1].Speedx := Objekt[1].Speedx - 1; { * links * }

           if key = 2 then
              Objekt[1].Speedx := Objekt[1].Speedx + 1; { * rechts * }
        end;

        { * Abbruch ? * }
        if GetChar() = $73 then     { * DEL-Taste gedrückt! * }
            CleanExit("Kontrollierter Abbruch !",0);

        key := 0;

        { * Jetzt kommt die Positionsüberprüfung. * }

        if (Objekt[1].Ox + Objekt[1].Speedx) < 1 then
           Objekt[1].Speedx := -Objekt[1].Speedx;
        if (Objekt[1].Ox + Objekt[1].Speedx) > 618 then
           Objekt[1].Speedx := -Objekt[1].Speedx;

        if (Objekt[1].Oy + Objekt[1].Speedy) < 11 then
           Objekt[1].Speedy := -Objekt[1].Speedy;
        if (Objekt[1].Oy + Objekt[1].Speedy) > 178 then
           Objekt[1].Speedy := -Objekt[1].Speedy;

        Objekt[1].Ox := Objekt[1].Ox + Objekt[1].Speedx;
        Objekt[1].Oy := Objekt[1].Oy + Objekt[1].Speedy;

        { * Ist da irgendwas im Weg ? * }

        GraphCollision(Objekt[1].Ox,Objekt[1].Oy,Objekt[1].sizex,Objekt[1].sizey);
     if blitctrl = 1 then begin

        temp1 := 101;
        temp4 := 0;
        temp5 := 255;
        repeat  { * Abfrage, ob mehrere Objekte an der Stelle stehen * }
               temp4 := CollObjekt(temp1,temp5, Objekt[1].Ox, Objekt[1].Oy,
                                   Objekt[1].sizex, Objekt[1].sizey);
               if temp4 <> -1 then begin
                 if Objekt[temp4].typ < 8 then begin
                  if Objekt[temp4].typ = 1 then begin
                     irspunkte:=irspunkte + 1;
                     UnDrawObjekt(temp4,temp4); { * Wech * }
                     Objekt[temp4].Ox := -1;   { * Als Tot kennzeichnen. * }
                  end;

                  if Objekt[temp4].typ = 2 then begin
                     irspunkte:=irspunkte + 3;
                     UnDrawObjekt(temp4,temp4); { * Wech * }
                     Objekt[temp4].Ox := -1;   { * Als Tot kennzeichnen. * }
                  end;

                  if Objekt[temp4].typ = 3 then begin
                     irspunkte:=irspunkte + RangeRandom(9) + 1;
                     UnDrawObjekt(temp4,temp4); { * Wech * }
                     Objekt[temp4].Ox := -1;   { * Als Tot kennzeichnen. * }
                  end;

                  if Objekt[temp4].typ = 4 then begin
                     irspunkte:=irspunkte + 5;
                     UnDrawObjekt(temp4,temp4); { * Wech * }
                     Objekt[temp4].Ox := -1;   { * Als Tot kennzeichnen. * }
                  end;

                  if Objekt[temp4].typ = 5 then begin
                     irstank := irstank + 10;
                     PrintTank();
                     UnDrawObjekt(temp4,temp4); { * Wech * }
                     Objekt[temp4].Ox := -1;   { * Als Tot kennzeichnen. * }
                  end;

                  if Objekt[temp4].typ = 7 then begin
                     Loop  := false; { * Falle * }
                     temp4 := -1; { * Sonst kommen wir nicht mehr raus! * }
                  end;

                  dec(MaxObjekts);  { * Wieder eins weniger. * }
                 end; { * if * }
                 temp1 := temp4 + 1;
               end; { if }
        until temp4 = -1;
        PrintScore();
     end;
        { * Jetzt das Raumschiff zeichnen * }

        DrawObjekt(1,1);

        { * Und jetzt animieren wir die restlichen Objekte. * }

        AniObjekts();

        { * Tank leer? * }

        if irstank<1 then loop:=false; { * Oh ja, also Ende. * }

        { * Noch Objekte da ? * }

        if MaxObjekts < 1 then begin { * Neue Runde! * }

           SetLevel();        { * Zeig dich! * }

           { * Spieldaten schreiben * }
           PrintScore();
           PrintTank();

        end;

    end;{loop}

    { * Tja, jedes Spiel geht einmal zu Ende. * }

    LoadHighscore();

    SetRast(MyRPort,0);
    i := IntToStr(tempstr,irspunkte);
    Print(10,10,"Punkte: ");
    Print(74,10,tempstr);

    itt := InsertScore(irspunkte);
    if (itt > 0) and (itt < 11)  then begin
          SetColour(1,0);
          Print(2,10,"Du hast den  . Platz mit        Punkten erreicht.");
          i := IntToStr(tempstr,itt);
          SetColour(5,0);
          Print(98,10,tempstr);
          IntToStr6(tempstr,irspunkte);
          Print(202,10,tempstr);
          SetColour(1,0);
          Print(40,40,"Bitte gebe deinen Namen ein: ");
          GetString(40, 50, 20, ADR(Spieler[itt].name));
          Spieler[itt].punkte:=irspunkte;
          Spieler[itt].lev   :=Level;
    end;{if}
    SaveHighscore();
end;

    CleanExit(Nil,0);   { * Und ein sauberer Abgang! * }
end.

