/*
   ================================================================
   SPRITE PLACEMENT GUN
   ================================================================

   Copyright (C) 1998 by 2015, Inc.
   All rights reserved.

   This source is may not be distributed and/or modified without
   expressly written permission by 2015, Inc.

   The purpose of this device is to allow level designers to
   actually walk through their levels and place sprite decals
   wherever they want.

   All sprites automatically align themselves to the surface that
   they are placed onto.

   All changes made to parameters affect the current sprite selected.

   In the below list, $ indicated a parameter is needed

   List of console commands used for the Sprite Gun
   ------------------------------------------------
   spg_save			: Saves current sprites to the map's sprite (msf) file
   spg_load			: Reloads the sprite file. Good for checking timming offsets.
   spg_file $			: Set which sprite to use (no file extension assumes a decal)
   spg_glow			: Toggle glowing for current sprite
   spg_hitwater		: Set to non-zero to make it collision detect water surfaces
   spg_next			: Select the next sprite in the level
   spg_previous		: Select the previous sprite in the level
   spg_delete			: Delete the currently selected sprite
   spg_scale $			: Set sprite scale to use
   spg_alpha $			: Set sprite alpha to use
   spg_frame $			: Set sprite frame to use
   spg_nextframe		: Advance sprite to next frame
   spg_prevframe		: Advance sprite to previous frame
   spg_movesize $		: Sets the amount a sprite is moved when nudged
   spg_moveright		: Nudge the current sprite to its right
   spg_moveleft		: Nudge the current sprite to its left
   spg_moveup			: Nudge the current sprite to its up
   spg_movedown		: Nudge the current sprite to its down
   spg_moveforward		: Nudge the current sprite forward
   spg_moveback		: Nudge the current sprite backwards
   spg_setpitch $		: Set the current sprite's pitch
   spg_setyaw $		: Set the current sprite's yaw
   spg_setroll $		: Set the current sprite's roll
   spg_rotsize $		: sets the free rotation increment
   spg_pitchup			: Pitch the current sprite up
   spg_pitchdown		: Pitch the current sprite down
   spg_yawright		: Yaw the current sprite clockwise
   spg_yawleft			: Yaw the current sprite counter-clockwise
   spg_rollright		: Roll the current sprite clockwise
   spg_rollleft		: Roll the current sprite counter-clockwise
   spg_get				: Gets all settings from the current sprite
   spg_give			: Gives the current sprite all of the current settings
   spg_clear			: Clears all current sprite settings.
   spg_flip			: flip the surface normal used by the sprite
   spg_animspeed $		: Specifies the amount of time between each frame of animation.
                      0 makes it not animate, and a negative value specifies the
                      animation speed without making it start out animating.
                      No specified value shows the current sprite's set value.
   spg_animdelay $		: Delay before the sprite starts animating when the level loads.
   spg_pitchspeed $	: Gives the sprite a constant pitch speed. 0 for no pitching.
                      No specified value shows the current sprite's set value.
   spg_yawspeed $		: Gives the sprite a constant yaw speed. 0 for no yawing.
                      No specified value shows the current sprite's set value.
   spg_rollspeed $		: Gives the sprite a constant roll speed. 0 for no rolling.
                      No specified value shows the current sprite's set value.
   spg_rotdelay $		: Delay before the sprite starts rotating when the level loads.
   spg_blinkspeed $	: Makes the sprite blink at the rate specified. 0 for no blinking.
                      No specified value shows the current sprite's set value.
   spg_blinkdelay $	: Delay before the sprite starts blinking when the level loads.
   spg_targetname $	: Sets the targetname value for the current sprite
                      No specified value shows the current sprite's set value.
   spg_triggereffect $	: Sets the triggering effect for the current sprite
                      The following is the list of accaptable paramaters
                      "none" : Sets the sprite do to nothing when triggered
                      "toggle" : toggls the sprite's animation on/off when triggered
                      "advance" : Makes the sprite advance one frame when triggered
                      "once" : makes the sprite animate through once when triggered
 */

#include "spritegun.h"
#include <fstream.h>
#include "archive.h"
#include <strstrea.h>

/*===================================================================

   PlacedSprite : event setup stuff, here for the SpriteMaster's use

   ===================================================================*/

CLASS_DECLARATION( Entity, PlacedSprite, "placedsprite" );

Event EV_PlacedSprite_Animate( "placedsprite_animate" );
Event EV_PlacedSprite_Rotate( "placedsprite_rotate" );
Event EV_PlacedSprite_Blink( "placedsprite_blink" );

ResponseDef PlacedSprite::Responses[] =
{
	{&EV_PlacedSprite_Animate, (Response)PlacedSprite::Animate},
	{&EV_PlacedSprite_Rotate, (Response)PlacedSprite::Rotate},
	{&EV_PlacedSprite_Blink, (Response)PlacedSprite::Blink},
	{&EV_Activate, (Response)PlacedSprite::TriggerEvent},
	{NULL, NULL}
};

/*===================================================================

   SpriteMaster : Main setup stuff

   ===================================================================*/

CLASS_DECLARATION( Entity, SpriteMaster, NULL );

Event EV_SpriteMaster_Load( "spg_load", EV_CHEAT );
Event EV_SpriteMaster_Save( "spg_save", EV_CHEAT );
Event EV_SpriteMaster_File( "spg_file", EV_CHEAT );
Event EV_SpriteMaster_Glow( "spg_glow", EV_CHEAT );
Event EV_SpriteMaster_Glow_Flicker( "spg_glowflicker" );
Event EV_SpriteMaster_HitWater( "spg_hitwater", EV_CHEAT );
Event EV_SpriteMaster_Next( "spg_next", EV_CHEAT );
Event EV_SpriteMaster_Previous( "spg_previous", EV_CHEAT );
Event EV_SpriteMaster_Delete( "spg_delete", EV_CHEAT );
Event EV_SpriteMaster_Scale( "spg_scale", EV_CHEAT );
Event EV_SpriteMaster_Alpha( "spg_alpha", EV_CHEAT );
Event EV_SpriteMaster_Frame( "spg_frame", EV_CHEAT );
Event EV_SpriteMaster_NextFrame( "spg_nextframe", EV_CHEAT );
Event EV_SpriteMaster_PrevFrame( "spg_prevframe", EV_CHEAT );
Event EV_SpriteMaster_MoveSize( "spg_movesize", EV_CHEAT );
Event EV_SpriteMaster_MoveRight( "spg_moveright", EV_CHEAT );
Event EV_SpriteMaster_MoveLeft( "spg_moveleft", EV_CHEAT );
Event EV_SpriteMaster_MoveUp( "spg_moveup", EV_CHEAT );
Event EV_SpriteMaster_MoveDown( "spg_movedown", EV_CHEAT );
Event EV_SpriteMaster_MoveForward( "spg_moveforward", EV_CHEAT );
Event EV_SpriteMaster_MoveBack( "spg_moveback", EV_CHEAT );
Event EV_SpriteMaster_SetPitch( "spg_setpitch", EV_CHEAT );
Event EV_SpriteMaster_SetYaw( "spg_setyaw", EV_CHEAT );
Event EV_SpriteMaster_SetRoll( "spg_setroll", EV_CHEAT );
Event EV_SpriteMaster_RotSize( "spg_rotsize", EV_CHEAT );
Event EV_SpriteMaster_PitchUp( "spg_pitchup", EV_CHEAT );
Event EV_SpriteMaster_PitchDown( "spg_pitchdown", EV_CHEAT );
Event EV_SpriteMaster_YawRight( "spg_yawright", EV_CHEAT );
Event EV_SpriteMaster_YawLeft( "spg_yawleft", EV_CHEAT );
Event EV_SpriteMaster_RollRight( "spg_rollright", EV_CHEAT );
Event EV_SpriteMaster_RollLeft( "spg_rollleft", EV_CHEAT );
Event EV_SpriteMaster_Get( "spg_get", EV_CHEAT );
Event EV_SpriteMaster_Give( "spg_give", EV_CHEAT );
Event EV_SpriteMaster_Clear( "spg_clear", EV_CHEAT );
Event EV_SpriteMaster_Flip( "spg_flip", EV_CHEAT );
Event EV_SpriteMaster_AnimSpeed( "spg_animspeed", EV_CHEAT );
Event EV_SpriteMaster_AnimDelay( "spg_animdelay", EV_CHEAT );
Event EV_SpriteMaster_PitchSpeed( "spg_pitchspeed", EV_CHEAT );
Event EV_SpriteMaster_YawSpeed( "spg_yawspeed", EV_CHEAT );
Event EV_SpriteMaster_RollSpeed( "spg_rollspeed", EV_CHEAT );
Event EV_SpriteMaster_RotDelay( "spg_rotdelay", EV_CHEAT );
Event EV_SpriteMaster_BlinkSpeed( "spg_blinkspeed", EV_CHEAT );
Event EV_SpriteMaster_BlinkDelay( "spg_blinkdelay", EV_CHEAT );
Event EV_SpriteMaster_Targetname( "spg_targetname", EV_CHEAT );
Event EV_SpriteMaster_TriggerEffect( "spg_triggereffect", EV_CHEAT );

ResponseDef SpriteMaster::Responses[] =
{
	{&EV_SpriteMaster_Load, (Response)SpriteMaster::LoadSpriteFile},
	{&EV_SpriteMaster_Save, (Response)SpriteMaster::SaveSpriteFile},
	{&EV_SpriteMaster_File, (Response)SpriteMaster::SetSprite},
	{&EV_SpriteMaster_Glow, (Response)SpriteMaster::ToggleGlow},
	{&EV_SpriteMaster_Glow_Flicker, (Response)SpriteMaster::GlowFlicker},
	{&EV_SpriteMaster_HitWater, (Response)SpriteMaster::ToggleHitWater},
	{&EV_SpriteMaster_Next, (Response)SpriteMaster::Next},
	{&EV_SpriteMaster_Previous, (Response)SpriteMaster::Previous},
	{&EV_SpriteMaster_Delete, (Response)SpriteMaster::Delete},
	{&EV_SpriteMaster_Scale, (Response)SpriteMaster::SetScale},
	{&EV_SpriteMaster_Alpha, (Response)SpriteMaster::SetAlpha},
	{&EV_SpriteMaster_Frame, (Response)SpriteMaster::SetFrame},
	{&EV_SpriteMaster_NextFrame, (Response)SpriteMaster::NextFrame},
	{&EV_SpriteMaster_PrevFrame, (Response)SpriteMaster::PrevFrame},
	{&EV_SpriteMaster_MoveSize, (Response)SpriteMaster::SetMoveSize},
	{&EV_SpriteMaster_MoveRight, (Response)SpriteMaster::MoveRight},
	{&EV_SpriteMaster_MoveLeft, (Response)SpriteMaster::MoveLeft},
	{&EV_SpriteMaster_MoveUp, (Response)SpriteMaster::MoveUp},
	{&EV_SpriteMaster_MoveDown, (Response)SpriteMaster::MoveDown},
	{&EV_SpriteMaster_MoveForward, (Response)SpriteMaster::MoveForward},
	{&EV_SpriteMaster_MoveBack, (Response)SpriteMaster::MoveBack},
	{&EV_SpriteMaster_SetPitch, (Response)SpriteMaster::SetPitch},
	{&EV_SpriteMaster_SetYaw, (Response)SpriteMaster::SetYaw},
	{&EV_SpriteMaster_SetRoll, (Response)SpriteMaster::SetRoll},
	{&EV_SpriteMaster_RotSize, (Response)SpriteMaster::SetRotateSize},
	{&EV_SpriteMaster_PitchUp, (Response)SpriteMaster::PitchUp},
	{&EV_SpriteMaster_PitchDown, (Response)SpriteMaster::PitchDown},
	{&EV_SpriteMaster_YawRight, (Response)SpriteMaster::YawRight},
	{&EV_SpriteMaster_YawLeft, (Response)SpriteMaster::YawLeft},
	{&EV_SpriteMaster_RollRight, (Response)SpriteMaster::RollRight},
	{&EV_SpriteMaster_RollLeft, (Response)SpriteMaster::RollLeft},
	{&EV_SpriteMaster_Get, (Response)SpriteMaster::GetSettings},
	{&EV_SpriteMaster_Give, (Response)SpriteMaster::GiveSettings},
	{&EV_SpriteMaster_Clear, (Response)SpriteMaster::ClearSettings},
	{&EV_SpriteMaster_Flip, (Response)SpriteMaster::FlipSprite},
	{&EV_SpriteMaster_AnimSpeed, (Response)SpriteMaster::SetAnimSpeed},
	{&EV_SpriteMaster_AnimDelay, (Response)SpriteMaster::SetAnimDelay},
	{&EV_SpriteMaster_PitchSpeed, (Response)SpriteMaster::SetPitchSpeed},
	{&EV_SpriteMaster_YawSpeed, (Response)SpriteMaster::SetYawSpeed},
	{&EV_SpriteMaster_RollSpeed, (Response)SpriteMaster::SetRollSpeed},
	{&EV_SpriteMaster_RotDelay, (Response)SpriteMaster::SetRotDelay},
	{&EV_SpriteMaster_BlinkSpeed, (Response)SpriteMaster::SetBlinkSpeed},
	{&EV_SpriteMaster_BlinkDelay, (Response)SpriteMaster::SetBlinkDelay},
	{&EV_SpriteMaster_Targetname, (Response)SpriteMaster::SetTargetname},
	{&EV_SpriteMaster_TriggerEffect, (Response)SpriteMaster::SetTriggerEffect},
	{NULL, NULL}
};

SpriteMaster Spritecontrol;

SpriteMaster::SpriteMaster()
{
	num_sprites = 0;
	spritename = "sprites/blastmark.spr";
	place_scale = 1;
	place_alpha = 1;
	place_animspeed = 0;
	place_animdelay = 0;
	place_pitchspeed = 0;
	place_yawspeed = 0;
	place_rollspeed = 0;
	place_rotdelay = 0;
	place_blinkspeed = 0;
	place_blinkdelay = 0;
	place_trigeffect = SEFFECT_NONE;

	curr_sprite = 0;
	glowcurrent = false;
	hitwater = false;
	movesize = 8;
	rotsize = 5;
}

/*===================================================================
   SpriteMaster : Sprite Index loading stuff
   ===================================================================*/

// pointer to the frame index list
sf_t *sf_index = NULL;
int sf_num = 0;

void SpriteMaster::ParseSpriteIndex( void )
{
	char tmpchar[128];
	str indexfile;
	int j;
	size_t length;
	char *buffer;

	sf_num = -1;

	indexfile = "decals/decals.lst";

	gi.dprintf( "using decal list: %s\n", indexfile.c_str());

	length = gi.LoadFile( indexfile.c_str(), (void **)&buffer, 0 );
	if(length == (size_t)(-1))
	{
		gi.dprintf( "Couldn't find decal list: %s\n", indexfile.c_str());
		return;
	}

	// put the data buffer into an easy to use class
	istrstream Data( buffer, (int)length );

	// init the list
	Data >> sf_num;
	sf_index = new sf_t[sf_num];

	// start making the list
	j = 0;
	while(j < sf_num)
	{
		Data >> tmpchar;
		sf_index[j].model = tmpchar;
		Data >> sf_index[j].frames;
		j++;
		if(Data.eof()) // no more index entries, just stop then
		{
			gi.TagFree((void *)buffer );
			return;
		}
	}

	gi.TagFree((void *)buffer );
}

/*===================================================================
   SpriteMaster : Sprite File loading stuff
   ===================================================================*/

void SpriteMaster::LoadSpriteFile( Event *ev )
{
	// get the file name to use for the sprite file
	SetFileName();
	// parse the sprite index before loading in the sprite file
	ParseSpriteIndex();
	// creat the sprites from the file
	ParseSpriteFile();
}

// finds where the map file is and then tries to load the
// sprite file from there. If it finds it, it sets filename.
void SpriteMaster::SetFileName( void )
{
	filename = "maps\\";
	filename += level.mapname;
	filename += ".msf";
	gi.dprintf( "Sprite filename set to: %s\n", filename.c_str());
}

void SpriteMaster::ParseSpriteFile( void )
{
	int i, num;
	PlacedSprite *sprite;
	Vector tmpvec;
	char tmpchar[128];
	size_t length;
	char *sf_buffer;

// first, clear out any existing sprites
	num = 0;
	for(i = 0; i < num_sprites; i++)
	{
		num = G_FindClass( num, "placedsprite" );
		if(!num) // ran out of sprite entities
			break;
		sprite = (PlacedSprite *)G_GetEntity( num );

		sprite->PostEvent( EV_Remove, 0.1 );
	}

	length = gi.LoadFile( filename.c_str(), (void **)&sf_buffer, 0 );
	if(length == (size_t)(-1))
	{
		gi.dprintf( "Couldn't find sprite file: %s\n", filename.c_str());
		return;
	}

	// put the data buffer into an easy to use class
	istrstream Data( sf_buffer, (int)length );

	// get the number of sprites to read in
	// read in the number of sprites
	Data >> num_sprites;

	for(i = 0; i < num_sprites; i++)
	{
		sprite = new PlacedSprite;

		sprite->setMoveType( MOVETYPE_NONE );
		sprite->setSolidType( SOLID_NOT );

		Data >> tmpchar;

		sprite->setModel( tmpchar );
		sprite->numframes = NumFrames( tmpchar );
		sprite->setSize( "0 0 0", "0 0 0" );

		Data >> tmpvec.x >> tmpvec.y >> tmpvec.z;
		sprite->setOrigin( tmpvec );

		Data >> tmpvec.x >> tmpvec.y >> tmpvec.z;
		sprite->setAngles( tmpvec );
		sprite->placedangles = tmpvec;

		Data >> sprite->placedframe;
		sprite->edict->s.frame = sprite->placedframe;
		Data >> sprite->edict->s.scale;
		Data >> sprite->edict->s.alpha;
		if(sprite->edict->s.alpha < 1) // it's transparent
			sprite->edict->s.renderfx |= RF_TRANSLUCENT;

		Data >> tmpchar;
		if(stricmp( tmpchar, "notargetname" ))
			sprite->SetTargetName( tmpchar );

		Data >> tmpchar;
		if(!stricmp( tmpchar, "toggle" ))
			sprite->triggereffect = SEFFECT_TOGGLE;
		else if(!stricmp( tmpchar, "advance" ))
			sprite->triggereffect = SEFFECT_ADVANCE;
		else if(!stricmp( tmpchar, "once" ))
			sprite->triggereffect = SEFFECT_ONCE;
		else
			sprite->triggereffect = SEFFECT_NONE;

		Data >> sprite->animatespeed;
		Data >> sprite->animatedelay;
		if(sprite->animatespeed > 0)
		{
			if(sprite->animatedelay > 0)
				sprite->PostEvent( EV_PlacedSprite_Animate, sprite->animatespeed + sprite->animatedelay );
			else
				sprite->PostEvent( EV_PlacedSprite_Animate, sprite->animatespeed );
		}

		Data >> sprite->blinkspeed;
		Data >> sprite->blinkdelay;
		if(sprite->blinkspeed)
		{
			if(sprite->blinkdelay > 0)
				sprite->PostEvent( EV_PlacedSprite_Blink, sprite->blinkspeed + sprite->blinkdelay );
			else
				sprite->PostEvent( EV_PlacedSprite_Blink, sprite->blinkspeed );
		}

		Data >> sprite->pitchspeed >> sprite->yawspeed >> sprite->rollspeed;
		Data >> sprite->rotatedelay;
		if(sprite->pitchspeed || sprite->yawspeed || sprite->rollspeed)
		{
			if(sprite->rotatedelay > 0)
				sprite->PostEvent( EV_PlacedSprite_Rotate, 0.1 + sprite->rotatedelay );
			else
				sprite->PostEvent( EV_PlacedSprite_Rotate, 0.1 );
		}

		//setup for when glowing is turned on
		sprite->edict->s.color_r    = 1;
		sprite->edict->s.color_g    = 1;
		sprite->edict->s.color_b    = 1;
		sprite->edict->s.radius     = 150;

		sprite->link();

		if(Data.eof()) // ran out of data too soon
		{
			gi.TagFree((void *)sf_buffer );
			gi.error( "Premature EOF in the sprite file %s\n", filename.c_str());
			return;
		}

		// set current sprite
		curr_sprite = sprite->entnum;
	}

	gi.dprintf( "Sprite file loaded\n" );

	// free the file buffer since we don't need it any more
	gi.TagFree((void *)sf_buffer );
}

void SpriteMaster::SaveSpriteFile( Event *ev )
{
	int i, num;
	PlacedSprite *sprite;
	str spritefile;

	// only save sprite files if in developer mode
	if(!developer->value)
	{
		return;
	}

	// set the full file path to save to
	if(gi.GameDir())
	{
		spritefile = gi.GameDir();
		spritefile += "\\";
		spritefile += filename;
	}
	else
	{
		spritefile = "base\\";
		spritefile += filename;
	}

	// make sure the file exists to write
	gi.CreatePath( spritefile.c_str());

	// open the file for writting
	ofstream Out( spritefile.c_str(), ios::out | ios::trunc );
	if(!Out) // couldn't create or find the file for writting
	{
		gi.dprintf( "Couldn't open sprite file for writing: %s\n", spritefile.c_str());
		return;
	}

	Out << num_sprites << "\n\n";

	// go through and save all the sprites to the file
	num = 0;
	for(i = 0; i < num_sprites; i++)
	{
		num = G_FindClass( num, "placedsprite" );
		if(!num) // ran out of sprite entities
			break;
		sprite = (PlacedSprite *)G_GetEntity( num );

		Out << sprite->model.c_str() << "\n";
		Out << sprite->origin.x << " ";
		Out << sprite->origin.y << " ";
		Out << sprite->origin.z << "\n";
		Out << sprite->placedangles.x << " ";
		Out << sprite->placedangles.y << " ";
		Out << sprite->placedangles.z << "\n";
		Out << sprite->placedframe << " ";
		Out << sprite->edict->s.scale << " ";
		Out << sprite->edict->s.alpha << "\n";
		if(sprite->targetname != "")
			Out << sprite->targetname.c_str() << "\n";
		else
			Out << "notargetname ";
		switch(sprite->triggereffect)
		{
		case SEFFECT_NONE:
			Out << "none\n";
			break;
		case SEFFECT_TOGGLE:
			Out << "toggle\n";
			break;
		case SEFFECT_ADVANCE:
			Out << "advance\n";
			break;
		case SEFFECT_ONCE:
			Out << "once\n";
			break;
		}
		Out << sprite->animatespeed << " ";
		Out << sprite->animatedelay << "\n";
		Out << sprite->blinkspeed << " ";
		Out << sprite->blinkdelay << "\n";
		Out << sprite->pitchspeed << " ";
		Out << sprite->yawspeed << " ";
		Out << sprite->rollspeed << " ";
		Out << sprite->rotatedelay << "\n";
		Out << "\n";
	}

	Out.close();
	gi.dprintf( "Sprite file saved\n" );
}

/*===================================================================
   SpriteMaster : frame number return function
   ===================================================================*/

int SpriteMaster::NumFrames( const char *testmodel )
{
	int num;

	num = gi.Anim_NumFrames( gi.modelindex( testmodel ), 0 );
	if(num) // it's a model so use the engine to count frames
		return num;

	// try loading a list if don't have one yet
	if(!sf_num)
	{
		ParseSpriteIndex();
	}

	// just always return 1 if there's no list to be found
	if(sf_num < 0)
		return 1;

	// it's a sprite, so look it up in our index list
	for(num = 0; num < sf_num; num++)
	{
		if(!stricmp( sf_index[num].model.c_str(), testmodel )) // found a match
			return sf_index[num].frames;
	}
	// couldn't find a match, so just return 1
	return 1;
}

/*===================================================================
   SpriteMaster : Sprite control commands
   ===================================================================*/

void SpriteMaster::SetSprite( Event *ev )
{
	const char *newspr;
	char str[128], tmpstr[128], *strptr;

	if(ev->NumArgs())
		newspr = ev->GetString( 1 );
	else
		newspr = NULL;

	// display current sprite if none specified
	if(!newspr || !newspr[0])
	{
		gi.dprintf( "spg_file = %s\n", spritename );
		return;
	}

	// attach the needed directory stuff to the file name
	str[0] = 0;
	if(newspr[1] != ':')
	{
		strcpy( tmpstr, newspr );
		strptr = tmpstr;
		while((strptr[0] != '.') && (strptr[0] != 0))
			strptr++;

		// if no extension given, assume a decal
		if(strptr[0] == 0)
		{
			// prepend decals/ if needed
			if(strnicmp( newspr, "decals/", 7 ))
			{
				strcpy( str, "decals/" );
			}
			strcat( str, newspr );
			strcat( str, ".spr" );
		}
		else // basically determine if it's a sprite or a model
		{
			if(!strcmp( strptr, ".def" )) // it's a model
			{
				// prepend models/ if needed
				if(strnicmp( newspr, "models/", 7 ))
				{
					strcpy( str, "models/" );
				}
			}
			else if(!strcmp( strptr, ".spr" )) // it's a sprite
			{
				// prepend sprites/ if needed
				if(strnicmp( newspr, "sprites/", 8 ))
				{
					strcpy( str, "sprites/" );
				}
			}
			else // it's something not right
			{
				ev->Error( "%s is bad extension. Must be str or def\n", strptr );
				return;
			}
			strcat( str, newspr );
		}
	}

	//set value if given a valid sprite name
	if(gi.modelindex( str ) > 0)
	{
		strcpy( spritename, str );
		gi.dprintf( "spg_file = %s\n", spritename );
	}
	else
		ev->Error( "Must specify a valid sprite or model name" );

	// set new sprite model for currently selected sprite
	if(curr_sprite)
	{
		PlacedSprite *sprite;

		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		sprite->setModel( spritename );
		sprite->numframes = NumFrames( spritename );
	}
}

void SpriteMaster::ToggleGlow( Event *ev )
{
	PlacedSprite *sprite;

	if(curr_sprite)
		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(glowcurrent)
	{
		glowcurrent = false;
		if(curr_sprite)
			sprite->edict->s.renderfx &= ~RF_DLIGHT;
		CancelEventsOfType( EV_SpriteMaster_Glow_Flicker );
		gi.dprintf( "Current sprite glow off\n" );
	}
	else
	{
		glowcurrent = true;
		if(curr_sprite)
			sprite->edict->s.renderfx |= RF_DLIGHT;
		PostEvent( EV_SpriteMaster_Glow_Flicker, 0.1 );
		gi.dprintf( "Current sprite glow on\n" );
	}
}

void SpriteMaster::GlowFlicker( Event *ev )
{
	if(curr_sprite)
	{
		PlacedSprite *sprite;
		int i;

		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		sprite->edict->s.radius = 100 + random()*50;

		// also draw a debug line from the player to the sprite
		for(i = 1; i <= maxclients->value; i++)
		{
			if(!g_edicts[i].inuse || !g_edicts[i].entity)
			{
				continue;
			}

			// we found a valid player entity to draw to
			break;
		}

		if(i <= maxclients->value)
		{
			Entity *ent;

			ent = g_edicts[i].entity;
			G_DebugLine( ent->centroid, sprite->origin, 1, 1, 1, random()*0.8 + 0.2 );
		}
	}
	PostEvent( EV_SpriteMaster_Glow_Flicker, 0.1 );
}

void SpriteMaster::ToggleHitWater( Event *ev )
{
	if(hitwater)
	{
		hitwater = false;
		gi.dprintf( "water surface tracing off\n" );
	}
	else
	{
		hitwater = true;
		gi.dprintf( "water surface tracing on\n" );
	}
}

void SpriteMaster::Next( Event *ev )
{
	PlacedSprite *sprite;
	int next;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprites in the level\n" );
		return;
	}

	// turn off glow on current sprite
	if(glowcurrent)
	{
		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		sprite->edict->s.renderfx &= ~RF_DLIGHT;
	}

	next = G_FindClass( curr_sprite, "placedsprite" );

	if(next)
	{
		curr_sprite = next;

		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		if(glowcurrent) // turn on glow if needed
			sprite->edict->s.renderfx |= RF_DLIGHT;
		gi.dprintf( "next sprite selected\n" );
	}
	else
	{
		next = G_FindClass( 0, "placedsprite" );
		curr_sprite = next;
		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		if(glowcurrent) // turn on glow if needed
			sprite->edict->s.renderfx |= RF_DLIGHT;
		gi.dprintf( "first sprite selected\n" );
	}
}

void SpriteMaster::Previous( Event *ev )
{
	PlacedSprite *sprite;
	int prev, next;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprites in the level\n" );
		return;
	}

	next = 0;
	do
	{
		prev = next;
		next = G_FindClass( prev, "placedsprite" );
	}
	while(next != curr_sprite);

	if(prev)
	{
		// turn off glow on current sprite
		if(glowcurrent)
		{
			sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
			sprite->edict->s.renderfx &= ~RF_DLIGHT;
		}

		curr_sprite = prev;

		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		if(glowcurrent) // turn on glow if needed
			sprite->edict->s.renderfx |= RF_DLIGHT;
		gi.dprintf( "previous sprite selected\n" );
	}
	else
	{
		gi.dprintf( "first sprite already selected.\n" );
	}
}

void SpriteMaster::Delete( Event *ev )
{
	PlacedSprite *sprite;
	int curr;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to delete\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	curr = curr_sprite;
	ProcessEvent( EV_SpriteMaster_Next );
	sprite->ProcessEvent( EV_Remove );
	// there was only one sprite in the level
	if(curr == curr_sprite)
		curr_sprite = 0;
	// decrement the sprite counter
	num_sprites--;

	gi.dprintf( "sprite deleted\n" );
}

void SpriteMaster::SetPitch( Event *ev )
{
	const char *newrot;
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to rotate\n" );
		return;
	}

	if(ev->NumArgs())
		newrot = ev->GetString( 1 );
	else
		newrot = NULL;

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	// display current sprite if none specified
	if(!newrot || !newrot[0])
	{
		gi.dprintf( "sprite pitch = %i\n", sprite->angles[PITCH] );
		return;
	}

	sprite->placedangles[PITCH] = (float)atof( newrot );
	sprite->angles[PITCH] = sprite->placedangles[PITCH];
	sprite->setAngles( sprite->angles );
	gi.dprintf( "sprite pitch = %i\n", sprite->angles[PITCH] );
}

void SpriteMaster::SetYaw( Event *ev )
{
	const char *newrot;
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to rotate\n" );
		return;
	}

	if(ev->NumArgs())
		newrot = ev->GetString( 1 );
	else
		newrot = NULL;

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	// display current sprite if none specified
	if(!newrot || !newrot[0])
	{
		gi.dprintf( "sprite yaw = %i\n", sprite->angles[YAW] );
		return;
	}

	sprite->placedangles[YAW] = (float)atof( newrot );
	sprite->angles[YAW] = sprite->placedangles[PITCH];
	sprite->setAngles( sprite->angles );
	gi.dprintf( "sprite yaw = %i\n", sprite->angles[YAW] );
}

void SpriteMaster::SetRoll( Event *ev )
{
	const char *newrot;
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to rotate\n" );
		return;
	}

	if(ev->NumArgs())
		newrot = ev->GetString( 1 );
	else
		newrot = NULL;

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	// display current sprite if none specified
	if(!newrot || !newrot[0])
	{
		gi.dprintf( "sprite roll = %i\n", (int)sprite->angles[ROLL] );
		return;
	}

	sprite->placedangles[ROLL] = (float)atof( newrot );
	sprite->angles[ROLL] = sprite->placedangles[PITCH];
	sprite->setAngles( sprite->angles );
	gi.dprintf( "sprite roll = %i\n", (int)sprite->angles[ROLL] );
}

void SpriteMaster::SetRotateSize( Event *ev )
{
	const char *newmove;

	if(ev->NumArgs())
		newmove = ev->GetString( 1 );
	else
		newmove = NULL;

	// display current sprite if none specified
	if(!newmove || !newmove[0])
	{
		gi.dprintf( "spg_rotsize = %i\n", rotsize );
		return;
	}

	rotsize = atoi( newmove );
	gi.dprintf( "spg_rotsize = %i\n", rotsize );
}

void SpriteMaster::PitchUp( Event *ev )
{
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to rotate\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	sprite->angles[PITCH] -= rotsize;
	sprite->setAngles( sprite->angles );

	sprite->placedangles[PITCH] -= rotsize;
	if(sprite->placedangles[PITCH] < 0)
		sprite->placedangles[PITCH] += 360;

	gi.dprintf( "sprite pitch = %i\n", (int)sprite->placedangles[PITCH] );
}

void SpriteMaster::PitchDown( Event *ev )
{
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to rotate\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	sprite->angles[PITCH] += rotsize;
	sprite->setAngles( sprite->angles );

	sprite->placedangles[PITCH] += rotsize;
	if(sprite->placedangles[PITCH] > 360)
		sprite->placedangles[PITCH] -= 360;

	gi.dprintf( "sprite pitch = %i\n", (int)sprite->placedangles[PITCH] );
}

void SpriteMaster::YawRight( Event *ev )
{
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to rotate\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	sprite->angles[YAW] += rotsize;
	sprite->setAngles( sprite->angles );

	sprite->placedangles[YAW] += rotsize;
	if(sprite->placedangles[YAW] > 360)
		sprite->placedangles[YAW] -= 360;

	gi.dprintf( "sprite yaw = %i\n", (int)sprite->placedangles[YAW] );
}

void SpriteMaster::YawLeft( Event *ev )
{
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to rotate\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	sprite->angles[YAW] -= rotsize;
	sprite->setAngles( sprite->angles );

	sprite->placedangles[YAW] -= rotsize;
	if(sprite->placedangles[YAW] < 0)
		sprite->placedangles[YAW] += 360;

	gi.dprintf( "sprite yaw = %i\n", (int)sprite->placedangles[YAW] );
}

void SpriteMaster::RollRight( Event *ev )
{
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to rotate\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	sprite->angles[ROLL] += rotsize;
	sprite->setAngles( sprite->angles );

	sprite->placedangles[ROLL] += rotsize;
	if(sprite->placedangles[ROLL] > 360)
		sprite->placedangles[ROLL] -= 360;

	gi.dprintf( "sprite roll = %i\n", (int)sprite->placedangles[ROLL] );
}

void SpriteMaster::RollLeft( Event *ev )
{
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to rotate\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	sprite->angles[ROLL] -= rotsize;
	sprite->setAngles( sprite->angles );

	sprite->placedangles[ROLL] -= rotsize;
	if(sprite->placedangles[ROLL] < 0)
		sprite->placedangles[ROLL] += 360;

	gi.dprintf( "sprite roll = %i\n", (int)sprite->placedangles[ROLL] );
}

void SpriteMaster::SetScale( Event *ev )
{
	const char *newscale;

	if(ev->NumArgs())
		newscale = ev->GetString( 1 );
	else
		newscale = NULL;

	// display current sprite if none specified
	if(!newscale || !newscale[0])
	{
		gi.dprintf( "spg_scale = %f\n", place_scale );
		return;
	}

	place_scale = (float)atof( newscale );
	if(place_scale < 0.0001)
		place_scale = 0.0001;
	gi.dprintf( "spg_scale = %f\n", place_scale );

	if(curr_sprite)
	{
		PlacedSprite *sprite;

		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		sprite->edict->s.scale = place_scale;
	}
}

void SpriteMaster::SetAlpha( Event *ev )
{
	const char *newalpha;

	if(ev->NumArgs())
		newalpha = ev->GetString( 1 );
	else
		newalpha = NULL;

	// display current sprite if none specified
	if(!newalpha || !newalpha[0])
	{
		gi.dprintf( "spg_alpha = %f\n", place_alpha );
		return;
	}

	place_alpha = (float)atof( newalpha );
	if(place_alpha < 0.001)
		place_alpha = 0.001;
	else if(place_alpha > 1)
		place_alpha = 1;
	gi.dprintf( "spg_alpha = %f\n", place_alpha );

	if(curr_sprite)
	{
		PlacedSprite *sprite;

		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		sprite->edict->s.alpha = place_alpha;
		if(sprite->edict->s.alpha < 1)
			sprite->edict->s.renderfx |= RF_TRANSLUCENT;
		else
			sprite->edict->s.renderfx &= ~RF_TRANSLUCENT;
	}
}

void SpriteMaster::SetFrame( Event *ev )
{
	const char *newframe;
	int maxframes;

	if(ev->NumArgs())
		newframe = ev->GetString( 1 );
	else
		newframe = NULL;

	// display current sprite if none specified
	if(!newframe || !newframe[0])
	{
		gi.dprintf( "spg_frame = %i\n", place_frame );
		return;
	}

	place_frame = atoi( newframe );
	maxframes = NumFrames( spritename );
	if(place_frame < 0)
		place_frame = 0;
	else if(place_frame > maxframes)
		place_frame = maxframes;
	gi.dprintf( "spg_frame = %i\n", place_frame );

	if(curr_sprite)
	{
		PlacedSprite *sprite;

		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		sprite->edict->s.frame = place_frame;
		if(sprite->edict->s.frame >= sprite->numframes)
			sprite->edict->s.frame = 0;
		sprite->link();
	}
}

void SpriteMaster::NextFrame( Event *ev )
{
	int maxframes;

	place_frame++;
	maxframes = NumFrames( spritename );
	if(place_frame > maxframes)
		place_frame = maxframes;
	gi.dprintf( "spg_frame = %i\n", place_frame );

	if(curr_sprite)
	{
		PlacedSprite *sprite;

		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		sprite->edict->s.frame = place_frame;
		if(sprite->edict->s.frame >= sprite->numframes)
			sprite->edict->s.frame = 0;
		sprite->link();
	}
}

void SpriteMaster::PrevFrame( Event *ev )
{
	int maxframes;

	place_frame--;
	maxframes = NumFrames( spritename );
	if(place_frame < 0)
		place_frame = maxframes - 1;
	gi.dprintf( "spg_frame = %i\n", place_frame );

	if(curr_sprite)
	{
		PlacedSprite *sprite;

		sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
		sprite->edict->s.frame = place_frame;
		if(sprite->edict->s.frame >= sprite->numframes)
			sprite->edict->s.frame = 0;
		sprite->link();
	}
}

void SpriteMaster::SetMoveSize( Event *ev )
{
	const char *newmove;

	if(ev->NumArgs())
		newmove = ev->GetString( 1 );
	else
		newmove = NULL;

	// display current sprite if none specified
	if(!newmove || !newmove[0])
	{
		gi.dprintf( "spg_movesize = %f\n", movesize );
		return;
	}

	movesize = (float)atof( newmove );
	if(movesize < 1)
		movesize = 1;
	gi.dprintf( "spg_movesize = %f\n", movesize );
}

void SpriteMaster::MoveRight( Event *ev )
{
	PlacedSprite *sprite;
	Vector tmpvec;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to move\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	tmpvec = sprite->origin;
	tmpvec -= Vector( sprite->orientation[1] )*movesize;
	sprite->setOrigin( tmpvec );
	gi.dprintf( "sprite moved right\n" );
}

void SpriteMaster::MoveLeft( Event *ev )
{
	PlacedSprite *sprite;
	Vector tmpvec;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to move\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	tmpvec = sprite->origin;
	tmpvec += Vector( sprite->orientation[1] )*movesize;
	sprite->setOrigin( tmpvec );
	gi.dprintf( "sprite moved left\n" );
}

void SpriteMaster::MoveUp( Event *ev )
{
	PlacedSprite *sprite;
	Vector tmpvec;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to move\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	tmpvec = sprite->origin;
	tmpvec += Vector( sprite->orientation[2] )*movesize;
	sprite->setOrigin( tmpvec );
	gi.dprintf( "sprite moved up\n" );
}

void SpriteMaster::MoveDown( Event *ev )
{
	PlacedSprite *sprite;
	Vector tmpvec;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to move\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	tmpvec = sprite->origin;
	tmpvec -= Vector( sprite->orientation[2] )*movesize;
	sprite->setOrigin( tmpvec );
	gi.dprintf( "sprite moved down\n" );
}

void SpriteMaster::MoveForward( Event *ev )
{
	PlacedSprite *sprite;
	Vector tmpvec;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to move\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	tmpvec = sprite->origin;
	tmpvec -= Vector( sprite->orientation[0] )*movesize;
	sprite->setOrigin( tmpvec );
	gi.dprintf( "sprite moved forward\n" );
}

void SpriteMaster::MoveBack( Event *ev )
{
	PlacedSprite *sprite;
	Vector tmpvec;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to move\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	tmpvec = sprite->origin;
	tmpvec += Vector( sprite->orientation[0] )*movesize;
	sprite->setOrigin( tmpvec );
	gi.dprintf( "sprite moved back\n" );
}

void SpriteMaster::GetSettings( Event *ev )
{
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to move\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	strcpy( spritename, sprite->model.c_str());
	gi.dprintf( "spg_file = %s\n", spritename );

	place_frame = sprite->placedframe;
	gi.dprintf( "spg_frame = %i\n", place_frame );

	place_scale = sprite->edict->s.scale;
	gi.dprintf( "spg_scale = %f\n", place_scale );

	place_alpha = sprite->edict->s.alpha;
	gi.dprintf( "spg_alpha = %f\n", place_alpha );

	place_animspeed = sprite->animatespeed;
	gi.dprintf( "spg_animspeed = %f\n", place_animspeed );

	place_animdelay = sprite->animatedelay;
	gi.dprintf( "spg_animdelay = %f\n", place_animdelay );

	place_pitchspeed = sprite->pitchspeed;
	gi.dprintf( "spg_pitchspeed = %f\n", place_pitchspeed );

	place_yawspeed = sprite->yawspeed;
	gi.dprintf( "spg_yawspeed = %f\n", place_yawspeed );

	place_rollspeed = sprite->rollspeed;
	gi.dprintf( "spg_rollspeed = %f\n", place_rollspeed );

	place_rotdelay = sprite->rotatedelay;
	gi.dprintf( "spg_rotdelay = %f\n", place_rotdelay );

	place_blinkspeed = sprite->blinkspeed;
	gi.dprintf( "spg_blinkspeed = %f\n", place_blinkspeed );

	place_blinkdelay = sprite->blinkdelay;
	gi.dprintf( "spg_blinkdelay = %f\n", place_blinkdelay );

	place_trigeffect = sprite->triggereffect;
	switch(place_trigeffect)
	{
	case SEFFECT_NONE:
		gi.dprintf( "spg_triggereffect = none\n" );
		break;
	case SEFFECT_TOGGLE:
		gi.dprintf( "spg_triggereffect = toggle\n" );
		break;
	case SEFFECT_ADVANCE:
		gi.dprintf( "spg_triggereffect = advance\n" );
		break;
	case SEFFECT_ONCE:
		gi.dprintf( "spg_triggereffect = once\n" );
		break;
	}
}

void SpriteMaster::GiveSettings( Event *ev )
{
	PlacedSprite *sprite;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );
	sprite->setModel( spritename );
	sprite->numframes = NumFrames( spritename );
	sprite->placedframe = place_frame;
	if(sprite->placedframe >= sprite->numframes)
		sprite->placedframe = 0;
	sprite->edict->s.frame = sprite->placedframe;
	sprite->edict->s.scale = place_scale;
	sprite->edict->s.alpha = place_alpha;
	if(sprite->edict->s.alpha < 1)
		sprite->edict->s.renderfx |= RF_TRANSLUCENT;
	else
		sprite->edict->s.renderfx &= ~RF_TRANSLUCENT;
	sprite->animatespeed = place_animspeed;
	sprite->animatedelay = place_animdelay;
	sprite->pitchspeed = place_pitchspeed;
	sprite->yawspeed = place_yawspeed;
	sprite->rollspeed = place_rollspeed;
	sprite->rotatedelay = place_rotdelay;
	sprite->blinkspeed = place_blinkspeed;
	sprite->blinkdelay = place_blinkdelay;
	sprite->triggereffect = place_trigeffect;

	gi.dprintf( "sprite now has all current settings\n" );
}

void SpriteMaster::ClearSettings( Event *ev )
{
	spritename = "sprites/blastmark.spr";
	place_scale = 1;
	place_alpha = 1;
	place_animspeed = 0;
	place_animdelay = 0;
	place_pitchspeed = 0;
	place_yawspeed = 0;
	place_rollspeed = 0;
	place_rotdelay = 0;
	place_blinkspeed = 0;
	place_blinkdelay = 0;
	place_trigeffect = SEFFECT_NONE;

	gi.dprintf( "all current settings have been cleared\n" );
}

void SpriteMaster::FlipSprite( Event *ev )
{
	PlacedSprite *sprite;
	Vector norm;
	float currrot;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to flip\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	norm = Vector( sprite->orientation[0] )*(-1);
	norm.x = -norm.x;
	norm.y = -norm.y;
	currrot = sprite->angles[ROLL];
	sprite->angles = norm.toAngles();
	sprite->angles[ROLL] = currrot;
	sprite->setAngles( sprite->angles );
}

void SpriteMaster::SetTargetname( Event *ev )
{
	PlacedSprite *sprite;
	const char *newname;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(ev->NumArgs())
		newname = ev->GetString( 1 );
	else
		newname = NULL;

	// display current sprite if none specified
	if(!newname || !newname[0])
	{
		if(sprite->targetname.length())
			gi.dprintf( "sprite targetname = %s\n", sprite->targetname.c_str());
		else
			gi.dprintf( "sprite targetname = (null)\n" );
		return;
	}

	sprite->SetTargetName( newname );
	gi.dprintf( "sprite targetname = %s\n", sprite->targetname.c_str());
}

void SpriteMaster::SetTriggerEffect( Event *ev )
{
	PlacedSprite *sprite;
	const char *neweffect;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(ev->NumArgs())
		neweffect = ev->GetString( 1 );
	else
		neweffect = NULL;

	// display current sprite if none specified
	if(!neweffect || !neweffect[0])
	{
		switch(sprite->triggereffect)
		{
		case SEFFECT_NONE:
			gi.dprintf( "sprite triggereffect = none\n" );
			break;
		case SEFFECT_TOGGLE:
			gi.dprintf( "sprite triggereffect = toggle\n" );
			break;
		case SEFFECT_ADVANCE:
			gi.dprintf( "sprite triggereffect = advance\n" );
			break;
		case SEFFECT_ONCE:
			gi.dprintf( "sprite triggereffect = once\n" );
			break;
		}
		return;
	}

	if(stricmp( neweffect, "toggle" ) == 0)
		sprite->triggereffect = SEFFECT_TOGGLE;
	else if(stricmp( neweffect, "advance" ) == 0)
		sprite->triggereffect = SEFFECT_ADVANCE;
	else if(stricmp( neweffect, "once" ) == 0)
		sprite->triggereffect = SEFFECT_ONCE;
	else
		sprite->triggereffect = SEFFECT_NONE;

	switch(sprite->triggereffect)
	{
	case SEFFECT_NONE:
		gi.dprintf( "sprite triggereffect = none\n" );
		break;
	case SEFFECT_TOGGLE:
		gi.dprintf( "sprite triggereffect = toggle\n" );
		break;
	case SEFFECT_ADVANCE:
		gi.dprintf( "sprite triggereffect = advance\n" );
		break;
	case SEFFECT_ONCE:
		gi.dprintf( "sprite triggereffect = once\n" );
		break;
	}
}

void SpriteMaster::SetAnimSpeed( Event *ev )
{
	PlacedSprite *sprite;
	const char *newspeed;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(ev->NumArgs())
		newspeed = ev->GetString( 1 );
	else
		newspeed = NULL;

	// display current sprite if none specified
	if(!newspeed || !newspeed[0])
	{
		gi.dprintf( "sprite animspeed = %f\n", sprite->animatespeed );
		return;
	}

	sprite->animatespeed = (float)atof( newspeed );
	sprite->CancelEventsOfType( EV_PlacedSprite_Animate );
	if(sprite->animatespeed > 0)
		sprite->PostEvent( EV_PlacedSprite_Animate, sprite->animatespeed );
	gi.dprintf( "sprite animspeed = %f\n", sprite->animatespeed );
}

void SpriteMaster::SetAnimDelay( Event *ev )
{
	PlacedSprite *sprite;
	const char *newspeed;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(ev->NumArgs())
		newspeed = ev->GetString( 1 );
	else
		newspeed = NULL;

	// display current sprite if none specified
	if(!newspeed || !newspeed[0])
	{
		gi.dprintf( "sprite animdelay = %f\n", sprite->animatedelay );
		return;
	}

	sprite->animatedelay = (float)atof( newspeed );
	gi.dprintf( "sprite animdelay = %f\n", sprite->animatedelay );
}

void SpriteMaster::SetPitchSpeed( Event *ev )
{
	PlacedSprite *sprite;
	const char *newspeed;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(ev->NumArgs())
		newspeed = ev->GetString( 1 );
	else
		newspeed = NULL;

	// display current sprite if none specified
	if(!newspeed || !newspeed[0])
	{
		gi.dprintf( "sprite pitch speed = %i\n", sprite->pitchspeed );
		return;
	}

	sprite->pitchspeed = atoi( newspeed );
	sprite->CancelEventsOfType( EV_PlacedSprite_Rotate );
	if(sprite->pitchspeed || sprite->yawspeed || sprite->rollspeed)
		sprite->PostEvent( EV_PlacedSprite_Rotate, 0.1 );
	gi.dprintf( "sprite pitch speed = %i\n", sprite->pitchspeed );
}

void SpriteMaster::SetYawSpeed( Event *ev )
{
	PlacedSprite *sprite;
	const char *newspeed;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(ev->NumArgs())
		newspeed = ev->GetString( 1 );
	else
		newspeed = NULL;

	// display current sprite if none specified
	if(!newspeed || !newspeed[0])
	{
		gi.dprintf( "sprite yaw speed = %i\n", sprite->yawspeed );
		return;
	}

	sprite->yawspeed = atoi( newspeed );
	sprite->CancelEventsOfType( EV_PlacedSprite_Rotate );
	if(sprite->pitchspeed || sprite->yawspeed || sprite->rollspeed)
		sprite->PostEvent( EV_PlacedSprite_Rotate, 0.1 );
	gi.dprintf( "sprite yaw speed = %i\n", sprite->yawspeed );
}

void SpriteMaster::SetRollSpeed( Event *ev )
{
	PlacedSprite *sprite;
	const char *newspeed;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(ev->NumArgs())
		newspeed = ev->GetString( 1 );
	else
		newspeed = NULL;

	// display current sprite if none specified
	if(!newspeed || !newspeed[0])
	{
		gi.dprintf( "sprite pitch speed = %i\n", sprite->pitchspeed );
		return;
	}

	sprite->rollspeed = atoi( newspeed );
	sprite->CancelEventsOfType( EV_PlacedSprite_Rotate );
	if(sprite->pitchspeed || sprite->yawspeed || sprite->rollspeed)
		sprite->PostEvent( EV_PlacedSprite_Rotate, 0.1 );
	gi.dprintf( "sprite roll speed = %i\n", sprite->rollspeed );
}

void SpriteMaster::SetRotDelay( Event *ev )
{
	PlacedSprite *sprite;
	const char *newspeed;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(ev->NumArgs())
		newspeed = ev->GetString( 1 );
	else
		newspeed = NULL;

	// display current sprite if none specified
	if(!newspeed || !newspeed[0])
	{
		gi.dprintf( "sprite rotate delay = %f\n", sprite->rotatedelay );
		return;
	}

	sprite->rotatedelay = (float)atof( newspeed );
	gi.dprintf( "sprite rotate delay = %f\n", sprite->rotatedelay );
}

void SpriteMaster::SetBlinkSpeed( Event *ev )
{
	PlacedSprite *sprite;
	const char *newspeed;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(ev->NumArgs())
		newspeed = ev->GetString( 1 );
	else
		newspeed = NULL;

	// display current sprite if none specified
	if(!newspeed || !newspeed[0])
	{
		gi.dprintf( "sprite blinkspeed = %f\n", sprite->blinkspeed );
		return;
	}

	sprite->blinkspeed = (float)atof( newspeed );
	sprite->CancelEventsOfType( EV_PlacedSprite_Blink );
	if(sprite->blinkspeed <= 0)
		sprite->showModel();
	else
		sprite->PostEvent( EV_PlacedSprite_Blink, sprite->blinkspeed );
	gi.dprintf( "sprite blinkspeed = %f\n", sprite->blinkspeed );
}

void SpriteMaster::SetBlinkDelay( Event *ev )
{
	PlacedSprite *sprite;
	const char *newspeed;

	if(!curr_sprite)
	{
		gi.dprintf( "no sprite to set\n" );
		return;
	}

	sprite = (PlacedSprite *)G_GetEntity( curr_sprite );

	if(ev->NumArgs())
		newspeed = ev->GetString( 1 );
	else
		newspeed = NULL;

	// display current sprite if none specified
	if(!newspeed || !newspeed[0])
	{
		gi.dprintf( "sprite blinkdelay = %f\n", sprite->blinkdelay );
		return;
	}

	sprite->blinkdelay = (float)atof( newspeed );
	gi.dprintf( "sprite blinkspeed = %f\n", sprite->blinkdelay );
}

/*===================================================================

   Sprite Class

   ===================================================================*/

PlacedSprite::PlacedSprite()
{
	setMoveType( MOVETYPE_NONE );
	setSolidType( SOLID_NOT );
	setSize( "0 0 0", "0 0 0" );

	//setup for when glowing is turned on
	edict->s.color_r    = 1;
	edict->s.color_g    = 1;
	edict->s.color_b    = 1;
	edict->s.radius     = 175;

	// make sure a new sprite doesn't have any effects on it
	triggereffect = SEFFECT_NONE;
	animatespeed = 0;
	animatedelay = 0;
	pitchspeed = 0;
	yawspeed = 0;
	rollspeed = 0;
	rotatedelay = 0;
	blinkspeed = 0;
	blinkdelay = 0;
	placedframe = 0;
}

EXPORT_FROM_DLL void PlacedSprite::Setup( Vector pos, Vector facing )
{
	Vector norm;

	setModel( Spritecontrol.spritename );
	numframes = Spritecontrol.NumFrames( Spritecontrol.spritename );
	placedframe = Spritecontrol.place_frame;
	if(placedframe >= numframes)
		placedframe = 0;
	edict->s.frame = placedframe;

	edict->s.scale = Spritecontrol.place_scale;
	edict->s.alpha = Spritecontrol.place_alpha;
	if(edict->s.alpha < 1) // it's transparent
		edict->s.renderfx |= RF_TRANSLUCENT;

	norm = facing;
	norm.x = -norm.x;
	norm.y = -norm.y;
	angles = norm.toAngles();
	setAngles( angles );
	placedangles = angles;

	setOrigin( pos );
	worldorigin.copyTo( edict->s.old_origin );
}

EXPORT_FROM_DLL void PlacedSprite::Animate( Event *ev )
{
	edict->s.frame++;

	if(edict->s.frame == numframes)
	{
		// check for it being animate once
		if(triggereffect == SEFFECT_ONCE)
		{
			edict->s.frame = numframes - 1;
			return;
		}

		edict->s.frame = 0;
	}

	PostEvent( EV_PlacedSprite_Animate, animatespeed );
}

EXPORT_FROM_DLL void PlacedSprite::Rotate( Event *ev )
{
	angles[PITCH] += pitchspeed;
	angles[YAW] += yawspeed;
	angles[ROLL] += rollspeed;
	setAngles( angles );
	PostEvent( EV_PlacedSprite_Rotate, 0.1 );
}

EXPORT_FROM_DLL void PlacedSprite::Blink( Event *ev )
{
	if(hidden())
		showModel();
	else
		hideModel();

	PostEvent( EV_PlacedSprite_Blink, blinkspeed );
}

EXPORT_FROM_DLL void PlacedSprite::TriggerEvent( Event *ev )
{
	if(triggereffect == SEFFECT_TOGGLE)
	{
		if(animatespeed > 0)
		{
			// turn animation off
			animatespeed * -1;
			CancelEventsOfType( EV_PlacedSprite_Animate );
		}
		else
		{
			// turn animation on
			if(!animatespeed)
				animatespeed = 0.1;
			else
				animatespeed *= -1;

			PostEvent( EV_PlacedSprite_Animate, animatespeed );
		}
	}
	else if(triggereffect == SEFFECT_ADVANCE)
	{
		edict->s.frame++;
		if(edict->s.frame == numframes)
			edict->s.frame = 0;
	}
	else if(triggereffect == SEFFECT_ONCE)
	{
		if(!animatespeed)
			animatespeed = 0.1;
		else if(animatespeed < 0)
			animatespeed *= -1;

		// make sure we start on our first frame
		edict->s.frame = placedframe;
		link();

		PostEvent( EV_PlacedSprite_Animate, animatespeed );
	}
}

/*===================================================================

   Sprite Gun

   ===================================================================*/

CLASS_DECLARATION( Weapon, SpriteGun, "weapon_spritegun" );

ResponseDef SpriteGun::Responses[] =
{
	{&EV_Weapon_Shoot, (Response)SpriteGun::Shoot},
	{NULL, NULL}
};

SpriteGun::SpriteGun()
{
	SetModels( NULL, "view_spritegun.def" );
	SetAmmo( NULL, 0, 0 );

	notdroppable = true;
}

void SpriteGun::Shoot( Event *ev )
{
	PlacedSprite *sprite, *tmpspr;
	Vector pos;
	Vector end;
	trace_t trace;
	Vector targ, dir, norm;
	int mask;

	assert( owner );
	if(!owner)
		return;

// set firing position and direction
	pos = owner->origin;
	pos[2] += owner->viewheight;
	end = pos + Vector( owner->orientation[0] )*2048;

// set target location
	mask = MASK_SOLID;
	if(Spritecontrol.hitwater)
		mask |= MASK_WATER;
	trace = G_Trace( pos, Vector( 0,0,0 ), Vector( 0,0,0 ), end, owner, mask, "SpriteGun::Shoot" );
	targ = Vector( trace.endpos );

	sprite = new PlacedSprite;
	targ = Vector( trace.endpos ) + (Vector( trace.plane.normal )*0.5);
	sprite->Setup( targ, trace.plane.normal );

	// make sure glowing is off for current sprite
	if(Spritecontrol.glowcurrent)
	{
		if(Spritecontrol.curr_sprite)
		{
			tmpspr = (PlacedSprite *)G_GetEntity( Spritecontrol.curr_sprite );
			tmpspr->edict->s.renderfx &= ~RF_DLIGHT;
		}
		sprite->edict->s.renderfx |= RF_DLIGHT;
	}

	// set current sprite
	Spritecontrol.curr_sprite = sprite->entnum;
	// incriment the sprite counter
	Spritecontrol.num_sprites++;

	MuzzleFlash( 1.0, 0.1, 0, 250, 0.6, 0.2 );


	NextAttack( 0.5 );

}
