Quest of Love is an Amiga project that I never finished, but always wanted to create…

The project started as an assembly language project, but it was too much work, so I switched to C language. The video below shows, what I managed to do:

For curiosity and historical — on the other hand educational — reasons, I’ve decided to publish the C language source I have for this project. The assembly version is published in this old blog post..

The comments and names of some variables are in Finnish..

#include <math.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/displayinfo.h>
#include <graphics/modeid.h>
#include <intuition/intuition.h>
#include <dos/stdio.h>
#include <dos/dos.h>
#include <proto/exec.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <libraries/iff.h>

#define INTUI_V38_NAMES_ONLY

#define LIBVER 39

#define TURN 83

struct IntuitionBase *IntuitionBase=NULL;
struct GfxBase *GfxBase=NULL;
struct Library *IFFBase=NULL;

struct Screen *screen=NULL;
struct Window *window=NULL;

struct ViewPort *viewport;
struct RastPort *rastport;

ULONG   errorcode;

unsigned char *BM_block=NULL, *BM0, *BM1;
unsigned char *source0, *source1, *source2, *source3, *source4;
unsigned char *dest0, *dest1, *dest2, *dest3, *dest4;

struct BitMap blockbitmap = {2, 1088, 0, 5, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
struct BitMap myBitMap0 = {40, 256, 0, 5, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
struct BitMap myBitMap1 = {40, 256, 0, 5, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
struct BitMap *DrawBitMap=NULL,*ShowBitMap=NULL;

struct Monster {
    int x,y;
    WORD block;
    WORD vital;
    WORD exp;
    char frame;
    char delay;
    char elossa;
};

struct Monster Monsters[21];
struct Monster MonType[1];

int Blocked(int x, int y, short suunta);
void    DoubleBuffering(short *);
void    DrawMap(UWORD *Map);
void    DrawBlock(int,int,int);
void    Cycle(void);
void  HandleMonsters(void);
void  InitMonsters(void);
void    SetDelay(WORD,WORD);
void  DeAllocateResources();
char  ReadMapFile(char [], UWORD *buffer, int lkm);

int cind, block, k, j;
UWORD colortable[32];
UWORD copytable[4];
UWORD *buffer=NULL;
LONG  count;

UWORD *Map = NULL;
UWORD *WorldMap = NULL;
int   map_width, map_heigth;

WORD MapX=0, MapY=0,isoX=0,isoY=0;
char delay=-1;
char turn=TURN;



int main(void) {

    short   keepgoing=TRUE;
    struct  IntuiMessage *msg;
    ULONG       class;
    UWORD       code,qualifier;
    IFFL_HANDLE iff=NULL;
    short       *Which;

    if((GfxBase=(struct GfxBase *)
    OpenLibrary("graphics.library",LIBVER))==NULL)
    {
      printf("graphics open failed (V39 kickstart required)\n");
      DeAllocateResources();
      exit(1);
    }

    if((IntuitionBase=(struct IntuitionBase *)
    OpenLibrary("intuition.library",LIBVER))==NULL)
    {
      printf("intuition open failed (V39 mkickstart required)\n");
      DeAllocateResources();
      exit(1);
    }

    IFFBase=(struct Library*)OpenLibrary(IFFNAME, IFFVERSION);
    if (IFFBase == NULL) {
      printf("Can not open iff.library\n");
      DeAllocateResources();
      exit(1);
    }
    
    BM_block = (unsigned char *)AllocMem(11520, MEMF_CHIP);
    if (!BM_block) {
      printf("Memory allocation failed\n");
      DeAllocateResources();
      exit(1);
    }
    
    BM0 = (unsigned char *)AllocMem(51200,MEMF_CHIP|MEMF_CLEAR);
    if (!BM0) {
      printf("Memory allocation failed\n");
      DeAllocateResources();
      exit(1);
    }
     
    BM1 = (unsigned char *)AllocMem(51200,MEMF_CHIP|MEMF_CLEAR);
    if (!BM1) {
      printf("Memory allocation failed\n");
      DeAllocateResources();
      exit(1);
     }

    buffer=(UWORD *)AllocMem(40, MEMF_CHIP);
    if (!buffer) {
      printf("Memory allocation failed\n");
      DeAllocateResources();
      exit(1);
    }
       
    InitMonsters();

    blockbitmap.Planes[0]=&BM_block[0];
    blockbitmap.Planes[1]=&BM_block[2176];
    blockbitmap.Planes[2]=&BM_block[4352];
    blockbitmap.Planes[3]=&BM_block[6528];
    blockbitmap.Planes[4]=&BM_block[8704];

    myBitMap0.Planes[0]=&BM0[0];
    myBitMap0.Planes[1]=&BM0[10240];
    myBitMap0.Planes[2]=&BM0[20480];
    myBitMap0.Planes[3]=&BM0[30720];
    myBitMap0.Planes[4]=&BM0[40960];

    myBitMap1.Planes[0]=&BM1[0];
    myBitMap1.Planes[1]=&BM1[10240];
    myBitMap1.Planes[2]=&BM1[20480];
    myBitMap1.Planes[3]=&BM1[30720];
    myBitMap1.Planes[4]=&BM1[40960];

    *Which=1;

    if((screen=OpenScreenTags(NULL,
        SA_Width, 320,
        SA_Height, 256,
        SA_Depth, 5,
        SA_Overscan, FALSE,
        SA_AutoScroll, TRUE,
        SA_Type, CUSTOMSCREEN|CUSTOMBITMAP,
        SA_DisplayID,PAL_MONITOR_ID,
        SA_PubName, "My Screen",
        SA_BitMap, &myBitMap0,
        TAG_END, NULL)
    )==NULL)
    {
        printf("Screen open failed\n");
      DeAllocateResources();
      exit(1);
    }

    if((window=OpenWindowTags(NULL,
        WA_Left, 0,
        WA_Top, 0,
        WA_Width, screen->Width,
        WA_Height, screen->Height,
        WA_IDCMP,IDCMP_VANILLAKEY,
        WA_Flags,WFLG_ACTIVATE|WFLG_RMBTRAP|WFLG_BORDERLESS,
        WA_ReportMouse, TRUE,
        WA_MinWidth,    50,
        WA_MinHeight,   20,
        WA_MaxWidth,    ~0,
        WA_MaxHeight,   ~0,
        WA_PubScreen,   screen,
        TAG_END, NULL)
    )==NULL)
    {
        printf("Window open failed\n");
        DeAllocateResources();
        exit(1);
    }

    viewport=&screen->ViewPort;
    rastport=window->RPort;

/* Bittikartoille on aiemmin varattu tila, mikä pitää laskea uudestaan, jos koko muuttuu */

    iff = IFFL_OpenIFF("Work:MyProjects/QoL/New/gfx/blocks16x1152.iff",IFFL_MODE_READ);
    if (!iff) {
      printf("IFF file open failed\n");
      DeAllocateResources();
      exit(1);
    }
  
    if (!(IFFL_DecodePic(iff, &blockbitmap))) {
      printf("IFF file decoding failed\n");
      DeAllocateResources();
      exit(1);
    }
 
    count = IFFL_GetColorTab(iff, colortable);
    LoadRGB4(&(screen->ViewPort), colortable, count);
    IFFL_CloseIFF(iff);

    iff = IFFL_OpenIFF("Work:MyProjects/QoL/IFFs/NewFrame.iff",IFFL_MODE_READ);
    if (!iff) {
      printf("IFF file open failed\n");
      DeAllocateResources();
      exit(1);
    }
    
    if (!(IFFL_DecodePic(iff,&myBitMap0))) {
      printf("IFF file decoding failed\n");
      DeAllocateResources();
      exit(1);
    }
       
    IFFL_CloseIFF(iff);

    for (int i=0; i<4; i++) copytable[i]=colortable[16+i];     BltBitMap(&myBitMap0,0,0,&myBitMap1,0,0,320,256,0xc0,0x1f,&buffer);     source0 = blockbitmap.Planes[0];     source1 = blockbitmap.Planes[1];     source2 = blockbitmap.Planes[2];     source3 = blockbitmap.Planes[3];     source4 = blockbitmap.Planes[4];     dest0 = screen->BitMap.Planes[0]+1001;
    dest1 = screen->BitMap.Planes[1]+1001;
    dest2 = screen->BitMap.Planes[2]+1001;
    dest3 = screen->BitMap.Planes[3]+1001;
    dest4 = screen->BitMap.Planes[4]+1001;

   WorldMap=(UWORD *)AllocMem(9920,MEMF_PUBLIC);
   if (WorldMap == NULL) {
      printf("Could not allocate memory for WorldMap\n");
      DeAllocateResources();
      exit(1);
   }
   
   if ((ReadMapFile("Work:Myprojects/QoL/Gamefield/Map11.gamefield",WorldMap,9920)) == 1) {
      DeAllocateResources();
      exit(1);
   }
    
   Map = WorldMap;
   map_width = 80;
   map_heigth = 62;

/* -------------------------------------------------------------------------- */
                                      
    while(keepgoing)
    {
        WaitTOF();
        DoubleBuffering(&Which);
        DrawMap(Map);
        HandlePlayer();
        HandleMonsters();
        GeneralRoutines();
        Cycle();
/*      printf("Delay %d ; Turn %d\n",delay,turn); */

/*      printf("Blocked %d\n",Blocked(MapX, MapY, 8)); */

        while(msg=(struct IntuiMessage *)GetMsg(window->UserPort))
       {
        class=msg->Class;
            code=msg->Code;
            qualifier=msg->Qualifier;
            ReplyMsg((struct Message *)msg);
/*          printf("%d\n",code); */

            switch(class) {
                case IDCMP_VANILLAKEY :
                     switch (code) {
                        case 27 : /* ESC */
                                    keepgoing=FALSE;
                                    DeAllocateResources();
                                    break;

/***
* Pelaajan ohjaus pääkartalla. Aluksi katsotaan, onko kulkusuunta blokattu;
* jos ei ole ja delay == -1 (eli maastotyypin viive on suoritettu), niin
* liikutaan
* painettuun kulkusuuntaan ja asetetaan uusi delay. Lisäksi tulostetaan
* joko kulkusuunta tai "Slow progress", jos viiveen takia kulkusuuntaan
* ei voinut mennä. Jos tulostettiin "Slow progress", niin delay-arvoa
* vähennetään yhdellä.

***/
                        case '2' : if (!Blocked(MapX, MapY, 2) && delay<0) {                                             MapY++;                                             isoY++;                                             if (MapY> map_heigth - 1) MapY=0;
                                            SetDelay(MapX,MapY);
                                            Print("South.",6);
                                     } else if (delay>-1) {
                                          Print("Slow progress.",14);
                                          delay--;
                                       }
                                    turn=TURN;
                                    break;

                        case '8' : if (!Blocked(MapX, MapY, 8) && delay<0) {
                                            MapY--;
                                            isoY--;
                                            if (MapY<0) MapY= map_heigth - 1;                                             SetDelay(MapX,MapY);                                             Print("North.",6);                                      } else if (delay>-1) {
                                          Print("Slow progress.",14);
                                          delay--;
                                       }
                                    turn=TURN;
                                    break;

                        case '6' : if (!Blocked(MapX, MapY, 6) && delay<0) {                                             MapX++;                                             isoX++;                                             if (MapX> map_width - 1) MapX=0;
                                            SetDelay(MapX,MapY);
                                            Print("East.",5);
                                        } else if (delay>-1) {
                                             Print("Slow progress.",14);
                                             delay--;
                                          }
                                    turn=TURN;
                                    break;

                        case '4' : if (!Blocked(MapX, MapY, 4) && delay<0) {
                                            MapX--;
                                            isoX--;
                                            if (MapX<0) MapX= map_width - 1;                                             SetDelay(MapX,MapY);                                             Print("West.",5);                                         } else if (delay>-1) {
                                             Print("Slow progress.",14);
                                             delay--;
                                    }
                                    turn=TURN;
                                    break;

                        case ' ' :  turn=TURN;
                                    Print("Pass.",5);
                                    break;
                }
          }

       }
    }
} /* END main */
 
void DeAllocateResources() {
    if (buffer) FreeMem(buffer,40);
/*  if (iff) IFFL_CloseIFF(iff); */
/*  if (BM0) FreeMem(BM0,51200);
    if (BM1) FreeMem(BM1,51200); */
    if (BM_block) FreeMem(BM_block,11520);
    if (window) CloseWindow(window);
    if (screen) CloseScreen(screen);
    if (BM0) FreeMem(BM0,51200);
    if (BM1) FreeMem(BM1,51200);
    if (GfxBase) CloseLibrary((struct Library *)GfxBase);
    if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
    if (IFFBase) CloseLibrary(IFFBase);
   if (WorldMap) FreeMem(WorldMap,9920);
}

void HandlePlayer(void) {
    DrawBlock(5,6,10);
}


void SetDelay(WORD x,WORD y) {
    x+=5; y+=6;
    if (x > map_width - 1) x=x - map_width;
    if (y > map_heigth - 1) y=y - map_heigth;
/*  printf("%d;%d; %d\n",x,y,Map[x+y*80]); */

    switch(Map[x+y*map_width]) {
      case 5 : delay=(char)Random(0,4); break;
      case 16: delay=(char)Random(0,2); break;
      default: delay=-1;
    }
/*  printf("%d\n",delay); */
}

void DoubleBuffering(short *Which) {
    switch(*Which) {
        case 0 : viewport->RasInfo->BitMap=&myBitMap0; *Which=1;
                    rastport->BitMap=&myBitMap1;
                    dest0 = myBitMap1.Planes[0]+1001;
                    dest1 = myBitMap1.Planes[1]+1001;
                    dest2 = myBitMap1.Planes[2]+1001;
                    dest3 = myBitMap1.Planes[3]+1001;
                    dest4 = myBitMap1.Planes[4]+1001;
                    DrawBitMap=&myBitMap1;
                    ShowBitMap=&myBitMap0;
                    break;
        case 1 : viewport->RasInfo->BitMap=&myBitMap1; *Which=0;
                    rastport->BitMap=&myBitMap0;
                    dest0 = myBitMap0.Planes[0]+1001;
                    dest1 = myBitMap0.Planes[1]+1001;
                    dest2 = myBitMap0.Planes[2]+1001;
                    dest3 = myBitMap0.Planes[3]+1001;
                    dest4 = myBitMap0.Planes[4]+1001;
                    DrawBitMap=&myBitMap0;
                    ShowBitMap=&myBitMap1;
                    break;
    }
    ScrollVPort(viewport);
}


/***
* Blocked-rutiini: Parametreina saadaan näkymän vasen yläkulma, mihin lisätään
* arvot, joilla saadan pelaajan sijainti eli näkymän keskikohta. Jos suunnassa,
* johon ollaan menossa on blocki, jonka päälle ei voi mennä palautetaan 1,
* mikä on merkkinä siitä, että hahmoa ei liikuteta, muutoin 0.
***/
int Blocked (int x, int y, short suunta) {
    int arvo;

    x+=5; y+=6;

    if (x > map_width - 1) x=x-map_width;
    if (y> map_heigth - 1) y=y- map_heigth;

/*  printf("Block: %d\n", Map[x+y*map_width]); */

    switch (suunta) {
        case 2 : y+=1; break;
        case 8 : y-=1; break;
        case 6 : x+=1; break;
        case 4 : x-=1; break;
    }

/*  printf("Block: %d\n", Map[x+y*map_width]); */

    switch (Map[x+y*map_width]) {
        case 3 :        arvo = 1; break;
        case 8 :        arvo = 1; break;
        case 12 :   arvo = 1; break;
        case 14 :   arvo = 1; break;
        default :   arvo = 0;
    }
    return arvo;
}


void DrawMap (UWORD *Map) {

/*  printf("DrawMap"); */

    int x,y,xg=0,yg=0,xx,yy;

    for (y=MapY,yg=0;y<MapY+14;y++,yg++) {
        for (x=MapX,xg=0;x<MapX+11;x++,xg++) { /*          printf("xx: %d", xx); */             if (x > map_width - 1) xx=x-map_width; else xx=x;
            if (y> map_heigth - 1) yy=y-map_heigth; else yy=y;

            if (x > map_width - 1 || y>map_heigth - 1) block = Map[xx+yy*map_width];
            else block = Map[x+y*map_width];

            block = block << 5;

            j=yg*640;
            for (int i=block; i<32+block; i++) {
                k=xg*2;
                dest0[k+j]=source0[i];
                i++; k++;
                dest0[k+j]=source0[i];
                j+=40;
            }

            j=yg*640;
            for (int i=block; i<32+block; i++) {
                k=xg*2;
                dest1[k+j]=source1[i];
                i++; k++;
                dest1[k+j]=source1[i];
                j+=40;
            }
    
            j=yg*640;
            for (int i=block; i<32+block; i++) {
                k=xg*2;
                dest2[k+j]=source2[i];
                i++; k++;
                dest2[k+j]=source2[i];
                j+=40;
            }

            j=yg*640;
            for (int i=block; i<32+block; i++) {
                k=xg*2;
                dest3[k+j]=source3[i];
                i++; k++;
                dest3[k+j]=source3[i];
                j+=40;
            }

            j=yg*640;
            for (int i=block; i<32+block; i++) {
                k=xg*2;
                dest4[k+j]=source4[i];
                i++; k++;
                dest4[k+j]=source4[i];
                j+=40;

            }

        }
    }
}


/* Koordinaatit blockeina */

void DrawBlock (int x, int y, int block) {

    block = block << 5;             /* kerrotaan 32:lla */

    j=y*640;                                /* kerrottaan 40 x 16:a */
    for (int i=block; i<32+block; i++) {
        k=x*2;
        dest0[k+j]=source0[i];
        i++; k++;
        dest0[k+j]=source0[i];
        j+=40;
    }

    j=y*640;
    for (int i=block; i<32+block; i++) {
        k=x*2;
        dest1[k+j]=source1[i];
        i++; k++;
        dest1[k+j]=source1[i];
        j+=40;
    }
    
    j=y*640;
    for (int i=block; i<32+block; i++) {
        k=x*2;
        dest2[k+j]=source2[i];
        i++; k++;
        dest2[k+j]=source2[i];
        j+=40;
    }

    j=y*640;
    for (int i=block; i<32+block; i++) {
        k=x*2;
        dest3[k+j]=source3[i];
        i++; k++;
        dest3[k+j]=source3[i];
        j+=40;
    }

    j=y*640;
    for (int i=block; i<32+block; i++) {
        k=x*2;
        dest4[k+j]=source4[i];
        i++; k++;
        dest4[k+j]=source4[i];
        j+=40;
    }

}

void Cycle (void) {
    int Rs = cind;

    for (int i=0; i<4; i++, Rs++) {         if (Rs > 3) Rs=0;
        colortable[i+16]=copytable[Rs];
    }
    cind++;
    if (cind > 3) cind = 0;

    LoadRGB4(&(screen->ViewPort), colortable, count);
}

void Print(char teksti[], int pituus) {
    ScrollRaster(rastport,0,8,195,119,300,245);
    Move(rastport,195,243);
    Text(rastport,teksti,pituus);
    BltBitMap(DrawBitMap,195,119,ShowBitMap,195,119,105,127,0xc0,0x1f,NULL);
}

char BlockedMonster(WORD x, WORD y, WORD count) {
    char i;

    for (i=0; i<21; i++) {
        if (Monsters[i].x==x && count!=i && Monsters[i].elossa==1)
            if (Monsters[i].y==y) return 1;
    }

    switch (Map[x+y*map_width]) {
        case 3 : return 1;
        case 8 : return 1;
        case 12 : return 1;
        case 14 : return 1;
        default : return 0;
    }

}

void HandleMonsters(void) {
    int i,apuX,apuY,RS;

    if (turn==TURN) {
        for(i=0; i<21; i++) {             if (Monsters[i].elossa) { /* 75% TN:llä lähdetään liikkeelle */                 RS=Random(0,4);                 if (RS>=1) {
                    apuX=Monsters[i].x;
                    apuY=Monsters[i].y;
                    RS=Random(0,2);
                    if (RS==0) {
                        if (isoX+5>apuX) apuX++; 
                        else apuX--; 
                        if (!BlockedMonster(apuX, apuY, i)) Monsters[i].x=apuX;
                    } else if (isoY+6>apuY) apuY++;
                         else apuY--; 
                    if (!BlockedMonster(apuX,apuY, i)) Monsters[i].y=apuY;
                }

            }
        }
    }

/* Laskeskellaan, piirretäänkö monsteria näkyviin */

    for(i=0; i<21; i++) {
        apuX=Monsters[i].x-isoX;
        apuY=Monsters[i].y-isoY;
        Monsters[i].frame=Monsters[i].block+Random(0,4);
        if (apuX<=10 && apuX>=0)
            if (apuY<=13 && apuY>=0) {
/*              printf("%d;%d\n",apuX,apuY); */
                DrawBlock(apuX,apuY,Monsters[i].frame);
            }
    }
}

void InitMonsters (void) {
    short type;

    MonType[0].block=64;
    MonType[0].vital=10;
    MonType[0].exp=10;
    MonType[0].delay=0;
    MonType[0].frame=64;

    for (int i=0; i<21; i++) {
        Monsters[i].x=Random(0,map_width);
        Monsters[i].y=Random(0,map_heigth);
        switch (Map[Monsters[i].x+Monsters[i].y*map_width]) {
            case 3,8,12,14 : Monsters[i].elossa=0; break;
            default : Monsters[i].elossa=1;
        }
            
        Monsters[i].block=64;
        type=Monsters[i].block;
        Monsters[i].vital=MonType[type-64].vital;
        Monsters[i].exp=MonType[type-64].exp;
        Monsters[i].delay=0;
        Monsters[i].frame=0;
    }

}


void GeneralRoutines() {
    turn--;
    if (turn<=0) { 
        delay--;
/*        printf("%d\n",delay); */
        if (delay<-1) delay=-1;
        turn=TURN;
        Print("Pass.",5);
    }

}

char ReadMapFile(char nimi[], UWORD *buffer, int koko) {
   BPTR file;
   LONG length;

/*   printf("%s\n",nimi); */

   file=Open(nimi,MODE_OLDFILE);
   length=Read(file,buffer,koko);

   if (length != koko) {
      printf("Error in reading mapfile\n");
      Close(file);
      return 1;
   }
   return 0;
}

int Random (int alaraja, int ylaraja) {
    int Luku;
    Luku=rand();
    Luku=alaraja+Luku%(ylaraja-alaraja);
    return Luku;
}
Advertisements