MODSon[line.com] Wiki - Beta 1.0

World at War: SP Dogs

From MODSonline Wiki

Jump to: navigation, search

Contents

[edit] Radiant Setup

Actor>enemy>dog

Dogs are always placed on the ground or slightly above it, never passing through anything.

Max AI dogs is unknown(32?)

Dogs will always target the player(s) and will attack as soon as they hear or see them.

[edit] Traverse/ Navigation

Dogs use the wall_hop traverse prefab just like other AI.

Dogs use pathnodes just like normal AI actors. Monnster clip or "ai_clip" will stop dogs from leaving or going into non-playable areas.

[edit] Sounds

Custom sound alias for dogs:

raw/soundaliases/dog.csv

name,mature,platform,file,sequence,vol_min,vol_max,dist_min,dist_max,limit_count,limit_type,entity_limit_count,entity_limit_type,bus,volume_min_falloff_curve,volumefalloffcurve,reverb_send,dist_reverb_max,reverb_min_falloff_curve,reverb_falloff_curve,randomize_type,pitch_min,pitch_max,spatialized,type,probability,loop,masterslave,loadspec,subtitle,compression,secondaryaliasname,chainaliasname,startdelay,speakermap,lfe percentage,center percentage,envelop_min,envelop_max,envelop percentage,occlusion_level,occlusion_wet_dry,real_delay,distance_lpf,move_type,move_time,min_priority,max_priority,min_priority_threshold,max_priority_threshold
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
# DOG Sfx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
anml_dog_bark,,,sfx/character/dog/bark_##,,0.6,0.7,500,6000,,,,,full_vol,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_mid,,,sfx/character/dog/bark_##,,0.7,0.8,500,6000,,,,,full_vol,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_close,,,sfx/character/dog/close_##,,0.8,0.9,500,6000,,,,,full_vol,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_attack_kill_player,,,sfx/character/dog/anml_dog_attack_kill_player2.wav,,1,1,100,200,,,,,voice,,curve3,1,2000,,curve3,pitch,1,1,3d,,,,,all_mp pel1b dog_test,,,,,,wpn_all,,,,,,0.2,,,,,,60,80,0.25,1
dog_neckbreak,,,sfx/character/dog/dog_neckbreak.wav,,1,1,400,1000,,,,,voice,,curve3,1,2000,,curve3,pitch,1,1,3d,,,,,all_mp pel1b dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_die_front,,,sfx/character/dog/death_##,,0.8,0.8,400,1250,,,,,full_vol,,curve3,1,1500,,curve3,pitch,0.95,1.05,3d,,,,,dog_test all_mp pel1b,,,,,50,,,,,,,0.2,,,,,,60,80,0.25,1
# loadspeced for dog_test memory,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
anml_dog_bark,,,SFX/character/dog/bark/bark_00.wav,1,1,1,4000,6000,,,,,full_vol,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark,,,sfx/character/dog/bark/bark_01.wav,2,1,1,4000,6000,,,,,full_vol,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark,,,sfx/character/dog/bark/bark_02.wav,3,1,1,4000,6000,,,,,full_vol,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark,,,sfx/character/dog/bark/bark_03.wav,4,1,1,4000,6000,,,,,full_vol,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark,,,sfx/character/dog/bark/bark_04.wav,5,1,1,4000,6000,,,,,full_vol,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_mid,,,sfx/character/dog/bark/bark_00.wav,1,0.5,0.6,500,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_mid,,,sfx/character/dog/bark/bark_01.wav,2,0.5,0.6,500,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_mid,,,sfx/character/dog/bark/bark_02.wav,3,0.5,0.6,500,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_mid,,,sfx/character/dog/bark/bark_03.wav,4,0.5,0.6,500,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_mid,,,sfx/character/dog/bark/bark_04.wav,5,0.5,0.6,500,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_close,,,sfx/character/dog/bark/bark_00.wav,1,0.6,0.7,500,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_close,,,sfx/character/dog/bark/bark_01.wav,2,0.6,0.7,500,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_close,,,sfx/character/dog/bark/bark_02.wav,3,0.6,0.7,500,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_close,,,sfx/character/dog/bark/bark_03.wav,4,0.6,0.7,500,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_attack_close,,,sfx/character/dog/bark/bark_04.wav,5,0.6,0.7,500,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_close,,,sfx/character/dog/bark/bark_00.wav,1,0.6,0.7,500,6000,5,oldest,1,oldest,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_close,,,sfx/character/dog/bark/bark_01.wav,2,0.6,0.7,500,6000,5,oldest,1,oldest,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_close,,,sfx/character/dog/bark/bark_02.wav,3,0.6,0.7,500,6000,5,oldest,1,oldest,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_close,,,sfx/character/dog/bark/bark_03.wav,4,0.6,0.7,500,6000,5,oldest,1,oldest,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_close,,,sfx/character/dog/bark/bark_04.wav,5,0.6,0.7,500,6000,5,oldest,1,oldest,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_run_start,,,sfx/character/dog/runstart_##,,0.6,0.7,200,6000,,,,,character,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_pain,,,sfx/character/dog/pain_##,,0.8,0.8,400,1250,,,,,full_vol,,curve3,1,1500,,curve3,pitch,0.95,1.05,3d,,,,,dog_test all_mp pel1b,,,,,50,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_death,,,sfx/character/dog/death_##,,0.8,0.8,400,1250,,,,,full_vol,,curve3,1,1500,,curve3,pitch,0.95,1.05,3d,,,,,dog_test all_mp pel1b,,,,,50,,,,,,,0.2,,,,,,60,80,0.25,1
#anml_dog_lock,,,sfx/character/dog/lock_##,,0.8,0.8,500,7000,,,,,character,,curve3,1,8000,,curve3,pitch,1,1,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_attack_jump,,,sfx/character/dog/attack_##,,0.75,0.75,200,500,,,,,full_vol,,curve3,1,700,,curve3,pitch,1,1,3d,,,,,all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_bark_close,,,sfx/character/dog/bark_##,,0.8,0.9,500,5000,5,oldest,1,oldest,full_vol,,curve3,1,7000,,curve3,pitch,1,1,3d,,,,,all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
#dog_test,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
anml_dog_attack_jump,,,sfx/character/dog/attack_##,,1,1,1000,5000,,,,,full_vol,,curve3,1,,,curve3,pitch,1,1,3d,,,,,dog_test,,,anml_dog_attack_jump2,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_attack_jump2,,,sfx/character/dog/attack_##,,1,1,1000,5000,,,,,full_vol,,curve3,1,,,curve3,pitch,1,1,3d,,,,,dog_test,,,,,1000,,,,,,,0.2,,,,,,60,80,0.25,1

melee_dog_hit,,,sfx/character/dog/dog_bite_00.wav,,1,1,50,800,,,,,full_vol,,curve3,,,,curve3,,1,1,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,0.2,,,,,,90,90,0.25,1
melee_dog_hit_other,,,sfx/character/dog/dog_bite_00.wav,,1,1,50,800,,,,,full_vol,,curve3,,,,curve3,,1,1,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,0.2,,,,,,90,90,0.25,1
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
dogstep_run_collar,,,sfx/character/dog/run_collar_##,,0.7,0.7,400,1750,,,,,character,,curve3,1,2250,,curve3,,1,1,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
dog_gear_rattle_run,,,sfx/character/dog/run_collar_##,,0.7,0.7,200,1750,,,,,character,,curve3,1,2250,,curve3,,1.1,1.4,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
dog_gear_rattle_run_enemyteam,,,sfx/character/dog/run_collar_##,,0.7,0.7,200,1750,,,,,character,,curve3,1,2250,,curve3,,1,1,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
dog_gear_rattle_run_enemy,,,sfx/character/dog/run_collar_##,,0.7,0.7,200,1750,,,,,character,,curve3,1,2250,,curve3,,1,1,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
anml_dog_growl,,,sfx/character/dog/growl_##,,0.5,0.5,200,1000,3,oldest,1,oldest,character,,curve3,,,,curve3,pitch,0.95,1,3d,,0.4,,,dog_test all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_idle_look,,,sfx/character/dog/idle_##,,0.5,0.5,200,1000,1,oldest,1,oldest,character,,curve3,,,,curve3,pitch,0.85,1.05,3d,,0.4,,,dog_test all_mp pel1b,,,,,,,,,,,,0.2,,,,,,60,80,0.25,1
anml_dog_attack_fence,,,null.wav,,,,,,,,,,,,,,,,,,,,,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,,,,
anml_dog_attack_miss,,,null.wav,,,,,,,,,,,,,,,,,,,,,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,,,,
# Dog Run 3rd Person,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
dogstep_run_default,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_bark,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_brick,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_carpet,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_cloth,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_concrete,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_dirt,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_flesh,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_foliage,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_glass,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_grass,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_gravel,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
#dogstep_run_ice,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_metal,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_mud,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_paper,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_plaster,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_rock,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_sand,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
#dogstep_run_snow,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_water,,,sfx/character/dog/run/gravel_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_wood,,,sfx/character/dog/run/wood_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_asphault,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_asphalt,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_ceramic,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_plastic,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_rubber,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_cushion,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1
dogstep_run_fruit,,,sfx/character/dog/run/other_##,,0.45,0.45,200,3000,4,oldest,,,character,,curve3,,4000,,curve3,pitch,0.98,1.02,3d,,,,,dog_test all_mp pel1b,,,,,,,,,,,,,,,,,,60,80,0.25,1

Dog Source Files

[edit] Scripts

Dogs use a variety of scripts:

This is the main gsc file for your map, we've added a thread to it though:

raw\maps\yourmapname.gsc:

#include maps\_utility;
#include maps\_anim;
#include common_scripts\utility;
#using_animtree( "generic_human" ); 
main()
{
	maps\_load::main();
	// dogs
	animscripts\dog_init::initDogAnimations();
		//***TEMP level.player fix for dog scripts
	level thread dog_script_fix();
}
		//**TEMP - will remove when dog scripts are clean
dog_script_fix()
{
	level.player = get_players()[0];
}

Dog Traverse Scripts (not included by default with the tools/saves editing the MP ones) All dog gsc files will go in: raw\maps\animscripts;

dog_combat.gsc

#include common_scripts\utility;
#include animscripts\utility;
#include maps\_utility;

#using_animtree ("dog");

main()
{
	self endon("killanimscript");

	assert( isdefined( self.enemy ) );
	if ( !isalive( self.enemy ) )
	{
		combatIdle();
		return;
	}

	if ( IsPlayer(self.enemy) )
		self meleeBiteAttackPlayer(self.enemy);
	else
		self meleeStruggleVsAI();
}

killplayer(player)
{
	self endon( "pvd_melee_interrupted" );
	
	player.specialDeath = true;
	player setcandamage( true );
	player.player_view hide();

	if ( isdefined( player.player_view ) ) 
	{
		tagPos = player.player_view gettagorigin( "tag_torso" );	// rough tag to play fx on
		playfx( level._effect["dog_bite_blood"], tagPos + (0, 0, 20), anglestoforward( player.angles ), anglestoup( player.angles ) );
	}

	wait 1;
	player enableHealthShield( false );
	
	damage = 10 * player.health / getdvarfloat("player_DamageMultiplier");
	
	if ( !isalive( player ) )
		return;
		
	player dodamage( damage, player.origin ); // set damage position to player origin to avoid knockback
	player shellshock("default", 5);
	waittillframeend; // so quote gets set after _quotes sets it
	//setDvar( "ui_deadquote", level.dog_death_quote );
	setDvar( "ui_deadquote", "" );
	thread dog_death_hud(player);	
}

dog_death_hud(player)
{
	wait( 1.5 );
	
	thread dog_deathquote(player);
	overlay = newclientHudElem(player);
	overlay.x = 0;
	overlay.y = 50;
	overlay setshader( "hud_dog_melee", 96, 96 );
	overlay.alignX = "center";
	overlay.alignY = "middle";
	overlay.horzAlign = "center";
	overlay.vertAlign = "middle";
	overlay.foreground = true;
	overlay.alpha = 0;
	overlay fadeOverTime( 1 );
	overlay.alpha = 1;
}

dog_deathquote(player)
{
	textOverlay = maps\_hud_util::createFontString( "default", 1.75, player );
	textOverlay.color = (1,1,1);
	textOverlay setText( level.dog_death_quote );	
	textOverlay.x = 0;
	textOverlay.y = -30;
	textOverlay.alignX = "center";
	textOverlay.alignY = "middle";
	textOverlay.horzAlign = "center";
	textOverlay.vertAlign = "middle";
	textOverlay.foreground = true;
	textOverlay.alpha = 0;
	textOverlay fadeOverTime( 1 );
	textOverlay.alpha = 1;
}

attackMiss()
{
	self clearanim(%root, 0.1);
	
	if ( isdefined( self.enemy ) )
	{
		forward	= anglestoforward( self.angles );
		dirToEnemy = self.enemy.origin - ( self.origin + vectorscale( forward, 50 ) );
		if ( vectordot( dirToEnemy, forward ) > 0 )
		{
			self setflaggedanimrestart( "miss_anim", %german_shepherd_attack_player_miss, 1, 0, 1 );
			self thread animscripts\dog_stop::lookAtTarget( "normal" );
		}
		else 
		{
			self.skipStartMove = true;
			self thread attackMissTrackTargetThread();
			
			if ( ( dirToEnemy[0] * forward[1] - dirToEnemy[1] * forward[0] ) > 0 )
				self setflaggedanimrestart( "miss_anim", %german_shepherd_attack_player_miss_turnR, 1, 0, 1 );
			else
				self setflaggedanimrestart( "miss_anim", %german_shepherd_attack_player_miss_turnL, 1, 0, 1 );
		}
	}
	else
	{
		self setflaggedanimrestart( "miss_anim", %german_shepherd_attack_player_miss, 1, 0, 1 );
	}

	self animscripts\shared::DoNoteTracks( "miss_anim" );
	self notify("stop tracking");
}


attackMissTrackTargetThread()
{
	self endon( "killanimscript" );

	wait 0.6;
	self OrientMode( "face enemy" );
}


handleMeleeBiteAttackNoteTracks( note )
{
	switch ( note )
	{
		case "dog_melee":
		{
			hitEnt = self melee( anglesToForward( self.angles ) );

			if ( isdefined( hitEnt ) )
			{
				if ( isplayer(hitEnt) )
					hitEnt shellshock("dog_bite", 1);
			}
			else
			{
				attackMiss();
				return true;
			}
		}
		break;

		case "stop_tracking":
			self OrientMode( "face current" );
			break;
	}
}

addSafetyHealth(player)
{
	healthFrac = player getnormalhealth();
	if ( healthFrac == 0 )
		return false;
		
	if ( healthFrac < 0.25 )
	{
		player setnormalhealth( healthFrac + 0.25 );
		return true;
	}
	return false;
}

removeSafetyHealth(player)
{
	healthFrac = player getnormalhealth();
	if ( healthFrac > 0.25 )
		player setnormalhealth( healthFrac - 0.25 );
	else
		player setnormalhealth( 0.01 );
}

handleMeleeFinishAttackNoteTracks( note, player )
{
	switch( note )
	{
		case "dog_melee":

			healthAdded = addSafetyHealth(player);
			
			hitEnt = self melee( anglesToForward( self.angles ) );
			if ( isdefined( hitEnt ) && isalive( player ) )
			{
				if ( healthAdded )
					removeSafetyHealth(player);
							
				if ( hitEnt == player )
				{
					/#
					if ( isgodmode( player ) )
					{
						println("Player in god mode, aborting dog attack");
						break;
					}
					#/
					
					// this shouldn't happen, added for safety
					if ( !isdefined( player.player_view ) ) 
						player.player_view = PlayerView_Spawn();
					//SCRIPTER_MOD: JesseS (3/25/2008) - 
					if ( player.player_view PlayerView_StartSequence( self, player ) )
						self setcandamage( false );
					break;
				}
			}
			else
			{
				if ( healthAdded )
					removeSafetyHealth(player);
									
				attackMiss();
				return true;
			}
			break;
			
		case "dog_early":
			self notify( "dog_early_notetrack" );
///////////////////////////////////////////////////////////////////////////////			
//DK - DA wants this to be consistent with the button mashing in banzai ...		
//added, this line was moved up from notetrack below
			thread set_melee_timer(player);
///////////////////////////////////////////////////////////////////////////////			

//			speed = randomfloatrange( 0.3, 1.2 );
			speed = 0.45 + 0.8 * level.dog_melee_timing_array[ level.dog_melee_index ];
//			println( "speed " + speed );
			level.dog_melee_index++;
			if ( level.dog_melee_index >= level.dog_melee_timing_array.size )
			{
				level.dog_melee_index = 0;
				// randomize the array for variety in dog attack timing
				level.dog_melee_timing_array = maps\_utility::array_randomize( level.dog_melee_timing_array );
			}

			self setflaggedanimlimited( "meleeanim", %german_shepherd_attack_player, 1, 0.2, speed );
			self setflaggedanimlimited( "meleeanim", %german_shepherd_attack_player_late, 1, 0.2, speed );
//			self setanimlimited( %attack_player, 1, 0, 1 );
//			self setanimlimited( %attack_player_late, 0, 0, 1 );

			player.player_view setflaggedanimlimited( "viewanim", get_player_view_dog_knock_down_anim(), 1, 0.2, speed );
			player.player_view setflaggedanimlimited( "viewanim", get_player_view_dog_knock_down_late_anim(), 1, 0.2, speed );
//			level.player_view setanimlimited( get_player_knockdown_knob(), 1, 0, 1 );
//			level.player_view setanimlimited( get_player_knockdown_late_knob(), 0, 0, 1 );
			break;
			
		case "dog_lunge":
///////////////////////////////////////////////////////////////////////////////			
//DK - DA wants this to be consistent with the button mashing in banzai ...		
//			thread set_melee_timer(player);
///////////////////////////////////////////////////////////////////////////////			
			self setflaggedanim( "meleeanim", %german_shepherd_attack_player, 1, 0.2, 1 );
			self setflaggedanim( "meleeanim", %german_shepherd_attack_player, 1, 0.2, 1 );

			if ( !isdefined( player.player_view ) ) 
				player.player_view = PlayerView_Spawn();

			player.player_view setflaggedanim( "viewanim", get_player_view_dog_knock_down_anim(), 1, 0.2, 1 );
			player.player_view setflaggedanim( "viewanim", get_player_view_dog_knock_down_late_anim(), 1, 0.2, 1 );
			break;
			
		case "dogbite_damage":
			/#
			if ( isgodmode( player ) )
				break;
			#/
		
			self thread killplayer(player);
			break;
			
		case "stop_tracking":
			self OrientMode("face current" );
			break;
	}
}

//handle_dogbite_notetrack( note )
//{
//	switch( note )
//	{
//		case "dogbite_damage":
//			 /#
//			if ( isgodmode( level.player ) )
//				break;
//			#/ 
//
//			self thread killplayer();
//			break;
//	}
//}

set_melee_timer(player)
{
	wait( 0.15 );
	self.melee_able_timer = gettime();
	self thread dog_hint(player);

	/#
	if ( getdebugdvar( "dog_hint" ) == "on" )
	{
		introblack = newclientHudElem(player);
		introblack.x = 50;
		introblack.y = 50;
		introblack.horzAlign = "fullscreen";
		introblack.vertAlign = "fullscreen";
		introblack.foreground = true;
		introblack setShader("black", 100, 100 );
		wait ( 0.25 );
		introblack destroy();
	}
	#/
}


meleeBiteAttackPlayer(player)
{
	attackRangeBuffer = 30;
	meleeRange = self.meleeAttackDist + attackRangeBuffer;

	for ( ;; )	
	{
		if ( !isalive( self.enemy ) )
			break;
			
		if ( ( isdefined( player.syncedMeleeTarget ) && player.syncedMeleeTarget != self ) ||
			 ( isdefined( player.player_view ) && isdefined( player.player_view.inSeq ) ) )
		{
			if ( checkEndCombat( meleeRange ) )
			{
				break;
			}
			else
			{
				combatIdle();
				continue;
			}
		}

		if ( self shouldWaitInCombatIdle() )
		{
			combatIdle();
			continue;
		}
			
		self OrientMode("face enemy");
		self animMode( "gravity" );

		self.safeToChangeScript = false;

			prepareAttackPlayer(player);

		self clearanim(%root, 0.1);
		self clearpitchorient();
		
/#		
		if ( getdebugdvar( "debug_dog_sound" ) != "" )
			iprintln( "dog " + (self getentnum()) + " attack player " + getTime() );

#/		
		//self thread play_sound_on_tag( "anml_dog_growl", "tag_eye" );
		
		player setNextDogAttackAllowTime( 500 );
		
		if ( dog_cant_kill_in_one_hit(player) )
		{
			level.lastDogMeleePlayerTime = getTime();
			level.dogMeleePlayerCounter++;
		
			self setflaggedanimrestart( "meleeanim", %german_shepherd_run_attack, 1, 0.2, 1 );
			self animscripts\shared::DoNoteTracks( "meleeanim", ::handleMeleeBiteAttackNoteTracks );
		}
		else
		{
			self thread dog_melee_death(player);
			player.attacked_by_dog = true;
			self thread clear_player_attacked_by_dog_on_death(player);
		self setflaggedanimrestart("meleeanim", %german_shepherd_attack_player, 1, 0.2, 1);
			self setflaggedanimrestart( "meleeanim", %german_shepherd_attack_player_late, 1, 0, 1 );
			self setanimlimited( %attack_player, 1, 0, 1 );
			self setanimlimited( %attack_player_late, 0.01, 0, 1 );
		
			self animscripts\shared::DoNoteTracks( "meleeanim", ::Handlemeleefinishattacknotetracks, undefined, player );
//			self animscripts\shared::DoNoteTracks( "meleeanim_late", ::canUseLaser );
			self notify( "dog_no_longer_melee_able" );
		self setcandamage( true );
		self unlink();
		}
		
		self.safeToChangeScript = true;

		if ( checkEndCombat( meleeRange ) )
			break;
	}
	
	self.safeToChangeScript = true;
	self animMode("none");
}

clear_player_attacked_by_dog_on_death(player)
{
	self waittill( "death" );
	player.attacked_by_dog = undefined;
}
			

dog_cant_kill_in_one_hit(player)
{
	if ( isdefined( player.dogs_dont_instant_kill ) )
	{
		assertex( player.dogs_dont_instant_kill, "Dont set player.dogs_dont_instant_kill to false, set to undefined" );
		return true;
	}
	
	if ( getTime() - level.lastDogMeleePlayerTime > 8000 )
		level.dogMeleePlayerCounter = 0;
	
	return level.dogMeleePlayerCounter < anim.dog_hits_before_kill && 
		   player.health > 25;	// little more than the damage one melee dog bite hit will do
}


// prevent multiple dogs attacking at the same time and overlapping
shouldWaitInCombatIdle()
{
	assert( isdefined( self.enemy ) && isalive( self.enemy ) );
	
	return isdefined( self.enemy.dogAttackAllowTime ) && ( gettime() < self.enemy.dogAttackAllowTime );
}

// call on target
setNextDogAttackAllowTime( time )
{
	self.dogAttackAllowTime = gettime() + time;
}


meleeStruggleVsAI()
{
	if ( !isalive( self.enemy ) )
		return;
	
	if ( isdefined( self.enemy.syncedMeleeTarget ) || self shouldWaitInCombatIdle() )
	{
		combatIdle();
		return;
	}

	self.enemy setNextDogAttackAllowTime( 500 );

	self.safeToChangeScript = false;
	self animMode( "zonly_physics" );
	self.pushable = false;

	self clearpitchorient();

	self.meleeKillTarget = !isdefined( self.enemy.magic_bullet_shield ) && 
						   ( isdefined( self.enemy.doingLongDeath ) || randomint( 100 ) > 50 );

	meleeSeqAnims = [];
	meleeSeqAnim[0] = %root;
	meleeSeqAnim[1] = %german_shepherd_attack_AI_01_start_a;
	meleeSeqAnim[2] = %german_shepherd_attack_AI_02_idle_a;
	if ( self.meleeKillTarget )
	{
		meleeSeqAnim[3] = %german_shepherd_attack_AI_03_pushed_a;
		meleeSeqAnim[4] = %german_shepherd_attack_AI_04_middle_a;
		meleeSeqAnim[5] = %german_shepherd_attack_AI_05_kill_a;
		numMeleeStage = 5;
	}
	else
	{
		meleeSeqAnim[3] = %german_shepherd_attack_AI_03_shot_a;
		numMeleeStage = 3;
	}

	angles = vectorToAngles( self.origin - self.enemy.origin );

	self.originalTarget = self.enemy;

	self setcandamage( false );
	self clearanim( meleeSeqAnim[0], 0.1 );
	self animrelative( "meleeanim", self.enemy.origin, angles, meleeSeqAnim[1] );
	self animscripts\shared::DoNoteTracks("meleeanim", ::handleStartAIPart);
	
	self setcandamage( true );
	self animMode( "zonly_physics" );
	
	for ( meleeSeq = 1; meleeSeq < numMeleeStage; meleeSeq++ )
	{
		self clearanim( meleeSeqAnim[meleeSeq], 0 );
		
		if ( !inSyncMeleeWithTarget() )
			break;
		
		// get ready to die
		if ( !self.meleeKillTarget && meleeSeq + 1 == numMeleeStage )
			self.health = 1;

		self setflaggedanimrestart("meleeanim", meleeSeqAnim[meleeSeq + 1], 1, 0, 1);
		self animscripts\shared::DoNoteTracks( "meleeanim" );
	}
	
	self unlink();
	self.pushable = true;
	self.safeToChangeScript = true;
}

combatIdle()
{
	self OrientMode("face enemy");
	self clearanim(%root, 0.1);
	self animMode( "zonly_physics" );
	
	idleAnims = [];
	idleAnims[ 0 ] = %german_shepherd_attackidle;
	idleAnims[ 1 ] = %german_shepherd_attackidle_bark;
	idleAnims[ 2 ] = %german_shepherd_attackidle_growl;
	
	idleAnim = maps\_utility::random( idleAnims );
	
	self thread combatIdlePreventOverlappingPlayer();
			
	self setflaggedanimrestart( "combat_idle", idleAnim, 1, 0.2, 1 );
	self animscripts\shared::DoNoteTracks( "combat_idle" );
	
	self notify( "combatIdleEnd" );
}


// when player is in melee sequence, other dogs need to move away
combatIdlePreventOverlappingPlayer()
{
	self endon( "killanimscript" );
	self endon( "combatIdleEnd" );
	
	while( 1 )
	{
		wait 0.1;
		
		// SCRIPTER_MOD: JesseS (3/25/2008): This might not work, going to set self.enemy here 
		// instead of level.player
		
		if ( !isdefined( self.enemy))
			continue;
			
		if ( !isdefined( self.enemy.syncedMeleeTarget ) || self.enemy.syncedMeleeTarget == self )
			continue;
		
		offsetVec = self.enemy.origin - self.origin;
		
		if ( offsetVec[2] * offsetVec[2] > 6400 )
			continue;			
		
		offsetVec = ( offsetVec[0], offsetVec[1], 0 );
	
		offset = length( offsetVec );
		
		if ( offset < 1 )
			offsetVec = anglestoforward( self.angles );
		
		if ( offset < 30 )
		{
			offsetVec = vectorscale( offsetVec, 3 / offset );
			self teleport( self.origin - offsetVec );
		}			
	}
}


inSyncMeleeWithTarget()
{
	return( isdefined( self.enemy ) && isdefined( self.enemy.syncedMeleeTarget ) && self.enemy.syncedMeleeTarget == self );
}

handleStartAIPart( note )
{
	if ( note != "ai_attack_start" )
		return false;

	if ( !isdefined( self.enemy ) )
		return true;
		
	if ( self.enemy != self.originalTarget )
		return true;

	// enemy already has a synced melee target
	if ( isdefined( self.enemy.syncedMeleeTarget ) )
		return true;
		
	//self.enemy thread draw_tag( "tag_sync" );

	self.enemy.syncedMeleeTarget = self;
	self.enemy animcustom(::meleeStruggleVsDog);
}

checkEndCombat( meleeRange )
{
	if ( !isdefined( self.enemy ) )
		return false;
		
	distToTargetSq = distanceSquared( self.origin, self.enemy.origin );
	
	return ( distToTargetSq > meleeRange * meleeRange );
}

prepareAttackPlayer(player)
{
	if ( !isdefined( player.player_view ) ) 
		player.player_view = PlayerView_Spawn(player);

	level.dog_death_quote = &"SCRIPT_PLATFORM_DOG_DEATH_DO_NOTHING";
	distanceToTarget = distance( self.origin, self.enemy.origin );
	
	if ( distanceToTarget > self.meleeAttackDist )
	{
		offset = self.enemy.origin - self.origin;

		length = ( distanceToTarget - self.meleeAttackDist ) / distanceToTarget;
		offset = ( offset[0] * length, offset[1] * length, offset[2] * length );
		
		self thread attackTeleportThread( offset );
	}
}

// make up for error in intial attack jump position
attackTeleportThread( offset )
{
	self endon ("death");
	self endon ("killanimscript");
	reps = 5;
	increment = ( offset[0] / reps, offset[1] / reps, offset[2] / reps );
	for ( i = 0; i < reps; i++ )
	{
		self teleport (self.origin + increment);
		wait (0.05);
	}
}

player_attacked()
{
	return isalive( self ) && ( self MeleeButtonPressed() );
}



dog_hint(player)
{
	if(!isdefined(level.dogHintElem))
	{
		level.dogHitElem = [];
	}
	
	press_time = anim.dog_presstime / 1000;
	level endon ( "clearing_dog_hint" );
	
	num = player GetEntityNumber();
	
	if ( isDefined( level.dogHintElem ) )
	{
		if(isdefined(level.dogHintElem[num]))
		{
			level.dogHintElem[num] maps\_hud_util::destroyElem();
			level.dogHintElem[num] = undefined;
		}
	}

	level.dogHintElem[num] = maps\_hud_util::createFontString( "default", 2, player );
	level.dogHintElem[num].color = (1,1,1);
	level.dogHintElem[num] setText( &"SCRIPT_PLATFORM_DOG_HINT" );
	level.dogHintElem[num].x = 0;
	level.dogHintElem[num].y = 20;
	level.dogHintElem[num].alignX = "center";
	level.dogHintElem[num].alignY = "middle";
	level.dogHintElem[num].horzAlign = "center";
	level.dogHintElem[num].vertAlign = "middle";
	level.dogHintElem[num].foreground = true;
	level.dogHintElem[num].alpha = 1;
	level.dogHintElem[num] endon ( "death" );

	wait ( press_time );
	thread dog_hint_fade(player);
}

dog_hint_fade(player)
{
	level notify ( "clearing_dog_hint" );
	
	num = player GetEntityNumber();
	
	if ( isDefined( level.dogHintElem[num] ) )
	{
		level.dogHintElem[num] maps\_hud_util::destroyElem();
		level.dogHintElem[num] = undefined;
	}
}

dog_melee_death(player)
{
	self endon( "killanimscript" );
	self endon( "dog_no_longer_melee_able" );
	pressed = false;

	// change this number for difficulty level:
	press_time = anim.dog_presstime;
	
	
	self waittill( "dog_early_notetrack" );


////////////////////////////////////////////////////////////////////
//DK - DA wants button mashing here	
////////////////////////////////////////////////////////////////////
//	while ( player player_attacked() )
//	{
//		// wait until the player lets go of the button, if he's holding it
//		wait( 0.05 );
//	}
////////////////////////////////////////////////////////////////////
	
	
	for ( ;; )
	{
		if ( !pressed )
		{
			/#
			if ( isdefined( level.dog_free_kill ) && isdefined( self.melee_able_timer ) && gettime() - self.melee_able_timer <= press_time )
			{
				player.player_view.custom_dog_save = "neck_snap";
				self notify( "melee_stop" );
				self setflaggedanimknobrestart( "dog_death_anim", %german_shepherd_player_neck_snap, 1, 0.2, 1 );
				
				self waittillmatch( "dog_death_anim", "dog_death" );
				self thread play_sound_in_space( "dog_neckbreak", self gettagorigin( "tag_eye" ) );
				self setcandamage( true );
				self.a.nodeath = true;
				self dodamage( self.health + 503, (0,0,0), player );
				self notify( "killanimscript" );
			}
			#/
			
			if ( player player_attacked() )
			{
				pressed = true;
				if ( isdefined( self.melee_able_timer ) && isalive( player ) )
				{
					if ( gettime() - self.melee_able_timer <= press_time )
					{
						player.player_view.custom_dog_save = "neck_snap";
						self notify( "melee_stop" );
						self setflaggedanimknobrestart( "dog_death_anim", %german_shepherd_player_neck_snap, 1, 0.2, 1 );
						
						self waittillmatch( "dog_death_anim", "dog_death" );
						self thread play_sound_in_space( "dog_neckbreak", self gettagorigin( "tag_eye" ) );
						self setcandamage( true );
						self.a.nodeath = true;
						dif = player.origin - self.origin;
						dif = ( dif[ 0 ], dif [ 1 ], 0 );
						arcademode_assignpoints( "arcademode_score_dog", player );
						self dodamage( self.health + 503, self geteye() - dif, player );
						self notify( "killanimscript" );
					}
					else
					{
						player.player_view setanimlimited( get_player_knockdown_knob(), 0.01, 0.2, 1 );
						player.player_view setanimlimited( get_player_knockdown_late_knob(), 1, 0.2, 1 );
						
						self setanimlimited( %attack_player, 0.01, 0.2, 1 );
						self setanimlimited( %attack_player_late, 1, 0.2, 1 );
						level.dog_death_quote = &"SCRIPT_PLATFORM_DOG_DEATH_TOO_LATE";
					}
					return;
				}
				
				level.dog_death_quote = &"SCRIPT_PLATFORM_DOG_DEATH_TOO_SOON";
				self setflaggedanimknobrestart( "meleeanim", %german_shepherd_player_neck_miss, 1, 0.2, 1 );
				player.player_view setflaggedanimknobrestart( "viewanim", get_player_dog_neck_miss_anim(), 1, 0.2, 1 );

				// once player clicks, if it is at the wrong time, he does not get another chance.
				return;
			}
		}
		else
		{
			if ( !player player_attacked() )
			{
				pressed = false;
			}
		}

		wait( 0.05 );
	}
}

//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
#using_animtree ("generic_human");

meleeStruggleVsDog()
{
	self endon( "killanimscript" );
	self endon( "death" );
	self endon( "end_melee_struggle" );
	self endon( "end_melee_all" );
	
	if ( !isdefined( self.syncedMeleeTarget ) )
		return;
		
	//self.syncedMeleeTarget = self;
	
	self OrientMode("face point", self.syncedMeleeTarget.origin, 1);
	self animMode( "gravity" );
	
	self.a.pose = "stand";
	self.a.special = "none";
	
	if ( usingSidearm() )
		self animscripts\shared::placeWeaponOn( self.primaryweapon, "right" );
	
	meleeSeqAnim = [];
	meleeSeqAnim[0] = %root;
	meleeSeqAnim[1] = %AI_attacked_german_shepherd_01_start_a;
	meleeSeqAnim[2] = %AI_attacked_german_shepherd_02_idle_a;
	if ( self.syncedMeleeTarget.meleeKillTarget )
	{
		meleeSeqAnim[3] = %AI_attacked_german_shepherd_03_push_a;
		meleeSeqAnim[4] = %AI_attacked_german_shepherd_04_middle_a;
		meleeSeqAnim[5] = %AI_attacked_german_shepherd_05_death_a;
		numMeleeStage = 5;
	}
	else
	{
		meleeSeqAnim[3] = %AI_attacked_german_shepherd_03_shoot_a;
		numMeleeStage = 3;
	}
	
	self.meleeSeq = 0;

	self thread meleeStruggleVsDog_interruptedCheck();
	
	self clearanim( meleeSeqAnim[0], 0.1 );
	
	self setflaggedanimrestart( "aianim", meleeSeqAnim[ 1 ], 1, 0.1, 1 );
	
	// this needs to happen here and not when the dog starts, because "tag_sync" won't be correct at that point
	wait 0.15; // also wait a bit before tag_sync in AI animation to settle to right spot
	self.syncedMeleeTarget linkto( self, "tag_sync", (0,0,0), (0,0,0) );

	self waittillmatch( "aianim", "end" );

	for ( self.meleeSeq = 1; self.meleeSeq < numMeleeStage;  )
	{
		self clearanim( meleeSeqAnim[self.meleeSeq], 0 );
		
		self.meleeSeq++;
		
		// if starting the pistol pull out to shoot, don't let any other dog attack me for a bit
		if ( numMeleeStage == 3 && self.meleeSeq == 3 )
			self setNextDogAttackAllowTime( getAnimLength( meleeSeqAnim[ self.meleeSeq ] ) * 1000 - 1000 );
		
		self setflaggedanimrestart( "aianim", meleeSeqAnim[self.meleeSeq], 1, 0, 1 );
		self animscripts\shared::DoNoteTracks( "aianim" );
		
		// hack to let %AI_attacked_german_shepherd_03_push_a play to end when interrupted
		if ( !isdefined( self.syncedMeleeTarget ) || !isAlive( self.syncedMeleeTarget ) )	
		{
			if ( self.meleeSeq == 3 && numMeleeStage == 5 )
			{
				meleeSeqAnim[4] = %AI_attacked_german_shepherd_04_getup_a;
				numMeleeStage = 4;
			}
		}
		
		if ( self.meleeSeq == 5 )
		{	
			if ( !isdefined( self.magic_bullet_shield ) )
			{
				self.a.nodeath = true;
				self animscripts\shared::DropAllAIWeapons();
				self dodamage( self.health * 10, (0, 0, 0) );
			}
		}
	}
	
	meleeStruggleVsDog_End();
}


// check for premature termination from dog being shot by another AI or player
meleeStruggleVsDog_interruptedCheck()
{
	self endon( "killanimscript" );
	self endon( "death" );
	self endon( "end_melee_all" );

	meleeSeqAnim = [];
	meleeSeqAnim[1] = %AI_attacked_german_shepherd_02_getup_a;
	meleeSeqAnim[2] = %AI_attacked_german_shepherd_02_getup_a;

	if ( self.syncedMeleeTarget.meleeKillTarget )
	{
		// meleeSeqAnim[3] = %AI_attacked_german_shepherd_04_getup_a;	// handle this in meleeStruggleVsDog()
		meleeSeqAnim[4] = %AI_attacked_german_shepherd_04_getup_a;
	}

	while ( 1 )
	{
		if ( !isdefined( self.syncedMeleeTarget ) || !isAlive( self.syncedMeleeTarget ) )
			break;
			
		wait 0.1;
	}

	if ( self.meleeSeq > 0 ) 
	{
		if ( !isdefined( meleeSeqAnim[self.meleeSeq] ) )
			return;	// don't call meleeStruggleVsDog_End()

		self clearanim( %melee_dog, 0.1 );
		self setflaggedanimrestart( "getupanim", meleeSeqAnim[self.meleeSeq], 1, 0.1, 1 );
		self animscripts\shared::DoNoteTracks( "getupanim" );
	}
	
	meleeStruggleVsDog_End();
}


// this should kill both meleeStruggleVsDog() and meleeStruggleVsDog_endCheck() threads
meleeStruggleVsDog_End()
{
	self orientmode("face default");
	self.syncedMeleeTarget = undefined;
	self.meleeSeq = undefined;
	self.allowPain = true;
	self setNextDogAttackAllowTime( 1000 );
	
	self notify( "end_melee_all" );
}


//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
#using_animtree( "player" );

PlayerView_Spawn(player)
{
	playerView = spawn( "script_model", player.origin );
	playerView.angles = player.angles;
	playerView setModel( level.player_interactive_hands );
	playerView useAnimTree( #animtree );
	playerView hide();
	
	return playerView;
}


handlePlayerKnockDownNotetracks( note )
{
	switch( note )
	{
		case "allow_player_save":
		{
			if ( getdvar( "friendlySaveFromDog" ) == "1" && isdefined( self.dog ) )
			{
				wait 1;
				self.dog setcandamage( true );
			}
		}
		break;
		
		case "blood_pool":
{
			tagPos = self gettagorigin( "tag_torso" );	// rough tag to play fx on
			tagAngles = self gettagangles( "tag_torso" );
			forward = anglestoforward( tagAngles );
			up = anglestoup( tagAngles );
			right = anglestoright( tagAngles );
		
			tagPos = tagPos + vectorScale( forward, -8.5 ) + vectorScale( up, 5 ) + vectorScale( right, 0 );
			playfx( level._effect[ "deathfx_bloodpool" ], tagPos, forward, up );	// Add to level initialization animscripts\dog_init::initDogAnimations();
		}
		break;
	}
}


PlayerView_KnockDownAnim( dog, player )
{
	self endon( "pvd_melee_interrupted" );
	
	self.dog = dog;
	self thread PlayerView_CheckInterrupted(player);
	
	self setflaggedanimrestart( "viewanim", %player_view_dog_knockdown );
	self setflaggedanimrestart( "viewanim", %player_view_dog_knockdown_late );

	self setanimlimited( get_player_knockdown_knob(), 1, 0, 1 );
	self setanimlimited( get_player_knockdown_late_knob(), 0.01, 0, 1 );
	
	self animscripts\shared::DoNoteTracks( "viewanim", ::handlePlayerKnockDownNotetracks );
//	self animscripts\shared::DoNoteTracks( "viewanim_late", ::handlePlayerKnockDownNotetracks );
	
	self dontInterpolate();
	
	self.dog = undefined;
	PlayerView_EndSequence(player);
	self notify( "pvd_melee_done" );
}


PlayerView_CheckInterrupted(player)
{
	self endon( "pvd_melee_done" );
	
	self.dog waittill_any( "death", "pain", "melee_stop" );

	if ( !isdefined( player.specialDeath ) && isAlive( player ) )
	{
		self notify( "pvd_melee_interrupted" );
		self.dog notify( "pvd_melee_interrupted" );
		PlayerView_EndSequence(player);
	}
}

//showafter( time )
//{
//	self endon( "death" );
//	wait time;
//	self show();
//}

PlayerView_StartSequence( dog, player )
{
	if ( isdefined( self.inSeq ) )
		return false;

	player notify( "dog_attacks_player" );
	self.inSeq = true;
	
	if ( isalive( player ) )
		setsaveddvar( "hud_drawhud", 0 );
	
	player allowStand( true );
	player setstance( "stand" );
	player.syncedMeleeTarget = dog;
	player.player_view PlayerView_Show(player);
	//player.player_view hide();
	//player.player_view thread showafter( 1.3 );

	
	direction = dog.origin - player.origin;
	self.angles = vectortoangles( direction );
	self.angles = ( 0, self.angles[1], 0 );	

	playerpos = player.origin;
	self.origin = playerphysicstrace( ( playerpos[ 0 ], playerpos[ 1 ], playerpos[ 2 ] + 50 ), ( playerpos[ 0 ], playerpos[ 1 ], playerpos[ 2 ] - 200 ) );
	
	self thread PlayerView_KnockDownAnim( dog, player );
	self dontInterpolate();
	
	player playerLinkToAbsolute( self, "tag_player" );
	dog linkto( self, "tag_sync", (0, 0, 0), (0, 0, 0) );
	
	syncTagAngles = self gettagangles( "tag_sync" );
	dog orientmode( "face angle", syncTagAngles[ 1 ] );
	dog orientmode( "face default" );
	
	//self thread draw_tag( "tag_player" );
	//self thread draw_tag( "tag_sync" );
	//dog thread draw_tag( "tag_origin" );

	player allowLean( false );
	player allowCrouch( false );
	player allowProne( false );
	player freezeControls( true );
	
	player setcandamage( false );
	
	return true;
}

SavedNotify(player)
{
	wait 0.5;
	player playsound( "saved_from_dog" );
}

player_gets_weapons_back(player)
{
	player endon( "death" );
	player showViewModel();
	player enableweapons();
}

PlayerView_EndSequence(player)
{
	setsaveddvar( "hud_drawhud", 1 );
	
	if ( isalive( player ) )
	{
		self clearanim( %player_view_dog_knockdown, 0.1 );
		if ( isdefined( self.custom_dog_save ) )
		{
			custom_saves = [];
			custom_saves[ "neck_snap" ] = %player_view_dog_knockdown_neck_snap;
			self setflaggedanimrestart( "viewanim", custom_saves[ self.custom_dog_save ], 1, 0.2, 1 );
		}
		else
		{
			thread SavedNotify(player);
			self setflaggedanimrestart( "viewanim", %player_view_dog_knockdown_saved );
		}
	
		delaythread( 3.0, ::player_gets_weapons_back, player );
		self animscripts\shared::DoNoteTracks( "viewanim" );
		player setcandamage( true );
		player notify( "player_saved_from_dog" );

		player unlink();
		player setOrigin( self.origin );
	
		self.inSeq = undefined;
		player.player_view delete();	// delete self
		
		angles = player getplayerangles();
		player setplayerangles( (0, angles[1], 0) );
	}
	else
	{
		setsaveddvar( "compass", 0 );
	}

	player.syncedMeleeTarget = undefined;
	
	player allowLean( true );
	player allowCrouch( true );
	player allowProne( true );
	player freezeControls( false );
	player.attacked_by_dog = undefined;
}



PlayerView_Show(player)
{
	self show();
	player hideViewModel();
	player disableweapons();
}


//draw_tag( tagname )
//{
//	self endon( "death" );
//	while( 1 )
//	{
//		self thread draw_tag_for_time( tagname, 6 );
//		wait 0.05;
//	}
//}
//
//draw_tag_for_time( tagname, drawTime )
//{
//	self endon( "death" );
//	
//	range = 25;
//
//	angles = self gettagangles( tagname );	
//	origin = self gettagorigin( tagname );
//	
//	forward = anglestoforward(angles);
//	forward = vectorscale(forward, range + 50);
//	right = anglestoright(angles);
//	right = vectorscale(right, range);
//	up = anglestoup(angles);
//	up = vectorscale(up, range);
//
//	time = 0;
//	while( time < drawTime )
//	{
//		line(origin, origin + forward, (1,0,0), 1);
//		line(origin, origin + up, (0,1,0), 1);
//		line(origin, origin + right, (0,0,1), 1);
//		wait 0.05;
//		time += 0.05;
//	}
//}


get_player_dog_neck_miss_anim()
{
	return %player_view_dog_knockdown_neck_miss;
}

get_player_view_dog_knock_down_anim()
{
	return %player_view_dog_knockdown;
}

get_player_view_dog_knock_down_late_anim()
{
	return %player_view_dog_knockdown_late;
}

get_player_knockdown_knob()
{
	return %knockdown;
}

get_player_knockdown_late_knob()
{
	return %knockdown_late;
}

dog_init.gsc

#using_animtree ("dog");

main()
{
	self useAnimTree( #animtree );
	
	initDogAnimations();
	animscripts\init::firstInit();
	
	self.ignoreSuppression = true;
	
	self.chatInitialized = false;
	self.noDodgeMove = true;
	self.root_anim = %root;

	self.meleeAttackDist = 0;
	self thread setMeleeAttackDist();

	self.a = spawnStruct();
	self.a.pose = "stand";					// to use DoNoteTracks()
	self.a.nextStandingHitDying = false;	// to allow dogs to use bullet shield
	self.a.movement = "run";

	animscripts\init::set_anim_playback_rate();

	self.suppressionThreshold = 1;
	self.disableArrivals = false;
	self.stopAnimDistSq = anim.dogStoppingDistSq;

	// MikeD (1/25/2008): We do not have Flash Grenades in CoD5, maybe we can use this technique for smoke?
	//self thread animscripts\combat_utility::monitorFlash();

	self.pathEnemyFightDist = 512;
	self setTalkToSpecies( "dog" );

	self.health = int( anim.dog_health * self.health );
}


setMeleeAttackDist()
{
	self endon( "death" );

	while ( 1 )
	{
		if ( isdefined( self.enemy ) && isplayer(self.enemy) )
			self.meleeAttackDist = anim.dogAttackPlayerDist;
		else
			self.meleeAttackDist = anim.dogAttackAIDist;

		self waittill( "enemy" );
	}
}


initDogAnimations()
{
	if ( !isdefined( level.dogsInitialized ) )
	{
		level.dogsInitialized = true;
		precachestring( &"SCRIPT_PLATFORM_DOG_DEATH_DO_NOTHING" );
		precachestring( &"SCRIPT_PLATFORM_DOG_DEATH_TOO_LATE" );
		precachestring( &"SCRIPT_PLATFORM_DOG_DEATH_TOO_SOON" );
		precachestring( &"SCRIPT_PLATFORM_DOG_HINT" );		
	}
	
	// Initialization that should happen once per level
	if ( isDefined (anim.NotFirstTimeDogs) ) // Use this to trigger the first init
		return;

	precacheShader( "hud_dog_melee"	);
	anim.NotFirstTimeDogs = 1;
	anim.dogStoppingDistSq = lengthSquared( getmovedelta( %german_shepherd_run_stop, 0, 1 ) * 1.2 ) ;
	anim.dogStartMoveDist = length( getmovedelta( %german_shepherd_run_start, 0, 1 ) );
	
	//notetime = getNotetrackTimes( %german_shepherd_attack_player, "dog_melee" );
	//anim.dogAttackPlayerDist = length( getmovedelta( %german_shepherd_attack_player, 0, notetime[0] ) );
	anim.dogAttackPlayerDist = 102; // hard code for now, above is not accurate.

	offset = getstartorigin( (0, 0, 0), (0, 0, 0), %german_shepherd_attack_AI_01_start_a );
	anim.dogAttackAIDist = length( offset );
	
	anim.dogTraverseAnims = [];
	anim.dogTraverseAnims["wallhop"]		= %german_shepherd_run_jump_40;
	anim.dogTraverseAnims[ "window_40" ]		 = %german_shepherd_run_jump_window_40;
	anim.dogTraverseAnims["jump_down_40"]	= %german_shepherd_traverse_down_40;
	anim.dogTraverseAnims["jump_up_40"]		= %german_shepherd_traverse_up_40;
	anim.dogTraverseAnims[ "jump_up_80" ]		 = %german_shepherd_traverse_up_80;

	// Dog start move animations
	// number indexes correspond to keyboard number directions
	anim.dogStartMoveAngles[8] = 0;
	anim.dogStartMoveAngles[6] = 90;
	anim.dogStartMoveAngles[4] = -90;
	anim.dogStartMoveAngles[3] = 180;
	anim.dogStartMoveAngles[1] = -180;
	
	/*
	anim.dogStartMoveAnim[8] = %german_shepherd_run_start;
	anim.dogStartMoveAnim[6] = %german_shepherd_run_start_L;
	anim.dogStartMoveAnim[4] = %german_shepherd_run_start_R;
	anim.dogStartMoveAnim[3] = %german_shepherd_run_start_180_L;
	anim.dogStartMoveAnim[1] = %german_shepherd_run_start_180_R;
	*/

	anim.dogLookPose["attackIdle"][2] = %german_shepherd_attack_look_down;
	anim.dogLookPose["attackIdle"][4] = %german_shepherd_attack_look_left;
	anim.dogLookPose["attackIdle"][6] = %german_shepherd_attack_look_right;
	anim.dogLookPose["attackIdle"][8] = %german_shepherd_attack_look_up;	

	anim.dogLookPose["normal"][2] = %german_shepherd_look_down;
	anim.dogLookPose["normal"][4] = %german_shepherd_look_left;
	anim.dogLookPose["normal"][6] = %german_shepherd_look_right;
	anim.dogLookPose["normal"][8] = %german_shepherd_look_up;

	// effects used by dog
	level._effect[ "dog_bite_blood" ] = loadfx( "impacts/fx_deathfx_bloodpool_view" );
	level._effect[ "deathfx_bloodpool" ] = loadfx( "impacts/fx_deathfx_dogbite" );
	
	// setup random timings for dogs attacking the player
	slices = 5;
	array = [];
	for ( i = 0; i <= slices; i++ )
	{
		array[ array.size ] = i / slices;
	}
	level.dog_melee_index = 0;
	level.dog_melee_timing_array = maps\_utility::array_randomize( array );
	
	level.lastDogMeleePlayerTime = 0;
	level.dogMeleePlayerCounter = 0;
	
	setdvar( "friendlySaveFromDog", "0" );
}

dog_scripted

#using_animtree( "dog" );

main()
{
	self endon( "death" );
	self notify( "killanimscript" );

	self.codeScripted[ "root" ] = %root;	// TEMP!

    self trackScriptState( "Scripted Main", "code" );
	self endon( "end_sequence" );
	self startscriptedanim( self.codeScripted[ "notifyName" ], self.codeScripted[ "origin" ], self.codeScripted[ "angles" ], self.codeScripted[ "anim" ], self.codeScripted[ "animMode" ], self.codeScripted[ "root" ]);

	self.a.script = "scripted";
	self.codeScripted = undefined;

	if ( isdefined( self.deathstring_passed ) )
		self.deathstring = self.deathstring_passed;

	self waittill( "killanimscript" );
}

init( notifyName, origin, angles, theAnim, animMode, root )
{
	self.codeScripted[ "notifyName" ] = notifyName;
	self.codeScripted[ "origin" ] = origin;
	self.codeScripted[ "angles" ] = angles;
	self.codeScripted[ "anim" ] = theAnim;
	if ( isDefined( animMode ) )
		self.codeScripted[ "animMode" ] = animMode;
	else
		self.codeScripted[ "animMode" ] = "normal";
	if ( isDefined( root ) )
		self.codeScripted[ "root" ] = root;
	else
		self.codeScripted[ "root" ] = %root;
}

shared.gsc

#include common_scripts\utility;
#include maps\animscripts\utility;
#include maps\_utility;

// no longer playing sounds on the server but we still want the returns to tell
// the script if a non-sound notify needs to be handled
HandleDogSoundNoteTracks( note )
{
	if ( note == "sound_dogstep_run_default" )
	{
		return true;
	}

	prefix = getsubstr( note, 0, 5 );

	if ( prefix != "sound" )
		return false;
		
	return true;
}

growling()
{
	return isdefined( self.script_growl );
}

HandleNoteTrack( note, flagName, customFunction, var1 )
{
/#
	if ( getdvarint( "debug_dog_notetracks" ) )
		println("dog notetrack: " + flagName + " " + note + " " + GetTime() );
#/
	
	if ( isAI( self ) && self.type == "dog" )
		if ( HandleDogSoundNoteTracks( note ) )
			return;
	
	switch ( note )
	{
	case "end":
	case "finish":
	case "undefined":
		return note;
	default:
		if (isDefined(customFunction))
		{
			if (!isdefined(var1))
			{
				return [[customFunction]] (note);
			}
			else
			{
				return [[customFunction]] (note, var1);
			}
		}
		break;
	}
}

// DoNoteTracks waits for and responds to standard noteTracks on the animation, returning when it gets an "end" or a "finish"
// For level scripts, a pointer to a custom function should be passed as the second argument, which handles notetracks not
// already handled by the generic function. This call should take the form DoNoteTracks(flagName, ::customFunction);
// The custom function will be called for each notetrack not recognized, and will pass the notetrack name. Note that this
// function could be called multiple times for a single animation.
DoNoteTracks( flagName, customFunction, var1 ) 
{
	for (;;)
	{
		self waittill (flagName, note);

		if ( !isDefined( note ) )
			note = "undefined";

		val = self HandleNoteTrack( note, flagName, customFunction, var1 );
		
		if ( isDefined( val ) )
			return val;
	}
}

DoNoteTracksForeverProc( notetracksFunc, flagName, killString, customFunction, var1 )
{
	if (isdefined (killString))
		self endon (killString);
	self endon ("killanimscript");

	for (;;)
	{
		time = GetTime();
		returnedNote = [[notetracksFunc]](flagName, customFunction, var1);
		timetaken = GetTime() - time;
		if ( timetaken < 0.05)
		{
			time = GetTime();
			returnedNote = [[notetracksFunc]](flagName, customFunction, var1);
			timetaken = GetTime() - time;
			if ( timetaken < 0.05)
			{
/#
				println (GetTime()+" animscripts\shared::DoNoteTracksForever is trying to cause an infinite loop on anim "+flagName+", returned "+returnedNote+".");
#/
				wait ( 0.05 - timetaken );
			}
		}
	}
}

// Don't call this function except as a thread you're going to kill - it lasts forever.
DoNoteTracksForever(flagName, killString, customFunction, var1 )
{
	DoNoteTracksForeverProc( ::DoNoteTracks, flagName, killString, customFunction, var1 );
}

DoNoteTracksForTimeProc( doNoteTracksForeverFunc, time, flagName, customFunction , ent, var1)
{
	ent endon ("stop_notetracks");
	[[doNoteTracksForeverFunc]](flagName, undefined, customFunction, var1);
}

// Designed for using DoNoteTracks on looping animations, so you can wait for a time instead of the "end" parameter
DoNoteTracksForTime(time, flagName, customFunction, var1)
{
	ent = spawnstruct();
	ent thread doNoteTracksForTimeEndNotify(time);
	DoNoteTracksForTimeProc( ::DoNoteTracksForever, time, flagName, customFunction, ent, var1);
}

doNoteTracksForTimeEndNotify(time)
{
	wait (time);
	self notify ("stop_notetracks");
}

trackLoop(  )
{

	players = get_players();
	deltaChangePerFrame = 5;
	
	aimBlendTime = .05;
	
	prevYawDelta = 0;
	prevPitchDelta = 0;
	maxYawDeltaChange = 5; // max change in yaw in 1 frame
	maxPitchDeltaChange = 5;
	
	pitchAdd = 0;
	yawAdd = 0;
	
	if ( self.type == "dog" )
	{
		doMaxAngleCheck = false;
		self.shootEnt = self.enemy;
	}
	else
	{
		doMaxAngleCheck = true;
	if ( self.a.script == "cover_crouch" && isdefined( self.a.coverMode ) && self.a.coverMode == "lean" )
		pitchAdd = -1 * anim.coverCrouchLeanPitch;
	if ( (self.a.script == "cover_left" || self.a.script == "cover_right") && isdefined( self.a.cornerMode ) && self.a.cornerMode == "lean" )
		yawAdd = self.coverNode.angles[1] - self.angles[1];
	}
	
	yawDelta = 0;
	pitchDelta = 0;
	
	firstFrame = true;
	
	for(;;)
	{
		incrAnimAimWeight();
		
		selfShootAtPos = (self.origin[0], self.origin[1], self getEye()[2]);

		shootPos = undefined;
		if ( isdefined( self.enemy ) )
			shootPos = self.enemy getShootAtPos();

		if ( !isdefined( shootPos ) )
		{
			yawDelta = 0;
			pitchDelta = 0;
		}
		else
		{
			vectorToShootPos = shootPos - selfShootAtPos;
			anglesToShootPos = vectorToAngles( vectorToShootPos );
			
			pitchDelta = 360 - anglesToShootPos[0];
			pitchDelta = AngleClamp180( pitchDelta + pitchAdd );
			
			yawDelta = self.angles[1] - anglesToShootPos[1];
			
			yawDelta = AngleClamp180( yawDelta + yawAdd );
		}
		
		if ( doMaxAngleCheck && ( abs( yawDelta ) > 60 || abs( pitchDelta ) > 60 ) )
		{
			yawDelta = 0;
			pitchDelta = 0;
		}
		else
		{
			if ( yawDelta > self.rightAimLimit )
				yawDelta = self.rightAimLimit;
			else if ( yawDelta < self.leftAimLimit )
				yawDelta = self.leftAimLimit;
			if ( pitchDelta > self.upAimLimit )
				pitchDelta = self.upAimLimit;
			else if ( pitchDelta < self.downAimLimit )
				pitchDelta = self.downAimLimit;
		}
		
		if ( firstFrame )
		{
			firstFrame = false;
		}
		else
		{
			yawDeltaChange = yawDelta - prevYawDelta;
			if ( abs( yawDeltaChange ) > maxYawDeltaChange )
				yawDelta = prevYawDelta + maxYawDeltaChange * sign( yawDeltaChange );
			
			pitchDeltaChange = pitchDelta - prevPitchDelta;
			if ( abs( pitchDeltaChange ) > maxPitchDeltaChange )
				pitchDelta = prevPitchDelta + maxPitchDeltaChange * sign( pitchDeltaChange );
		}
		
		prevYawDelta = yawDelta;
		prevPitchDelta = pitchDelta;
		
		updown = 0;
		leftright = 0;
		
		if ( yawDelta > 0 )
		{
			assert( yawDelta <= self.rightAimLimit );
			weight = yawDelta / self.rightAimLimit * self.a.aimweight;
			leftright = weight;
		}
		else if ( yawDelta < 0 )
		{
			assert( yawDelta >= self.leftAimLimit );
			weight = yawDelta / self.leftAimLimit * self.a.aimweight;
			leftright = -1 * weight;
		}
		
		if ( pitchDelta > 0 )
		{
			assert( pitchDelta <= self.upAimLimit );
			weight = pitchDelta / self.upAimLimit * self.a.aimweight;
			updown = weight;
		}
		else if ( pitchDelta < 0 )
		{
			assert( pitchDelta >= self.downAimLimit );
			weight = pitchDelta / self.downAimLimit * self.a.aimweight;
			updown = -1 * weight;
		}
		
		//self SetAimAnimWeights( updown, leftright );
		wait(0.05);
	}
}

//setAnimAimWeight works just like setanimlimited on an imaginary anim node that affects the four aiming directions.
setAnimAimWeight(goalweight, goaltime)
{
	if ( !isdefined( goaltime ) || goaltime <= 0 )
	{
		self.a.aimweight = goalweight;
		self.a.aimweight_start = goalweight;
		self.a.aimweight_end = goalweight;
		self.a.aimweight_transframes = 0;
	}
	else
	{
		self.a.aimweight = goalweight;
		self.a.aimweight_start = self.a.aimweight;
		self.a.aimweight_end = goalweight;
		self.a.aimweight_transframes = int(goaltime * 20);
	}
	self.a.aimweight_t = 0;
}

incrAnimAimWeight()
{
	if ( self.a.aimweight_t < self.a.aimweight_transframes )
	{
		self.a.aimweight_t++;
		t = 1.0 * self.a.aimweight_t / self.a.aimweight_transframes;
		self.a.aimweight = self.a.aimweight_start * (1 - t) + self.a.aimweight_end * t;
	}
}

dog_death


#using_animtree ("dog");

main()
{
	self endon("killanimscript");
	if ( isdefined( self.a.nodeath ) )
	{
		assertex( self.a.nodeath, "Nodeath needs to be set to true or undefined." );
		
		// allow death script to run for a bit so it doesn't turn to corpse and get deleted too soon during melee sequence
		wait 3;
		return;
	}

	self unlink();

	if ( isdefined( self.enemy ) && isdefined( self.enemy.syncedMeleeTarget ) && self.enemy.syncedMeleeTarget == self )
	{
		self.enemy.syncedMeleeTarget = undefined;
	}

	self clearanim(%root, 0.2);
	self setflaggedanimrestart("dog_anim", %german_shepherd_death_front, 1, 0.2, 1);
	self animscripts\shared::DoNoteTracks( "dog_anim" );
}

dog_move

#include maps\_utility;
#include animscripts\utility;
#include common_scripts\utility;

#using_animtree ("dog");

main()
{
	self endon("killanimscript");
	//self endon("movemode");
	
	self clearanim( %root, 0.2 );
	self clearanim(%german_shepherd_run_stop, 0);
	
	self thread randomSoundDuringRunLoop();

	if ( !isdefined( self.traverseComplete ) && !isdefined( self.skipStartMove ) && self.a.movement == "run" )
	{	
		self startMove();
		blendTime = 0;
	}
	else
	{
		blendTime = 0.2;
	}

	self.traverseComplete = undefined;
	self.skipStartMove = undefined;

	self clearanim(%german_shepherd_run_start, 0);

	if ( self.a.movement == "run" )
	{
	weights = undefined;
	weights = self getRunAnimWeights();
	
		self setanimrestart( %german_shepherd_run, weights[ "center" ], blendTime, 1 );
	self setanimrestart(%german_shepherd_run_lean_L, weights["left"], 0.1, 1);
	self setanimrestart(%german_shepherd_run_lean_R, weights["right"], 0.1, 1);
		self setflaggedanimknob( "dog_run", %german_shepherd_run_knob, 1, blendTime, self.moveplaybackrate );
	animscripts\shared::DoNoteTracksForTime(0.1, "dog_run");
	}
	else
	{
		self setflaggedanimrestart( "dog_walk", %german_shepherd_walk, 1, 0.2, self.moveplaybackrate );
	}
	
	//self thread animscripts\dog_stop::lookAtTarget( "normal" );
	
	while ( 1 )
	{	
		self moveLoop();
		
		if ( self.a.movement == "run" )
		{
			if ( self.disableArrivals == false )
		self thread stopMove();

		// if a "run" notify is received while stopping, clear stop anim and go back to moveLoop
		self waittill( "run" );
		self clearanim( %german_shepherd_run_stop, 0.1 );
	}
}
}


moveLoop()
{
	self endon( "killanimscript" );
	self endon( "stop_soon" );

	while (1)
	{
		if ( self.disableArrivals )
			self.stopAnimDistSq = 0;
		else
			self.stopAnimDistSq = anim.dogStoppingDistSq;

		if ( self.a.movement == "run" )
		{
		weights = self getRunAnimWeights();

			self clearanim( %german_shepherd_walk, 0.3 );

		self setanim(%german_shepherd_run, weights["center"], 0.2, 1);
		self setanim(%german_shepherd_run_lean_L, weights["left"], 0.2, 1);
		self setanim(%german_shepherd_run_lean_R, weights["right"], 0.2, 1);
			self setflaggedanimknob( "dog_run", %german_shepherd_run_knob, 1, 0.2, self.moveplaybackrate );
		
		animscripts\shared::DoNoteTracksForTime(0.2, "dog_run");
	}
		else
{
			assert( self.a.movement == "walk" );

			self clearanim( %german_shepherd_run_knob, 0.3 );
			self setflaggedanim( "dog_walk", %german_shepherd_walk, 1, 0.2, self.moveplaybackrate );
			animscripts\shared::DoNoteTracksForTime( 0.2, "dog_walk" );
		}
	}
}

startMoveTrackLookAhead()
{
	self endon("killanimscript");
	for ( i = 0; i < 2; i++ )
	{
		lookaheadAngle = vectortoangles( self.lookaheaddir );
		self OrientMode( "face angle", lookaheadAngle );
	}
}


startMove()
{
/*
	wait 0.05;	// wait a frame for lookaheaddir to settle
	
	endPos = self.origin;
	endPos += vectorScale( self.lookaheaddir, anim.dogStartMoveDist );
	
	tooClose = distanceSquared( self.origin, self.pathgoalpos ) < anim.dogStartMoveDist * anim.dogStartMoveDist;

	if ( !tooClose && self mayMoveToPoint( endPos ) )
	{
		lookaheadAngle = vectortoangles( self.lookaheaddir );
		angle = AngleClamp180( lookaheadAngle[ 1 ] - self.angles[ 1 ] );

		if ( angle >= 0 )
		{
			if ( angle < 45 )
				index = 8;
			else if ( angle < 135 )
				index = 6;
			else
				index = 3;
	}
		else
	{
			if ( angle > -45 )
				index = 8;
			else if ( angle > -135 )
				index = 4;
			else
				index = 1;
		}

		self setanimrestart( anim.dogStartMoveAnim[ index ], 1, 0.2, 1 );

		animEndAngle = self.angles[ 1 ] + anim.dogStartMoveAngles[ index ];
		offsetAngle = AngleClamp180( lookaheadAngle[ 1 ] - animEndAngle );
		
		self OrientMode( "face angle", self.angles[ 1 ] + offsetAngle );
		self animMode( "gravity" );
	}
	else
*/	
	{
		// just use code movement
		self setanimrestart( %german_shepherd_run_start, 1, 0.2, 1 );
	}

	self setflaggedanimknobrestart( "dog_prerun", %german_shepherd_run_start_knob, 1, 0.2, self.moveplaybackrate );
	
	self animscripts\shared::DoNoteTracks( "dog_prerun" );
	
	self animMode( "none" );
	self OrientMode( "face motion" );
}

		
stopMove()
{
	self endon( "killanimscript" );
	self endon( "run" );

	self clearanim( %german_shepherd_run_knob, 0.1 );
	self setflaggedanimrestart( "stop_anim", %german_shepherd_run_stop, 1, 0.2, 1 );
	self animscripts\shared::DoNoteTracks( "stop_anim" );
	}

	
randomSoundDuringRunLoop()
{
	self endon( "killanimscript" );
	while ( 1 )
	{
/#		
		if ( getdebugdvar( "debug_dog_sound" ) != "" )
			iprintln( "dog " + (self getentnum()) + " bark start " + getTime() );
#/
		if ( isdefined( self.script_growl ) )
			self play_sound_on_tag( "anml_dog_growl", "tag_eye" );
		else
			self play_sound_on_tag( "anml_dog_bark", "tag_eye" );
			
/#		
		if ( getdebugdvar( "debug_dog_sound" ) != "" )
			iprintln( "dog " + (self getentnum()) + " bark end " + getTime() );
#/
			
		wait( randomfloatrange( 0.1, 0.3 ) );
	}
}


getRunAnimWeights()
{
	weights = [];
	weights["center"] = 0;
	weights["left"] = 0;
	weights["right"] = 0;
	
	if ( self.leanAmount > 0 )
	{
		if ( self.leanAmount < 0.95 )
			self.leanAmount	= 0.95;

		weights["left"] = 0;
		weights["right"] = (1 - self.leanAmount) * 20;

		if ( weights["right"] > 1 )
			weights["right"] = 1;	
		else if ( weights["right"] < 0 )
			weights["right"] = 0;	
			
		weights["center"] = 1 - weights["right"];
	}
	else if ( self.leanAmount < 0 )
	{
		if ( self.leanAmount > -0.95 )
			self.leanAmount	= -0.95;

		weights["right"] = 0;
		weights["left"] = (1 + self.leanAmount) * 20;

		if ( weights["left"] > 1 )
			weights["left"] = 1;
		if ( weights["left"] < 0 )
			weights["left"] = 0;		

		weights["center"] = 1 - weights["left"];
	}
	else
	{
		weights["left"] = 0;
		weights["right"] = 0;
		weights["center"] = 1;		
	}
	
	return weights;
}

[edit] Zone_Source

The required zone source properties for yourmapname.csv are:

// dog assets
include,common_dogs
// moredogs
fx,impacts/fx_deathfx_dogbite
material,hud_dog_melee
// if using the custom soundalias
sound,dog,dog_test,all_sp
sound,dog,dog_test,all_sp 

This line calls on the csv file from earlier "dog.csv" and is calling all sounds under the name dog_test (already done for you)inside of the .csv.

[edit] Common Errors

Dogs don't move - Check you are compiling with "Paths" ticked.

Dogs don't Spawn with spawner ticked - UNKOWN for some reason dogs don't like trigger_spawn

Game Crashes When Dog incapacitates player - Check your zone source has the fx and material as stated above.

Dogs don't move when compiling paths - Check your nav layout. Watch for path node errors in compile log.

[edit] Tutorials and Other Links

SP Dog Tutorial

Personal tools