1 #using scripts\codescripts\struct;
3 #using scripts\shared\_oob;
4 #using scripts\shared\array_shared;
5 #using scripts\shared\callbacks_shared;
6 #using scripts\shared\challenges_shared;
7 #using scripts\shared\clientfield_shared;
8 #using scripts\shared\hud_shared;
9 #using scripts\shared\killstreaks_shared;
10 #using scripts\shared\scoreevents_shared;
11 #using scripts\shared\system_shared;
12 #using scripts\shared\util_shared;
13 #using scripts\shared\weapons\_weapons;
14 #using scripts\shared\vehicle_ai_shared;
15 #using scripts\shared\vehicle_shared;
16 #using scripts\shared\vehicles\_amws;
18 #using scripts\mp\_challenges;
19 #using scripts\mp\_util;
20 #using scripts\mp\gametypes\_dev;
21 #using scripts\mp\gametypes\_spawning;
22 #using scripts\mp\gametypes\_spawnlogic;
23 #using scripts\mp\killstreaks\_dogs;
24 #using scripts\mp\killstreaks\_emp;
25 #using scripts\mp\killstreaks\_killstreak_bundles;
26 #using scripts\mp\killstreaks\_killstreak_detect;
27 #using scripts\mp\killstreaks\_killstreak_hacking;
28 #using scripts\mp\killstreaks\_killstreakrules;
29 #using scripts\mp\killstreaks\_killstreaks;
30 #using scripts\mp\killstreaks\_remote_weapons;
31 #using scripts\mp\killstreaks\_satellite;
32 #using scripts\mp\killstreaks\_supplydrop;
33 #using scripts\mp\killstreaks\_uav;
34 #using scripts\shared\visionset_mgr_shared;
36 #insert scripts\mp\_hacker_tool.gsh;
37 #insert scripts\mp\killstreaks\_killstreaks.gsh;
38 #insert scripts\shared\shared.gsh;
39 #insert scripts\shared\version.gsh;
41 #define AI_TANK_WEAPON_NAME "ai_tank_marker"
42 #define AI_TANK_HEALTH 1500
43 #define AI_TANK_PATH_TIMEOUT 45
44 #define AI_TANK_BULLET_MITIGATION .8
45 #define AI_TANK_EXPLOSIVE_MITIGATION 1
46 #define AI_TANK_STUN_DURATION 4
47 #define AI_TANK_STUN_DURATION_PROXIMITY 1.5
48 #define AI_TANK_MISSILE_TURRET 0
49 #define AI_TANK_GUN_TURRET 1
50 #define AI_TANK_MISSILE_FLASH_TAG "tag_flash"
51 #define AI_TANK_GUNNER_FLASH_TAG "tag_flash_gunner1"
52 #define AI_TANK_GUNNER_AIM_TAG "tag_gunner_aim1"
53 #define AI_TANK_GUNNER_AIM_OFFSET -24
54 #define AI_TANK_THINK_DEBUG GetDvarInt( "scr_ai_tank_think_debug" )
55 #define AI_TANK_TIME_TO_WAIT_FOR_LOST_TARGET 5
56 #define AI_TANK_FURTHEST_FROM_NAVMESH_ALLOWED ( 40 * 12 )
58 #define AI_TANK_NAV_MESH_VALID_LOCATION_BOUNDARY 16
59 #define AI_TANK_NAV_MESH_VALID_LOCATION_TOLERANCE 4
61 #precache( "string", "KILLSTREAK_EARNED_AI_TANK_DROP" );
62 #precache( "string", "KILLSTREAK_AI_TANK_NOT_AVAILABLE" );
63 #precache( "string", "KILLSTREAK_AI_TANK_INBOUND" );
64 #precache( "string", "KILLSTREAK_AI_TANK_HACKED" );
65 #precache( "string", "KILLSTREAK_DESTROYED_AI_TANK" );
66 #precache( "string", "mpl_killstreak_ai_tank" );
67 #precache( "triggerstring", "MP_REMOTE_USE_TANK" );
68 #precache( "fx", "killstreaks/fx_agr_emp_stun" );
69 #precache( "fx", "killstreaks/fx_agr_rocket_flash_1p" );
70 #precache( "fx", "killstreaks/fx_agr_rocket_flash_3p" );
71 #precache( "fx", "killstreaks/fx_agr_damage_state" );
72 #precache( "fx", "killstreaks/fx_agr_explosion" );
73 #precache( "fx", "killstreaks/fx_agr_drop_box" );
75 #using_animtree ( "mp_vehicles" );
83 level.ai_tank_minigun_flash_3p =
"killstreaks/fx_agr_rocket_flash_3p";
94 killstreaks::register_dialog(
AI_TANK_AGR_NAME,
"mpl_killstreak_ai_tank",
"aiTankDialogBundle",
"aiTankPilotDialogBundle",
"friendlyAiTank",
"enemyAiTank",
"enemyAiTankMultiple",
"friendlyAiTankHacked",
"enemyAiTankHacked",
"requestAiTank",
"threatAiTank" );
101 level.ai_tank_fov = Cos( 160 );
102 level.ai_tank_turret_weapon = GetWeapon(
"ai_tank_drone_gun" );
103 level.ai_tank_turret_fire_rate = level.ai_tank_turret_weapon.fireTime;
104 level.ai_tank_remote_weapon = GetWeapon(
"killstreak_ai_tank" );
108 level.ai_tank_damage_fx =
"killstreaks/fx_agr_damage_state";
109 level.ai_tank_explode_fx =
"killstreaks/fx_agr_explosion";
110 level.ai_tank_crate_explode_fx =
"killstreaks/fx_agr_drop_box";
113 anims[ anims.size ] = %o_drone_tank_missile1_fire;
114 anims[ anims.size ] = %o_drone_tank_missile2_fire;
115 anims[ anims.size ] = %o_drone_tank_missile3_fire;
116 anims[ anims.size ] = %o_drone_tank_missile_full_reload;
118 DEFAULT( bundle.ksMainTurretRecoilForceZOffset, 0 );
119 DEFAULT( bundle.ksWeaponReloadTime, 0.5 );
144 if ( killstreak_id == -1 )
149 context = SpawnStruct();
150 if ( !isdefined( context ) )
156 context.radius = level.killstreakCoreBundle.ksAirdropAITankRadius;
159 context.perform_physics_trace =
true;
160 context.check_same_floor =
true;
162 context.objective = &
"airdrop_aitank";
163 context.killstreakRef = hardpointType;
164 context.validLocationSound = level.killstreakCoreBundle.ksValidAITankLocationSound;
166 context.dropTag =
"tag_attach";
167 context.dropTagOffset = ( -35, 0, 10 );
172 self notify(
"supply_drop_marker_done" );
191 context.perform_physics_trace =
false;
192 context.dist_from_boundary = 24;
193 context.max_dist_from_location = 96;
200 if ( isdefined( crate ) )
206 origin = crate.origin;
208 crateBottom = BulletTrace( origin, origin + (0, 0, -50),
false, crate );
209 if ( isdefined( crateBottom ) )
211 origin = crateBottom[
"position"] + (0,0,1);
214 PlayFX( level.ai_tank_crate_explode_fx, origin, (1, 0, 0), (0, 0, 1) );
215 PlaySoundAtPosition(
"veh_talon_crate_exp", crate.origin );
229 if ( !isdefined( location ) )
230 location =
self.origin;
233 if ( !isPlayer(
self ) )
235 start =
self GetCentroid();
236 end = location + ( 0, 0, 16 );
240 if (
trace[
"fraction"] < 1 )
258 if( drone.controlled ===
true )
262 drone.owner unlink();
271 drone notify(
"WatchRemoteControlDeactivate_remoteWeapons");
287 if ( level.gameEnded )
290 drone = SpawnVehicle(
"spawner_bo3_ai_tank_mp", origin, (0, 0, 0),
"talon" );
292 if ( !isdefined( drone ) )
300 drone.customDamageMonitor =
true;
301 drone.avoid_shooting_owner =
true;
308 drone.original_vehicle_type = drone.vehicletype;
311 drone SetVehicleAvoidance(
true );
313 drone.killstreak_id = killstreak_id;
314 drone.type =
"tank_drone";
315 drone.dontDisconnectPaths = 1;
316 drone.isStunned =
false;
317 drone.soundmod =
"drone_land";
318 drone.ignore_vehicle_underneath_splash_scalar =
true;
319 drone.treat_owner_damage_as_friendly_fire =
true;
320 drone.ignore_team_kills =
true;
322 drone.controlled =
false;
323 drone MakeVehicleUnusable();
326 drone.warningShots = 3;
327 drone SetDrawInfrared(
true );
330 if (!isdefined(drone.owner.numTankDrones))
331 drone.owner.numTankDrones=1;
333 drone.owner.numTankDrones++;
334 drone.ownerNumber = drone.owner.numTankDrones;
337 Target_Set( drone, (0,0,20) );
338 Target_SetTurretAquire( drone,
false );
358 switch( vehicle_version )
362 return "spawner_bo3_ai_tank_mp";
366 return "ai_tank_drone_mp";
385 self endon(
"death" );
386 self.owner waittill(
"teamKillKicked" );
387 self notify (
"death" );
392 self endon(
"death" );
395 kill_vo_spacing = 4000;
399 self waittill(
"killed", victim );
401 if ( !isdefined(
self.owner ) || !isdefined( victim ) )
404 if (
self.owner == victim )
407 if ( level.teamBased &&
self.owner.team == victim.team )
410 if ( !
self.controlled && last_kill_vo + kill_vo_spacing < GetTime() )
414 last_kill_vo = GetTime();
430 self.timed_out =
true;
432 self notify(
"death" );
437 self notify(
"tank_watch_owner_events_singleton" );
438 self endon (
"tank_watch_owner_events_singleton" );
439 self endon(
"death" );
443 self MakeVehicleUsable();
444 self.controlled =
false;
446 if ( isdefined(
self.owner ) )
452 self MakeVehicleUnusable();
454 if ( isdefined(
self.owner ) && (
self.controlled ===
true ) )
460 self.abandoned =
true;
462 self notify(
"death" );
467 drone endon(
"death" );
469 level waittill(
"game_ended");
471 drone notify(
"death" );
477 if ( !isdefined(
self ) )
489 hackedDamageTaken = tank.defaultMaxHealth - tank.hackedHealth;
490 assert ( hackedDamageTaken > 0 );
491 if ( hackedDamageTaken > tank.damageTaken )
493 tank.damageTaken = hackedDamageTaken;
500 self endon(
"death" );
502 assert( isdefined(
self.maxhealth ) );
503 self.defaultMaxHealth =
self.maxhealth;
504 maxhealth =
self.maxhealth;
506 self.maxhealth = 999999;
507 self.health =
self.maxhealth;
508 self.isStunned =
false;
514 self.damageTaken = 0;
518 self waittill(
"damage",
damage, attacker, dir, point, mod, model, tag, part, weapon, flags, inflictor, chargeLevel );
520 self.maxhealth = 999999;
521 self.health =
self.maxhealth;
523 if ( weapon.isEmp && (mod ==
"MOD_GRENADE_SPLASH"))
527 if ( !isdefined( emp_damage_to_apply ) )
528 emp_damage_to_apply = ( maxhealth / 2 );
530 self.damageTaken += emp_damage_to_apply;
532 if ( !
self.
isStunned && emp_damage_to_apply > 0 )
534 self.isStunned =
true;
542 if ( weapon.doStun && (mod ==
"MOD_GRENADE_SPLASH" || mod ==
"MOD_GAS") )
544 self.isStunned =
true;
551 if ( !isdefined( weapon_damage ) )
553 if ( mod ==
"MOD_RIFLE_BULLET" || mod ==
"MOD_PISTOL_BULLET" || weapon.name ==
"hatchet" || (mod ==
"MOD_PROJECTILE_SPLASH" && weapon.bulletImpactExplode) )
555 if ( isPlayer( attacker ) )
556 if ( attacker HasPerk(
"specialty_armorpiercing" ) )
559 if ( weapon.weapClass ==
"spread")
565 if ( ( mod ==
"MOD_PROJECTILE" || mod ==
"MOD_GRENADE_SPLASH" || mod ==
"MOD_PROJECTILE_SPLASH" ) &&
damage != 0 && !weapon.isEmp && !weapon.bulletImpactExplode)
570 if ( !isdefined( weapon_damage ) )
576 self.damageTaken += weapon_damage;
578 if (
self.controlled )
580 self.owner SendKillstreakDamageEvent(
int( weapon_damage ) );
584 if (
self.damageTaken >= maxhealth )
586 if( isdefined(
self.owner ) )
587 self.owner.dofutz =
true;
590 self notify(
"death", attacker, mod, weapon );
594 if ( !low_health &&
self.damageTaken > maxhealth / 1.8 )
606 self endon(
"death" );
608 self.damage_fx =
spawn(
"script_model",
self GetTagOrigin(
"tag_origin") + (0,0,-14) );
609 if ( !isdefined(
self.damage_fx ) )
615 self.damage_fx SetModel(
"tag_origin" );
616 self.damage_fx LinkTo(
self,
"tag_turret", (0,0,-14), (0,0,0) );
618 PlayFXOnTag( level.ai_tank_damage_fx,
self.damage_fx,
"tag_origin" );
623 player endon(
"disconnect");
626 killbrushes = GetEntArray(
"trigger_hurt",
"classname" );
630 for (i = 0; i < killbrushes.size; i++)
632 if (
self istouching(killbrushes[i]) )
634 if ( isdefined(
self) )
636 self notify(
"death",
self.owner );
649 self endon(
"death" );
650 self notify(
"stunned" );
652 self ClearVehGoalPos();
653 forward = AnglesToForward(
self.angles );
654 forward =
self.origin + forward * 128;
655 forward = forward - ( 0, 0, 64 );
656 self SetTurretTargetVec( forward );
657 self DisableGunnerFiring( 0,
true );
662 self.owner FreezeControls(
true );
664 self.owner SendKillstreakDamageEvent( 400 );
666 if (isdefined(
self.owner.fullscreen_static))
673 if(
self.controlled )
680 if(
self.controlled )
685 self.owner FreezeControls(
false );
688 self DisableGunnerFiring( 0,
false );
689 self.isStunned =
false;
695 self notify (
"death");
698 randomAngle = RandomInt(360);
701 self SetTurretTargetVec(
self.origin + AnglesToForward((RandomIntRange(305, 315),
int((randomAngle + time * 180)), 0)) * 100);
705 if ( RandomInt(100) > 85)
709 if ( isdefined( rocket ) )
711 rocket.from_ai =
true;
720 PlayFX( level.ai_tank_explode_fx,
self.origin, (0, 0, 1) );
721 PlaySoundAtPosition(
"wpn_agr_explode",
self.origin );
729 killstreak_id =
self.killstreak_id;
731 self waittill(
"death", attacker, damageFromUnderneath, weapon );
734 if ( !isdefined(
self ) )
743 self ClearVehGoalPos();
745 not_abandoned = ( !isdefined(
self.abandoned ) || !
self.abandoned );
747 if (
self.controlled ==
true )
749 self.owner SendKillstreakDamageEvent( 600 );
756 settings =
self.settings;
758 if ( isdefined( settings ) && (
self.timed_out ===
true ||
self.abandoned ===
true ) )
760 fx_origin =
self GetTagOrigin(
VAL( settings.timed_out_death_tag_1,
"tag_origin" ) );
761 PlayFx(
VAL( settings.timed_out_death_fx_1, level.ai_tank_explode_fx ),
VAL( fx_origin,
self.origin ), ( 0, 0, 1 ) );
762 PlaySoundAtPosition(
VAL( settings.timed_out_death_sound_1,
"wpn_agr_explode" ),
self.origin );
766 PlayFX( level.ai_tank_explode_fx,
self.origin, ( 0, 0, 1 ) );
767 PlaySoundAtPosition(
"wpn_agr_explode",
self.origin );
775 if ( !isdefined(
self ) )
782 if (
self.controlled )
791 if (isdefined(
self.damage_fx))
793 self.damage_fx
delete();
796 attacker =
self [[ level.figure_out_attacker ]]( attacker );
798 if ( isdefined( attacker ) && IsPlayer( attacker ) && isdefined(
self.owner ) && attacker !=
self.owner )
804 LUINotifyEvent( &
"player_callout", 2, &
"KILLSTREAK_DESTROYED_AI_TANK", attacker.entnum );
805 attacker AddWeaponStat( weapon,
"destroyed_aitank", 1 );
807 if ( isdefined(
self.wasControlledNowDead ) &&
self.wasControlledNowDead )
809 attacker AddWeaponStat( weapon,
"destroyed_controlled_killstreak", 1 );
815 attacker AddWeaponStat( weapon,
"destroy_aitank_or_setinel", 1 );
829 if ( !isdefined(
self ) )
838 if ( isdefined(
self.aim_entity ) )
839 self.aim_entity
delete();
847 AssertMsg( assert_msg );
852 self endon(
"death" );
854 not_on_nav_mesh_count = 0;
862 if ( not_on_nav_mesh_count >= 4 )
864 self notify(
"death" );
871 if ( level.teambased )
883 if ( !isdefined( on_radar ) )
893 foreach( teamKey, team
in level.alivePlayers )
895 if ( level.teambased && teamKey ==
self.team )
900 foreach( player
in team )
915 enemies[ enemies.size ] = player;
925 position = undefined;
933 foreach( enemy
in enemies )
935 x += enemy.origin[0];
936 y += enemy.origin[1];
937 z += enemy.origin[2];
944 position = ( x, y, z );
952 if ( !isdefined( target ) )
957 if ( !IsAlive( target ) )
962 if ( target == owner )
967 if ( IsPlayer( target ) )
969 if ( target.sessionstate !=
"playing" )
974 if ( isdefined( target.lastspawntime ) && GetTime() - target.lastspawntime < 3000 )
979 if ( target hasPerk(
"specialty_nottargetedbyaitank" ) )
985 if ( level.teambased )
987 if ( isdefined( target.team ) && team == target.team )
993 if ( isdefined( target.owner ) && target.owner == owner )
998 if ( isdefined( target.script_owner ) && target.script_owner == owner )
1008 if ( isdefined( target.targetname ) && target.targetname ==
"riotshield_mp" )
1010 if ( isdefined( target.damageTaken ) && target.damageTaken >= GetDvarInt(
"riotshield_deployed_health" ) )
1021 drone MakeVehicleUsable();
1022 drone ClearVehGoalPos();
1023 drone ClearTurretTarget();
1026 drone.treat_owner_damage_as_friendly_fire =
false;
1027 drone.ignore_team_kills =
false;
1029 if ( isdefined( drone.PlayerDrivenVersion ) )
1030 drone SetVehicleType( drone.PlayerDrivenVersion );
1032 drone usevehicle(
self, 0 );
1036 drone MakeVehicleUnusable();
1037 drone SetBrake(
false );
1051 not_dead = !
IS_TRUE( drone.dead );
1053 if ( isdefined( drone.owner ) )
1058 drone.treat_owner_damage_as_friendly_fire =
true;
1059 drone.ignore_team_kills =
true;
1061 if( drone.classname ==
"script_vehicle")
1062 drone MakeVehicleUnusable();
1064 if ( isdefined( drone.original_vehicle_type ) && not_dead )
1065 drone SetVehicleType( drone.original_vehicle_type );
1067 if ( isdefined( drone.owner ) )
1070 if( exitRequestedByOwner && not_dead )
1075 if ( drone.cobra ===
true && not_dead )
1078 if ( isdefined( drone.owner ) && ( drone.controlled ===
true ) )
1087 Earthquake( 0.4, 0.5,
self.origin, 200 );
1088 self perform_recoil(
"tag_barrel", ( (
IS_TRUE(
self.controlled ) ? bundle.ksMainTurretRecoilForceControlled : bundle.ksMainTurretRecoilForce ) ), bundle.ksMainTurretRecoilForceZOffset );
1090 if (
self.controlled && isdefined( player ) )
1092 player PlayRumbleOnEntity(
"sniper_fire" );
1098 angles =
self GetTagAngles( recoil_tag );
1099 dir = AnglesToForward( angles );
1100 self LaunchVehicle( dir * force_scale_factor,
self.origin + ( 0, 0, force_z_offset ),
false );
1105 if ( !driver_only_update )
1110 if (
self.controlled )
1118 self endon(
"death" );
1119 player endon(
"stopped_using_remote");
1121 if (
self.numberRockets <= 0 )
1128 self DisableDriverFiring(
false );
1133 player waittill(
"missile_fire", missile );
1134 missile.ignore_team_kills =
self.ignore_team_kills;
1136 self.numberRockets--;
1141 if (
self.numberRockets <= 0 )
1150 self endon(
"death" );
1154 self waittill(
"missile_fire", missile );
1155 missile.ignore_team_kills =
self.ignore_team_kills;
1156 missile.killCamEnt =
self;
1163 self DisableDriverFiring(
true );
1166 weapon_wait_duration_ms = Int( bundle.ksWeaponReloadTime * 1000 );
1167 player SetVehicleWeaponWaitDuration( weapon_wait_duration_ms );
1168 player SetVehicleWeaponWaitEndTime( GetTime() + weapon_wait_duration_ms );
1170 wait ( bundle.ksWeaponReloadTime );
1179 self DisableDriverFiring(
false );
1183 #define AI_TANK_IN_WATER_TRACE_MINS ( -2, -2, -2 )
1184 #define AI_TANK_IN_WATER_TRACE_MAXS ( 2, 2, 2 )
1185 #define AI_TANK_IN_WATER_TRACE_MASK ( PHYSICS_TRACE_MASK_WATER )
1186 #define AI_TANK_IN_WATER_TRACE_WAIT ( 0.3 )
1187 #define AI_TANK_IN_WATER_TRACE_START ( 42 )
1188 #define AI_TANK_IN_WATER_TRACE_REF ( 36 )
1189 #define AI_TANK_IN_WATER_TRACE_END ( 12 )
1190 #define AI_TANK_IN_WATER_TRACE_FRACTION ( (AI_TANK_IN_WATER_TRACE_START - AI_TANK_IN_WATER_TRACE_REF) / ( AI_TANK_IN_WATER_TRACE_START - AI_TANK_IN_WATER_TRACE_END ) )
1191 #define AI_TANK_IN_WATER_TRACE_DISTANCE ( AI_TANK_IN_WATER_TRACE_START - AI_TANK_IN_WATER_TRACE_END )
1192 #define AI_TANK_IN_WATER_REF_DISTANCE ( AI_TANK_IN_WATER_TRACE_REF - AI_TANK_IN_WATER_TRACE_END )
1193 #define AI_TANK_IN_WATER_FUTZ_FRACTION ( 12 / AI_TANK_IN_WATER_TRACE_START )
1197 self endon(
"death" );
1210 if ( isdefined(
self.owner ) &&
self.controlled )
1214 if( isdefined(
self.owner ) )
1215 self.owner.dofutz =
true;
1217 self notify(
"death" );