boiii-scripts/mp/killstreaks/_sentinel.gsc
2023-04-13 17:30:38 +02:00

588 lines
29 KiB
Plaintext

#using scripts\codescripts\struct;
#using scripts\shared\array_shared;
#using scripts\shared\audio_shared;
#using scripts\shared\callbacks_shared;
#using scripts\shared\challenges_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\math_shared;
#using scripts\shared\killstreaks_shared;
#using scripts\shared\statemachine_shared;
#using scripts\shared\system_shared;
#using scripts\shared\turret_shared;
#using scripts\shared\util_shared;
#using scripts\shared\vehicle_ai_shared;
#using scripts\shared\vehicle_death_shared;
#using scripts\shared\vehicle_shared;
#using scripts\shared\vehicles\_wasp;
#using scripts\shared\visionset_mgr_shared;
#using scripts\shared\scoreevents_shared;
#using scripts\shared\weapons\_heatseekingmissile;
#using scripts\mp\_util;
#using scripts\mp\gametypes\_shellshock;
#using scripts\mp\gametypes\_spawning;
#using scripts\mp\killstreaks\_airsupport;
#using scripts\mp\killstreaks\_helicopter;
#using scripts\mp\killstreaks\_killstreak_bundles;
#using scripts\mp\killstreaks\_killstreak_detect;
#using scripts\mp\killstreaks\_killstreak_hacking;
#using scripts\mp\killstreaks\_killstreaks;
#using scripts\mp\killstreaks\_killstreakrules;
#using scripts\mp\killstreaks\_qrdrone;
#using scripts\mp\killstreaks\_rcbomb;
#using scripts\mp\killstreaks\_remote_weapons;
#using scripts\mp\teams\_teams;
#precache( "string", "mpl_killstreak_sentinel_strt" );
#precache( "string", "KILLSTREAK_SENTINEL_HACKED" );
#precache( "string", "KILLSTREAK_SENTINEL_INBOUND" );
#precache( "string", "KILLSTREAK_SENTINEL_NOT_AVAILABLE" );
#precache( "string", "KILLSTREAK_SENTINEL_EARNED" );
#precache( "string", "KILLSTREAK_SENTINEL_NOT_PLACEABLE" );
#precache( "string", "KILLSTREAK_DESTROYED_SENTINEL" );
#precache( "triggerstring", "KILLSTREAK_SENTINEL_USE_REMOTE" );
#namespace sentinel;
function init()
{
killstreaks::register( "sentinel", "sentinel", "killstreak_" + "sentinel", "sentinel" + "_used", &ActivateSentinel, true );
killstreaks::register_strings( "sentinel", &"KILLSTREAK_SENTINEL_EARNED", &"KILLSTREAK_SENTINEL_NOT_AVAILABLE", &"KILLSTREAK_SENTINEL_INBOUND", undefined, &"KILLSTREAK_SENTINEL_HACKED" );
killstreaks::register_dialog( "sentinel", "mpl_killstreak_sentinel_strt", "sentinelDialogBundle", "sentinelPilotDialogBundle", "friendlySentinel", "enemySentinel", "enemySentinelMultiple", "friendlySentinelHacked", "enemySentinelHacked", "requestSentinel", "threatSentinel" );
killstreaks::register_alt_weapon( "sentinel", "killstreak_remote" );
killstreaks::register_alt_weapon( "sentinel", "sentinel_turret" );
remote_weapons::RegisterRemoteWeapon( "sentinel", &"KILLSTREAK_SENTINEL_USE_REMOTE", &StartSentinelRemoteControl, &EndSentinelRemoteControl, false );
// TODO: Move to killstreak data
level.killstreaks["sentinel"].threatOnKill = true;
vehicle::add_main_callback( "veh_sentinel_mp", &InitSentinel );
visionset_mgr::register_info( "visionset", "sentinel_visionset", 1, 100, 16, true, &visionset_mgr::ramp_in_out_thread_per_player_death_shutdown, false );
}
function InitSentinel()
{
self.settings = struct::get_script_bundle( "vehiclecustomsettings", self.scriptbundlesettings );
Target_Set( self, ( 0, 0, 0 ) );
self.health = self.healthdefault;
self.numFlares = 1;
self.damageTaken = 0;
self vehicle::friendly_fire_shield();
self EnableAimAssist();
self SetNearGoalNotifyDist( ( 40 ) );
self SetHoverParams( ( 50.0 ), ( 100.0 ), ( 100.0 ) );
self.fovcosine = 0; // +/-90 degrees = 180
self.fovcosinebusy = 0; //+/- 55 degrees = 110 fov
self.vehAirCraftCollisionEnabled = true;
self thread vehicle_ai::nudge_collision();
self thread heatseekingmissile::MissileTarget_ProximityDetonateIncomingMissile( "explode", "death" ); // fires chaff if needed
self thread helicopter::create_flare_ent( (0,0,-20) );
self thread audio::vehicleSpawnContext();
self.do_scripted_crash = false;
self.overrideVehicleDamage = &SentinelDamageOverride;
self.selfDestruct = false;
self.enable_target_laser = true;
self.aggresive_navvolume_recover = true;
self vehicle_ai::init_state_machine_for_role( "default" );
self vehicle_ai::get_state_callbacks( "combat" ).enter_func = &wasp::state_combat_enter;
self vehicle_ai::get_state_callbacks( "combat" ).update_func = &wasp::state_combat_update;
self vehicle_ai::get_state_callbacks( "death" ).update_func = &wasp::state_death_update;
self vehicle_ai::get_state_callbacks( "driving" ).enter_func = &driving_enter;
wasp::init_guard_points();
self vehicle_ai::add_state( "guard",
&wasp::state_guard_enter,
&wasp::state_guard_update,
&wasp::state_guard_exit );
vehicle_ai::add_utility_connection( "combat", "guard", &wasp::state_guard_can_enter );
vehicle_ai::add_utility_connection( "guard", "combat" );
vehicle_ai::add_interrupt_connection( "guard", "emped", "emped" );
vehicle_ai::add_interrupt_connection( "guard", "surge", "surge" );
vehicle_ai::add_interrupt_connection( "guard", "off", "shut_off" );
vehicle_ai::add_interrupt_connection( "guard", "pain", "pain" );
vehicle_ai::add_interrupt_connection( "guard", "driving", "enter_vehicle" );
self vehicle_ai::StartInitialState( "combat" );
}
function driving_enter( params )
{
vehicle_ai::defaultstate_driving_enter( params );
}
function drone_pain_for_time( time, stablizeParam, restoreLookPoint, weapon )
{
self endon( "death" );
self.painStartTime = GetTime();
if ( !( isdefined( self.inpain ) && self.inpain ) && isdefined( self.health ) && self.health > 0 )
{
self.inpain = true;
while ( GetTime() < self.painStartTime + time * 1000 )
{
self SetVehVelocity( self.velocity * stablizeParam );
self SetAngularVelocity( self GetAngularVelocity() * stablizeParam );
wait 0.1;
}
if ( isdefined( restoreLookPoint ) && isdefined( self.health ) && self.health > 0 )
{
restoreLookEnt = Spawn( "script_model", restoreLookPoint );
restoreLookEnt SetModel( "tag_origin" );
self ClearLookAtEnt();
self SetLookAtEnt( restoreLookEnt );
self setTurretTargetEnt( restoreLookEnt );
wait 1.5;
self ClearLookAtEnt();
self ClearTurretTarget();
restoreLookEnt delete();
}
if( weapon.isEmp ) remote_weapons::set_static( 0 );
self.inpain = false;
}
}
function drone_pain( eAttacker, damageType, hitPoint, hitDirection, hitLocationInfo, partName, weapon )
{
if ( !( isdefined( self.inpain ) && self.inpain ) )
{
yaw_vel = math::randomSign() * RandomFloatRange( 280, 320 );
ang_vel = self GetAngularVelocity();
ang_vel += ( RandomFloatRange( -120, -100 ), yaw_vel, RandomFloatRange( -200, 200 ) );
self SetAngularVelocity( ang_vel );
self thread drone_pain_for_time( 0.8, 0.7, undefined, weapon );
}
}
function SentinelDamageOverride( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
{
if( sMeansOfDeath == "MOD_TRIGGER_HURT" )
return 0;
emp_damage = self.healthdefault * ( 0.5 ) + 0.5;
iDamage = killstreaks::OnDamagePerWeapon( "sentinel", eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, self.maxhealth, &destroyed_cb, self.maxhealth*0.4, &low_health_cb, emp_damage, undefined, true, 1.0 );
if( isdefined( eAttacker ) && isdefined( eAttacker.team ) && eAttacker.team != self.team )
{
drone_pain( eAttacker, sMeansOfDeath, vPoint, vDir, sHitLoc, partName, weapon );
}
self.damageTaken += iDamage;
return iDamage;
}
function destroyed_cb( attacker, weapon )
{
if( isdefined( attacker ) && isdefined( attacker.team ) && attacker.team != self.team )
self.owner.dofutz = true;
}
function low_health_cb( attacker, weapon )
{
if( self.playedDamaged == false )
{
self killstreaks::play_pilot_dialog_on_owner( "damaged", "sentinel", self.killstreak_id );
self.playedDamaged = true;
}
}
function CalcSpawnOrigin( origin, angles )
{
heightOffset = rcbomb::GetPlacementStartHeight();
mins = ( -5, -5, 0 );
maxs = ( 5, 5, 10 );
startPoints = [];
testangles = [];
testangles[0] = ( 0, 0, 0 );
testangles[1] = ( 0, 30, 0 );
testangles[2] = ( 0, -30, 0 );
testangles[3] = ( 0, 60, 0 );
testangles[4] = ( 0, -60, 0 );
testangles[3] = ( 0, 90, 0 );
testangles[4] = ( 0, -90, 0 );
bestOrigin = origin;
bestAngles = angles;
bestFrac = 0;
for( i = 0; i < testangles.size; i++ )
{
startPoint = origin + ( 0, 0, heightOffset );
endPoint = startPoint + VectorScale( anglestoforward( ( 0, angles[1], 0 ) + testangles[i] ), ( 70 ) );
mask = (1 << 0) | (1 << 1);
trace = physicstrace( startPoint, endPoint, mins, maxs, self, mask );
if( isdefined( trace["entity"] ) && IsPlayer( trace["entity"] ) )
continue;
if( trace["fraction"] > bestFrac )
{
bestFrac = trace["fraction"];
bestOrigin = trace["position"];
bestAngles = ( 0, angles[1], 0 ) + testangles[i];
if( bestFrac == 1 )
break;
}
}
if( bestFrac > 0 )
{
if( Distance2DSquared( origin, bestOrigin ) < 20 * 20 )
return undefined;
trace = physicstrace( bestOrigin, bestOrigin + ( 0, 0, ( 25 ) ), mins, maxs, self, mask );
placement = SpawnStruct();
placement.origin = trace["position"];
placement.angles = bestAngles;
return placement;
}
else
return undefined;
}
function ActivateSentinel( killstreakType )
{
assert( IsPlayer( self ) );
player = self;
if( !IsNavVolumeLoaded() )
{
/# IPrintLnBold( "Error: NavVolume Not Loaded" ); #/
self iPrintLnBold( &"KILLSTREAK_SENTINEL_NOT_AVAILABLE" );
return false;
}
if( player IsPlayerSwimming() )
{
self iPrintLnBold( &"KILLSTREAK_SENTINEL_NOT_PLACEABLE" );
return false;
}
spawnPos = CalcSpawnOrigin( player.origin, player.angles );
if( !isdefined( spawnPos ) )
{
self iPrintLnBold( &"KILLSTREAK_SENTINEL_NOT_PLACEABLE" );
return false;
}
killstreak_id = player killstreakrules::killstreakStart( "sentinel", player.team, false, true );
if( killstreak_id == (-1) )
{
return false;
}
player AddWeaponStat( GetWeapon( "sentinel" ), "used", 1 );
sentinel = SpawnVehicle( "veh_sentinel_mp", spawnPos.origin, spawnPos.angles, "dynamic_spawn_ai" );
sentinel killstreaks::configure_team( "sentinel", killstreak_id, player, "small_vehicle", undefined, &ConfigureTeamPost );
sentinel killstreak_hacking::enable_hacking( "sentinel", &HackedCallbackPre, &HackedCallbackPost );
sentinel.killstreak_id = killstreak_id;
sentinel.killstreak_end_time = GetTime() + ( 60000 );
sentinel.original_vehicle_type = sentinel.vehicletype;
sentinel.ignore_vehicle_underneath_splash_scalar = true;
sentinel clientfield::set( "enemyvehicle", 1 );
sentinel.hardpointType = "sentinel";
sentinel.soundmod = "player";
sentinel.maxhealth = killstreak_bundles::get_max_health( "sentinel" );
sentinel.lowhealth = killstreak_bundles::get_low_health( "sentinel" );
sentinel.health = sentinel.maxhealth;
sentinel.hackedhealth = killstreak_bundles::get_hacked_health( "sentinel" );
sentinel.rocketDamage = ( sentinel.maxhealth / ( 1 ) ) + 1;
sentinel.playedDamaged = false;
sentinel.treat_owner_damage_as_friendly_fire = true;
sentinel.ignore_team_kills = true;
sentinel thread HealthMonitor();
sentinel.goalradius = ( 1200 );
sentinel.goalHeight = 500;
//sentinel SetGoal( player, false, sentinel.goalRadius, sentinel.goalHeight );
sentinel.enable_guard = true;
sentinel.always_face_enemy = true;
sentinel thread killstreaks::WaitForTimeout( "sentinel", ( 60000 ), &OnTimeout, "sentinel_shutdown" );
sentinel thread WatchWater();
sentinel thread WatchDeath();
sentinel thread WatchShutdown();
player remote_weapons::UseRemoteWeapon( sentinel, "sentinel", false );
sentinel killstreaks::play_pilot_dialog_on_owner( "arrive", "sentinel", killstreak_id );
sentinel vehicle::init_target_group();
sentinel vehicle::add_to_target_group( sentinel );
self killstreaks::play_killstreak_start_dialog( "sentinel", self.team, killstreak_id );
sentinel thread WatchGameEnded();
return true;
}
function HackedCallbackPre( hacker )
{
sentinel = self;
sentinel.owner unlink();
sentinel clientfield::set( "vehicletransition", 0 );
if( sentinel.controlled === true )
visionset_mgr::deactivate( "visionset", "sentinel_visionset", sentinel.owner );
sentinel.owner remote_weapons::RemoveAndAssignNewRemoteControlTrigger( sentinel.useTrigger );
sentinel remote_weapons::EndRemoteControlWeaponUse( true );
EndSentinelRemoteControl( sentinel, true );
}
function HackedCallbackPost( hacker )
{
sentinel = self;
hacker remote_weapons::UseRemoteWeapon( sentinel, "sentinel", false );
sentinel notify("WatchRemoteControlDeactivate_remoteWeapons");
sentinel.killstreak_end_time = hacker killstreak_hacking::set_vehicle_drivable_time_starting_now( sentinel );
}
function ConfigureTeamPost( owner, isHacked )
{
sentinel = self;
sentinel thread WatchTeamChange();
}
function WatchGameEnded()
{
sentinel = self;
sentinel endon( "death" );
level waittill("game_ended");
sentinel.abandoned = true;
sentinel.selfDestruct = true;
sentinel notify( "sentinel_shutdown" );
}
function StartSentinelRemoteControl( sentinel )
{
player = self;
assert( IsPlayer( player ) );
sentinel UseVehicle( player, 0 );
sentinel clientfield::set( "vehicletransition", 1 );
sentinel thread audio::sndUpdateVehicleContext(true);
sentinel thread vehicle::monitor_missiles_locked_on_to_me( player );
sentinel.inHeliProximity = false;
sentinel.treat_owner_damage_as_friendly_fire = false;
sentinel.ignore_team_kills = false;
minHeightOverride = undefined;
minz_struct = struct::get( "vehicle_oob_minz", "targetname");
if( isdefined( minz_struct ) )
minHeightOverride = minz_struct.origin[2];
sentinel thread qrdrone::QRDrone_watch_distance( ( 0 ), minHeightOverride );
sentinel.distance_shutdown_override = &SentinelDistanceFailure;
player vehicle::set_vehicle_drivable_time( ( 60000 ), sentinel.killstreak_end_time );
visionset_mgr::activate( "visionset", "sentinel_visionset", player, 1, 90000, 1 );
if ( isdefined( sentinel.PlayerDrivenVersion ) )
sentinel SetVehicleType( sentinel.PlayerDrivenVersion );
}
function EndSentinelRemoteControl( sentinel, exitRequestedByOwner )
{
sentinel.treat_owner_damage_as_friendly_fire = true;
sentinel.ignore_team_kills = true;
if ( isdefined( sentinel.owner ) )
{
sentinel.owner vehicle::stop_monitor_missiles_locked_on_to_me();
if( sentinel.controlled === true )
visionset_mgr::deactivate( "visionset", "sentinel_visionset", sentinel.owner );
}
if( exitRequestedByOwner )
{
if ( isdefined( sentinel.owner ) )
{
sentinel.owner qrdrone::destroyHud();
sentinel.owner unlink();
sentinel clientfield::set( "vehicletransition", 0 );
}
sentinel thread audio::sndUpdateVehicleContext(false);
}
if ( isdefined( sentinel.original_vehicle_type ) )
sentinel SetVehicleType( sentinel.original_vehicle_type );
}
function OnTimeout()
{
sentinel = self;
sentinel killstreaks::play_pilot_dialog_on_owner( "timeout", "sentinel" );
params = level.killstreakBundle["sentinel"];
if( isdefined( sentinel.owner ) )
{
RadiusDamage( sentinel.origin,
params.ksExplosionOuterRadius,
params.ksExplosionInnerDamage,
params.ksExplosionOuterDamage,
sentinel.owner,
"MOD_EXPLOSIVE",
GetWeapon( "sentinel" ) );
if( isdefined( params.ksExplosionRumble ) )
sentinel.owner PlayRumbleOnEntity( params.ksExplosionRumble );
}
sentinel notify( "sentinel_shutdown" );
}
function HealthMonitor()
{
self endon( "death" );
params = level.killstreakBundle["sentinel"];
if( isdefined( params.fxLowHealth ) )
{
while( 1 )
{
if( self.lowhealth > self.health )
{
PlayFXOnTag( params.fxLowHealth, self, "tag_origin" );
break;
}
{wait(.05);};
}
}
}
function SentinelDistanceFailure()
{
sentinel = self;
sentinel notify( "sentinel_shutdown" );
}
function WatchDeath()
{
sentinel = self;
sentinel waittill( "death", attacker, damageFromUnderneath, weapon, point, dir, modType );
sentinel notify( "sentinel_shutdown" );
attacker = self [[ level.figure_out_attacker ]]( attacker );
if ( isdefined( attacker ) && ( !isdefined( self.owner ) || self.owner util::IsEnemyPlayer( attacker ) ) )
{
if ( isPlayer( attacker ) )
{
challenges::destroyedAircraft( attacker, weapon, sentinel.controlled === true );
attacker challenges::addFlySwatterStat( weapon, self );
attacker AddWeaponStat( weapon, "destroy_aitank_or_setinel", 1 );
scoreevents::processScoreEvent( "destroyed_sentinel", attacker, sentinel.owner, weapon );
if ( modType == "MOD_RIFLE_BULLET" || modType == "MOD_PISTOL_BULLET" )
{
attacker addPlayerStat( "shoot_down_sentinel", 1 );
}
LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_DESTROYED_SENTINEL", attacker.entnum );
}
if ( isdefined( sentinel ) && isdefined( sentinel.owner ) )
{
sentinel killstreaks::play_destroyed_dialog_on_owner( "sentinel", sentinel.killstreak_id );
}
}
}
function WatchTeamChange()
{
self notify( "Sentinel_WatchTeamChange_Singleton" );
self endon ( "Sentinel_WatchTeamChange_Singleton" );
sentinel = self;
sentinel endon( "sentinel_shutdown" );
sentinel.owner util::waittill_any( "joined_team", "disconnect", "joined_spectators" );
sentinel notify( "sentinel_shutdown" );
}
function WatchWater()
{
sentinel = self;
sentinel endon( "sentinel_shutdown" );
while( true )
{
wait ( 0.1 );
trace = physicstrace( self.origin + ( 0, 0, 10 ), self.origin + ( 0, 0, 6 ), ( -2, -2, -2 ), ( 2, 2, 2 ), self, ( (1 << 2) ));
if( trace["fraction"] < 1.0 )
break;
}
sentinel notify( "sentinel_shutdown" );
}
function WatchShutdown()
{
sentinel = self;
sentinel waittill( "sentinel_shutdown" );
if( ( isdefined( sentinel.control_initiated ) && sentinel.control_initiated ) || ( isdefined( sentinel.controlled ) && sentinel.controlled ) )
{
sentinel remote_weapons::EndRemoteControlWeaponUse( false );
while( ( isdefined( sentinel.control_initiated ) && sentinel.control_initiated ) || ( isdefined( sentinel.controlled ) && sentinel.controlled ) )
{wait(.05);};
}
if( isdefined( sentinel.owner ) )
{
sentinel.owner qrdrone::destroyHud();
}
killstreakrules::killstreakStop( "sentinel", sentinel.originalTeam, sentinel.killstreak_id );
if( isalive( sentinel ) )
sentinel Kill();
}