415 lines
16 KiB
Plaintext
415 lines
16 KiB
Plaintext
#using scripts\shared\gameskill_shared;
|
|
#using scripts\shared\name_shared;
|
|
#using scripts\shared\util_shared;
|
|
#using scripts\shared\ai\systems\debug;
|
|
#using scripts\shared\ai\systems\shared;
|
|
#using scripts\shared\ai\archetype_utility;
|
|
#using scripts\shared\ai\systems\weaponList;
|
|
|
|
|
|
|
|
|
|
|
|
#using_animtree("generic");
|
|
|
|
//--------------------------------------------------------------------------------
|
|
// Weapon Initialization
|
|
//--------------------------------------------------------------------------------
|
|
function initWeapon( weapon )
|
|
{
|
|
self.weaponInfo[weapon.name] = SpawnStruct();
|
|
|
|
self.weaponInfo[weapon.name].position = "none";
|
|
self.weaponInfo[weapon.name].hasClip = true;
|
|
|
|
if ( IsDefined( weapon.ClipModel ) )
|
|
self.weaponInfo[weapon.name].useClip = true;
|
|
else
|
|
self.weaponInfo[weapon.name].useClip = false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------
|
|
// Main
|
|
//--------------------------------------------------------------------------------
|
|
function main()
|
|
{
|
|
self.a = SpawnStruct();
|
|
self.a.weaponPos = [];
|
|
|
|
// Init weapons to "none"
|
|
// This is done here for now, but might be better to
|
|
// just have the converter set the empty weapon slots to "none" instead of ""
|
|
if ( self.weapon == level.weaponNone )
|
|
self AiUtility::setCurrentWeapon( level.weaponNone );
|
|
|
|
// primary weapon
|
|
self AiUtility::setPrimaryWeapon(self.weapon);
|
|
|
|
// secondary weapon
|
|
if ( self.secondaryweapon == level.weaponNone )
|
|
self AiUtility::setSecondaryWeapon( level.weaponNone );
|
|
|
|
self AiUtility::setSecondaryWeapon( self.secondaryweapon );
|
|
self AiUtility::setCurrentWeapon(self.primaryweapon);
|
|
|
|
self.initial_primaryweapon = self.primaryweapon;
|
|
self.initial_secondaryweapon = self.secondaryweapon;
|
|
|
|
self initWeapon( self.primaryweapon );
|
|
self initWeapon( self.secondaryweapon );
|
|
self initWeapon( self.sidearm );
|
|
|
|
self.weapon_positions = array( "left", "right", "chest", "back" );
|
|
|
|
for (i = 0; i < self.weapon_positions.size; i++)
|
|
{
|
|
self.a.weaponPos[self.weapon_positions[i]] = level.weaponNone;
|
|
}
|
|
|
|
self.lastWeapon = self.weapon;
|
|
|
|
self thread beginGrenadeTracking();
|
|
self thread globalGrenadeTracking();
|
|
|
|
firstInit();
|
|
|
|
// AI_TODO - Proper ammo tracking for rocketlauncher AI's
|
|
self.a.rockets = 3;
|
|
self.a.rocketVisible = true;
|
|
|
|
// Set initial states for poses
|
|
self.a.pose = "stand";
|
|
self.a.prevPose = self.a.pose;
|
|
self.a.movement = "stop";
|
|
self.a.special = "none";
|
|
self.a.gunHand = "none"; // Initialize so that PutGunInHand works properly.
|
|
|
|
shared::placeWeaponOn( self.primaryweapon, "right" );
|
|
if ( isdefined( self.secondaryweaponclass ) && self.secondaryweaponclass != "none" && self.secondaryweaponclass != "pistol" )
|
|
shared::placeWeaponOn( self.secondaryweapon, "back");
|
|
|
|
self.a.combatEndTime = GetTime();
|
|
self.a.nextGrenadeTryTime = 0;
|
|
|
|
// set up all the aiming stuff
|
|
self.a.isAiming = false;
|
|
self.rightAimLimit = 45;
|
|
self.leftAimLimit = -45;
|
|
self.upAimLimit = 45;
|
|
self.downAimLimit = -45;
|
|
|
|
// setup the speed variables
|
|
self.walk = false;
|
|
self.sprint = false;
|
|
|
|
self.a.postScriptFunc = undefined;
|
|
|
|
// use the GDT settings to start with
|
|
self.baseAccuracy = self.accuracy;
|
|
|
|
// set default accuracy mod
|
|
if( !isdefined(self.script_accuracy) )
|
|
self.script_accuracy = 1;
|
|
|
|
// scale baseAccuracy based on number of coop players
|
|
if( self.team == "axis" || self.team == "team3" )
|
|
self thread gameskill::axisAccuracyControl();
|
|
else if (self.team == "allies")
|
|
self thread gameskill::alliesAccuracyControl();
|
|
|
|
self.a.missTime = 0;
|
|
self.bulletsInClip = self.weapon.clipSize;
|
|
|
|
// state tracking
|
|
self.lastEnemySightTime = 0; // last time we saw our current enemy
|
|
self.combatTime = 0; // how long we've been in/out of combat
|
|
|
|
self.suppressed = false; // if we're currently suppressed
|
|
self.suppressedTime = 0; // how long we've been in/out of suppression
|
|
|
|
if ( self.team == "allies" )
|
|
self.suppressionThreshold = 0.75;
|
|
else
|
|
self.suppressionThreshold = 0.5;
|
|
|
|
// Random range makes the grenades less accurate and do less damage, but also makes it difficult to throw back.
|
|
if ( self.team == "allies" )
|
|
self.randomGrenadeRange = 0;
|
|
else
|
|
self.randomGrenadeRange = 128;
|
|
|
|
self.reacquire_state = 0;
|
|
}
|
|
|
|
function setNameAndRank()
|
|
{
|
|
self endon ( "death" );
|
|
|
|
self name::get();
|
|
}
|
|
|
|
function DoNothing()
|
|
{
|
|
}
|
|
|
|
function set_anim_playback_rate()
|
|
{
|
|
self.animplaybackrate = 0.9 + RandomFloat( 0.2 );
|
|
self.moveplaybackrate = 1;
|
|
}
|
|
|
|
function trackVelocity()
|
|
{
|
|
self endon ("death");
|
|
|
|
for (;;)
|
|
{
|
|
self.oldOrigin = self.origin;
|
|
wait (0.2);
|
|
}
|
|
}
|
|
|
|
/#
|
|
function checkApproachAngles( transTypes )
|
|
{
|
|
idealTransAngles[1] = 45;
|
|
idealTransAngles[2] = 0;
|
|
idealTransAngles[3] = -45;
|
|
idealTransAngles[4] = 90;
|
|
idealTransAngles[6] = -90;
|
|
idealTransAngles[7] = 135;
|
|
idealTransAngles[8] = 180;
|
|
idealTransAngles[9] = -135;
|
|
|
|
wait .05;
|
|
|
|
for ( i = 1; i <= 9; i++ )
|
|
{
|
|
for ( j = 0; j < transTypes.size; j++ )
|
|
{
|
|
trans = transTypes[j];
|
|
|
|
idealAdd = 0;
|
|
if ( trans == "left" || trans == "left_crouch" )
|
|
{
|
|
idealAdd = 90;
|
|
}
|
|
else if ( trans == "right" || trans == "right_crouch" )
|
|
{
|
|
idealAdd = -90;
|
|
}
|
|
|
|
if ( isdefined( anim.coverTransAngles[ trans ][i] ) )
|
|
{
|
|
correctAngle = AngleClamp180( idealTransAngles[i] + idealAdd );
|
|
actualAngle = AngleClamp180( anim.coverTransAngles[ trans ][i] );
|
|
if ( AbsAngleClamp180( actualAngle - correctAngle ) > 7 )
|
|
{
|
|
println( "^1Cover approach animation has bad yaw delta: anim.coverTrans[\"" + trans + "\"][" + i + "]; is ^2" + actualAngle + "^1, should be closer to ^2" + correctAngle + "^1." );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( i = 1; i <= 9; i++ )
|
|
{
|
|
for ( j = 0; j < transTypes.size; j++ )
|
|
{
|
|
trans = transTypes[j];
|
|
|
|
idealAdd = 0;
|
|
if ( trans == "left" || trans == "left_crouch" )
|
|
{
|
|
idealAdd = 90;
|
|
}
|
|
else if ( trans == "right" || trans == "right_crouch" )
|
|
{
|
|
idealAdd = -90;
|
|
}
|
|
|
|
if ( isdefined( anim.coverExitAngles[ trans ][i] ) )
|
|
{
|
|
correctAngle = AngleClamp180( -1 * (idealTransAngles[i] + idealAdd + 180) );
|
|
actualAngle = AngleClamp180( anim.coverExitAngles[ trans ][i] );
|
|
if ( AbsAngleClamp180( actualAngle - correctAngle ) > 7 )
|
|
{
|
|
println( "^1Cover exit animation has bad yaw delta: anim.coverTrans[\"" + trans + "\"][" + i + "]; is ^2" + actualAngle + "^1, should be closer to ^2" + correctAngle + "^1." );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#/
|
|
|
|
function getExitSplitTime( approachType, dir )
|
|
{
|
|
return anim.coverExitSplit[ approachType ][ dir ];
|
|
}
|
|
|
|
function getTransSplitTime( approachType, dir )
|
|
{
|
|
return anim.coverTransSplit[ approachType ][ dir ];
|
|
}
|
|
|
|
function firstInit()
|
|
{
|
|
// Initialization that should happen once per level
|
|
if ( IsDefined(anim.NotFirstTime) ) // Use this to trigger the first init
|
|
{
|
|
return;
|
|
}
|
|
|
|
anim.NotFirstTime = true;
|
|
|
|
anim.grenadeTimers["player_frag_grenade_sp"] = randomIntRange( 1000, 20000 );
|
|
anim.grenadeTimers["player_flash_grenade_sp"] = randomIntRange( 1000, 20000 );
|
|
anim.grenadeTimers["player_double_grenade"] = randomIntRange( 10000, 60000 );
|
|
anim.grenadeTimers["AI_frag_grenade_sp"] = randomIntRange( 0, 20000 );
|
|
anim.grenadeTimers["AI_flash_grenade_sp"] = randomIntRange( 0, 20000 );
|
|
|
|
anim.numGrenadesInProgressTowardsPlayer = 0;
|
|
anim.lastGrenadeLandedNearPlayerTime = -1000000;
|
|
anim.lastFragGrenadeToPlayerStart = -1000000;
|
|
thread setNextPlayerGrenadeTime();
|
|
|
|
if ( !isdefined( level.flag ) )
|
|
{
|
|
level.flag = [];
|
|
}
|
|
|
|
level.painAI = undefined;
|
|
anim.coverCrouchLeanPitch = -55;
|
|
}
|
|
|
|
|
|
function onPlayerConnect()
|
|
{
|
|
player = self;
|
|
|
|
firstInit();
|
|
|
|
player.invul = false;
|
|
}
|
|
|
|
function setNextPlayerGrenadeTime()
|
|
{
|
|
waittillframeend;
|
|
// might not be defined if load::main() wasn't called
|
|
if ( isdefined( anim.playerGrenadeRangeTime ) )
|
|
{
|
|
maxTime = int( anim.playerGrenadeRangeTime * 0.7 );
|
|
if ( maxTime < 1 )
|
|
{
|
|
maxTime = 1;
|
|
}
|
|
|
|
anim.grenadeTimers["player_frag_grenade_sp"] = randomIntRange( 0, maxTime );
|
|
anim.grenadeTimers["player_flash_grenade_sp"] = randomIntRange( 0, maxTime );
|
|
}
|
|
|
|
if ( isdefined( anim.playerDoubleGrenadeTime ) )
|
|
{
|
|
maxTime = int( anim.playerDoubleGrenadeTime );
|
|
minTime = int( maxTime / 2 );
|
|
if ( maxTime <= minTime )
|
|
{
|
|
maxTime = minTime + 1;
|
|
}
|
|
|
|
anim.grenadeTimers["player_double_grenade"] = randomIntRange( minTime, maxTime );
|
|
}
|
|
}
|
|
|
|
function AddToMissiles(grenade)
|
|
{
|
|
if(!isdefined(level.missileEntities))level.missileEntities=[];
|
|
if ( !isdefined( level.missileEntities ) ) level.missileEntities = []; else if ( !IsArray( level.missileEntities ) ) level.missileEntities = array( level.missileEntities ); level.missileEntities[level.missileEntities.size]=grenade;;
|
|
|
|
while ( IsDefined(grenade) )
|
|
{
|
|
{wait(.05);};
|
|
}
|
|
|
|
ArrayRemoveValue(level.missileEntities,grenade);
|
|
}
|
|
|
|
|
|
function globalGrenadeTracking()
|
|
{
|
|
if(!isdefined(level.missileEntities))level.missileEntities=[];
|
|
|
|
self endon ( "death" );
|
|
self thread globalGrenadeLauncherTracking();
|
|
self thread globalMissileTracking();
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill ( "grenade_fire", grenade, weapon );
|
|
grenade.owner=self;
|
|
grenade.weapon = weapon;
|
|
level thread AddToMissiles(grenade);
|
|
}
|
|
}
|
|
|
|
function globalGrenadeLauncherTracking()
|
|
{
|
|
self endon ( "death" );
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill ( "grenade_launcher_fire", grenade, weapon );
|
|
grenade.owner=self;
|
|
grenade.weapon = weapon;
|
|
level thread AddToMissiles(grenade);
|
|
}
|
|
}
|
|
|
|
function globalMissileTracking()
|
|
{
|
|
self endon ( "death" );
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill ( "missile_fire", grenade, weapon );
|
|
grenade.owner=self;
|
|
grenade.weapon = weapon;
|
|
level thread AddToMissiles(grenade);
|
|
}
|
|
}
|
|
|
|
function beginGrenadeTracking()
|
|
{
|
|
self endon ( "death" );
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill ( "grenade_fire", grenade, weapon );
|
|
grenade thread grenade_earthQuake();
|
|
}
|
|
}
|
|
|
|
function endOnDeath()
|
|
{
|
|
self waittill( "death" );
|
|
waittillframeend;
|
|
self notify ( "end_explode" );
|
|
}
|
|
|
|
function grenade_earthQuake() //self == grenade
|
|
{
|
|
self thread endOnDeath();
|
|
self endon( "end_explode" );
|
|
self waittill( "explode", position );
|
|
PlayRumbleOnPosition( "grenade_rumble", position );
|
|
earthquake( 0.3, 0.5, position, 400 );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------
|
|
// end script
|
|
//--------------------------------------------------------------------------------
|
|
|
|
function end_script()
|
|
{
|
|
}
|