/***********************************************************************/
/*                                                                     */
/*  Programm: 3-D Motorrad © 1991 by AMIGO!                            */
/*  Autor   : Armin Harich                                             */
/*                                                                     */
/*  S.A.S_C 5.xx :LC -Lfdc -ff -v -w 3D-Motorrad                       */
/*  Aztec_C 5.0  :CC -ff -so -ps 3D-Motorrad                           */
/*                LN 3D-Motorrad -lmf16 -lc16                          */
/*  Aztec_C 3.6  :CC 3D-Motorrad                                       */
/*                LN 3D-Motorrad -lm -lc                               */
/*  DICE    ??   :DCC -ffp 3d.c -o3D-Motorrad (!Nur Reg. Version!)     */
/*                                                                     */
/***********************************************************************/

#include <math.h>
#include <stdio.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#ifdef LATTICE_50
  #include <proto/dos.h>
  #include <proto/exec.h>
  #include <proto/graphics.h>
  #include <proto/intuition.h>
  #include <mffp.h>
#else
  #include <functions.h>
#endif


/* PolyLine ausprobieren */

#define SICHTWEITE 15000  /* Sichtweite nicht zu größ wählen           */
                          /* Z_MIN wird so gewählt, daß x_bild und     */
#define Z_MIN (SICHTWEITE/4*800L/32000)
                          /* y_bild immer kleiner ± 32000 (short) ist. */
                          /* Da sonst die Zeichenroutine fehler macht. */

#define MAX_PKT 1000      /* MAX_PKT/MAX_VER Bitte  groß  genug  wählen */
#define MAX_VER 1500

#define DRAW         0
#define MOVE         1
#define ALTER_PUNKT  2

#define TONNE       -500  /* Hier  sind  die  y-Werte  von */
#define LEITPLANKE  -220  /* den verschiedened Gegeständen */
#define SPRUNG      -199  /* definiert,   an   denen   sie */
#define MENSCH      -550  /* erkannt    werden     können. */
#define PFUETZE        1
#define BRUECKE     -750
#define KOPF        -300

#define STRBR (2*1700)    /* Straßenbreite */
#define G          20     /* Die Erdanziehung (beim Sprung wichtig) */


#define ENDE               -32000
#define NICHT_PROJEZIERT   -32000
#define ANDERE_BMNR       ((bmnr+1)&1)
#define RND_plus_minus(x) ((int)((rand()+VBeamPos())%(2*((x)+1))-(x)))
#define RND(x)            ((int)((rand()+VBeamPos())%(x)))
#define SWAP(x,y)         {register short zw=x;x=y;y=zw;}
#define DREHE_Y(x,y)      ((short)( ( (x) * cos_dy + (y) * sin_dy ) >> 14 ))
#define DREHE_Z(x,y)      ((short)( ( (x) * cos_dz + (y) * sin_dz ) >> 14 ))

typedef unsigned long  ulong ;
typedef unsigned short ushort;

struct BitMap *bm[2],bm1;
ushort bmnr;
struct GfxBase       *GfxBase;
struct IntuitionBase *IntuitionBase;
struct RastPort  *rp;
struct ViewPort  *vp;
struct Screen *scr;
struct Window *win;
struct NewScreen scrdef={0,0,0,0,2,3,0,0,CUSTOMSCREEN};
struct NewWindow windef={0,0,0,0,0,0,CLOSEWINDOW|RAWKEY|MOUSEBUTTONS|ACTIVEWINDOW|INACTIVEWINDOW,WINDOWCLOSE|NOCAREREFRESH|ACTIVATE|BORDERLESS|RMBTRAP,NULL,NULL,NULL,NULL,NULL,0,0,0,0,CUSTOMSCREEN};

#define AUGEN_NASEN_ABSTAND 35
#define RECHTES_AUGE 0
#define  LINKES_AUGE 1

struct Gegenstand {short flag,x,y,z;}
   loch[]   ={MOVE,-250,PFUETZE,375, DRAW,    0,PFUETZE, 125, DRAW,   500,PFUETZE,-375, DRAW,       250,PFUETZE,-125, DRAW            ,       500,PFUETZE,-375, DRAW,     0,PFUETZE,-125, DRAW, -500,PFUETZE,-125, DRAW            ,     -250,PFUETZE, 125, DRAW            ,     -250,PFUETZE,375, ENDE};
   tonne[]  ={MOVE, 500,  TONNE,  0, DRAW,    0,  TONNE,-500, DRAW,  -500,  TONNE,   0, DRAW,      -500,  TONNE,   0, DRAW            ,      -500,      0,   0, DRAW,     0,      0,-500, DRAW,  500,      0,   0, DRAW|ALTER_PUNKT,      500,  TONNE,   0, ENDE};
   sprung[] ={MOVE, 250, SPRUNG,250, DRAW, -250, SPRUNG, 250, DRAW,  -250,      0,-250, DRAW,       250,      0,-250, DRAW|ALTER_PUNKT,       250, SPRUNG, 250, ENDE};
   bruecke[]={MOVE, 3*STRBR/2,0,500, DRAW,STRBR,BRUECKE, 500, DRAW,-STRBR,BRUECKE, 500, DRAW,-3*STRBR/2,      0, 500, DRAW            ,-3*STRBR/2,      0,-500, DRAW,-STRBR,BRUECKE,-500, DRAW,STRBR,BRUECKE,-500, DRAW            ,3*STRBR/2,      0,-500, DRAW|ALTER_PUNKT,3*STRBR/2,      0,500, ENDE};
   mensch[] ={MOVE,  10,   -450,  0, DRAW,  -40, MENSCH,   0, DRAW,    60, MENSCH,    0, DRAW|ALTER_PUNKT,10,-450, 0, DRAW            ,         0,   -400,   0, DRAW,  -100,MENSCH ,-100, MOVE,  150,   -450,-150, DRAW|ALTER_PUNKT,        0,   -400,   0, DRAW            ,      200,      0,  0, MOVE, 100,-200, 0, DRAW, -50, 0, -20, ENDE};           

struct Gegenstand  *geg[5]={
   (struct Gegenstand *)loch,
   (struct Gegenstand *)tonne,
   (struct Gegenstand *)sprung,
   (struct Gegenstand *)bruecke,
   (struct Gegenstand *)mensch  };

struct Punkt {short  x,y,z;
              short  x_bild[2],y_bild;}
       punkt[MAX_PKT+1],*pkt=punkt,*anf_pkt=punkt,*end_pkt=punkt;

struct Verbindung{struct Punkt *Punkt;
                  ushort Modus;}
       verbindung[MAX_VER+1],*ver=verbindung,*anf_ver=verbindung,*end_ver=verbindung;

 short x_pos,y_pos=KOPF,z_pos,vz;

short score,hiscr=100;

float dy,dz;
long  sin_dy,cos_dy,sin_dz,cos_dz;

short xsizeh,ysizeh;
long  z_bild10;


void close_all(err_txt) /* schließt alles wieder ordnungsgemäß */

char *err_txt;

{if (err_txt)                  puts(err_txt);
 if (bm1.Planes[ LINKES_AUGE]) FreeRaster(bm1.Planes[ LINKES_AUGE],(long)scrdef.Width,(long)scrdef.Height);
 if (bm1.Planes[RECHTES_AUGE]) FreeRaster(bm1.Planes[RECHTES_AUGE],(long)scrdef.Width,(long)scrdef.Height);
 if (win)                     {rp->BitMap=bm[0];CloseWindow (win);}
 if (scr)                      CloseScreen (scr);
 CloseLibrary ((struct Library *)GfxBase);
 CloseLibrary ((struct Library *)IntuitionBase);
 exit(0);
 }


void open_all() /* Hier wird alles wichtige geöffnet */

{IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0L);
 GfxBase      =(struct       GfxBase *)OpenLibrary( "graphics.library",0L);
 windef.Width = scrdef.Width = GfxBase->NormalDisplayColumns>>1;
 windef.Height= scrdef.Height= GfxBase->NormalDisplayRows;
 xsizeh  =      windef.Width >>1;
 ysizeh  =      windef.Height>>1;
 z_bild10=(long)windef.Width <<10;
 if (!(windef.Screen = scr = OpenScreen (&scrdef))) close_all("Kein Screen");
 if (!(                win = OpenWindow (&windef))) close_all("Kein Window");
 rp=  win->RPort    ;
 vp=&(scr->ViewPort);
 ShowTitle(scr,FALSE);
 bm[0]=rp->BitMap;
 bm[1]=&bm1;
 InitBitMap(&bm1,2L,(long)scrdef.Width,(long)scrdef.Height);
 if (!(bm1.Planes[ LINKES_AUGE] = AllocRaster((long)scrdef.Width,(long)scrdef.Height))) close_all("Kein Chip-MEM");
 if (!(bm1.Planes[RECHTES_AUGE] = AllocRaster((long)scrdef.Width,(long)scrdef.Height))) close_all("Kein Chip-MEM");
 SetRGB4(vp,0L, 0L, 2L, 0L);/* Hintergrundfarbe  für      kein      Auge sichtbar */
 SetRGB4(vp,1L, 7L, 1L, 0L);/*            Farben für linkes         Auge sichtbar */
 SetRGB4(vp,2L, 0L, 0L,15L);/*            Farben für        rechtes Auge sichtbar */
 SetRGB4(vp,3L, 7L, 1L,15L);/*            Farben für linkes+rechtes Auge sichtbar */
 }



void Ver3D(flags,x,y,z) /* Dies ist die allgemeine Routiene, */
                        /* um 3D Verbindungen zu "zeichnen"  */
short x,y,z;
ushort flags;

{register struct Punkt *p=end_pkt;

 if (flags&ALTER_PUNKT)
   while ( (p = p-punkt <=0 ? punkt+MAX_PKT : p-1) != anf_pkt )
      if ( p->x==x && p->y==y && p->z==z )  goto ende;

 end_pkt->x=x;
 end_pkt->y=y;
 end_pkt->z=z;
 p = end_pkt;
 end_pkt = end_pkt-punkt >= MAX_PKT ? punkt : end_pkt+1;

ende:
 end_ver->Modus = flags;
 end_ver->Punkt=p;
 end_ver = end_ver-verbindung >= MAX_VER ? verbindung : end_ver+1;
 return;
 }


void setze_gegenstand(g,x,y,z,dy)

struct Gegenstand *g;
short x,y,z;
float dy;

{for (;g->flag!=ENDE;g++) Ver3D(g->flag,DREHE_Y(g->x,-g->z)+x,g->y+y,DREHE_Y(g->z,g->x)+z);}


#define QUA(x) ((long)(x)*(long)(x))
#define VZ  1300  /* Länge der Mittellinie */

void baue_strasse()

{static short x_str,z_str=-VZ,i,fdy,ffdy,x1[2],z1[2],x2[2],z2[2],j,b;
 static float dy; /* static, damit die variableninhalte trotz jedem neuaufruf ihre alten inhalte behalten */

 while (QUA(x_str-x_pos)+QUA(z_str-z_pos)<QUA(SICHTWEITE)) 
  {ffdy+=RND_plus_minus(1024)-ffdy-(fdy>>2); /* Ist die Straße innerhalb der Sichtweite ? */ 
   fdy +=ffdy;              /* Hier wird die neue Richtung */
   dy +=fdy/20000.;         /* der Straße berechnet        */

   sin_dy=(1<<14)*sin(dy);
   cos_dy=(1<<14)*cos(dy);
   x_str+=DREHE_Y( 0,-VZ);  /* die Position der Straße, an */
   z_str+=DREHE_Y(VZ,  0);  /* der weitergebaut werden muß */

   if ((i++)%2==1)          /* Abwächselnd jedes 2. mal */
    {Ver3D(DRAW,x_str,0,z_str);/* Der Mittelstreifen */
     for ( j=0,b=-STRBR/2 ; j<=1 ; j++,b=-b)
      {if (RND(10)==0)      /* Ab und zu einen Gegenstand */
        {score+=vz>>6;setze_gegenstand(geg[RND(5)],DREHE_Y(b>>1,0)+x_str,0,DREHE_Y(0,b>>1)+z_str);}
       x1[j]=x2[j];
       z1[j]=z2[j];         /* Alte Position merken ! */
       x2[j]=DREHE_Y(b,0)+x_str; /* neue  Position  der   Eckpfosten */	
       z2[j]=DREHE_Y(0,b)+z_str; /* in Richtung der Straße berechnen */
       Ver3D(MOVE            ,x1[j],0,z1[j]);          /* Die Pfosten und  */
       Ver3D(DRAW|ALTER_PUNKT,x1[j],LEITPLANKE,z1[j]); /* die Leitplanken als */
       Ver3D(DRAW            ,x2[j],LEITPLANKE,z2[j]); /* 3D-Verbindung zeichnen */
       }
     }
   else
     Ver3D(MOVE,x_str,0,z_str); /* Der Mittelstreifen */
   }
 }

char txt[]="HI:0000 PTS:0000 SPD:000 km/h";

void zeichne_bild(auge)

ushort auge;

{register short new=TRUE,noch_nicht_benutzt=TRUE;

 rp->Mask = 1<<auge ;    /* Text mit Punkten und Geschw. ausgeben */
 Move(rp,xsizeh-29L*4+((auge==LINKES_AUGE?-AUGEN_NASEN_ABSTAND:AUGEN_NASEN_ABSTAND)>>1),8L);
 Text(rp,txt,29L);

 /* alle Verbindungen Zeichnen */
 for ( ver=anf_ver ; ver!=end_ver ;  ver = ver-verbindung >= MAX_VER ? verbindung : ver+1 )
  {if ( ver->Punkt->y_bild == NICHT_PROJEZIERT )
     {new=TRUE;if (noch_nicht_benutzt) anf_ver=ver;}
   else
     {if (new || ver->Modus&MOVE)  Move(rp,(long)ver->Punkt->x_bild[auge],(long)ver->Punkt->y_bild);
      else                         Draw(rp,(long)ver->Punkt->x_bild[auge],(long)ver->Punkt->y_bild);
      new=FALSE;noch_nicht_benutzt=FALSE;
      }
   }
 }


void naechstes_bild()

{if (score>hiscr) hiscr=score;   /* Score und Hiscore eventuell */
 if (score<0) score=0;           /* korregieren und             */
 txt[6] = hiscr %10        +'0';
 txt[5] = hiscr %100  /10  +'0';
 txt[4] = hiscr %1000 /100 +'0';
 txt[3] = hiscr %10000/1000+'0'; /* dann in Text verwandeln.    */
 txt[15]= score %10        +'0';
 txt[14]= score %100  /10  +'0';
 txt[13]= score %1000 /100 +'0';
 txt[12]= score %10000/1000+'0';
 txt[23]=(vz>>2)%10        +'0';
 txt[22]=(vz>>2)%100  /10  +'0';
 txt[21]=(vz>>2)%1000 /100 +'0';

 rp->BitMap=bm[bmnr];
 zeichne_bild( LINKES_AUGE);     /* Beide Bilder zeichnen */
 zeichne_bild(RECHTES_AUGE);
 vp->RasInfo->BitMap=bm[bmnr];   /* und sie dann durch umschalten */
 ScrollVPort(vp);                /* der     Bitmap's    anzeigen. */
 WaitTOF();                      /* Bildschirm     syncroniesiert */
 bmnr=ANDERE_BMNR;               /* die    2 .  BitMap    löschen.*/
 BltClear((char *)bm[bmnr]->Planes[ LINKES_AUGE],((ulong)bm1.Rows<<16)+bm1.BytesPerRow,2L);
 BltClear((char *)bm[bmnr]->Planes[RECHTES_AUGE],((ulong)bm1.Rows<<16)+bm1.BytesPerRow,2L);
 }


short verschiebe_drehe_und_projeziere()

{register short x,y,z,xx;
          short noch_nicht_benutzt=TRUE,crash=FALSE;

 for ( pkt=anf_pkt ; pkt!=end_pkt ; pkt = pkt-punkt >= MAX_PKT ? punkt : pkt+1 ) /* Für alle Punkte : */
  {
   x = pkt->x - x_pos;            /* verschiebe um pos */
   z = pkt->z - z_pos;
   y = pkt->y - y_pos;

   xx= DREHE_Y( x, z);            /* drehe um Y-Achsa  */
   z = DREHE_Y( z,-x);

   if (z<vz+Z_MIN && z>Z_MIN && xx>-250 && xx<250)
     {switch (y+KOPF)             /* auf Crash testen  */
       {case TONNE     :
        case LEITPLANKE:
        case PFUETZE   :
        case MENSCH    :
        case SPRUNG    :crash=y+KOPF;break;}}

   x =DREHE_Z(xx,  y);            /* drehe um Z_Achse  */
   y =DREHE_Z( y,-xx);

   if (z>Z_MIN)                   /* Berechne Projektion */
      {register long f=z_bild10/z;/* für beide Augen     */
       pkt->x_bild[ LINKES_AUGE]=(short)((x+AUGEN_NASEN_ABSTAND)*f >>11)+xsizeh-AUGEN_NASEN_ABSTAND;
       pkt->x_bild[RECHTES_AUGE]=(short)((x-AUGEN_NASEN_ABSTAND)*f >>11)+xsizeh+AUGEN_NASEN_ABSTAND;
       pkt->y_bild              =(short)((y                 )*f >>11)+ysizeh;
       noch_nicht_benutzt=FALSE;}
   else
      {pkt->y_bild = NICHT_PROJEZIERT;     /* alle unbrauchbaren Punkt */
       if (noch_nicht_benutzt) anf_pkt=pkt;/* wegfallenfassen          */
       }
   }
 return(crash);
 }


void main()

{struct IntuiMessage *mess;
 short fz=0,vy=0;
 float vdy=0;

 open_all();

 for (;;)
  {baue_strasse();

   if (mess=(struct IntuiMessage *)GetMsg(win->UserPort))
    {switch (mess->Class)
      {case CLOSEWINDOW   :   /* Eingaben auswerten */
             case RAWKEY        :switch (mess->Code)
                  {case 0x00:
                   case 0x10:
                   case 0x45:close_all(NULL);
                   case 0x44:
                   case 0x40:x_pos=anf_pkt->x;z_pos=anf_pkt->z;vz=20;break;}
                 break;
       case INACTIVEWINDOW:ReplyMsg((struct Message *)mess);
                           WaitPort(win->UserPort);
                           mess=(struct IntuiMessage *)GetMsg(win->UserPort);
                           break; /* Multitasking freundlich Schlafen */
       case MOUSEBUTTONS  :switch (mess->Code)
                  {case MENUDOWN  :fz=-20;break; /* Geschwinikeit */
                   case SELECTDOWN:fz= 20;break;
       default        :fz=  0;break;}
       }
     ReplyMsg((struct Message *)mess);
     }

   vz+= fz;         /* Geschwindikeit */
   if (vz<0) vz=0;

   vy+=G;
   y_pos+=vy;       /* Springen */
   if (y_pos>KOPF) {y_pos=KOPF;vy=-(vy-G)>>1;}

                    /* Lenken */
   vdy=(vdy+(-sqrt((float)vz)*(win->MouseX-xsizeh)/40000.)) / 2;
   dy+= vdy;
   dz =atan(abs((long)vz)*vz*vdy/-70000.);
   sin_dy=(1<<14)*sin(dy); /* sin/cos werden für das */
   cos_dy=(1<<14)*cos(dy); /* DREHE_Y / X gebraucht  */
   sin_dz=(1<<14)*sin(dz);
   cos_dz=(1<<14)*cos(dz);

   x_pos+=DREHE_Y( 0,-vz); /* neue Posiniton ausrechnen */
   z_pos+=DREHE_Y(vz,  0);

   switch (verschiebe_drehe_und_projeziere())
     {case PFUETZE   : vz=(7*vz)>>3;     break; /* die crashabfragen */
      case MENSCH    : vz=     0;score=0;break;
      case TONNE     :
      case LEITPLANKE: vz= vz>>3;score-=   10;break;
      case SPRUNG    : vy=-vz>>2;score+=vz>>5;break;}

   naechstes_bild();

   }
 }
