2023-04-13 17:30:38 +02:00

533 lines
19 KiB
Plaintext

#using scripts\shared\ai_shared;
#using scripts\shared\math_shared;
#using scripts\shared\sound_shared;
#using scripts\shared\throttle_shared;
#using scripts\shared\util_shared;
#using scripts\shared\ai\systems\debug;
#using scripts\shared\ai\systems\init;
#using scripts\shared\ai\archetype_utility;
#using scripts\shared\ai\systems\weaponList;
#using_animtree("generic");
function autoexec main()
{
level.ai_weapon_throttle = new Throttle();
[[ level.ai_weapon_throttle ]]->Initialize( 1, 0.1 );
}
function private _throwStowedWeapon( entity, weapon, weaponModel )
{
entity waittill( "death" );
if(IsDefined(entity))
{
weaponModel Unlink();
entity shared::ThrowWeapon( weapon, getTagForPos( "back" ), false );
}
weaponModel Delete();
}
function StowWeapon( weapon, positionOffset, orientationOffset )
{
entity = self;
if ( !IsDefined( positionOffset ) )
{
positionOffset = (0, 0, 0);
}
if ( !IsDefined( orientationOffset ) )
{
orientationOffset = (0, 0, 0);
}
weaponModel = Spawn( "script_model", (0, 0, 0) );
weaponModel SetModel( weapon.worldmodel );
weaponModel LinkTo( entity, "tag_stowed_back", positionOffset, orientationOffset );
entity thread _throwStowedWeapon( entity, weapon, weaponModel );
}
function placeWeaponOn( weapon, position )
{
self notify("weapon_position_change");
if ( IsString( weapon ) )
{
weapon = GetWeapon( weapon );
}
if (!isdefined(self.weaponInfo[weapon.name]))
self init::initWeapon( weapon );
curPosition = self.weaponInfo[weapon.name].position;
// make sure we're not out of sync
assert( curPosition == "none" || self.a.weaponPos[curPosition] == weapon );
if ( !IsArray( self.a.weaponPos ) )
{
self.a.weaponPos = [];
}
assert( IsArray( self.a.weaponPos ) );
assert( position == "none" || IsDefined( self.a.weaponPos[position] ), "Weapon position \"" + position + "\"" );
assert( IsWeapon( weapon ) );
// weapon already in place
if ( position != "none" && self.a.weaponPos[position] == weapon )
return;
//println("detach all (" + weapon.name + ", " + position + ")");
self detachAllWeaponModels();
// detach if we're already in a position
if ( curPosition != "none" )
self detachWeapon( weapon );
// nothing more to do
if ( position == "none" )
{
//println("update(1) all (" + weapon.name + ", " + position + ")");
self updateAttachedWeaponModels();
self AiUtility::setCurrentWeapon( level.weaponNone );
return;
}
if ( self.a.weaponPos[position] != level.weaponNone )
self detachWeapon( self.a.weaponPos[position] );
// to ensure that the correct tags for the active weapon are used, we need to make sure it gets attached first
if ( position == "left" || position == "right" )
{
self updateScriptWeaponInfoAndPos( weapon, position );
self AiUtility::setCurrentWeapon(weapon);
}
else
{
self updateScriptWeaponInfoAndPos( weapon, position );
}
self updateAttachedWeaponModels();
// make sure we don't have a weapon in each hand
assert( self.a.weaponPos["left"] == level.weaponNone || self.a.weaponPos["right"] == level.weaponNone );
}
function detachWeapon( weapon )
{
self.a.weaponPos[self.weaponInfo[weapon.name].position] = level.weaponNone;
self.weaponInfo[weapon.name].position = "none";
}
function updateScriptWeaponInfoAndPos( weapon, position )
{
self.weaponInfo[weapon.name].position = position;
self.a.weaponPos[position] = weapon;
}
function detachAllWeaponModels()
{
if( isdefined(self.weapon_positions) )
{
for ( index = 0; index < self.weapon_positions.size; index++ )
{
weapon = self.a.weaponPos[self.weapon_positions[index]];
if ( weapon == level.weaponNone )
continue;
self SetActorWeapon( level.weaponNone, self GetActorWeaponOptions() );
}
}
}
function updateAttachedWeaponModels()
{
if( isdefined(self.weapon_positions) )
{
for ( index = 0; index < self.weapon_positions.size; index++ )
{
weapon = self.a.weaponPos[self.weapon_positions[index]];
if ( weapon == level.weaponNone )
continue;
// weapon should only be assigned (means self.weapon will be set) to the weapon if its set to go to "right"
if( self.weapon_positions[index] != "right" )
continue;
self SetActorWeapon( weapon, self GetActorWeaponOptions() );
if ( self.weaponInfo[weapon.name].useClip && !self.weaponInfo[weapon.name].hasClip )
self hidepart( "tag_clip" );
}
}
}
function getTagForPos( position )
{
switch ( position )
{
case "chest":
return "tag_weapon_chest";
case "back":
return "tag_stowed_back";
case "left":
return "tag_weapon_left";
case "right":
return "tag_weapon_right";
case "hand":
return "tag_inhand";
default:
assertMsg( "unknown weapon placement position: " + position );
break;
}
}
function ThrowWeapon( weapon, positionTag, scavenger )
{
waitTime = 0.1;
linearScalar = 2.0;
angularScalar = 10;
// Calculate the linear and angular velocity to launch the weapon.
startPosition = self GetTagOrigin( positionTag );
startAngles = self GetTagAngles( positionTag );
// Wait two server frames to calculate velocity.
wait( waitTime );
if ( IsDefined( self ) )
{
endPosition = self GetTagOrigin( positionTag );
endAngles = self GetTagAngles( positionTag );
linearVelocity = ( endPosition - startPosition ) * ( 1.0 / waitTime ) * linearScalar;
angularVelocity = VectorNormalize( endAngles - startAngles ) * angularScalar;
throwWeapon = self DropWeapon( weapon, positionTag, linearVelocity, angularVelocity, scavenger );
if ( IsDefined( throwWeapon ) )
{
throwWeapon SetContents( throwWeapon SetContents( 0 ) & ~( (1 << 15) | (1 << 26) | (1 << 23) | (1 << 25) ) );
}
return throwWeapon;
}
}
function DropAIWeapon()
{
self endon("death");
if( self.weapon == level.weaponNone )
{
return;
}
if (( isdefined( self.script_nodropsecondaryweapon ) && self.script_nodropsecondaryweapon ) && (self.weapon == self.initial_secondaryweapon))
{
/#PrintLn("Not dropping secondary weapon '" + self.weapon.name + "'");#/
return;
}
else if (( isdefined( self.script_nodropsidearm ) && self.script_nodropsidearm ) && (self.weapon == self.sidearm))
{
/#PrintLn("Not dropping sidearm '" + self.weapon.name + "'");#/
return;
}
[[ level.ai_weapon_throttle ]]->WaitInQueue( self );
current_weapon = self.weapon;
dropWeaponName = player_weapon_drop( current_weapon );
position = self.weaponInfo[ current_weapon.name ].position;
shouldDropWeapon = !IsDefined( self.dontDropWeapon ) || self.dontDropWeapon === false;
if( current_weapon.isScavengable == false )
{
shouldDropWeapon = false;
}
if ( shouldDropWeapon && self.dropWeapon )
{
self.dontDropWeapon = true;
positionTag = getTagForPos( position );
ThrowWeapon( dropWeaponName, positionTag, false );
}
if ( self.weapon != level.weaponNone )
{
shared::placeWeaponOn( current_weapon, "none" );
if( self.weapon == self.primaryweapon )
{
self AiUtility::setPrimaryWeapon( level.weaponNone );
}
else if( self.weapon == self.secondaryweapon )
{
self AiUtility::setSecondaryWeapon( level.weaponNone );
}
}
self AiUtility::setCurrentWeapon( level.weaponNone );
}
function DropAllAIWeapons()
{
if (( isdefined( self.a.dropping_weapons ) && self.a.dropping_weapons ))
{
// already called
return;
}
if( !self.dropweapon )
{
if( self.weapon != level.weaponNone )
{
shared::placeWeaponOn( self.weapon, "none" );
self AiUtility::setCurrentWeapon( level.weaponNone );
}
return;
}
self.a.dropping_weapons = true;
self detachAllWeaponModels();
droppedSideArm = false;
if( isdefined(self.weapon_positions) )
{
for ( index = 0; index < self.weapon_positions.size; index++ )
{
weapon = self.a.weaponPos[ self.weapon_positions[ index ] ];
if ( weapon != level.weaponNone )
{
self.weaponInfo[ weapon.name ].position = "none";
self.a.weaponPos[ self.weapon_positions[ index ] ] = level.weaponNone;
if (( isdefined( self.script_nodropsecondaryweapon ) && self.script_nodropsecondaryweapon ) && (weapon == self.initial_secondaryweapon))
{
/#PrintLn("Not dropping secondary weapon '" + weapon.name + "'");#/
}
else if (( isdefined( self.script_nodropsidearm ) && self.script_nodropsidearm ) && (weapon == self.sidearm))
{
/#PrintLn("Not dropping sidearm '" + weapon.name + "'");#/
}
else
{
velocity = self GetVelocity();
speed = Length( velocity ) * 0.5;
weapon = player_weapon_drop(weapon);
droppedWeapon = self DropWeapon( weapon, self.weapon_positions[ index ], speed );
if ( self.sideArm != level.weaponNone )
{
if ( weapon == self.sideArm )
droppedSideArm = true;
}
}
}
}
}
if( !droppedSideArm && self.sideArm != level.weaponNone )
{
// 10% chance of dropping sidearm
if( RandomInt(100) <= 10 )
{
velocity = self GetVelocity();
speed = Length( velocity ) * 0.5;
droppedWeapon = self DropWeapon( self.sideArm, "chest", speed );
}
}
self AiUtility::setCurrentWeapon( level.weaponNone );
self.a.dropping_weapons = undefined;
}
function player_weapon_drop( weapon )
{
if ( IsSubStr( weapon.name, "rpg" ) )
{
return GetWeapon( "rpg_player" );
}
return weapon;
}
function HandleNoteTrack( note, flagName, customFunction, var1 )
{
}
// DoNoteTracks waits for and responds to standard noteTracks on the animation, returning when it gets an "end" or a "finish"
// For level scripts, a pointer to a custom function should be passed as the second argument, which handles notetracks not
// already handled by the generic function. This call should take the form DoNoteTracks(flagName,&customFunction);
// The custom function will be called for each notetrack not recognized, and will pass the notetrack name. Note that this
// function could be called multiple times for a single animation.
function DoNoteTracks( flagName, customFunction, debugIdentifier, var1 ) // debugIdentifier isn't even used. we should get rid of it.
{
for (;;)
{
self waittill (flagName, note);
if ( !isdefined( note ) )
{
note = "undefined";
}
val = self HandleNoteTrack( note, flagName, customFunction, var1 );
if ( isdefined( val ) )
{
return val;
}
}
}
function DoNoteTracksIntercept( flagName, interceptFunction, debugIdentifier ) // debugIdentifier isn't even used. we should get rid of it.
{
assert( isdefined( interceptFunction ) );
for (;;)
{
self waittill ( flagName, note );
if ( !isdefined( note ) )
{
note = "undefined";
}
intercepted = [[interceptFunction]]( note );
if ( isdefined( intercepted ) && intercepted )
{
continue;
}
val = self HandleNoteTrack( note, flagName );
if ( isdefined( val ) )
{
return val;
}
}
}
function DoNoteTracksPostCallback( flagName, postFunction )
{
assert( isdefined( postFunction ) );
for (;;)
{
self waittill ( flagName, note );
if ( !isdefined( note ) )
{
note = "undefined";
}
val = self HandleNoteTrack( note, flagName );
[[postFunction]]( note );
if ( isdefined( val ) )
{
return val;
}
}
}
// Don't call this function except as a thread you're going to kill - it lasts forever.
function DoNoteTracksForever(flagName, killString, customFunction, debugIdentifier)
{
DoNoteTracksForeverProc(&DoNoteTracks, flagName, killString, customFunction, debugIdentifier);
}
function DoNoteTracksForeverIntercept(flagName, killString, interceptFunction, debugIdentifier)
{
DoNoteTracksForeverProc(&DoNoteTracksIntercept, flagName, killString, interceptFunction, debugIdentifier );
}
function DoNoteTracksForeverProc( notetracksFunc, flagName, killString, customFunction, debugIdentifier )
{
if (isdefined (killString))
{
self endon (killString);
}
self endon ("killanimscript");
if (!isdefined(debugIdentifier))
{
debugIdentifier = "undefined";
}
for (;;)
{
time = GetTime();
returnedNote = [[notetracksFunc]](flagName, customFunction, debugIdentifier);
timetaken = GetTime() - time;
if ( timetaken < 0.05)
{
time = GetTime();
returnedNote = [[notetracksFunc]](flagName, customFunction, debugIdentifier);
timetaken = GetTime() - time;
if ( timetaken < 0.05)
{
/#println (GetTime()+" "+debugIdentifier+" shared::DoNoteTracksForever is trying to cause an infinite loop on anim "+flagName+", returned "+returnedNote+".");#/
wait ( 0.05 - timetaken );
}
}
//(GetTime()+" "+debugIdentifier+" DoNoteTracksForever returned in "+timetaken+" ms.");#/
}
}
// Designed for using DoNoteTracks on looping animations, so you can wait for a time instead of the "end" parameter
function DoNoteTracksForTime(time, flagName, customFunction, debugIdentifier)
{
ent = SpawnStruct();
ent thread doNoteTracksForTimeEndNotify(time);
DoNoteTracksForTimeProc(&DoNoteTracksForever, time, flagName, customFunction, debugIdentifier, ent);
}
function DoNoteTracksForTimeIntercept( time, flagName, interceptFunction, debugIdentifier)
{
ent = SpawnStruct();
ent thread doNoteTracksForTimeEndNotify(time);
DoNoteTracksForTimeProc(&DoNoteTracksForeverIntercept, time, flagName, interceptFunction, debugIdentifier, ent);
}
function DoNoteTracksForTimeProc( doNoteTracksForeverFunc, time, flagName, customFunction, debugIdentifier, ent)
{
ent endon ("stop_notetracks");
[[doNoteTracksForeverFunc]](flagName, undefined, customFunction, debugIdentifier);
}
function doNoteTracksForTimeEndNotify(time)
{
wait (time);
self notify ("stop_notetracks");
}