/*

   PLASMA.C - Written by Phil Inch for Game Developers Magazine (Issue 4)

   This is actually a translation of a Pascal version which I found on
   a local BBS, but regrettably no author details were included so I am
   unable to credit the original author.

   I have tried to re-use the same variable and function names in the hope
   that someone recognises the code and can let me know who the author was.

   This version, as was the original Pascal version, is contributed to the
	 public domain.

   This program written and compiled with Borland C++ v3.1
   Compatibility with other compilers is not guaranteed.

   Usage of this program is subject to the disclaimer printed
   in the magazine.  You assume all risks associated with the use
   of this program.

*/


#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <dos.h>
#include <time.h>
#include "vga.h"

#define MAX_PALETTE 192
#define MAX_X 319
#define MAX_Y 199

unsigned char palette[MAX_PALETTE][3];

const double F = 4.0;

void adjust(int xa, int ya, int x, int y, int xb, int yb ) {
	unsigned char a,b;
  int d;
  double r,v;

  GetPoint(x,y,&a); if (a) return;

  r = (double)rand()/RAND_MAX - 0.5;
  d = abs(xa-xb)+abs(ya-yb);

  GetPoint(xa,ya,&a);
  GetPoint(xb,yb,&b);

  v = (a+b)/2+d*r*F;

  if ( v < 1 ) v = 1;
  if ( v > MAX_PALETTE ) v = MAX_PALETTE;

  SetPoint( x, y, (unsigned char)v );
}

void split_box( int x1, int y1, int x2, int y2 ) {
  unsigned char c1, c2, c3, c4;
  int diffx, diffy;
  int x,y;

	diffx = x2-x1;
  diffy = y2-y1;
  if ( diffx < 2 && diffy < 2 ) return;

  x = (x1+x2)/2;
  y = (y1+y2)/2;

  adjust(x1,y1,x,y1,x2,y1);
  adjust(x2,y1,x2,y,x2,y2);
  adjust(x1,y2,x,y2,x2,y2);
  adjust(x1,y1,x1,y,x1,y2);

  GetPoint( x, y, &c1 );
  if ( c1 == 0 ) {
	  GetPoint( x1, y1, &c1 );	/* top left */
	  GetPoint( x2, y1, &c2 );  /* top right */
	  GetPoint( x1, y2, &c3 );  /* bot left */
	  GetPoint( x2, y2, &c4 );  /* bot right */
    SetPoint( x, y, (c1+c2+c3+c4)/4 );
    }

  split_box(x1,y1,x,y);
  split_box(x,y1,x2,y);
  split_box(x,y,x2,y2);
  split_box(x1,y,x,y2);
}

void set_palette(void) {
	unsigned char c;

  for ( c = 0; c < 64; c++ ) {
    palette[c][0] = c;
    palette[c][1] = 63-c;
    palette[c][2] = 0;

    palette[c+64][0] = 63-c;
    palette[c+64][1] = 0;
    palette[c+64][2] = c;

    palette[c+128][0] = 0;
    palette[c+128][1] = c;
    palette[c+128][2] = 63-c;
    }
  SetBlock( 1, &palette[0][0], MAX_PALETTE );
}

void main(void) {
  int c;
  char c1, c2, c3, c4;

	SetGraphicsMode();
  set_palette();
 	randomize();

  /* Set the starting points */
  SetPoint( 0, 0, 1+(rand()%MAX_PALETTE) );
  SetPoint( MAX_X, 0, 1+(rand()%MAX_PALETTE) );
  SetPoint( 0, MAX_Y, 1+(rand()%MAX_PALETTE) );
  SetPoint( MAX_X, MAX_Y, 1+(rand()%MAX_PALETTE) );

  /* Generate the plasma pattern */
  split_box( 0, 0, MAX_X, MAX_Y );

  /* Cycle the palette until a key is pressed */
  while ( !kbhit() ) {
    c1 = palette[0][0];
    c2 = palette[0][1];
    c3 = palette[0][2];
    for ( c = 0; c < MAX_PALETTE-1; c++ ) {
			palette[c][0] = palette[c+1][0];
			palette[c][1] = palette[c+1][1];
			palette[c][2] = palette[c+1][2];
      }
    palette[MAX_PALETTE-1][0] = c1;
    palette[MAX_PALETTE-1][1] = c2;
    palette[MAX_PALETTE-1][2] = c3;
	  SetBlock( 1, &palette[0][0], MAX_PALETTE );
    delay(10);
    }

  SetTextMode();
}
