Pushed raw files

This commit is contained in:
RezTech committed 2017-07-08 14:47:21 -04:00
1 parent 22d0d4d39f
commit 79f967ecc9
5854 files changed
+2989978

No files matched your search

File diff suppressed because it is too large. Load diff
+25
View File
@@ -0,0 +1,25 @@
#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" );
}
+31
View File
@@ -0,0 +1,31 @@
#include animscripts\Combat_utility;
#include maps\_utility;
#using_animtree( "dog" );
main()
{
self endon( "killanimscript" );
self endon( "stop_flashbang_effect" );
wait randomfloatrange( 0, 0.4 );
self clearanim( %root, 0.1 );
duration = self flashBangGetTimeLeftSec();
if ( duration > 2 && randomint( 100 ) > 60 )
self setflaggedanimrestart( "flashed_anim", %german_shepherd_run_pain, 1, 0.2, self.animplaybackrate * 0.75 );
else
self setflaggedanimrestart( "flashed_anim", %german_shepherd_run_flashbang, 1, 0.2, self.animplaybackrate );
animLength = getanimlength( %german_shepherd_run_flashbang ) * self.animplaybackrate;
if ( duration < animLength )
self animscripts\shared::DoNoteTracksForTime( duration, "flashed_anim" );
else
self animscripts\shared::DoNoteTracks( "flashed_anim" );
self.flashed = false;
self notify( "stop_flashbang_effect" );
}
+141
View File
@@ -0,0 +1,141 @@
#using_animtree( "dog" );
main()
{
self useAnimTree( #animtree );
initDogAnimations();
animscripts\init::firstInit();
self.ignoreSuppression = true;
self.newEnemyReactionDistSq = 0;
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;
self.useChokePoints = false;
self.turnRate = 0.2;
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;
// [{+melee}] Melee the dog right when it bites to grab its throat.
precachestring( &"SCRIPT_PLATFORM_DOG_DEATH_DO_NOTHING" );
// [{+melee}] Melee the dog before it bites to grab its throat.
precachestring( &"SCRIPT_PLATFORM_DOG_DEATH_TOO_LATE" );
// [{+melee}] Wait for the dog to bite to grab its throat.
precachestring( &"SCRIPT_PLATFORM_DOG_DEATH_TOO_SOON" );
// [{+melee}]
precachestring( &"SCRIPT_PLATFORM_DOG_HINT" );
// Watch for the ^3[{+melee}]^7 hint to grab a dog.
precachestring( &"NEW_DOG_DEATH_DO_NOTHING_ALT" );
// Too late. Watch for the ^3[{+melee}]^7 hint to grab a dog.
precachestring( &"NEW_DOG_DEATH_TOO_LATE_ALT" );
// Too early. Wait for the ^3[{+melee}]^7 hint to grab a dog.
precachestring( &"NEW_DOG_DEATH_TOO_SOON_ALT" );
}
// 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/deathfx_dogbite" );
level._effect[ "deathfx_bloodpool" ] = loadfx( "impacts/deathfx_bloodpool_view" );
// 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 );
setdvar( "friendlySaveFromDog", "0" );
}
+392
View File
@@ -0,0 +1,392 @@
#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 );
if ( !isdefined( self.traverseComplete ) && !isdefined( self.skipStartMove ) && self.a.movement == "run" && ( !isdefined( self.disableExits ) || self.disableExits == false ) )
self startMove();
self thread randomSoundDuringRunLoop();
self.traverseComplete = undefined;
self.skipStartMove = undefined;
if ( self.a.movement == "run" )
{
weights = undefined;
weights = self getRunAnimWeights();
self setanimrestart( %german_shepherd_run, weights[ "center" ], 0.2, 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, 0.2, 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\dog_stop::lookAtTarget( "normal" );
self thread pathChangeCheck();
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" );
self.moveLoopCleanupFunc = undefined;
while ( 1 )
{
if ( self.disableArrivals )
self.stopAnimDistSq = 0;
else
self.stopAnimDistSq = anim.dogStoppingDistSq;
if ( isDefined( self.moveLoopCleanupFunc ) )
{
self [[self.moveLoopCleanupFunc]]();
self.moveLoopCleanupFunc = undefined;
}
if ( isdefined( self.moveLoopOverrideFunc ) )
self [[ self.moveLoopOverrideFunc ]]();
else
self moveLoopStep();
}
}
moveLoopStep()
{
self endon( "move_loop_restart" );
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" );
}
}
pathChangeCheck()
{
self endon( "killanimscript" );
self.ignorePathChange = undefined; // this will be turned on / off in other threads at appropriate times
while ( 1 )
{
// no other thread should end on "path_changed"
self waittill( "path_changed", doingReacquire, newDir );
// no need to check for doingReacquire since faceMotion should be a good check
if ( isdefined( self.ignorePathChange ) || isdefined( self.noTurnAnims ) )
continue;
if ( self.a.movement != "run" )
continue;
angleDiff = AngleClamp180( self.angles[ 1 ] - vectortoyaw( newDir ) );
turnAnim = pathChange_getDogTurnAnim( angleDiff );
if ( isdefined( turnAnim ) )
{
self.turnAnim = turnAnim;
self.turnTime = getTime();
self.moveLoopOverrideFunc = ::pathChange_doDogTurnAnim;
self notify( "move_loop_restart" );
}
}
}
pathChange_getDogTurnAnim( angleDiff )
{
turnAnim = undefined;
if ( angleDiff < -135 )
{
turnAnim = %german_shepherd_run_start_180_L;
}
else if ( angleDiff > 135 )
{
turnAnim = %german_shepherd_run_start_180_R;
}
return turnAnim;
}
pathChange_doDogTurnAnim()
{
self endon( "killanimscript" );
self.moveLoopOverrideFunc = undefined;
turnAnim = self.turnAnim;
if ( gettime() > self.turnTime + 50 )
return; // too late
self animMode( "zonly_physics", false );
self clearanim( %root, 0.2 );
self.moveLoopCleanupFunc = ::pathChange_cleanupDogTurnAnim;
self.ignorePathChange = true;
self setflaggedanimrestart( "turnAnim", turnAnim, 1, 0.2, self.movePlaybackRate );
self OrientMode( "face current" );
// code move at 60%
playTime = getanimlength( turnAnim ) * self.movePlaybackRate;
self animscripts\shared::DoNoteTracksForTime( playTime * 0.60, "turnAnim" );
self.ignorePathChange = undefined;
self OrientMode( "face motion" ); // want to face motion, don't do l / r / b anims
self animmode( "none", false );
// cut off at 85%
self animscripts\shared::DoNoteTracksForTime( playTime * 0.25, "turnAnim" );
}
pathChange_cleanupDogTurnAnim()
{
self.ignorePathChange = undefined;
self OrientMode( "face default" );
self clearanim( %root, 0.2 );
self animMode( "none", false );
}
startMoveTrackLookAhead()
{
self endon( "killanimscript" );
for ( i = 0; i < 2; i++ )
{
lookaheadAngle = vectortoangles( self.lookaheaddir );
self OrientMode( "face angle", lookaheadAngle );
}
}
playMoveStartAnim()
{
endPos = self.origin;
endPos += vector_multiply( 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( "zonly_physics", false );
// code move at 60%
playTime = getanimlength( anim.dogStartMoveAnim[ index ] ) * self.movePlaybackRate;
self animscripts\shared::DoNoteTracksForTime( playTime * 0.60, "turnAnim" );
self OrientMode( "face motion" ); // want to face motion, don't do l / r / b anims
self animmode( "none", false );
// cut off at 85%
self animscripts\shared::DoNoteTracksForTime( playTime * 0.25, "turnAnim" );
self clearanim( %root, 0.2 );
}
}
startMove()
{
if ( isdefined( self.pathgoalpos ) )
{
wait 0.05; // wait for lookaheaddir to settle
if ( isdefined( self.pathgoalpos ) )
{
self playMoveStartAnim();
return;
}
}
// just use code movement
self OrientMode( "face default" );
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", false );
self clearanim( %root, 0.2 );
}
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" );
}
dogPlaySoundAndNotify( sound, notifyStr )
{
self play_sound_on_tag_endon_death( sound, "tag_eye" );
if ( isalive( self ) )
self notify( notifyStr );
}
randomSoundDuringRunLoop()
{
self endon( "killanimscript" );
wait 0.2; // incase move script gets killed right away
while ( 1 )
{
/#
if ( getdebugdvar( "debug_dog_sound" ) != "" )
iprintln( "dog " + ( self getentnum() ) + " bark start " + getTime() );
#/
sound = undefined;
if ( isdefined( self.script_growl ) )
sound = "anml_dog_growl";
else if ( !isdefined( self.script_nobark ) )
sound = "anml_dog_bark";
if ( !isdefined( sound ) )
break;
self thread dogPlaySoundAndNotify( sound, "randomRunSound" );
self waittill( "randomRunSound" );
/#
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;
}
+17
View File
@@ -0,0 +1,17 @@
#using_animtree( "dog" );
main()
{
self endon( "killanimscript" );
if ( isdefined( self.enemy ) && isdefined( self.enemy.syncedMeleeTarget ) && self.enemy.syncedMeleeTarget == self )
{
self unlink();
self.enemy.syncedMeleeTarget = undefined;
}
self clearanim( %root, 0.2 );
self setflaggedanimrestart( "dog_pain_anim", %german_shepherd_run_pain, 1, 0.2, 1 );
self animscripts\shared::DoNoteTracks( "dog_pain_anim" );
}
+35
View File
@@ -0,0 +1,35 @@
#using_animtree( "dog" );
main()
{
self endon( "death" );
self notify( "killanimscript" );
self.codeScripted[ "root" ] = %root; // TEMP!
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.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;
}
+130
View File
@@ -0,0 +1,130 @@
#using_animtree( "dog" );
main()
{
self endon( "killanimscript" );
self clearanim( %root, 0.2 );
self clearanim( %german_shepherd_idle, 0.2 );
self clearanim( %german_shepherd_attackidle_knob, 0.2 );
self thread lookAtTarget( "attackIdle" );
while ( 1 )
{
if ( shouldAttackIdle() )
{
self clearanim( %german_shepherd_idle, 0.2 );
self randomAttackIdle();
}
else
{
self orientmode( "face current" );
self clearanim( %german_shepherd_attackidle_knob, 0.2 );
self setflaggedanimrestart( "dog_idle", %german_shepherd_idle, 1, 0.2, self.animplaybackrate );
}
animscripts\shared::DoNoteTracks( "dog_idle" );
}
}
isFacingEnemy( toleranceCosAngle )
{
assert( isdefined( self.enemy ) );
vecToEnemy = self.enemy.origin - self.origin;
distToEnemy = length( vecToEnemy );
if ( distToEnemy < 1 )
return true;
forward = anglesToForward( self.angles );
return( ( forward[ 0 ] * vecToEnemy[ 0 ] ) + ( forward[ 1 ] * vecToEnemy[ 1 ] ) ) / distToEnemy > toleranceCosAngle;
}
randomAttackIdle()
{
if ( isFacingEnemy( -0.5 ) ) // cos120
self orientmode( "face current" );
else
self orientmode( "face enemy" );
self clearanim( %german_shepherd_attackidle_knob, 0.1 );
if ( should_growl() )
{
// just growl
self setflaggedanimrestart( "dog_idle", %german_shepherd_attackidle_growl, 1, 0.2, 1 );
return;
}
idleChance = 33;
barkChance = 66;
if ( isdefined( self.mode ) )
{
if ( self.mode == "growl" )
{
idleChance = 15;
barkChance = 30;
}
else if ( self.mode == "bark" )
{
idleChance = 15;
barkChance = 85;
}
}
rand = randomInt( 100 );
if ( rand < idleChance )
self setflaggedanimrestart( "dog_idle", %german_shepherd_attackidle, 1, 0.2, self.animplaybackrate );
else if ( rand < barkChance )
self setflaggedanimrestart( "dog_idle", %german_shepherd_attackidle_bark, 1, 0.2, self.animplaybackrate );
else
self setflaggedanimrestart( "dog_idle", %german_shepherd_attackidle_growl, 1, 0.2, self.animplaybackrate );
}
shouldAttackIdle()
{
return( isdefined( self.enemy ) && isalive( self.enemy ) && distanceSquared( self.origin, self.enemy.origin ) < 1000000 );
}
should_growl()
{
if ( isdefined( self.script_growl ) )
return true;
if ( !isalive( self.enemy ) )
return true;
return !( self cansee( self.enemy ) );
}
lookAtTarget( lookPoseSet )
{
self endon( "killanimscript" );
self endon( "stop tracking" );
self clearanim( %german_shepherd_look_2, 0 );
self clearanim( %german_shepherd_look_4, 0 );
self clearanim( %german_shepherd_look_6, 0 );
self clearanim( %german_shepherd_look_8, 0 );
self setDefaultAimLimits();
self.rightAimLimit = 90;
self.leftAimLimit = -90;
self setanimlimited( anim.dogLookPose[ lookPoseSet ][ 2 ], 1, 0 );
self setanimlimited( anim.dogLookPose[ lookPoseSet ][ 4 ], 1, 0 );
self setanimlimited( anim.dogLookPose[ lookPoseSet ][ 6 ], 1, 0 );
self setanimlimited( anim.dogLookPose[ lookPoseSet ][ 8 ], 1, 0 );
self animscripts\shared::setAnimAimWeight( 1, 0.2 );
/#
assert( !isdefined( self.trackLoopThread ) );
self.trackLoopThread = thisthread;
self.trackLoopThreadType = "lookAtTarget";
#/
self animscripts\shared::trackLoop( %german_shepherd_look_2, %german_shepherd_look_4, %german_shepherd_look_6, %german_shepherd_look_8 );
}