IW4-Dump-Files/animscripts/dog/dog_combat.gsc

1478 lines
36 KiB
Plaintext

#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();
else
self meleeStruggleVsAI();
}
killplayer()
{
self endon( "pvd_melee_interrupted" );
if ( !isdefined( self.meleeingPlayer.player_view ) )
return;
player_view = self.meleeingPlayer.player_view;
if ( isdefined( player_view.player_killed ) )
return;
player_view.player_killed = true;
if ( is_coop() && !is_other_player_downed( self.meleeingPlayer ) )
{
knock_down_player_coop( self.meleeingPlayer, self );
return;
}
self.meleeingPlayer.specialDeath = true;
self.meleeingPlayer setcandamage( true );
PlayFXOnTag( level._effect[ "dog_bite_blood" ], player_view, "tag_torso" );
wait 1;
self.meleeingPlayer enableHealthShield( false );
if ( !isalive( self.meleeingPlayer ) )
return;
self.meleeingPlayer dog_player_kill( self );
self.meleeingPlayer 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( self.meleeingPlayer );
}
knock_down_player_coop( player, dog )
{
player.coop_downed = true;
rate = dog_vs_player_anim_rate();
self setflaggedanimknobrestart( "meleeanim", %german_shepherd_player_getoff, 1, 0.1, rate );
assert( isdefined( player.player_view ) );
player.player_view notify( "pvd_melee_interrupted" );
player.player_view notify( "pvd_melee_done" );
player.player_view PlayerView_EndSequence( player );
if ( is_coop() && is_other_player_downed( player ) )
{
// if the other player died during the "dog leaves" anim then die.
player dog_player_kill( dog );
}
}
dog_player_kill( killer )
{
if ( is_coop() )
self enableDeathShield( false );
self DisableInvulnerability();
if ( isalive( killer ) )
self kill( self.origin, killer );
else
self kill( self.origin );
}
dog_death_hud( player )
{
// SO does death hints differently from SP.
if ( is_specialop() )
return;
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 = player maps\_hud_util::createClientFontString( "default", 1.75 );
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 );
missAnim = %german_shepherd_attack_player_miss;
if ( isdefined( self.enemy ) )
{
forward = anglestoforward( ( 0, self.desiredAngle, 0 ) );
dirToEnemy = vectorNormalize( self.enemy.origin - self.origin );
landPosToEnemy = self.enemy.origin - ( self.origin + vector_multiply( forward, 40 ) );
if ( vectordot( dirToEnemy, forward ) > 0.707 || vectordot( landPosToEnemy, forward ) > 0 )
{
self thread animscripts\dog\dog_stop::lookAtTarget( "normal" );
}
else
{
self.skipStartMove = true;
self thread attackMissTrackTargetThread();
if ( ( dirToEnemy[ 0 ] * forward[ 1 ] - dirToEnemy[ 1 ] * forward[ 0 ] ) > 0 )
missAnim = %german_shepherd_attack_player_miss_turnR;
else
missAnim = %german_shepherd_attack_player_miss_turnL;
}
}
self setflaggedanimrestart( "miss_anim", missAnim, 1, 0, 1 );
animLength = getAnimLength( missAnim );
self animscripts\shared::DoNoteTracksForTime( animLength - 0.1, "miss_anim" );
self notify( "stop tracking" );
}
attackMissTrackTargetThread()
{
self endon( "killanimscript" );
wait 0.6;
self OrientMode( "face enemy" );
}
KnockOutOfADS( player )
{
player endon( "death" );
player AllowAds( false );
wait 0.75;
player AllowAds( true );
}
dogMelee()
{
/#
if ( isgodmode( self.meleeingPlayer ) )
{
println( "Player in god mode, aborting dog attack" );
return;
}
#/
if ( isdefined( self.meleeingPlayer ) )
{
if ( self.meleeingPlayer isLinked() )
return undefined;
if ( self.meleeingPlayer isMantling() )
return undefined;
if ( self.meleeingPlayer.lastStand && self.meleeingPlayer.ignoreMe )
return undefined;
}
if ( isdefined( self.enemy ) )
{
if ( distance2D( self.origin, self.enemy.origin ) < 32 )
return self melee();
}
return self melee( anglesToForward( self.angles ) );
}
handleMeleeBiteAttackNoteTracks( note )
{
switch( note )
{
case "dog_melee":
{
hitEnt = self dogMelee();
if ( isdefined( hitEnt ) )
{
if ( isplayer( hitEnt ) )
{
hitEnt shellshock( "dog_bite", 1 );
thread KnockOutOfADS( hitEnt );
}
}
else
{
attackMiss();
return true;
}
}
break;
case "stop_tracking":
self OrientMode( "face current" );
break;
}
}
// to prevent the player from dying from the intial dog jump bite attack
addSafetyHealth()
{
healthFrac = self.meleeingPlayer getnormalhealth();
if ( healthFrac == 0 )
return false;
if ( healthFrac < 0.25 )
{
self.meleeingPlayer setnormalhealth( healthFrac + 0.25 );
return true;
}
return false;
}
removeSafetyHealth()
{
healthFrac = self.meleeingPlayer getnormalhealth();
if ( healthFrac > 0.25 )
self.meleeingPlayer setnormalhealth( healthFrac - 0.25 );
else
self.meleeingPlayer setnormalhealth( 0.01 );
}
handleMeleeFinishAttackNoteTracks( note )
{
switch( note )
{
case "dog_melee":
healthAdded = addSafetyHealth();
hitEnt = self dogMelee();
if ( isdefined( hitEnt ) && isplayer( hitEnt ) && isalive( self.meleeingPlayer ) )
{
if ( healthAdded )
removeSafetyHealth();
self.skipStartMove = undefined;
assert( !isdefined( self.meleeingPlayer.player_view ) );
self.meleeingPlayer.player_view = PlayerView_Spawn( self );
if ( self.meleeingPlayer.player_view PlayerView_StartSequence( self ) )
self setcandamage( false );
}
else
{
if ( healthAdded )
removeSafetyHealth();
attackMiss();
return true;
}
break;
case "dog_early":
self notify( "dog_early_notetrack" );
rate = 0.45 + 0.8 * level.dog_melee_timing_array[ level.dog_melee_index ];
rate = rate * dog_vs_player_anim_rate();
//println( "rate " + rate );
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, rate );
self setflaggedanimlimited( "meleeanim", %german_shepherd_attack_player_late, 1, 0.2, rate );
self.meleeingPlayer.player_view PlayerView_PlayKnockDownAnimLimited( rate );
break;
case "dog_lunge":
thread set_melee_timer();
rate = dog_vs_player_anim_rate();
self setflaggedanim( "meleeanim", %german_shepherd_attack_player, 1, 0.2, rate );
self.meleeingPlayer.player_view PlayerView_PlayKnockDownAnim( rate );
break;
case "dogbite_damage":
/#
if ( isgodmode( self.meleeingPlayer ) )
break;
#/
self thread killplayer();
break;
case "stop_tracking":
self OrientMode( "face current" );
break;
}
}
handle_dogbite_notetrack( note )
{
switch( note )
{
case "dogbite_damage":
/#
if ( isgodmode( self.meleeingPlayer ) )
break;
#/
self thread killplayer();
break;
}
}
set_melee_timer()
{
wait( 0.1 ); // used to be 0.15
// Have the dog_hint show up 50ms before the timer is set
// because it takes 50ms for the clienthudelem to start drawing
self thread dog_hint();
wait( 0.05 );
self.melee_able_timer = gettime();
/#
if ( getdebugdvar( "dog_hint" ) == "on" )
{
introblack = newHudElem();
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();
}
#/
}
playerDogInit()
{
assert( isPlayer( self ) );
self.lastDogMeleePlayerTime = 0;
self.dogMeleePlayerCounter = 0;
}
meleeBiteAttackPlayer()
{
assert( isPlayer( self.enemy ) );
self.meleeingPlayer = self.enemy;
if ( !isdefined( self.meleeingPlayer.dogInited ) )
self.meleeingPlayer playerDogInit();
attackRangeBuffer = 30;
meleeRange = self.meleeAttackDist + attackRangeBuffer;
for ( ;; )
{
if ( !isalive( self.enemy ) )
break;
if ( ( isdefined( self.meleeingPlayer.syncedMeleeTarget ) && self.meleeingPlayer.syncedMeleeTarget != self ) ||
( isdefined( self.meleeingPlayer.player_view ) && isdefined( self.meleeingPlayer.player_view.inSeq ) ) )
{
if ( checkEndCombat( meleeRange ) )
{
break;
}
else
{
combatIdle();
continue;
}
}
if ( self shouldWaitInCombatIdle() )
{
combatIdle();
continue;
}
self OrientMode( "face enemy" );
self animMode( "zonly_physics" );
self.safeToChangeScript = false;
prepareAttackPlayer();
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" );
self.meleeingPlayer setNextDogAttackAllowTime( 500 );
yawToEnemy = vectorToYaw( self.enemy.origin - self.origin );
if ( AbsAngleClamp180( yawToEnemy - self.angles[1] ) > 120 )
{
enemy = self.enemy;
wait 0.25;
if ( !isAlive( self.enemy ) || self.enemy != enemy )
continue;
}
if ( dog_cant_kill_in_one_hit() )
{
self.meleeingPlayer.lastDogMeleePlayerTime = getTime();
self.meleeingPlayer.dogMeleePlayerCounter++ ;
self setflaggedanimrestart( "meleeanim", %german_shepherd_run_attack, 1, 0.2, 1 );
self animscripts\shared::DoNoteTracks( "meleeanim", ::handleMeleeBiteAttackNoteTracks );
}
else
{
self thread dog_melee_death();
self.meleeingPlayer.attacked_by_dog = true;
self.meleeingPlayer.laststand = false;
self.meleeingPlayer.achieve_downed_kills = undefined;
self thread clear_player_attacked_by_dog_on_death();
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 );
self notify( "dog_no_longer_melee_able" );
self setcandamage( true );
self unlink();
}
self.safeToChangeScript = true;
wait 0.05; // give code chance to react
if ( checkEndCombat( meleeRange ) )
break;
}
self.safeToChangeScript = true;
self animMode( "none" );
}
clear_player_attacked_by_dog_on_death()
{
self waittill( "death" );
self.meleeingPlayer.attacked_by_dog = undefined;
}
dog_cant_kill_in_one_hit()
{
if ( isdefined( self.meleeingPlayer.dogs_dont_instant_kill ) )
{
assertex( self.meleeingPlayer.dogs_dont_instant_kill, "Dont set self.meleeingPlayer.dogs_dont_instant_kill to false, set to undefined" );
return true;
}
if ( self.meleeingPlayer isPlayerDown() )
return true;
if ( isdefined( self.meleeingPlayer.slideModel ) )
return true;
if ( getTime() - self.meleeingPlayer.lastDogMeleePlayerTime > 8000 )
self.meleeingPlayer.dogMeleePlayerCounter = 0;
return self.meleeingPlayer.dogMeleePlayerCounter < self.meleeingPlayer.gs.dog_hits_before_kill &&
self.meleeingPlayer.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() || !isAI( self.enemy ) )
{
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.a.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();
if ( !self.meleeKillTarget )
{
self kill();
}
else
{
self.pushable = true;
self.safeToChangeScript = true;
self.flashBangImmunity = false;
}
}
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 = 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;
players = getentarray( "player", "classname" );
for ( i = 0; i < players.size; i++ )
{
player = players[ i ];
if ( !isdefined( player.syncedMeleeTarget ) || player.syncedMeleeTarget == self )
continue;
offsetVec = player.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 = vector_multiply( offsetVec, 3 / offset );
self safeTeleport( self.origin - offsetVec, ( 0, 30, 0 ) );
}
}
}
}
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.flashBangImmunity = true;
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()
{
// [{+melee}] Melee the dog right when it bites to grab its throat.
// Watch for the ^3[{+melee}]^7 hint to grab a dog.
level.dog_death_quote = &"NEW_DOG_DEATH_DO_NOTHING_ALT";
level.dog_death_type = "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.meleeingPlayer ) && ( self.meleeingPlayer MeleeButtonPressed() );
}
dog_hint()
{
press_time = self.meleeingPlayer.gs.dog_presstime / 1000 / dog_vs_player_anim_rate();
level endon( "clearing_dog_hint" );
if ( isDefined( self.meleeingPlayer.dogHintElem ) )
self.meleeingPlayer.dogHintElem maps\_hud_util::destroyElem();
self.meleeingPlayer.dogHintElem = self.meleeingPlayer maps\_hud_util::createClientFontString( "default", 3 );
self.meleeingPlayer.dogHintElem.color = ( 1, 1, 1 );
// [{+melee}]
self.meleeingPlayer.dogHintElem setText( &"SCRIPT_PLATFORM_DOG_HINT" );
self.meleeingPlayer.dogHintElem.x = 0;
self.meleeingPlayer.dogHintElem.y = 20;
self.meleeingPlayer.dogHintElem.alignX = "center";
self.meleeingPlayer.dogHintElem.alignY = "middle";
self.meleeingPlayer.dogHintElem.horzAlign = "center";
self.meleeingPlayer.dogHintElem.vertAlign = "middle";
self.meleeingPlayer.dogHintElem.foreground = true;
self.meleeingPlayer.dogHintElem.alpha = 1;
self.meleeingPlayer.dogHintElem endon( "death" );
wait( press_time );
thread dog_hint_fade();
}
dog_hint_fade()
{
level notify( "clearing_dog_hint" );
if ( isDefined( self.meleeingPlayer.dogHintElem ) )
{
hud = self.meleeingPlayer.dogHintElem;
if ( IsDefined( self.meleeingPlayer.player_view.neckSnapped ) && self.meleeingPlayer.player_view.neckSnapped )
{
time = 0.5;
hud ChangeFontScaleOvertime( time );
hud.fontScale = hud.fontScale * 1.5;
hud.glowColor = ( 0.3, 0.6, 0.3 );
hud.glowAlpha = 1;
hud FadeOverTime( time );
hud.color = ( 0, 0, 0 );
hud.alpha = 0;
wait( time );
hud maps\_hud_util::destroyElem();
}
else
{
hud maps\_hud_util::destroyElem();
}
}
}
dog_delayed_unlink()
{
wait 0.7;
if ( isdefined( self ) )
self unlink();
}
// for safety
dog_delayed_allow_damage()
{
self endon( "death" );
wait 1.5;
if ( isdefined( self ) )
self setCanDamage( true );
}
dog_melee_death()
{
self endon( "killanimscript" );
self endon( "dog_no_longer_melee_able" );
pressed = false;
// change this number for difficulty level:
press_time = self.meleeingPlayer.gs.dog_presstime / dog_vs_player_anim_rate();
self waittill( "dog_early_notetrack" );
while ( player_attacked() )
{
// wait until the player lets go of the button, if he's holding it
wait( 0.05 );
}
pressed_too_soon = false;
for ( ;; )
{
if ( !pressed )
{
if ( self player_attacked() )
{
pressed = true;
if ( isdefined( self.melee_able_timer ) && isalive( self.meleeingPlayer ) )
{
if ( gettime() - self.melee_able_timer <= press_time )
{
self.meleeingPlayer set_melee_early( pressed_too_soon );
self.meleeingPlayer.player_view.neckSnapped = true;
self notify( "melee_stop" );
self setflaggedanimknobrestart( "dog_death_anim", %german_shepherd_player_neck_snap, 1, 0.2, 1 );
self thread dog_delayed_allow_damage();
self setcandamage( false );
//maps\_utility::giveachievement_wrapper( "DOWN_BOY_DOWN" );
self waittillmatch( "dog_death_anim", "dog_death" );
self thread play_sound_in_space( "dog_neckbreak", self getEye() );
self setcandamage( true );
self.a.nodeath = true;
dif = self.meleeingPlayer.origin - self.origin;
dif = ( dif[ 0 ], dif [ 1 ], 0 );
arcadeMode_kill( self.origin, "melee", 50 );
self thread dog_delayed_unlink();
self kill( self geteye() - dif, self.meleeingPlayer );
self notify( "killanimscript" );
}
else
{
self.meleeingPlayer set_melee_early( pressed_too_soon );
self.meleeingPlayer.player_view PlayerView_KnockDownLate();
self setanimlimited( %attack_player, 0.01, 0.2, 1 );
self setanimlimited( %attack_player_late, 1, 0.2, 1 );
// [{+melee}] Melee the dog before it bites to grab its throat.
// Too late. Watch for the ^3[{+melee}]^7 hint to grab a dog.
level.dog_death_quote = &"NEW_DOG_DEATH_TOO_LATE_ALT";
level.dog_death_type = "late";
}
return;
}
pressed_too_soon = true;
if ( self.meleeingPlayer can_early_melee() )
{
// [{+melee}] Wait for the dog to bite to grab its throat.
// Too early. Wait for the ^3[{+melee}]^7 hint to grab a dog.
level.dog_death_quote = &"NEW_DOG_DEATH_TOO_SOON_ALT";
level.dog_death_type = "soon";
rate = dog_vs_player_anim_rate();
self setflaggedanimknobrestart( "meleeanim", %german_shepherd_player_neck_miss, 1, 0.2, rate );
self.meleeingPlayer.player_view PlayerView_PlayMissAnim( rate );
// once player clicks, if it is at the wrong time, he does not get another chance.
return;
}
}
}
else
{
if ( !self player_attacked() )
{
pressed = false;
}
}
wait( 0.05 );
}
}
can_early_melee()
{
if ( self.gameskill == 3 )
{
return true;
}
if ( IsDefined( self.dogMeleeEarly ) && self.dogMeleeEarly )
{
return true;
}
return false;
}
set_melee_early( pressed_too_soon )
{
if ( !pressed_too_soon )
{
return;
}
// Hardened will return false the first time, but after that
// the player can melee too early and be penalized
if ( level.gameskill > 1 && !IsDefined( self.dogMeleeEarly ) )
{
self.dogMeleeEarly = true;
}
}
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
//
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
#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 kill();
}
}
}
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_3rd_person" );
playerDrone_create( player )
{
playerDrone = spawn( "script_model", player.origin );
assert( isdefined( player.last_modelfunc ) );
playerDrone [[ player.last_modelfunc ]]();
playerDrone useAnimTree( #animtree );
return playerDrone;
}
playerDrone_anim_knockdown( rate )
{
self endon( "death" );
time = getAnimLength( %player_3rd_dog_knockdown );
self setanim( %player_3rd_dog_knockdown, 1, 0, rate );
}
playerDone_anim_neck_snap()
{
self setanimknobrestart( %player_3rd_dog_knockdown_neck_snap, 1, 0, 1 );
}
playerDone_anim_saved()
{
self setanimknobrestart( %player_3rd_dog_knockdown_saved, 1, 0, 1 );
}
playerDone_anim_laststand()
{
self setanimknobrestart( %player_3rd_dog_knockdown_laststand, 1, 0, 1 );
}
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
//
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
#using_animtree( "player" );
PlayerView_Spawn( dog )
{
playerView = spawn( "script_model", dog.meleeingPlayer.origin );
playerView.angles = dog.meleeingPlayer.angles;
playerView setModel( level.player_viewhand_model ); // Add to level initialization maps\_load::set_player_viewhand_model;
playerView useAnimTree( #animtree );
playerView hide();
return playerView;
}
handlePlayerKnockDownNotetracks( note )
{
switch( note )
{
case "allow_player_save":
{
if ( isdefined( self.dog ) )
{
wait 1;
self.dog setcandamage( true );
}
}
break;
case "blood_pool":
{
if ( !isdefined( self.dog.meleeingPlayer ) )
break;
if ( is_coop() && !is_other_player_downed( self.dog.meleeingPlayer ) )
break;
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 + vector_multiply( forward, -8.5 ) + vector_multiply( up, 5 ) + vector_multiply( right, 0 );
playfx( level._effect[ "deathfx_bloodpool" ], tagPos, forward, up ); // Add to level initialization animscripts\dog\dog_init::initDogAnimations();
}
break;
}
}
PlayerView_KnockDownAnim( dog )
{
self endon( "pvd_melee_interrupted" );
player = dog.meleeingPlayer;
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( %knockdown, 1, 0, 1 );
self setanimlimited( %knockdown_late, 0.01, 0, 1 );
self animscripts\shared::DoNoteTracks( "viewanim", ::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 );
}
}
PlayerView_StartSequence( dog )
{
if ( isdefined( self.inSeq ) )
return false;
player = dog.meleeingPlayer;
player notify( "dog_attacks_player" );
self.inSeq = true;
if ( isalive( player ) )
player hideHud();
player setstance( "stand" );
player.syncedMeleeTarget = dog;
player.player_view PlayerView_Show( player );
player.coop_downed = undefined;
direction = dog.origin - player.origin;
self.angles = vectortoangles( direction );
self.angles = ( 0, self.angles[ 1 ], 0 );
self.startAngles = self.angles;
playerpos = player.origin;
newOrigin = player getDropToFloorPosition( player.origin );
if ( isdefined( newOrigin ) )
self.origin = newOrigin;
else
self.origin = player.origin;
self thread PlayerView_KnockDownAnim( dog );
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_camera" );
//self 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()
{
self endon( "death" );
self showViewModel();
self enableweapons();
}
PlayerView_EndSequence( player )
{
player showHud();
if ( isalive( player ) )
{
self clearanim( %player_view_dog_knockdown, 0.1 );
if ( isdefined( self.neckSnapped ) )
{
self setflaggedanimrestart( "viewanim", %player_view_dog_knockdown_neck_snap, 1, 0.2, 1 );
if ( isdefined( self.playerDrone ) )
self.playerDrone playerDone_anim_neck_snap();
}
else if ( isdefined( player.coop_downed ) )
{
self setflaggedanimknobrestart( "viewanim", %player_view_dog_knockdown_laststand, 1, 0.1, 1 );
if ( isdefined( self.playerDrone ) )
self.playerDrone playerDone_anim_laststand();
}
else
{
thread SavedNotify( player );
self setflaggedanimrestart( "viewanim", %player_view_dog_knockdown_saved );
if ( isdefined( self.playerDrone ) )
self.playerDrone playerDone_anim_saved();
}
if ( !isdefined( player.coop_downed ) )
{
player delaythread( 2.5, ::player_gets_weapons_back );
self animscripts\shared::DoNoteTracks( "viewanim" );
player notify( "player_saved_from_dog" );
}
else
{
self animscripts\shared::DoNoteTracks( "viewanim" );
player notify( "deathshield", 1000000, self.dog );
if ( player isPlayerDown() )
player.laststand = true;
player showViewModel();
}
PlayerView_UnlinkPlayerAndDelete( player );
}
else
{
setsaveddvar( "compass", 0 );
}
player.syncedMeleeTarget = undefined;
RestorePlayerControls( player );
}
PlayerView_UnlinkPlayerAndDelete( player )
{
player show();
player unlink();
player setOrigin( self.origin );
player setplayerangles( self.startAngles );
player setcandamage( true );
player_view = player.player_view;
if ( isdefined( player_view ) )
{
if ( isdefined( player_view.playerDrone ) )
player_view.playerDrone delete();
player_view delete();
player.player_view = undefined;
}
}
RestorePlayerControls( player )
{
player allowLean( true );
player allowCrouch( true );
player allowProne( true );
player freezeControls( false );
player.attacked_by_dog = undefined;
}
PlayerView_Show( player )
{
self showOnClient( player );
if ( is_coop() )
{
playerDrone = playerDrone_create( player );
playerDrone linkto( self, "tag_origin", ( 0, 0, 0 ), ( 0, 0, 0 ) );
playerDrone thread playerDrone_anim_knockdown( 1 );
self.playerDrone = playerDrone;
if ( level.player == player && isdefined( level.player2 ) )
{
player hideOnClient( level.player2 );
playerDrone hideOnClient( level.player );
}
else
{
player hideOnClient( level.player );
playerDrone hideOnClient( level.player2 );
}
}
player hideViewModel();
player disableweapons();
}
PlayerView_PlayKnockDownAnimLimited( rate )
{
self setflaggedanimlimited( "viewanim", %player_view_dog_knockdown, 1, 0.2, rate );
self setflaggedanimlimited( "viewanim", %player_view_dog_knockdown_late, 1, 0.2, rate );
if ( isdefined( self.playerDrone ) )
self.playerDrone playerDrone_anim_knockdown( rate );
}
PlayerView_PlayKnockDownAnim( rate )
{
self setflaggedanimlimited( "viewanim", %player_view_dog_knockdown, 1, 0.2, rate );
self setflaggedanimlimited( "viewanim", %player_view_dog_knockdown_late, 1, 0.2, rate );
if ( isdefined( self.playerDrone ) )
self.playerDrone playerDrone_anim_knockdown( rate );
}
PlayerView_PlayMissAnim( rate )
{
self setflaggedanimknobrestart( "viewanim", %player_view_dog_knockdown_neck_miss, 1, 0.2, rate );
}
PlayerView_KnockDownLate()
{
self setanimlimited( %knockdown, 0.01, 0.2, 1 );
self setanimlimited( %knockdown_late, 1, 0.2, 1 );
}
/*draw_tag( tagname )
{
self endon( "death" );
range = 25;
while ( 1 )
{
angles = self gettagangles( tagname );
origin = self gettagorigin( tagname );
forward = anglestoforward( angles );
forward = vector_multiply( forward, range );
right = anglestoright( angles );
right = vector_multiply( right, range );
up = anglestoup( angles );
up = vector_multiply( up, range );
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;
}
}*/
dog_vs_player_anim_rate()
{
//if ( is_coop() )
// return 0.25;
return 1;
}