IW4-Dump-Files/animscripts/utility.gsc

2219 lines
52 KiB
Plaintext

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