
#include "mons.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 <XList.h>
#include <XDialog.h>
#include "skelmodel.h"
#include "formatters.h"
#include "vengeance_script.h"


// ==============================  BYTE SWAPPING DATA  ========================================

byte_swap_code monster_attack_sequence_byte_swapping_data[]=
{
	_begin_bs_array, MAXIMUM_NUMBER_OF_ATTACK_SEQUENCES,
		// word flags;
		_2byte,
		// short sequence_index;
		// fixed_fraction skip_fraction;
		_2byte,
		_2byte,
		// short unused;
		sizeof(short),
	_end_bs_array
};

byte_swap_code monster_attack_skelanim_byte_swapping_data[]=
{
	_begin_bs_array, 1,
		1,	// byte state_index
	_end_bs_array
};

byte_swap_code monster_attack_definition_byte_swapping_data[]=
{
	_begin_bs_array, 1,

		// word flags;
		// fixed_fraction miss_fraction;
		_2byte,
		_2byte,
		
		// file_tag projectile_tag;
		_4byte,
		
		// short_world_distance minimum_range, maximum_range;
		_2byte, _2byte,

		// struct monster_attack_sequence sequences[MAXIMUM_NUMBER_OF_ATTACK_SEQUENCES];
/*		_begin_bs_array, MAXIMUM_NUMBER_OF_ATTACK_SEQUENCES,
			// word flags;
			_2byte,
			// short sequence_index;
			// fixed_fraction skip_fraction;
			_2byte,
			_2byte,
			// short unused;
			sizeof(short),
		_end_bs_array, */
		MAXIMUM_NUMBER_OF_ATTACK_SEQUENCES * sizeof(monster_attack_sequence),

		// short repetitions;
		_2byte,

		// short_world_distance initial_velocity_lower_bound, initial_velocity_delta;
		// short_world_distance initial_velocity_error; // applied randomly in x, y, z
		_2byte, _2byte,
		_2byte,
		
		// short recovery_time;
		// short recovery_time_experience_delta;
		_2byte,		
		_2byte,
		
		// short_world_distance velocity_improvement_with_experience;
		// short_fixed mana_cost;
		_2byte,
		_2byte,

		// word extra_flags;
		_2byte,

		// short projectile_type;
		sizeof(short),
		
	_end_bs_array,
};

static byte_swap_code monster_definition_byte_swapping_data[]=
{
	_begin_bs_array, 1,
	
	// unsigned long flags;
	_4byte,
	
	// file_tag collection_tag;
	_4byte,

	// short sequence_indexes[MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SEQUENCES];
	_begin_bs_array, MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SEQUENCES, _2byte, _end_bs_array,

	// char terrain_costs[MAXIMUM_NUMBER_OF_TERRAIN_TYPES];
	MAXIMUM_NUMBER_OF_TERRAIN_TYPES,

	// word impassable_terrain_flags;
	// short_world_distance pathfinding_radius;
	_2byte,
	_2byte,
	
	// short_fixed_fraction movement_modifiers[MAXIMUM_NUMBER_OF_TERRAIN_TYPES];
	MAXIMUM_NUMBER_OF_TERRAIN_TYPES,

	// fixed_fraction absorbed_fraction;
	_2byte,

	// short_world_distance warning_distance; // used to awake nearby friends to new target
	// short_world_distance critical_distance; // used for fight vs. flight
	_2byte,
	_2byte,

	// short_fixed healing_fraction;
	_2byte,

	// short initial_ammunition_lower_bound, initial_ammunition_delta;
	_2byte, _2byte,

	// short_world_distance activation_distance; // range of "sight and hearing" for automatic attack vs. retreat
	// short unused;
	_2byte,
	sizeof(short),

	// angle turning_speed; // not a function of terrain
	// short_world_distance base_movement_speed;
	_2byte,
	_2byte,

	// fixed_fraction left_handed_fraction; // the fraction of this type that is left-handed
	// short size;
	_2byte,
	_2byte,

	// file_tag object_tag;
	_4byte,

	// short number_of_attacks;
	// short desired_projectile_volume;
	_2byte,
	_2byte,
	
	// struct monster_attack_definition attacks[MAXIMUM_NUMBER_OF_ATTACKS_PER_MONSTER];
	MAXIMUM_NUMBER_OF_ATTACKS_PER_MONSTER * sizeof(struct monster_attack_definition), // byteswap it later

	// file_tag map_action_tag; // for ambient life
	_4byte,

	// short attack_frequency_lower_bound, attack_frequency_delta;
	_2byte, _2byte,

	// file_tag exploding_projectile_group_tag;
	_4byte,

	// short unused4;
	// short maximum_ammunition_count;
	sizeof(short),
	_2byte,

	// fixed_fraction hard_death_system_shock, flinch_system_shock;
	_2byte, _2byte,

	// file_tag melee_impact_projectile_group_tag;
	// file_tag dying_projectile_group_tag;
	_4byte,
	_4byte,
	
	// file_tag spelling_string_list_tag; // sigular and plural spelling
	// file_tag names_string_list_tag; // common names for this monster
	// file_tag flavor_string_list_tag; // flavor texts for this monster
	_4byte,
	_4byte,
	_4byte,
	
	// short unused3;
	sizeof(short),

	// short monster_class;
	// short monster_allegiance;
	// short experience_point_value;
	_2byte,
	_2byte,
	_2byte,
	
	// file_tag sounds_tags[MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS];
	_begin_bs_array, MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS, _4byte, _end_bs_array,

	// file_tag blocked_impact_projectile_group_tag;
	// file_tag absorbed_impact_projectile_group_tag;
	_4byte,
	_4byte,

	// file_tag ammunition_projectile_tag;
	_4byte,

	// short visiblity_type;
	_2byte,

	// short combined_power;
	// short_world_distance longest_range;
	_2byte,
	_2byte,
	
	// short cost;
	_2byte,

	// char sound_types[MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS];
	MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS,

	// word pad3
	sizeof(word),
	
	// file_tag entrance_projectile_group_tag;
	// file_tag local_projectile_group_tag;
	// file_tag special_ability_string_list_tag;
	// file_tag exit_projectile_group_tag;
	_4byte,
	_4byte,
	_4byte,
	_4byte,

	// short_fixed maximum_mana;
	// short_fixed mana_recharge_rate;
	_2byte,
	_2byte,
	
	// fixed_fraction berserk_system_shock;
	// fixed_fraction berserk_vitality;
	_2byte,
	_2byte,

	// file_tag portrait_collection_tag;
	_4byte,
	
	// short kills_for_maximum_experience_bonus;
	_2byte,
	
	// short experience_bonus_size;
	// short_world_distance experience_bonus_radius;
	_2byte,
	_2byte,
	
	// fixed_fraction blocking_fraction
	_2byte,
	
	// carrying_tag
	_4byte,
	
	// short unused2[216];
	207*sizeof(short),

	// computed during postprocessing
	
	// short portrait_collection_index;
	// short next_name_string_index;
	// short ammunition_projectile_type;
	3*sizeof(short),

	// short sound_indexes[MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS];
	MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS*sizeof(short),
	
	// short spelling_string_list_index; // sigular and plural spelling
	// short names_string_list_index; // common names for this monster
	// short flavor_string_list_index; // flavor texts for this monster
	// short special_ability_string_list_index;
	// short exploding_projectile_group_type;
	// short melee_impact_projectile_group_type;
	// short dying_projectile_group_type;
	// short blocked_impact_projectile_group_type;
	// short absorbed_impact_projectile_group_type;
	// short entrance_projectile_group_type, exit_projectile_group_type;
	// short collection_index; // from collection_tag
	// short object_type; // from object_tag
	// short local_projectile_group_type;
	14*sizeof(short),
	
	_end_bs_array
};

monster_definition default_mons =
{
	0,
	-1,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0,
	0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0,
	0,
	0,
	0,
	0, 0,
	0,
	0,
	0,
	0,
	0,
	0,
	-1,
	0,
	0,
	// monster attack definition
	0, 0, -1, 0, 0, /*att sequence*/0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*end*/ 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, -1, 0, 0, /*att sequence*/0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*end*/ 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, -1, 0, 0, /*att sequence*/0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*end*/ 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, -1, 0, 0, /*att sequence*/0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*end*/ 0, 0, 0, 0, 0, 0, 0, 0, 0,
	// end monster attack definition
	-1,
	0, 0,
	-1,
	0,
	0,
	0, 0,
	-1,
	-1,
	-1,
	-1,
	-1,
	0,
	0,
	0,
	0,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1,
	-1,
	-1,
	0,
	0,
	0,
	0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0,
	-1,
	-1,
	-1,
	-1,
	0,
	0,
	0,
	0,
	-1,
	5,
	0,
	0,
	32768,
	0
};

// ==============================  EDIT WINDOW  ========================================
void edit_mons( tag_entry* e )
{
	assert( sizeof(monster_definition) == SIZEOF_STRUCT_MONSTER_DEFINITION );
	TmonsEditForm* form = new TmonsEditForm( e );
	form->AttachWindow( 1015 );
}

void defaultnew_mons( tag_entry *e )
{
	monster_definition def;
	byte_swap_move_be( "mons", &def, &default_mons, sizeof(monster_definition), 1, monster_definition_byte_swapping_data );
	set_entry_data( e, &def, sizeof(def), false );
}

void dependency_mons( tag_entry *e )
{
	monster_definition def;
	read_monster_definition( &def, e );
	bool skel = def.flags & _monster_has_3d_model_flag;
	if (skel)
	{
		add_tag_dependency( 'skmd', def.skelmodel_tag );
	}
	else
	{
		add_tag_dependency( '.256', def.collection_tag );
	}
	add_tag_dependency( 'obje', def.object_tag );
	for( int i = 0; i < def.number_of_attacks; i++ )
	{
		add_tag_dependency( 'proj', def.attacks[i].projectile_tag );
	}
	
	// FIXME: add a dependency for the map action tag?
	add_tag_dependency( 'prgr', def.exploding_projectile_group_tag );
	add_tag_dependency( 'prgr', def.melee_impact_projectile_group_tag );
	add_tag_dependency( 'prgr', def.dying_projectile_group_tag );
	add_tag_dependency( 'stli', def.spelling_string_list_tag );
	add_tag_dependency( 'stli', def.names_string_list_tag );
	add_tag_dependency( 'stli', def.flavor_string_list_tag );
	for (int i = 0; i < MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS; ++i)
	{
		add_tag_dependency( 'soun', def.sound_tags[i] );
	}
	add_tag_dependency( 'prgr', def.blocked_impact_projectile_group_tag );
	add_tag_dependency( 'prgr', def.absorbed_impact_projectile_group_tag );
	add_tag_dependency( 'proj', def.ammunition_projectile_tag );
	add_tag_dependency( 'prgr', def.entrance_projectile_group_tag );
	add_tag_dependency( 'lpgr', def.local_projectile_group_tag );
	add_tag_dependency( 'stli', def.special_ability_string_list_tag );
	add_tag_dependency( 'prgr', def.exit_projectile_group_tag );
	add_tag_dependency( '.256', def.portrait_collection_tag );
}

// ==============================  READ TAG DATA  ========================================
bool read_monster_definition( monster_definition* def, tag_entry* e )
{
	if (load_tag_data_into_struct( "mons", e, def, SIZEOF_STRUCT_MONSTER_DEFINITION, 1, monster_definition_byte_swapping_data)) {
		for (int i = 0; i < MAXIMUM_NUMBER_OF_ATTACKS_PER_MONSTER; ++i) {
			byte_swap_data_be( "monster attack", def->attacks + i, sizeof(monster_attack_definition), 1,
								monster_attack_definition_byte_swapping_data );
			if (def->flags & _monster_has_3d_model_flag) {
				byte_swap_data_be( "monster attack skelanim", &def->attacks[i].skelanim,
									sizeof(def->attacks[i].skelanim), 1,
									monster_attack_skelanim_byte_swapping_data );
			} else {
				byte_swap_data_be( "monster attack sequences", &def->attacks[i].sequences,
									sizeof(def->attacks[i].sequences), 1,
									monster_attack_sequence_byte_swapping_data );
			}
		}
		return TRUE;
	}
	
	return FALSE;
}


// ==============================  SAVE TAG DATA  ========================================
bool save_monster_definition( monster_definition* def, tag_entry* e )
{
	
	monster_definition *buf = (monster_definition *)malloc( sizeof(monster_definition) );
	byte_swap_move_be( "mons", buf, def, sizeof(monster_definition), 1, monster_definition_byte_swapping_data );
	for (int i = 0; i < MAXIMUM_NUMBER_OF_ATTACKS_PER_MONSTER; ++i) {
		byte_swap_data_be( "monster attack", buf->attacks + i, sizeof(monster_attack_definition), 1,
							monster_attack_definition_byte_swapping_data );
		if (def->flags & _monster_has_3d_model_flag) {
			byte_swap_data_be( "monster attack skelanim", &buf->attacks[i].skelanim,
								sizeof(buf->attacks[i].skelanim), 1,
								monster_attack_skelanim_byte_swapping_data );
		} else {
			byte_swap_data_be( "monster attack sequences", &buf->attacks[i].sequences,
								sizeof(buf->attacks[i].sequences), 1,
								monster_attack_sequence_byte_swapping_data );
		}
	}
	set_entry_data( e, buf, sizeof(monster_definition), TRUE );
	return true;
}


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


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

	return save_monster_definition( def, e );
}


const char *AnimStateNames[] = {
	"Idle",
	"Alert",
	"Gesture",
	"Move",
	"Run",
	"Turn Left",
	"Turn Right",
	"Pause for Obstacle",
	"Hold for Attack",
	"Block Attack",
	"Hit Light from Front",
	"Hit Light from Back",
	"Hit Hard from Front",
	"Hit Hard from Back",
	"Die Soft from Front",
	"Die Soft from Back",
	"Die Hard from Front",
	"Die Hard from Back",
	"Explosion Death",
	"Pick Up Object",
	"Set Down Object",
	"Taunt",
	"Celebrate",
	"Receive Healing",
	"Enter Map",
	"Exit Map",
	"Auxiliary 1",
	"Auxiliary 2",
	0
};

const char *SequenceNames[] = {
	"Idle",
	"Moving",
	"Placeholder",
	"Pausing for Obstacle",
	"Turning",
	"Blocking",
	"Taking Damage",
	"PIcking up Object",
	"HEad Shot",
	"Celebration",
	"Transition to Secondary Idle",
	"Secondary Idle",
	"Transition to Primary Idle",
	"Running",
	"Ammo Icon",
	"Hold for Attack",
	"Taunt",
	"Glide",
	0
};

struct MonsterSequenceData {
	short sequences[32];
	XGStringList mons;
	XGStringList anim;
};

const char *monster_sound_type_desc[] =
{
	"None",
	"Attack Order",
	"Multiple Attack Order",
	"Attack Order vs. Undead",
	"Move Order",
	"Multiple Move Order",
	"Selection",
	"Multiple Selection",
	"Hit Friendly Unit",
	"Hit By Friendly Unit",
	"Attack Obstructed By Friendly Unit",
	"Attack Enemy Unit",
	"Attack Enemy Unit With Friends Nearby",
	"Sprayed By Gore",
	"Caused Enemy Death",
	"Caused Friendly Death",
	"Caused Death of Enemy Undead",
	"Caused Multiple Enemy Deaths",
	""
};

struct MonsterSoundsTable
{
	tag_file *source;
	char sound_type[MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS];
	file_tag sound_tag[MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS];
};

struct MonsterTerrainData
{
	short_world_distance base_movement_speed;
	short_fixed_fraction movement_modifiers[MAXIMUM_NUMBER_OF_TERRAIN_TYPES];
	char terrain_costs[MAXIMUM_NUMBER_OF_TERRAIN_TYPES];
};

static long SequenceDlogProc( XGDialog *dlog, void *data, long msg, long arg, void * /*parg*/ )
{
	MonsterSequenceData *sd = (MonsterSequenceData *)data;
	XGListControl *mons = dynamic_cast<XGListControl *>(dlog->FindViewByID(1));
	XGListControl *anim = dynamic_cast<XGListControl *>(dlog->FindViewByID(2));
	char buf[256];
	int i, j;
	
	switch (msg) {
		case KXGDialogMessage:
			switch (arg) {
				case KXGDMInitDialog: {
						mons->DeleteAll();
						for (i = 0; i < sd->mons.GetNumStrings(); ++i) {
							sd->mons.GetString( i, buf );
							mons->Insert( i, buf );
							if (sd->sequences[i] < 0 || sd->sequences[i] >= sd->anim.GetNumStrings()) {
								sd->sequences[i] = 0;
							}
						}
						
						anim->DeleteAll();
						for (i = 0; i < sd->anim.GetNumStrings(); ++i) {
							sd->anim.GetString( i, buf );
							anim->Insert( i, buf );
						}
						
						mons->SetValue( 0 );
						anim->SetValue( sd->sequences[0] );
					} return 0;
				default:
					break;
			} break;
		case KXGListValue:
			switch (arg) {
				case 1:	// mons
					i = mons->GetValue();
					if (i < 0 || i >= mons->Length()) {
						i = 0;
						mons->SetValue(i);
					}
					anim->SetValue( sd->sequences[i] );
					return 0;
				case 2: // anim
					i = mons->GetValue();
					if (i < 0 || i >= mons->Length()) return 0;
					j = anim->GetValue();
					if (j < 0 || j >= anim->Length()) {
						j = 0;
						anim->SetValue( j );
					}
					sd->sequences[i] = j;
					return 0;
			} break;
		case KXGControlClick:
			switch (arg) {
				case 101:	// ok
					dlog->EndDialog(1);
					return 0;
				case 102:	// cancel
					dlog->EndDialog(0);
					return 0;
			} break;
		default:
			break;
	}
	
	return -1;
}

static long SoundsDlogProc( XGDialog *dlog, void *data, long msg, long arg, void *parg )
{
	XGPopControl *pList;
	tag_entry *e;
	
	MonsterSoundsTable *sounds = (MonsterSoundsTable *)data;
	int ii, jj;
	
	switch( msg )
	{
		case KXGDialogMessage:
		{
			switch( arg )
			{
				case KXGDMInitDialog:
				{
					// fill all of the  list boxes with the sound states
					for( ii = 0; ii < MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS; ii++ )
					{
						pList = dynamic_cast<XGPopControl *>(dlog->FindViewByID( ii + 101 ));
						
						for( jj = 0; *monster_sound_type_desc[jj] != 0; jj++ )
							pList->Insert( jj, monster_sound_type_desc[jj] );
					}
					
					for( ii = 0; ii < MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS; ii++ )
					{
						pList = dynamic_cast<XGPopControl *>(dlog->FindViewByID( ii + 101 ));
						pList->SetValue( sounds->sound_type[ii] + 1 );
						
						e = get_entry( 'soun', sounds->sound_tag[ii] );
						dlog->SetText( ii + 201, e ? get_entry_name( e ) : tagstr( sounds->sound_tag[ii] ) );
					}
					
					return 0;
				}
				
				default:
					break;
			}
		}
		case KXGControlClick:
		{
			switch( arg )
			{
				case 1001: // ok
				{
					for( ii = 0; ii < MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS; ii++ )
					{
						pList = dynamic_cast<XGPopControl *>(dlog->FindViewByID( ii + 101 ));
						sounds->sound_type[ii] = pList->GetValue() - 1;
					}
					
					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: // pick a sound tag
				{
					tag new_tag = pick_tag_of_type( 'soun', sounds->sound_tag[arg - 201], sounds->source );
					if( new_tag )
						sounds->sound_tag[arg - 201] = new_tag;
					
					e = get_entry( 'soun', sounds->sound_tag[arg - 201] );
					dlog->SetText( arg, e ? get_entry_name( e ) : tagstr( sounds->sound_tag[arg - 201] ) );
					return 0;
				}
				
				case 301:
				case 302:
				case 303:
				case 304:
				case 305:
				case 306:
				case 307:
				case 308:
				case 309:
				case 310: // open the sound tag
				{
					e = get_entry( 'soun', sounds->sound_tag[arg - 301] );
					if( e )
						TRevengeApp::OpenItem( e );
					
					return 0;
				}
				
				default:
					break;
			}
		}
		
		default:
			break;
	}
	
	return -1;
}

static long TerrainDlogProc( XGDialog *dlog, void *data, long msg, long arg, void *parg )
{
	MonsterTerrainData *terrain = (MonsterTerrainData *)data;

	int ii;
	
	switch( msg )
	{
		case KXGDialogMessage:
		{
			switch( arg )
			{
				case KXGDMInitDialog:
				{
					SetShortWorldDistance( dlog, 100, terrain->base_movement_speed );
					
					for( ii = 0; ii < MAXIMUM_NUMBER_OF_TERRAIN_TYPES; ii++ )
					{
						dlog->SetValue( ii + 101, terrain->terrain_costs[ii] < 0 ? false : true );
						SetShortPercentage( dlog, 201 + ii, terrain->movement_modifiers[ii] );
						
						if( terrain->terrain_costs[ii] < 0 )
							dlog->FindViewByID( ii + 201 )->HideView();
					}
					
					return 0;
				}
				
				default:
					break;
			}
		}
		case KXGControlClick:
		{
			switch( arg )
			{
				case 1001: // ok
				{
					terrain->base_movement_speed = GetShortWorldDistance( dlog, 100 );
					
					for( ii = 0; ii < MAXIMUM_NUMBER_OF_TERRAIN_TYPES; ii++ )
					{
						if( dlog->GetValue( ii + 101 ) )
						{
							terrain->terrain_costs[ii] = 0;
							terrain->movement_modifiers[ii] = GetShortPercentage( dlog, 201 + ii );
						}
						else
						{
							terrain->terrain_costs[ii] = -1;
							terrain->movement_modifiers[ii] = 0;
						}
					}
					
					dlog->EndDialog( 1 );
					return 0;
				}
				case 1002: // cancel
				{
					dlog->EndDialog( 0 );
					return 0;
				}
				case 101:
				case 102:
				case 103:
				case 104:
				case 105:
				case 106:
				case 107:
				case 108:
				case 109:
				case 110:
				case 111:
				case 112:
				case 113:
				case 114:
				case 115:
				case 116: // terrain type check boxes
				{
					int val = !dlog->GetValue( arg );
					dlog->SetValue( arg, val );
					
					if( val )
						dlog->FindViewByID( arg + 100 )->ShowView();
					else
						dlog->FindViewByID( arg + 100 )->HideView();

					return 0;
				}
				
				default:
					break;
			}
		}
		
		default:
			break;
	}
	
	return -1;
}

static long FlagsDlogProc( XGDialog *dlog, void *data, long msg, long arg, void *parg )
{
	unsigned long *flags = (unsigned long *)data;
	int ii;
	
	switch( msg )
	{
		case KXGDialogMessage:
		{
			switch( arg )
			{
				case KXGDMInitDialog:
				{
					for( ii = 101; ii <= 125; ii++ )
					{
						if (ii == 122) continue; // 3d model flag
						dlog->SetValue( ii, (bool)((*flags) & (1 << (ii - 101))) );
					}
					
					return 0;
				}
				
				default:
					break;
			}
		}
		case KXGControlClick:
		{
			switch( arg )
			{
				case 1001: // ok
				{
					for( ii = 101; ii <= 125; ii++ )
					{
						if (ii == 122) continue; // 3d model flag
						(dlog->GetValue( ii )) ? *flags |= (1 << (ii - 101)) : *flags &= ~(1 << (ii - 101));
					}
					
					dlog->EndDialog( 1 );
					return 0;
				}
				case 1002: // cancel
				{
					dlog->EndDialog( 0 );
					return 0;
				}
				
				case 101:
				case 102:
				case 103:
				case 104:
				case 105:
				case 106:
				case 107:
				case 108:
				case 109:
				case 110:
				case 111:
				case 112:
				case 113:
				case 114:
				case 115:
				case 116:
				case 117:
				case 118:
				case 119:
				case 120:
				case 121:
				// case 122: is 3d model flag
				case 123: // flags
				case 124:
				case 125:
				{
					dlog->SetValue( arg, !dlog->GetValue( arg ) );
					return 0;
				}
				
				default:
					break;
			}
		}
		
		default:
			break;
	}
	
	return -1;
}

static void ExtractCollectionNames( tag_entry *e, XGStringList *out )
{
	e = 0;
	char buf[40];
	for (int i = 0; i < 20; ++i) {
		sprintf( buf, "Andrew is lazy %d", i );
		out->Insert( i, buf );
	}
}

#pragma mark -

struct attack_dialog_data {
	tag_file *source;
	const monster_definition *def;
	monster_attack_definition a;
};

static long EditAttackProc( XGDialog *dlog, void *data, long msg, long arg, void * /*parg*/ )
{
	if (!dlog) return -1;
	attack_dialog_data *add = (attack_dialog_data *)data;
	monster_attack_definition *a = &add->a;
	
	switch (msg) {
		case KXGDialogMessage:
			switch (arg) {
				case KXGDMInitDialog: {
						tag_entry *e = get_entry( 'proj', a->projectile_tag );
						dlog->SetText( 1, e ? get_entry_name( e ) : tagstr( a->projectile_tag ) );
						XGStringList states;
						ExtractSKMDNames( get_entry( 'skmd', add->def->skelmodel_tag ), &states );
						XGPopControl *pop = dynamic_cast<XGPopControl *>(dlog->FindViewByID( 200 ));
						pop->DeleteAll();
						for (int i = 0; i < states.GetNumStrings(); ++i) {
							char buf[64];
							states.GetString( i, buf );
							pop->Insert( i, buf );
						}
						pop->SetValue( a->skelanim.state_index );
						for (int i = 0; i < NUMBER_OF_MONSTER_ATTACK_DEFINITION_FLAGS; ++i) {
							dlog->SetValue( 101 + i, (a->flags >> i) & 1 );
						}
						for (int i = 0; i < NUMBER_OF_MONSTER_ATTACK_DEFINITION_EXTRA_FLAGS; ++i) {
							dlog->SetValue( 151 + i, (a->extra_flags >> i) & 1 );
						}
						
						SetShortWorldDistance( dlog, 2, a->minimum_range );
						SetShortWorldDistance( dlog, 3, a->maximum_range );
						dlog->SetValue( 4, a->repetitions );
						SetPercentage( dlog, 5, a->miss_fraction );
						SetShortWorldDistance( dlog, 6, a->initial_velocity_lower_bound );
						SetShortWorldDistance( dlog, 7, get_upper_bound( a->initial_velocity_lower_bound, a->initial_velocity_delta ) );
						SetShortWorldDistance( dlog, 8, a->initial_velocity_error );
						SetShortWorldDistance( dlog, 9, a->velocity_improvement_with_experience );
						SetFloat( dlog, 10, (float)a->recovery_time / (float)TICKS_PER_SECOND, 3 );
						SetFloat( dlog, 11, (float)a->recovery_time_experience_delta / (float)TICKS_PER_SECOND, 3 );
						SetShortFixed( dlog, 12, a->mana_cost );
					} return 0;
				default:
					break;
			} break;
		case KXGPopValue:
			switch (arg) {
					case 200: {
						int val = dlog->GetValue( arg );
						if (val >= 0) {
							a->skelanim.state_index = val;
						}
					} return 0;
			} break;
		case KXGControlClick:
			if (arg >= 101 && arg < 101 + NUMBER_OF_MONSTER_ATTACK_DEFINITION_FLAGS) {
				int v = !dlog->GetValue( arg );
				dlog->SetValue( arg, v );
				unsigned m = 1 << (arg - 101);
				if (v) {
					a->flags |= m;
				} else {
					a->flags &= ~m;
				}
				return 0;
			} else if (arg >= 151 && arg < 151 + NUMBER_OF_MONSTER_ATTACK_DEFINITION_EXTRA_FLAGS) {
				int v = !dlog->GetValue( arg );
				dlog->SetValue( arg, v );
				unsigned m = 1 << (arg - 151);
				if (v) {
					a->extra_flags |= m;
				} else {
					a->extra_flags &= ~m;
				}
				return 0;
			} else if (arg == 1) { // projectile
				tag new_proj = pick_tag_of_type( 'proj', a->projectile_tag, add->source );
				if (new_proj) {
					a->projectile_tag = new_proj;
				}
				tag_entry *e = get_entry( 'proj', a->projectile_tag );
				dlog->SetText( 1, e ? get_entry_name( e ) : tagstr( a->projectile_tag )	 );
				return 0;
			} else if( arg == 50 ) { // open the tag
				tag_entry *e = get_entry( 'proj',a->projectile_tag );
				if( e )
					TRevengeApp::OpenItem( e );
			} else if (arg == 1001) { // ok
				a->minimum_range = GetShortWorldDistance( dlog, 2 );
				a->maximum_range = GetShortWorldDistance( dlog, 3 );
				a->repetitions = dlog->GetValue( 4 );
				a->miss_fraction = GetPercentage( dlog, 5 );
				a->initial_velocity_lower_bound = GetShortWorldDistance( dlog, 6 );
				a->initial_velocity_delta = get_delta( GetShortWorldDistance( dlog, 6 ), GetShortWorldDistance( dlog, 7 ) );
				a->initial_velocity_error = GetShortWorldDistance( dlog, 8 );
				a->velocity_improvement_with_experience = GetShortWorldDistance( dlog, 9 );
				a->recovery_time = GetFloat( dlog, 10 ) * (float)TICKS_PER_SECOND;
				a->recovery_time_experience_delta = GetFloat( dlog, 11 ) * (float)TICKS_PER_SECOND;
				a->mana_cost = GetShortFixed( dlog, 12 );
				
				
				dlog->EndDialog( 1 );
				return 0;
			} else if (arg == 1002) { // cancel
				dlog->EndDialog( 0 );
				return 0;
			}
			return -1;
		default:
			break;
	}
	
	return -1;
}


void TmonsEditForm::RebuildAttackList()
{
	XGListControl *aList = dynamic_cast<XGListControl *>(GetWindow()->FindViewByID( 11514 ));
	aList->DeleteAll();
	for (int i = 0; i < def.number_of_attacks; ++i)
	{
		tag aID = def.attacks[i].projectile_tag;
		tag_entry *aE = get_entry( 'proj', aID );
		if (aE) {
			aList->Insert( i, get_entry_name( aE ) );
		} else {
			aList->Insert( i, tagstr( aID ) );
		}
	}
}


void TmonsEditForm::EditAttack( int attack_index )
{
	if (attack_index < 0 || attack_index >= def.number_of_attacks) return;
//	if (attack_index < 0 || attack_index > def.number_of_attacks) return;
	attack_dialog_data add;
	add.source = get_entry_file( fEntry );
	add.def = &def;
	add.a = def.attacks[attack_index];
	bool ok = XGDialog::ModalDialog( 31333, EditAttackProc, &add );
	if (ok) {
		def.attacks[attack_index] = add.a;
//		def.number_of_attacks++;
	}
	
	RebuildAttackList();
	SetInt( 11514, attack_index );
}


#pragma mark -


// ==============================  DISPATCH  ========================================
BEGINDISPATCH(TmonsEditForm,TTagEditForm)
	DISPATCHTABLE(KXGControlClick,TmonsEditForm::DoControlClick)
	DISPATCHTABLE(KXGListSelect,TmonsEditForm::DoListSelect)
ENDDISPATCH


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

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

}


// ==============================  INPUT HANDLING  ========================================
long TmonsEditForm::DoControlClick( long arg, void *ptr )
{
#ifdef __MWERKS__
	#pragma unused(ptr)
#endif
	tag_entry* e;
	tag new_id;
	tag artType = GetFlag( 12000 ) ? 'skmd' : '.256';
	
	bool ok;
	
	switch( arg )
	{
		case 11501:			// Choose a tag
			new_id = pick_tag_of_type(artType, GetTag( 11501 ), fEntry );
			if (new_id != 0) SetTag( 11501, artType, new_id );
		break; 
		
		case 11502:			// Open tag window
			e = get_entry(artType, GetTag( 11501 ) );
			if (e) TRevengeApp::OpenItem( e );
		break;
		
		case 12000:
			SetFlag( 12000, !GetFlag( 12000 ) );
			artType = GetFlag( 12000 ) ? 'skmd' : '.256';
			SetTag( 11501, artType, GetTag( 11501 ) );
			break;
		
		case 12001:			// Choose a tag
			new_id = pick_tag_of_type('.256', GetTag( arg ), fEntry );
			if (new_id != 0) SetTag( arg, '.256', new_id );
		break; 
		
		case 12002:			// Open tag window
			e = get_entry('.256', GetTag( arg-1 ) );
			if (e) TRevengeApp::OpenItem( e );
		break;
		
		case 11503:			// Choose a tag
			new_id = pick_tag_of_type('obje', GetTag( 11503 ), fEntry );
			if(new_id != 0) SetTag( 11503, 'obje', new_id );
		break; 
		
		case 11504:			// Open tag window
			e = get_entry('obje', GetTag( 11503 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;
		
		case 11505:			// Choose a tag
			new_id = pick_tag_of_type('lpgr', GetTag( 11505 ), fEntry );
			if(new_id != 0) SetTag( 11505, 'lpgr', new_id );
		break; 
		
		case 11506:			// Open tag window
			e = get_entry('lpgr', GetTag( 11505 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;
		
		case 11507:			// Choose a tag
			new_id = pick_tag_of_type('temp', GetTag( 11507 ), fEntry );
			if(new_id != 0) SetTag( 11507, 'temp', new_id );
		break; 
		
		case 11508:			// Open tag window
			e = get_entry('temp', GetTag( 11507 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;
		
		case 11515: {
			if (def.number_of_attacks < MAXIMUM_NUMBER_OF_ATTACKS_PER_MONSTER) {
				memset( &def.attacks[def.number_of_attacks], 0, sizeof(monster_attack_definition) );
				def.number_of_attacks += 1;
//				EditAttack( def.number_of_attacks );
				EditAttack( def.number_of_attacks - 1 );
			}
		} break;
		
		case 11516: {
			if (def.number_of_attacks < MAXIMUM_NUMBER_OF_ATTACKS_PER_MONSTER && GetInt( 11514 ) >= 0 && GetInt( 11514 ) < def.number_of_attacks) {
				memcpy( def.attacks + def.number_of_attacks, def.attacks + GetInt( 11514 ), sizeof(monster_attack_definition) );
				def.number_of_attacks += 1;
//				EditAttack( def.number_of_attacks );
				EditAttack( def.number_of_attacks - 1 );
			}
		} break;
		
		case 11517: {
			int sel = GetInt( 11514 );
			if (sel >= 0 && sel < def.number_of_attacks) {
				memmove( def.attacks + sel, def.attacks + sel + 1, sizeof(monster_attack_definition) * (def.number_of_attacks - sel - 1) );
				def.number_of_attacks -= 1;
				RebuildAttackList();
			}
		} break;
		
		
		// ==============================================================================
		case 11518:			// Choose a tag
			new_id = pick_tag_of_type('prgr', GetTag( 11518 ), fEntry );
			if(new_id != 0) SetTag( 11518, 'prgr', new_id );
		break; 
		
		case 11519:			// Open tag window
			e = get_entry('prgr', GetTag( 11518 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;
		
		case 11520:			// Choose a tag
			new_id = pick_tag_of_type('prgr', GetTag( 11520 ), fEntry );
			if(new_id != 0) SetTag( 11520, 'prgr', new_id );
		break; 
		
		case 11521:			// Open tag window
			e = get_entry('prgr', GetTag( 11520 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;
		
		case 11522:			// Choose a tag
			new_id = pick_tag_of_type('prgr', GetTag( 11522 ), fEntry );
			if(new_id != 0) SetTag( 11522, 'prgr', new_id );
		break; 
		
		case 11523:			// Open tag window
			e = get_entry('prgr', GetTag( 11522 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;
		
		case 11524:			// Choose a tag
			new_id = pick_tag_of_type('prgr', GetTag( 11524 ), fEntry );
			if(new_id != 0) SetTag( 11524, 'prgr', new_id );
		break; 
		
		case 11525:			// Open tag window
			e = get_entry('prgr', GetTag( 11524 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;
		
		case 11526:			// Choose a tag
			new_id = pick_tag_of_type('prgr', GetTag( 11526 ), fEntry );
			if(new_id != 0) SetTag( 11526, 'prgr', new_id );
		break; 
		
		case 11527:			// Open tag window
			e = get_entry('prgr', GetTag( 11526 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;
		
		case 11528:			// Choose a tag
			new_id = pick_tag_of_type('prgr', GetTag( 11528 ), fEntry );
			if(new_id != 0) SetTag( 11528, 'prgr', new_id );
		break; 
		
		case 11529:			// Open tag window
			e = get_entry('prgr', GetTag( 11528 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;

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

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

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

		case 11536:			// Choose a tag
			new_id = pick_tag_of_type('stli', GetTag( 11536 ), fEntry );
			if(new_id != 0) SetTag( 11536, 'stli', new_id );
		break; 
		
		case 11537:			// Open tag window
			e = get_entry('stli', GetTag( 11536 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;
	
		// ==============================================================================
		case 11541:			// Choose a tag
			new_id = pick_tag_of_type('proj', GetTag( 11541 ), fEntry );
			if(new_id != 0) SetTag( 11541, 'proj', new_id );
		break; 
		
		case 11542:			// Open tag window
			e = get_entry('proj', GetTag( 11541 ) );
			if(e) TRevengeApp::OpenItem( e );
		break;
		
		case 13000:			// Choose a tag
			new_id = pick_tag_of_type('.256', GetTag( 13000 ), fEntry );
			if(new_id != 0) SetTag( 13000, '.256', new_id );
		break; 
		
		// ==============================================================================
		case 11561:	{		// Open Sequences dialog
			e = get_entry( artType, GetTag( 11501 ) );
			if (e) {
				MonsterSequenceData sd;
				const char **left = (artType == 'skmd' ? AnimStateNames : SequenceNames);
				int i = 0;
				while (left[i]) {
					sd.mons.Insert( i, left[i] );
					++i;
				}
				memcpy( sd.sequences, def.sequence_indexes, sizeof(sd.sequences) );
				if (artType == 'skmd') {
					ExtractSKMDNames( e, &sd.anim );
				} else {
					ExtractCollectionNames( e, &sd.anim );
				}
				bool ok = XGDialog::ModalDialog( KWindowMonsterAnimState, SequenceDlogProc, &sd );
				if (ok) {
					memcpy( def.sequence_indexes, sd.sequences, sizeof(def.sequence_indexes) );
				}
			}
		} break;
		
		case 11562:			// Open Sounds dialog
		{
			MonsterSoundsTable sounds;
			
			sounds.source = get_entry_file( fEntry );
			memcpy( &sounds.sound_type, &def.sound_types, sizeof(char) * MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS );
			memcpy( &sounds.sound_tag, &def.sound_tags, sizeof(file_tag) * MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS );
			
			ok = XGDialog::ModalDialog( KWindowMonsterSounds, SoundsDlogProc, &sounds );
			if( ok )
			{
				memcpy( &def.sound_types, &sounds.sound_type, sizeof(char) * MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS );
				memcpy( &def.sound_tags, &sounds.sound_tag, sizeof(file_tag) * MAXIMUM_NUMBER_OF_MONSTER_DEFINITION_SOUNDS );
			}
		}
		
		break;
		
		case 11563:			// Open Terrain Movement dialog
		{
			MonsterTerrainData terrain;
			
			terrain.base_movement_speed = def.base_movement_speed;
			memcpy( &terrain.movement_modifiers, &def.movement_modifiers, sizeof(short_fixed_fraction) * MAXIMUM_NUMBER_OF_TERRAIN_TYPES );
			memcpy( &terrain.terrain_costs, &def.terrain_costs, sizeof(char) * MAXIMUM_NUMBER_OF_TERRAIN_TYPES );
			
			ok = XGDialog::ModalDialog( KWindowMonsterTerrain, TerrainDlogProc, &terrain );
			if( ok )
			{
				def.base_movement_speed = terrain.base_movement_speed;
				memcpy(&def.movement_modifiers, &terrain.movement_modifiers, sizeof(short_fixed_fraction) * MAXIMUM_NUMBER_OF_TERRAIN_TYPES );
				memcpy( &def.terrain_costs, &terrain.terrain_costs, sizeof(char) * MAXIMUM_NUMBER_OF_TERRAIN_TYPES );
			}
		}
		
		break;
		
		case 11564:			// Open Flags dialog
		{
			unsigned long flags = def.flags;
			ok = XGDialog::ModalDialog( KWindowMonsterFlags, FlagsDlogProc, &flags );
			if( ok )
				def.flags = flags;
		
		}	
		break;
		
		default:
			return -1;
	}
	
	return 0;
}


long TmonsEditForm::DoListSelect( int arg, void * )
{
	int sel = GetInt( arg );
	switch (arg) {
		case 11514: {
			if (sel >= 0 && sel < def.number_of_attacks && (def.flags & _monster_has_3d_model_flag)) {
				EditAttack( sel );
			}
		} return 0;
	}
	
	return -1;
}

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


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


// ========================================  TAG NEEDS UPDATING  ==================================================
bool TmonsEditForm::NeedCommit()
{
	monster_definition original;
	int ii;

	GetValues();
	
	// get the definition from the tag
	if( !read_monster_definition( &original, fEntry ) )
		return FALSE;

#if 1
	for( ii = 0; ii < sizeof(monster_definition) / sizeof(unsigned long); ii++ )
	{
		if( ((unsigned long *)&original)[ii] != ((unsigned long *)&def)[ii] )
			return TRUE;
	}
#else
	if( memcmp( &original, &def, sizeof(monster_definition) ) );
		return TRUE;
#endif	
	return FALSE;
}


// ========================================  SET INTERFACE VALUES  ==================================================
void TmonsEditForm::SetValues()
{
	tag artType = TEST_FLAG32(def.flags, _monster_has_3d_model_bit) ? 'skmd' : '.256';
	
	SetFlag( 12000, TEST_FLAG32( def.flags, _monster_has_3d_model_bit ) );
	
	SetTag( 11501, artType, def.collection_tag );
	SetTag( 11503, 'obje', def.object_tag );
	SetTag( 11505, 'lpgr', def.local_projectile_group_tag );
	SetTag( 11507, 'temp', def.map_action_tag );
	SetTag( 11518, 'prgr', def.exploding_projectile_group_tag );
	SetTag( 11520, 'prgr', def.melee_impact_projectile_group_tag );
	SetTag( 11522, 'prgr', def.dying_projectile_group_tag );
	SetTag( 11524, 'prgr', def.blocked_impact_projectile_group_tag );
	SetTag( 11526, 'prgr', def.absorbed_impact_projectile_group_tag );
	SetTag( 11528, 'prgr', def.entrance_projectile_group_tag );
	SetTag( 11530, 'stli', def.spelling_string_list_tag );
	SetTag( 11532, 'stli', def.names_string_list_tag );
	SetTag( 11534, 'stli', def.flavor_string_list_tag );
	SetTag( 11536, 'stli', def.special_ability_string_list_tag );
	SetTag( 11541, 'proj', def.ammunition_projectile_tag );
	SetTag( 12001, '.256', def.portrait_collection_tag );

	XGPopControl* pList1;
	pList1 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 11509 ));
	assert(pList1);
	pList1->DeleteAll();
	pList1->Insert(0, "Small");
	pList1->Insert(1, "Man-Sized");
	pList1->Insert(2, "Giant");
	pList1->SetValue( def.size );

	XGPopControl* pList2;
	pList2 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 11510 ));
	assert(pList2);
	pList2->DeleteAll();
	pList2->Insert(0, "None");
	pList2->Insert(1, "Nearsighted");
	pList2->Insert(2, "Farsighted");
	pList2->SetValue( def.visibility_type + 1 );

	XGPopControl* pList3;
	pList3 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 11511 ));
	assert(pList3);
	pList3->DeleteAll();
	pList3->Insert(0, "Melee");
	pList3->Insert(1, "Missile");
	pList3->Insert(2, "Suicide");
	pList3->Insert(3, "Special");
	pList3->Insert(4, "Harmless");
	pList3->Insert(5, "Ambient Life");
	pList3->SetValue( def.monster_class );

	XGPopControl* pList4;
	pList4 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 11512 ));
	assert(pList4);
	pList4->DeleteAll();
	pList4->Insert(0, "Light");
	pList4->Insert(1, "Dark");
	pList4->SetValue( def.monster_allegiance );

	XGPopControl* pList5;
	pList5 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 11513 ));
	assert(pList5);
	pList5->DeleteAll();
	pList5->Insert(0, "None");
	pList5->Insert(1, "Unwieldly");
	pList5->Insert(2, "Small");
	pList5->Insert(3, "Large");
	pList5->SetValue( def.desired_projectile_volume + 1 );
	
	RebuildAttackList();
	
	SetShortWorldDistance( 11538, def.warning_distance );
	SetShortWorldDistance( 11539, def.critical_distance );
	SetShortWorldDistance( 11540, def.activation_distance );

	SetInt( 11543, def.initial_ammunition_lower_bound );
	SetInt( 11544, get_upper_bound( def.initial_ammunition_lower_bound, def.initial_ammunition_delta ) );
	SetInt( 11545, def.maximum_ammunition_count );
	SetPercentage( 11546, def.left_handed_fraction );
	SetAngleInSeconds( 11547, def.turning_speed );
	SetPercentage( 11548, def.hard_death_system_shock );
	SetPercentage( 11549, def.flinch_system_shock );
	SetPercentage( 11550, def.absorbed_fraction );
	SetShortFixed( 11551, def.healing_fraction );
	SetInt( 11552, def.combined_power );
	SetShortWorldDistance( 11553, def.longest_range );
	SetInt( 11554, def.experience_point_value );
	SetInt( 11555, def.cost );
	SetShortWorldDistance( 11556, def.pathfinding_radius );
	SetShortFixed( 11557, def.maximum_mana );
	SetShortFixed( 11558, def.mana_recharge_rate );
	SetPercentage( 11559, def.berserk_system_shock );
	SetPercentage( 11560, def.berserk_vitality );
	SetInt( 12003, def.kills_for_maximum_experience_bonus > 0 ? def.kills_for_maximum_experience_bonus : 5 );
	SetInt( 12004, def.experience_bonus_size );
	SetShortWorldDistance( 12005, def.experience_bonus_radius );
	SetPercentage( 12006, def.blocking_fraction );
	SetTag( 13000, '.256', def.carrying_tag );
}


// ========================================  GET INTERFACE VALUES  ==================================================
void TmonsEditForm::GetValues()
{
	SET_FLAG32( def.flags, _monster_has_3d_model_bit, GetFlag( 12000 ) );
	def.collection_tag = GetTag( 11501 );
	def.object_tag = GetTag( 11503 );
	def.local_projectile_group_tag = GetTag( 11505 );
	def.map_action_tag = GetTag( 11507 );
	def.exploding_projectile_group_tag = GetTag( 11518 );
	def.melee_impact_projectile_group_tag = GetTag( 11520 );
	def.dying_projectile_group_tag = GetTag( 11522 );
	def.blocked_impact_projectile_group_tag = GetTag( 11524 );
	def.absorbed_impact_projectile_group_tag = GetTag( 11526 );
	def.entrance_projectile_group_tag = GetTag( 11528 );
	def.spelling_string_list_tag = GetTag( 11530 );
	def.names_string_list_tag = GetTag( 11532 );
	def.flavor_string_list_tag = GetTag( 11534 );
	def.special_ability_string_list_tag = GetTag( 11536 );
	def.ammunition_projectile_tag = GetTag( 11541 );
	def.portrait_collection_tag = GetTag( 12001 );
	
	XGPopControl* pList1;
	pList1 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 11509 ));
	assert(pList1);
	def.size = pList1->GetValue();

	XGPopControl* pList2;
	pList2 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 11510 ));
	assert(pList2);
	def.visibility_type = pList2->GetValue() - 1;

	XGPopControl* pList3;
	pList3 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 11511 ));
	assert(pList3);
	def.monster_class = pList3->GetValue();

	XGPopControl* pList4;
	pList4 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 11512 ));
	assert(pList4);
	def.monster_allegiance = pList4->GetValue();

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

	def.warning_distance = GetShortWorldDistance( 11538 );
	def.critical_distance = GetShortWorldDistance( 11539 );
	def.activation_distance = GetShortWorldDistance( 11540 );
	def.initial_ammunition_lower_bound = GetInt( 11543 );
	def.initial_ammunition_delta = get_delta( (short)GetInt( 11543 ), (short)GetInt( 11544 ) );
	def.maximum_ammunition_count = GetInt( 11545 );
	def.left_handed_fraction = GetPercentage( 11546 );
	def.turning_speed = GetAngleInSeconds( 11547 );
	def.hard_death_system_shock = GetPercentage( 11548 );
	def.flinch_system_shock = GetPercentage( 11549 );
	def.absorbed_fraction = GetPercentage( 11550 );
	def.healing_fraction = GetShortFixed( 11551 );
	def.combined_power = GetInt( 11552 );
	def.longest_range = GetShortWorldDistance( 11553 );
	def.experience_point_value = GetInt( 11554 );
	def.cost = GetInt( 11555 );
	def.pathfinding_radius = GetShortWorldDistance( 11556 );
	def.maximum_mana = GetShortFixed( 11557 );
	def.mana_recharge_rate = GetShortFixed( 11558 );
	def.berserk_system_shock = GetPercentage( 11559 );
	def.berserk_vitality = GetPercentage( 11560 );
	def.kills_for_maximum_experience_bonus = GetInt( 12003 );
	def.experience_bonus_size = GetInt( 12004 );
	def.experience_bonus_radius = GetShortWorldDistance( 12005 );
	def.blocking_fraction = GetPercentage( 12006 );
	def.carrying_tag = GetTag(13000);
}

bool replace_monster_data( tag_entry *te, vengeance_script_mons *mons )
{
	monster_definition monster;
	
	if( read_monster_definition( &monster, te ) )
	{
		monster.desired_projectile_volume = mons->desired_projectile_volume;
		monster.left_handed_fraction = mons->left_handed_fraction;
		monster.turning_speed = mons->turning_speed;
		monster.hard_death_system_shock = mons->hard_death_system_shock;
		monster.flinch_system_shock = mons->flinch_system_shock;
		monster.absorbed_fraction = mons->absorbed_fraction;
		monster.healing_fraction = mons->healing_fraction;
		monster.combined_power = mons->combined_power;
		monster.longest_range = mons->longest_range;
		monster.experience_point_value = mons->experience_point_value;
		monster.cost = mons->cost;
		monster.pathfinding_radius = mons->pathfinding_radius;
		monster.maximum_mana = mons->maximum_mana;
		monster.mana_recharge_rate = mons->mana_recharge_rate;
		monster.berserk_system_shock = mons->berserk_system_shock;
		monster.berserk_vitality = mons->berserk_vitality;
		monster.kills_for_maximum_experience_bonus = mons->kills_for_maximum_experience_bonus;
		monster.experience_bonus_size = mons->experience_bonus_size;
		monster.experience_bonus_radius = mons->experience_bonus_radius;
		monster.blocking_fraction = mons->blocking_fraction;
		monster.warning_distance = mons->warning_distance;
		monster.critical_distance = mons->critical_distance;
		monster.activation_distance = mons->activation_distance;
		monster.initial_ammunition_lower_bound = mons->initial_ammunition_lower_bound;
		monster.initial_ammunition_delta = mons->initial_ammunition_delta;
		monster.maximum_ammunition_count = mons->maximum_ammunition_count;
		monster.base_movement_speed = mons->base_movement_speed;
		memcpy( &monster.terrain_costs, &mons->terrain_costs, MAXIMUM_NUMBER_OF_TERRAIN_TYPES * sizeof(char) );
		memcpy( &monster.movement_modifiers, &mons->movement_modifiers, MAXIMUM_NUMBER_OF_TERRAIN_TYPES * sizeof(short_fixed_fraction) );
		monster.flags &= mons->flag_mask;
		monster.flags |= mons->flags;
	}
	
	return save_monster_definition( &monster, te );
}

bool retrive_monster_data( tag_entry *te, vengeance_script_mons *mons )
{
	monster_definition monster;
	
	if( read_monster_definition( &monster, te ) )
	{
		mons->desired_projectile_volume = monster.desired_projectile_volume;
		mons->left_handed_fraction = monster.left_handed_fraction;
		mons->turning_speed = monster.turning_speed;
		mons->hard_death_system_shock = monster.hard_death_system_shock;
		mons->flinch_system_shock = monster.flinch_system_shock;
		mons->absorbed_fraction = monster.absorbed_fraction;
		mons->healing_fraction = monster.healing_fraction;
		mons->combined_power = monster.combined_power;
		mons->longest_range = monster.longest_range;
		mons->experience_point_value = monster.experience_point_value;
		mons->cost = monster.cost;
		mons->pathfinding_radius = monster.pathfinding_radius;
		mons->maximum_mana = monster.maximum_mana;
		mons->mana_recharge_rate = monster.mana_recharge_rate;
		mons->berserk_system_shock = monster.berserk_system_shock;
		mons->berserk_vitality = monster.berserk_vitality;
		mons->kills_for_maximum_experience_bonus = monster.kills_for_maximum_experience_bonus;
		mons->experience_bonus_size = monster.experience_bonus_size;
		mons->experience_bonus_radius = monster.experience_bonus_radius;
		mons->blocking_fraction = monster.blocking_fraction;
		mons->warning_distance = monster.warning_distance;
		mons->critical_distance = monster.critical_distance;
		mons->activation_distance = monster.activation_distance;
		mons->initial_ammunition_lower_bound = monster.initial_ammunition_lower_bound;
		mons->initial_ammunition_delta = monster.initial_ammunition_delta;
		mons->maximum_ammunition_count = monster.maximum_ammunition_count;
		mons->base_movement_speed = monster.base_movement_speed;
		memcpy( &mons->terrain_costs, &monster.terrain_costs, MAXIMUM_NUMBER_OF_TERRAIN_TYPES * sizeof(char) );
		memcpy( &mons->movement_modifiers, &monster.movement_modifiers, MAXIMUM_NUMBER_OF_TERRAIN_TYPES * sizeof(short_fixed_fraction) );
		mons->flags = monster.flags;
		
		mons->number_of_attacks = monster.number_of_attacks;
		
		return TRUE;
	}
	
	return FALSE;
}

bool replace_attack_data( tag_entry *te, vengeance_script_attk *attk )
{
	monster_definition monster;
	
	if( read_monster_definition( &monster, te ) )
	{
		monster.attacks[attk->index].minimum_range = attk->minimum_range;
		monster.attacks[attk->index].maximum_range = attk->maximum_range;
		monster.attacks[attk->index].repetitions = attk->repetitions;
		monster.attacks[attk->index].miss_fraction = attk->miss_fraction;
		monster.attacks[attk->index].initial_velocity_lower_bound =  attk->initial_velocity_lower_bound;
		monster.attacks[attk->index].initial_velocity_delta = attk->initial_velocity_delta;
		monster.attacks[attk->index].initial_velocity_error = attk->initial_velocity_error;
		monster.attacks[attk->index].velocity_improvement_with_experience = attk->velocity_improvement_with_experience;
		monster.attacks[attk->index].recovery_time = attk->recovery_time;
		monster.attacks[attk->index].recovery_time_experience_delta = attk->recovery_time_experience_delta;
		monster.attacks[attk->index].mana_cost = attk->mana_cost;
		monster.attacks[attk->index].flags = attk->flags;
		monster.attacks[attk->index].extra_flags = attk->extra_flags;
	}
	
	return save_monster_definition( &monster, te );
}

bool retrive_attack_data( tag_entry *te, vengeance_script_attk *attk )
{
	monster_definition monster;
	
	if( read_monster_definition( &monster, te ) )
	{
		attk->minimum_range = monster.attacks[attk->index].minimum_range;
		attk->maximum_range = monster.attacks[attk->index].maximum_range;
		attk->repetitions = monster.attacks[attk->index].repetitions;
		attk->miss_fraction = monster.attacks[attk->index].miss_fraction;
		attk->initial_velocity_lower_bound =  monster.attacks[attk->index].initial_velocity_lower_bound;
		attk->initial_velocity_delta = monster.attacks[attk->index].initial_velocity_delta;
		attk->initial_velocity_error = monster.attacks[attk->index].initial_velocity_error;
		attk->velocity_improvement_with_experience = monster.attacks[attk->index].velocity_improvement_with_experience;
		attk->recovery_time = monster.attacks[attk->index].recovery_time;
		attk->recovery_time_experience_delta = monster.attacks[attk->index].recovery_time_experience_delta;
		attk->mana_cost = monster.attacks[attk->index].mana_cost;
		attk->flags = monster.attacks[attk->index].flags;
		attk->extra_flags = monster.attacks[attk->index].extra_flags;

		return TRUE;
	}
	
	return FALSE;
}
