#ifndef PandaWave_RESOURCES
#include "RevengeResources.h"
#endif

#include <XFile.h>
#include <XError.h>
#include <assert.h>
#include <math.h>

#include "tags.h"
#include "tagprivate.h"
#include "mesh.h"
#include "soun.h"
#include "proj.h"
#include "stli.h"
#include "temp.h"
#include "text.h"
#include "damage.h"
#include "skelmodel.h"
#include "formatters.h"
#include "vengeance_script.h"

#define MAX_TOKEN_LENGTH 1024

// parsing variables
static char *script_beg = 0;
static char *script_pos = 0;
static char *script_old = 0;
static int script_lin = 1;
static int script_len = 0;
static char *script_end = 0;
static bool script_eof = FALSE;

static char temp_token[MAX_TOKEN_LENGTH];


#ifdef macintosh
#include <XStringUtil.h>
#define _stricmp XGCompareStrings
#endif

bool load_script( XGFileSpecifier *fs )
{
	FILE *fp;
	
	fp = fs->OpenFile( "rt" );
	if( !fp )
		return FALSE;
	
	fseek( fp, 0, SEEK_END );
	script_len = ftell( fp );
	fseek( fp, 0, SEEK_SET );
	
	if( script_len == 0 )
	{
		fclose( fp );
		return FALSE;
	}
	
	script_beg = (char*)malloc( script_len );
	if( !script_beg )
	{
		fclose( fp );
		return FALSE;
	}
	
	memset( script_beg, 0x00, script_len );
	
	script_pos = script_beg;
	script_end = script_beg + script_len;
	
	script_len = fread( script_beg, 1, script_len, fp );
	fclose( fp );
	
	script_eof = FALSE;
	
	return TRUE;
}

void free_script( void )
{
	if( script_beg )
		free( script_beg );

	script_beg = 0;
	script_pos = 0;
	script_old = 0;
	script_lin = 1;
	script_len = 0;
	script_end = 0;
	script_eof = FALSE;
}

bool parse_token( char *token, bool cross_lines, long *line )
{
	char *token_start = token;
	
	if( script_eof || !script_beg )
		return FALSE;
		
	script_old = script_pos;
		
	// skip whitespace
	// oooooo, look at the fun platform differences
	while( (*script_pos < 33 || *script_pos == '/') && script_pos != script_end )
	{
		// skip comments
		if( *script_pos == '/' && *(script_pos - 1) == '/' )
		{
#if OPT_MACOS
				if( *script_pos == 0x0a || *script_pos == '\0' )
			{
				if( *script_pos == 0x0d )
					break;
				
				script_pos++;
			}
#endif

#if OPT_WINOS
// phook - why am I only seeing 0x0a right now?
#if 0
			while( script_pos != script_end )
			{
				if( *script_pos == 0x0a || *script_pos == '\0' )
					break;
				
				script_pos++;
			}
#else
			while( script_pos != script_end )
			{
				if( *script_pos == 0x0a || *script_pos == '\0' )
					break;
				
				script_pos++;
			}
#endif
#endif

#if OPT_XWIN
			while( script_pos != script_end )
			{
				if( *script_pos == 0x0a || *script_pos == '\0' )
					break;
				
				script_pos++;
			}
#endif
		}
		
		// check for line increments
#if OPT_MACOS
		if( *script_pos == 0x0d )
		{
			script_lin++;
			if( !cross_lines )
			{
				script_pos++;
				return FALSE;
			}
		}
#endif

#if OPT_WINOS
// phook - why am I only seeing 0x0a right now?
#if 0
		if( *(script_pos - 1) == 0x0d && *script_pos == 0x0a )
		{
			script_lin++;
			if( !cross_lines )
			{
				script_pos++;
				return FALSE;
			}
		}
#else
		if( *script_pos == 0x0a )
		{
			script_lin++;
			if( !cross_lines )
			{
				script_pos++;
				return FALSE;
			}
		}
#endif
#endif

#if OPT_XWIN
		if( *script_pos == 0x0a )
		{
			script_lin++;
			if( !cross_lines )
			{
				script_pos++;
				return FALSE;
			}
		}
#endif

		++script_pos;
	}
		
	if( script_pos == script_end )
	{
		script_eof = TRUE;
		return FALSE;
	}

	if( line )
		*line = script_lin;
		
	// copy a non-quoted token	
	if( *script_pos != '\"' )
	{
		// copy till next whitespace.
		while( *script_pos > 32 && *script_pos < 127 && script_pos != script_end )
		{
			if( token - token_start < MAX_TOKEN_LENGTH )
			{
				*token = *script_pos;
				token++;
				script_pos++;
			}
			else
				return FALSE;
		}
	}
	// copy the quoted token
	else
	{
		// copy till next quote.
		script_pos++;

		while( *script_pos != '\"' && script_pos != script_end )
		{
			// even quoted tokens shouldn't cross any lines
#if OPT_MACOS
			if( *script_pos == 0x0d )
				return FALSE;
#endif

#if OPT_WINOS
// phook - why am I only seeing 0x0a right now?
#if 0
			if( *(script_pos - 1) == 0x0d && *script_pos == 0x0a )
				return FALSE;
#else
			if( *script_pos == 0x0a )
				return FALSE;
#endif
#endif

#if OPT_XWIN
			if( *script_pos == 0x0a )
				return FALSE;
#endif
		
			if( token - token_start < MAX_TOKEN_LENGTH )
			{
				*token = *script_pos;
				token++;
				script_pos++;
			}
			else
				return FALSE;
		}
		
		// make sure we had an ending quote
		if( *script_pos != '\"' )
			return FALSE;
		
		// skip ending quote
		if( script_pos != script_end )
			script_pos++;
	}
	
	if( script_pos == script_end )
		script_eof = TRUE;
	
	*token = 0; // terminate;
	return TRUE;
}

void unparse_token( void )
{
	script_pos = script_old;
}

bool parse_float( float *f )
{
	if( !parse_token( temp_token ) )
		return FALSE;
		
	*f = (float)atof( temp_token );
	
	return TRUE;
}

bool parse_int( int *i )
{
	if( !parse_token( temp_token ) )
		return FALSE;
		
	*i = atoi( temp_token );
	
	return TRUE;
}

bool seek_to_token( const char *token_to_find, bool case_sensitive )
{
/*
	int(const char *, const char *)comp;
	
	if( case_sensitive )
		comp = strcmp;
	else
		comp = stricmp;
*/
		
	while( parse_token( temp_token ) )
	{
		if( case_sensitive )
			if( !strcmp( temp_token, token_to_find ) )
				return TRUE;
		else
			if( !_stricmp( temp_token, token_to_find ) )
				return TRUE;
	}
	
	return FALSE;
}



/*
Since my implimentation of the UI for the script only allows for one to be loaded and
run at once, I've chose to just keep it in a global variable for now, and not pass it around
to all the script functions as a handle
*/
vengeance_script *script = NULL;

vengeance_script_table script_table[] =
{
	VS_BUILD_MONOLITHIC, BUILD_MONOLITHIC_TOKEN,
	VS_EXPORT_RAW, EXPORT_RAW_TOKEN,
	VS_REPLACE_SOUNDS, REPLACE_SOUNDS_TOKEN,
	VS_REPLACE_MODELS, REPLACE_MODELS_TOKEN,
	VS_REPLACE_MONSTER_STATS, REPLACE_MONSTER_STATS_TOKEN,
	VS_CREATE_MONSTER_STATS, CREATE_MONSTER_STATS_TOKEN,
	VS_CREATE_SOUNDS, CREATE_SOUNDS_TOKEN,
	VS_EXPORT_TEXT, EXPORT_TEXT_TOKEN,
	VS_IMPORT_TEXT, IMPORT_TEXT_TOKEN,
	VS_DUMP_NAMES, DUMP_NAMES_TOKEN,
	VS_NULL, ""
};

vengeance_script_table script_parm_table[] =
{
	VSP_FILE, FILE_TOKEN,
	VSP_TYPE, TYPE_TOKEN,
	VSP_TAG, TAG_TOKEN,
	VSP_MONS, MONS_TOKEN,
	VSP_ATTK, ATTK_TOKEN,
	VSP_OBJE, OBJE_TOKEN,
	VSP_PROJ, PROJ_TOKEN,
	VSP_SOUN, SOUN_TOKEN,
	VSP_NULL, ""
};

vengeance_script_table mons_projectile_volume[] =
{
	0, "none",
	0, "unwieldy",
	0, "small",
	0, "large"
};

vengeance_script_table mons_flags[] =
{
	_monster_translates_continuously_flag, "trans_constant",
	_monster_holds_with_clear_shot_flag, "holds_with_clear_shot",
	_monster_floats_flag, "floats",
	_monster_flies_flag, "flies",
	_monster_allows_projectiles_to_pass_through_flag, "allows_proj_pass_thru",
	_monster_experience_proportional_to_damage_flag, "exp_proportional",
	_monster_is_anti_missile_unit_flag, "is_anit-missle_unit",
	_monster_is_anti_missile_target_flag, "is_anti-missle_target",
	_monster_turns_to_stone_when_killed_flag, "turns_to_stone",
	_monster_concentrates_on_single_attacker_flag, "concentrates_on_single_target",
	_monster_is_undead_flag, "is_undead",
	_monster_cannot_be_autotargeted_flag, "cannot_be_autotargeted",
	_monster_is_giant_sized_flag, "is_giant_sized",
	_monster_does_not_respect_visibility_flag, "does_not_respect_visibility",
	_monster_is_not_solid_flag, "is_not_solid",
	_monster_leaves_contrail_flag, "leaves_contrail",
	_monster_invisible_on_overhead_map_flag, "invisible_on_overhead",
	_monster_cannot_be_healed_flag, "cannot_be_healed",
	_monster_does_not_drop_ammo_when_dying_flag, "does_not_drop_ammo",
	_monster_is_inanimate_object_flag, "is_inanimate",
	_monster_is_skittish_flag, "is_skittish",
	_monster_has_3d_model_flag, "has_3d_model",
	_monster_uses_small_radius_with_allies_flag, "small_radius_with_alies"
};

vengeance_script_table attk_flags[] =
{
	_monster_attack_is_indirect_flag, "is_indirect",
	_monster_attack_does_not_require_solution_flag, "does_not_require_solution",
	_monster_attack_aimed_at_targets_feet_flag, "aimed_at_feet",
	_monster_attack_leads_target_flag, "leads_target",
	_monster_attack_uses_ammunition_flag, "uses_ammo",
	_monster_attack_uses_carried_projectile_flag, "uses_carried_projectile",
	_monster_attack_is_reflexive_flag, "is_reflexive",
	_monster_attack_is_special_ability_flag, "is_special_ability",
	_monster_attack_cannot_be_aborted_flag, "cannot_be_aborted",
	_monster_attack_is_primary_flag, "is_primary_attack",
	_monster_attack_avoids_friendly_units_flag, "avoids_friendly_units",
	_monster_attack_vs_giant_sized_flag, "vs_giant_sized",
	_monster_attack_prohibited_vs_giant_sized_flag, "prohibit_vs_giant_sized",
	_monster_attack_is_fixed_pitch_flag, "is_fixed_pitch",
	_monster_attack_dont_try_to_shoot_over_nearby_units_flag, "dont_shoot_over_nearby",
	_monster_attack_lob_to_hit_lower_nearby_units_flag, "lob_to_hit_lower_nearby",
	// extra flags start ----
	_monster_attack_can_be_used_against_friendlies_extra_flag, "can_be_used_against_friendlies",
	_monster_attack_vs_small_sized_extra_flag, "vs_small_sized",
	_monster_attack_prohibited_vs_small_sized_extra_flag, "prohibit_vs_small_sized"
};

vengeance_script_table obje_flags[] =
{
	_draw_selection_box_flag, "draw_selection",
	_draw_vitality_box_flag, "draw_vitality",
	_maintains_constant_height_above_ground_flag, "maintain_constant_height",
	_ignore_model_and_object_collisions_flag, "ignore_collisions"
};

vengeance_script_table proj_detonation_flags[] =
{
	_projectile_detonates_when_animation_loops_flag, "when_animation_loops",
	_projectile_detonates_when_transfer_loops_flag, "when_transfer_loops",
	_projectile_detonates_at_rest_flag, "at_rest",
	_projectile_remains_after_detonation_flag, "remains_after_detonation"
};

vengeance_script_table proj_physics_volume[] =
{
	0, "none",
	0, "unwieldy",
	0, "small",
	0, "large"
};

vengeance_script_table proj_physics_flags[] =
{
	_projectile_affected_by_wind_flag, "affected_by_wind",
	_projectile_cannot_be_accelerated_flag, "cannot_be_accelerated",
	_projectile_is_melee_attack_flag, "melee_attack"
};

vengeance_script_table proj_damage_type[] =
{
	0, "none",
	0, "explosion",
	0, "magical",
	0, "holding",
	0, "healing",
	0, "kinetic",
	0, "slashing_metal",
	0, "stoning",
	0, "slashing_claws",
	0, "explosive_electricity",
	0, "fire",
	0, "gas",
	0, "charm"
};


vengeance_script_table proj_damage_flags[] =
{
	_damage_proportional_to_velocity_flag, "proportional_to_velocity",
	_damage_area_of_effect_flag, "area_of_effect",
	_damage_does_not_interrupt_events_flag, "does_not_interrupt_events",
	_damage_cannot_be_healed_flag, "cannot_be_healed",
	_damage_can_cause_paralysis_flag, "can_cause_paralysis",
	_damage_can_stun_flag, "can_stun",
	_damage_cannot_be_blocked_flag, "cannot_be_blocked",
	_damage_does_not_effect_monsters_flag, "does_not_affect_monsters",
	_damage_detonates_explosives_immediately_flag, "detonated_explosive_immediately",
	_damage_proportional_to_mana_flag, "proportional_to_mana",
	_damage_can_destroy_large_object_flag, "can_destroy_large_objects",
	_damage_instantaneous_flag, "instantaneous",
	_damage_cannot_hurt_owner_flag, "cannot_hurt_owner",
	_damage_does_not_make_monsters_flinch_flag, "does_not_make_monsters_flinch",
	_damage_can_cause_confusion_flag, "can_cause_confusion"
};

vengeance_script_table proj_flags[] =
{
	_projectile_uses_owner_color_table_index_flag, "uses_owner_color_table_index",
	_projectile_angle_follows_trajectory_flag, "angle_follows_trajectory",
	_projectile_can_be_mirrored_flag, "can_be_mirrored",
	_projectile_becomes_dormant_at_rest_flag, "becomes_dormant_at_rest",
	_projectile_animates_at_rest_flag, "animates_at_rest",
	_projectile_bloodies_landscape_flag, "bloodies_landscape",
	_projectile_is_on_fire_flag, "is_on_fire",
	_projectile_passes_through_target_flag, "passes_through_target",
	_projectile_cannot_be_mirrored_vertically_flag, "cannot_be_mirrored_vertically",
	_projectile_floats_flag, "floats",
	_projectile_is_media_surface_effect_flag, "is_media_surface_effect",
	_projectile_continually_detonates_flag, "continually_detonates",
	_projectile_is_lightning_flag, "is_lightning",
	_projectile_chooses_nearby_target_flag, "chooses_nearby_target",
	_projectile_only_destroyed_by_lifespan_flag, "is_only_destroyed_by_lifespan",
	_projectile_makes_owner_monster_visible_flag, "makes_owner_monster_visible",
	_projectile_can_set_landscape_on_fire_flag, "can_set_landscape_on_fire",
	_projectile_centered_on_target_flag, "centered_on_target",
	_projectile_marks_target_has_having_been_chosen_flag, "marks_target_as_having_been_chosen",
	_projectile_is_bloody_do_not_draw_flag, "is_bloody",
	_projectile_is_guided_flag, "is_guided",
	_projectile_definition_detonates_immediately_flag, "detonates_immediately",
	_projectile_uses_sprite_contrail_flag, "has sprite contrail"
};

vengeance_script_exception::vengeance_script_exception( int line, char *detail)
{
	m_line = line;
	m_detail = detail;
}

void vengeance_script_exception::message_box( void )
{
	char smessage[256];
	char lmessage[256];
	
	sprintf( smessage, "Script Error" );
	sprintf( lmessage, "Line %d: %s", m_line, m_detail );
	
	Message( "Vengeance Scripting", smessage, lmessage );
}


bool parse_vengeance_script( XGFileSpecifier *fs )
{
	char token[MAX_TOKEN_LENGTH];
	unsigned long parm_mask = 0;
	long *index;
	long line;
	int last_line = -1;
	int ii;
	
	if( script != NULL )
		dispose_vengeance_script();
	
	try
	{
		if( !load_script( fs ) )
			throw vengeance_script_exception( -1, "script load failed" );
			
		script = (vengeance_script *)malloc( sizeof(vengeance_script) );
		if( !script )
			throw vengeance_script_exception( -1, "memory allocation failed" );
		
		memset( script, 0x00, sizeof(vengeance_script) );
		
		script->fs = new XGFileSpecifier( *fs );
			
		// parse the script type out
		if( !parse_token( token ) )
			throw vengeance_script_exception( -1, "empty script" );

		for( ii = 0; script_table[ii].id != VS_NULL; ii++ )
		{
			if( !_stricmp( token, script_table[ii].token ) )
			{
				script->type = script_table[ii].id;
				break;
			}
		}
		
		switch( script->type )
		{
			case VS_BUILD_MONOLITHIC:
				if( !parse_token( token, false, NULL ) )
					throw vengeance_script_exception( -1, "build_monolithic has no destination" );
				
				strcpy( script->data, token );
				
				parm_mask = BUILD_MONOLITHIC_TOKEN_MASK;
				break;
				
			case VS_EXPORT_RAW:
				parm_mask = EXPORT_RAW_TOKEN_MASK;
				break;
				
			case VS_REPLACE_SOUNDS:
				parm_mask = REPLACE_SOUNDS_TOKEN_MASK;
				break;
				
			case VS_REPLACE_MODELS:
				parm_mask = REPLACE_MODELS_TOKEN_MASK;
				break;
				
			case VS_REPLACE_MONSTER_STATS:
				parm_mask = REPLACE_MONSTER_STATS_TOKEN_MASK;
				break;
				
				
			case VS_CREATE_MONSTER_STATS:
				if( !parse_token( token, false, NULL ) )
					throw vengeance_script_exception( -1, "create_monster_stats has no destination" );
				
				strcpy( script->data, token );
				
				parm_mask = CREATE_MONSTER_STATS_TOKEN_MASK;
				break;
				
			case VS_CREATE_SOUNDS:
				if( !parse_token( token, false, NULL ) )
					throw vengeance_script_exception( -1, "create_sounds has no destination" );
				
				strcpy( script->data, token );

				parm_mask = CREATE_SOUNDS_TOKEN_MASK;
				break;
				
			case VS_EXPORT_TEXT:
				parm_mask = EXPORT_TEXT_TOKEN_MASK;
				break;
				
			case VS_IMPORT_TEXT:
				if( !parse_token( token, false, NULL ) )
					throw vengeance_script_exception( -1, "import_text has no destination" );
				
				strcpy( script->data, token );

				parm_mask = IMPORT_TEXT_TOKEN_MASK;
				break;
				
			case VS_DUMP_NAMES:
				parm_mask = DUMP_NAMES_TOKEN_MASK;
				break;
				
			case VS_NULL:
				throw vengeance_script_exception( -1, "does not appear to be a vengeance script" );
				break;
		}
		
		index = &script->parms_used;
		
		// parse out the parms
		for( script->parms_used = 0; script->parms_used < VSP_MAX_PARAMETERS; )
		{
			// get a parm
			if( !parse_token( token, true, &line ) )
				// no more parameters, stop parsing
				break;

			if( last_line == -1 )
				last_line = line;
				
			if( last_line != line )
			{
				last_line = line;
				script->parms_used++;
			}

			// grow the parm block if needed
			if( script->parms_used == script->parms_size )
			{
				if( !grow_vengeance_script_parm_block() )
					throw vengeance_script_exception( -1, "memory allocation failed" );
			}
			
			script->parms[*index].line = line;
			
			for( ii = 0; script_parm_table[ii].id != VSP_NULL; ii++ )
			{
				if( !_stricmp( token, script_parm_table[ii].token ) )
				{
					script->parms[*index].parm = script_parm_table[ii].id;
					break;
				}
			}
			
			switch( script->parms[*index].parm )
			{
				case VSP_FILE:
				{
					if( !(parm_mask & FLAG(VSP_FILE)) )
						throw vengeance_script_exception( script->parms[*index].line, "the script type does not support the file parameter" );
						
					// get the file names
					for( ii = 0; ii < VSP_MAX_PARM_ARRAY_LENGTH; ii++ )
					{
						if( !parse_token( token, false, NULL ) )
							break;

						strcpy( script->parms[*index].strings[ii], token );
					}
					
					if( ii == 0 )
						throw vengeance_script_exception( script->parms[*index].line, "no parameters found" );
						
					script->parms[*index].count = ii;
				}
				
				break;
					
				case VSP_TYPE:
				{
					if( !(parm_mask & FLAG(VSP_TYPE)) )
						throw vengeance_script_exception( script->parms[*index].line, "the script type does not support the type parameter" );

					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "4 character type code expected, none found" );
						
					if( strlen( token ) != 4 )
						throw vengeance_script_exception( script->parms[*index].line, "the type code is not the correct size" );
					
					script->parms[*index].tag_type = tag_type_from_string( token );
				}
				
				break;
					
				case VSP_TAG:
				{
					if( !(parm_mask & FLAG(VSP_TAG)) )
						throw vengeance_script_exception( script->parms[*index].line, "the script type does not support the tag parameter" );

					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "4 character tag code expected, none found" );
						
					if( strlen( token ) != 4 )
						throw vengeance_script_exception( script->parms[*index].line, "the tag code is not the correct size" );
					
					script->parms[*index].tag_id = tag_type_from_string( token );
					
					// get the file names
					for( ii = 0; ii < VSP_MAX_PARM_ARRAY_LENGTH; ii += 2 )
					{
						if( !parse_token( token, false, NULL ) )
							break;

						strcpy( script->parms[*index].strings[ii], token );

						// must retrieve a skip percentage						
						if( !parse_token( token, false, NULL ) )
							break;
							
						strcpy( script->parms[*index].strings[ii + 1], token );
					}
					
					script->parms[*index].count = ii;
				}
					
				break;
					
				case VSP_MONS:
				{
					int ii;
					
					if( !(parm_mask & FLAG(VSP_MONS)) )
						throw vengeance_script_exception( script->parms[*index].line, "the script type does not support the mons parameter" );
				
					script->parms[*index].tag_type = 'mons';
					if( *index == 223 )
					{
						int i = 0;
					}

					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "4 character tag code expected, none found" );

					if( strlen( token ) != 4 )
						throw vengeance_script_exception( script->parms[*index].line, "the tag code is not the correct size" );

					script->parms[*index].tag_id = tag_type_from_string( token );
					
					// keep the settings of any flags that we don't touch
					for( ii = 0; ii < sizeof(unsigned int) * 8; ii++ )
					{
						script->parms[*index].create_type.mons.flag_mask |= mons_flags[ii].id;
					}
					
					script->parms[*index].create_type.mons.flag_mask = ~script->parms[*index].create_type.mons.flag_mask;

					// desired_projectile_volume
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(desired_projectile_volume) parameter was expected, none found" );
						
					for( ii = 0; ii < sizeof(mons_projectile_volume) / sizeof(vengeance_script_table); ii++ )
					{
						if( !_stricmp( token, mons_projectile_volume[ii].token ) )
						{
							script->parms[*index].create_type.mons.desired_projectile_volume = ii - 1;
							break;
						}
					}
					
					// left_handed_fraction
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(left_handed_fraction) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.left_handed_fraction = float_to_percentage( atof( token ) );
					
					// turning_speed
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(turning_speed) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.turning_speed = float_to_angle( atof( token ) ) / TICKS_PER_SECOND;
					
					// hard_death_system_shock
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(hard_death_system_shock) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.hard_death_system_shock = float_to_percentage( atof( token ) );
					
					// flinch_system_shock
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(flinch_system_shock) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.flinch_system_shock = float_to_percentage( atof( token ) );
					
					// absorbed_fraction
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(absorbed_fraction) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.absorbed_fraction = float_to_percentage( atof( token ) );
					
					// healing_fraction
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(healing_fraction) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.healing_fraction = float_to_short_fixed( atof( token ) );
						
					// combined_power
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(combined_power) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.combined_power = (short)atoi( token );
						
					// longest_range
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(longest_range) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.longest_range = float_to_short_world_distance( atof( token ) );

					// experience_point_value
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(experience_point_value) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.experience_point_value = (short)atoi( token );
						
					// cost
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(cost) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.cost = (short)atoi( token );

					// pathfinding_radius
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(pathfinding_radius) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.pathfinding_radius = float_to_short_world_distance( atof( token ) );

					// maximum_mana
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(maximum_mana) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.maximum_mana = float_to_short_fixed( atof( token ) );
						
					// mana_recharge_rate
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(mana_recharge_rate) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.mana_recharge_rate = float_to_short_fixed( atof( token ) );
						
					// berserk_system_shock
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(berserk_system_shock) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.berserk_system_shock = float_to_percentage( atof( token ) );
					
					// berserk_vitality
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(berserk_vitality) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.berserk_vitality = float_to_percentage( atof( token ) );
					
					// kills_for_maximum_experience_bonus
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(kills_for_maximum_experience_bonus) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.kills_for_maximum_experience_bonus = (short)atoi( token );

					// experience_bonus_size
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(experience_bonus_size) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.experience_bonus_size = (short)atoi( token );

					// experience_bonus_radius
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(experience_bonus_radius) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.experience_bonus_radius = float_to_short_world_distance( atof( token ) );

					// blocking_fraction
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(blocking_fraction) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.blocking_fraction = float_to_percentage( atof( token ) );

					// warning_distance
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(warning_distance) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.warning_distance = float_to_short_world_distance( atof( token ) );

					// critical_distance
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(critical_distance) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.critical_distance = float_to_short_world_distance( atof( token ) );

					// activation_distance
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(activation_distance) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.activation_distance = float_to_short_world_distance( atof( token ) );

					// initial_ammunition_lower_bound
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(initial_ammunition_lower_bound) parameter was expected, none found" );

					script->parms[*index].create_type.mons.initial_ammunition_lower_bound = (short)atoi( token );

					// initial_ammunition_delta
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(initial_ammunition_delta) parameter was expected, none found" );

					script->parms[*index].create_type.mons.initial_ammunition_delta = get_delta( script->parms[*index].create_type.mons.initial_ammunition_lower_bound, (short)atoi( token ) );

					// maximum_ammunition_count
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(maximum_ammunition_count) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.maximum_ammunition_count = (short)atoi( token );

					// base_movement_speed
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(base_movement_speed) parameter was expected, none found" );
						
					script->parms[*index].create_type.mons.base_movement_speed = float_to_short_world_distance( atof( token ) );
					
					// terrain_costs / movement_modifiers
					for( ii = 0; ii < MAXIMUM_NUMBER_OF_TERRAIN_TYPES; ii++ )
					{
						if( !parse_token( token, false, NULL ) )
							throw vengeance_script_exception( script->parms[*index].line, "(movement_modifiers) parameter was expected, none found" );
							
						if( atoi( token ) == -1 )
						{
							script->parms[*index].create_type.mons.terrain_costs[ii] = -1;
							script->parms[*index].create_type.mons.movement_modifiers[ii] = 0;
						}
						else
						{
							script->parms[*index].create_type.mons.terrain_costs[ii] = 0;
							script->parms[*index].create_type.mons.movement_modifiers[ii] = float_to_short_percentage( atof( token ) );
						}
					}
					
					// flags
					while( parse_token( token, false, NULL ) )
					{
						for( ii = 0; ii < sizeof(mons_flags) / sizeof(vengeance_script_table); ii++ )
						{
							if( !_stricmp( token, "-" ) )
								break;
								
							if( !_stricmp( token, mons_flags[ii].token ) )
							{
								script->parms[*index].create_type.mons.flags |= mons_flags[ii].id;
								break;
							}
						}

						if( ii == sizeof(mons_flags) / sizeof(vengeance_script_table) )
							break;
					}
				}

				break;
				
				case VSP_ATTK:
				{
					int ii;
					
					if( !(parm_mask & FLAG(VSP_ATTK)) )
						throw vengeance_script_exception( script->parms[*index].line, "the script type does not support the attk parameter" );
				
					script->parms[*index].tag_type = 'attk';

					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "4 character tag code expected, none found" );

					if( strlen( token ) != 4 )
						throw vengeance_script_exception( script->parms[*index].line, "the tag code is not the correct size" );

					script->parms[*index].tag_id = tag_type_from_string( token );
					
					// look for the attack index
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(attack_index) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.index = (short)atoi( token );
					
					// minimum_range
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(minimum_range) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.minimum_range = float_to_short_world_distance( atof( token ) );

					// maximum_range
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(maximum_range) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.maximum_range = float_to_short_world_distance( atof( token ) );

					// repetitions
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(repetitions) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.repetitions = (short)atoi( token );
					
					// miss_fraction
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(miss_fraction) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.miss_fraction = float_to_percentage( atof( token ) );
					
					// initial_velocity_lower_bound
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(initial_velocity_lower_bound) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.initial_velocity_lower_bound = float_to_short_world_distance( atof( token ) );

					// initial_velocity_delta
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(initial_velocity_delta) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.initial_velocity_delta = get_delta( script->parms[*index].create_type.attk.initial_velocity_lower_bound, float_to_short_world_distance( atof( token ) ) );

					// initial_velocity_error
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(initial_velocity_error) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.initial_velocity_error = float_to_short_world_distance( atof( token ) );

					// velocity_improvement_with_experience
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(velocity_improvement_with_experience) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.velocity_improvement_with_experience = float_to_short_world_distance( atof( token ) );

					// recovery_time
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(recovery_time) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.recovery_time = (short)(atof( token ) * (float)TICKS_PER_SECOND);
					
					// recovery_time_experience_delta
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(recovery_time_experience_delta) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.recovery_time_experience_delta = (short)(atof( token ) * (float)TICKS_PER_SECOND);
					
					// mana_cost
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(mana_cost) parameter was expected, none found" );
						
					script->parms[*index].create_type.attk.mana_cost = float_to_short_fixed( atof( token ) );

					// flags
					script->parms[*index].create_type.attk.flags = 0;
					script->parms[*index].create_type.attk.extra_flags = 0;
					
					while( parse_token( token, false, NULL ) )
					{
						for( ii = 0; ii < sizeof(attk_flags) / sizeof(vengeance_script_table); ii++ )
						{
							if( !_stricmp( token, "-" ) )
								break;
								
							if( !_stricmp( token, attk_flags[ii].token ) )
							{
								if( ii < 16 )
									script->parms[*index].create_type.attk.flags |= attk_flags[ii].id;
								else
									script->parms[*index].create_type.attk.extra_flags |= attk_flags[ii].id;
								
								break;
							}
						}

						if( ii == sizeof(mons_flags) / sizeof(vengeance_script_table) )
						{
							unparse_token();
							break;
						}
					}
				}
				
				break;
					
				case VSP_OBJE:
				{
					int ii;
					
					if( !(parm_mask & FLAG(VSP_OBJE)) )
						throw vengeance_script_exception( script->parms[*index].line, "the script type does not support the obje parameter" );
				
					script->parms[*index].tag_type = 'obje';

					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "4 character tag code expected, none found" );

					if( strlen( token ) != 4 )
						throw vengeance_script_exception( script->parms[*index].line, "the tag code is not the correct size" );

					script->parms[*index].tag_id = tag_type_from_string( token );

					// keep the settings of any flags that we don't touch
					for( ii = 0; ii < sizeof(word) * 8; ii++ )
					{
						script->parms[*index].create_type.obje.flag_mask |= obje_flags[ii].id;
					}
					
					script->parms[*index].create_type.obje.flag_mask = ~script->parms[*index].create_type.obje.flag_mask;
					
					// maximum_vitality
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(maximum_vitality) parameter was expected, none found" );
						
					script->parms[*index].create_type.obje.maximum_vitality = float_to_short_fixed( atof( token ) );
						
					// maximum_vitality_delta
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(maximum_vitality_delta) parameter was expected, none found" );
						
					script->parms[*index].create_type.obje.maximum_vitality_delta = get_delta( script->parms[*index].create_type.obje.maximum_vitality, float_to_short_fixed( atof( token ) ) );

					// scale_lower_bound
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(scale_lower_bound) parameter was expected, none found" );
						
					script->parms[*index].create_type.obje.scale_lower_bound = float_to_short_fixed( atof( token ) );
						
					// scale_delta
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(scale_delta) parameter was expected, none found" );
						
					script->parms[*index].create_type.obje.scale_delta = get_delta( script->parms[*index].create_type.obje.scale_lower_bound, float_to_short_fixed( atof( token ) ) );

					// minimum_damage
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(minimum_damage) parameter was expected, none found" );
						
					script->parms[*index].create_type.obje.minimum_damage = float_to_short_fixed( atof( token ) );
						
					// flags
					while( parse_token( token, false, NULL ) )
					{
						for( ii = 0; ii < sizeof(obje_flags) / sizeof(vengeance_script_table); ii++ )
						{
							if( !_stricmp( token, "-" ) )
								break;
								
							if( !_stricmp( token, obje_flags[ii].token ) )
							{
								script->parms[*index].create_type.obje.flags |= obje_flags[ii].id;
								break;
							}
						}
						
						if( ii == sizeof(obje_flags) / sizeof(vengeance_script_table) )
							break;
					}

					// effect_modifiers
//					unparse_token();
					for( ii = 0; ii < NUMBER_OF_EFFECT_MODIFIERS; ii++ )
					{
						script->parms[*index].create_type.obje.effect_modifiers[ii] = float_to_short_fixed( atof( token ) );

						if( !parse_token( token, false, NULL ) )
							break;
//							throw vengeance_script_exception( script->parms[*index].line, "(effect_modifiers) parameter was expected, none found" );
					}
				}
				
				break;
				
				case VSP_PROJ:
				{
					unsigned long long_flag_mask = 0;
					word word_flag_mask = 0;
					
					int ii;
					
					if( !(parm_mask & FLAG(VSP_PROJ)) )
						throw vengeance_script_exception( script->parms[*index].line, "the script type does not support the proj parameter" );
				
					script->parms[*index].tag_type = 'proj';

					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "4 character tag code expected, none found" );

					if( strlen( token ) != 4 )
						throw vengeance_script_exception( script->parms[*index].line, "the tag code is not the correct size" );

					script->parms[*index].tag_id = tag_type_from_string( token );
					
					// keep the settings of any flags that we don't touch
					for( ii = 0; ii < sizeof(unsigned int) * 8; ii++ )
					{
						script->parms[*index].create_type.proj.flag_mask |= proj_flags[ii].id;
						script->parms[*index].create_type.proj.flag_mask &= !_projectile_uses_sprite_contrail_flag;
					}
					
					script->parms[*index].create_type.proj.flag_mask = ~script->parms[*index].create_type.proj.flag_mask;
					
					// keep the settings of any flags that we don't touch
					for( ii = 0; ii < sizeof(word) * 8; ii++ )
					{
						script->parms[*index].create_type.proj.damage.flag_mask |= proj_damage_flags[ii].id;
					}
					
					script->parms[*index].create_type.proj.damage.flag_mask = ~script->parms[*index].create_type.proj.damage.flag_mask;
					
					// detonation_frequency
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(detonation_frequency) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.detonation_frequency = float_to_percentage( atof( token ) );

					// media_detonation_frequency
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(media_detonation_frequency) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.media_detonation_frequency = float_to_percentage( atof( token ) );
					
					// detonation_velocity
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(detonation_velocity) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.detonation_velocity = float_to_short_world_distance( atof( token ) );

					// detonation flags
					while( parse_token( token, false, NULL ) )
					{
						for( ii = 0; ii < sizeof(proj_detonation_flags) / sizeof(vengeance_script_table); ii++ )
						{
							if( !_stricmp( token, "-" ) )
								break;
								
							if( !_stricmp( token, proj_detonation_flags[ii].token ) )
							{
								script->parms[*index].create_type.proj.flags |= proj_detonation_flags[ii].id;
								break;
							}
						}
						
						if( ii == sizeof(proj_detonation_flags) / sizeof(vengeance_script_table) )
							break;
					}
					
					// volume
					// we have the token already
//					if( !parse_token( token, false, NULL ) )
//						throw vengeance_script_exception( script->parms[*index].line, "(volume) parameter was expected, none found" );

					for( ii = 0; ii < sizeof(proj_physics_volume) / sizeof(vengeance_script_table); ii++ )
					{
						if( !_stricmp( token, proj_physics_volume[ii].token ) )
						{
							script->parms[*index].create_type.proj.volume = ii - 1;
							break;
						}
					}
					
					if( ii == sizeof(proj_physics_volume) / sizeof(vengeance_script_table) )
						throw vengeance_script_exception( script->parms[*index].line, "(volume) value not recognized" );

					// inertia_lower_bound
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(inertia_lower_bound) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.inertia_lower_bound = float_to_short_fixed( atof( token ) );
						
					// inertia_delta
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(inertia_delta) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.inertia_delta = get_delta( script->parms[*index].create_type.proj.inertia_lower_bound, float_to_short_fixed( atof( token ) ) );

					// random_initial_velocity
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(random_initial_velocity) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.random_initial_velocity = float_to_short_world_distance( atof( token ) );

					// physics flags
					while( parse_token( token, false, NULL ) )
					{
						for( ii = 0; ii < sizeof(proj_physics_flags) / sizeof(vengeance_script_table); ii++ )
						{
							if( !_stricmp( token, "-" ) )
								break;
								
							if( !_stricmp( token, proj_physics_flags[ii].token ) )
							{
								script->parms[*index].create_type.proj.flags |= proj_physics_flags[ii].id;
								break;
							}
						}
						
						if( ii == sizeof(proj_physics_flags) / sizeof(vengeance_script_table) )
							break;
					}

					// nearby_target_radius
					// we have the token already
//					if( !parse_token( token, false, NULL ) )
//						throw vengeance_script_exception( script->parms[*index].line, "(nearby_target_radius) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.nearby_target_radius = (short)atoi( token );
						
					// tracking_priority
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(tracking_priority) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.tracking_priority = (short)atoi( token );
						
					// guided_turning_speed
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(guided_turning_speed) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.guided_turning_speed = float_to_angle( atof( token ) ) / TICKS_PER_SECOND;
					
					// type
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(damage_type) parameter was expected, none found" );
					
					for( ii = 0; ii < sizeof(proj_damage_type) / sizeof(vengeance_script_table); ii++ )
					{
						if( !_stricmp( token, proj_damage_type[ii].token ) )
						{
							script->parms[*index].create_type.proj.damage.type = ii - 1;
							break;
						}
					}
					
					if( ii == sizeof(proj_damage_type) / sizeof(vengeance_script_table) )
						throw vengeance_script_exception( script->parms[*index].line, "(damage_type) value not recognized" );

					// damage_lower_bound
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(damage_lower_bound) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.damage.damage_lower_bound = float_to_short_fixed( atof( token ) );
						
					// damage_delta
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(damage_delta) parameter was expected, none found" );
					
					script->parms[*index].create_type.proj.damage.damage_delta = get_delta( script->parms[*index].create_type.proj.damage.damage_lower_bound, float_to_short_fixed( atof( token ) ) );
					
					// radius_lower_bound
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(radius_lower_bound) parameter was expected, none found" );
					
					script->parms[*index].create_type.proj.damage.radius_lower_bound = float_to_short_world_distance( atof( token ) );

					// radius_delta
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(radius_delta) parameter was expected, none found" );
					
					script->parms[*index].create_type.proj.damage.radius_delta = get_delta( script->parms[*index].create_type.proj.damage.radius_lower_bound, float_to_short_world_distance( atof( token ) ) );

					// rate_of_expansion
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(rate_of_expansion) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.damage.rate_of_expansion = float_to_short_world_distance( atof( token ) );

					// damage_to_velocity
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(damage_to_velocity) parameter was expected, none found" );
						
					script->parms[*index].create_type.proj.damage.damage_to_velocity = float_to_short_fixed( atof( token ) );
						
					// projectile damage flags
					while( parse_token( token, false, NULL ) )
					{
						for( ii = 0; ii < sizeof(proj_damage_flags) / sizeof(vengeance_script_table); ii++ )
						{
							if( !_stricmp( token, "-" ) )
								break;
								
							if( !_stricmp( token, proj_damage_flags[ii].token ) )
							{
								script->parms[*index].create_type.proj.damage.flags |= proj_damage_flags[ii].id;
								break;
							}
						}
						
						if( ii == sizeof(proj_damage_flags) / sizeof(vengeance_script_table) )
							break;
					}

					// projectile flags
					do
					{
						for( ii = 0; ii < sizeof(proj_flags) / sizeof(vengeance_script_table); ii++ )
						{
							if( !_stricmp( token, "-" ) )
								break;
							
							if( !_stricmp( token, proj_flags[ii].token ) )
							{
								script->parms[*index].create_type.proj.flags |= proj_flags[ii].id;
								break;
							}
						}
						
						if( ii == sizeof(proj_flags) / sizeof(vengeance_script_table) )
							break;
					} while( parse_token( token, false, NULL ) );
				}
				
				break;
				
				case VSP_SOUN:
				{
					if( !(parm_mask & FLAG(VSP_SOUN)) )
						throw vengeance_script_exception( script->parms[*index].line, "the script type does not support the soun parameter" );
					
					// name
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(name) parameter was expected, none found" );
						
					strncpy( script->parms[*index].create_type.soun.name, token, TAG_NAME_LENGTH );
					script->parms[*index].create_type.soun.name[TAG_NAME_LENGTH] = 0;
					
					// new tag name
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(tag) parameter was expected, none found" );

					if( strlen( token ) != 4 )
						throw vengeance_script_exception( script->parms[*index].line, "the tag code is not the correct size" );
					
					script->parms[*index].create_type.soun.id = tag_type_from_string( token );
						
					// loudness
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(loudness) parameter was expected, none found" );
						
					script->parms[*index].create_type.soun.loudness = atoi( token );
					
					if( script->parms[*index].create_type.soun.loudness < 1 || script->parms[*index].create_type.soun.loudness > 4 )
						throw vengeance_script_exception( script->parms[*index].line, "(loudness) parameter out of range, should be 1, 2, 3, or 4" );

					// subtitle tag
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(subtitle_stringlist) parameter was expected, none found" );

					if( strlen( token ) != 4 )
						throw vengeance_script_exception( script->parms[*index].line, "the tag code is not the correct size" );
					
					script->parms[*index].create_type.soun.subtitle_stringlist = tag_type_from_string( token );

					// subtitle index
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(subtitle_index) parameter was expected, none found" );

					script->parms[*index].create_type.soun.subtitle_index = atoi( token );

					// play fraction
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(play_fraction) parameter was expected, none found" );

					script->parms[*index].create_type.soun.play_fraction = atof( token );
					
					if( script->parms[*index].create_type.soun.play_fraction < 0 || script->parms[*index].create_type.soun.play_fraction > 100 )
						throw vengeance_script_exception( script->parms[*index].line, "(play_fraction) parameter out of range (0-100)" );

					// frequency modifier
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(frequency_modifier) parameter was expected, none found" );

					script->parms[*index].create_type.soun.frequency_modifier = atof( token );
					
//					if( script->parms[*index].create_type.soun.frequency_modifier < 0 || script->parms[*index].create_type.soun.frequency_modifier > 1 )
//						throw vengeance_script_exception( script->parms[*index].line, "(play_fraction) parameter out of range (0-100)" );

					// pitch min
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(pitch_min) parameter was expected, none found" );

					script->parms[*index].create_type.soun.pitch_min = atof( token );
					
					// pitch max
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(pitch_max) parameter was expected, none found" );
						
					script->parms[*index].create_type.soun.pitch_max = atof( token );
					
					// volume min
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(volume_min) parameter was expected, none found" );

					script->parms[*index].create_type.soun.volume_min = atof( token );
					
					if( script->parms[*index].create_type.soun.volume_min < 0 || script->parms[*index].create_type.soun.volume_min > 1 )
						throw vengeance_script_exception( script->parms[*index].line, "(volume_min) parameter out of range (0-1)" );
					
					// volume max
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(volume_max) parameter was expected, none found" );
						
					script->parms[*index].create_type.soun.volume_max = atof( token );

					if( script->parms[*index].create_type.soun.volume_max < 0 || script->parms[*index].create_type.soun.volume_max > 1 )
						throw vengeance_script_exception( script->parms[*index].line, "(volume_max) parameter out of range (0-1)" );

					// flags
					if( !parse_token( token, false, NULL ) )
						throw vengeance_script_exception( script->parms[*index].line, "(flags) parameter was expected, none found" );
						
					script->parms[*index].create_type.soun.flags = (unsigned)atol( token );
				}
				
				break;
					
				default:
					throw vengeance_script_exception( -1, "internal scripting error" );
			}
		}
		
		script->parms_used++;
		
		free_script();
		return TRUE;
	}
	catch( vengeance_script_exception e )
	{
		e.message_box();
		dispose_vengeance_script();
		free_script();
		return FALSE;
	}	
}

void dispose_vengeance_script( void )
{
	if( script == NULL )
		return;
		
	if( script->parms )
		free( script->parms );
		
	if( script->fs )
		delete script->fs;
		
	free( script );
	
	script = NULL;
}

bool grow_vengeance_script_parm_block( void )
{
	#define SCRIPT_PARM_BLOCK_GROW_AMOUNT 64
	
	script->parms = (vengeance_script_parm *)realloc( script->parms, (script->parms_size + SCRIPT_PARM_BLOCK_GROW_AMOUNT) * sizeof(vengeance_script_parm) );
	if( !script->parms )
		return FALSE;
		
	script->parms_size += SCRIPT_PARM_BLOCK_GROW_AMOUNT;
	
	memset( &script->parms[script->parms_size - SCRIPT_PARM_BLOCK_GROW_AMOUNT], 0x00, SCRIPT_PARM_BLOCK_GROW_AMOUNT * sizeof(vengeance_script_parm) );
	
	return TRUE;
}

bool execute_vengeance_script( void )
{
#if defined(macintosh)
	return FALSE;
#else
	FILE *script_log = fopen( "script_log.txt", "a" );
	fprintf( script_log, "Running script %s...\n", script->fs->GetFileName().Get() );
	
	switch( script->type )
	{
		case VS_BUILD_MONOLITHIC:
		{
			tag_file *mf;
			tag_file *tf;
			int ii;
			
			mf = new_empty_tag_file();
			
			for( ii = 0; ii < script->parms_used; ii++ )
			{
				XGFileSpecifier fs( script->parms[ii].strings[0] );

				tf = get_tag_file( fs, false );
				if( tf )
					copy_tag_file_entries_to_another_file( tf, mf );
				else
				{
					if( script_log )
						fprintf( script_log, "script line %d: (%s) failed on copy\n", script->parms[ii].line, BUILD_MONOLITHIC_TOKEN );
				}
			}
			
			XGFileSpecifier ms( script->data );
			if( !write_tags_to_file( ms, mf, false, TAG_FILE_TYPE_MONOLITHIC ) )
			{
				if( script_log )
					fprintf( script_log, "(%s) output file failed to write\n", BUILD_MONOLITHIC_TOKEN );
			}
		}
		
		break;
		
		case VS_EXPORT_RAW:
		{
			tag_file *tf;
			tag_entry *te;
			int ii;
			
			for( ii = 0; ii < script->parms_used; ii++ )
			{
				te = get_entry( script->parms[ii].tag_type, script->parms[ii].tag_id );
				if( te )
				{
					XGFileSpecifier fs( script->parms[ii].strings[0] );
					tf = new_empty_tag_file();
					
					const void* data;
					unsigned length;
					
					get_entry_data( te, &data, &length );
					if( length )
					{
						add_tag_entry(
							tf,
							script->parms[ii].tag_type,
							script->parms[ii].tag_id,
							get_type_version( script->parms[ii].tag_type ),
							get_entry_name( te ),
							0,
							length,
							data,
							false
							);
						
						if( !write_tags_to_file( fs, tf, false, TAG_FILE_TYPE_LOCAL ) )
						{
							if( script_log )
								fprintf( script_log, "script line %d: (%s) file failed to write\n", script->parms[ii].line, EXPORT_RAW_TOKEN );
						}
					}
					
					close_tag_file( tf );
				}
				else
				{
					if( script_log )
						fprintf(
							script_log,
							"script line %d: (%s) could not find %s/%s - nothing written\n",
							script->parms[ii].line,
							EXPORT_RAW_TOKEN,
							tagstr( script->parms[ii].tag_type ),
							tagstr( script->parms[ii].tag_id )
							);
				}
			}
		}
		
		break;
			
		case VS_REPLACE_SOUNDS:
		{
			tag_entry *te;
			float skip_percentage;
			bool bPermCont;
			int ii;
			int jj;
			
			for( ii = 0; ii < script->parms_used; ii++ )
			{
				te = get_entry( 'soun', script->parms[ii].tag_id );
				if( te )
				{
					bPermCont = true;
					for( jj = 0; jj < script->parms[ii].count && bPermCont; jj += 2 )
					{
						// if only 1 permutation is listed, set it's skip fraction to 0
						if( jj >= MAXIMUM_PERMUTATIONS_PER_SOUND * 2 )
						{
							if( script_log )
								fprintf( script_log, "script line %d: (%s) tried to add too many permutations, skipping\n", script->parms[ii].line, REPLACE_SOUNDS_TOKEN );
								
							break;
						}
						
						skip_percentage = atof( script->parms[ii].strings[jj + 1] );
						if( skip_percentage == 100.0 )
						{
							if( script_log )
								fprintf( script_log, "WARNING: script line %d: (%s) skip_percentage was 100.0\n", script->parms[ii].line, REPLACE_SOUNDS_TOKEN );
						}
						
						if( script->parms[ii].count <= 2 )
						{
							skip_percentage = 0;
						}
						
						int error = replace_sound_data_at_index( te, &XGFileSpecifier( script->parms[ii].strings[jj] ), skip_percentage, jj / 2 );
						if( error != LOAD_SOUND_ERROR_SUCCESS )
						{
							if( script_log )
							switch( error )
							{
								case LOAD_SOUND_ERROR_FAILURE:
									fprintf( script_log, "script line %d: (%s) failed on permutation %d\n", script->parms[ii].line, REPLACE_SOUNDS_TOKEN, jj / 2 );
									break;
									
								case LOAD_SOUND_ERROR_NOT_16_BIT:
									fprintf( script_log, "script line %d: (%s) failed on permutation %d (sound not 16 bit)\n", script->parms[ii].line, REPLACE_SOUNDS_TOKEN, jj / 2 );
									break;
									
								case LOAD_SOUND_ERROR_NOT_22050:
									fprintf( script_log, "script line %d: (%s) failed on permutation %d (sound not 22khz)\n", script->parms[ii].line, REPLACE_SOUNDS_TOKEN, jj / 2 );
									break;
									
								default:
									break;
							}
								
							bPermCont = false;
						}
					}
				}
			}
		}
			
		break;
			
		case VS_REPLACE_MODELS:
		{
			tag_entry *te;
			int ii;
			int jj;

			for( ii = 0; ii < script->parms_used; ii++ )
			{
				te = get_entry( 'skmd', script->parms[ii].tag_id );
				if( te )
				{
					for( jj = 0; jj < script->parms[ii].count; jj++ )
					{
						if( !replace_model_data_at_index( te, &XGFileSpecifier( script->parms[ii].strings[jj] ), jj ) )
						{
							if( script_log )
								fprintf( script_log, "script line %d: (%s) failed on permutation %d\n", script->parms[ii].line, REPLACE_MODELS_TOKEN, jj );
						}
					}
				}
			}
		}
			
		break;
			
		case VS_REPLACE_MONSTER_STATS:
		{
			tag_entry *te;
			int ii;
			int jj;
			
			for( ii = 0; ii < script->parms_used; ii++ )
			{
				// FIXME: will I need to code in something to only get entries from plugins and not foundation files?
				te = get_entry( script->parms[ii].tag_type, script->parms[ii].tag_id );
				if( !te )
				{
					if( script_log )
						fprintf( script_log, "(%s) couldn't load tag %s/%s\n", REPLACE_MONSTER_STATS_TOKEN, tagstr( script->parms[ii].tag_type ), tagstr( script->parms[ii].tag_id ) );
				
					continue;
				}
				
				if( get_entry_file( te )->readonly )
				{
					if( script_log )
						fprintf( script_log, "(%s) skipping readonly file %s\n", REPLACE_MONSTER_STATS_TOKEN, get_entry_file( te )->filespec->GetFileName().Get() );
						
					for( jj = ii + 1; script->parms[jj].tag_type == 'attk'; jj++ )
					{
					}
					
					ii = jj - 1;
				}
				else
				{
					switch( script->parms[ii].parm )
					{
						case VSP_MONS:
						{
							if( script_log )
								fprintf( script_log, "(%s) updating data for tag %s/%s\n", REPLACE_MONSTER_STATS_TOKEN, tagstr( script->parms[ii].tag_type ), tagstr( script->parms[ii].tag_id ) );
							
							if( !replace_monster_data( te, &script->parms[ii].create_type.mons ) )
							{
								if( script_log )
									fprintf( script_log, "script line %d: (%s) monster tag data import failed\n", script->parms[ii].line, REPLACE_MONSTER_STATS_TOKEN );
							}
							
							for( jj = ii + 1; script->parms[jj].tag_type == 'attk'; jj++ )
							{
								if( script_log )
									fprintf( script_log, "(%s) updating attack index %d for tag %s/%s\n", REPLACE_MONSTER_STATS_TOKEN, (jj - ii - 1), tagstr( script->parms[ii].tag_type ), tagstr( script->parms[ii].tag_id ) );
							
								if( !replace_attack_data( te, &script->parms[jj].create_type.attk ) )
								{
									if( script_log )
										fprintf( script_log, "script line %d: (%s) monster tag attack import failed\n", script->parms[ii].line, REPLACE_MONSTER_STATS_TOKEN );
								}
							}
							
							ii = jj - 1;
						}
						
						break;
						
						case VSP_OBJE:
						{
							if( script_log )
								fprintf( script_log, "(%s) updating data for tag %s/%s\n", REPLACE_MONSTER_STATS_TOKEN, tagstr( script->parms[ii].tag_type ), tagstr( script->parms[ii].tag_id ) );
							
							if( !replace_object_data( te, &script->parms[ii].create_type.obje ) )
							{
								if( script_log )
									fprintf( script_log, "script line %d: (%s) object tag data import failed\n", script->parms[ii].line, REPLACE_MONSTER_STATS_TOKEN );
							}
						}
						
						break;
						
						case VSP_PROJ:
						{
							if( script_log )
								fprintf( script_log, "(%s) updating data for tag %s/%s\n", REPLACE_MONSTER_STATS_TOKEN, tagstr( script->parms[ii].tag_type ), tagstr( script->parms[ii].tag_id ) );
							
							if( !replace_projectile_data( te, &script->parms[ii].create_type.proj ) )
							{
								if( script_log )
									fprintf( script_log, "script line %d: (%s) project tag data import failed\n", script->parms[ii].line, REPLACE_MONSTER_STATS_TOKEN );
							}
						}
						
						break;
					}
				}
			}
			
			
		}
		break;
		
		case VS_CREATE_MONSTER_STATS:
		{
			XGFileSpecifier fs( script->data );
			FILE *script_file;
			
			tag_entry *te;
		
			int ii;
			int jj;
			int kk;
			
			script_file = fs.OpenFile( "w" );
			if( script_file )
			{
				// write the header
				fprintf( script_file, "replace_monster_stats // (don't change this)\n" );
				fprintf( script_file, "// tag type\ttag id\n\n" );
				
				for( ii = 0; ii < script->parms_used; ii++ )
				{
					switch( script->parms[ii].tag_type )
					{
						case 'mons':
						{
							vengeance_script_mons mons;
							vengeance_script_attk attk;
							
							te = get_entry( script->parms[ii].tag_type, script->parms[ii].tag_id );
							if( !te )
							{
								if( script_log )
									fprintf( script_log, "script line %d: (%s) monster tag export failed\n", script->parms[ii].line, CREATE_MONSTER_STATS_TOKEN );
									
								break;
							}
							
//							fprintf( script_file, "// %s\t\t", te->name );
//							fprintf( script_file, CREATE_MONSTER_STATS_MONS_TEXT );
							fprintf( script_file, "// %s\n", te->name );
							fprintf( script_file, "%s\t%s\t", tagstr( script->parms[ii].tag_type ), tagstr( script->parms[ii].tag_id ) );
							
							if( !retrive_monster_data( te, &mons ) )
							{
								if( script_log )
									fprintf( script_log, "script line %d: (%s) monster tag data retrieval failed\n", script->parms[ii].line, CREATE_MONSTER_STATS_TOKEN );
							
								break;
							}
							
							fprintf( script_file, "%s\t", mons_projectile_volume[mons.desired_projectile_volume + 1].token );
							fprintf( script_file, "%s\t", format_float( percentage_to_float( mons.left_handed_fraction ), 4 ) );
							fprintf( script_file, "%s\t", format_float( angle_to_float( mons.turning_speed ) * TICKS_PER_SECOND, 2 ) );
							fprintf( script_file, "%s\t", format_float( percentage_to_float( mons.hard_death_system_shock ), 4 ) );
							fprintf( script_file, "%s\t", format_float( percentage_to_float( mons.flinch_system_shock ), 4 ) );
							fprintf( script_file, "%s\t", format_float( percentage_to_float( mons.absorbed_fraction ), 4 ) );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( mons.healing_fraction ), 3 ) );
							fprintf( script_file, "%d\t", mons.combined_power );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( mons.longest_range ), 3 ) );
							fprintf( script_file, "%d\t", mons.experience_point_value );
							fprintf( script_file, "%d\t", mons.cost );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( mons.pathfinding_radius ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( mons.maximum_mana ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( mons.mana_recharge_rate ), 3 ) );
							fprintf( script_file, "%s\t", format_float( percentage_to_float( mons.berserk_system_shock ), 4 ) );
							fprintf( script_file, "%s\t", format_float( percentage_to_float( mons.berserk_vitality ), 4 ) );
							fprintf( script_file, "%d\t", mons.kills_for_maximum_experience_bonus );
							fprintf( script_file, "%d\t", mons.experience_bonus_size );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( mons.experience_bonus_radius ), 3 ) );
							fprintf( script_file, "%s\t", format_float( percentage_to_float( mons.blocking_fraction ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( mons.warning_distance ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( mons.critical_distance ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( mons.activation_distance ), 3 ) );
							fprintf( script_file, "%d\t", mons.initial_ammunition_lower_bound );
							fprintf( script_file, "%d\t", get_upper_bound( mons.initial_ammunition_lower_bound, mons.initial_ammunition_delta ) );
							fprintf( script_file, "%d\t", mons.maximum_ammunition_count );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( mons.base_movement_speed ), 3 ) );
							
							for( jj = 0; jj < MAXIMUM_NUMBER_OF_TERRAIN_TYPES; jj++ )
							{
								if( mons.terrain_costs[jj] == -1 )
									fprintf( script_file, "-1\t" );
								else
									fprintf( script_file, "%f\t", short_percentage_to_float( mons.movement_modifiers[jj] ) );
							}
							
							for( jj = 0; jj < sizeof(mons_flags) / sizeof(vengeance_script_table); jj++ )
							{
								if( mons.flags == 0 )
								{
									fprintf( script_file, "-" );
									break;
								}
									
								if( mons.flags & mons_flags[jj].id )
									fprintf( script_file, "%s ", mons_flags[jj].token );
							}
							
							// now lets output the attacks
							for( jj = 0; jj < mons.number_of_attacks; jj++ )
							{
								fprintf( script_file, "\nattk\t%s\t%d\t", tagstr( script->parms[ii].tag_id ), jj );
								
								attk.index = jj;
							
								if( !retrive_attack_data( te, &attk ) )
								{
									if( script_log )
										fprintf( script_log, "script line %d: (%s) monster tag attack data retrieval failed\n", script->parms[ii].line, CREATE_MONSTER_STATS_TOKEN );
								
									break;
								}
								
								fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( attk.minimum_range ), 3 ) );
								fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( attk.maximum_range ), 3 ) );
								fprintf( script_file, "%d\t", attk.repetitions );
								fprintf( script_file, "%s\t", format_float( percentage_to_float( attk.miss_fraction ), 4 ) );
								fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( attk.initial_velocity_lower_bound ), 3 ) );
								fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( get_upper_bound( attk.initial_velocity_lower_bound, attk.initial_velocity_delta ) ), 3 ) );
								fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( attk.initial_velocity_error ), 3 ) );
								fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( attk.velocity_improvement_with_experience ), 3 ) );
								fprintf( script_file, "%f\t", ((float)attk.recovery_time / (float)TICKS_PER_SECOND) );
								fprintf( script_file, "%f\t", ((float)attk.recovery_time_experience_delta / (float)TICKS_PER_SECOND) );
								fprintf( script_file, "%s\t", format_float( short_fixed_to_float( attk.mana_cost ), 3 ) );

								for( kk = 0; kk < sizeof(attk_flags) / sizeof(vengeance_script_table); kk++ )
								{
									if( (attk.flags == 0) && (attk.extra_flags == 0) )
									{
										fprintf( script_file, "-" );
										break;
									}
									
									if( kk < 16 )
									{
										if( attk.flags & attk_flags[kk].id )
											fprintf( script_file, "%s ", attk_flags[kk].token );
									}
									else
									{
										if( attk.extra_flags & (attk_flags[kk].id - sizeof(word)) )
											fprintf( script_file, "%s ", attk_flags[kk].token );
									}
								}
							}
							
							fprintf( script_file, "\n//\n" );
						}
						break;
						
						case 'obje':
						{
							vengeance_script_obje obje;
							
							te = get_entry( script->parms[ii].tag_type, script->parms[ii].tag_id );
							if( !te )
							{
								if( script_log )
									fprintf( script_log, "script line %d: (%s) object tag export failed\n", script->parms[ii].line, CREATE_MONSTER_STATS_TOKEN );

								break;
							}
								
//							fprintf( script_file, "// %s\t\t", te->name );
//							fprintf( script_file, CREATE_MONSTER_STATS_OBJE_TEXT );
							fprintf( script_file, "%s\t%s\t", tagstr( script->parms[ii].tag_type ), tagstr( script->parms[ii].tag_id ) );
							
							if( !retrive_object_data( te, &obje ) )
							{
								if( script_log )
									fprintf( script_log, "script line %d: (%s) object tag data retrieval failed\n", script->parms[ii].line, CREATE_MONSTER_STATS_TOKEN );
							
								break;
							}
							
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( obje.maximum_vitality ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( get_upper_bound( obje.maximum_vitality, obje.maximum_vitality_delta ) ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( obje.scale_lower_bound ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( get_upper_bound( obje.scale_lower_bound, obje.scale_delta ) ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( obje.minimum_damage ), 3 ) );

							for( jj = 0; jj < sizeof(obje_flags) / sizeof(vengeance_script_table); jj++ )
							{
								if( obje.flags == 0 )
								{
									fprintf( script_file, "-\t" );
									break;
								}
									
								if( obje.flags & obje_flags[jj].id )
									fprintf( script_file, "%s ", obje_flags[jj].token );
							}
							
							for( jj = 0; jj < NUMBER_OF_EFFECT_MODIFIERS; jj++ )
							{
								fprintf( script_file, "\t%s", format_float( short_fixed_to_float( obje.effect_modifiers[jj] ), 3 ) );
							}

							fprintf( script_file, "\n//\n" );
						}
						break;
						
						case 'proj':
						{
							vengeance_script_proj proj;
							int printed;
							
							te = get_entry( script->parms[ii].tag_type, script->parms[ii].tag_id );
							if( !te )
							{
								if( script_log )
									fprintf( script_log, "script line %d: (%s) projectile tag export failed\n", script->parms[ii].line, CREATE_MONSTER_STATS_TOKEN );

								break;
							}
								
//							fprintf( script_file, "// %s\t\t", te->name );
//							fprintf( script_file, CREATE_MONSTER_STATS_PROJ_TEXT );
							fprintf( script_file, "%s\t%s\t", tagstr( script->parms[ii].tag_type ), tagstr( script->parms[ii].tag_id ) );
							
							if( !retrive_projectile_data( te, &proj ) )
							{
								if( script_log )
									fprintf( script_log, "script line %d: (%s) projectile tag data retrieval failed\n", script->parms[ii].line, CREATE_MONSTER_STATS_TOKEN );
							
								break;
							}
							
							fprintf( script_file, "%s\t", format_float( percentage_to_float( proj.detonation_frequency ), 4 ) );
							fprintf( script_file, "%s\t", format_float( percentage_to_float( proj.media_detonation_frequency ), 4 ) );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( proj.detonation_velocity ), 3 ) );
							
							printed = 0;
							for( jj = 0; jj < sizeof(proj_detonation_flags) / sizeof(vengeance_script_table); jj++ )
							{
								if( proj.flags & proj_detonation_flags[jj].id )
								{
									fprintf( script_file, "%s ", proj_detonation_flags[jj].token );
									printed++;
								}
							}
							
							if( !printed )
								fprintf( script_file, "-" );
							
							fprintf( script_file, "\t" );
							
							fprintf( script_file, "%s\t", proj_physics_volume[proj.volume + 1].token );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( proj.inertia_lower_bound ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( get_upper_bound( proj.inertia_lower_bound, proj.inertia_delta ) ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( proj.random_initial_velocity ), 3 ) );
							
							printed = 0;
							for( jj = 0; jj < sizeof(proj_physics_flags) / sizeof(vengeance_script_table); jj++ )
							{
								if( proj.flags & proj_physics_flags[jj].id )
								{
									fprintf( script_file, "%s ", proj_physics_flags[jj].token );
									printed++;
								}
							}
							
							if( !printed )
								fprintf( script_file, "-" );
							
							fprintf( script_file, "\t" );
							
							fprintf( script_file, "%d\t", proj.nearby_target_radius );
							fprintf( script_file, "%d\t", proj.tracking_priority );
							fprintf( script_file, "%s\t", format_float( angle_to_float( proj.guided_turning_speed ) * TICKS_PER_SECOND, 2 ) );
							
							fprintf( script_file, "%s\t", proj_damage_type[proj.damage.type + 1].token );

							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( proj.damage.damage_lower_bound ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( get_upper_bound( proj.damage.damage_lower_bound, proj.damage.damage_delta ) ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( proj.damage.radius_lower_bound ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( get_upper_bound( proj.damage.radius_lower_bound, proj.damage.radius_delta ) ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_world_distance_to_float( proj.damage.rate_of_expansion ), 3 ) );
							fprintf( script_file, "%s\t", format_float( short_fixed_to_float( proj.damage.damage_to_velocity ), 3 ) );

							printed = 0;
							for( jj = 0; jj < sizeof(proj_damage_flags) / sizeof(vengeance_script_table); jj++ )
							{
								if( proj.damage.flags & proj_damage_flags[jj].id )
								{
									fprintf( script_file, "%s ", proj_damage_flags[jj].token );
									printed++;
								}
							}
							
							if( !printed )
								fprintf( script_file, "-" );
							
							fprintf( script_file, "\t" );

							printed = 0;
							for( jj = 0; jj < sizeof(proj_flags) / sizeof(vengeance_script_table); jj++ )
							{
								if( proj.flags & proj_flags[jj].id )
								{
									fprintf( script_file, "%s ", proj_flags[jj].token );
									printed++;
								}
							}
							
							if( !printed )
								fprintf( script_file, "-" );
							
							fprintf( script_file, "\n//\n" );
						}
						
						break;
					}
				}
			}
			
			fclose( script_file );
		}
		
		break;
			
		case VS_CREATE_SOUNDS:
		{
			tag_file *tf;
			int ii;
			
			tf = new_empty_tag_file();
			if( !tf )
				break;
				
			for( ii = 0; ii < script->parms_used; ii++ )
			{
				if( script->parms[ii].create_type.soun.id == AUTOGENERATE_TAG_TOKEN )
					script->parms[ii].create_type.soun.id = new_tag_id_from_string( script->parms[ii].create_type.soun.name, 'soun', NULL );

				if( script->parms[ii].create_type.soun.subtitle_stringlist == AUTOGENERATE_TAG_TOKEN )
					script->parms[ii].create_type.soun.subtitle_stringlist = 0;

				if( !create_sound_data( tf, &script->parms[ii].create_type.soun ) )
				{
					if( script_log )
						fprintf( script_log, "script line %d: (%s) sound creation failed\n", script->parms[ii].line, CREATE_SOUNDS_TOKEN );
				}
			}

			XGFileSpecifier fs( script->data );
			
			if( !write_tags_to_file( fs, tf, false, TAG_FILE_TYPE_PLUGIN ) )
			{
				if( script_log )
					fprintf( script_log, "script line %d: (%s) could not write tag file\n", script->parms[ii].line, CREATE_SOUNDS_TOKEN );
			}
		}

		break;
		
		case VS_EXPORT_TEXT:
		{
			tag_entry *te;
			string_list_definition text;
			
			FILE *fp;
			
			bool read;
			
			int ii;
			
			for( ii = 0; ii < script->parms_used; ii++ )
			{
				te = get_entry( script->parms[ii].tag_type, script->parms[ii].tag_id );
				if( !te )
				{
					if( script_log )
						fprintf(
							script_log,
							"script line %d: (%s) could not find tag %s/%s\n",
							script->parms[ii].line,
							EXPORT_TEXT_TOKEN,
							tagstr( script->parms[ii].tag_type ),
							tagstr( script->parms[ii].tag_id )
							);
					
					continue;
				}

				// check the type
				switch( script->parms[ii].tag_type )
				{
					case 'stli':
					{
						read = read_string_list_definition( (string_list_definition *)&text, te );
					}
					
					break;
					
					case 'text':
					{
						read = read_string_definition( (string_definition *)&text, te );
					}
					
					break;
					
					case 'temp':
					{
						read = read_template_definition( (template_definition *)&text, te );
					}
					
					break;
					
					default:
						if( script_log )
							fprintf( script_log, "script line %d: (%s) the tag type to export from was not recognized\n", script->parms[ii].line, EXPORT_TEXT_TOKEN );
				}
				
				if( read )
				{
					fp = fopen( script->parms[ii].strings[0], "wt" );
					if( fp )
					{
						if( fwrite( text.string_buffer, text.string_length, 1, fp ) != 1 )
						{
							fprintf(
								script_log,
								"script line %d: (%s) error writting to output file %s\n",
								script->parms[ii].line,
								EXPORT_TEXT_TOKEN,
								script->parms[ii].strings[0]
								);
						}
						
						fclose( fp );
					}
					else
					{
						if( script_log )
							fprintf(
								script_log,
								"script line %d: (%s) could not open output file %s\n",
								script->parms[ii].line,
								EXPORT_TEXT_TOKEN,
								script->parms[ii].strings[0]
								);
					}
				}
				else
				{
					if( script_log )
						fprintf(
							script_log,
							"script line %d: (%s) unable to retrive data from %s/%s\n",
							script->parms[ii].line,
							EXPORT_TEXT_TOKEN,
							tagstr( script->parms[ii].tag_type ),
							tagstr( script->parms[ii].tag_id )
							);
				}
			}
		}
		
		break;
		
		case VS_IMPORT_TEXT:
		{
			tag_file *tf;
			string_list_definition text;
			
			FILE *fp;
			
			int ii;
			
			tf = new_empty_tag_file();
			
			for( ii = 0; ii < script->parms_used; ii++ )
			{
				fp = fopen( script->parms[ii].strings[1], "rt" );
				if( fp )
				{
					fseek( fp, 0, SEEK_END );
					text.string_length = ftell( fp );
					fseek( fp, 0, SEEK_SET );
					
					text.string_length = fread( text.string_buffer, 1, text.string_length, fp );
					fclose( fp );
					if( text.string_length )
					{
						add_tag_entry(
							tf,
							script->parms[ii].tag_type,
							script->parms[ii].tag_id,
							get_type_version( script->parms[ii].tag_type ),
							script->parms[ii].strings[0],
							0,
							text.string_length,
							text.string_buffer,
							false
							);
					}
					else
					{
						if( script_log )
							fprintf(
								script_log,
								"script line %d: (%s) couldn't read file %s\n",
								script->parms[ii].line,
								IMPORT_TEXT_TOKEN,
								script->parms[ii].strings[1]
								);
					}
				}
				else
				{
					if( script_log )
						fprintf(
							script_log,
							"script line %d: (%s) couldn't open file %s\n",
							script->parms[ii].line,
							IMPORT_TEXT_TOKEN,
							script->parms[ii].strings[1]
							);
				}
			}
			
			XGFileSpecifier tfs( script->data );
			if( !write_tags_to_file( tfs, tf, false, TAG_FILE_TYPE_PLUGIN ) )
			{
				if( script_log )
					fprintf( script_log, "script line %d: (%s) file failed to write\n", script->parms[ii].line, IMPORT_TEXT_TOKEN );
			}
					
			close_tag_file( tf );
		}
		
		break;
		
		case VS_DUMP_NAMES:
		{
			tag_file *tf;
			tag_group *tg;
			tag_entry *te;
			
			FILE *fp;
			
			int ii;
			
			for( ii = 0; ii < script->parms_used; ii++ )
			{
				fp = fopen( script->parms[ii].strings[0], "w" );
				if( fp )
				{
					for( tf = tag_file_list; tf; tf = tf->next )
					{
						tg = get_group_in_file( tf, script->parms[ii].tag_type );
						if( tg )
						{
							for( te = get_next_entry_in_group( tg, 0 ); te; te = get_next_entry_in_group( tg, te ) )
							{
								fprintf( fp, "%s:%s\n", tagstr( te->id ), te->name );
							}
						}
					}
					
					fclose( fp );
				}
				else
				{
					if( script_log )
						fprintf(
							script_log,
							"script line %d: (%s) couldn't open output file %s\n",
							script->parms[ii].line,
							DUMP_NAMES_TOKEN,
							script->parms[ii].strings[0]
							);
				}
			}
		}
		
		break;
	}
	
	fprintf( script_log, "Finished with script %s\n\n", script->fs->GetFileName().Get() );
	fclose( script_log );
	
	return TRUE;
#endif
}
