
/*-------------------------------------------------------------------------*/
/* Porno Shoot'em Up Game               by Mark E. Whitehead  07 July 89   */
/*									   */
/* Okay, it's sleazy, it's immoral; it's one of those things that Oprah's  */
/* army would kill you for - but it's still good for a laugh!  I started   */
/* this game as a simple GUN-SHOOTS-BULLET-AT-TARGET game just to play     */
/* around with Sprites.  It was getting boring so I came up with a way to  */
/* spice it up a bit.  Basically, what happens is this female moves around */
/* on the screen making 'passion sounds' while you, a space penis, fire    */
/* the, uh, bullets, that's right, bullets at the target.  Scoring is      */
/* based upon the distance between the two.  The sound effects by the way  */
/* were not made by my girlfriend (she refused).  Hope you have fun!  MEW  */


#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <graphics/sprite.h>
#include <devices/audio.h>
#include <stdio.h>

#include "pornimages.c"

#define	TXBIAS		-42
#define	TYBIAS		24
#define	GXBIAS		24
#define	GYBIAS		6
#define	TOP		10
#define TBOTTOM		140
#define BOTTOM 		180
#define	LEFT		200
#define	RIGHT		600
#define HITZONE		8
#define GUN_H  		18
#define BULL_H 		6
#define TARG_H 		55
#define ABS(x) 		(x < 0 ? -x : x)
#define	ever		(;;)
#define	REV		0L

#define	BLK		0
#define	WHT		1
#define	BLU		2
#define	GRN		3

#define	SCRNWIDTH	640
#define	SCRNHEIGHT	200
#define	SCRNDEPTH	2
#define	NUMSTARS	20

#define	JS_UP		0
#define	JS_DOWN		1
#define	JS_LEFT		2
#define	JS_RIGHT	3
#define	JS_PASS		4

extern	void	*OpenLibrary(), *GetMsg(), *OpenWindow(), *AllocMem(),
		*OpenScreen(),  *OpenDevice(), *CreatePort();

extern	long	GetSprite();

void	*GfxBase, *IntuitionBase;

struct	Window	 *win;
struct	ViewPort *vp;
struct	RastPort *rp;
struct	Screen	 *scrn;

struct NewWindow windef = {
	0, 0, SCRNWIDTH, 10,
	1, 0,
	CLOSEWINDOW,
	WINDOWCLOSE | BORDERLESS | ACTIVATE,
	NULL, NULL,
	NULL,
	NULL, NULL,
	0, 0, 0, 0,
	CUSTOMSCREEN
};

struct	NewScreen scrndef = {
	0, 0, SCRNWIDTH, SCRNHEIGHT,
	SCRNDEPTH,
	1, 0,
	HIRES | SPRITES, 
	CUSTOMSCREEN,
	NULL, NULL, NULL, NULL
};

	
UWORD	colortable[] = {
	0x0000, 0x0eca, 0x55f, 0x00f0, /* Backgnd */		
	0x005f, 0x0000, 0x0000, 0x0000, /* Unused  */
	0x0000, 0x0000, 0x0000, 0x0000, /* Unused  */	
	0x0000, 0x0000, 0x0000, 0x0000, /* Unused  */
	0x0000, 0x0b64, 0x0e59, 0x0e86, /* Targ    */
	0x0000, 0x0853, 0x0e86, 0x0f48, /* Gun     */
	0x0000, 0x0cbb, 0x0999, 0x0000, /* Bull    */
	0x0000, 0x0b64, 0x0dc0, 0x0e86  /* Targ    */
};

short	gdir = 0, dir = 0, count = 50, explode = FALSE,
	intensity, jiggle = 100, armout = TRUE, score = 0, dx = 0, dy = 1,
	Pbutton, Ydelta;

struct	SimpleSprite	gun1, gun2, targ1, targ2, targ3;

typedef	struct Starpoint {
	short	x,
		y,
		twinkletime;
};

struct	Starpoint	star[NUMSTARS];

typedef	struct	Bull {
	short	inuse, dy;
	struct	SimpleSprite spr;
};

struct	Bull	bull[2];

/*--------------------- A U D I O  S T U F F -----------------------------*/

#define	MOANSND		0
#define	SCREAMSND	1
#define	AAHSND		2
#define	ORGASMSND	3
#define	WONKSND		4

struct	MsgPort	*port = 0;
struct	Device	*device = 0;
struct	IOAudio	audio;

#define	NMAXSAMP 	7
short	n_samples = 0;

struct	Sample { UBYTE	*data;
		 ULONG	length;
		 UWORD 	period,
			volume;
		} samp[NMAXSAMP];

struct	IOAudio	snd;


main()
{
	void	*msg;

	openstuff();
	audiostuff();
	starstuff();
	setupsprites();

	for ever {
		if (msg = GetMsg(win->UserPort) ) {
			ReplyMsg(msg);
			closestuff();
			return;
		}
		twinkle();
		joystick( &gdir, &Pbutton);
		movebullet( &(bull[0]));
		joystick( &gdir, &Pbutton);
		movebullet( &(bull[1]));
		movetarget();
		movegun();
		WaitTOF();
	}
}


/*------------------------------------------------------------------------*/
/* Stars */

twinkle()
{
	short	i, x, y;

	for (i=0; i<NUMSTARS; i++) {
		if (star[i].twinkletime-- < 0) {
			SetAPen( rp, (long) BLK);
			WritePixel( rp, (long) star[i].x, (long) star[i].y); 
			x = RangeRand(639L);
			y = 50 + RangeRand(130L);
			star[i].x = x;
			star[i].y = y;
			SetAPen( rp, (long) RangeRand(4L));
			WritePixel( rp, (long) x, (long) y);
			star[i].twinkletime = RangeRand(50L);
		}
	}
}

	
/*------------------------------------------------------------------------*/
/* Stars */

starstuff()
{
	short	i, x, y;

	SetRast( rp, (long) BLK);	/* Clear screen */

	SetAPen( rp, (long) WHT);
	for (i=0; i<100; i++) {
		x = RangeRand(639L);
		y = RangeRand(200L);
		WritePixel( rp, (long) x, (long) y);
	}

	SetAPen( rp, (long) BLU);
	for (i=0; i<100; i++) {
		x = RangeRand(639L);
		y = RangeRand(200L);
		WritePixel( rp, (long) x, (long) y);
	}

	SetAPen( rp, (long) WHT);
	for (i=0; i<50; i++) {
		x = RangeRand(639L);
		y = RangeRand(200L);
		WritePixel( rp, (long) x, (long) y);
		WritePixel( rp, (long) x+1, (long) y);
	}

	print_rp( "Presenting...", 276, 20, WHT);
	print_rp( "The Revenge of the Amazing Space Bimbo", 168, 30, GRN);
	print_rp( "From the Planet Lacton!", 228, 40, GRN);
	print_rp( "By Marcus", 284, 50, BLU); 
	print_rp( "Score 00000", 276, 190, WHT);

	for (i=0; i<NUMSTARS; i++) {
		x = RangeRand(639L);
		y = 50 + RangeRand(130L);
		star[i].x = x;
		star[i].y = y;
		SetAPen( rp, (long) RangeRand(4L));
		WritePixel( rp, (long) x, (long) y);
		star[i].twinkletime = RangeRand(40L);
	}
}


/*------------------------------------------------------------------------*/
/* Print to rastport */

print_rp( txt, x, y, color)
char	*txt;
short	x, y, color;
{
	SetAPen( rp, (long) color); Move( rp, (long) x, (long) y);
	Text( rp, (long) txt, (long) strlen(txt) );
}
	

/*-----------------------------------------------------------------------*/
/* Convert UWORD into a fixed length string of chars */

#define	NDIGITS		5
#define	TEN_EXP_NDIGITS	10000
char  	 scorebuf[NDIGITS+1];
	
char	*itos(number)
UWORD	number;
{
	short	i;
	UWORD 	f;
	register char	*loc;

	loc = scorebuf;

	if (number > 65000) number = 65000;
	
	for (i=0, f=TEN_EXP_NDIGITS; i<NDIGITS; i++, f=f/10)
		*loc++ = (number/f) % 10 + '0';

	scorebuf[NDIGITS] = NULL;
	return(scorebuf);
}

/*------------------------------------------------------------------------*/
/* Move gun with joystick.  Limit the onscreen position */ 

movegun()
{
	short	gx, gy;

	gx = gun1.x;
	gy = gun1.y;
	
	switch (gdir) {
		case JS_UP:   Ydelta = -1; gy -= 2; break;
		case JS_DOWN: Ydelta =  1; gy += 2; break;
		default: Ydelta = 0;
	}

	if (gy > BOTTOM)
		gy = BOTTOM;
	else
		if (gy < TOP) gy = TOP;

	gun1.y = gy;
	MoveSprite( vp, &(gun1), (long) gx, (long) gy);
	gun2.y = gy;
	MoveSprite( vp, &(gun2), (long) gx + 32, (long) gy)
;
}


/*-------------------------------------------------------------------------*/
/* Read joystick hardware directly and return a joystick direction.  This  */
/* routine handles joystick #2 (the usual one) and does not latch inputs.  */
/* Only if this routine is called at the same time the joystick is active  */
/* will anything besides PASS be returned.                                 */

joystick( cmd, fire)
USHORT	*cmd, *fire;
{
	USHORT	*joy = (USHORT *) 0xdff00c;
	USHORT	*button = (USHORT *) 0xbfe0fe;
	static	short	count = 40;
	static	short	latch = FALSE;

	*cmd = JS_PASS;	*fire = FALSE;

	if (*joy & 0x0001)  *cmd = JS_DOWN;
	if (*joy & 0x0100)  *cmd = JS_UP;
	if (*joy & 0x0002)  *cmd = JS_RIGHT;
	if (*joy & 0x0200)  *cmd = JS_LEFT;
	
	count--;

	if (count > 35) 
		*fire = FALSE;
	else
		if (count < 0) {
			*fire = latch;
			count = 40;
			latch = FALSE;
		} else {
			if (!latch) latch = !(*button & 0x0080);
			*fire = FALSE;
		}
}


/*------------------------------------------------------------------------*/
/* Move bullet across screen */

movebullet(b)
struct	Bull	*b;
{
	if ( b->inuse ) {
		b->spr.x += 8;			/* move fast     */	
		b->spr.y += b->dy;
		if (b->spr.x > RIGHT || b->spr.y > 190 || b->spr.y < 10) {
			b->spr.height = 0;	/* remove sprite */
			b->inuse = FALSE;
			b->dy = 0;
		}
		if ( checkforhit( b->spr.x, b->spr.y) ) {
			b->inuse = FALSE;
			b->spr.height = 0;

			if (targ1.x > 500)
				intensity = 3;		/* triple orgasm!!! */
			else
				if (targ1.x > 350)
					intensity = 2;	/* double orgasm!!  */
				else
					intensity = 1;	/* single orgasm!   */
			score += 5*intensity;
			print_rp( itos(score), 324, 190, WHT);
			explode = TRUE;
		}
	} else
		if (Pbutton) {				/* joystick flag    */
			sound_fx(WONKSND);		/* sound effects!   */
			b->inuse = TRUE;
			b->spr.height = BULL_H;		/* setup sperm      */
			b->spr.x = gun1.x + GXBIAS;	/* adjust position  */
			b->spr.y = gun1.y + GYBIAS;	/* relative to dick */
			b->dy = Ydelta;			/* relative vert vel*/
		}

	MoveSprite(vp, &(b->spr), (long) b->spr.x, (long) b->spr.y);
}


/*----------------------------------------------------------------------*/
/* Check for hit. If within the clitoral (ahem) region, return 1 else 0 */

checkforhit( bx, by)
short	bx, by;
{
	short z1, z2;

	z1 = bx - (targ1.x + TXBIAS);
	z2 = by - (targ1.y + TYBIAS);

	if ( (ABS(z1)<HITZONE)&&(ABS(z2)<HITZONE) ) {
		return(TRUE);
	} else
		return(FALSE);
}


/*-------------------------------------------------------------------------*/  
/* Move target in square circles (!?!). Wave the arms occasionally for     */
/* amusement. If 'explode' is set, then do the Great Orgasmic Convulsion!  */

movetarget()
{
	short	i, tx, periodsave;

	if ( explode ) {
		periodsave = samp[ORGASMSND].period;
		for (i=0; i < intensity; i++) {
			samp[ORGASMSND].period -= i * 30;
			sound_fx( ORGASMSND);
			ChangeSprite (vp, &targ1, targ1b_image);
			ChangeSprite (vp, &targ2, targ2b_image); /* oooh */
			ChangeSprite (vp, &targ3, targ3b_image);
			Delay(15L);
			ChangeSprite (vp, &targ1, targ1a_image);
			ChangeSprite (vp, &targ2, targ2a_image); /* ahhh */
			ChangeSprite (vp, &targ3, targ3a_image);
			Delay(15L);
		}
		samp[ORGASMSND].period = periodsave;
		explode = FALSE;
	} else {
		if (jiggle-- == 0) {
			sound_fx( RangeRand(4L) );
			jiggle = 50 + RangeRand(200L);
			if (armout = !armout)	/* NOT the flag and test */
				ChangeSprite (vp, &targ2, targ2a_image);
			else
				ChangeSprite (vp, &targ2, targ2b_image);
		}
		
		count--;

		if (dx != 0) {
			targ1.x += dx;
			if (targ1.x < LEFT || targ1.x > RIGHT) count = 0;
		} else {
			targ1.y += dy;
			if (targ1.y < TOP || targ1.y > TBOTTOM) count = 0;
		}

		if (count == 0) {
			dir++;
			if (dir > 3) dir = 0;
			dx = 0; dy = 0;
			switch (dir) {
				case 0: dy = -1; break;	
				case 1: dx =  2; break;
				case 2: dy =  1; break;
				case 3: dx = -2; break;
			}
			count = RangeRand(100L) + 20;
		}
	}

	tx = targ1.x;

	MoveSprite(vp, &targ3, (long) (tx - 64), (long) targ1.y);
	MoveSprite(vp, &targ2, (long) (tx - 32), (long) targ1.y);
	MoveSprite(vp, &targ1, (long) (tx), (long) targ1.y);
}


/*-------------------------------------------------------------------------*/
/* Read in some samples and store the vitals in the samp structure. Make   */
/* sure that n_samples is correct 'cause it is used in closestuff() for    */
/* memory deallocation.							   */

audiostuff()
{
	short	i;

	init_audio();		/* Setup the audio device and structure */
	snd = audio;		/* Copy audio struct for each sampled sound */

	read_sample( "moan.snd",   &samp[MOANSND]);
	read_sample( "scream.snd", &samp[SCREAMSND]);
	read_sample( "aah.snd",    &samp[AAHSND]);
	read_sample( "orgasm.snd", &samp[ORGASMSND]);
	read_sample( "wonk.snd",   &samp[WONKSND]);

	n_samples = 5;

	/* Doctor up recorded sounds */

	for (i=0; i<4; i++) samp[i].volume = 25;

	samp[WONKSND].volume = 50;
}


/*-------------------------------------------------------------------------*/
/* Let's hear some sound effects! */

sound_fx( which )
short	which;
{
	if (!samp[which].data)	/* If sound data missing... */	 
		return(); 
	
	AbortIO(&snd);		/* Bag any active sound */

	if (which >= n_samples) which = 0;

	setup_sample(&snd, &samp[which]);

	BeginIO(&snd);		/* This actually requests sound */
}

/*------------------------------------------------------------------------*/
/* Initialize the audio device and audio structure.  Note that the global */
/* variable 'port' and 'audio' are assigned - these will be necessary     */
/* later for cleanup.  This function opens up a reply port for the audio  */
/* device, sets up priority and channels requested, then opens the audio  */
/* device itself.  If anything goes bad we exit via the 'die' routine.    */

init_audio()
{
	UBYTE	sunit=0x0f;	/* All four channels requested - why not? */

	if (!(port = CreatePort("p1",0L)))
		die("Can't create sound port.");

	audio.ioa_Request.io_Message.mn_ReplyPort = port;
	audio.ioa_Request.io_Message.mn_Node.ln_Pri = 10;
	audio.ioa_Data = &sunit;
	audio.ioa_Length = (ULONG) sizeof(sunit);

	if( (OpenDevice(AUDIONAME,0L,&audio,0L)) )
		die("Can't open audio device.");

	device = audio.ioa_Request.io_Device;
}


/*-------------------------------------------------------------------------*/
/* Setup a sampled sound specifying: volume, number of cycles, period, etc.*/

setup_sample(chnl, sample)
struct	IOAudio	*chnl;
struct	Sample	*sample;
{
	chnl -> ioa_Request.io_Command = CMD_WRITE;
	chnl -> ioa_Request.io_Flags   = ADIOF_PERVOL | IOF_QUICK;
	chnl -> ioa_Data   = sample -> data;
	chnl -> ioa_Cycles = 1;
	chnl -> ioa_Length = sample -> length;
	chnl -> ioa_Period = sample -> period;
	chnl -> ioa_Volume = sample -> volume;
}


/*------------------------------------------------------------------------*/
/* This function reads in a particular kind of 8SVX sampled sound.  Just  */
/* pass it file name and a Sample structure and it'll fill in the fields. */

read_sample( name, sample)
UBYTE	*name;
struct	Sample	*sample;
{
	FILE	*fp, *fopen();
	UBYTE	c, c0, c1, c2, c3, *count, *samp_data;
	void	*AllocMem();
	UWORD	i, length, rate, period;

	if ( (fp = fopen( name, "r")) == NULL ) {
		sample -> data = NULL;
		return();
	}

	for (i=0; i<136; i++) {
		c = getc(fp);
		switch (i) {
			case 32 : c0 = c; break;
			case 33 : c1 = c; break;
			case 134: c2 = c; break;
			case 135: c3 = c; break;
			default : break;
		}
	}

	rate   = (UWORD) c0 << 8 | (UWORD) c1;
	length = (UWORD) c2 << 8 | (UWORD) c3;

	if (!(samp_data = AllocMem( (long) length, MEMF_CHIP)))
		die("Memory allocation error.");

	count = samp_data;
	for ( i = 0; i < length; i++)		/* Read sound data */ 
		*count++ = getc(fp);		/* into buffer     */

	fclose(fp);

	/* Convert samples per second to amiga hardware period value */

	period = (35795465L/rate)/10;

	/* Now assign parameters to the appropriate sample fields */

	sample -> data   = samp_data;
	sample -> length = length;
	sample -> period = period;
	sample -> volume = 64;

}


/*-------------------------------------------------------------------------*/
/* Set up the sprites */
 
setupsprites()
{
	short	gx = 40, gy = 100, bx = 0, by = 200, tx = 400, ty = 50;

	make_sprite( &targ2, targ2a_image,  1, tx, ty,    TARG_H);
	make_sprite( &gun1,  gun1_image,    2, 40, gy,    GUN_H);
	make_sprite( &gun2,  gun2_image,    3, gx+32, gy, GUN_H);
	make_sprite( &(bull[0].spr),  bull0_image, 4, bx, by, BULL_H);
	make_sprite( &(bull[1].spr),  bull1_image, 5, bx, by, BULL_H);
	make_sprite( &targ1, targ1a_image,  6, tx+64, ty, TARG_H);
	make_sprite( &targ3, targ3a_image,  7, tx+32, ty, TARG_H);

	bull[0].inuse = FALSE; 	bull[1].inuse = FALSE;
}


/*-------------------------------------------------------------------------*/
/* Get a requested sprite and initialize position */

make_sprite( sprite, src, n, x, y, h)
struct	SimpleSprite	*sprite;
UWORD	*src;
short	n, x, y, h;
{
	if (GetSprite( sprite, (long) n) < 0) die("Sprite not available.");

	sprite->x = x;
	sprite->y = y;
	sprite->height = h;

	ChangeSprite( vp, sprite, src);
}


/*-------------------------------------------------------------------------*/
/* Deallocate all sprites */

free_sprites()
{
	if (bull[0].spr.num)  FreeSprite( (long) bull[0].spr.num );
	if (bull[1].spr.num)  FreeSprite( (long) bull[1].spr.num );
	if (gun1.num)  FreeSprite( (long) gun1.num );
	if (gun2.num)  FreeSprite( (long) gun2.num );
	if (targ1.num) FreeSprite( (long) targ1.num );
	if (targ2.num) FreeSprite( (long) targ2.num );
	if (targ3.num) FreeSprite( (long) targ3.num );
}


/*-------------------------------------------------------------------------*/
/* Open up system libraries and stuff */

openstuff()
{
	if (!(IntuitionBase = OpenLibrary ("intuition.library", REV)))
		die ("Intuition doesn't 'open up' to me.");

	if (!(GfxBase = OpenLibrary ("graphics.library", REV)))
		die ("Art shop closed.");

	if (!(scrn = OpenScreen (&scrndef)))
		die ("Screen painted shut.");

	windef.Screen = scrn;

	if (!(win = OpenWindow( &windef)))
		die ("Window stuck tight.");

	rp = &(scrn -> RastPort);
	vp = &(scrn -> ViewPort);

	LoadRGB4( vp, &colortable[0], 32L);

	print_rp( "One Moment Please, You Sleaze...", 192, 100, WHT);
}


/*-------------------------------------------------------------------------*/
/* Close what we opened, deallocate what we allocated */

closestuff()
{
	short	i;

	AbortIO(&snd);

	free_sprites();

	if (win)
		CloseWindow(win);
	if (scrn)
		CloseScreen(scrn);
	if (device)
		CloseDevice(&audio);
	if (port)
		DeletePort(port);
	for (i=0; i<n_samples; i++)
		if (samp[i].data != NULL)
			FreeMem( samp[i].data, (long) samp[i].length);
	if (GfxBase)
		CloseLibrary (GfxBase);
	if (IntuitionBase)
		CloseLibrary (IntuitionBase);
}


/*-------------------------------------------------------------------------*/
/* Post a complaint on the way out */

die(str)
char *str;
{
	puts(str);
	closestuff();
	exit (100);
}
