#include animscripts\SetPoseMovement; #include animscripts\combat_utility; #include common_scripts\utility; #using_animtree( "generic_human" ); // Every script calls initAnimTree to ensure a clean, fresh, known animtree state. // clearanim should never be called directly, and this should never occur other than // at the start of an animscript // This function now also does any initialization for the scripts that needs to happen // at the beginning of every main script. initAnimTree( animscript ) { self clearAnim( %body, 0.3 ); self setAnim( %body, 1, 0 ); // The %body node should always have weight 1. if ( animscript != "pain" && animscript != "death" ) self.a.special = "none"; self.missedSightChecks = 0; self.a.aimweight = 1.0; self.a.aimweight_start = 1.0; self.a.aimweight_end = 1.0; self.a.aimweight_transframes = 0; self.a.aimweight_t = 0; IsInCombat(); assertEX( isDefined( animscript ), "Animscript not specified in initAnimTree" ); } // UpdateAnimPose does housekeeping at the start of every script's main function. It does stuff like making prone // calculations are only being done if the character is actually prone. UpdateAnimPose() { assertEX( self.a.movement == "stop" || self.a.movement == "walk" || self.a.movement == "run", "UpdateAnimPose " + self.a.pose + " " + self.a.movement ); if ( isDefined( self.desired_anim_pose ) && self.desired_anim_pose != self.a.pose ) { if ( self.a.pose == "prone" ) self ExitProneWrapper( 0.5 ); if ( self.desired_anim_pose == "prone" ) { self SetProneAnimNodes( -45, 45, %prone_legs_down, %exposed_aiming, %prone_legs_up ); self EnterProneWrapper( 0.5 ); self setAnimKnobAll( %prone_aim_5, %body, 1, 0.1, 1 ); } } self.desired_anim_pose = undefined; } initialize( animscript ) { if ( isdefined( self.longDeathStarting ) ) { if ( animscript != "pain" && animscript != "death" ) { // we probably just came out of an animcustom. // just die, it's not safe to do anything else self kill( self.origin ); } if ( animscript != "pain" ) { self.longDeathStarting = undefined; self notify( "kill_long_death" ); } } if ( isdefined( self.a.mayOnlyDie ) && animscript != "death" ) { // we probably just came out of an animcustom. // just die, it's not safe to do anything else self kill( self.origin ); } // scripts can define this to allow cleanup before moving on if ( isDefined( self.a.postScriptFunc ) ) { scriptFunc = self.a.postScriptFunc; self.a.postScriptFunc = undefined; [[ scriptFunc ]]( animscript ); } // TODO: proper handing when animations exist if ( animscript != "combat" && animscript != "pain" && animscript != "death" && usingSidearm() ) { self animscripts\combat::switchToLastWeapon( %pistol_stand_switch, true ); //self animscripts\shared::placeWeaponOn( self.primaryweapon, "right" ); } if ( animscript != "combat" && animscript != "move" && animscript != "pain" ) self.a.magicReloadWhenReachEnemy = undefined; if ( animscript != "death" ) self.a.nodeath = false; if ( isDefined( self.isHoldingGrenade ) && ( animscript == "pain" || animscript == "death" || animscript == "flashed" ) ) { self dropGrenade(); } self.isHoldingGrenade = undefined; /# //thread checkGrenadeInHand( animscript ); #/ self animscripts\squadmanager::aiUpdateAnimState( animscript ); self.coverNode = undefined; self.suppressed = false; self.isReloading = false; self.changingCoverPos = false; self.a.aimIdleThread = undefined; self.a.scriptStartTime = gettime(); self.a.atConcealmentNode = false; if ( isdefined( self.node ) && ( self.node.type == "Conceal Prone" || self.node.type == "Conceal Crouch" || self.node.type == "Conceal Stand" ) ) self.a.atConcealmentNode = true; initAnimTree( animscript ); UpdateAnimPose(); } /# checkGrenadeInHand( animscript ) { // ensure no grenade left in hand self endon( "killanimscript" ); // pain and death animscripts don't execute script between notifying killanimscript and starting the next animscript, // so the grenade cleanup thread might still be waiting to run. if ( animscript == "pain" || animscript == "death" ) { wait .05; waittillframeend; } attachSize = self getattachsize(); for ( i = 0; i < attachSize; i++ ) { model = toLower( self getAttachModelName( i ) ); assertex( model != "weapon_m67_grenade", "AI has a grenade in hand after animscript finished. Please call over an animscripter! " + self.origin ); assertex( model != "weapon_m84_flashbang_grenade", "AI has a grenade in hand after animscript finished. Please call over an animscripter! " + self.origin ); assertex( model != "weapon_us_smoke_grenade", "AI has a grenade in hand after animscript finished. Please call over an animscripter! " + self.origin ); } } #/ getPreferredWeapon() { if ( isdefined( self.wantshotgun ) && self.wantshotgun ) { if ( isShotgun( self.primaryweapon ) ) return self.primaryweapon; else if ( isShotgun( self.secondaryweapon ) ) return self.secondaryweapon; } return self.primaryweapon; } badplacer( time, org, radius ) { for ( i = 0;i < time * 20;i++ ) { for ( p = 0;p < 10;p++ ) { angles = ( 0, randomint( 360 ), 0 ); forward = anglestoforward( angles ); scale = vector_multiply( forward, radius ); line( org, org + scale, ( 1, 0.3, 0.3 ) ); } wait( 0.05 ); } } printDisplaceInfo() { self endon( "death" ); self notify( "displaceprint" ); self endon( "displaceprint" ); for ( ;; ) { print3d( self.origin + ( 0, 0, 60 ), "displacer", ( 0, 0.4, 0.7 ), 0.85, 0.5 ); wait( 0.05 ); } } // Returns whether or not the character should be acting like he's under fire or expecting an enemy to appear // any second. IsInCombat() { if ( self.alertLevelInt > 1 ) return true; if ( isdefined( self.enemy ) ) { self.a.combatEndTime = gettime() + anim.combatMemoryTimeConst + randomint( anim.combatMemoryTimeRand ); return true; } return( self.a.combatEndTime > gettime() ); } /# DebugIsInCombat() { return( self.a.combatEndTime > gettime() ); } #/ GetEnemyEyePos() { if ( isdefined( self.enemy ) ) { self.a.lastEnemyPos = self.enemy getShootAtPos(); self.a.lastEnemyTime = gettime(); return self.a.lastEnemyPos; } else if ( ( isDefined( self.a.lastEnemyTime ) ) && ( isDefined( self.a.lastEnemyPos ) ) && ( self.a.lastEnemyTime + 3000 < gettime() ) ) { return self.a.lastEnemyPos; } else { // Return a point in front of you. Note that the distance to this point is significant, because // this function is used to determine an appropriate attack stance. 16 feet( 196 units ) seems good... targetPos = self getShootAtPos(); targetPos = targetPos + ( 196 * self.lookforward[ 0 ], 196 * self.lookforward[ 1 ], 196 * self.lookforward[ 2 ] ); return targetPos; } } GetNodeForwardYaw( node ) { if ( !isdefined( self.heat ) ) { if ( node.type == "Cover Left" ) return node.angles[1] + 90; else if ( node.type == "Cover Right" ) return node.angles[1] - 90; } return node.angles[1]; } GetNodeYawToOrigin( pos ) { if ( isdefined( self.node ) ) yaw = self.node.angles[ 1 ] - GetYaw( pos ); else yaw = self.angles[ 1 ] - GetYaw( pos ); yaw = AngleClamp180( yaw ); return yaw; } GetNodeYawToEnemy() { pos = undefined; if ( isdefined( self.enemy ) ) { pos = self.enemy.origin; } else { if ( isdefined( self.node ) ) forward = anglestoforward( self.node.angles ); else forward = anglestoforward( self.angles ); forward = vector_multiply( forward, 150 ); pos = self.origin + forward; } if ( isdefined( self.node ) ) yaw = self.node.angles[ 1 ] - GetYaw( pos ); else yaw = self.angles[ 1 ] - GetYaw( pos ); yaw = AngleClamp180( yaw ); return yaw; } GetYawToSpot( spot ) { yaw = self.angles[ 1 ] - GetYaw( spot ); yaw = AngleClamp180( yaw ); return yaw; } // warning! returns (my yaw - yaw to enemy) instead of (yaw to enemy - my yaw) GetYawToEnemy() { pos = undefined; if ( isdefined( self.enemy ) ) { pos = self.enemy.origin; } else { forward = anglestoforward( self.angles ); forward = vector_multiply( forward, 150 ); pos = self.origin + forward; } yaw = self.angles[ 1 ] - GetYaw( pos ); yaw = AngleClamp180( yaw ); return yaw; } GetYaw( org ) { return VectorToYaw( org - self.origin ); } GetYaw2d( org ) { angles = VectorToAngles( ( org[ 0 ], org[ 1 ], 0 ) - ( self.origin[ 0 ], self.origin[ 1 ], 0 ) ); return angles[ 1 ]; } // 0 if I'm facing my enemy, 90 if I'm side on, 180 if I'm facing away. AbsYawToEnemy() { assert( isdefined( self.enemy ) ); yaw = self.angles[ 1 ] - GetYaw( self.enemy.origin ); yaw = AngleClamp180( yaw ); if ( yaw < 0 ) yaw = -1 * yaw; return yaw; } // 0 if I'm facing my enemy, 90 if I'm side on, 180 if I'm facing away. AbsYawToEnemy2d() { assert( isdefined( self.enemy ) ); yaw = self.angles[ 1 ] - GetYaw2d( self.enemy.origin ); yaw = AngleClamp180( yaw ); if ( yaw < 0 ) yaw = -1 * yaw; return yaw; } // 0 if I'm facing my enemy, 90 if I'm side on, 180 if I'm facing away. AbsYawToOrigin( org ) { yaw = self.angles[ 1 ] - GetYaw( org ); yaw = AngleClamp180( yaw ); if ( yaw < 0 ) yaw = -1 * yaw; return yaw; } AbsYawToAngles( angles ) { yaw = self.angles[ 1 ] - angles; yaw = AngleClamp180( yaw ); if ( yaw < 0 ) yaw = -1 * yaw; return yaw; } GetYawFromOrigin( org, start ) { angles = VectorToAngles( org - start ); return angles[ 1 ]; } GetYawToTag( tag, org ) { yaw = self gettagangles( tag )[ 1 ] - GetYawFromOrigin( org, self gettagorigin( tag ) ); yaw = AngleClamp180( yaw ); return yaw; } GetYawToOrigin( org ) { yaw = self.angles[ 1 ] - GetYaw( org ); yaw = AngleClamp180( yaw ); return yaw; } GetEyeYawToOrigin( org ) { yaw = self gettagangles( "TAG_EYE" )[ 1 ] - GetYaw( org ); yaw = AngleClamp180( yaw ); return yaw; } isStanceAllowedWrapper( stance ) { if ( isdefined( self.coverNode ) ) return self.coverNode doesNodeAllowStance( stance ); return self isStanceAllowed( stance ); } choosePose( preferredPose ) { if ( !isDefined( preferredPose ) ) { preferredPose = self.a.pose; } // Find out if we should be standing, crouched or prone switch( preferredPose ) { case "stand": if ( self isStanceAllowedWrapper( "stand" ) ) { resultPose = "stand"; } else if ( self isStanceAllowedWrapper( "crouch" ) ) { resultPose = "crouch"; } else if ( self isStanceAllowedWrapper( "prone" ) ) { resultPose = "prone"; } else { println( "No stance allowed! Remaining standing." ); resultPose = "stand"; } break; case "crouch": if ( self isStanceAllowedWrapper( "crouch" ) ) { resultPose = "crouch"; } else if ( self isStanceAllowedWrapper( "stand" ) ) { resultPose = "stand"; } else if ( self isStanceAllowedWrapper( "prone" ) ) { resultPose = "prone"; } else { println( "No stance allowed! Remaining crouched." ); resultPose = "crouch"; } break; case "prone": if ( self isStanceAllowedWrapper( "prone" ) ) { resultPose = "prone"; } else if ( self isStanceAllowedWrapper( "crouch" ) ) { resultPose = "crouch"; } else if ( self isStanceAllowedWrapper( "stand" ) ) { resultPose = "stand"; } else { println( "No stance allowed! Remaining prone." ); resultPose = "prone"; } break; default: println( "utility::choosePose, called in " + self.script + " script: Unhandled anim_pose " + self.a.pose + " - using stand." ); resultPose = "stand"; break; } return resultPose; } GetClaimedNode() { myNode = self.node; if ( isdefined( myNode ) && ( self nearNode( myNode ) || ( isdefined( self.coverNode ) && myNode == self.coverNode ) ) ) return myNode; return undefined; } GetNodeType() { myNode = GetClaimedNode(); if ( isDefined( myNode ) ) return myNode.type; return "none"; } GetNodeDirection() { myNode = GetClaimedNode(); if ( isdefined( myNode ) ) { // thread [[ anim.println ]]( "GetNodeDirection found node, returned: " + myNode.angles[ 1 ] );#/ return myNode.angles[ 1 ]; } // thread [[ anim.println ]]( "GetNodeDirection didn't find node, returned: " + self.desiredAngle );#/ return self.desiredAngle; } GetNodeForward() { myNode = GetClaimedNode(); if ( isdefined( myNode ) ) return AnglesToForward( myNode.angles ); return AnglesToForward( self.angles ); } GetNodeOrigin() { myNode = GetClaimedNode(); if ( isdefined( myNode ) ) return myNode.origin; return self.origin; } /# isDebugOn() { return( ( getdebugdvarint( "animDebug" ) == 1 ) || ( isDefined( anim.debugEnt ) && anim.debugEnt == self ) ); } drawDebugLineInternal( fromPoint, toPoint, color, durationFrames ) { // println( "Drawing line, color " + color[ 0 ] + ", " + color[ 1 ] + ", " + color[ 2 ] ); // player = getentarray( "player", "classname" )[0]; // println( "Point1 : " + fromPoint + ", Point2: " + toPoint + ", player: " + player.origin ); for ( i = 0;i < durationFrames;i++ ) { line( fromPoint, toPoint, color ); wait( 0.05 ); } } drawDebugLine( fromPoint, toPoint, color, durationFrames ) { if ( isDebugOn() ) thread drawDebugLineInternal( fromPoint, toPoint, color, durationFrames ); } debugLine( fromPoint, toPoint, color, durationFrames ) { for ( i = 0;i < durationFrames * 20;i++ ) { line( fromPoint, toPoint, color ); wait( 0.05 ); } } drawDebugCross( atPoint, radius, color, durationFrames ) { atPoint_high = atPoint + ( 0, 0, radius ); atPoint_low = atPoint + ( 0, 0, - 1 * radius ); atPoint_left = atPoint + ( 0, radius, 0 ); atPoint_right = atPoint + ( 0, - 1 * radius, 0 ); atPoint_forward = atPoint + ( radius, 0, 0 ); atPoint_back = atPoint + ( -1 * radius, 0, 0 ); thread debugLine( atPoint_high, atPoint_low, color, durationFrames ); thread debugLine( atPoint_left, atPoint_right, color, durationFrames ); thread debugLine( atPoint_forward, atPoint_back, color, durationFrames ); } drawDebugCrossOld( atPoint, radius, color, durationFrames ) { if ( thread isDebugOn() ) { atPoint_high = atPoint + ( 0, 0, radius ); atPoint_low = atPoint + ( 0, 0, - 1 * radius ); atPoint_left = atPoint + ( 0, radius, 0 ); atPoint_right = atPoint + ( 0, - 1 * radius, 0 ); atPoint_forward = atPoint + ( radius, 0, 0 ); atPoint_back = atPoint + ( -1 * radius, 0, 0 ); thread animscripts\utility::drawDebugLine( atPoint_high, atPoint_low, color, durationFrames ); thread animscripts\utility::drawDebugLine( atPoint_left, atPoint_right, color, durationFrames ); thread animscripts\utility::drawDebugLine( atPoint_forward, atPoint_back, color, durationFrames ); } } UpdateDebugInfoInternal() { if ( isDefined( anim.debugEnt ) && ( anim.debugEnt == self ) ) doInfo = true; else doInfo = getdebugdvarInt( "animscriptinfo" ); if ( doInfo ) { thread drawDebugInfoThread(); } else { self notify( "EndDebugInfo" ); } } UpdateDebugInfo() { self endon( "death" ); for ( ;; ) { thread UpdateDebugInfoInternal(); wait 1; } } drawDebugInfoThread() { self endon( "EndDebugInfo" ); self endon( "death" ); for ( ;; ) { self thread drawDebugInfo(); wait 0.05; } } drawDebugInfo() { // What do we want to print? line[ 0 ] = self getEntityNumber() + " " + self.script; line[ 1 ] = self.a.pose + " " + self.a.movement; if ( self thread DebugIsInCombat() ) line[ 2 ] = "in combat for " + ( self.a.combatEndTime - gettime() ) + " ms."; else line[ 2 ] = "not in combat"; line[ 3 ] = self.a.lastDebugPrint1; belowFeet = self.origin + ( 0, 0, -8 ); // aboveHead = self getShootAtPos() + ( 0, 0, 8 ); offset = ( 0, 0, -10 ); for ( i = 0 ; i < line.size ; i++ ) { if ( isDefined( line[ i ] ) ) { textPos = ( belowFeet[ 0 ] + ( offset[ 0 ] * i ), belowFeet[ 1 ] + ( offset[ 1 ] * i ), belowFeet[ 2 ] + ( offset[ 2 ] * i ) ); print3d( textPos, line[ i ], ( .2, .2, 1 ), 1, 0.75 ); // origin, text, RGB, alpha, scale } } } #/ safemod( a, b ) { /* Here are some modulus results from in - game: 10 % 3 = 1 10 % - 3 = 1 - 10 % 3 = -1 - 10 % - 3 = -1 however, we never want a negative result. */ result = int( a ) % b; result += b; return result % b; } AbsAngleClamp180( angle ) { return abs( AngleClamp180( angle ) ); } // Returns an array of 4 weights( 2 of which are guaranteed to be 0 ), which should be applied to forward, // right, back and left animations to get the angle specified. // front // / -- -- | -- -- \ // / 180 \ // / \ | / \ // / - 135 | 135 \ // | \ | / | // left| - 90 -- -- + -- -- 90 - |right // | / | \ | // \ - 45 | 45 / // \ / | \ / // \ 0 / // \ -- -- | -- -- / // back QuadrantAnimWeights( yaw ) { forwardWeight = cos( yaw ); leftWeight = sin( yaw ); result[ "front" ] = 0; result[ "right" ] = 0; result[ "back" ] = 0; result[ "left" ] = 0; if ( isdefined( self.alwaysRunForward ) ) { assert( self.alwaysRunForward );// always set alwaysRunForward to either true or undefined. result[ "front" ] = 1; return result; } if ( forwardWeight > 0 ) { if ( leftWeight > forwardWeight ) result[ "left" ] = 1; else if ( leftWeight < -1 * forwardWeight ) result[ "right" ] = 1; else result[ "front" ] = 1; } else { // if moving backwards, don't blend. // it looks horrible because the feet cycle in the opposite direction. // either way, feet slide, but this looks better. backWeight = -1 * forwardWeight; if ( leftWeight > backWeight ) result[ "left" ] = 1; else if ( leftWeight < forwardWeight ) result[ "right" ] = 1; else result[ "back" ] = 1; } return result; } getQuadrant( angle ) { angle = AngleClamp( angle ); if ( angle < 45 || angle > 315 ) { quadrant = "front"; } else if ( angle < 135 ) { quadrant = "left"; } else if ( angle < 225 ) { quadrant = "back"; } else { quadrant = "right"; } return quadrant; } // Checks to see if the input is equal to any of up to ten other inputs. IsInSet( input, set ) { for ( i = set.size - 1; i >= 0; i -- ) { if ( input == set[ i ] ) return true; } return false; } playAnim( animation ) { if ( isDefined( animation ) ) { // self thread drawString( animation ); // Doesn't work for animations, only strings. println( "NOW PLAYING: ", animation ); self setFlaggedAnimKnobAllRestart( "playAnim", animation, %root, 1, .1, 1 ); timeToWait = getanimlength( animation ); timeToWait = ( 3 * timeToWait ) + 1; // So looping animations play through 3 times. self thread NotifyAfterTime( "time is up", "time is up", timeToWait ); self waittill( "time is up" ); self notify( "enddrawstring" ); } } NotifyAfterTime( notifyString, killmestring, time ) { self endon( "death" ); self endon( killmestring ); wait time; self notify( notifyString ); } // Utility function, made for MilestoneAnims(), which displays a string until killed. drawString( stringtodraw ) { self endon( "killanimscript" ); self endon( "enddrawstring" ); for ( ;; ) { wait .05; print3d( ( self GetDebugEye() ) + ( 0, 0, 8 ), stringtodraw, ( 1, 1, 1 ), 1, 0.2 ); } } drawStringTime( msg, org, color, timer ) { maxtime = timer * 20; for ( i = 0;i < maxtime;i++ ) { print3d( org, msg, color, 1, 1 ); wait .05; } } showLastEnemySightPos( string ) { self notify( "got known enemy2" ); self endon( "got known enemy2" ); self endon( "death" ); if ( !isdefined( self.enemy ) ) return; if ( self.enemy.team == "allies" ) color = ( 0.4, 0.7, 1 ); else color = ( 1, 0.7, 0.4 ); while ( 1 ) { wait( 0.05 ); if ( !isdefined( self.lastEnemySightPos ) ) continue; print3d( self.lastEnemySightPos, string, color, 1, 2.15 ); // origin, text, RGB, alpha, scale } } /# printDebugTextProc( string, org, printTime, color ) { level notify( "stop debug print " + org ); level endon( "stop debug print " + org ); if ( !isdefined( color ) ) color = ( 0.3, 0.9, 0.6 ); timer = printTime * 20; for ( i = 0;i < timer;i += 1 ) { wait( 0.05 ); print3d( org, string, color, 1, 1 ); // origin, text, RGB, alpha, scale } } printDebugText( string, org, printTime, color ) { if ( getdebugdvar( "anim_debug" ) != "" ) level thread printDebugTextProc( string, org, printTime, color ); } #/ hasEnemySightPos() { if ( isdefined( self.node ) ) return( canSeeEnemyFromExposed() || canSuppressEnemyFromExposed() ); else return( canSeeEnemy() || canSuppressEnemy() ); } getEnemySightPos() { return self.goodShootPos; } util_ignoreCurrentSightPos() { if ( !hasEnemySightPos() ) return; self.ignoreSightPos = getEnemySightPos(); self.ignoreOrigin = self.origin; } util_evaluateKnownEnemyLocation() { if ( !hasEnemySightPos() ) return false; myGunPos = self getMuzzlePos(); myEyeOffset = ( self getShootAtPos() - myGunPos ); if ( ( isdefined( self.ignoreSightPos ) ) && ( isdefined( self.ignoreOrigin ) ) ) { // Ignore the current last sight pos if you've previously invalidated it from this position if ( distance( self.origin, self.ignoreOrigin ) < 25 ) return false; } self.ignoreSightPos = undefined; canSee = self canshoot( getEnemySightPos(), myEyeOffset ); if ( !canSee ) { self.ignoreSightPos = getEnemySightPos(); return false; } return true; } debugTimeout() { wait( 5 ); self notify( "timeout" ); } debugPosInternal( org, string, size ) { self endon( "death" ); self notify( "stop debug " + org ); self endon( "stop debug " + org ); ent = spawnstruct(); ent thread debugTimeout(); ent endon( "timeout" ); if ( self.enemy.team == "allies" ) color = ( 0.4, 0.7, 1 ); else color = ( 1, 0.7, 0.4 ); while ( 1 ) { wait( 0.05 ); print3d( org, string, color, 1, size ); // origin, text, RGB, alpha, scale } } debugPos( org, string ) { thread debugPosInternal( org, string, 2.15 ); } debugPosSize( org, string, size ) { thread debugPosInternal( org, string, size ); } debugBurstPrint( numShots, maxShots ) { burstSize = numShots / maxShots; burstSizeStr = undefined; if ( numShots == self.bulletsInClip ) burstSizeStr = "all rounds"; else if ( burstSize < 0.25 ) burstSizeStr = "small burst"; else if ( burstSize < 0.5 ) burstSizeStr = "med burst"; else burstSizeStr = "long burst"; thread animscripts\utility::debugPosSize( self.origin + ( 0, 0, 42 ), burstSizeStr, 1.5 ); thread animscripts\utility::debugPos( self.origin + ( 0, 0, 60 ), "Suppressing" ); } printShootProc() { self endon( "death" ); self notify( "stop shoot " + self.export ); self endon( "stop shoot " + self.export ); printTime = 0.25; timer = printTime * 20; for ( i = 0;i < timer;i += 1 ) { wait( 0.05 ); print3d( self.origin + ( 0, 0, 70 ), "Shoot", ( 1, 0, 0 ), 1, 1 ); // origin, text, RGB, alpha, scale } } printShoot() { /# if ( getdebugdvar( "anim_debug" ) == "3" ) self thread printShootProc(); #/ } showDebugProc( fromPoint, toPoint, color, printTime ) { self endon( "death" ); // self notify( "stop debugline " + self.export ); // self endon( "stop debugline " + self.export ); timer = printTime * 20; for ( i = 0;i < timer;i += 1 ) { wait( 0.05 ); line( fromPoint, toPoint, color ); } } showDebugLine( fromPoint, toPoint, color, printTime ) { self thread showDebugProc( fromPoint, toPoint + ( 0, 0, -5 ), color, printTime ); } shootEnemyWrapper() { [[ anim.shootEnemyWrapper_func ]](); } shootEnemyWrapper_normal() { self.a.lastShootTime = gettime(); // set accuracy at time of shoot rather than in a separate thread that is vulnerable to timing issues maps\_gameskill::set_accuracy_based_on_situation(); self notify( "shooting" ); self shoot(); } shootEnemyWrapper_shootNotify() { level notify( "an_enemy_shot", self ); shootEnemyWrapper_normal(); } shootPosWrapper( shootPos ) { endpos = bulletSpread( self getMuzzlePos(), shootPos, 4 ); self.a.lastShootTime = gettime(); self notify( "shooting" ); self shoot( 1, endpos ); } throwGun() { org = spawn( "script_model", ( 0, 0, 0 ) ); org setmodel( "temp" ); org.origin = self getTagOrigin( "tag_weapon_right" ) + ( 50, 50, 0 ); org.angles = self getTagAngles( "tag_weapon_right" ); right = anglestoright( org.angles ); right = vector_multiply( right, 15 ); forward = anglestoforward( org.angles ); forward = vector_multiply( forward, 15 ); org moveGravity( ( 0, 50, 150 ), 100 ); weaponClass = "weapon_" + self.weapon; weapon = spawn( weaponClass, org.origin ); weapon.angles = self getTagAngles( "tag_weapon_right" ); weapon linkto( org ); // org rotateVelocity( ( 100, 0, 0 ), 12 ); lastOrigin = org.origin; while ( ( isdefined( weapon ) ) && ( isdefined( weapon.origin ) ) ) { start = lastOrigin; end = org.origin; angles = vectortoangles( end - start ); forward = anglestoforward( angles ); forward = vector_multiply( forward, 4 ); trace = bulletTrace( end, end + forward, true, weapon ); if ( isalive( trace[ "entity" ] ) && trace[ "entity" ] == self ) { wait( 0.05 ); continue; } if ( trace[ "fraction" ] < 1.0 ) break; lastOrigin = org.origin; wait( 0.05 ); } /* if( isdefined( trace[ "entity" ] ) ) { if( isSentient( trace[ "entity" ] ) ) trace[ "entity" ] DoDamage( 300, weapon.origin ); } */ if ( ( isdefined( weapon ) ) && ( isdefined( weapon.origin ) ) ) weapon unlink(); org delete(); } setEnv( env ) { anim.idleAnimTransition [ "stand" ][ "in" ] = %casual_stand_idle_trans_in; // anim.idleAnimTransition [ "stand" ][ "out" ] = %casual_stand_idle_trans_out; anim.idleAnimArray [ "stand" ][ 0 ][ 0 ] = %casual_stand_idle; anim.idleAnimArray [ "stand" ][ 0 ][ 1 ] = %casual_stand_idle_twitch; anim.idleAnimArray [ "stand" ][ 0 ][ 2 ] = %casual_stand_idle_twitchB; anim.idleAnimWeights [ "stand" ][ 0 ][ 0 ] = 2; anim.idleAnimWeights [ "stand" ][ 0 ][ 1 ] = 1; anim.idleAnimWeights [ "stand" ][ 0 ][ 2 ] = 1; anim.idleAnimArray [ "stand" ][ 1 ][ 0 ] = %casual_stand_v2_idle; anim.idleAnimArray [ "stand" ][ 1 ][ 1 ] = %casual_stand_v2_twitch_radio; anim.idleAnimArray [ "stand" ][ 1 ][ 2 ] = %casual_stand_v2_twitch_shift; anim.idleAnimArray [ "stand" ][ 1 ][ 3 ] = %casual_stand_v2_twitch_talk; anim.idleAnimWeights [ "stand" ][ 1 ][ 0 ] = 10; anim.idleAnimWeights [ "stand" ][ 1 ][ 1 ] = 4; anim.idleAnimWeights [ "stand" ][ 1 ][ 2 ] = 7; anim.idleAnimWeights [ "stand" ][ 1 ][ 3 ] = 4; anim.idleAnimArray [ "stand_cqb" ][ 0 ][ 0 ] = %cqb_stand_idle; anim.idleAnimArray [ "stand_cqb" ][ 0 ][ 1 ] = %cqb_stand_twitch; anim.idleAnimWeights [ "stand_cqb" ][ 0 ][ 0 ] = 2; anim.idleAnimWeights [ "stand_cqb" ][ 0 ][ 1 ] = 1; anim.idleAnimTransition [ "crouch" ][ "in" ] = %casual_crouch_idle_in; // anim.idleAnimTransition [ "crouch" ][ "out" ] = %casual_crouch_idle_out; anim.idleAnimArray [ "crouch" ][ 0 ][ 0 ] = %casual_crouch_idle; //anim.idleAnimArray [ "crouch" ][ 0 ][ 1 ] = %casual_crouch_twitch; // %casual_crouch_point anim.idleAnimWeights [ "crouch" ][ 0 ][ 0 ] = 6; //anim.idleAnimWeights [ "crouch" ][ 0 ][ 1 ] = 3; } /* ============= ///ScriptDocBegin "Name: PersonalColdBreath()" "Summary: Makes cold breath effect play on an AI" "Module: AI" "CallOn: An AI" "Example: level.price thread PersonalColdBreath()" "SPMP: singleplayer" ///ScriptDocEnd ============= */ PersonalColdBreath() { tag = "TAG_EYE"; self endon( "death" ); self notify( "stop personal effect" ); self endon( "stop personal effect" ); while ( isdefined( self ) ) { wait( 0.05 ); if( !isdefined( self ) ) break; //only play cold breath when stopped if ( ( isdefined( self.a.movement ) ) && ( self.a.movement == "stop" ) ) { //don't play cold breath if indoors if ( ( isdefined( self.isindoor ) ) && ( self.isindoor == 1 ) ) continue; playfxOnTag( level._effect[ "cold_breath" ], self, tag ); wait( 2.5 + randomfloat( 3 ) ); } else wait( 0.5 ); } } /* ============= ///ScriptDocBegin "Name: PersonalColdBreathStop()" "Summary: Stops cold breath effect playing on an AI" "Module: AI" "CallOn: An AI" "Example: level.price thread PersonalColdBreathStop()" "SPMP: singleplayer" ///ScriptDocEnd ============= */ PersonalColdBreathStop() { self notify( "stop personal effect" ); } PersonalColdBreathSpawner() { self endon( "death" ); self notify( "stop personal effect" ); self endon( "stop personal effect" ); for ( ;; ) { self waittill( "spawned", spawn ); if ( maps\_utility::spawn_failed( spawn ) ) continue; spawn thread PersonalColdBreath(); } } isSuppressedWrapper() { if ( isdefined( self.forceSuppression ) ) return self.forceSuppression; if ( self.suppressionMeter <= self.suppressionThreshold ) return false; return self issuppressed(); // takes into account .ignoreSuppression } // if not suppressed, sometimes we still want to look cautious, like leaning out of a corner instead of stepping out. // this determines whether we should do that or not. isPartiallySuppressedWrapper() { if ( self.suppressionMeter <= self.suppressionThreshold * 0.25 ) return false; return( self issuppressed() ); // takes into account .ignoreSuppression } getNodeOffset( node ) { if ( isdefined( node.offset ) ) return node.offset; // ( right offset, forward offset, vertical offset ) // you can get an actor's current eye offset by setting scr_eyeoffset to his entnum. // this should be redone whenever animations change significantly. cover_left_crouch_offset = ( -26, .4, 36 ); cover_left_stand_offset = ( -32, 7, 63 ); cover_right_crouch_offset = ( 43.5, 11, 36 ); cover_right_stand_offset = ( 36, 8.3, 63 ); cover_crouch_offset = ( 3.5, -12.5, 45 );// maybe we could account for the fact that in cover crouch he can stand if he needs to? cover_stand_offset = ( -3.7, -22, 63 ); cornernode = false; nodeOffset = ( 0, 0, 0 ); right = anglestoright( node.angles ); forward = anglestoforward( node.angles ); switch( node.type ) { case "Cover Left": if ( node getHighestNodeStance() == "crouch" ) nodeOffset = calculateNodeOffset( right, forward, cover_left_crouch_offset ); else nodeOffset = calculateNodeOffset( right, forward, cover_left_stand_offset ); break; case "Cover Right": if ( node getHighestNodeStance() == "crouch" ) nodeOffset = calculateNodeOffset( right, forward, cover_right_crouch_offset ); else nodeOffset = calculateNodeOffset( right, forward, cover_right_stand_offset ); break; case "Cover Stand": case "Conceal Stand": case "Turret": nodeOffset = calculateNodeOffset( right, forward, cover_stand_offset ); break; case "Cover Crouch": case "Cover Crouch Window": case "Conceal Crouch": nodeOffset = calculateNodeOffset( right, forward, cover_crouch_offset ); break; } node.offset = nodeOffset; return node.offset; } calculateNodeOffset( right, forward, baseoffset ) { return vector_multiply( right, baseoffset[ 0 ] ) + vector_multiply( forward, baseoffset[ 1 ] ) + ( 0, 0, baseoffset[ 2 ] ); } recentlySawEnemy() { return( isdefined( self.enemy ) && self seeRecently( self.enemy, 5 ) ); } canSeeEnemy( cacheDuration ) { if ( !isdefined( self.enemy ) ) return false; if ( (isDefined( cacheDuration ) && self canSee( self.enemy, cacheDuration )) || self canSee( self.enemy ) ) { if ( !checkPitchVisibility( self geteye(), self.enemy getshootatpos() ) ) return false; self.goodShootPos = GetEnemyEyePos(); dontGiveUpOnSuppressionYet(); return true; } return false; } canSeeEnemyFromExposed() { //prof_begin( "canSeeEnemyFromExposed" ); if ( !isdefined( self.enemy ) ) { self.goodShootPos = undefined; //prof_end( "canSeeEnemyFromExposed" ); return false; } enemyEye = GetEnemyEyePos(); if ( !isDefined( self.node ) ) { result = self canSee( self.enemy ); } else { result = canSeePointFromExposedAtNode( enemyEye, self.node ); } if ( result ) { self.goodShootPos = enemyEye; dontGiveUpOnSuppressionYet(); } else { /# if ( self getentnum() == getdebugdvarint( "anim_dotshow" ) ) thread persistentDebugLine( self.node.origin + getNodeOffset( self.node ), enemyEye ); #/ } //prof_end( "canSeeEnemyFromExposed" ); return result; } canSeePointFromExposedAtNode( point, node ) { if ( node.type == "Cover Left" || node.type == "Cover Right" ) { if ( !self animscripts\corner::canSeePointFromExposedAtCorner( point, node ) ) return false; } nodeOffset = getNodeOffset( node ); lookFromPoint = node.origin + nodeOffset; if ( !checkPitchVisibility( lookFromPoint, point, node ) ) return false; if ( !sightTracePassed( lookFromPoint, point, false, undefined ) ) { if ( node.type == "Cover Crouch" || node.type == "Conceal Crouch" ) { // also consider the ability to stand at crouch nodes lookFromPoint = ( 0, 0, 64 ) + node.origin; return sightTracePassed( lookFromPoint, point, false, undefined ); } return false; } return true; } // check vertical angle is within our aiming abilities checkPitchVisibility( fromPoint, toPoint, atNode ) { // Compute Aiming Limits + Tolerance minPitch = self.downAimLimit - anim.aimPitchDiffTolerance; maxPitch = self.upAimLimit + anim.aimPitchDiffTolerance; pitch = AngleClamp180( vectorToAngles( toPoint - fromPoint )[ 0 ] ); if( pitch > maxPitch ) return false; if ( pitch < minPitch ) { if ( isdefined( atNode ) && atNode.type != "Cover Crouch" && atNode.type != "Conceal Crouch" ) return false; if ( pitch < (anim.coverCrouchLeanPitch + minPitch) ) return false; } return true; } dontGiveUpOnSuppressionYet() { // we'll reset the giveUpOnSuppression timer the next time we want to suppress self.a.shouldResetGiveUpOnSuppressionTimer = true; } updateGiveUpOnSuppressionTimer() { if ( !isdefined( self.a.shouldResetGiveUpOnSuppressionTimer ) ) self.a.shouldResetGiveUpOnSuppressionTimer = true; if ( self.a.shouldResetGiveUpOnSuppressionTimer ) { // after this time, we will decide that our enemy might not be where we thought they were // this will cause us to look for better cover self.a.giveUpOnSuppressionTime = gettime() + randomintrange( 15000, 30000 ); self.a.shouldResetGiveUpOnSuppressionTimer = false; } } showLines( start, end, end2 ) { for ( ;; ) { line( start, end, ( 1, 0, 0 ), 1 ); wait( 0.05 ); line( start, end2, ( 0, 0, 1 ), 1 ); wait( 0.05 ); } } aiSuppressAI() { if ( !self canAttackEnemyNode() ) return false; shootPos = undefined; if ( isdefined( self.enemy.node ) ) { nodeOffset = getNodeOffset( self.enemy.node ); shootPos = self.enemy.node.origin + nodeOffset; } else shootPos = self.enemy getShootAtPos(); // canAttackEnemyNode sometimes returns true even though we can't see the point, because // our eye pos is not right at our node's offset if ( !self canShoot( shootPos ) ) return false; if ( self.script == "combat" ) { // make sure we can also see the tip of our gun if ( !sighttracepassed( self geteye(), self getMuzzlePos(), false, undefined ) ) return false; } self.goodShootPos = shootPos; return true; } canSuppressEnemyFromExposed() { // FromExposed includes checking from the offset of the node the AI is at if ( !hasSuppressableEnemy() ) { self.goodShootPos = undefined; return false; } if ( !isplayer( self.enemy ) ) return aiSuppressAI(); if ( isdefined( self.node ) ) { if ( self.node.type == "Cover Left" || self.node.type == "Cover Right" ) { // Don't try to shoot at stuff behind the node if ( !self animscripts\corner::canSeePointFromExposedAtCorner( self GetEnemyEyePos(), self.node ) ) return false; } nodeOffset = getNodeOffset( self.node ); startOffset = self.node.origin + nodeOffset; } else startOffset = self getMuzzlePos(); if ( !checkPitchVisibility( startOffset, self.lastEnemySightPos ) ) return false; return findGoodSuppressSpot( startOffset ); } canSuppressEnemy() { if ( !hasSuppressableEnemy() ) { self.goodShootPos = undefined; return false; } if ( !isplayer( self.enemy ) ) return aiSuppressAI(); startOffset = self getMuzzlePos(); if ( !checkPitchVisibility( startOffset, self.lastEnemySightPos ) ) return false; return findGoodSuppressSpot( startOffset ); } hasSuppressableEnemy() { if ( !isdefined( self.enemy ) ) return false; if ( !isdefined( self.lastEnemySightPos ) ) return false; updateGiveUpOnSuppressionTimer(); if ( gettime() > self.a.giveUpOnSuppressionTime ) return false; if ( !needRecalculateSuppressSpot() ) return isdefined( self.goodShootPos ); return true; } canSeeAndShootPoint( point ) { if ( !sightTracePassed( self getShootAtPos(), point, false, undefined ) ) return false; if ( self.a.weaponPos[ "right" ] == "none" ) return false; gunpoint = self getMuzzlePos(); return sightTracePassed( gunpoint, point, false, undefined ); } needRecalculateSuppressSpot() { if ( isdefined( self.goodShootPos ) && !self canSeeAndShootPoint( self.goodShootPos ) ) return true; // we need to recalculate the suppress spot // if we've moved or if we saw our enemy in a different place than when we // last calculated it return( !isdefined( self.lastEnemySightPosOld ) || self.lastEnemySightPosOld != self.lastEnemySightPos || distanceSquared( self.lastEnemySightPosSelfOrigin, self.origin ) > 1024// 1024 = 32 * 32 ); } findGoodSuppressSpot( startOffset ) { if ( !needRecalculateSuppressSpot() ) return isdefined( self.goodShootPos ); if ( isdefined( self.enemy ) && distanceSquared( self.origin, self.enemy.origin ) > squared( self.enemy.maxVisibleDist ) ) { self.goodShootPos = undefined; return false; } // make sure we can see from our eye to our gun; if we can't then we really shouldn't be trying to suppress at all! if ( !sightTracePassed( self getShootAtPos(), startOffset, false, undefined ) ) { self.goodShootPos = undefined; return false; } self.lastEnemySightPosSelfOrigin = self.origin; self.lastEnemySightPosOld = self.lastEnemySightPos; currentEnemyPos = GetEnemyEyePos(); trace = bullettrace( self.lastEnemySightPos, currentEnemyPos, false, undefined ); startTracesAt = trace[ "position" ]; percievedMovementVector = self.lastEnemySightPos - startTracesAt; lookVector = vectorNormalize( self.lastEnemySightPos - startOffset ); percievedMovementVector = percievedMovementVector - vector_multiply( lookVector, vectorDot( percievedMovementVector, lookVector ) ); // percievedMovementVector is what self.lastEnemySightPos - startTracesAt looks like from our position( that is, projected perpendicular to the direction we're looking ). idealTraceInterval = 20.0; numTraces = int( length( percievedMovementVector ) / idealTraceInterval + 0.5 );// one trace every 20 units, ideally if ( numTraces < 1 ) numTraces = 1; if ( numTraces > 20 ) numTraces = 20;// cap it vectorDif = self.lastEnemySightPos - startTracesAt; vectorDif = ( vectorDif[ 0 ] / numTraces, vectorDif[ 1 ] / numTraces, vectorDif[ 2 ] / numTraces ); numTraces++ ;// to get both start and end points for traces traceTo = startTracesAt; /# if ( getdebugdvarint( "debug_dotshow" ) == self getentnum() ) { thread print3dtime( 15, self.lastEnemySightPos, "lastpos", ( 1, .2, .2 ), 1, 0.75 ); // origin, text, RGB, alpha, scale thread print3dtime( 15, startTracesAt, "currentpos", ( 1, .2, .2 ), 1, 0.75 ); // origin, text, RGB, alpha, scale } #/ self.goodShootPos = undefined; goodTraces = 0; neededGoodTraces = 2;// we stop at 3 good traces away from the cover where they disappeared, should be about 40 units for ( i = 0; i < numTraces + neededGoodTraces; i++ ) { tracePassed = sightTracePassed( startOffset, traceTo, false, undefined ); thisTraceTo = traceTo; /# if ( getdebugdvarint( "debug_dotshow" ) == self getentnum() ) { if ( tracePassed ) color = ( .2, .2, 1 ); else color = ( .2, .2, .2 ); // showDebugLine( startOffset, traceTo, color, 0.75 ); thread print3dtime( 15, traceTo, ".", color, 1, 0.75 ); // origin, text, RGB, alpha, scale } #/ // after we've hit self.lastEnemySightPos, look only perpendicular to our line of sight if ( i == numTraces - 1 ) { vectorDif = vectorDif - vector_multiply( lookVector, vectorDot( vectorDif, lookVector ) ); } traceTo += vectorDif;// for next time if ( tracePassed ) { goodTraces++ ; self.goodShootPos = thisTraceTo; // if first trace succeeded, we take it, because it probably means they're crouched under cover and we can shoot over it if ( i > 0 && goodTraces < neededGoodTraces && i < numTraces + neededGoodTraces - 1 ) continue; return true; } else { goodTraces = 0; } } return isdefined( self.goodShootPos ); } // Returns an animation from an array of animations with a corrosponding array of weights. anim_array( animArray, animWeights ) { total_anims = animArray.size; idleanim = randomint( total_anims ); assert( total_anims ); assert( animArray.size == animWeights.size ); if ( total_anims == 1 ) return animArray[ 0 ]; weights = 0; total_weight = 0; for ( i = 0;i < total_anims;i++ ) total_weight += animWeights[ i ]; anim_play = randomfloat( total_weight ); current_weight = 0; for ( i = 0;i < total_anims;i++ ) { current_weight += animWeights[ i ]; if ( anim_play >= current_weight ) continue; idleanim = i; break; } return animArray[ idleanim ]; } print3dtime( timer, org, msg, color, alpha, scale ) { newtime = timer / 0.05; for ( i = 0;i < newtime;i++ ) { print3d( org, msg, color, alpha, scale ); wait( 0.05 ); } } print3drise( org, msg, color, alpha, scale ) { newtime = 5 / 0.05; up = 0; org = org + randomvector( 30 ); for ( i = 0;i < newtime;i++ ) { up += 0.5; print3d( org + ( 0, 0, up ), msg, color, alpha, scale ); wait( 0.05 ); } } crossproduct( vec1, vec2 ) { return( vec1[ 0 ] * vec2[ 1 ] - vec1[ 1 ] * vec2[ 0 ] > 0 ); } getGrenadeModel() { return getWeaponModel( self.grenadeweapon ); } sawEnemyMove( timer ) { if ( !isdefined( timer ) ) timer = 500; return( gettime() - self.personalSightTime < timer ); } canThrowGrenade() { if ( !self.grenadeAmmo ) return false; if ( self.script_forceGrenade ) return true; return( isplayer( self.enemy ) ); } usingBoltActionWeapon() { return( weaponIsBoltAction( self.weapon ) ); } random_weight( array ) { idleanim = randomint( array.size ); if ( array.size > 1 ) { anim_weight = 0; for ( i = 0;i < array.size;i++ ) anim_weight += array[ i ]; anim_play = randomfloat( anim_weight ); anim_weight = 0; for ( i = 0;i < array.size;i++ ) { anim_weight += array[ i ]; if ( anim_play < anim_weight ) { idleanim = i; break; } } } return idleanim; } setFootstepEffect( name, fx ) { assertEx( isdefined( name ), "Need to define the footstep surface type." ); assertEx( isdefined( fx ), "Need to define the footstep effect." ); if ( !isdefined( anim.optionalStepEffects ) ) anim.optionalStepEffects = []; anim.optionalStepEffects[ anim.optionalStepEffects.size ] = name; level._effect[ "step_" + name ] = fx; } setFootstepEffectSmall( name, fx ) { assertEx( isdefined( name ), "Need to define the footstep surface type." ); assertEx( isdefined( fx ), "Need to define the mud footstep effect." ); if ( !isdefined( anim.optionalStepEffectsSmall ) ) anim.optionalStepEffectsSmall = []; anim.optionalStepEffectsSmall[ anim.optionalStepEffectsSmall.size ] = name; level._effect[ "step_small_" + name ] = fx; } setNotetrackEffect( notetrack, tag, surfacename, fx, sound_prefix, sound_suffix ) { assert( isdefined( notetrack ) ); assert( isdefined( tag ) ); assert( isdefined( fx ) ); assertEx( isstring( notetrack ), "Notetrack name must be a string" ); if ( !isdefined( surfacename ) ) surfacename = "all"; if ( !isdefined( level._notetrackFX ) ) level._notetrackFX = []; level._notetrackFX[ notetrack ][ surfacename ] = spawnStruct(); level._notetrackFX[ notetrack ][ surfacename ].tag = tag; level._notetrackFX[ notetrack ][ surfacename ].fx = fx; if ( isdefined( sound_prefix ) ) level._notetrackFX[ notetrack ][ surfacename ].sound_prefix = sound_prefix; if ( isdefined( sound_suffix ) ) level._notetrackFX[ notetrack ][ surfacename ].sound_suffix = sound_suffix; } persistentDebugLine( start, end ) { self endon( "death" ); level notify( "newdebugline" ); level endon( "newdebugline" ); for ( ;; ) { line( start, end, ( 0.3, 1, 0 ), 1 ); wait( 0.05 ); } } EnterProneWrapper( timer ) { thread enterProneWrapperProc( timer ); } enterProneWrapperProc( timer ) { self endon( "death" ); self notify( "anim_prone_change" ); self endon( "anim_prone_change" ); // wrapper so we can put a breakpoint on it self EnterProne( timer, isDefined( self.a.onback ) ); self waittill( "killanimscript" ); // in case we dont actually make it into prone by the time another script comes in if ( self.a.pose != "prone" && !isdefined( self.a.onback ) ) self.a.pose = "prone"; } ExitProneWrapper( timer ) { thread ExitProneWrapperProc( timer ); } ExitProneWrapperProc( timer ) { self endon( "death" ); self notify( "anim_prone_change" ); self endon( "anim_prone_change" ); // wrapper so we can put a breakpoint on it self ExitProne( timer ); self waittill( "killanimscript" ); // in case we dont actually leave prone, change it out of prone if ( self.a.pose == "prone" ) self.a.pose = "crouch"; } canBlindfire() { if ( self.a.atConcealmentNode ) return false; if ( !animscripts\weaponList::usingAutomaticWeapon() ) return false; if ( weaponClass( self.weapon ) == "mg" ) return false; if ( isdefined( self.disable_blindfire ) && self.disable_blindfire == true ) return false; return true; } canHitSuppressSpot() { if ( !hasEnemySightPos() ) return false; myGunPos = self getMuzzlePos(); return( sightTracePassed( myGunPos, getEnemySightPos(), false, undefined ) ); } moveAnim( animname ) /* string */ { assert( isdefined( self.a.moveAnimSet ) ); return self.a.moveAnimSet[ animname ]; } randomAnimOfTwo( anim1, anim2 ) { if ( randomint( 2 ) ) return anim1; else return anim2; } animArray( animname ) /* string */ { // println( "playing anim: ", animname ); assert( isdefined( self.a.array ) ); /# if ( !isdefined( self.a.array[ animname ] ) ) { dumpAnimArray(); assertex( isdefined( self.a.array[ animname ] ), "self.a.array[ \"" + animname + "\" ] is undefined" ); } #/ return self.a.array[ animname ]; } animArrayAnyExist( animname ) { assert( isdefined( self.a.array ) ); /# if ( !isdefined( self.a.array[ animname ] ) ) { dumpAnimArray(); assertex( isdefined( self.a.array[ animname ] ), "self.a.array[ \"" + animname + "\" ] is undefined" ); } #/ return self.a.array[ animname ].size > 0; } animArrayPickRandom( animname ) { assert( isdefined( self.a.array ) ); /# if ( !isdefined( self.a.array[ animname ] ) ) { dumpAnimArray(); assertex( isdefined( self.a.array[ animname ] ), "self.a.array[ \"" + animname + "\" ] is undefined" ); } #/ assert( self.a.array[ animname ].size > 0 ); index = randomint( self.a.array[ animname ].size ); return self.a.array[ animname ][ index ]; } /# dumpAnimArray() { println( "self.a.array:" ); keys = getArrayKeys( self.a.array ); for ( i = 0; i < keys.size; i++ ) { if ( isarray( self.a.array[ keys[ i ] ] ) ) println( " array[ \"" + keys[ i ] + "\" ] = {array of size " + self.a.array[ keys[ i ] ].size + "}" ); else println( " array[ \"" + keys[ i ] + "\" ] = ", self.a.array[ keys[ i ] ] ); } } #/ array( a, b, c, d, e, f, g, h, i, j, k, l, m, n ) { array = []; if ( isdefined( a ) ) array[ 0 ] = a; else return array; if ( isdefined( b ) ) array[ 1 ] = b; else return array; if ( isdefined( c ) ) array[ 2 ] = c; else return array; if ( isdefined( d ) ) array[ 3 ] = d; else return array; if ( isdefined( e ) ) array[ 4 ] = e; else return array; if ( isdefined( f ) ) array[ 5 ] = f; else return array; if ( isdefined( g ) ) array[ 6 ] = g; else return array; if ( isdefined( h ) ) array[ 7 ] = h; else return array; if ( isdefined( i ) ) array[ 8 ] = i; else return array; if ( isdefined( j ) ) array[ 9 ] = j; else return array; if ( isdefined( k ) ) array[ 10 ] = k; else return array; if ( isdefined( l ) ) array[ 11 ] = l; else return array; if ( isdefined( m ) ) array[ 12 ] = m; else return array; if ( isdefined( n ) ) array[ 13 ] = n; return array; } getAIPrimaryWeapon() { return self.primaryweapon; } getAISecondaryWeapon() { return self.secondaryweapon; } getAISidearmWeapon() { return self.sidearm; } getAICurrentWeapon() { return self.weapon; } usingPrimary() { return( self.weapon == self.primaryweapon && self.weapon != "none" ); } usingSecondary() { return( self.weapon == self.secondaryweapon && self.weapon != "none" ); } usingSidearm() { return( self.weapon == self.sidearm && self.weapon != "none" ); } getAICurrentWeaponSlot() { if ( self.weapon == self.primaryweapon ) return "primary"; else if ( self.weapon == self.secondaryweapon ) return "secondary"; else if ( self.weapon == self.sidearm ) return "sidearm"; else assertMsg( "self.weapon does not match any known slot" ); } AIHasWeapon( weapon ) { if ( isDefined( self.weaponInfo[ weapon ] ) ) return true; return false; } getAnimEndPos( theanim ) { moveDelta = getMoveDelta( theanim, 0, 1 ); return self localToWorldCoords( moveDelta ); } damageLocationIsAny( a, b, c, d, e, f, g, h, i, j, k, ovr ) { /* possibile self.damageLocation's: "torso_upper" "torso_lower" "helmet" "head" "neck" "left_arm_upper" "left_arm_lower" "left_hand" "right_arm_upper" "right_arm_lower" "right_hand" "gun" "none" "left_leg_upper" "left_leg_lower" "left_foot" "right_leg_upper" "right_leg_lower" "right_foot" */ if ( !isdefined( a ) ) return false; if ( self.damageLocation == a ) return true; if ( !isdefined( b ) ) return false; if ( self.damageLocation == b ) return true; if ( !isdefined( c ) ) return false; if ( self.damageLocation == c ) return true; if ( !isdefined( d ) ) return false; if ( self.damageLocation == d ) return true; if ( !isdefined( e ) ) return false; if ( self.damageLocation == e ) return true; if ( !isdefined( f ) ) return false; if ( self.damageLocation == f ) return true; if ( !isdefined( g ) ) return false; if ( self.damageLocation == g ) return true; if ( !isdefined( h ) ) return false; if ( self.damageLocation == h ) return true; if ( !isdefined( i ) ) return false; if ( self.damageLocation == i ) return true; if ( !isdefined( j ) ) return false; if ( self.damageLocation == j ) return true; if ( !isdefined( k ) ) return false; if ( self.damageLocation == k ) return true; assert( !isdefined( ovr ) ); return false; } usingPistol() { return weaponClass( self.weapon ) == "pistol"; } usingRocketLauncher() { return weaponClass( self.weapon ) == "rocketlauncher"; } usingMG() { return weaponClass( self.weapon ) == "mg"; } usingShotGun() { return weaponclass( self.weapon ) == "spread"; } usingRifleLikeWeapon() { class = weaponClass( self.weapon ); switch( class ) { case "mg": case "smg": case "sniper": case "rifle": case "spread": return true; } return false; } ragdollDeath( moveAnim ) { self endon( "killanimscript" ); lastOrg = self.origin; moveVec = ( 0, 0, 0 ); for ( ;; ) { wait( 0.05 ); force = distance( self.origin, lastOrg ); lastOrg = self.origin; if ( self.health == 1 ) { self.a.nodeath = true; self startRagdoll(); self clearAnim( moveAnim, 0.1 ); wait( 0.05 ); physicsExplosionSphere( lastOrg, 600, 0, force * 0.1 ); self notify( "killanimscript" ); return; } } } shouldCQB() { return isdefined( self.cqbwalking ) && !isdefined( self.grenade ); } isCQBWalking() { return isdefined( self.cqbwalking ); } isCQBWalkingOrFacingEnemy() { return !self.faceMotion || isdefined( self.cqbwalking ); } randomizeIdleSet() { self.a.idleSet = randomint( 2 ); } isShotgun( weapon ) { return weaponclass( weapon ) == "spread"; } isSniperRifle( weapon ) { return weaponclass( weapon ) == "sniper"; } weapon_pump_action_shotgun() { return self.weapon != "none" && weaponIsBoltAction( self.weapon ) && weaponclass( self.weapon ) == "spread"; } // meant to be used with any integer seed, for a small integer maximum (ideally one that divides anim.randomIntTableSize) getRandomIntFromSeed( intSeed, intMax ) { assert( intMax > 0 ); index = intSeed % anim.randomIntTableSize; return anim.randomIntTable[ index ] % intMax; } getCurrentWeaponSlotName() { assert( isDefined( self ) ); if ( self usingSecondary() ) return "secondary"; if ( self usingSidearm() ) return "sidearm"; // primary and unknowns/none return this slot by default return "primary"; }