324 lines
13 KiB
Plaintext
324 lines
13 KiB
Plaintext
#using scripts\shared\ai_shared;
|
|
#using scripts\shared\array_shared;
|
|
#using scripts\shared\flag_shared;
|
|
#using scripts\shared\system_shared;
|
|
#using scripts\shared\trigger_shared;
|
|
#using scripts\shared\spawner_shared;
|
|
#using scripts\shared\util_shared;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#namespace AiSquads;
|
|
|
|
function autoexec __init__sytem__() { system::register("ai_squads",&__init__,undefined,undefined); }
|
|
|
|
function __init__()
|
|
{
|
|
level._squads = [];
|
|
|
|
actorSpawnerArray = GetActorSpawnerTeamArray( "axis" );
|
|
array::run_all( actorSpawnerArray, &spawner::add_spawn_function, &SquadMemberThink );
|
|
}
|
|
|
|
// ------- AI SQUAD -----------//
|
|
class Squad
|
|
{
|
|
var squadLeader;
|
|
var squadMembers;
|
|
var squadBreadCrumb;
|
|
|
|
constructor()
|
|
{
|
|
squadLeader = 0;
|
|
squadMembers = [];
|
|
squadBreadCrumb = [];
|
|
}
|
|
|
|
function addSquadBreadCrumbs( ai )
|
|
{
|
|
assert( squadLeader == ai );
|
|
|
|
if( Distance2DSquared( squadBreadCrumb, ai.origin ) >= 96 * 96 )
|
|
{
|
|
/# RecordCircle( ai.origin, 4, ( 0, 0, 1 ), "Animscript", ai ); #/
|
|
squadBreadCrumb = ai.origin;
|
|
}
|
|
}
|
|
|
|
function getSquadBreadCrumb()
|
|
{
|
|
return squadBreadCrumb;
|
|
}
|
|
|
|
function GetLeader()
|
|
{
|
|
return squadLeader;
|
|
}
|
|
|
|
function GetMembers()
|
|
{
|
|
return squadMembers;
|
|
}
|
|
|
|
function AddAIToSquad( ai )
|
|
{
|
|
if( !IsInArray( squadMembers, ai ) )
|
|
{
|
|
// TEMP : Turn off other behaviors of the robot, to make sure that he will respect and follow the squad
|
|
if( ai.archetype == "robot" )
|
|
{
|
|
ai ai::set_behavior_attribute( "move_mode", "squadmember" );
|
|
}
|
|
|
|
squadMembers[squadMembers.size] = ai;
|
|
}
|
|
}
|
|
|
|
function RemoveAIFromSqaud( ai )
|
|
{
|
|
if( IsInArray( squadMembers, ai ) )
|
|
{
|
|
ArrayRemoveValue( squadMembers, ai, false );
|
|
|
|
if( squadLeader === ai )
|
|
{
|
|
squadLeader = undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
function Think()
|
|
{
|
|
// update squad leader - pick the first one in the squadMembers list
|
|
// remove the squad if no one is left
|
|
if( (IsInt(squadLeader) && squadLeader == 0) || !IsDefined( squadLeader ) )
|
|
{
|
|
if( squadMembers.size > 0 )
|
|
{
|
|
squadLeader = squadMembers[0];
|
|
squadBreadCrumb = squadLeader.origin;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function private CreateSquad( squadName )
|
|
{
|
|
level._squads[squadName] = new Squad();
|
|
return level._squads[squadName];
|
|
}
|
|
|
|
function private RemoveSquad( squadName )
|
|
{
|
|
if( IsDefined( level._squads ) && IsDefined( level._squads[squadName] ) )
|
|
{
|
|
level._squads[squadName] = undefined;
|
|
}
|
|
}
|
|
|
|
function private GetSquad( squadName )
|
|
{
|
|
return level._squads[squadName];
|
|
}
|
|
|
|
function private ThinkSquad( squadName )
|
|
{
|
|
while(1)
|
|
{
|
|
if( [[ level._squads[squadName] ]]->Think() )
|
|
{
|
|
wait 0.5;
|
|
}
|
|
else
|
|
{
|
|
RemoveSquad( squadName );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function private SquadMemberDeath()
|
|
{
|
|
self waittill( "death" );
|
|
|
|
if( IsDefined( self.squadName ) && IsDefined( level._squads[self.squadName] ) )
|
|
{
|
|
[[ level._squads[self.squadName] ]]->RemoveAIFromSqaud( self );
|
|
}
|
|
}
|
|
|
|
function private SquadMemberThink()
|
|
{
|
|
self endon("death");
|
|
|
|
if( !IsDefined( self.script_aisquadname ) )
|
|
return;
|
|
|
|
// wait for other ai systems to initialize
|
|
wait 0.5;
|
|
|
|
// Assign the squad name
|
|
self.squadName = self.script_aisquadname;
|
|
|
|
if( IsDefined( self.squadName ) )
|
|
{
|
|
// Create squad if it does not exist, then create it
|
|
if( !IsDefined( level._squads[self.squadName] ) )
|
|
{
|
|
squad = CreateSquad( self.squadName );
|
|
newSquadCreated = true;
|
|
}
|
|
else
|
|
{
|
|
squad = GetSquad( self.squadName );
|
|
}
|
|
|
|
// Add this AI as a member of the squad
|
|
[[squad]]->AddAIToSquad( self );
|
|
|
|
// Handle the death of the sqaudMember and remove him from squad
|
|
self thread SquadMemberDeath();
|
|
|
|
// start ticking this squad
|
|
if( ( isdefined( newSquadCreated ) && newSquadCreated ) )
|
|
{
|
|
level thread ThinkSquad( self.squadName );
|
|
}
|
|
|
|
// Keep updating and following the leader of the squad
|
|
while(1)
|
|
{
|
|
squadLeader = [[ level._squads[self.squadName] ]]->GetLeader();
|
|
|
|
if( IsDefined( squadLeader ) && !(IsInt(squadLeader) && squadLeader == 0) )
|
|
{
|
|
if( squadLeader == self )
|
|
{
|
|
/# RecordEntText( self.squadName + ": LEADER", self, ( 0, 1, 0 ), "Animscript" ); #/
|
|
/# RecordEntText( self.squadName + ": LEADER", self, ( 0, 1, 0 ), "Animscript" ); #/
|
|
/# RecordCircle( self.origin, 300, ( 1, .5, 0 ), "Animscript", self ); #/
|
|
|
|
if( IsDefined( self.enemy ) )
|
|
{
|
|
self SetGoal( self.enemy );
|
|
}
|
|
|
|
[[squad]]->addSquadBreadCrumbs( self );
|
|
}
|
|
else
|
|
{
|
|
/# RecordLine( self.origin, squadLeader.origin, ( 0, 1, 0 ), "Animscript", self ); #/
|
|
/# RecordEntText( self.squadName+ ": FOLLOWER", self, ( 0, 1, 0 ), "Animscript" ); #/
|
|
|
|
followPosition = [[squad]]->getSquadBreadCrumb();
|
|
followDistSq = Distance2DSquared( self.goalPos, followPosition );
|
|
|
|
if( IsDefined( squadLeader.enemy ) )
|
|
{
|
|
if( !IsDefined( self.enemy ) || ( IsDefined( self.enemy ) && self.enemy != squadLeader.enemy ) )
|
|
self SetEntityTarget( squadLeader.enemy, 1 );
|
|
}
|
|
|
|
if( IsDefined( self.goalPos ) && followDistSq >= 16 * 16 )
|
|
{
|
|
if( followDistSq >= 150 * 150 )
|
|
{
|
|
self ai::set_behavior_attribute( "sprint", true );
|
|
}
|
|
else
|
|
{
|
|
self ai::set_behavior_attribute( "sprint", false );
|
|
}
|
|
|
|
self SetGoal( followPosition, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
wait 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------- UTILITY -----------//
|
|
function isFollowingSquadLeader( ai )
|
|
{
|
|
if( ai ai::get_behavior_attribute( "move_mode" ) != "squadmember" )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
squadMember = isSquadMember( ai );
|
|
currentSquadLeader = getSquadLeader( ai ) ;
|
|
isAISquadLeader = IsDefined( currentSquadLeader ) && currentSquadLeader == ai;
|
|
|
|
if( squadMember && !isAISquadLeader )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function isSquadMember( ai )
|
|
{
|
|
if( IsDefined( ai.squadName ) )
|
|
{
|
|
squad = GetSquad( ai.squadName );
|
|
|
|
if( IsDefined( squad ) )
|
|
{
|
|
return IsInArray( [[squad]]->GetMembers(), ai );
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function isSquadLeader( ai )
|
|
{
|
|
if( IsDefined( ai.squadName ) )
|
|
{
|
|
squad = GetSquad( ai.squadName );
|
|
|
|
if( IsDefined( squad ) )
|
|
{
|
|
squadLeader = [[squad]]->GetLeader();
|
|
|
|
return IsDefined( squadLeader ) && squadLeader == ai;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function getSquadLeader( ai )
|
|
{
|
|
if( IsDefined( ai.squadName ) )
|
|
{
|
|
squad = GetSquad( ai.squadName );
|
|
|
|
if( IsDefined( squad ) )
|
|
{
|
|
return [[squad]]->GetLeader();
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
} |