2024-12-11 11:28:08 +01:00

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;
}