1486 lines
46 KiB
Plaintext
1486 lines
46 KiB
Plaintext
#using scripts\codescripts\struct;
|
|
|
|
#using scripts\shared\_oob;
|
|
#using scripts\shared\array_shared;
|
|
#using scripts\shared\callbacks_shared;
|
|
#using scripts\shared\challenges_shared;
|
|
#using scripts\shared\clientfield_shared;
|
|
#using scripts\shared\hud_shared;
|
|
#using scripts\shared\killstreaks_shared;
|
|
#using scripts\shared\scoreevents_shared;
|
|
#using scripts\shared\system_shared;
|
|
#using scripts\shared\util_shared;
|
|
#using scripts\shared\weapons\_weapons;
|
|
#using scripts\shared\vehicle_ai_shared;
|
|
#using scripts\shared\vehicle_shared;
|
|
#using scripts\shared\vehicles\_amws;
|
|
|
|
#using scripts\mp\_challenges;
|
|
#using scripts\mp\_util;
|
|
#using scripts\mp\gametypes\_dev;
|
|
#using scripts\mp\gametypes\_spawning;
|
|
#using scripts\mp\gametypes\_spawnlogic;
|
|
#using scripts\mp\killstreaks\_dogs;
|
|
#using scripts\mp\killstreaks\_emp;
|
|
#using scripts\mp\killstreaks\_killstreak_bundles;
|
|
#using scripts\mp\killstreaks\_killstreak_detect;
|
|
#using scripts\mp\killstreaks\_killstreak_hacking;
|
|
#using scripts\mp\killstreaks\_killstreakrules;
|
|
#using scripts\mp\killstreaks\_killstreaks;
|
|
#using scripts\mp\killstreaks\_remote_weapons;
|
|
#using scripts\mp\killstreaks\_satellite;
|
|
#using scripts\mp\killstreaks\_supplydrop;
|
|
#using scripts\mp\killstreaks\_uav;
|
|
#using scripts\shared\visionset_mgr_shared;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#precache( "string", "KILLSTREAK_EARNED_AI_TANK_DROP" );
|
|
#precache( "string", "KILLSTREAK_AI_TANK_NOT_AVAILABLE" );
|
|
#precache( "string", "KILLSTREAK_AI_TANK_INBOUND" );
|
|
#precache( "string", "KILLSTREAK_AI_TANK_HACKED" );
|
|
#precache( "string", "KILLSTREAK_DESTROYED_AI_TANK" );
|
|
#precache( "string", "mpl_killstreak_ai_tank" );
|
|
#precache( "triggerstring", "MP_REMOTE_USE_TANK" );
|
|
#precache( "fx", "killstreaks/fx_agr_emp_stun" );
|
|
#precache( "fx", "killstreaks/fx_agr_rocket_flash_1p" );
|
|
#precache( "fx", "killstreaks/fx_agr_rocket_flash_3p" );
|
|
#precache( "fx", "killstreaks/fx_agr_damage_state" );
|
|
#precache( "fx", "killstreaks/fx_agr_explosion" );
|
|
#precache( "fx", "killstreaks/fx_agr_drop_box" );
|
|
|
|
#using_animtree ( "mp_vehicles" );
|
|
|
|
#namespace ai_tank;
|
|
|
|
function init()
|
|
{
|
|
bundle = struct::get_script_bundle( "killstreak", "killstreak_" + "ai_tank_drop" );
|
|
|
|
level.ai_tank_minigun_flash_3p = "killstreaks/fx_agr_rocket_flash_3p";
|
|
|
|
killstreaks::register( "ai_tank_drop", "ai_tank_marker", "killstreak_ai_tank_drop", "ai_tank_drop_used",&useKillstreakAITankDrop );
|
|
|
|
killstreaks::register_alt_weapon( "ai_tank_drop", "amws_gun_turret" );
|
|
killstreaks::register_alt_weapon( "ai_tank_drop", "amws_launcher_turret" );
|
|
killstreaks::register_alt_weapon( "ai_tank_drop", "amws_gun_turret_mp_player" );
|
|
killstreaks::register_alt_weapon( "ai_tank_drop", "amws_launcher_turret_mp_player" );
|
|
|
|
killstreaks::register_remote_override_weapon( "ai_tank_drop", "killstreak_ai_tank" );
|
|
killstreaks::register_strings( "ai_tank_drop", &"KILLSTREAK_EARNED_AI_TANK_DROP", &"KILLSTREAK_AI_TANK_NOT_AVAILABLE", &"KILLSTREAK_AI_TANK_INBOUND", undefined, &"KILLSTREAK_AI_TANK_HACKED" );
|
|
killstreaks::register_dialog( "ai_tank_drop", "mpl_killstreak_ai_tank", "aiTankDialogBundle", "aiTankPilotDialogBundle", "friendlyAiTank", "enemyAiTank", "enemyAiTankMultiple", "friendlyAiTankHacked", "enemyAiTankHacked", "requestAiTank", "threatAiTank" );
|
|
killstreaks::devgui_scorestreak_command( "ai_tank_drop", "Debug Routes", "set devgui_tank routes");
|
|
|
|
// TODO: Move to killstreak data
|
|
level.killstreaks["ai_tank_drop"].threatOnKill = true;
|
|
|
|
remote_weapons::RegisterRemoteWeapon( "killstreak_ai_tank", &"MP_REMOTE_USE_TANK", &startTankRemoteControl, &endTankRemoteControl, false );
|
|
|
|
level.ai_tank_fov = Cos( 160 );
|
|
level.ai_tank_turret_weapon = GetWeapon( "ai_tank_drone_gun" );
|
|
level.ai_tank_turret_fire_rate = level.ai_tank_turret_weapon.fireTime;
|
|
level.ai_tank_remote_weapon = GetWeapon( "killstreak_ai_tank" );
|
|
|
|
spawns = spawnlogic::get_spawnpoint_array( "mp_tdm_spawn" );
|
|
|
|
level.ai_tank_damage_fx = "killstreaks/fx_agr_damage_state";
|
|
level.ai_tank_explode_fx = "killstreaks/fx_agr_explosion";
|
|
level.ai_tank_crate_explode_fx = "killstreaks/fx_agr_drop_box";
|
|
|
|
anims = [];
|
|
anims[ anims.size ] = %o_drone_tank_missile1_fire;
|
|
anims[ anims.size ] = %o_drone_tank_missile2_fire;
|
|
anims[ anims.size ] = %o_drone_tank_missile3_fire;
|
|
anims[ anims.size ] = %o_drone_tank_missile_full_reload;
|
|
|
|
if(!isdefined(bundle.ksMainTurretRecoilForceZOffset))bundle.ksMainTurretRecoilForceZOffset=0;
|
|
if(!isdefined(bundle.ksWeaponReloadTime))bundle.ksWeaponReloadTime=0.5;
|
|
|
|
visionset_mgr::register_info( "visionset", "agr_visionset", 1, 80, 16, true, &visionset_mgr::ramp_in_out_thread_per_player_death_shutdown, false );
|
|
|
|
/#
|
|
level thread tank_devgui_think();
|
|
#/
|
|
thread register();
|
|
}
|
|
|
|
function register()
|
|
{
|
|
clientfield::register( "vehicle", "ai_tank_death", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "ai_tank_missile_fire", 1, 2, "int" );
|
|
clientfield::register( "vehicle", "ai_tank_stun", 1, 1, "int" );
|
|
clientfield::register( "toplayer", "ai_tank_update_hud", 1, 1, "counter" );
|
|
}
|
|
|
|
function useKillstreakAITankDrop(hardpointType)
|
|
{
|
|
team = self.team;
|
|
|
|
if( !self supplydrop::isSupplyDropGrenadeAllowed( hardpointType ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
killstreak_id = self killstreakrules::killstreakStart( hardpointType, team, false, false );
|
|
if ( killstreak_id == -1 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
context = SpawnStruct();
|
|
if ( !isdefined( context ) )
|
|
{
|
|
killstreak_stop_and_assert( hardpointType, team, killstreak_id, "Failed to spawn struct for ai tank." );
|
|
return false;
|
|
}
|
|
|
|
context.radius = level.killstreakCoreBundle.ksAirdropAITankRadius;
|
|
context.dist_from_boundary = 16;
|
|
context.max_dist_from_location = 4;
|
|
context.perform_physics_trace = true;
|
|
context.check_same_floor = true;
|
|
context.isLocationGood = &is_location_good;
|
|
context.objective = &"airdrop_aitank";
|
|
context.killstreakRef = hardpointType;
|
|
context.validLocationSound = level.killstreakCoreBundle.ksValidAITankLocationSound;
|
|
context.tracemask = (1 << 0) | (1 << 2);
|
|
context.dropTag = "tag_attach";
|
|
context.dropTagOffset = ( -35, 0, 10 );
|
|
|
|
result = self supplydrop::useSupplyDropMarker( killstreak_id, context );
|
|
|
|
// the marker is out but the chopper is yet to come
|
|
self notify( "supply_drop_marker_done" );
|
|
|
|
if ( !isdefined(result) || !result )
|
|
{
|
|
//if( !self.supplyGrenadeDeathDrop )
|
|
killstreakrules::killstreakStop( hardpointType, team, killstreak_id );
|
|
return false;
|
|
}
|
|
|
|
self killstreaks::play_killstreak_start_dialog( "ai_tank_drop", self.team, killstreak_id );
|
|
self killstreakrules::displayKillstreakStartTeamMessageToAll( "ai_tank_drop" );
|
|
self AddWeaponStat( GetWeapon( "ai_tank_marker" ), "used", 1 );
|
|
|
|
return result;
|
|
}
|
|
|
|
function crateLand( crate, category, owner, team, context )
|
|
{
|
|
// note: original context is being changed here
|
|
context.perform_physics_trace = false;
|
|
context.dist_from_boundary = 24;
|
|
context.max_dist_from_location = 96;
|
|
|
|
if ( !crate is_location_good( crate.origin, context ) || !isdefined( owner ) || team != owner.team || ( owner EMP::EnemyEMPActive() && !owner hasperk("specialty_immuneemp") ) )
|
|
{
|
|
killstreakrules::killstreakStop( category, team, crate.package_contents_id );
|
|
wait( 10 );
|
|
|
|
if ( isdefined( crate ) )
|
|
crate delete();
|
|
|
|
return;
|
|
}
|
|
|
|
origin = crate.origin;
|
|
|
|
crateBottom = BulletTrace( origin, origin + (0, 0, -50), false, crate );
|
|
if ( isdefined( crateBottom ) )
|
|
{
|
|
origin = crateBottom["position"] + (0,0,1);
|
|
}
|
|
|
|
PlayFX( level.ai_tank_crate_explode_fx, origin, (1, 0, 0), (0, 0, 1) );
|
|
PlaySoundAtPosition( "veh_talon_crate_exp", crate.origin );
|
|
|
|
level thread ai_tank_killstreak_start( owner, origin, crate.package_contents_id, category );
|
|
|
|
crate delete();
|
|
}
|
|
|
|
function is_location_good( location, context )
|
|
{
|
|
return supplydrop::IsLocationGood( location, context ) && valid_location( location );
|
|
}
|
|
|
|
function valid_location( location )
|
|
{
|
|
if ( !isdefined( location ) )
|
|
location = self.origin;
|
|
|
|
// only do this check if we are not the player, intended for deploy box only
|
|
if ( !isPlayer( self ) )
|
|
{
|
|
start = self GetCentroid();
|
|
end = location + ( 0, 0, 16 );
|
|
|
|
trace = PhysicsTrace( start, end, ( 0, 0, 0 ), ( 0, 0, 0 ), self, (1 << 4) );
|
|
|
|
if ( trace["fraction"] < 1 )
|
|
return false;
|
|
}
|
|
|
|
if( self oob::IsTouchingAnyOOBTrigger() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function HackedCallbackPre( hacker )
|
|
{
|
|
drone = self;
|
|
drone clientfield::set( "enemyvehicle", 2 );
|
|
drone.owner stop_remote();
|
|
drone.owner clientfield::set_to_player( "static_postfx", 0 );
|
|
if( drone.controlled === true )
|
|
visionset_mgr::deactivate( "visionset", "agr_visionset", drone.owner );
|
|
drone.owner remote_weapons::RemoveAndAssignNewRemoteControlTrigger( drone.useTrigger );
|
|
drone remote_weapons::EndRemoteControlWeaponUse( true );
|
|
drone.owner unlink();
|
|
drone clientfield::set( "vehicletransition", 0 );
|
|
}
|
|
|
|
function HackedCallbackPost( hacker )
|
|
{
|
|
drone = self;
|
|
|
|
hacker remote_weapons::UseRemoteWeapon( drone, "killstreak_ai_tank", false );
|
|
drone notify("WatchRemoteControlDeactivate_remoteWeapons");
|
|
drone.killstreak_end_time = hacker killstreak_hacking::set_vehicle_drivable_time_starting_now( drone );
|
|
}
|
|
|
|
function ConfigureTeamPost( owner, isHacked )
|
|
{
|
|
drone = self;
|
|
drone thread tank_watch_owner_events();
|
|
}
|
|
|
|
function ai_tank_killstreak_start( owner, origin, killstreak_id, category )
|
|
{
|
|
team = owner.team;
|
|
|
|
waittillframeend;
|
|
|
|
if ( level.gameEnded )
|
|
return;
|
|
|
|
drone = SpawnVehicle( "spawner_bo3_ai_tank_mp", origin, (0, 0, 0), "talon" );
|
|
|
|
if ( !isdefined( drone ) )
|
|
{
|
|
killstreak_stop_and_assert( category, team, killstreak_id, "Failed to spawn ai tank vehicle." );
|
|
return;
|
|
}
|
|
|
|
drone.settings = struct::get_script_bundle( "vehiclecustomsettings", drone.scriptbundlesettings );
|
|
|
|
drone.customDamageMonitor = true; // Disable the default monitor_damage_as_occupant thread
|
|
drone.avoid_shooting_owner = true;
|
|
drone.avoid_shooting_owner_ref_tag = "tag_flash_gunner1";
|
|
|
|
drone killstreaks::configure_team( "ai_tank_drop", killstreak_id, owner, "small_vehicle", undefined, &ConfigureTeamPost );
|
|
drone killstreak_hacking::enable_hacking( "ai_tank_drop", &HackedCallbackPre, &HackedCallbackPost );
|
|
|
|
drone killstreaks::setup_health( "ai_tank_drop", 1500, 0 );
|
|
drone.original_vehicle_type = drone.vehicletype;
|
|
|
|
drone clientfield::set( "enemyvehicle", 1 );
|
|
drone SetVehicleAvoidance( true );
|
|
drone clientfield::set( "ai_tank_missile_fire", ( 3 ) );
|
|
drone.killstreak_id = killstreak_id;
|
|
drone.type = "tank_drone";
|
|
drone.dontDisconnectPaths = 1;
|
|
drone.isStunned = false;
|
|
drone.soundmod = "drone_land";
|
|
drone.ignore_vehicle_underneath_splash_scalar = true;
|
|
drone.treat_owner_damage_as_friendly_fire = true;
|
|
drone.ignore_team_kills = true;
|
|
|
|
drone.controlled = false;
|
|
drone MakeVehicleUnusable();
|
|
|
|
drone.numberRockets = ( 3 );
|
|
drone.warningShots = 3;
|
|
drone SetDrawInfrared( true );
|
|
|
|
//set up number for this drone
|
|
if (!isdefined(drone.owner.numTankDrones))
|
|
drone.owner.numTankDrones=1;
|
|
else
|
|
drone.owner.numTankDrones++;
|
|
drone.ownerNumber = drone.owner.numTankDrones;
|
|
|
|
// make the drone targetable
|
|
Target_Set( drone, (0,0,20) );
|
|
Target_SetTurretAquire( drone, false );
|
|
|
|
// setup target group for missile lock on monitoring
|
|
drone vehicle::init_target_group();
|
|
drone vehicle::add_to_target_group( drone );
|
|
|
|
drone setup_gameplay_think( category );
|
|
|
|
drone.killstreak_end_time = GetTime() + ( 120 * 1000 );
|
|
|
|
owner remote_weapons::UseRemoteWeapon( drone, "killstreak_ai_tank", false );
|
|
|
|
drone thread kill_monitor();
|
|
drone thread deleteOnKillbrush( drone.owner );
|
|
drone thread tank_rocket_watch_ai();
|
|
level thread tank_game_end_think(drone);
|
|
|
|
/#
|
|
drone thread tank_think_debug();
|
|
#/
|
|
|
|
/#
|
|
//drone thread tank_debug_health();
|
|
#/
|
|
}
|
|
|
|
function get_vehicle_name( vehicle_version )
|
|
{
|
|
switch( vehicle_version )
|
|
{
|
|
case 2:
|
|
default:
|
|
return "spawner_bo3_ai_tank_mp";
|
|
break;
|
|
|
|
case 1:
|
|
return "ai_tank_drone_mp";
|
|
break;
|
|
}
|
|
}
|
|
|
|
function setup_gameplay_think( category )
|
|
{
|
|
drone = self;
|
|
|
|
drone thread tank_abort_think();
|
|
drone thread tank_team_kill();
|
|
drone thread tank_too_far_from_nav_mesh_abort_think();
|
|
drone thread tank_death_think( category );
|
|
drone thread tank_damage_think();
|
|
drone thread WatchWater();
|
|
}
|
|
|
|
|
|
function tank_think_debug() // self == drone
|
|
{
|
|
self endon ( "death" );
|
|
|
|
server_frames_to_persist = 1;
|
|
text_scale = 0.5;
|
|
text_alpha = 1.0;
|
|
text_color = ( 1, 1, 1 );
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( GetDvarInt( "scr_ai_tank_think_debug" ) == 0 )
|
|
{
|
|
wait 5;
|
|
continue;
|
|
}
|
|
|
|
target_name = "unknown";
|
|
target_entity = undefined;
|
|
|
|
tank_is_idle = !isdefined( self.enemy ); // NOTE: enemy is set by code-side ai system
|
|
target_entity = self.enemy;
|
|
|
|
if ( isdefined( target_entity ) && !tank_is_idle )
|
|
{
|
|
if ( isdefined ( target_entity.name ) )
|
|
{
|
|
target_name = target_entity.name;
|
|
}
|
|
else if ( isdefined ( target_entity.remotename ) )
|
|
{
|
|
target_name = target_entity.remotename;
|
|
}
|
|
}
|
|
|
|
target_text = ( ( tank_is_idle ) ? "Target: none" : "Target: " + target_name );
|
|
/# Print3d( self.origin, target_text, text_color, text_alpha, text_scale, server_frames_to_persist ); #/
|
|
|
|
duration_text = "Duration: " + ( (self.killstreak_end_time - GetTime()) * 0.001 );
|
|
/# Print3d( self.origin + ( 0, 0, 12 ), duration_text, text_color, text_alpha, text_scale, server_frames_to_persist ); #/
|
|
|
|
can_see_text = "Can see: ";
|
|
if ( tank_is_idle )
|
|
{
|
|
can_see_text += "---";
|
|
}
|
|
else
|
|
{
|
|
can_see_text += ( ( self VehCanSee( target_entity ) ) ? "yes" : "no" );
|
|
}
|
|
/# Print3d( self.origin + ( 0, 0, -12 ), can_see_text, text_color, text_alpha, text_scale, server_frames_to_persist ); #/
|
|
|
|
movement_type_text = "Movement: ";
|
|
if ( isdefined( self.debug_ai_movement_type ) )
|
|
{
|
|
movement_type_text += self.debug_ai_movement_type;
|
|
}
|
|
else
|
|
{
|
|
movement_type_text += "---";
|
|
}
|
|
/# Print3d( self.origin + ( 0, 0, -24 ), movement_type_text, text_color, text_alpha, text_scale, server_frames_to_persist ); #/
|
|
|
|
if ( isdefined( self.debug_ai_move_to_point ) )
|
|
{
|
|
/# util::debug_sphere( self.debug_ai_move_to_point + ( 0, 0, 16 ), 10, ( 0.1, 0.95, 0.1 ), 0.9, server_frames_to_persist ); #/
|
|
|
|
if ( isdefined( self.debug_ai_move_to_points_considered ) )
|
|
{
|
|
foreach( point in self.debug_ai_move_to_points_considered )
|
|
{
|
|
point_color = ( 0.65, 0.65, 0.65 ); // grey-ish
|
|
|
|
if ( isdefined( point.score ) )
|
|
{
|
|
if ( point.score != 0 )
|
|
{
|
|
if ( point.score < 0 )
|
|
{
|
|
point_color = ( 0.65, 0.1, 0.1 ); //dark red-ish
|
|
}
|
|
else if ( point.score > 50 )
|
|
{
|
|
point_color = ( 0.1, 0.65, 0.1 ); // dark green-ish
|
|
}
|
|
else
|
|
{
|
|
point_color = ( 0.95, 0.95, 0.1 ); // yellow-ish
|
|
}
|
|
|
|
score_text_scale = text_scale;
|
|
score_text_color = text_color;
|
|
if ( point.origin != self.debug_ai_move_to_point )
|
|
{
|
|
score_text_scale *= 0.67;
|
|
}
|
|
else
|
|
{
|
|
score_text_scale *= 1.5;
|
|
score_text_color = ( 0.05, 0.98, 0.05 ); // green-ish
|
|
}
|
|
|
|
/# Print3d( point.origin + ( 0, 0, 16 ), point.score, score_text_color, text_alpha, score_text_scale, server_frames_to_persist ); #/
|
|
}
|
|
}
|
|
|
|
if ( point.origin != self.debug_ai_move_to_point )
|
|
{
|
|
/# util::debug_sphere( point.origin + ( 0, 0, 16 ), 3, point_color, 0.5, server_frames_to_persist ); #/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
function tank_team_kill()
|
|
{
|
|
self endon( "death" );
|
|
self.owner waittill( "teamKillKicked" );
|
|
self notify ( "death" );
|
|
}
|
|
|
|
function kill_monitor()
|
|
{
|
|
self endon( "death" );
|
|
|
|
last_kill_vo = 0;
|
|
kill_vo_spacing = 4000;
|
|
|
|
while(1)
|
|
{
|
|
self waittill( "killed", victim );
|
|
|
|
if ( !isdefined( self.owner ) || !isdefined( victim ) )
|
|
continue;
|
|
|
|
if ( self.owner == victim )
|
|
continue;
|
|
|
|
if ( level.teamBased && self.owner.team == victim.team )
|
|
continue;
|
|
|
|
if ( !self.controlled && last_kill_vo + kill_vo_spacing < GetTime() )
|
|
{
|
|
self killstreaks::play_pilot_dialog_on_owner( "kill", "ai_tank_drop", self.killstreak_id );
|
|
|
|
last_kill_vo = GetTime();
|
|
}
|
|
}
|
|
}
|
|
|
|
function tank_abort_think()
|
|
{
|
|
tank = self;
|
|
|
|
tank thread killstreaks::WaitForTimeout( "ai_tank_drop", ( 120 * 1000 ), &tank_timeout_callback, "death", "emp_jammed" );
|
|
}
|
|
|
|
function tank_timeout_callback()
|
|
{
|
|
self killstreaks::play_pilot_dialog_on_owner( "timeout", "ai_tank_drop" );
|
|
|
|
self.timed_out = true;
|
|
|
|
self notify( "death" );
|
|
}
|
|
|
|
function tank_watch_owner_events()
|
|
{
|
|
self notify( "tank_watch_owner_events_singleton" );
|
|
self endon ( "tank_watch_owner_events_singleton" );
|
|
self endon( "death" );
|
|
|
|
self.owner util::waittill_any( "joined_team", "disconnect", "joined_spectators" );
|
|
|
|
self MakeVehicleUsable();
|
|
self.controlled = false;
|
|
|
|
if ( isdefined( self.owner ) )
|
|
{
|
|
self.owner unlink();
|
|
self clientfield::set( "vehicletransition", 0 );
|
|
}
|
|
|
|
self MakeVehicleUnusable();
|
|
|
|
if ( isdefined( self.owner ) && ( self.controlled === true ) )
|
|
{
|
|
visionset_mgr::deactivate( "visionset", "agr_visionset", self.owner );
|
|
self.owner stop_remote();
|
|
}
|
|
|
|
self.abandoned = true;
|
|
|
|
self notify( "death" );
|
|
}
|
|
|
|
function tank_game_end_think(drone)
|
|
{
|
|
drone endon( "death" );
|
|
|
|
level waittill("game_ended");
|
|
|
|
drone notify( "death" );
|
|
}
|
|
|
|
|
|
function stop_remote() // dead
|
|
{
|
|
if ( !isdefined( self ) )
|
|
return;
|
|
|
|
self killstreaks::clear_using_remote();
|
|
self remote_weapons::destroyRemoteHUD();
|
|
self util::clientNotify( "nofutz" );
|
|
}
|
|
|
|
|
|
function tank_hacked_health_update( hacker )
|
|
{
|
|
tank = self;
|
|
hackedDamageTaken = tank.defaultMaxHealth - tank.hackedHealth;
|
|
assert ( hackedDamageTaken > 0 );
|
|
if ( hackedDamageTaken > tank.damageTaken )
|
|
{
|
|
tank.damageTaken = hackedDamageTaken;
|
|
}
|
|
}
|
|
|
|
|
|
function tank_damage_think()
|
|
{
|
|
self endon( "death" );
|
|
|
|
assert( isdefined( self.maxhealth ) );
|
|
self.defaultMaxHealth = self.maxhealth;
|
|
maxhealth = self.maxhealth; // actual max heath should be set now.
|
|
|
|
self.maxhealth = 999999;
|
|
self.health = self.maxhealth;
|
|
self.isStunned = false;
|
|
|
|
self.hackedHealthUpdateCallback = &tank_hacked_health_update;
|
|
self.hackedHealth = killstreak_bundles::get_hacked_health( "ai_tank_drop" );
|
|
|
|
low_health = false;
|
|
self.damageTaken = 0;
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill( "damage", damage, attacker, dir, point, mod, model, tag, part, weapon, flags, inflictor, chargeLevel );
|
|
|
|
self.maxhealth = 999999;
|
|
self.health = self.maxhealth;
|
|
|
|
/#
|
|
self.damage_debug = ( damage + " (" + weapon.name + ")" );
|
|
#/
|
|
|
|
if ( weapon.isEmp && (mod == "MOD_GRENADE_SPLASH"))
|
|
{
|
|
emp_damage_to_apply = killstreak_bundles::get_emp_grenade_damage( "ai_tank_drop", maxhealth );
|
|
|
|
if ( !isdefined( emp_damage_to_apply ) )
|
|
emp_damage_to_apply = ( maxhealth / 2 );
|
|
|
|
self.damageTaken += emp_damage_to_apply;
|
|
damage = 0;
|
|
if ( !self.isStunned && emp_damage_to_apply > 0 )
|
|
{
|
|
self.isStunned = true;
|
|
challenges::stunnedTankWithEMPGrenade( attacker );
|
|
self thread tank_stun( 4 );
|
|
}
|
|
}
|
|
|
|
if ( !self.isStunned )
|
|
{
|
|
if ( weapon.doStun && (mod == "MOD_GRENADE_SPLASH" || mod == "MOD_GAS") )
|
|
{
|
|
self.isStunned = true;
|
|
self thread tank_stun( 1.5 );
|
|
}
|
|
}
|
|
|
|
weapon_damage = killstreak_bundles::get_weapon_damage( "ai_tank_drop", maxhealth, attacker, weapon, mod, damage, flags, chargeLevel );
|
|
|
|
if ( !isdefined( weapon_damage ) )
|
|
{
|
|
if ( mod == "MOD_RIFLE_BULLET" || mod == "MOD_PISTOL_BULLET" || weapon.name == "hatchet" || (mod == "MOD_PROJECTILE_SPLASH" && weapon.bulletImpactExplode) )
|
|
{
|
|
if ( isPlayer( attacker ) )
|
|
if ( attacker HasPerk( "specialty_armorpiercing" ) )
|
|
damage += int( damage * level.cac_armorpiercing_data );
|
|
|
|
if ( weapon.weapClass == "spread")
|
|
damage = damage * 1.5;
|
|
|
|
weapon_damage = damage * .8;
|
|
}
|
|
|
|
if ( ( mod == "MOD_PROJECTILE" || mod == "MOD_GRENADE_SPLASH" || mod == "MOD_PROJECTILE_SPLASH" ) && damage != 0 && !weapon.isEmp && !weapon.bulletImpactExplode)
|
|
{
|
|
weapon_damage = damage * 1;
|
|
}
|
|
|
|
if ( !isdefined( weapon_damage ) )
|
|
{
|
|
weapon_damage = damage;
|
|
}
|
|
}
|
|
|
|
self.damageTaken += weapon_damage;
|
|
|
|
if ( self.controlled )
|
|
{
|
|
self.owner SendKillstreakDamageEvent( int( weapon_damage ) );
|
|
self.owner vehicle::update_damage_as_occupant( self.damageTaken, maxhealth );
|
|
}
|
|
|
|
if ( self.damageTaken >= maxhealth )
|
|
{
|
|
if( isdefined( self.owner ) )
|
|
self.owner.dofutz = true;
|
|
|
|
self.health = 0;
|
|
self notify( "death", attacker, mod, weapon );
|
|
return;
|
|
}
|
|
|
|
if ( !low_health && self.damageTaken > maxhealth / 1.8 )
|
|
{
|
|
self killstreaks::play_pilot_dialog_on_owner( "damaged", "ai_tank_drop", self.killstreak_id );
|
|
|
|
self thread tank_low_health_fx();
|
|
low_health = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
function tank_low_health_fx()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self.damage_fx = spawn( "script_model", self GetTagOrigin("tag_origin") + (0,0,-14) );
|
|
if ( !isdefined( self.damage_fx ) )
|
|
{
|
|
// intentionally not adding an AssertMsg() here
|
|
return;
|
|
}
|
|
|
|
self.damage_fx SetModel( "tag_origin" );
|
|
self.damage_fx LinkTo(self, "tag_turret", (0,0,-14), (0,0,0) );
|
|
wait ( 0.1 );
|
|
PlayFXOnTag( level.ai_tank_damage_fx, self.damage_fx, "tag_origin" );
|
|
}
|
|
|
|
function deleteOnKillbrush(player)
|
|
{
|
|
player endon("disconnect");
|
|
self endon("death");
|
|
|
|
killbrushes = GetEntArray( "trigger_hurt","classname" );
|
|
|
|
while(1)
|
|
{
|
|
for (i = 0; i < killbrushes.size; i++)
|
|
{
|
|
if (self istouching(killbrushes[i]) )
|
|
{
|
|
if ( isdefined(self) )
|
|
{
|
|
self notify( "death", self.owner );
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
wait( 0.1 );
|
|
}
|
|
|
|
}
|
|
|
|
function tank_stun( duration )
|
|
{
|
|
self endon( "death" );
|
|
self notify( "stunned" );
|
|
|
|
self ClearVehGoalPos();
|
|
forward = AnglesToForward( self.angles );
|
|
forward = self.origin + forward * 128;
|
|
forward = forward - ( 0, 0, 64 );
|
|
self SetTurretTargetVec( forward );
|
|
self DisableGunnerFiring( 0, true );
|
|
self LaserOff();
|
|
|
|
if (self.controlled)
|
|
{
|
|
self.owner FreezeControls( true );
|
|
|
|
self.owner SendKillstreakDamageEvent( 400 );
|
|
}
|
|
if (isdefined(self.owner.fullscreen_static))
|
|
{
|
|
self.owner thread remote_weapons::stunStaticFX( duration );
|
|
}
|
|
|
|
self clientfield::set( "ai_tank_stun", 1 );
|
|
|
|
if( self.controlled )
|
|
self.owner clientfield::set_to_player( "static_postfx", 1 );
|
|
|
|
wait ( duration );
|
|
|
|
self clientfield::set( "ai_tank_stun", 0 );
|
|
|
|
if( self.controlled )
|
|
self.owner clientfield::set_to_player( "static_postfx", 0 );
|
|
|
|
if (self.controlled)
|
|
{
|
|
self.owner FreezeControls( false );
|
|
}
|
|
|
|
self DisableGunnerFiring( 0, false );
|
|
self.isStunned = false;
|
|
}
|
|
|
|
function emp_crazy_death()
|
|
{
|
|
self clientfield::set( "ai_tank_stun", 1 );
|
|
self notify ("death");
|
|
|
|
time = 0;
|
|
randomAngle = RandomInt(360);
|
|
while (time < 1.45)
|
|
{
|
|
self SetTurretTargetVec(self.origin + AnglesToForward((RandomIntRange(305, 315), int((randomAngle + time * 180)), 0)) * 100);
|
|
if (time > 0.2)
|
|
{
|
|
self FireWeapon( 1 );
|
|
if ( RandomInt(100) > 85)
|
|
{
|
|
rocket = self FireWeapon( 0 );
|
|
|
|
if ( isdefined( rocket ) )
|
|
{
|
|
rocket.from_ai = true;
|
|
}
|
|
}
|
|
}
|
|
time += 0.05;
|
|
{wait(.05);};
|
|
}
|
|
self clientfield::set( "ai_tank_death", 1 );
|
|
|
|
PlayFX( level.ai_tank_explode_fx, self.origin, (0, 0, 1) );
|
|
PlaySoundAtPosition( "wpn_agr_explode", self.origin );
|
|
{wait(.05);};
|
|
self hide();
|
|
}
|
|
|
|
function tank_death_think( hardpointName )
|
|
{
|
|
team = self.team;
|
|
killstreak_id = self.killstreak_id;
|
|
|
|
self waittill( "death", attacker, damageFromUnderneath, weapon );
|
|
// self waittill( "death", attacker, damageFromUnderneath, weapon, point, dir, modType );
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
killstreak_stop_and_assert( hardpointName, team, killstreak_id, "Failed to handle death. A." );
|
|
return;
|
|
}
|
|
|
|
self.dead = true;
|
|
self LaserOff();
|
|
|
|
self ClearVehGoalPos();
|
|
|
|
not_abandoned = ( !isdefined( self.abandoned ) || !self.abandoned );
|
|
|
|
if ( self.controlled == true )
|
|
{
|
|
self.owner SendKillstreakDamageEvent( 600 );
|
|
self.owner remote_weapons::destroyRemoteHUD();
|
|
}
|
|
|
|
self clientfield::set( "ai_tank_death", 1 );
|
|
stunned = false;
|
|
|
|
settings = self.settings;
|
|
|
|
if ( isdefined( settings ) && ( self.timed_out === true || self.abandoned === true ) )
|
|
{
|
|
fx_origin = self GetTagOrigin( (isdefined(settings.timed_out_death_tag_1)?settings.timed_out_death_tag_1:"tag_origin") );
|
|
PlayFx( (isdefined(settings.timed_out_death_fx_1)?settings.timed_out_death_fx_1:level.ai_tank_explode_fx), (isdefined(fx_origin)?fx_origin:self.origin), ( 0, 0, 1 ) );
|
|
PlaySoundAtPosition( (isdefined(settings.timed_out_death_sound_1)?settings.timed_out_death_sound_1:"wpn_agr_explode"), self.origin );
|
|
}
|
|
else
|
|
{
|
|
PlayFX( level.ai_tank_explode_fx, self.origin, ( 0, 0, 1 ) );
|
|
PlaySoundAtPosition( "wpn_agr_explode", self.origin );
|
|
}
|
|
|
|
|
|
if ( not_abandoned )
|
|
{
|
|
util::wait_network_frame();
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
killstreak_stop_and_assert( hardpointName, team, killstreak_id, "Failed to handle death. B." );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( self.controlled )
|
|
{
|
|
self Ghost(); // keep the view for player with the dead by using ghost, otherwise, will end up at feet of player
|
|
}
|
|
else
|
|
{
|
|
self Hide();
|
|
}
|
|
|
|
if (isdefined(self.damage_fx))
|
|
{
|
|
self.damage_fx delete();
|
|
}
|
|
|
|
attacker = self [[ level.figure_out_attacker ]]( attacker );
|
|
|
|
if ( isdefined( attacker ) && IsPlayer( attacker ) && isdefined( self.owner ) && attacker != self.owner )
|
|
{
|
|
if ( self.owner util::IsEnemyPlayer( attacker ) )
|
|
{
|
|
|
|
scoreevents::processScoreEvent( "destroyed_aitank", attacker, self.owner, weapon );
|
|
LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_DESTROYED_AI_TANK", attacker.entnum );
|
|
attacker AddWeaponStat( weapon, "destroyed_aitank", 1 );
|
|
controlled = false;
|
|
if ( isdefined( self.wasControlledNowDead ) && self.wasControlledNowDead )
|
|
{
|
|
attacker AddWeaponStat( weapon, "destroyed_controlled_killstreak", 1 );
|
|
controlled = true;
|
|
}
|
|
|
|
attacker challenges::destroyScoreStreak( weapon, controlled, true );
|
|
attacker challenges::destroyNonAirScoreStreak_PostStatsLock( weapon );
|
|
attacker AddWeaponStat( weapon, "destroy_aitank_or_setinel", 1 );
|
|
|
|
self killstreaks::play_destroyed_dialog_on_owner( "ai_tank_drop", self.killstreak_id );
|
|
}
|
|
else
|
|
{
|
|
//Destroyed Friendly Killstreak
|
|
}
|
|
}
|
|
|
|
if ( not_abandoned )
|
|
{
|
|
self util::waittill_any_timeout( 2.0, "remote_weapon_end" );
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
killstreak_stop_and_assert( hardpointName, team, killstreak_id, "Failed to handle death. C." );
|
|
return;
|
|
}
|
|
}
|
|
|
|
killstreakrules::killstreakStop( hardpointName, team, self.killstreak_id );
|
|
|
|
if ( isdefined( self.aim_entity ) )
|
|
self.aim_entity delete();
|
|
|
|
self delete();
|
|
}
|
|
|
|
function killstreak_stop_and_assert( hardpoint_name, team, killstreak_id, assert_msg )
|
|
{
|
|
killstreakrules::killstreakStop( hardpoint_name, team, killstreak_id );
|
|
AssertMsg( assert_msg );
|
|
}
|
|
|
|
function tank_too_far_from_nav_mesh_abort_think()
|
|
{
|
|
self endon( "death" );
|
|
|
|
not_on_nav_mesh_count = 0;
|
|
|
|
for ( ;; )
|
|
{
|
|
wait( 1 );
|
|
|
|
not_on_nav_mesh_count = ( isdefined( GetClosestPointOnNavMesh( self.origin, ( 40 * 12 ) ) ) ? 0 : not_on_nav_mesh_count + 1 );
|
|
|
|
if ( not_on_nav_mesh_count >= 4 )
|
|
{
|
|
self notify( "death" );
|
|
}
|
|
}
|
|
}
|
|
|
|
function tank_has_radar()
|
|
{
|
|
if ( level.teambased )
|
|
{
|
|
return ( uav::HasUAV( self.team ) || satellite::HasSatellite( self.team ) );
|
|
}
|
|
|
|
return ( uav::HasUAV( self.entnum ) || satellite::HasSatellite( self.entnum ) );
|
|
}
|
|
|
|
function tank_get_player_enemies( on_radar )
|
|
{
|
|
enemies = [];
|
|
|
|
if ( !isdefined( on_radar ) )
|
|
{
|
|
on_radar = false;
|
|
}
|
|
|
|
if ( on_radar )
|
|
{
|
|
time = GetTime();
|
|
}
|
|
|
|
foreach( teamKey, team in level.alivePlayers )
|
|
{
|
|
if ( level.teambased && teamKey == self.team )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach( player in team )
|
|
{
|
|
if ( !valid_target( player, self.team, self.owner ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( on_radar )
|
|
{
|
|
if ( time - player.lastFireTime > 3000 && !tank_has_radar() )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
enemies[ enemies.size ] = player;
|
|
}
|
|
}
|
|
|
|
return enemies;
|
|
}
|
|
|
|
function tank_compute_enemy_position()
|
|
{
|
|
enemies = tank_get_player_enemies( false );
|
|
position = undefined;
|
|
|
|
if ( enemies.size )
|
|
{
|
|
x = 0;
|
|
y = 0;
|
|
z = 0;
|
|
|
|
foreach( enemy in enemies )
|
|
{
|
|
x += enemy.origin[0];
|
|
y += enemy.origin[1];
|
|
z += enemy.origin[2];
|
|
}
|
|
|
|
x /= enemies.size;
|
|
y /= enemies.size;
|
|
z /= enemies.size;
|
|
|
|
position = ( x, y, z );
|
|
}
|
|
|
|
return position;
|
|
}
|
|
|
|
function valid_target( target, team, owner )
|
|
{
|
|
if ( !isdefined( target ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !IsAlive( target ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( target == owner )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( IsPlayer( target ) )
|
|
{
|
|
if ( target.sessionstate != "playing" )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( isdefined( target.lastspawntime ) && GetTime() - target.lastspawntime < 3000 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( target hasPerk( "specialty_nottargetedbyaitank" ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/#
|
|
if ( target IsInMoveMode( "ufo", "noclip" ) )
|
|
{
|
|
return false;
|
|
}
|
|
#/
|
|
}
|
|
|
|
if ( level.teambased )
|
|
{
|
|
if ( isdefined( target.team ) && team == target.team )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( isdefined( target.owner ) && target.owner == owner )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( isdefined( target.script_owner ) && target.script_owner == owner )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( ( isdefined( target.dead ) && target.dead ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( isdefined( target.targetname ) && target.targetname == "riotshield_mp" )
|
|
{
|
|
if ( isdefined( target.damageTaken ) && target.damageTaken >= GetDvarInt( "riotshield_deployed_health" ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function startTankRemoteControl( drone ) // self == player
|
|
{
|
|
drone MakeVehicleUsable();
|
|
drone ClearVehGoalPos();
|
|
drone ClearTurretTarget();
|
|
drone LaserOff();
|
|
|
|
drone.treat_owner_damage_as_friendly_fire = false;
|
|
drone.ignore_team_kills = false;
|
|
|
|
if ( isdefined( drone.PlayerDrivenVersion ) )
|
|
drone SetVehicleType( drone.PlayerDrivenVersion );
|
|
|
|
drone usevehicle( self, 0 );
|
|
drone clientfield::set( "vehicletransition", 1 );
|
|
|
|
|
|
drone MakeVehicleUnusable();
|
|
drone SetBrake( false );
|
|
|
|
drone thread tank_rocket_watch( self );
|
|
drone thread vehicle::monitor_missiles_locked_on_to_me( self );
|
|
|
|
self vehicle::set_vehicle_drivable_time( ( 120 * 1000 ), drone.killstreak_end_time );
|
|
self vehicle::update_damage_as_occupant( (isdefined(drone.damageTaken)?drone.damageTaken:0), (isdefined(drone.defaultmaxhealth)?drone.defaultmaxhealth:100) );
|
|
drone update_client_ammo( drone.numberRockets, true );
|
|
|
|
visionset_mgr::activate( "visionset", "agr_visionset", self, 1, 90000, 1 );
|
|
}
|
|
|
|
function endTankRemoteControl( drone, exitRequestedByOwner )
|
|
{
|
|
not_dead = !( isdefined( drone.dead ) && drone.dead );
|
|
|
|
if ( isdefined( drone.owner ) )
|
|
{
|
|
drone.owner remote_weapons::destroyRemoteHUD();
|
|
}
|
|
|
|
drone.treat_owner_damage_as_friendly_fire = true;
|
|
drone.ignore_team_kills = true;
|
|
|
|
if( drone.classname == "script_vehicle")
|
|
drone MakeVehicleUnusable();
|
|
|
|
if ( isdefined( drone.original_vehicle_type ) && not_dead )
|
|
drone SetVehicleType( drone.original_vehicle_type );
|
|
|
|
if ( isdefined( drone.owner ) )
|
|
drone.owner vehicle::stop_monitor_missiles_locked_on_to_me();
|
|
|
|
if( exitRequestedByOwner && not_dead )
|
|
{
|
|
drone vehicle_ai::set_state( "combat" );
|
|
}
|
|
|
|
if ( drone.cobra === true && not_dead )
|
|
drone thread amws::cobra_retract();
|
|
|
|
if ( isdefined( drone.owner ) && ( drone.controlled === true ) )
|
|
visionset_mgr::deactivate( "visionset", "agr_visionset", drone.owner );
|
|
|
|
drone clientfield::set( "vehicletransition", 0 );
|
|
}
|
|
|
|
function perform_recoil_missile_turret( player ) // self == drone
|
|
{
|
|
bundle = level.killstreakBundle["ai_tank_drop"];
|
|
Earthquake( 0.4, 0.5, self.origin, 200 );
|
|
self perform_recoil( "tag_barrel", ( ( ( isdefined( self.controlled ) && self.controlled ) ? bundle.ksMainTurretRecoilForceControlled : bundle.ksMainTurretRecoilForce ) ), bundle.ksMainTurretRecoilForceZOffset );
|
|
|
|
if ( self.controlled && isdefined( player ) )
|
|
{
|
|
player PlayRumbleOnEntity( "sniper_fire" );
|
|
}
|
|
}
|
|
|
|
function perform_recoil( recoil_tag, force_scale_factor, force_z_offset ) // self == drone
|
|
{
|
|
angles = self GetTagAngles( recoil_tag );
|
|
dir = AnglesToForward( angles );
|
|
self LaunchVehicle( dir * force_scale_factor, self.origin + ( 0, 0, force_z_offset ), false );
|
|
}
|
|
|
|
function update_client_ammo( ammo_count, driver_only_update = false ) // self == vehicle
|
|
{
|
|
if ( !driver_only_update )
|
|
{
|
|
self clientfield::set( "ai_tank_missile_fire", ammo_count );
|
|
}
|
|
|
|
if ( self.controlled )
|
|
{
|
|
self.owner clientfield::increment_to_player( "ai_tank_update_hud", 1 );
|
|
}
|
|
}
|
|
|
|
function tank_rocket_watch( player )
|
|
{
|
|
self endon( "death" );
|
|
player endon( "stopped_using_remote");
|
|
|
|
if ( self.numberRockets <= 0 )
|
|
{
|
|
self reload_rockets( player );
|
|
}
|
|
|
|
if ( !self.isStunned )
|
|
{
|
|
self DisableDriverFiring( false );
|
|
}
|
|
|
|
while( true )
|
|
{
|
|
player waittill( "missile_fire", missile );
|
|
missile.ignore_team_kills = self.ignore_team_kills;
|
|
|
|
self.numberRockets--;
|
|
self update_client_ammo( self.numberRockets );
|
|
|
|
self perform_recoil_missile_turret( player );
|
|
|
|
if ( self.numberRockets <= 0 )
|
|
{
|
|
self reload_rockets( player );
|
|
}
|
|
}
|
|
}
|
|
|
|
function tank_rocket_watch_ai()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "missile_fire", missile );
|
|
missile.ignore_team_kills = self.ignore_team_kills;
|
|
missile.killCamEnt = self;
|
|
}
|
|
}
|
|
|
|
function reload_rockets( player )
|
|
{
|
|
bundle = level.killstreakBundle["ai_tank_drop"];
|
|
self DisableDriverFiring( true );
|
|
|
|
// setup the "reload" time for the player's vehicle HUD
|
|
weapon_wait_duration_ms = Int( bundle.ksWeaponReloadTime * 1000 );
|
|
player SetVehicleWeaponWaitDuration( weapon_wait_duration_ms );
|
|
player SetVehicleWeaponWaitEndTime( GetTime() + weapon_wait_duration_ms );
|
|
|
|
wait ( bundle.ksWeaponReloadTime );
|
|
|
|
self.numberRockets = ( 3 );
|
|
self update_client_ammo( self.numberRockets );
|
|
|
|
wait (0.4);
|
|
|
|
if ( !self.isStunned )
|
|
{
|
|
self DisableDriverFiring( false );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function WatchWater()
|
|
{
|
|
self endon( "death" );
|
|
|
|
inWater = false;
|
|
while( !inWater )
|
|
{
|
|
wait ( 0.3 );
|
|
trace = physicstrace( self.origin + ( 0, 0, ( 42 ) ), self.origin + ( 0, 0, ( 12 ) ), ( -2, -2, -2 ), ( 2, 2, 2 ), self, ( (1 << 2) ));
|
|
inWater = ( trace["fraction"] < ( (( 42 ) - ( 36 )) / ( ( 42 ) - ( 12 ) ) ) && trace["fraction"] != 1.0 );
|
|
|
|
waterTraceDistanceFromEnd = ( ( 42 ) - ( 12 ) ) - ( trace["fraction"] * ( ( 42 ) - ( 12 ) ) );
|
|
static_alpha = min( 1.0, waterTraceDistanceFromEnd / ( ( 36 ) - ( 12 ) ) );
|
|
|
|
// design does not want beeping audio for when water is an issue, maybe a different kind of audio?
|
|
if ( isdefined( self.owner ) && self.controlled )
|
|
self.owner clientfield::set_to_player( "static_postfx", ( ( static_alpha > 0.0 ) ? 1 : 0 ) );
|
|
}
|
|
|
|
if( isdefined( self.owner ) )
|
|
self.owner.dofutz = true;
|
|
|
|
self notify( "death" );
|
|
}
|
|
|
|
/#
|
|
function tank_devgui_think()
|
|
{
|
|
SetDvar( "devgui_tank", "" );
|
|
|
|
for ( ;; )
|
|
{
|
|
wait( 0.25 );
|
|
|
|
level.ai_tank_turret_fire_rate = level.ai_tank_turret_weapon.fireTime;
|
|
|
|
if ( GetDvarString( "devgui_tank" ) == "routes" )
|
|
{
|
|
devgui_debug_route();
|
|
SetDvar( "devgui_tank", "" );
|
|
}
|
|
}
|
|
}
|
|
|
|
function tank_debug_patrol( node1, node2 )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "debug_patrol" );
|
|
|
|
for( ;; )
|
|
{
|
|
self SetVehGoalPos( node1.origin, true );
|
|
self waittill( "reached_end_node" );
|
|
|
|
wait( 1 );
|
|
|
|
self SetVehGoalPos( node2.origin, true );
|
|
self waittill( "reached_end_node" );
|
|
|
|
wait( 1 );
|
|
}
|
|
}
|
|
|
|
function devgui_debug_route()
|
|
{
|
|
iprintln( "Choose nodes with 'A' or press 'B' to cancel" );
|
|
nodes = dev::dev_get_node_pair();
|
|
|
|
if ( !isdefined( nodes ) )
|
|
{
|
|
iprintln( "Route Debug Cancelled" );
|
|
return;
|
|
}
|
|
|
|
iprintln( "Sending talons to chosen nodes" );
|
|
|
|
tanks = GetEntArray( "talon", "targetname" );
|
|
|
|
foreach( tank in tanks )
|
|
{
|
|
tank notify( "debug_patrol" );
|
|
tank thread tank_debug_patrol( nodes[0], nodes[1] );
|
|
}
|
|
}
|
|
|
|
function tank_debug_hud_init()
|
|
{
|
|
host = util::getHostPlayer();
|
|
|
|
while ( !isdefined( host ) )
|
|
{
|
|
wait( 0.25 );
|
|
host = util::getHostPlayer();
|
|
}
|
|
|
|
x = 80;
|
|
y = 40;
|
|
|
|
level.ai_tank_bar = NewClientHudElem( host );
|
|
level.ai_tank_bar.x = x + 80;
|
|
level.ai_tank_bar.y = y + 2;
|
|
level.ai_tank_bar.alignX = "left";
|
|
level.ai_tank_bar.alignY = "top";
|
|
level.ai_tank_bar.horzAlign = "fullscreen";
|
|
level.ai_tank_bar.vertAlign = "fullscreen";
|
|
level.ai_tank_bar.alpha = 0;
|
|
level.ai_tank_bar.foreground = 0;
|
|
level.ai_tank_bar setshader( "black", 1, 8 );
|
|
|
|
level.ai_tank_text = NewClientHudElem( host );
|
|
level.ai_tank_text.x = x + 80;
|
|
level.ai_tank_text.y = y;
|
|
level.ai_tank_text.alignX = "left";
|
|
level.ai_tank_text.alignY = "top";
|
|
level.ai_tank_text.horzAlign = "fullscreen";
|
|
level.ai_tank_text.vertAlign = "fullscreen";
|
|
level.ai_tank_text.alpha = 0;
|
|
level.ai_tank_text.fontScale = 1;
|
|
level.ai_tank_text.foreground = 1;
|
|
}
|
|
|
|
function tank_debug_health()
|
|
{
|
|
self.damage_debug = "";
|
|
|
|
level.ai_tank_bar.alpha = 1;
|
|
level.ai_tank_text.alpha = 1;
|
|
|
|
for ( ;; )
|
|
{
|
|
{wait(.05);};
|
|
|
|
if ( !isdefined( self ) || !IsAlive( self ) )
|
|
{
|
|
level.ai_tank_bar.alpha = 0;
|
|
level.ai_tank_text.alpha = 0;
|
|
return;
|
|
}
|
|
|
|
width = self.health / self.maxhealth * 300;
|
|
width = int( max( width, 1 ) );
|
|
level.ai_tank_bar setShader( "black", width, 8 );
|
|
|
|
str = ( self.health + " Last Damage: " + self.damage_debug );
|
|
level.ai_tank_text SetText( str );
|
|
}
|
|
}
|
|
|
|
#/
|