351 lines
8.0 KiB
Plaintext
351 lines
8.0 KiB
Plaintext
#include maps\mp\agents\_scriptedAgents;
|
|
#include maps\mp\_utility;
|
|
#include common_scripts\utility;
|
|
|
|
main()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "killanimscript" );
|
|
|
|
assert( IsDefined( self.curMeleeTarget ) );
|
|
|
|
self.curMeleeTarget endon( "disconnect" );
|
|
|
|
// get desired end pos.
|
|
meToTarget = self.curMeleeTarget.origin - self.origin;
|
|
distMeToTarget = Length( meToTarget );
|
|
|
|
bTestCanMove = true;
|
|
if ( distMeToTarget < self.attackOffset )
|
|
{
|
|
attackPos = self.origin;
|
|
bTestCanMove = false;
|
|
}
|
|
else
|
|
{
|
|
meToTarget = meToTarget / distMeToTarget;
|
|
attackPos = self.curMeleeTarget.origin - meToTarget * self.attackOffset;
|
|
}
|
|
|
|
bLerp = false;
|
|
|
|
startPos = self.origin + (0,0,30);
|
|
endPos = self.curMeleeTarget.origin + (0,0,30);
|
|
hitPos = PhysicsTrace( startPos, endPos );
|
|
if ( DistanceSquared( hitPos, endPos ) > 1 )
|
|
{
|
|
self MeleeFailed();
|
|
return;
|
|
}
|
|
|
|
if ( bTestCanMove )
|
|
bCanMoveToAttackPos = self CanMovePointToPoint( self.origin, attackPos );
|
|
else
|
|
bCanMoveToAttackPos = true;
|
|
|
|
animEntry = undefined;
|
|
if ( !bCanMoveToAttackPos )
|
|
{
|
|
bShouldDoExtendedKill = false;
|
|
}
|
|
else
|
|
{
|
|
animEntry = self ShouldDoExtendedKill( self.curMeleeTarget );
|
|
bShouldDoExtendedKill = IsDefined( animEntry );
|
|
}
|
|
|
|
self.bLockGoalPos = true;
|
|
|
|
if ( bShouldDoExtendedKill )
|
|
{
|
|
assert( IsDefined( animEntry ) );
|
|
self DoExtendedKill( animEntry );
|
|
}
|
|
else
|
|
{
|
|
self DoStandardKill( attackPos, bCanMoveToAttackPos );
|
|
}
|
|
}
|
|
|
|
end_script()
|
|
{
|
|
self ScrAgentSetAnimScale( 1, 1 );
|
|
self.bLockGoalPos = false;
|
|
}
|
|
|
|
GetMeleeAnimState()
|
|
{
|
|
return "attack_run_and_jump";
|
|
}
|
|
|
|
// returns kill direction, if any.
|
|
ShouldDoExtendedKill( victim )
|
|
{
|
|
if( !self.enableExtendedKill )
|
|
return undefined;
|
|
|
|
cMaxHeightDiff = 4;
|
|
|
|
if ( !IsGameParticipant( victim ) ) // humans only.
|
|
return undefined;
|
|
if ( self IsProtectedByRiotshield( victim ) )
|
|
return undefined;
|
|
if ( victim IsJuggernaut() )
|
|
return undefined;
|
|
|
|
victimToMe = self.origin - victim.origin;
|
|
if ( abs( victimToMe[2] ) > cMaxHeightDiff )
|
|
return undefined;
|
|
|
|
victimToMe2D = VectorNormalize( (victimToMe[0], victimToMe[1], 0) );
|
|
victimFacing = AnglesToForward( victim.angles );
|
|
angleToMe = VectorDot( victimFacing, victimToMe2D );
|
|
|
|
if ( angleToMe > 0.707 )
|
|
{
|
|
animEntry = 0; // front
|
|
snappedVictimToMe = RotateVector( ( 1, 0, 0 ), victim.angles );
|
|
}
|
|
else if ( angleToMe < -0.707 )
|
|
{
|
|
animEntry = 1; // back
|
|
snappedVictimToMe = RotateVector( (-1, 0, 0), victim.angles );
|
|
}
|
|
else
|
|
{
|
|
cross = maps\mp\agents\dog\_dog_think::cross2D( victimToMe, victimFacing );
|
|
if ( cross > 0 )
|
|
{
|
|
animEntry = 3; // right
|
|
snappedVictimToMe = RotateVector( (0, -1, 0), victim.angles );
|
|
}
|
|
else
|
|
{
|
|
animEntry = 2; // left
|
|
snappedVictimToMe = RotateVector( (0, 1, 0), victim.angles );
|
|
}
|
|
}
|
|
|
|
if ( animEntry == 1 )
|
|
cClearanceRequired = 128;
|
|
else
|
|
cClearanceRequired = 96;
|
|
landPos = victim.origin - cClearanceRequired * snappedVictimToMe;
|
|
|
|
landPosDropped = self DropPosToGround( landPos );
|
|
if ( !IsDefined( landPosDropped ) )
|
|
return undefined;
|
|
|
|
if ( abs( landPosDropped[2] - landPos[2] ) > cMaxHeightDiff )
|
|
return undefined;
|
|
|
|
if ( !self AIPhysicsTracePassed( victim.origin + (0,0,4), landPosDropped + (0,0,4), self.radius, self.height ) )
|
|
return undefined;
|
|
|
|
return animEntry;
|
|
}
|
|
|
|
DoExtendedKill( animEntry )
|
|
{
|
|
meleeAnimState = "attack_extended";
|
|
|
|
self DoMeleeDamage( self.curMeleeTarget, self.curMeleeTarget.health, "MOD_MELEE_DOG" );
|
|
|
|
attackAnim = self GetAnimEntry( meleeAnimState, animEntry );
|
|
self thread ExtendedKill_StickToVictim( attackAnim, self.curMeleeTarget.origin, self.curMeleeTarget.angles );
|
|
|
|
if ( animEntry == 1 ) // back
|
|
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "mp_wolf_attack_quick_back_npc", "mp_dog_attack_quick_back_npc" ) );
|
|
else
|
|
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "mp_wolf_attack_short_npc", "mp_dog_attack_short_npc" ) );
|
|
|
|
self PlayAnimNUntilNotetrack( meleeAnimState, animEntry, "attack", "end" );
|
|
|
|
self notify( "kill_stick" );
|
|
self.curMeleeTarget = undefined;
|
|
|
|
self ScrAgentSetAnimMode( "anim deltas" );
|
|
self Unlink();
|
|
}
|
|
|
|
ExtendedKill_StickToVictim( attackAnim, targetOrigin, targetAngles )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "killanimscript" );
|
|
self endon( "kill_stick" );
|
|
|
|
wait( 0.05 ); // must wait for anim to kick in before we can properly calculate our offsets.
|
|
assert( IsDefined( self.curMeleeTarget ) );
|
|
|
|
if ( IsAlive( self.curMeleeTarget ) ) // godmode, etc.
|
|
return;
|
|
|
|
corpse = self.curMeleeTarget GetCorpseEntity();
|
|
assert( IsDefined( corpse ) );
|
|
self LinkTo( corpse );
|
|
|
|
self ScrAgentDoAnimRelative( attackAnim, targetOrigin, targetAngles );
|
|
}
|
|
|
|
DoStandardKill( attackPos, bCanMoveToAttackPos )
|
|
{
|
|
meleeAnimState = self GetMeleeAnimState();
|
|
|
|
bLerp = false;
|
|
|
|
if ( !bCanMoveToAttackPos )
|
|
{
|
|
if ( self AgentCanSeeSentient( self.curMeleeTarget ) )
|
|
{
|
|
groundPos = self DropPosToGround( self.curMeleeTarget.origin );
|
|
if ( IsDefined( groundPos ) )
|
|
{
|
|
bLerp = true;
|
|
attackPos = groundPos; // i'm going to clip the heck through him, but i need a guaranteed safe spot.
|
|
}
|
|
else
|
|
{
|
|
self MeleeFailed();
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self MeleeFailed();
|
|
return;
|
|
}
|
|
}
|
|
|
|
self.lastMeleeFailedMyPos = undefined;
|
|
self.lastMeleeFailedPos = undefined;
|
|
|
|
attackAnim = self GetAnimEntry( meleeAnimState, 0 );
|
|
animLength = GetAnimLength( attackAnim );
|
|
meleeNotetracks = GetNotetrackTimes( attackAnim, "dog_melee" );
|
|
if ( meleeNotetracks.size > 0 )
|
|
lerpTime = meleeNotetracks[0] * animLength;
|
|
else
|
|
lerpTime = animLength;
|
|
|
|
self ScrAgentDoAnimLerp( self.origin, attackPos, lerpTime );
|
|
|
|
self thread UpdateLerpPos( self.curMeleeTarget, lerpTime, bCanMoveToAttackPos );
|
|
|
|
self PlayAnimNUntilNotetrack( meleeAnimState, 0, "attack", "dog_melee" );
|
|
|
|
self notify( "cancel_updatelerppos" );
|
|
|
|
damageDealt = 0;
|
|
if( IsDefined( self.curMeleeTarget ) )
|
|
damageDealt = self.curMeleeTarget.health;
|
|
if( IsDefined( self.meleeDamage ) )
|
|
damageDealt = self.meleeDamage;
|
|
|
|
if( IsDefined( self.curMeleeTarget ) )
|
|
self DoMeleeDamage( self.curMeleeTarget, damageDealt, "MOD_IMPACT" );
|
|
|
|
self.curMeleeTarget = undefined; // dude's dead now, or soon will be.
|
|
|
|
if ( bLerp )
|
|
self ScrAgentSetAnimScale( 0, 1 );
|
|
else
|
|
self ScrAgentSetAnimScale( 1, 1 );
|
|
|
|
self ScrAgentSetPhysicsMode( "gravity" );
|
|
self ScrAgentSetAnimMode( "anim deltas" );
|
|
|
|
self WaitUntilNotetrack( "attack", "end" );
|
|
}
|
|
|
|
UpdateLerpPos( enemy, lerpTime, bCanMoveToAttackPos )
|
|
{
|
|
self endon( "killanimscript" );
|
|
self endon( "death" );
|
|
self endon( "cancel_updatelerppos" );
|
|
enemy endon( "disconnect" );
|
|
enemy endon( "death" );
|
|
|
|
timeRemaining = lerpTime;
|
|
interval = 0.05;
|
|
while ( true )
|
|
{
|
|
wait( interval );
|
|
timeRemaining -= interval;
|
|
|
|
if ( timeRemaining <= 0 )
|
|
break;
|
|
|
|
attackPos = GetUpdatedAttackPos( enemy, bCanMoveToAttackPos );
|
|
if ( !IsDefined( attackPos ) )
|
|
break;
|
|
|
|
self ScrAgentDoAnimLerp( self.origin, attackPos, timeRemaining );
|
|
}
|
|
}
|
|
|
|
GetUpdatedAttackPos( enemy, bCanMove )
|
|
{
|
|
if ( !bCanMove )
|
|
{
|
|
droppedPos = self DropPosToGround( enemy.origin );
|
|
return droppedPos;
|
|
}
|
|
else
|
|
{
|
|
meToTarget = enemy.origin - self.origin;
|
|
distMeToTarget = Length( meToTarget );
|
|
|
|
if ( distMeToTarget < self.attackOffset )
|
|
{
|
|
return self.origin;
|
|
}
|
|
else
|
|
{
|
|
meToTarget = meToTarget / distMeToTarget;
|
|
attackPos = enemy.origin - meToTarget * self.attackOffset;
|
|
if ( self CanMovePointToPoint( self.origin, attackPos ) )
|
|
return attackPos;
|
|
else
|
|
return undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
IsProtectedByRiotshield( enemy )
|
|
{
|
|
if ( IsDefined( enemy.hasRiotShield ) && enemy.hasRiotShield )
|
|
{
|
|
enemyToMe = self.origin - enemy.origin;
|
|
meToEnemy = VectorNormalize( ( enemyToMe[0], enemyToMe[1], 0 ) );
|
|
|
|
enemyFacing = AnglesToForward( enemy.angles );
|
|
angleToMe = VectorDot( enemyFacing, enemyToMe );
|
|
|
|
if ( enemy.hasRiotShieldEquipped )
|
|
{
|
|
if ( angleToMe > 0.766 )
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if ( angleToMe < -0.766 )
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
DoMeleeDamage( enemy, damage, meansOfDeath )
|
|
{
|
|
if ( self IsProtectedByRiotshield( enemy ) )
|
|
return;
|
|
|
|
enemy DoDamage( damage, self.origin, self, self, meansOfDeath );
|
|
}
|
|
|
|
MeleeFailed()
|
|
{
|
|
self.lastMeleeFailedMyPos = self.origin;
|
|
self.lastMeleeFailedPos = self.curMeleeTarget.origin;
|
|
} |