IW4-Dump-Files/maps/_zodiac_ai.gsc

545 lines
13 KiB
Plaintext

#include animscripts\combat_utility;
#include animscripts\shared;
#include animscripts\utility;
#include maps\_utility;
#using_animtree( "generic_human" );
main()
{
anim.boatanims = [];
anim.boatanims[ "left" ] = spawnstruct();
anim.boatanims[ "left" ].base = %zodiac_aim_left;
anim.boatanims[ "left" ].trans = %zodiac_trans_R2L; // Why would we use the "R_2_L" animation to transition from the "left" pose to the "right" pose, you ask? I don't know, my friend. I don't know.
anim.boatanims[ "left" ].aim = spawnstruct();
anim.boatanims[ "left" ].aim.left = %zodiac_rightside_aim4;
anim.boatanims[ "left" ].aim.center = %zodiac_rightside_aim5;
anim.boatanims[ "left" ].aim.right = %zodiac_rightside_aim6;
anim.boatanims[ "left" ].reload = array( %zodiac_rightside_reload );
anim.boatanims[ "left" ].leftAimLimit = -49;
anim.boatanims[ "left" ].rightAimLimit = 48;
anim.boatanims[ "left" ].idle = %zodiac_rightside_idle;
anim.boatanims[ "left" ].twitch = array( %zodiac_rightside_shift, %zodiac_rightside_react );
anim.boatanims[ "right" ] = spawnstruct();
anim.boatanims[ "right" ].base = %zodiac_aim_right;
anim.boatanims[ "right" ].trans = %zodiac_trans_L2R;
anim.boatanims[ "right" ].aim = spawnstruct();
anim.boatanims[ "right" ].aim.left = %zodiac_leftside_aim4;
anim.boatanims[ "right" ].aim.center = %zodiac_leftside_aim5;
anim.boatanims[ "right" ].aim.right = %zodiac_leftside_aim6;
anim.boatanims[ "right" ].reload = array( %zodiac_leftside_reload, %zodiac_leftside_reloadB );
anim.boatanims[ "right" ].idle = %zodiac_leftside_idle;
anim.boatanims[ "right" ].twitch = array( %zodiac_leftside_duck );
anim.boatanims[ "right" ].leftAimLimit = -51;
anim.boatanims[ "right" ].rightAimLimit = 51;
}
draw_line_toshootpos()
{
while(1)
{
if( isdefined( self.shootpos ) )
Line( self.shootpos, self.origin, (1,0,0), 1 );
if( isdefined( self.favoriteenemy ) )
Line( self.favoriteenemy.origin, self.origin, (0,0,1), 1 );
if( isdefined( self.zodiac_enemy ) )
Line( self.zodiac_enemy.origin, self.origin, (0,1,0), 1 );
wait .05;
}
}
endthink() // this function is not called right now, but it should be if an AI ever gets off a zodiac.
{
self.a.specialShootBehavior = undefined;
}
think()
{
self endon ( "killanimscript" ); // (includes death)
if( !ent_flag_exist( "transitioning_positions" ) )
ent_flag_init( "transitioning_positions" );
else
ent_flag_clear( "transitioning_positions" );
animscripts\utility::initialize( "zodiac" );
self.a.boatAimYaw = 0;
if( !isdefined( self.a.boat_pose ) )
self.a.boat_pose = "right";
self.a.last_boat_pose_switch = gettime();
self.a.lastBoatTwitchTime = gettime();
self childthread animscripts\shoot_behavior::decideWhatAndHowToShoot( "normal" );
self setup_anim_array_boat();
self.a.wantBoatReloadTime = undefined;
self.a.specialShootBehavior = ::zodiacShootBehavior;
self childthread watchVelocity();
self childthread idleAimDir();
// self childthread draw_line_toshootpos();
for ( ;; )
{
self thread disableBoatIdle();
if ( self shouldReload() )
{
self boatReload();
continue;
}
newPose = needToChangePose();
if ( newPose != "none" )
{
assert( newPose != self.a.boat_pose );
transanim = anim.boatanims[ self.a.boat_pose ].trans;
self.a.boat_pose = newPose;
ent_flag_set( "transitioning_positions" );
self setFlaggedAnimKnobAllRestart( "trans", transanim, %body, 1, 0.2 );
self animscripts\shared::DoNoteTracksForTime( getAnimLength( transanim ) - 0.3, "trans" );
self.a.last_boat_pose_switch = gettime();
ent_flag_clear( "transitioning_positions" );
theanim = anim.boatanims[ self.a.boat_pose ].aim.center;
self setAnimKnobAllRestart( theanim, %body, 1, 0.2 );
self notify( "boat_pose_change" );
self.a.boatAimYaw = 0;
self setup_anim_array_boat();
continue;
}
if ( shouldDoTwitch() )
{
doBoatTwitch();
continue;
}
// we want the additive idle for anything after this point in the loop (shooting or aiming)
self thread enableBoatIdle();
if ( aimedAtShootEntOrPos() )
{
self shootUntilNeedToChangePose();
continue;
}
else
{
self updateBoatAim();
}
wait .1;
}
self waittill( "forever" );
}
shouldReload()
{
if ( NeedToReload( 0 ) )
{
// it looks bad to reload when we have targets.
// we'll reload when we get a chance.
if ( !isDefined( self.a.wantBoatReloadTime ) )
self.a.wantBoatReloadTime = gettime();
self animscripts\weaponList::RefillClip();
}
if ( isDefined( self.a.wantBoatReloadTime ) )
{
// don't wait too long.
if ( gettime() - self.a.wantBoatReloadTime > 2500 )
return true;
if ( !canAimAtEnemy() )
return true;
if ( self.a.lastShootTime < gettime() - 1500 )
return true;
}
return false;
}
boatReload()
{
reloads = anim.boatanims[ self.a.boat_pose ].reload;
reloadanim = reloads[ randomint( reloads.size ) ];
self.a.wantBoatReloadTime = undefined;
self setFlaggedAnimKnobAllRestart( "reload", reloadanim, %body, 1, 0.2 );
self animscripts\shared::DoNoteTracks( "reload" );
self animscripts\weaponList::RefillClip();
}
disableBoatIdle()
{
if ( !isDefined( self.a.boatIdle ) )
return;
self endon( "killanimscript" );
// actually wait a bit before clearing it in case we still want it
self endon( "want_boat_idle" );
wait .05;
self notify( "end_boat_idle" );
self.a.boatIdle = undefined;
self clearAnim( %zodiac_idle, 0.2 );
}
enableBoatIdle()
{
self notify( "want_boat_idle" );
if ( isdefined( self.a.boatIdle ) )
return;
self.a.boatIdle = true;
self endon( "end_boat_idle" );
idleAnim = anim.boatanims[ self.a.boat_pose ].idle;
if ( isDefined( idleAnim ) )
self setAnimKnob( idleAnim, 1, 0.2 );
}
shouldDoTwitch()
{
if ( self.a.lastShootTime > gettime() - 2000 )
return false;
if ( gettime() < self.a.lastBoatTwitchTime + 1500 )
return false;
if ( isDefined( self.enemy ) && self.enemy sightconetrace( self getEye() ) )
return false;
if ( !isDefined( anim.boatanims[ self.a.boat_pose ].twitch ) )
return false;
return true;
}
doBoatTwitch()
{
twitches = anim.boatanims[ self.a.boat_pose ].twitch;
twitchAnim = twitches[ randomint( twitches.size ) ];
for ( i = 0; i < 5; i++ )
{
if ( !isdefined( self.a.lastBoatTwitchAnim ) || twitchAnim != self.a.lastBoatTwitchAnim )
break;
twitchAnim = twitches[ randomint( twitches.size ) ];
}
self setFlaggedAnimKnobAllRestart( "twitch", twitchAnim, %body, 1, 0.2 );
self animscripts\shared::DoNoteTracks( "twitch" );
self.a.lastBoatTwitchAnim = twitchAnim;
self.a.lastBoatTwitchTime = gettime();
}
zodiacShootBehavior()
{
if ( !isdefined( self.enemy ) || !self.enemy sightconetrace( self getEye() ) )
{
self.shootent = undefined;
self.shootpos = undefined;
self.shootstyle = "none";
return;
}
self.shootent = self.enemy;
self.shootpos = self.enemy getShootAtPos();
distSq = distanceSquared( self.origin, self.enemy.origin );
if ( distSq < 4000*4000 )
self.shootstyle = "burst";
else
self.shootstyle = "single";
}
watchVelocity()
{
self endon( "killanimscript" );
self.prevpos = self.origin;
self.boatvelocity = (0,0,0);
for ( ;; )
{
wait .05;
self.boatvelocity = (self.origin - self.prevpos) / .05;
self.prevpos = self.origin;
}
}
waitRandomTimeBoat()
{
self endon( "boat_pose_change" );
wait randomfloatrange( 0.5, 3.5 );
}
idleAimDir()
{
self endon( "killanimscript" );
for ( ;; )
{
if ( self.a.boat_pose == "left" )
self.idleAimYaw = randomfloatrange( -20, 40 );
else
self.idleAimYaw = randomfloatrange( -40, 20 );
self waitRandomTimeBoat();
}
}
getBoatAimYawToShootPos( predictionTime )
{
if ( !isDefined( self.shootPos ) )
return 0;
predictedShootPos = self.shootPos - self.boatvelocity * predictionTime;
aimYaw = getAimYawToPoint( predictedShootPos );
return aimYaw;
}
canAimAtEnemy()
{
if ( !isDefined( self.shootPos ) )
return false;
aimYaw = getDesiredBoatAimYaw();
anims = anim.boatanims[ self.a.boat_pose ];
return ( aimYaw >= anims.leftAimLimit && aimYaw <= anims.rightAimLimit );
}
getDesiredBoatAimYaw()
{
aimYaw = 0;
if ( isDefined( self.shootPos ) )
{
aimYaw = getBoatAimYawToShootPos( .1 );
if ( self.a.boat_pose == "left" )
aimYaw = AngleClamp180( aimYaw + 40.5 );
else
aimYaw = AngleClamp180( aimYaw - 36 );
}
else
{
aimYaw = self.idleAimYaw;
}
return aimYaw;
}
updateBoatAim()
{
// need to be able to aim quickly because we're moving quickly, so don't cap too much
maxTurn = 15;
if ( !isDefined( self.shootPos ) )
maxTurn = 5;
aimYaw = getDesiredBoatAimYaw();
if ( abs( aimYaw - self.a.boatAimYaw ) > maxTurn )
{
if ( aimYaw < self.a.boatAimYaw )
aimYaw = self.a.boatAimYaw - maxTurn;
else
aimYaw = self.a.boatAimYaw + maxTurn;
}
anims = anim.boatanims[ self.a.boat_pose ];
if ( aimYaw < 0 )
{
frac = aimYaw / anims.leftAimLimit;
if ( frac > 1 )
frac = 1;
self setAnimKnob( anims.aim.center, 1 - frac, 0.1 );
self setAnim ( anims.aim.left , frac, 0.1 );
}
else
{
frac = aimYaw / anims.rightAimLimit;
if ( frac > 1 )
frac = 1;
self setAnimKnob( anims.aim.center, 1 - frac, 0.1 );
self setAnim ( anims.aim.right , frac, 0.1 );
}
self setAnimKnobAll( anims.base, %zodiac_actions, 1, 0.2 );
self.a.boatAimYaw = aimYaw;
}
updateBoatAimThread()
{
self endon( "killanimscript" );
self endon( "end_shootUntilNeedToChangePose" );
for ( ;; )
{
updateBoatAim();
wait .1;
}
}
shootUntilNeedToChangePose()
{
self thread watchForNeedToChangePoseOrTimeout();
self endon( "end_shootUntilNeedToChangePose" );
self thread updateBoatAimThread();
shootUntilShootBehaviorChange();
self notify( "end_shootUntilNeedToChangePose" );
}
watchForNeedToChangePoseOrTimeout()
{
self endon( "killanimscript" );
self endon( "end_shootUntilNeedToChangePose" );
endtime = gettime() + 4000 + randomint( 2000 );
while ( 1 )
{
if ( gettime() > endtime || needToChangePose() != "none" )
break;
if ( self shouldReload() )
break;
wait .1;
}
self notify( "end_shootUntilNeedToChangePose" );
}
needToChangePose_other()
{
if ( self.a.last_boat_pose_switch > gettime() - 2000 )
return "none";
if ( self.a.lastShootTime > gettime() - 2000 )
return "none";
if ( !isDefined( self.shootPos ) )
return "none";
aimYaw = getBoatAimYawToShootPos( 0.5 ); // half second prediction
if ( self.a.boat_pose == "left" )
{
if ( aimYaw > 15 && aimYaw < 160 )
return "right";
}
else if ( self.a.boat_pose == "right" )
{
if ( aimYaw < -15 && aimYaw > -160 )
return "left";
}
return "none";
}
needToChangePose()
{
if ( isdefined( self.use_auto_pose ) )
return needToChangePose_other();
if ( isDefined( self.scripted_boat_pose ) )
{
if ( self.a.boat_pose == self.scripted_boat_pose )
return "none";
return self.scripted_boat_pose;
}
// we always want the "left" pose now
if ( self.a.boat_pose == "right" )
return "left";
return "none";
/*
if ( !isDefined( self.shootPos ) )
return "none";
aimYaw = getBoatAimYawToShootPos( 0.5 ); // half second prediction
if ( self.a.boat_pose == "left" )
{
if ( aimYaw > 15 && aimYaw < 160 )
return "right";
}
else if ( self.a.boat_pose == "right" )
{
if ( aimYaw < -15 && aimYaw > -160 )
return "left";
}
return "none";
*/
}
setup_anim_array_boat()
{
self.a.array = [];
self.a.array[ "fire" ] = %exposed_shoot_auto_v3;
if ( self.a.boat_pose == "left" )
{
self.a.array[ "single" ] = array( %zodiac_rightside_fire_single );
self.a.array[ "burst2" ] = %zodiac_rightside_fire_burst;
self.a.array[ "burst3" ] = %zodiac_rightside_fire_burst;
self.a.array[ "burst4" ] = %zodiac_rightside_fire_burst;
self.a.array[ "burst5" ] = %zodiac_rightside_fire_burst;
self.a.array[ "burst6" ] = %zodiac_rightside_fire_burst;
self.a.array[ "semi2" ] = %zodiac_rightside_fire_burst;
self.a.array[ "semi3" ] = %zodiac_rightside_fire_burst;
self.a.array[ "semi4" ] = %zodiac_rightside_fire_burst;
self.a.array[ "semi5" ] = %zodiac_rightside_fire_burst;
self.a.array[ "semi6" ] = %zodiac_rightside_fire_burst;
}
else
{
self.a.array[ "single" ] = array( %zodiac_leftside_fire_single );
self.a.array[ "burst2" ] = %zodiac_leftside_fire_burst;
self.a.array[ "burst3" ] = %zodiac_leftside_fire_burst;
self.a.array[ "burst4" ] = %zodiac_leftside_fire_burst;
self.a.array[ "burst5" ] = %zodiac_leftside_fire_burst;
self.a.array[ "burst6" ] = %zodiac_leftside_fire_burst;
self.a.array[ "semi2" ] = %zodiac_leftside_fire_burst;
self.a.array[ "semi3" ] = %zodiac_leftside_fire_burst;
self.a.array[ "semi4" ] = %zodiac_leftside_fire_burst;
self.a.array[ "semi5" ] = %zodiac_leftside_fire_burst;
self.a.array[ "semi6" ] = %zodiac_leftside_fire_burst;
}
}