/* Fireworks demo written by Dave Ashley */
/* dash@xdr.com */
/* http://www.xdr.com/dash */
/* Sat Jun 13 02:46:09 PDT 1998 */
/* This is my first attempt at an SDL program */
/* See the SDL home page http://www.devolution.com/~slouken/projects/SDL/ */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "SDL.h"

#define XSIZE 240
#define YSIZE 160

SDL_Surface *thescreen;
unsigned char *vmem1, *vmem2;
int mousex,mousey;
SDL_Color themap[256];

int scrlock()
{
	if(SDL_MUSTLOCK(thescreen))
	{
		if ( SDL_LockSurface(thescreen) < 0 )
		{
			fprintf(stderr, "Couldn't lock display surface: %s\n",
								SDL_GetError());
			return -1;
		}
	}
	return 0;
}
void scrunlock(void)
{
	if(SDL_MUSTLOCK(thescreen))
		SDL_UnlockSurface(thescreen);
	SDL_UpdateRect(thescreen, 0, 0, 0, 0);
}

#define MOUSEFRAC 2
#define MAXBLOBS 512
#define BLOBFRAC 6
#define BLOBGRAVITY 5
#define THRESHOLD 20
#define SMALLSIZE 3
#define BIGSIZE 6

#define ABS(x) ((x)<0 ? -(x) : (x))

int explodenum;

char sizes[]={2,3,4,5,8,5,4,3};


struct blob {
	struct blob *blobnext;
	int blobx;
	int bloby;
	int blobdx;
	int blobdy;
	int bloblife;
	int blobsize;
} *blobs,*freeblobs,*activeblobs;


unsigned char **mul640;
int oldmode;

char sqrttab[]={
0,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,
5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,
6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,
11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,
13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15
};


void nomem(void)
{
	printf("Not enough low memory!\n");
	SDL_Quit();
	exit(1);
}



extern Uint32 __FarFunction (Uint32 (*ptr)(), ...);
extern void __FarProcedure (void (*ptr)(), ...);
void fire(Uint32* FuncAddr,unsigned char *p1,unsigned char *p2,int pitch,char *map) __attribute__ ((section(".iwram")));

void fire(Uint32* FuncAddr,unsigned char *p1,unsigned char *p2,int pitch,char *map)
{
int x,y;
unsigned char *p3, *p4;

	for(y=2;y<YSIZE;y++)
	{
		for(x=0;x<XSIZE;x++)
		{
			p3 = p1+y*XSIZE+x;
			p4 = p2+y*pitch+x;
			*p4=map[*p3+p3[-XSIZE]+p3[-XSIZE-1]+p3[-XSIZE+1]+p3[-1]+p3[1]+p3[-XSIZE-XSIZE-1]+p3[-XSIZE-XSIZE]+p3[-XSIZE-XSIZE+1]];
		}
	}
}

void disk(x,y,rad)
{
unsigned char *p;
int i,j,k,aj;
int rad2=rad*rad;
int w;


	for(j=-rad;j<=rad;j++)
	{
		w=sqrttab[rad2-j*j];
		aj=ABS(j)<<2;
		if(w)
		{
			p=mul640[y+j]+x-w;
			k=w+w+1;
			i=-w;
			while(k--) {*p++=255-(ABS(i)<<2)-aj;i++;}
		}
	}
}
void trydisk(void)
{
	if(mousex>10 && mousex<XSIZE-10 && mousey>10 && mousey<YSIZE-10)
		disk(mousex,mousey,8);
}

void addblob(void)
{
int dx,dy;
struct blob *ablob;

	if(!freeblobs) return;
	dx=(rand()&255)-128;
	dy=(rand()%100)+170;
	ablob=freeblobs;
	freeblobs=freeblobs->blobnext;
	ablob->bloblife=(rand()&127)+128;
	ablob->blobdx=dx;
	ablob->blobdy=dy;
	ablob->blobx=(128+(rand()&127))<<BLOBFRAC;
	ablob->bloby=2<<BLOBFRAC;
	ablob->blobnext=activeblobs;
	ablob->blobsize=BIGSIZE;
	activeblobs=ablob;
}
void moveblobs(void)
{
struct blob **lastblob,*ablob;
int x,y;

	lastblob=&activeblobs;
	while(ablob=*lastblob)
	{
		x=ablob->blobx>>BLOBFRAC;
		y=ablob->bloby>>BLOBFRAC;
		if(!--ablob->bloblife || y<0 || x<10 || x>XSIZE-10)
		{
			*lastblob=ablob->blobnext;
			ablob->blobnext=freeblobs;
			freeblobs=ablob;
			continue;
		}
		ablob->blobx+=ablob->blobdx;
		ablob->bloby+=ablob->blobdy;
		ablob->blobdy-=BLOBGRAVITY;
		lastblob=&ablob->blobnext;
	}
}
void putblobs(void)
{
struct blob *ablob,*ablob2,*temp;
int x,y,dy;
int i,size;
long x2,y2,vel;

	ablob=activeblobs;
	activeblobs=0;
	while(ablob)
	{
		dy=ablob->blobdy;
		if(ablob->blobsize!=SMALLSIZE && (dy>-THRESHOLD && dy<THRESHOLD && !(rand()&7) || (rand()&127)==63))
		{
			i=explodenum;
			while(i-- && freeblobs)
			{
				ablob2=freeblobs;
				freeblobs=freeblobs->blobnext;
				ablob2->blobx=ablob->blobx;
				ablob2->bloby=ablob->bloby;
				for(;;)
				{
					x2=(rand()&511)-256;
					y2=(rand()&511)-256;
					vel=x2*x2+y2*y2;
					if(vel>0x3000 && vel<0x10000L) break;
				}
				ablob2->blobdx=ablob->blobdx+x2;
				ablob2->blobdy=ablob->blobdy+y2;
				ablob2->bloblife=16+(rand()&31);
				ablob2->blobsize=SMALLSIZE;
				ablob2->blobnext=activeblobs;
				activeblobs=ablob2;
				ablob->bloblife=1;
			}			
		}
		x=ablob->blobx>>BLOBFRAC;
		y=ablob->bloby>>BLOBFRAC;
		size=ablob->blobsize;
		if(size==BIGSIZE && ablob->blobdy>0 && ablob->blobdy<200)
			size=sizes[ablob->bloblife&7];
		if(x>10 && x<XSIZE-10 && y>10 && y<YSIZE-10)
			disk(x,YSIZE-1-y,size);
		temp=ablob;
		ablob=ablob->blobnext;
		temp->blobnext=activeblobs;
		activeblobs=temp;
	}
}



#define RATE 1
void normal(char *map)
{
int i,j;
	for(i=0;i<8192;i++)
	{
		j=i/9;
		map[i]=j<256 ? (j>=RATE ? j-RATE : 0) : 255;
	}
}
void bright(char *map)
{
int i;
	for(i=0;i<8192;i++) map[i]=i>>3<255 ? (i>>3) : 255;
}

void updatemap(void)
{
	SDL_SetColors(thescreen, themap, 0, 256);
}


void loadcolor(int n,int r,int g,int b)
{
	themap[n].r=r<<2;
	themap[n].g=g<<2;
	themap[n].b=b<<2;
}


void loadcolors(unsigned int which)
{
int i,j;
int r,g,b;

	which%=11;
	for(i=0;i<256;i++)
	{
		switch(which)
		{
		case 0:
			if(i<64) loadcolor(i,0,0,0);
			else if(i<128)	loadcolor(i,i-64,0,0);
			else if(i<192) loadcolor(i,63,i-128,0);
			else loadcolor(i,63,63,i-192);
			break;
		case 1:
			if(i<64) loadcolor(i,0,0,0);
			else if(i<128)	loadcolor(i,0,0,i-64);
			else loadcolor(i,(i-128)>>1,(i-128)>>1,63);
			break;
		case 2:
			loadcolor(i,i>>2,i>>2,i>>2);
			break;
		case 3:
			r=rand()&0x3f;
			g=rand()&0x3f;
			b=rand()&0x3f;
			loadcolor(i,r*i>>8,g*i>>8,b*i>>8);
			break;
		case 4:
			loadcolor(i,i>>2,0,0);
			break;
		case 5:
			loadcolor(i,0,i>>2,0);
			break;
		case 6:
			loadcolor(i,0,0,i>>2);
			break;
		case 7:
			j=i&15;
			if(i&16) j=15-j;
			j=(i>>2)*j/16;
			loadcolor(i,j,j,j);
			break;
		case 8:
			j=0;
			if(i>8 && i<128) j=63;
			loadcolor(i,j,j,j);
			break;
		case 9:
			j=31-(i&31)<<1;
			r=i&32 ? j : 0;
			g=i&64 ? j : 0;
			b=i&128 ? j : 0;
			loadcolor(i,r,g,b);
			break;
		case 10:
			j=(i&15)<<2;
			if(i&16) j=63-j;
			r=i&32 ? j : 0;
			g=i&64 ? j : 0;
			b=i&128 ? j : 0;
			loadcolor(i,r,g,b);
			break;
		}
	}
	updatemap();
}

main(int argc, char *argv[])
{
int i,k;
char *remap,*remap2;
unsigned char *p1, *p2;
long frames;
int flash;
int whichmap;
int key;
int ispaused;
unsigned long videoflags;
int done;
int now;
SDL_Event event;
long starttime;
int buttonstate;

	__debug_shutup=1;
	
	srand(5 /*time(NULL)*/ );
	if ( SDL_Init(SDL_INIT_VIDEO) < 0 )
	{
		fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
		exit(1);
	}
	videoflags = SDL_SWSURFACE|SDL_FULLSCREEN|SDL_HWPALETTE;

	thescreen = SDL_SetVideoMode(XSIZE, YSIZE, 8, videoflags);
	if ( thescreen == NULL )
	{
		fprintf(stderr, "Couldn't set display mode: %s\n",
							SDL_GetError());
		SDL_Quit();
		exit(5);
	}

	vmem1=NULL;
	vmem2=malloc(XSIZE*YSIZE);
	if(!vmem2) nomem();
	mul640=malloc(YSIZE*sizeof(char *));
	if(!mul640) nomem();
	remap=malloc(16384);
	if(!remap) nomem();
	remap2=malloc(16384);
	if(!remap2) nomem();
	blobs=malloc(MAXBLOBS*sizeof(struct blob));
	if(!blobs) nomem();

	puts("Fire demo by David Ashley (dash@xdr.com)");
	puts("1 = Change color map");
	puts("2 = Randomly change color map");
	puts("p = Pause");
	puts("spc = Fire");
	puts("esc = Exit");
	puts("Left mouse button = paint");
	puts("Right mouse button, CR = ignite atmosphere");

	freeblobs=activeblobs=0;
	for(i=0;i<MAXBLOBS;i++)
	{
		blobs[i].blobnext=freeblobs;
		freeblobs=blobs+i;
	}

	normal(remap);
	bright(remap2);


	flash=0;
	whichmap=0;
	loadcolors(whichmap);
	frames=0;
	ispaused=0;
	addblob();
	done = 0;
	now=0;
	starttime=SDL_GetTicks();
	buttonstate=0;
	mousex=mousey=0;

	while(!done)
	{
		if ( scrlock() < 0 ) continue;
		frames++;
		if ( vmem1 != (unsigned char *)thescreen->pixels )
		{
			p1=vmem1=thescreen->pixels;
			for (i=0;i<YSIZE;i++)
			{
				mul640[i]=i*thescreen->pitch+vmem1;
				memset(p1,0,XSIZE);
				p1+=thescreen->pitch;
			}
		}
		if(!ispaused)
		{
			now++;
			if(!flash)
			{
				if(explodenum>96 && explodenum<160 && !(rand()&511) || (buttonstate&8))
					flash=60;
			} else --flash;
			explodenum=(now>>4)+1;if(explodenum==320) now=0;
			if(explodenum>256) explodenum=256;
			if(!(rand()&31))
				addblob();
			moveblobs();
			putblobs();
			if(buttonstate&2) trydisk();
			p1=vmem1;
			p2=vmem2;
			k=thescreen->pitch;
			memcpy(vmem2,vmem1,XSIZE*YSIZE);
			__FarProcedure(fire,vmem2,vmem1,(int)k,flash ? remap2 :remap);
		}
		scrunlock();

		while(SDL_PollEvent(&event))
		{
			switch (event.type)
			{
			case SDL_MOUSEBUTTONDOWN:
			case SDL_MOUSEBUTTONUP:
				if ( event.button.state == SDL_PRESSED )
					buttonstate|=1<<event.button.button;
				else
					buttonstate&=~(1<<event.button.button);
				mousex=event.button.x;
				mousey=event.button.y;
				if(!ispaused && buttonstate&2) trydisk();
				break;
			case SDL_MOUSEMOTION:
				mousex=event.motion.x;
				mousey=event.motion.y;
				if(!ispaused && buttonstate&2) trydisk();
				break;
			case SDL_KEYDOWN:
				key=event.key.keysym.sym;
				if(key==SDLK_RETURN) {flash=60;break;}
				if(key==SDLK_1 || key==SDLK_2)
				{
					if(key==SDLK_1)
						++whichmap;
					else
						whichmap=rand();
					loadcolors(whichmap);
					break;
				}
				if(key==SDLK_ESCAPE) {done=1;break;}
				if(key==SDLK_SPACE && !ispaused) {printf("laber");addblob();break;}
				if(key==SDLK_p) {ispaused=!ispaused;break;}
				break;
			case SDL_QUIT:
				done = 1;
				break;
			default:
				break;
			}
		}
	}

	starttime=SDL_GetTicks()-starttime;
	if(!starttime) starttime=1;
	SDL_Quit();
	printf("fps = %d\n",1000*frames/starttime);
	exit(0);
}
