#include animscripts\SetPoseMovement; #include animscripts\Utility; #include common_scripts\Utility; #using_animtree( "generic_human" ); main() { self endon( "killanimscript" ); animscripts\utility::initialize( "reactions" ); self newEnemySurprisedReaction(); } initReactionAnims() { anim.runningReactToBullets = []; anim.runningReactToBullets[ anim.runningReactToBullets.size ] = %run_react_duck; anim.runningReactToBullets[ anim.runningReactToBullets.size ] = %run_react_flinch; anim.runningReactToBullets[ anim.runningReactToBullets.size ] = %run_react_stumble; anim.lastRunningReactAnim = 0; anim.coverReactions = []; anim.coverReactions[ "cover_stand" ] = array( %stand_cover_reaction_A, %stand_cover_reaction_B ); anim.coverReactions[ "cover_crouch" ] = array( %crouch_cover_reaction_A, %crouch_cover_reaction_B ); anim.coverReactions[ "cover_left" ] = array( %CornerStndL_react_A ); anim.coverReactions[ "cover_right" ] = array( %CornerStndR_react_A ); } /////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////// reactionsCheckLoop() { self thread bulletWhizbyCheckLoop(); } /////////////////////////////////////////////////////////////////////////// // death reactions /////////////////////////////////////////////////////////////////////////// /* disabled for now since the animations aren't in common csv MoveDeathReaction() { // Decide what pose to use desiredPose = self animscripts\utility::choosePose(); if ( desiredPose == "stand" ) { deathAnim = getDeathReactionAnim(); DoDeathReactionAnim( deathAnim ); } } ExposedCombatDeathReaction() { // Decide what pose to use desiredPose = self animscripts\utility::choosePose(); if ( desiredPose == "stand" ) { deathAnim = getDeathReactionAnim(); DoDeathReactionAnim( deathAnim ); } } DoDeathReactionAnim( deathAnim ) { self endon( "movemode" ); rate = self.moveplaybackrate; self setFlaggedAnimKnobAll( "deathanim", deathAnim, %body, 1, 1, rate, true ); // self animscripts\run::SetMoveNonForwardAnims( %walk_backward, %walk_left, %walk_right ); // self thread animscripts\run::SetCombatStandMoveAnimWeights( "walk" ); self animscripts\shared::DoNoteTracks( "deathanim" ); self.deathTeamate = false; } getDeathReactionAnim() { if ( self.deathTeamateReaction == "back" ) return %run_reaction_180; else if ( self.deathTeamateReaction == "left" ) return %run_reaction_L_quick; else if ( self.deathTeamateReaction == "right" ) return %run_reaction_R_quick; } deathCheck() { self endon( "killanimscript" ); self.deathTeamateReaction = "none"; self.deathTeamate = false; minDeathDistance = 100; maxDeathDistance = 500; minGoalDistance = 200; maxTurnAngle = 135; minTurnAngle = 10; self AddAIEventListener( "death" ); for ( ;; ) { self waittill( "ai_event", event, originator, position ); if ( event != "death" ) continue; deathDirection = position - self.origin; deathDistance = Length( deathDirection ); if ( deathDistance >= minDeathDistance && deathDistance <= maxDeathDistance ) { goalDirection = self.goalpos - self.origin; goalDistance = Length( goalDirection ); if ( goalDistance >= minGoalDistance ) { goalAngles = VectorToAngles( goalDirection ); deltaAngles = Abs( self.angles[1] - goalAngles[1] ); if ( deltaAngles > minTurnAngle ) { if ( deltaAngles > maxTurnAngle ) self.deathTeamateReaction = "back"; else if ( self.angles[1] > goalAngles[1] ) self.deathTeamateReaction = "left"; else self.deathTeamateReaction = "right"; self.deathTeamate = true; } } } } } */ canReactAgain() { return ( !isdefined( self.lastReactTime ) || gettime() - self.lastReactTime > 2000 ); } /////////////////////////////////////////////////////////////////////////// // bullet whizby reaction /////////////////////////////////////////////////////////////////////////// bulletWhizbyReaction() { self endon( "killanimscript" ); self.lastReactTime = gettime(); self.a.movement = "stop"; enemyNear = ( isDefined( self.whizbyEnemy ) && distanceSquared( self.origin, self.whizbyEnemy.origin ) < 400 * 400 ); self animmode( "gravity" ); self orientmode( "face current" ); // react and go to prone if ( enemyNear || cointoss() ) { self clearanim( %root, 0.1 ); reactAnim = []; reactAnim[ 0 ] = %exposed_idle_reactA; reactAnim[ 1 ] = %exposed_idle_reactB; reactAnim[ 2 ] = %exposed_idle_twitch; reactAnim[ 3 ] = %exposed_idle_twitch_v4; reaction = reactAnim[ randomint( reactAnim.size ) ]; if ( enemyNear ) waitTime = 1 + randomfloat( 0.5 ); else waitTime = 0.2 + randomfloat( 0.5 ); self setFlaggedAnimKnobRestart( "reactanim", reaction, 1, 0.1, 1 ); self animscripts\shared::DoNoteTracksForTime( waitTime, "reactanim" ); self clearanim( %root, 0.1 ); if ( !enemyNear && self.stairsState == "none" ) { rate = 1 + randomfloat( 0.2 ); diveAnim = randomAnimOfTwo( %exposed_dive_grenade_B, %exposed_dive_grenade_F ); self setFlaggedAnimKnobRestart( "dive", diveAnim, 1, 0.1, rate ); self animscripts\shared::DoNoteTracks( "dive" ); } } else // crouch then handsignal or turn { wait randomfloat( 0.2 ); rate = 1.2 + randomfloat( 0.3 ); if ( self.a.pose == "stand" ) { self clearanim( %root, 0.1 ); self setFlaggedAnimKnobRestart( "crouch", %exposed_stand_2_crouch, 1, 0.1, rate ); self animscripts\shared::DoNoteTracks( "crouch" ); } forward = anglesToForward( self.angles ); if ( isDefined( self.whizbyEnemy ) ) dirToEnemy = vectorNormalize( self.whizbyEnemy.origin - self.origin ); else dirToEnemy = forward; if ( vectordot( dirToEnemy, forward ) > 0 ) { twitchAnim = randomAnimOfTwo( %exposed_crouch_idle_twitch_v2, %exposed_crouch_idle_twitch_v3 ); self clearanim( %root, 0.1 ); self setFlaggedAnimKnobRestart( "twitch", twitchAnim, 1, 0.1, 1 ); self animscripts\shared::DoNoteTracks( "twitch" ); //if ( cointoss() ) // self handsignal( "go" ); } else { turnAnim = randomAnimOfTwo( %exposed_crouch_turn_180_left, %exposed_crouch_turn_180_right ); self clearanim( %root, 0.1 ); self setFlaggedAnimKnobRestart( "turn", turnAnim, 1, 0.1, 1 ); self animscripts\shared::DoNoteTracks( "turn" ); } } self clearanim( %root, 0.1 ); self.whizbyEnemy = undefined; self animmode( "normal" ); self orientmode( "face default" ); } bulletWhizbyCheckLoop() { self endon( "killanimscript" ); if ( isdefined( self.disableBulletWhizbyReaction ) ) return; while ( 1 ) { self waittill( "bulletwhizby", shooter ); if ( !isdefined( shooter.team ) || self.team == shooter.team ) continue; if ( isdefined( self.coverNode ) || isdefined( self.ambushNode ) ) continue; if ( self.a.pose != "stand" ) continue; if ( !canReactAgain() ) continue; self.whizbyEnemy = shooter; self animcustom( ::bulletWhizbyReaction ); } } /////////////////////////////////////////////////////////////////////////// // surprised by new enemy reaction /////////////////////////////////////////////////////////////////////////// clearLookAtThread() { self endon( "killanimscript" ); wait 0.3; self setLookAtEntity(); } getNewEnemyReactionAnim() { reactAnim = undefined; if ( self nearClaimNodeAndAngle() && isdefined( anim.coverReactions[ self.prevScript ] ) ) { nodeForward = anglesToForward( self.node.angles ); dirToReactionTarget = vectorNormalize( self.reactionTargetPos - self.origin ); if ( vectorDot( nodeForward, dirToReactionTarget ) < -0.5 ) { self orientmode( "face current" ); index = randomint( anim.coverReactions[ self.prevScript ].size ); reactAnim = anim.coverReactions[ self.prevScript ][ index ]; } } if ( !isdefined( reactAnim ) ) { reactAnimArray = []; reactAnimArray[ 0 ] = %exposed_backpedal; reactAnimArray[ 1 ] = %exposed_idle_reactB; if ( isdefined( self.enemy ) && distanceSquared( self.enemy.origin, self.reactionTargetPos ) < 256 * 256 ) self orientmode( "face enemy" ); else self orientmode( "face point", self.reactionTargetPos ); if ( self.a.pose == "crouch" ) { dirToReactionTarget = vectorNormalize( self.reactionTargetPos - self.origin ); forward = anglesToForward( self.angles ); if ( vectorDot( forward, dirToReactionTarget ) < -0.5 ) { self orientmode( "face current" ); reactAnimArray[ 0 ] = %crouch_cover_reaction_A; reactAnimArray[ 1 ] = %crouch_cover_reaction_B; } } reactAnim = reactAnimArray[ randomint( reactAnimArray.size ) ]; } return reactAnim; } stealthNewEnemyReactAnim() { self clearanim( %root, 0.2 ); if ( randomint( 4 ) < 3 ) { self orientmode( "face enemy" ); self setFlaggedAnimKnobRestart( "reactanim", %exposed_idle_reactB, 1, 0.2, 1 ); time = getAnimLength( %exposed_idle_reactB ); self animscripts\shared::DoNoteTracksForTime( time * 0.8, "reactanim" ); self orientmode( "face current" ); } else { self orientmode( "face enemy" ); self setFlaggedAnimKnobRestart( "reactanim", %exposed_backpedal, 1, 0.2, 1 ); time = getAnimLength( %exposed_backpedal ); self animscripts\shared::DoNoteTracksForTime( time * 0.8, "reactanim" ); self orientmode( "face current" ); self clearanim( %root, 0.2 ); self setFlaggedAnimKnobRestart( "reactanim", %exposed_backpedal_v2, 1, 0.2, 1 ); self animscripts\shared::DoNoteTracks( "reactanim" ); } } newEnemyReactionAnim() { self endon( "death" ); self endon( "endNewEnemyReactionAnim" ); self.lastReactTime = gettime(); self.a.movement = "stop"; if ( isdefined( self._stealth ) && self.alertLevel != "combat" ) { stealthNewEnemyReactAnim(); } else { reactAnim = self getNewEnemyReactionAnim(); self clearanim( %root, 0.2 ); self setFlaggedAnimKnobRestart( "reactanim", reactAnim, 1, 0.2, 1 ); self animscripts\shared::DoNoteTracks( "reactanim" ); } self notify( "newEnemyReactionDone" ); } newEnemySurprisedReaction() { self endon( "death" ); if ( isdefined( self.disableReactionAnims ) ) return; if ( !canReactAgain() ) return; if ( self.a.pose == "prone" || isdefined( self.a.onback ) ) return; self animmode( "gravity" ); if ( isdefined( self.enemy ) ) newEnemyReactionAnim(); }