boiii-scripts/shared/ai/systems/ai_squads.gsc
2023-04-13 17:30:38 +02:00

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