
#include "proj.h"

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

#include "RevengeApp.h"
#include <XMessages.h>
#include "tagpick.h"
#include <assert.h>
#include <XPopup.h>
#include <XWindow.h>
#include <XDialog.h>
#include "skelmodel.h"
#include "formatters.h"
#include "vengeance_script.h"
#include <XError.h>


// ==============================  BYTE SWAPPING DATA  ========================================
static byte_swap_code projectile_definition_byte_swapping_data[]=
{
	_begin_bs_array, 1,
	
	// unsigned long flags;
	_4byte,
	
	// file_tag collection_tag;
	_4byte,

	// short sequence_indexes[MAXIMUM_NUMBER_OF_PROJECTILE_DEFINITION_SEQUENCES];
	
		_begin_bs_array, MAXIMUM_NUMBER_OF_PROJECTILE_DEFINITION_SEQUENCES,
		
		_2byte,
		
		_end_bs_array,
	
	// short splash_or_rebound_effect_type;
	// file_tag detonation_projectile_group_tag;
	_2byte,
	_4byte,
	
	// file_tag contrail_projectile_tag;
	// short ticks_between_contrails, maximum_contrail_count;
	_4byte,
	_2byte, _2byte,
	
	// file_tag object_tag;
	_4byte,

	// short_fixed inertia_lower_bound, inertia_delta; // 1.0 is standard, 0.0 is infinite (how much acceleration we accept)
	// short_world_distance random_initial_velocity; // applied in a random direction
	// short volume;
	// short tracking_priority;
	// fixed_fraction promotion_on_detonation_fraction; // chance this projectile will be promoted instead of detonating
	_2byte, _2byte,
	_2byte,
	_2byte,
	_2byte,
	_2byte,
	
	// fixed_fraction detonation_frequency, media_detonation_frequency;
	// short_world_distance detonation_velocity; // can be zero (will take highest of local and external)
	_2byte, _2byte,
	_2byte,
	
	// short projectile_class;
	_2byte,
	
	// file_tag lightning_tag;
	_4byte,
	
	// short unused[2];
	2*sizeof(short),
	// file_tag sound_tags[MAXIMUM_NUMBER_OF_PROJECTILE_DEFINITION_SOUNDS];
	_begin_bs_array, MAXIMUM_NUMBER_OF_PROJECTILE_DEFINITION_SOUNDS, _4byte, _end_bs_array,
	
	// short_fixed animation_rate_lower_bound, animation_rate_delta;
	// short lifespan_lower_bound, lifespan_delta; // in ticks, zero is maximum
	_2byte, _2byte,
	_2byte, _2byte,
	
	// fixed_fraction initial_contrail_frequency, final_contrail_frequency;
	// short contrail_frequency_delta;
	// angle guided_turning_speed;
	_2byte, _2byte,
	_2byte,
	_2byte,
	
	// file_tag promoted_projectile_tag; // the tag we become if we are promoted
	// file_tag promotion_projectile_group_tag; // the projectile group created if we are promoted
	// file_tag mesh_stain_texturestacks_tag;
	_4byte,
	_4byte,
	_4byte,
	
	// struct damage_definition damage;

		// short type;
		// word flags;
		_2byte,
		_2byte,
		
		// short damage_lower_bound, damage_delta;
		_2byte, _2byte,

		// short_world_distance radius_lower_bound, radius_delta;
		// short_world_distance rate_of_expansion; // world units per tick
		_2byte, _2byte,
		_2byte,

		// short_fixed damage_to_velocity;
		_2byte,
	
	// file_tag artifact_tag;
	// file_tag target_detonation_projectile_group_tag;
	// file_tag geometry_tag;
	_4byte,
	_4byte,
	_4byte,
		
	// short delay_lower_bound, delay_delta;
	_2byte,
	_2byte,

	// file_tag local_projectile_group_tag;
	_4byte,
	
	// short nearby_target_radius; // in cells; only valid if bit set
	// word pad2;
	_2byte,
	sizeof(short),
	
	// short skel_model_code;
	_2byte,
	
	// blood_rgba
	4 * sizeof(byte),
	
	// short unused3[40];
	35*sizeof(short),
	
	// computed during postprocessing
	
	// short mesh_stain_txst_index;
	// short local_projectile_group_type;
	// short geometry_index;
	// short target_detonation_projectile_group_type;
	// short lightning_index, artifact_index;
	// short detonation_projectile_group_type, contrail_projectile_type;
	// short promotion_projectile_group_type, promoted_projectile_type;
	10*sizeof(short),
	
	// short sound_indexes[MAXIMUM_NUMBER_OF_PROJECTILE_DEFINITION_SOUNDS];
	// short collection_index, color_table_index;
	// short object_type;
	8*sizeof(short),
	
	_end_bs_array
};

const char *DamageTypeNames[] = {
	"NONE",
	"Explosion",
	"Magical",
	"Holding (i.e. shocking)",
	"Healing",
	"Kinetic",
	"Slashing Metal",
	"Stoning",
	"Slashing Claws",
	"Explosive Electricity",
	"Fire",
	"Gas",
	"Charm",
	"Tain",
	0
};

// ==============================  EDIT WINDOW  ========================================
void edit_proj( tag_entry* e )
{
	TprojEditForm* form = new TprojEditForm( e );
	form->AttachWindow( 1020 );
}


void dependency_proj( tag_entry *e )
{
	projectile_definition def;
	read_projectile_definition( &def, e );
	
	tag artType = (def.skelmodel_code == 0xFEDC) ? 'skmd' : '.256';
	tag contrailType = (def.flags & _projectile_uses_sprite_contrail_flag) ? 'spri' : 'proj';
	add_tag_dependency( artType, def.collection_tag );
	add_tag_dependency( 'prgr', def.detonation_projectile_group_tag );
	add_tag_dependency( contrailType, def.contrail_projectile_or_sprite_tag );
	add_tag_dependency( 'obje', def.object_tag );
	add_tag_dependency( 'ligh', def.lightning_tag );
	for (int i = 0; i < MAXIMUM_NUMBER_OF_PROJECTILE_DEFINITION_SOUNDS; ++i)
	{
		add_tag_dependency( 'soun', def.sound_tags[i] );
	}
	add_tag_dependency( 'proj', def.promoted_projectile_tag );
	add_tag_dependency( 'arti', def.artifact_tag );
	add_tag_dependency( 'prgr', def.promotion_projectile_group_tag );
	add_tag_dependency( 'prgr', def.target_detonation_projectile_group_tag );
	add_tag_dependency( 'lpgr', def.local_projectile_group_tag );
	add_tag_dependency( 'txst', def.bounce_stain_texturestacks_tag );
	add_tag_dependency( 'txst', def.detonation_stain_texturestacks_tag );
}

void update_proj( tag_entry *e )
{
	int i, j, k;
	
	for (;;)
	{
		int version = get_entry_version( e );
		switch (version)
		{
			case 1: {
					projectile_definition def;
					read_projectile_definition( &def, e );
					def.bounce_stain_texturestacks_tag = -1;
					def.detonation_stain_texturestacks_tag = -1;
					save_projectile_definition( &def, e );
					set_entry_version( e, 2 ); // no changes!
				} break;
				
			case 2:
				return;
			default:
				Assert(0);
				return;
		}
	}
}


// ==============================  READ TAG DATA  ========================================
bool read_projectile_definition( projectile_definition* def, tag_entry* e )
{
	return load_tag_data_into_struct( "proj", e, def, SIZEOF_STRUCT_PROJECTILE_DEFINITION, 1, projectile_definition_byte_swapping_data);
}


// ==============================  SAVE TAG DATA  ========================================
bool save_projectile_definition( projectile_definition* def, tag_entry* e )
{
	return save_tag_data_from_struct( "proj", e, def, SIZEOF_STRUCT_PROJECTILE_DEFINITION, 1, projectile_definition_byte_swapping_data);
}


// ==============================  READ TAG DATA (WITH ID) ========================================
bool read_projectile_definition( projectile_definition* def, tag id )
{
	tag_entry* e;
	
	assert( def );
	
	e = get_entry( 'proj', id );
	if( 0 == e ) {
		return FALSE;
	}
	
	return read_projectile_definition( def, e );	
}


// ==============================  SAVE TAG DATA (WITH ID) ========================================
bool save_projectile_definition( projectile_definition* def, tag id )
{
	tag_entry* e;
	
	assert( def );
	
	e = get_entry( 'proj', id );
	if( 0 == e ) {
		return FALSE;
	}

	return save_projectile_definition( def, e );
}


// ==============================  DISPATCH  ========================================
BEGINDISPATCH(TprojEditForm,TTagEditForm)
	DISPATCHTABLE(KXGControlClick,TprojEditForm::DoControlClick)
ENDDISPATCH


TprojEditForm::TprojEditForm( struct tag_entry *e ) : TTagEditForm( e )
{
}

// ==============================  INITIALIZE FORM =======================================
void TprojEditForm::DoFormInitialize()
{
	TTagEditForm::DoFormInitialize();

}

static long EditDamageProc( XGDialog* dlog, void* data, long msg, long arg, void* )
{
	XGPopControl *pList = dynamic_cast<XGPopControl *>(dlog->FindViewByID( 101 ));

	damage_definition *damage = (damage_definition *)data;
	
	int ii;
	
	switch( msg )
	{
		case KXGDialogMessage:
		{
			switch( arg )
			{
				case KXGDMInitDialog:
				{
					pList->DeleteAll();
					for( ii = 0; DamageTypeNames[ii] != 0; ii++ )
					{
						pList->Insert( ii, DamageTypeNames[ii] );
					}
					
					pList->SetValue( damage->type + 1 );
					
					SetShortFixed( dlog, 102, damage->damage_lower_bound );
					SetShortFixed( dlog, 103, get_upper_bound( damage->damage_lower_bound, damage->damage_delta ) );
					SetWorldDistance( dlog, 104, damage->radius_lower_bound );
					SetWorldDistance( dlog, 105, get_upper_bound( damage->radius_lower_bound, damage->radius_delta ) );
					SetShortWorldDistance( dlog, 106, damage->rate_of_expansion );
					SetShortFixed( dlog, 107, damage->damage_to_velocity );

					for( ii = 0; ii < NUMBER_OF_DAMAGE_DEFINITION_FLAGS; ii++ )
					{
						if( ii == 3 )
							continue;
							
						dlog->SetValue( ii + 201, (bool)(damage->flags & (1 << ii)) );
					}
					
					return 0;
				}
				
				default:
					break;
			}
		}
		case KXGControlClick:
		{
			switch( arg )
			{
				case 1001: // ok
				{
					damage->type = pList->GetValue() - 1;
					
					damage->damage_lower_bound = GetShortFixed( dlog, 102 );
					damage->damage_delta = get_delta( GetShortFixed( dlog, 102 ), GetShortFixed( dlog, 103 ) );
					damage->radius_lower_bound = GetWorldDistance( dlog, 104 );
					damage->radius_delta = get_delta( GetWorldDistance( dlog, 104 ), GetWorldDistance( dlog, 105 ) );
					damage->rate_of_expansion = GetShortWorldDistance( dlog, 106 );
					damage->damage_to_velocity = GetShortFixed( dlog, 107 );
					
					// flags
					for( ii = 0; ii < NUMBER_OF_DAMAGE_DEFINITION_FLAGS; ii++ )
					{
						if( ii == 3 )
							continue;
						
						dlog->GetValue( ii + 201 ) ? damage->flags |= (1 << ii) : damage->flags &= ~(1 << ii);
					}
					
					dlog->EndDialog( 1 );
					return 0;
				}
				case 1002: // cancel
				{
					dlog->EndDialog( 0 );
					return 0;
				}
				case 201:
				case 202:
				case 203:
//				case 204:
				case 205:
				case 206:
				case 207:
				case 208:
				case 209:
				case 210:
				case 211:
				case 212:
				case 213:
				case 214:
				case 215:
				case 216: // flags
				{
					int val = !dlog->GetValue( arg );
					dlog->SetValue( arg, val );
					
					return 0;
				}
				
				default:
					break;
			}
		}
		
		default:
			break;
	}
	
	return -1;
}

static long EditFlagsProc( XGDialog* dlog, void* flags_void_ptr, long msg, long arg, void* )
{
	if( !dlog )
		return -1;
		
	unsigned long *flags_ptr = (unsigned long *)flags_void_ptr;
	
	switch(msg)
	{
		case KXGDialogMessage:
		{
			switch(arg)
			{
				case KXGDMInitDialog:
				{
					dlog->SetValue( 21501, ((*flags_ptr) & _projectile_uses_owner_color_table_index_flag) ? 1 : 0 );
					dlog->SetValue( 21502, ((*flags_ptr) & _projectile_angle_follows_trajectory_flag) ? 1 : 0 );
					dlog->SetValue( 21503, ((*flags_ptr) & _projectile_can_be_mirrored_flag) ? 1 : 0 );
					dlog->SetValue( 21504, ((*flags_ptr) & _projectile_becomes_dormant_at_rest_flag) ? 1 : 0 );
					dlog->SetValue( 21505, ((*flags_ptr) & _projectile_animates_at_rest_flag) ? 1 : 0 );
					dlog->SetValue( 21506, ((*flags_ptr) & _projectile_bloodies_landscape_flag) ? 1 : 0 );
					dlog->SetValue( 21507, ((*flags_ptr) & _projectile_is_on_fire_flag) ? 1 : 0 );
					dlog->SetValue( 21508, ((*flags_ptr) & _projectile_passes_through_target_flag) ? 1 : 0 );
					dlog->SetValue( 21509, ((*flags_ptr) & _projectile_cannot_be_mirrored_vertically_flag) ? 1 : 0 );
					dlog->SetValue( 21510, ((*flags_ptr) & _projectile_floats_flag) ? 1 : 0 );
					dlog->SetValue( 21511, ((*flags_ptr) & _projectile_is_media_surface_effect_flag) ? 1 : 0 );
					dlog->SetValue( 21512, ((*flags_ptr) & _projectile_continually_detonates_flag) ? 1 : 0 );
					dlog->SetValue( 21513, ((*flags_ptr) & _projectile_is_lightning_flag) ? 1 : 0 );
					dlog->SetValue( 21514, ((*flags_ptr) & _projectile_chooses_nearby_target_flag) ? 1 : 0 );
					dlog->SetValue( 21515, ((*flags_ptr) & _projectile_only_destroyed_by_lifespan_flag) ? 1 : 0 );
					dlog->SetValue( 21516, ((*flags_ptr) & _projectile_makes_owner_monster_visible_flag) ? 1 : 0 );
					dlog->SetValue( 21517, ((*flags_ptr) & _projectile_can_set_landscape_on_fire_flag) ? 1 : 0 );
					dlog->SetValue( 21518, ((*flags_ptr) & _projectile_centered_on_target_flag) ? 1 : 0 );
					dlog->SetValue( 21519, ((*flags_ptr) & _projectile_marks_target_has_having_been_chosen_flag) ? 1 : 0 );
					dlog->SetValue( 21520, ((*flags_ptr) & _projectile_is_bloody_do_not_draw_flag) ? 1 : 0 );
					dlog->SetValue( 21521, ((*flags_ptr) & _projectile_is_guided_flag) ? 1 : 0 );
					dlog->SetValue( 21522, ((*flags_ptr) & _projectile_definition_detonates_immediately_flag) ? 1 : 0 );
				}
				
				return 0;
			}
			
		} break;
		
		case KXGControlClick:
		{
			if( arg > 21500 && arg < 21523 )
			{
				dlog->SetValue( arg, !dlog->GetValue( arg ) );
			}
			
			if( arg == 21523 ) // OK
			{
				dlog->GetValue( 21501 ) ? (*flags_ptr |= _projectile_uses_owner_color_table_index_flag) : (*flags_ptr &= ~_projectile_uses_owner_color_table_index_flag);
				dlog->GetValue( 21502 ) ? (*flags_ptr |= _projectile_angle_follows_trajectory_flag) : (*flags_ptr &= ~_projectile_angle_follows_trajectory_flag);
				dlog->GetValue( 21503 ) ? (*flags_ptr |= _projectile_can_be_mirrored_flag) : (*flags_ptr &= ~_projectile_can_be_mirrored_flag);
				dlog->GetValue( 21504 ) ? (*flags_ptr |= _projectile_becomes_dormant_at_rest_flag) : (*flags_ptr &= ~_projectile_becomes_dormant_at_rest_flag);
				dlog->GetValue( 21505 ) ? (*flags_ptr |= _projectile_animates_at_rest_flag) : (*flags_ptr &= ~_projectile_animates_at_rest_flag);
				dlog->GetValue( 21506 ) ? (*flags_ptr |= _projectile_bloodies_landscape_flag) : (*flags_ptr &= ~_projectile_bloodies_landscape_flag);
				dlog->GetValue( 21507 ) ? (*flags_ptr |= _projectile_is_on_fire_flag) : (*flags_ptr &= ~_projectile_is_on_fire_flag);
				dlog->GetValue( 21508 ) ? (*flags_ptr |= _projectile_passes_through_target_flag) : (*flags_ptr &= ~_projectile_passes_through_target_flag);
				dlog->GetValue( 21509 ) ? (*flags_ptr |= _projectile_cannot_be_mirrored_vertically_flag) : (*flags_ptr &= ~_projectile_cannot_be_mirrored_vertically_flag);
				dlog->GetValue( 21510 ) ? (*flags_ptr |= _projectile_floats_flag) : (*flags_ptr &= ~_projectile_floats_flag);
				dlog->GetValue( 21511 ) ? (*flags_ptr |= _projectile_is_media_surface_effect_flag) : (*flags_ptr &= ~_projectile_is_media_surface_effect_flag);
				dlog->GetValue( 21512 ) ? (*flags_ptr |= _projectile_continually_detonates_flag) : (*flags_ptr &= ~_projectile_continually_detonates_flag);
				dlog->GetValue( 21513 ) ? (*flags_ptr |= _projectile_is_lightning_flag) : (*flags_ptr &= ~_projectile_is_lightning_flag);
				dlog->GetValue( 21514 ) ? (*flags_ptr |= _projectile_chooses_nearby_target_flag) : (*flags_ptr &= ~_projectile_chooses_nearby_target_flag);
				dlog->GetValue( 21515 ) ? (*flags_ptr |= _projectile_only_destroyed_by_lifespan_flag) : (*flags_ptr &= ~_projectile_only_destroyed_by_lifespan_flag);
				dlog->GetValue( 21516 ) ? (*flags_ptr |= _projectile_makes_owner_monster_visible_flag) : (*flags_ptr &= ~_projectile_makes_owner_monster_visible_flag);
				dlog->GetValue( 21517 ) ? (*flags_ptr |= _projectile_can_set_landscape_on_fire_flag) : (*flags_ptr &= ~_projectile_can_set_landscape_on_fire_flag);
				dlog->GetValue( 21518 ) ? (*flags_ptr |= _projectile_centered_on_target_flag) : (*flags_ptr &= ~_projectile_centered_on_target_flag);
				dlog->GetValue( 21519 ) ? (*flags_ptr |= _projectile_marks_target_has_having_been_chosen_flag) : (*flags_ptr &= ~_projectile_marks_target_has_having_been_chosen_flag);
				dlog->GetValue( 21520 ) ? (*flags_ptr |= _projectile_is_bloody_do_not_draw_flag) : (*flags_ptr &= ~_projectile_is_bloody_do_not_draw_flag);
				dlog->GetValue( 21521 ) ? (*flags_ptr |= _projectile_is_guided_flag) : (*flags_ptr &= ~_projectile_is_guided_flag);
				dlog->GetValue( 21522 ) ? (*flags_ptr |= _projectile_definition_detonates_immediately_flag) : (*flags_ptr &= ~_projectile_definition_detonates_immediately_flag);
				dlog->EndDialog( 1 );
			}
			
			if( arg == 21524 ) // Cancel
			{
				dlog->EndDialog( 0 );
			}
			
		}
		
		break;
	}
	
	return -1;
}

// ==============================  INPUT HANDLING  ========================================
long TprojEditForm::DoControlClick( long arg, void *ptr )
{
#ifdef __MWERKS__
	#pragma unused(ptr)
#endif

	tag_entry* e;
	tag new_id;
	tag artType;
	tag contrailType;
	unsigned long flags;
	
	artType = GetFlag(12500) ? 'skmd' : '.256';
	contrailType = GetFlag(12501) ? 'spri' : 'proj';
	
	switch( arg )
	{
		case 12001:			// Choose a tag
			new_id = pick_tag_of_type(artType, GetTag( 12001 ), fEntry );
			if(new_id != 0){
				SetTag( 12001, artType, new_id );
				LoadAnimLists();
			}
		break; 
		case 12002:			// Open tag window
			e = get_entry(artType, GetTag( 12001 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12003:			// Choose a tag
			new_id = pick_tag_of_type('obje', GetTag( 12003 ), fEntry );
			if(new_id != 0) SetTag( 12003, 'obje', new_id );
		break; 
		case 12004:			// Open tag window
			e = get_entry('obje', GetTag( 12003 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12005:			// Choose a tag
			new_id = pick_tag_of_type('ligh', GetTag( 12005 ), fEntry );
			if(new_id != 0) SetTag( 12005, 'ligh', new_id );
		break; 
		case 12006:			// Open tag window
			e = get_entry('ligh', GetTag( 12005 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12007:			// Choose a tag
			new_id = pick_tag_of_type('arti', GetTag( 12007 ), fEntry );
			if(new_id != 0) SetTag( 12007, 'arti', new_id );
		break; 
		case 12008:			// Open tag window
			e = get_entry('arti', GetTag( 12007 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12009:			// Choose a tag
			new_id = pick_tag_of_type('lpgr', GetTag( 12009 ), fEntry );
			if(new_id != 0) SetTag( 12009, 'lpgr', new_id );
		break; 
		case 12010:			// Open tag window
			e = get_entry('lpgr', GetTag( 12009 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12017:			// Choose a tag
			new_id = pick_tag_of_type('prgr', GetTag( 12017 ), fEntry );
			if(new_id != 0) SetTag( 12017, 'prgr', new_id );
		break; 
		case 12018:			// Open tag window
			e = get_entry('prgr', GetTag( 12017 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12019:			// Choose a tag
			new_id = pick_tag_of_type('prgr', GetTag( 12019 ), fEntry );
			if(new_id != 0) SetTag( 12019, 'prgr', new_id );
		break; 
		case 12020:			// Open tag window
			e = get_entry('prgr', GetTag( 12019 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12029:			// Choose a tag
			new_id = pick_tag_of_type(contrailType, GetTag( 12029 ), fEntry );
			if(new_id != 0) SetTag( 12029, contrailType, new_id );
		break; 
		case 12030:			// Open tag window
			e = get_entry(contrailType, GetTag( 12029 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12045:			// Choose a tag
			new_id = pick_tag_of_type('proj', GetTag( 12045 ), fEntry );
			if(new_id != 0) SetTag( 12045, 'proj', new_id );
		break; 
		case 12046:			// Open tag window
			e = get_entry('proj', GetTag( 12045 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12047:			// Choose a tag
			new_id = pick_tag_of_type('prgr', GetTag( 12047 ), fEntry );
			if(new_id != 0) SetTag( 12047, 'prgr', new_id );
		break; 
		case 12048:			// Open tag window
			e = get_entry('prgr', GetTag( 12047 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12052:			// Choose a tag
			new_id = pick_tag_of_type('soun', GetTag( 12052 ), fEntry );
			if(new_id != 0) SetTag( 12052, 'soun', new_id );
		break; 
		case 12053:			// Open tag window
			e = get_entry('soun', GetTag( 12052 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12054:			// Choose a tag
			new_id = pick_tag_of_type('soun', GetTag( 12054 ), fEntry );
			if(new_id != 0) SetTag( 12054, 'soun', new_id );
		break; 
		case 12055:			// Open tag window
			e = get_entry('soun', GetTag( 12054 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12072:			// Choose a tag
			new_id = pick_tag_of_type('txst', GetTag( arg ), fEntry );
			if(new_id != 0) SetTag( arg, 'txst', new_id );
		break; 
		case 12073:			// Open tag window
			e = get_entry('txst', GetTag( arg-1 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12074:			// Choose a tag
			new_id = pick_tag_of_type('txst', GetTag( arg ), fEntry );
			if(new_id != 0) SetTag( arg, 'txst', new_id );
		break; 
		case 12075:			// Open tag window
			e = get_entry('txst', GetTag( arg-1 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

		case 12500:
			{
				bool f = !GetFlag(12500);
				SetFlag(12500, f);
				def.skelmodel_code = ( f ? 0xFEDC : 0x0000 );
				artType = f ? 'skmd' : '.256';
				SetTag( 12001, artType, def.collection_tag );
			}
		break;
		
		case 12501:
			{
				bool f = !GetFlag(12501);
				SetFlag(12501, f);
				contrailType = f ? 'spri' : 'proj';
				SetTag( 12029, contrailType, def.contrail_projectile_or_sprite_tag );
			}
		break;
		
		case 12066:			// Open Damage Dialog (2002)
		{
			damage_definition damage = def.damage;
			bool ok = XGDialog::ModalDialog( KWindowProjectileDamage, EditDamageProc, &damage);
			if(ok)
				def.damage = damage;
		}
		break;

		case 12067:			// Open Projectile Flags Dialog (2015)
		{
			flags = def.flags;
			bool ok = XGDialog::ModalDialog( KWindowProjectileFlags, EditFlagsProc, &flags);
			if(ok)
				def.flags = flags;
		}
		break;
		
		default:
			return -1;
	}

	return 0;
}

		
// ========================================  SAVE TO TAG  ==================================================
void TprojEditForm::CommitToTag()
{
	GetValues();
	save_projectile_definition( &def, fEntry );
}


// ========================================  RELOAD TAG  ==================================================
void TprojEditForm::ReloadTag()
{
	read_projectile_definition( &def, fEntry );
	SetValues();
}


// ========================================  TAG NEEDS UPDATING  ==================================================
bool TprojEditForm::NeedCommit()
{
	GetValues();
	return tag_data_needs_update( "proj", fEntry, &def, SIZEOF_STRUCT_PROJECTILE_DEFINITION, 1, projectile_definition_byte_swapping_data);
}

static void ExtractCollectionNames( tag_entry* e, XGStringList* stli )
{
#if 0
	for(int i = 0; i < 5; i++)
		stli->Insert(i, "I should write this");
#else
	stli->Insert( 0, "Collections Unsupported" );
#endif
}

void TprojEditForm::LoadAnimLists()
{
	int i;
	XGStringList stli;
	tag_entry* e;
	char buff[256];
	
	tag artType = (def.skelmodel_code == 0xFEDC) ? 'skmd' : '.256';
	
	e = get_entry( artType, GetTag(12001) );
	if(e)
	{
		if( artType == 'skmd' )
			ExtractSKMDNames( e, &stli );
		else
			ExtractCollectionNames( e, &stli );
	}
	else
		stli.Insert(0, "(Entry Not Found)");
		
	XGPopControl* pList1;
	pList1 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12011 ));
	assert(pList1);
	pList1->DeleteAll();
	pList1->Insert(0, "(None)");
	for (i = 0; i < stli.GetNumStrings(); i++) {
		stli.GetString(i, buff);
		pList1->Insert(i+1, buff);
	}
	pList1->SetValue( def.sequence_indexes[_projectile_sequence_in_flight]+1);

	XGPopControl* pList2;
	pList2 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12013 ));
	assert(pList2);
	pList2->DeleteAll();
	pList2->Insert(0, "(None)");
	for (i = 0; i < stli.GetNumStrings(); i++) {
		stli.GetString(i, buff);
		pList2->Insert(i+1, buff);
	}
	pList2->SetValue( def.sequence_indexes[_projectile_sequence_detonation_stain]+1);

	XGPopControl* pList3;
	pList3 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12015 ));
	assert(pList3);
	pList3->DeleteAll();
	pList3->Insert(0, "(None)");
	for (i = 0; i < stli.GetNumStrings(); i++) {
		stli.GetString(i, buff);
		pList3->Insert(i+1, buff);
	}
	pList3->SetValue( def.sequence_indexes[_projectile_sequence_bounce_stain]+1);
}

// ========================================  SET INTERFACE VALUES  ==================================================
void TprojEditForm::SetValues()
{
	tag artType = (def.skelmodel_code == 0xFEDC) ? 'skmd' : '.256';
	tag contrailType = (def.flags & _projectile_uses_sprite_contrail_flag) ? 'spri' : 'proj';
	
	SetTag( 12001, artType, def.collection_tag );
	SetTag( 12003, 'obje', def.object_tag );
	SetTag( 12005, 'ligh', def.lightning_tag );
	SetTag( 12007, 'arti', def.artifact_tag );
	SetTag( 12009, 'lpgr', def.local_projectile_group_tag );
	SetTag( 12017, 'prgr', def.detonation_projectile_group_tag );
	SetTag( 12019, 'prgr', def.target_detonation_projectile_group_tag );
	SetTag( 12029, contrailType, def.contrail_projectile_or_sprite_tag );
	SetTag( 12045, 'proj', def.promoted_projectile_tag );
	SetTag( 12047, 'prgr', def.promotion_projectile_group_tag );
	SetTag( 12052, 'soun', def.sound_tags[_projectile_sound_flight] );
	SetTag( 12054, 'soun', def.sound_tags[_projectile_sound_rebound] );
	
	LoadAnimLists();
	
	XGPopControl* pList4;
	pList4 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12021 ));
	assert(pList4);
	pList4->DeleteAll();
	pList4->Insert(0, "None");
	pList4->Insert(1, "Small");
	pList4->Insert(2, "Medium");
	pList4->Insert(3, "Large");
	pList4->SetValue( def.splash_or_rebound_effect_type + 1);

	XGPopControl* pList5;
	pList5 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12038 ));
	assert(pList5);
	pList5->DeleteAll();
	pList5->Insert(0, "None");
	pList5->Insert(1, "Unwieldy");
	pList5->Insert(2, "Small");
	pList5->Insert(3, "Large");
	pList5->SetValue( def.volume + 1);
	
	XGPopControl* pList6;
	pList6 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12051 ));
	assert(pList6);
	pList6->DeleteAll();
	pList6->Insert(0, "None");
	pList6->Insert(1, "Unknown");
	pList6->Insert(2, "Head");
	pList6->Insert(3, "Arm");
	pList6->Insert(4, "Leg");
	pList6->Insert(5, "Torso");
	pList6->Insert(6, "Bloody Chunk");
	pList6->Insert(7, "Leathal Object");
	pList6->Insert(8, "Non-leathal Object");
	pList6->Insert(9, "Explosive Object");
	pList6->Insert(10, "Potentially Explosive Object");
	pList6->Insert(11, "Item");
	pList6->SetValue( def.projectile_class + 1);

	SetFlag( 12500, def.skelmodel_code == 0xFEDC );
	
	SetFlag( 12025, FALSE );
	if(def.flags&_projectile_detonates_when_animation_loops_flag ) SetFlag( 12025, TRUE );
	SetFlag( 12026, FALSE );
	if(def.flags&_projectile_detonates_when_transfer_loops_flag ) SetFlag( 12026, TRUE );
	SetFlag( 12027, FALSE );
	if(def.flags&_projectile_detonates_at_rest_flag ) SetFlag( 12027, TRUE );
	SetFlag( 12028, FALSE );
	if(def.flags&_projectile_remains_after_detonation_flag ) SetFlag( 12028, TRUE );

	SetFlag( 12037, FALSE );
	if(def.flags&_projectile_contrail_frequency_reset_after_bouncing_flag ) SetFlag( 12037, TRUE );
	SetFlag( 12042, FALSE );
	if(def.flags&_projectile_affected_by_wind_flag ) SetFlag( 12042, TRUE );
	SetFlag( 12043, FALSE );
	if(def.flags&_projectile_cannot_be_accelerated_flag ) SetFlag( 12043, TRUE );
	SetFlag( 12044, FALSE );
	if(def.flags&_projectile_is_melee_attack_flag ) SetFlag( 12044, TRUE );
	SetFlag( 12050, FALSE );
	if(def.flags&_projectile_is_promoted_at_end_of_lifespan_flag ) SetFlag( 12050, TRUE );
	SetFlag( 12501, FALSE );
	if(def.flags&_projectile_uses_sprite_contrail_flag ) SetFlag( 12501, TRUE );

	SetPercentage( 12022, def.detonation_frequency );
	SetPercentage( 12023, def.media_detonation_frequency );
	SetShortWorldDistance( 12024, def.detonation_velocity );
	SetInt ( 12031, def.ticks_between_contrails );
	SetInt ( 12033, def.maximum_contrail_count );
	SetPercentage( 12034, def.initial_contrail_frequency );
	SetPercentage( 12035, def.final_contrail_frequency );
	SetPercentage( 12036, def.contrail_frequency_delta );
	SetShortFixed ( 12039, def.inertia_lower_bound );
	SetShortFixed ( 12040, get_upper_bound( def.inertia_lower_bound, def.inertia_delta ) );
	SetShortWorldDistance( 12041, def.random_initial_velocity );
	SetPercentage( 12049, def.promotion_on_detonation_fraction );
	SetShortFixed( 12056, def.animation_rate_lower_bound );
	SetShortFixed( 12057, get_upper_bound( def.animation_rate_lower_bound, def.animation_rate_delta ) );
	
	if( def.lifespan_lower_bound )
	{
		SetFlag( 12058, true );
		SetFloat( 12059, def.lifespan_lower_bound / (float)TICKS_PER_SECOND, 3 );
		SetFloat( 12060, (def.lifespan_lower_bound + def.lifespan_delta) / (float)TICKS_PER_SECOND, 3 );
	}
	else
	{
		SetFloat( 12059, 0.0, 3 );
		SetFloat( 12060, 0.0, 3 );
	}
	
	SetInt ( 12061, def.delay_lower_bound );
	SetShortFixed( 12062, get_upper_bound( def.delay_lower_bound, def.delay_delta ) );
	
	SetInt ( 12063, def.nearby_target_radius );
	SetInt ( 12064, def.tracking_priority );
	SetAngleInSeconds ( 12065, def.guided_turning_speed );
	
	SetInt( 12068, def.blood_rgba[0] );
	SetInt( 12069, def.blood_rgba[1] );
	SetInt( 12070, def.blood_rgba[2] );
	SetInt( 12071, def.blood_rgba[3] );
	
	SetTag( 12072, 'txst', def.bounce_stain_texturestacks_tag );
	SetTag( 12074, 'txst', def.detonation_stain_texturestacks_tag );
}

// ========================================  GET INTERFACE VALUES  ==================================================
void TprojEditForm::GetValues()
{
	def.collection_tag = GetTag( 12001 );
	def.object_tag = GetTag( 12003 );
	def.lightning_tag = GetTag( 12005 );
	def.artifact_tag = GetTag( 12007 );
	def.local_projectile_group_tag = GetTag( 12009 );
	def.detonation_projectile_group_tag = GetTag( 12017 );
	def.target_detonation_projectile_group_tag = GetTag( 12019 );
	def.contrail_projectile_or_sprite_tag = GetTag( 12029 );
	def.promoted_projectile_tag = GetTag( 12045 );
	def.promotion_projectile_group_tag = GetTag( 12047 );
	def.sound_tags[_projectile_sound_flight] = GetTag( 12052 );
	def.sound_tags[_projectile_sound_rebound] = GetTag( 12054 );

	XGPopControl* pList1;
	pList1 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12011 ));
	assert(pList1);
	def.sequence_indexes[_projectile_sequence_in_flight] = pList1->GetValue()-1;

	XGPopControl* pList2;
	pList2 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12013 ));
	assert(pList2);
	def.sequence_indexes[_projectile_sequence_detonation_stain] = pList2->GetValue()-1;

	XGPopControl* pList3;
	pList3 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12015 ));
	assert(pList3);
	def.sequence_indexes[_projectile_sequence_bounce_stain] = pList3->GetValue()-1;

	XGPopControl* pList4;
	pList4 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12021 ));
	assert(pList4);
	def.splash_or_rebound_effect_type = pList4->GetValue() - 1;

	XGPopControl* pList5;
	pList5 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12038 ));
	assert(pList5);
	def.volume = pList5->GetValue() - 1;

	XGPopControl* pList6;
	pList6 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12051 ));
	assert(pList6);
	def.projectile_class = pList6->GetValue() - 1;

	def.skelmodel_code = GetFlag(12500) ? 0xFEDC : 0x0000;
	
	def.flags = (GetFlag( 12025 )) ? (def.flags | _projectile_detonates_when_animation_loops_flag) : (def.flags &~ _projectile_detonates_when_animation_loops_flag);
	def.flags = (GetFlag( 12026 )) ? (def.flags | _projectile_detonates_when_transfer_loops_flag) : (def.flags &~ _projectile_detonates_when_transfer_loops_flag);
	def.flags = (GetFlag( 12027 )) ? (def.flags | _projectile_detonates_at_rest_flag) : (def.flags &~ _projectile_detonates_at_rest_flag);
	def.flags = (GetFlag( 12028 )) ? (def.flags | _projectile_remains_after_detonation_flag) : (def.flags &~ _projectile_remains_after_detonation_flag);
	def.flags = (GetFlag( 12037 )) ? (def.flags | _projectile_contrail_frequency_reset_after_bouncing_flag) : (def.flags &~ _projectile_contrail_frequency_reset_after_bouncing_flag);
	def.flags = (GetFlag( 12042 )) ? (def.flags | _projectile_affected_by_wind_flag) : (def.flags &~ _projectile_affected_by_wind_flag);
	def.flags = (GetFlag( 12043 )) ? (def.flags | _projectile_cannot_be_accelerated_flag) : (def.flags &~ _projectile_cannot_be_accelerated_flag);
	def.flags = (GetFlag( 12044 )) ? (def.flags | _projectile_is_melee_attack_flag) : (def.flags &~ _projectile_is_melee_attack_flag);
	def.flags = (GetFlag( 12050 )) ? (def.flags | _projectile_is_promoted_at_end_of_lifespan_flag) : (def.flags &~ _projectile_is_promoted_at_end_of_lifespan_flag);
	def.flags = (GetFlag( 12501 )) ? (def.flags | _projectile_uses_sprite_contrail_flag) : (def.flags &~ _projectile_uses_sprite_contrail_flag);

	def.detonation_frequency = GetPercentage( 12022 );
	def.media_detonation_frequency = GetPercentage( 12023 );
	def.detonation_velocity = GetShortWorldDistance( 12024 );
	def.ticks_between_contrails = GetInt ( 12031 );
	def.maximum_contrail_count = GetInt ( 12033 );
	def.initial_contrail_frequency = GetPercentage( 12034 );
	def.final_contrail_frequency = GetPercentage( 12035 );
	def.contrail_frequency_delta = GetPercentage( 12036 );
	def.inertia_lower_bound = GetShortFixed ( 12039 );
	def.inertia_delta = get_delta( GetShortFixed( 12039 ), GetShortFixed( 12040 ) );
	def.random_initial_velocity = GetShortWorldDistance( 12041 );
	def.promotion_on_detonation_fraction = GetPercentage( 12049 );
	def.animation_rate_lower_bound = GetShortFixed ( 12056 );
	def.animation_rate_delta = get_delta( GetShortFixed( 12056 ), GetShortFixed( 12057 ) );
	
	if( GetFlag( 12058 ) )
	{
		def.lifespan_lower_bound = GetFloat( 12059 ) * (float)TICKS_PER_SECOND;
		def.lifespan_delta = (GetFloat( 12060 ) - GetFloat( 12059 )) * (float)TICKS_PER_SECOND;
	}
	else
	{
		def.lifespan_lower_bound = 0;
		def.lifespan_delta = 0;
	}
	
	def.delay_lower_bound = GetInt ( 12061 );
	def.delay_delta = GetInt ( 12062 ) - def.delay_lower_bound;
	def.nearby_target_radius = GetInt ( 12063 );
	def.tracking_priority = GetInt ( 12064 );
	def.guided_turning_speed = GetAngleInSeconds ( 12065 );
	
	def.blood_rgba[0] = GetInt( 12068 );
	def.blood_rgba[1] = GetInt( 12069 );
	def.blood_rgba[2] = GetInt( 12070 );
	def.blood_rgba[3] = GetInt( 12071 );
	
	def.bounce_stain_texturestacks_tag = GetTag( 12072 );
	def.detonation_stain_texturestacks_tag = GetTag( 12074 );
}

bool replace_projectile_data( tag_entry *te, vengeance_script_proj *proj )
{
	projectile_definition projectile;
	
	if( read_projectile_definition( &projectile, te ) )
	{
		projectile.detonation_frequency = proj->detonation_frequency;
		projectile.media_detonation_frequency = proj->media_detonation_frequency;
		projectile.detonation_velocity = proj->detonation_velocity;
		projectile.volume = proj->volume;
		projectile.inertia_lower_bound = proj->inertia_lower_bound;
		projectile.inertia_delta = proj->inertia_delta;
		projectile.random_initial_velocity = proj->random_initial_velocity;
		projectile.nearby_target_radius = proj->nearby_target_radius;
		projectile.tracking_priority = proj->tracking_priority;
		projectile.guided_turning_speed = proj->guided_turning_speed;
		
		projectile.flags &= proj->flag_mask;
		projectile.flags |= proj->flags;
		
		projectile.damage.type = proj->damage.type;
		projectile.damage.damage_lower_bound = proj->damage.damage_lower_bound;
		projectile.damage.damage_delta = proj->damage.damage_delta;
		projectile.damage.radius_lower_bound = proj->damage.radius_lower_bound;
		projectile.damage.radius_delta = proj->damage.radius_delta;
		projectile.damage.rate_of_expansion = proj->damage.rate_of_expansion;
		projectile.damage.damage_to_velocity = proj->damage.damage_to_velocity;

		projectile.damage.flags &= proj->damage.flag_mask;
		projectile.damage.flags |= proj->damage.flags;
	}
	
	return save_projectile_definition( &projectile, te );
}


bool retrive_projectile_data( tag_entry *te, vengeance_script_proj *proj )
{
	projectile_definition projectile;
	
	if( read_projectile_definition( &projectile, te ) )
	{
		proj->detonation_frequency = projectile.detonation_frequency;
		proj->media_detonation_frequency = projectile.media_detonation_frequency;
		proj->detonation_velocity = projectile.detonation_velocity;
		proj->volume = projectile.volume;
		proj->inertia_lower_bound = projectile.inertia_lower_bound;
		proj->inertia_delta = projectile.inertia_delta;
		proj->random_initial_velocity = projectile.random_initial_velocity;
		proj->nearby_target_radius = projectile.nearby_target_radius;
		proj->tracking_priority = projectile.tracking_priority;
		proj->guided_turning_speed = projectile.guided_turning_speed;
		proj->flags = projectile.flags;
		
		proj->damage.type = projectile.damage.type;
		proj->damage.damage_lower_bound = projectile.damage.damage_lower_bound;
		proj->damage.damage_delta = projectile.damage.damage_delta;
		proj->damage.radius_lower_bound = projectile.damage.radius_lower_bound;
		proj->damage.radius_delta = projectile.damage.radius_delta;
		proj->damage.rate_of_expansion = projectile.damage.rate_of_expansion;
		proj->damage.damage_to_velocity = projectile.damage.damage_to_velocity;
		proj->damage.flags = projectile.damage.flags;
	
		return TRUE;
	}
	
	return FALSE;
}
