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

635 lines
29 KiB
Plaintext

#using scripts\shared\ai_shared;
#using scripts\shared\callbacks_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\fx_shared;
#using scripts\shared\laststand_shared;
#using scripts\shared\math_shared;
#using scripts\shared\scene_shared;
#using scripts\shared\spawner_shared;
#using scripts\shared\util_shared;
#using scripts\shared\array_shared;
#using scripts\shared\system_shared;
#using scripts\shared\ai\systems\animation_state_machine_utility;
#using scripts\shared\ai\systems\animation_state_machine_notetracks;
#using scripts\shared\ai\archetype_utility;
#using scripts\shared\ai\systems\behavior_tree_utility;
#using scripts\shared\ai\systems\blackboard;
#using scripts\shared\ai\systems\debug;
#using scripts\shared\ai\systems\gib;
#using scripts\codescripts\struct;
#using scripts\shared\ai\archetype_mocomps_utility;
#namespace SkeletonBehavior;
function autoexec __init__sytem__() { system::register("skeleton",&__init__,undefined,undefined); }
function __init__()
{
// INIT BEHAVIORS
InitSkeletonBehaviorsAndASM();
// INIT BLACKBOARD
spawner::add_archetype_spawn_function( "skeleton", &ArchetypeSkeletonBlackboardInit );
// INIT SKELETON ON SPAWN
spawner::add_archetype_spawn_function( "skeleton", &skeletonSpawnSetup );
if( ai::shouldRegisterClientFieldForArchetype( "skeleton" ) )
{
clientfield::register(
"actor",
"skeleton",
1,
1,
"int");
}
}
function skeletonSpawnSetup()
{
self.zombie_move_speed = "walk";
if(randomint( 2 ) == 0)
self.zombie_arms_position = "up";
else
self.zombie_arms_position = "down";
self.missingLegs = false;
self setAvoidanceMask( "avoid none" );
self PushActors( true );
clientfield::set( "skeleton", true );
}
function private InitSkeletonBehaviorsAndASM()
{
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("skeletonTargetService",&skeletonTargetService);;
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("skeletonShouldMelee",&skeletonShouldMeleeCondition);;
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("skeletonGibLegsCondition",&skeletonGibLegsCondition);;
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("isSkeletonWalking",&isSkeletonWalking);;
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("skeletonDeathAction",&skeletonDeathAction);;
AnimationStateNetwork::RegisterNotetrackHandlerFunction("contact",&skeletonNotetrackMeleeFire);;
}
function private ArchetypeSkeletonBlackboardInit()
{
// CREATE BLACKBOARD
Blackboard::CreateBlackBoardForEntity( self );
// USE UTILITY BLACKBOARD
self AiUtility::RegisterUtilityBlackboardAttributes();
// CREATE SKELETON BLACKBOARD
Blackboard::RegisterBlackBoardAttribute(self,"_arms_position","arms_up",&BB_GetArmsPosition); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_arms_position");#/ };
Blackboard::RegisterBlackBoardAttribute(self,"_locomotion_speed","locomotion_speed_walk",&BB_GetLocomotionSpeedType); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_locomotion_speed");#/ };
Blackboard::RegisterBlackBoardAttribute(self,"_has_legs","has_legs_yes",&BB_GetHasLegsStatus); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_has_legs");#/ };
Blackboard::RegisterBlackBoardAttribute(self,"_which_board_pull",undefined,undefined); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_which_board_pull");#/ };
Blackboard::RegisterBlackBoardAttribute(self,"_board_attack_spot",undefined,undefined); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_board_attack_spot");#/ };
// REGISTER ANIMSCRIPTED CALLBACK
self.___ArchetypeOnAnimscriptedCallback = &ArchetypeSkeletonOnAnimscriptedCallback;
// ENABLE DEBUGGING IN ODYSSEY
/#self FinalizeTrackedBlackboardAttributes();#/;
}
function private ArchetypeSkeletonOnAnimscriptedCallback( entity )
{
// UNREGISTER THE BLACKBOARD
entity.__blackboard = undefined;
// REREGISTER BLACKBOARD
entity ArchetypeSkeletonBlackboardInit();
}
// ------- BLACKBOARD -----------//
function BB_GetArmsPosition()
{
if( IsDefined( self.skeleton_arms_position ) )
{
if( self.zombie_arms_position == "up" )
return "arms_up";
return "arms_down";
}
return "arms_up";
}
function BB_GetLocomotionSpeedType()
{
if ( IsDefined( self.zombie_move_speed ) )
{
if( self.zombie_move_speed == "walk" )
{
return "locomotion_speed_walk";
}
else if( self.zombie_move_speed == "run" )
{
return "locomotion_speed_run";
}
else if( self.zombie_move_speed == "sprint" )
{
return "locomotion_speed_sprint";
}
else if( self.zombie_move_speed == "super_sprint" )
{
return "locomotion_speed_super_sprint";
}
}
return "locomotion_speed_walk";
}
function BB_GetHasLegsStatus()
{
if( self.missingLegs )
return "has_legs_no";
return "has_legs_yes";
}
// ------- BLACKBOARD -----------//
function isSkeletonWalking( behaviorTreeEntity )
{
if( !isdefined(behaviorTreeEntity.zombie_move_speed))
return true;
return behaviorTreeEntity.zombie_move_speed == "walk" && !( isdefined( behaviorTreeEntity.missingLegs ) && behaviorTreeEntity.missingLegs ) && behaviorTreeEntity.zombie_arms_position == "up";
}
function skeletonGibLegsCondition( behaviorTreeEntity)
{
return GibServerUtils::IsGibbed( behaviorTreeEntity, 256) || GibServerUtils::IsGibbed( behaviorTreeEntity, 128);
}
function skeletonNotetrackMeleeFire( animationEntity )
{
hitEnt = animationentity Melee();
if(isDefined(hitEnt) && isDefined(animationentity.aux_melee_damage) && self.team != hitEnt.team )
{
animationentity [[animationentity.aux_melee_damage]](hitEnt);
}
}
function is_within_fov( start_origin, start_angles, end_origin, fov )
{
normal = VectorNormalize( end_origin - start_origin );
forward = AnglesToForward( start_angles );
dot = VectorDot( forward, normal );
return dot >= fov;
}
function skeletonCanSeePlayer( player )
{
self endon( "death" );
if(!isDefined(self.players_visCache))
{
self.players_visCache = [];
}
entNum = player GetEntityNumber();
if(!isDefined(self.players_visCache[entNum]))
{
self.players_visCache[entNum] = 0;
}
if (self.players_visCache[entNum] > GetTime() )
{
return true;
}
//zombie_eye = self GetTagOrigin( "tag_eye" );
zombie_eye = self.origin + ( 0, 0, 40);
player_pos = player.origin + ( 0, 0, 40);
//as_debug::drawDebugCross( zombie_eye, 5.0, ( 0.0, 0.0, 1.0 ), 1 );
//as_debug::drawDebugCross( player_pos, 5.0, ( 0.0, 1.0, 1.0 ), 1 );
distanceSq = DistanceSquared( zombie_eye, player_pos );
//Check to see if the skeleton is super close OR super far
if( distanceSq < 64 * 64 )
{
self.players_visCache[entNum] = GetTime() + 3000;
return true;
}
else if( distanceSq > 1024*1024 )
{
return false;
}
if( is_within_fov( zombie_eye, self.angles, player_pos, cos(60.0) ) )
{
trace = GroundTrace( zombie_eye, player_pos, false, undefined );
if( trace["fraction"] < 1.0 )
{
return false;
}
else
{
//as_debug::debugLine( zombie_eye, player_pos, ( 0.0, 1.0, 1.0 ), 1 );
self.players_visCache[entNum] = GetTime() + 3000;
return true;
}
}
return false;
}
function is_player_valid( player, checkIgnoreMeFlag, ignore_laststand_players )
{
if( !IsDefined( player ) )
{
return false;
}
if( !IsAlive( player ) )
{
return false;
}
if( !IsPlayer( player ) )
{
return false;
}
if( IsDefined(player.is_zombie) && player.is_zombie == true )
{
return false;
}
if( player.sessionstate == "spectator" )
{
return false;
}
if( player.sessionstate == "intermission" )
{
return false;
}
if( ( isdefined( self.intermission ) && self.intermission ) )
{
return false;
}
if(!( isdefined( ignore_laststand_players ) && ignore_laststand_players ))
{
if( player laststand::player_is_in_laststand() )
{
return false;
}
}
//T6.5todo if ( player isnotarget() )
//T6.5todo {
//T6.5todo return false;
//T6.5todo }
//We only want to check this from the zombie attack script
if( ( isdefined( checkIgnoreMeFlag ) && checkIgnoreMeFlag ) && player.ignoreme )
{
return false;
}
//for additional level specific checks
if( IsDefined( level.is_player_valid_override ) )
{
return [[ level.is_player_valid_override ]]( player );
}
return true;
}
function get_closest_valid_player( origin, ignore_player )
{
valid_player_found = false;
players = GetPlayers();
if( IsDefined( ignore_player ) )
{
//PI_CHANGE_BEGIN - 7/2/2009 JV Reenabling change 274916 (from DLC3)
for(i = 0; i < ignore_player.size; i++ )
{
ArrayRemoveValue( players, ignore_player[i] );
}
//PI_CHANGE_END
}
// pre-cull any players that are in last stand
done = false;
while ( players.size && !done )
{
done = true;
for(i = 0; i < players.size; i++ )
{
player = players[i];
if( !is_player_valid( player, true ) )
{
ArrayRemoveValue( players, player );
done = false;
break;
}
}
}
if( players.size == 0 )
{
return undefined;
}
while( !valid_player_found )
{
// find the closest player
if( IsDefined( self.closest_player_override ) )
{
player = [[ self.closest_player_override ]]( origin, players );
}
else if( isdefined( level.closest_player_override ) )
{
player = [[ level.closest_player_override ]]( origin, players );
}
else
{
player = ArrayGetClosest( origin, players );
}
if( !isdefined( player ) || players.size == 0 )
{
return undefined;
}
// make sure they're not a zombie or in last stand
if( !is_player_valid( player, true ) )
{
// unlikely to get here unless there is a wait in one of the closest player overrides
ArrayRemoveValue( players, player );
if( players.size == 0 )
{
return undefined;
}
continue;
}
return player;
}
}
function skeletonSetGoal(goal)
{
if(isDefined(self.setGoalOverrideCB))
{
return [[self.setGoalOverrideCB]](goal);
}
else
{
self SetGoal(goal);
}
}
function skeletonTargetService( behaviorTreeEntity)
{
self endon( "death" );
if ( ( isdefined( behaviorTreeEntity.ignoreall ) && behaviorTreeEntity.ignoreall ) )
{
return false;
}
if(isDefined(behaviorTreeEntity.enemy) && behaviorTreeEntity.enemy.team == behaviorTreeEntity.team )
behaviorTreeEntity ClearEntityTarget();
if(behaviorTreeEntity.team == "allies")
{
if(isDefined(behaviorTreeEntity.favoriteenemy))
{
behaviorTreeEntity skeletonSetGoal( behaviorTreeEntity.favoriteenemy.origin );
return true;
}
if(isDefined(behaviorTreeEntity.enemy))
{
behaviorTreeEntity skeletonSetGoal( behaviorTreeEntity.enemy.origin );
return true;
}
target = getClosestToMe(GetAITeamArray( "axis" ));
if(isDefined(target))
{
behaviorTreeEntity skeletonSetGoal( target.origin );
return true;
}
else
{
behaviorTreeEntity skeletonSetGoal( behaviorTreeEntity.origin );
return false;
}
}
else
{
player = get_closest_valid_player( behaviorTreeEntity.origin, behaviorTreeEntity.ignore_player );
if( !isDefined( player ) )
{
if( IsDefined( behaviorTreeEntity.ignore_player ) )
{
if(isDefined(level._should_skip_ignore_player_logic) && [[level._should_skip_ignore_player_logic]]() )
{
return;
}
behaviorTreeEntity.ignore_player = [];
}
behaviorTreeEntity skeletonSetGoal( behaviorTreeEntity.origin );
return false;
}
else
{
if ( IsDefined( player.last_valid_position ) )
{
canSEE = self skeletonCanSeePlayer( player );
//Fill in influence map
if( canSEE )
{
//self SetInfluenceAt( 5, player.last_valid_position, 1.0 );
behaviorTreeEntity skeletonSetGoal( player.last_valid_position );
//as_debug::drawDebugCross( player.last_valid_position, 5.0, ( 0.0, 1.0, 0.0 ), 5 );
return true;
}
else
{
influencePos = undefined;
//try to find player - get hottest point on influence map and go there
//influencePos = self GetBestInfluencepos( 5, 0.01, 1.0 );
if( isdefined(influencePos) )
{
//as_debug::drawDebugCross( influencePos, 5.0, ( 1.0, 0.0, 0.0 ), 5 );
//Are we close to our goal?
if( DistanceSquared( influencePos, behaviorTreeEntity.origin ) > 32*32 )
{
behaviorTreeEntity skeletonSetGoal( influencePos );
return true;
}
//self SetGoal( self.origin );
behaviorTreeEntity ClearPath();
return false;
}
else
{
//self SetGoal( self.origin );
behaviorTreeEntity ClearPath();
return false;
}
}
/*attackPlayer = false;
if( DistanceSquared( player.last_valid_position, self.origin ) < (512*512) )
{
if( canSEE )
{
behaviorTreeEntity SetGoal( player.last_valid_position );
attackPlayer = true;
}
}
if( !attackPlayer )
{
//Get hottest point on influence map and go there
influencePos = self GetBestInfluencepos( 5, 0.01, 1.0 );
if( isdefined(influencePos) )
{
as_debug::drawDebugCross( influencePos, 5.0, ( 1.0, 0.0, 0.0 ), 5 );
self SetGoal( influencePos );
return true;
}
else
{
self SetGoal( self.origin );
return false;
}
}*/
return true;
}
else
{
behaviorTreeEntity skeletonSetGoal( behaviorTreeEntity.origin );
return false;
}
}
}
}
function isValidEnemy( enemy )
{
if ( !isdefined( enemy ) )
{
return false;
}
return true;
}
function GetYaw(org)
{
angles = VectorToAngles(org-self.origin);
return angles[1];
}
// warning! returns (my yaw - yaw to enemy) instead of (yaw to enemy - my yaw)
function GetYawToEnemy()
{
pos = undefined;
if ( isValidEnemy( self.enemy ) )
{
pos = self.enemy.origin;
}
else
{
forward = AnglesToForward(self.angles);
forward = VectorScale (forward, 150);
pos = self.origin + forward;
}
yaw = self.angles[1] - GetYaw(pos);
yaw = AngleClamp180( yaw );
return yaw;
}
function skeletonShouldMeleeCondition( behaviorTreeEntity )
{
if( !IsDefined( behaviortreeentity.enemy ) )
{
return false;
}
if( IsDefined( behaviorTreeEntity.marked_for_death ) )
{
return false;
}
if( ( isdefined( behaviorTreeEntity.stunned ) && behaviorTreeEntity.stunned ) )
{
return false;
}
yaw = abs( getYawToEnemy() );
if( ( yaw > 45 ) )
{
return false;
}
if( DistanceSquared( behaviorTreeEntity.origin, behaviorTreeEntity.enemy.origin ) < 64 * 64 )
{
return true;
}
return false;
}
function skeletonDeathAction( behaviorTreeEntity )
{
if ( IsDefined( behaviorTreeEntity.deathFunction ) )
{
behaviorTreeEntity [[ behaviorTreeEntity.deathFunction ]]();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function getClosestTo(origin,entArray)
{
if (!isDefined(entArray) )
return;
if (entArray.size == 0 )
return;
return ArrayGetClosest( origin, entArray );
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function getClosestToMe(entArray)
{
return getClosestTo(self.origin,entArray);
}