/* All real-time graphic effects for LSDino are housed in this file. */

#include "lsdino.h"

xyrec *makebox(void)
{
	int x,y;
        xyrec *xy;

        xy=malloc(lenx*leny*sizeof(xyrec));
	if(xy!=NULL){
		for(y=0;y<leny;y++){
                        rotate_dac();
                        update_dac();
			for(x=0;x<lenx;x++){
                                xy[y*lenx+x].xoff=((x*320)/lenx) + ((y*200)/leny)*320; 
				xy[y*lenx+x].multiplier=1;
			}
		}
	}else{
                notenoughmem("bouncing boxes");
	}
	return(xy);
}

#define BOUNCE 0
#define GRAVITY 1
void dobox(void)
{
        int update,clrlp,xcart,ycart,x,y,xmove,ymove,i,yclen,xyloc,stop,ballmovetype;
        int maxy,maxx,ixc,maxvvel;
        unsigned char *mask,*fullsave;
        register int xc,yc;
        xyrec *xy;
        char *clr;

        xy=makebox();
        fullsave=malloc(64000);
        if(fullsave==NULL){
              notenoughmem("a copy of the screen (64000 bytes)");
        }
        fmemcpy(fullsave,screenbuffer,64000);
        ballmovetype=rand()&1;

        switch(ballmovetype){
                case BOUNCE:   // ball bounces off of sides of screen
                        xmove=((rand()&0x01)?1:-1);
                        ymove=((rand()&0x01)?1:-1);
                        x=rand()%(320-lenx-10);
                        y=rand()%(200-leny-5);
                        break;
                case GRAVITY:  // ball bounces back and forth w/ gravity.
                        xmove=((rand()&0x01)?2:-2);;
                        maxvvel=rand()%10+7;
                        ymove=maxvvel;
                        x=rand()%(320-lenx-10);
                        y=200-leny;
                        break;
        }
	  
        mask=masks[rand()%NUMMASKS];

	/* from here on down we don't call a SINGLE subroutine.  This is 
	due to the terrible inefficencys of calling subroutines.  Any one 
	with any knowledge of assembly knows this, and we want our code as
	fast as possible.  I tried to document it to an understandable level*/
         for(i=0;i<600 && !kbhit();i++){
            waitretrace();

            if(ballmovetype==GRAVITY){
                    if(y>(200-leny)){
                           ymove=maxvvel;
                           y=(200-leny);
                    }else if(x<=0){
                           xmove*=-1;
                           x=0;
                    }else if(x>(320-lenx)){
                           xmove*=-1;
                           x=320-lenx;
                    }
            }
		/* Draw our window in the screen */
                    yc=0;
		    while(yc<leny){
                                ycart=abs(20-yc);
				yclen=(y+yc)*320;
				xc=0;
				while(xc<lenx){
                                          xcart=abs(25-xc);
					  xyloc=xy[yc*lenx+xc].xoff;
                                          if((xcart*xcart+ycart*ycart)<20*20)
                                                  *(screenbuffer+yclen+xc+x)=*(screenbuffer+xyloc) & mask[yc*lenx+xc];
					  xc++;
				}
				yc++;
		    }

                    /* Now we erase the edges, need to determine max box size */

                    if((y-(abs(ymove)+3))>=0)
                        yc = -(abs(ymove)+3);
                    else
                        yc = -y;        
                    if((y+abs(ymove)+3+leny)<200)
                        maxy=abs(ymove)+leny+3;
                    else
                        maxy=(200-y);
                    if((x-(abs(xmove)+5))>=0)
                        ixc = -(abs(xmove)+5);
                    else
                        ixc = -x;        
                    if((x+abs(xmove)+5+lenx)<320)
                        maxx=abs(xmove)+3+lenx;
                    else      
                        maxx=(320-x);

                    while(yc<maxy){
                                ycart=abs(20-yc);
				yclen=(y+yc)*320;
                                xc=ixc;
                                while(xc<maxx){
                                          xcart=abs(25-xc);
                                          if((xcart*xcart+ycart*ycart)>400)
                                                  *(screenbuffer+yclen+xc+x)=*(fullsave+yclen+xc+x);
					  xc++;
				}
				yc++;
		    }

#if 0
		    if(xmove>0) /* moving to right, restore left edge */
				xc=x;
		    else /* moving left, so we need to restore the right edge */
				xc=x+lenx-1;
		    yc=y;
		    stop=y+leny;
		    while(yc<=stop){
				yclen=t320(yc)+xc;
				*(screenbuffer+yclen)=*(fullsave+yclen);
				yc++;
		    }

		    if(ymove>0) /* moving down, restore top line */
				yc=y*320;
		    else /* moving up, so we need to restore the bottom line */
				yc=(y+leny-1)*320; 
		    xc=x;
		    stop=lenx+x;
		    while(xc<=stop){
				yclen=yc+xc++;
				*(screenbuffer+yclen)=*(fullsave+yclen);
		    }

#endif
		/* Now, we need to see if we hit a corner */
		    if((x==0 && xmove==-1) || (x==(320-lenx) && xmove==1)){
			xc=319;
			if(x==0)
				xc=0;             
			xmove*=-1;
			for(yc=y;yc<(y+leny);yc++)
					  *(screenbuffer+yc*320+xc)=*(fullsave+yc*320+xc);             
                    }
		    if((y==0 && ymove==-1) || (y==(200-leny) && ymove==1)){
				yc=199*320;
			if(y==0)
				yc=0;
			for(xc=x;xc<(x+lenx);xc++)
					  *(screenbuffer+yc+xc)=*(fullsave+yc+xc);             
			ymove*=-1;
                    }                
                switch(ballmovetype){
                        case BOUNCE:
                                x+=xmove; y+=ymove;
                                break;
                        case GRAVITY:
                                x+=xmove;
                                y-=ymove;
                                ymove-=1;
                                break;

                }
		/* We're done now, we'll either continue doing more loops
			like this or we'll be completely done */
		rotate_dac();
		update_dac();
        }        
/* We're done bouncing, so now we move the position of the box to where it
   really is on the screen so we can fade it out properly. */

        switch(ballmovetype){
                case BOUNCE:
                        x-=xmove; y-=ymove;
                        break;
                case GRAVITY:
                        x-=xmove;
                        y+=ymove+1;
                        break;
        }

/* This code will fade the box into the original image that was there before. */
/* It starts by fading the outside first, and then works it's way into the */
/* inside */
         update=1;
         i=1; /* i here represents the max we can go in from the edge. */
             /* ie, how far into the box we're allowed to update so far */
         while(update){
                update=0;
                rotate_dac();
                update_dac();
                if(i<26)
                        i++;
                for(yc=0;yc<leny;yc++){
                        for(xc=0;xc<lenx;xc++){
                                xyloc=(yc+y)*320+xc+x;
                                if( (xc<i) || (lenx-xc)<i || (yc<i) || (leny-yc)<i){  
                                        if( ((*(screenbuffer+xyloc))&0xFF) > ((*(fullsave+xyloc))&0xFF) ){ 
                                                *(screenbuffer+xyloc) -= 1;
                                                update=1;
                                        }
                                        if( ((*(screenbuffer+xyloc))&0xFF) < ((*(fullsave+xyloc))&0xFF) ){
                                                *(screenbuffer+xyloc) += 1;
                                                update=1;
                                        }
                                 }                                                 
                        }                                
                }
         }
#ifdef __SC__
	  fmemcpy(screenbuffer,fullsave,64000);
#else
	  memcpy(screenbuffer,fullsave,64000);
#endif
	  free(xy);
	free(fullsave);
}

/* Here's how this table look up thing works.  There are so many 
different levels of distortion (ACID_LEN).  Inside each of these levels 
there is an array 320*sizeof(short) length long.  This array stores a 
location.  Normally the location would be y*320+x, but this is a 
distorted location to do trippy looks.  So, to convert from (x,y) to a 
distorted (newx,newy) we do something like this:

newx = acidtablex[distortionlevel*320 + x]
newy = acidtabley[distortionlevel*320 + y]

Then we take the (newx,newy) locations from the old screen, and plot them 
at (x,y) on the current screen, this creates the sense of distortion.

The distortion can easily be changed by altering the formula used to 
create the distortion table.
*/

/* ACIDLEVEL determines how intense the visuals will be.  A lower value */
/* indicates increased visuals.  Highest setting recommend is 7, lowest  */
/* setting is probably a 4 but a 3 is possible (just isn't quite as good */
/* looking in _MY_ opinion */

#define ACIDLEVEL 4

void make_acid_table(int type)
{
     static int made=0;
     unsigned int i,n,n2;
     int zx,zy;
	
		
     if(made==type && type!=7)
	    return;
     if(!made){
	     acidtable=(unsigned short *)calloc(320*(ACID_LEN+3),sizeof(short));
             acidtabley=(unsigned short *)calloc(200*(ACID_LEN+3),sizeof(short));
     }
     switch(type){
        case 7:
                zx=rand()%290;
                zy=rand()%180;
                break;
     }
     
     if(acidtable==NULL || acidtabley==NULL){
             notenoughmem("acid table (apx 100k)");             
     }
     made=type;
             for(n=1;n<=ACID_LEN && !kbhit();n++){
                  rotate_dac();
                  update_dac();
		  for(i=0;i<320;i++){
			 switch(type){
                                 case 4:
				 case 1:
					 n2=(n/3)+1;
					 if(i<200){
						 if((i+abs(i%n2-n2/2)/ACIDLEVEL)>199)
                                                        acidtabley[n*200+i]=199*320;
						 else
                                                        acidtabley[n*200+i]=(i+abs(i%n2-n2/2)/ACIDLEVEL)*320;        
					 }
					 if((i+abs(i%n2-n2/2)/ACIDLEVEL) >319)
						acidtable[n*320+i]=319;
					 else
                                                acidtable[n*320+i]=(i+abs(i%n2-n2/2)/ACIDLEVEL);
					 break;
				case 2:
					acidtable[n*320+i]=i;
					if(i<200){
						if(i<(n) || i>(200-(n)))
                                                        acidtabley[n*200+i]=200*320;
						else
                                                        acidtabley[n*200+i]=((i-(n))*200)/((200-n*2)+1)*320;
					}
					break;      
				case 3:
					acidtable[n*320+i]=( (abs(costable[i])*319/256)*n + i*(ACID_LEN-n) )/(ACID_LEN+2);
					if(i<200)
                                                acidtabley[n*200+i]=( (abs(sintable[i])*199/256)*n + i*(ACID_LEN-n) )/(ACID_LEN+2)*320;
					break;
                                case 7:                          
                                        if(n>100)
                                                return;
                                        acidtable[n*320+i]=((sqrttable[i]+zx)*n+i*(100-n))/100;
                                        if(i<200)
                                                acidtabley[n*200+i]=((sqrttable[i]+zy)*n+i*(100-n))/100*320;          
                                        break;
			}
		  }
             }                        
    
}

int RADIUS=0;
#define STARTX (160-RADIUS)
#define ENDX (160+RADIUS)
#define STARTY (100-RADIUS)
#define ENDY (100+RADIUS)


#if 0
/* This code has been copied down below into realtime_acid, so that it'll
run a little faster (calling functions is quite time consuming, you know.
I keep it here so you can study it in it's 'original', isolated form...*/
void do_acid(int n,unsigned char *scr)
{         
	   register int x,z,ny,n320;
           int y=STARTY,n200,t;

	   
	   n320=320*n;
           n200=200*n;
           while(y<ENDY){
		   z=t320(y); 
                   ny=acidtabley[n200+y];  
                   x=STARTX;
                   while(x<ENDX){                             
			     screenbuffer[z+x]=(scr[ny+acidtable[n320+x]]+(n))|0x01;
			     x++;
		   }
		   y++;
	   }
}
#endif


void realtime_acid(int type)
{
     int n,length,cx,cy,nocirclemode=rand()&0x3;
     unsigned char *scr;
     int xcen,ycen;
     register int x,z,ny,n320;
     int y=0,n200;
     unsigned short *xtable,*ytable,*xtabledflt,*ytablestop,*xtablestop;
     char far *screen;

/* Current types of Acid are as follows, and it shows their weightings:
	  4,1 - Realtime Acid  (THE ORIGINAL!)
	  3     - Distortion
	  2     - Compression
*/
     make_acid_table(type);       
     switch(type){
        case 7: length=100;       break;
	case 2: length=100;       break; /* Verical Compression */
	default: length=ACID_LEN; break;  
     }
     scr=(unsigned char *)malloc(64961);
     if(scr==NULL)
        notenoughmem("screen copy (64961 bytes)");
#ifdef __SC__
     fmemcpy(scr,screenbuffer,64000);
#else
     memcpy(scr,screenbuffer,64000);
#endif
     memset(scr+64000,0,960);


     if(!nocirclemode){
             /* Circle Acid mode */
             n=1;
             RADIUS=0;
                   
                
             while(n<length && !kbhit()){
                  {                 
                             y=STARTY;

                             n320=320*n;
                             n200=200*n;
                             while(y<ENDY){
                                     cy=abs(100-y);
                                     z=t320(y);
                                     ny=acidtabley[n200+y]; 
                                     x=STARTX;
                                     while(x<ENDX){
                                                 cx=abs(160-x);
                                                 if((cx*cx+cy*cy)<RADIUS*RADIUS)
                                                         screenbuffer[z+x]=(scr[ny+acidtable[n320+x]])|0x01;
                                                 x++;
                                     }
                                     y++; /* CHanged from y++, update every other line */
                             }
                  }                     
                  n++;
                  if(n&1)
                          RADIUS++;
             }          
             while(n>0 && !kbhit()){
                  {         
                             y=STARTY;

                             n320=320*n;
                             n200=200*n;
                             while(y<(ENDY+2)){
                                     cy=abs(100-y);
                                     z=t320(y); 
                                     ny=acidtabley[n200+y]; 
                                     x=STARTX;
                                     while(x<(ENDX+2)){
                                                 cx=abs(160-x);
                                                 if((cx*cx+cy*cy)<RADIUS*RADIUS)
                                                         screenbuffer[z+x]=(scr[ny+acidtable[n320+x]])|0x01;
                                                 else
                                                         screenbuffer[z+x]=scr[z+x];
                                                 x++;
                                     }
                                     y++;
                             }
                  }             
                  n--;
                  if(n&1)
                          RADIUS--;
             }
     }else{
             /* Acid mode */
             n=1;
             while(n<length && !kbhit()){
                  n320=t320(n);
                  ytable=acidtabley+200*n;
                  ytablestop=ytable+200;
                  xtabledflt=acidtable+320*n;
                  xtablestop=xtabledflt+320;
                  screen=screenbuffer;

                  while(ytable<ytablestop){
                          xtable=xtabledflt;
                          while(xtable<xtablestop){
                                  *screen++ = (scr[*ytable+*xtable]+n)|0x01;
                                  xtable++;
                          }
                          ytable++;
                  }
                  n++;
             }          
             while(n>0 && !kbhit()){
                  n320=t320(n);
                  ytable=acidtabley+200*n;
                  ytablestop=ytable+200;
                  xtabledflt=acidtable+320*n;
                  xtablestop=xtabledflt+320;
                  screen=screenbuffer;

                  while(ytable<ytablestop){
                          xtable=xtabledflt;
                          while(xtable<xtablestop){
                                  *screen++ = (scr[*ytable+*xtable]+n)|0x01;
                                  xtable++;
                          }
                          ytable++;
                  }
                  n--;
             }          
     }



#ifdef __SC__
     fmemcpy(screenbuffer,scr,64000);
#else
     memcpy(screenbuffer,scr,64000);
#endif
     free(scr);
}

