init
This commit is contained in:
1567
raw/maps/mp/killstreaks/_aerial_utility.gsc
Normal file
1567
raw/maps/mp/killstreaks/_aerial_utility.gsc
Normal file
File diff suppressed because it is too large
Load Diff
203
raw/maps/mp/killstreaks/_agent_killstreak.gsc
Normal file
203
raw/maps/mp/killstreaks/_agent_killstreak.gsc
Normal file
@ -0,0 +1,203 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
|
||||
//===========================================
|
||||
// constants
|
||||
//===========================================
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_GAME = 5;
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER = 2;
|
||||
|
||||
|
||||
//===========================================
|
||||
// init
|
||||
//===========================================
|
||||
init()
|
||||
{
|
||||
level.killStreakFuncs["agent"] = ::tryUseSquadmate;
|
||||
level.killStreakFuncs["recon_agent"] = ::tryUseReconSquadmate;
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// setup_callbacks
|
||||
//===========================================
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"] = level.agent_funcs["player"];
|
||||
|
||||
level.agent_funcs["squadmate"]["think"] = ::squadmate_agent_think;
|
||||
level.agent_funcs["squadmate"]["on_killed"] = ::on_agent_squadmate_killed;
|
||||
level.agent_funcs["squadmate"]["on_damaged"] = maps\mp\agents\_agents::on_agent_player_damaged;
|
||||
level.agent_funcs["squadmate"]["gametype_update"]= ::no_gametype_update;
|
||||
}
|
||||
|
||||
no_gametype_update()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//===========================================
|
||||
// tryUseSquadmate
|
||||
//===========================================
|
||||
tryUseSquadmate( lifeId, streakName )
|
||||
{
|
||||
return useSquadmate( "agent" );
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// tryUseReconSquadmate
|
||||
//===========================================
|
||||
tryUseReconSquadmate( lifeId, streakName )
|
||||
{
|
||||
return useSquadmate( "reconAgent" );
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// useSquadmate
|
||||
//===========================================
|
||||
useSquadmate( killStreakType )
|
||||
{
|
||||
// limit the number of active "squadmate" agents allowed per game
|
||||
if( getNumActiveAgents( "squadmate" ) >= CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_GAME )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AGENT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// limit the number of active agents allowed per player
|
||||
if( getNumOwnedActiveAgents( self ) >= CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AGENT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to spawn the agent on a path node near the player
|
||||
nearestPathNode = self getValidSpawnPathNodeNearPlayer( false, true );
|
||||
|
||||
if( !IsDefined(nearestPathNode) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the player is still alive before the agent trys to spawn on the player
|
||||
if( !isReallyAlive(self) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
spawnOrigin = nearestPathNode.origin;
|
||||
spawnAngles = VectorToAngles( self.origin - nearestPathNode.origin );
|
||||
|
||||
agent = maps\mp\agents\_agents::add_humanoid_agent( "squadmate", self.team, undefined, spawnOrigin, spawnAngles, self, false, false, "veteran" );
|
||||
if( !IsDefined( agent ) )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AGENT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
agent.killStreakType = killStreakType;
|
||||
|
||||
if ( agent.killStreakType == "reconAgent" )
|
||||
{
|
||||
// 2013-06-26 wallace
|
||||
// At the time of this comment, giveLoadout runs and finishes execution immediately
|
||||
// We run sendAgentWeaponNotify and finishReconAgentLoadout since they block until giveLoadout sends its notify
|
||||
agent thread sendAgentWeaponNotify( "iw6_riotshield_mp" );
|
||||
agent thread finishReconAgentLoadout();
|
||||
agent thread maps\mp\gametypes\_class::giveAndApplyLoadout( self.pers["team"], "reconAgent", false );
|
||||
agent maps\mp\agents\_agent_common::set_agent_health( 250 );
|
||||
agent maps\mp\perks\_perkfunctions::setLightArmor();
|
||||
}
|
||||
else
|
||||
{
|
||||
agent maps\mp\perks\_perkfunctions::setLightArmor();
|
||||
}
|
||||
|
||||
agent _setNameplateMaterial( "player_name_bg_green_agent", "player_name_bg_red_agent" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
finishReconAgentLoadout()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self waittill( "applyLoadout" );
|
||||
|
||||
self maps\mp\perks\_perkfunctions::setLightArmor();
|
||||
self givePerk( "specialty_quickswap", false );
|
||||
self givePerk( "specialty_regenfaster", false );
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// sendAgentWeaponNotify
|
||||
//===========================================
|
||||
sendAgentWeaponNotify( weaponName )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self waittill( "applyLoadout" );
|
||||
|
||||
if( !IsDefined(weaponName) )
|
||||
weaponName = "iw6_riotshield_mp";
|
||||
|
||||
self notify( "weapon_change", weaponName );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// squadmate_agent_think
|
||||
//=======================================================
|
||||
squadmate_agent_think()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
level endon( "game_ended" );
|
||||
|
||||
while(1)
|
||||
{
|
||||
// Squad mate agent prefers to have shield out when not in combat and guarding player
|
||||
self BotSetFlag( "prefer_shield_out", true );
|
||||
|
||||
handled_by_gametype = self [[ self agentFunc("gametype_update") ]]();
|
||||
if ( !handled_by_gametype )
|
||||
{
|
||||
if ( !self bot_is_guarding_player( self.owner ) )
|
||||
self bot_guard_player( self.owner, 350 );
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_agent_squadmate_killed
|
||||
//=======================================================
|
||||
on_agent_squadmate_killed(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration)
|
||||
{
|
||||
self maps\mp\agents\_agents::on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, false);
|
||||
|
||||
// award XP for killing agents
|
||||
if( IsPlayer( eAttacker ) && IsDefined(self.owner) && eAttacker != self.owner )
|
||||
{
|
||||
self.owner leaderDialogOnPlayer( "squad_killed" );
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( eAttacker, sWeapon, sMeansOfDeath, iDamage, "destroyed_squad_mate" );
|
||||
}
|
||||
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
}
|
1955
raw/maps/mp/killstreaks/_airdrop.gsc
Normal file
1955
raw/maps/mp/killstreaks/_airdrop.gsc
Normal file
File diff suppressed because it is too large
Load Diff
1410
raw/maps/mp/killstreaks/_airstrike.gsc
Normal file
1410
raw/maps/mp/killstreaks/_airstrike.gsc
Normal file
File diff suppressed because it is too large
Load Diff
843
raw/maps/mp/killstreaks/_assaultdrone_ai.gsc
Normal file
843
raw/maps/mp/killstreaks/_assaultdrone_ai.gsc
Normal file
@ -0,0 +1,843 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_aerial_pathnodes;
|
||||
|
||||
SCR_CONST_PATH_TIMEOUT_TIME_AIR = 7.5;
|
||||
SCR_CONST_REACHED_NODE_RADIUS = 24.0;
|
||||
SCR_CONST_ENEMY_MOVED_FAR_FROM_GOAL_DIST = 128.0;
|
||||
SCR_CONST_WEAPON_RADAR_PING_TIME = 3.0;
|
||||
SCR_CONST_ENEMY_TOO_CLOSE_FOR_ROCKETS_DIST_SQ = 132 * 132;
|
||||
SCR_CONST_ENEMY_TARGET_OFFSET = (0,0,40);
|
||||
|
||||
/#
|
||||
SCR_CONST_ALWAYS_SHOW_VEHICLE_PATH_DEBUG = false;
|
||||
SCR_CONST_SHOW_AI_TARGET = false;
|
||||
SCR_CONST_ONLY_ASCEND_NO_ADDITIONAL_MOVEMENT = false;
|
||||
#/
|
||||
|
||||
/*
|
||||
TODO
|
||||
|
||||
Rockets sometimes blow up in face
|
||||
*/
|
||||
|
||||
assault_vehicle_ai_init()
|
||||
{
|
||||
thread maps\mp\_aerial_pathnodes::calculate_aerial_pathnodes();
|
||||
}
|
||||
|
||||
AIStartUsingAssaultVehicle( AssaultVehicle )
|
||||
{
|
||||
self thread assault_vehicle_ai_end_on_owner_disconnect( AssaultVehicle );
|
||||
|
||||
wait(2.0); // give time for the vehicle to appear
|
||||
|
||||
waittill_aerial_pathnodes_calculated();
|
||||
|
||||
if ( IsDefined(AssaultVehicle) )
|
||||
{
|
||||
AssaultVehicle.enemy_target_last_vis_time = 0;
|
||||
AssaultVehicle.enemy_target_visible = false;
|
||||
|
||||
AssaultVehicle thread assault_vehicle_ai_aerial_movement();
|
||||
|
||||
AssaultVehicle thread assault_vehicle_ai_threat();
|
||||
AssaultVehicle thread assault_vehicle_ai_weapons( !AssaultVehicle.hasTurret );
|
||||
|
||||
if( IsDefined( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
self.aerialAssaultDrone = AssaultVehicle;
|
||||
AssaultVehicle.isAerialAssaultDrone = true;
|
||||
AssaultVehicle thread assault_vehicle_horde_monitor_death( self );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assault_vehicle_horde_monitor_death( player ) // self == aerial assault drone
|
||||
{
|
||||
self waittill( "death" );
|
||||
self.aerialAssaultDrone = undefined;
|
||||
}
|
||||
|
||||
assault_vehicle_ai_end_on_owner_disconnect( AssaultVehicle ) // self == owner who called in the AI vehicle
|
||||
{
|
||||
AssaultVehicle endon("death");
|
||||
|
||||
self waittill("disconnect");
|
||||
AssaultVehicle notify("death");
|
||||
}
|
||||
|
||||
assault_vehicle_ai_aerial_movement() // self == Assault Vehicle
|
||||
{
|
||||
self notify("assault_vehicle_ai_aerial_movement");
|
||||
self endon("assault_vehicle_ai_aerial_movement");
|
||||
self endon("death");
|
||||
|
||||
speed = self Vehicle_GetTopSpeed();
|
||||
self Vehicle_SetSpeed(speed,8,60);
|
||||
self SetHoverParams(0,0,0);
|
||||
|
||||
// First try to get the vehicle outside
|
||||
|
||||
// Find the closest outside node and calculate a path to it
|
||||
path = undefined;
|
||||
nearest_node_to_vehicle = assault_vehicle_ai_get_nearest_node();
|
||||
if ( !IsDefined(nearest_node_to_vehicle) )
|
||||
{
|
||||
// Couldn't find a nearest node to the vehicle, so movement will be impossible. Assert and return.
|
||||
AssertMsg("AI Aerial Assault Vehicle unable to find nearby node near pos " + self.origin );
|
||||
return;
|
||||
}
|
||||
|
||||
valid_close_aerial_node = nearest_node_to_vehicle;
|
||||
if ( !node_is_aerial(valid_close_aerial_node) )
|
||||
{
|
||||
node = get_ent_closest_aerial_node(64, 0);
|
||||
if ( IsDefined(node) )
|
||||
valid_close_aerial_node = node;
|
||||
}
|
||||
|
||||
if ( node_is_aerial(valid_close_aerial_node) )
|
||||
path = GetNodesOnPath( self.origin, valid_close_aerial_node.origin, true, nearest_node_to_vehicle );
|
||||
|
||||
if ( !IsDefined(path) )
|
||||
{
|
||||
max_dist_search = 1500;
|
||||
min_dist_search = 0;
|
||||
|
||||
while(!IsDefined(path) && min_dist_search < max_dist_search)
|
||||
{
|
||||
nearest_node = get_ent_closest_aerial_node(max_dist_search, min_dist_search);
|
||||
if ( IsDefined(nearest_node) )
|
||||
{
|
||||
path = GetNodesOnPath( self.origin, nearest_node.origin, true, nearest_node_to_vehicle );
|
||||
if ( !IsDefined(path) )
|
||||
min_dist_search = Distance(self.origin,nearest_node.origin) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_dist_search = max_dist_search + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( min_dist_search > max_dist_search )
|
||||
{
|
||||
// Couldn't find any outside nodes (or couldn't find a path to them), so movement will be impossible. Assert and return.
|
||||
AssertMsg("AI Aerial Assault Vehicle unable to find any nearby aerial nodes near pos " + self.origin );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now follow the path to the closest outside node
|
||||
end_node = assault_vehicle_ai_aerial_follow_path_outside(path);
|
||||
|
||||
// Then path up to the aerial version of this pathnode
|
||||
assault_vehicle_ai_move_to_aerial_node(end_node);
|
||||
wait(0.85); // Settle at the node before continuing
|
||||
|
||||
should_aerial_move = true;
|
||||
/#
|
||||
if ( SCR_CONST_ONLY_ASCEND_NO_ADDITIONAL_MOVEMENT )
|
||||
should_aerial_move = false;
|
||||
#/
|
||||
if ( should_aerial_move )
|
||||
{
|
||||
self notify("in_air");
|
||||
self assault_vehicle_ai_aerial_pathing_turret(end_node);
|
||||
}
|
||||
}
|
||||
|
||||
assault_vehicle_ai_aerial_follow_path_outside( path )
|
||||
{
|
||||
node_offset = (0,0,40);
|
||||
|
||||
for( i=0; i<path.size; i++ )
|
||||
{
|
||||
next_node = path[i];
|
||||
self assault_vehicle_ai_air_movement_func(next_node.origin + node_offset);
|
||||
drive_time = 0;
|
||||
|
||||
while( Distance2DSquared( next_node.origin, self.origin ) > squared(SCR_CONST_REACHED_NODE_RADIUS) )
|
||||
{
|
||||
/#
|
||||
self assault_vehicle_ai_draw_debug_path(path,i,node_offset);
|
||||
#/
|
||||
drive_time += 0.05;
|
||||
if ( drive_time > self assault_vehicle_ai_path_timeout_time() )
|
||||
return;
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
|
||||
// Vehicle is really close to next_node. If it is aerial, just bail so we can ascend
|
||||
if ( node_is_aerial( next_node ) )
|
||||
return next_node;
|
||||
}
|
||||
|
||||
return path[path.size-1];
|
||||
}
|
||||
|
||||
assault_vehicle_ai_move_to_aerial_node( node )
|
||||
{
|
||||
Assert(node_is_aerial(node));
|
||||
// We're assuming here that the vehicle is currently at a node that can reach this node by moving directly toward it
|
||||
|
||||
current_node_origin = node.origin + get_aerial_offset();
|
||||
self assault_vehicle_ai_air_movement_func( current_node_origin );
|
||||
while ( DistanceSquared( self.origin, current_node_origin ) > SCR_CONST_REACHED_NODE_RADIUS * SCR_CONST_REACHED_NODE_RADIUS )
|
||||
{
|
||||
/#
|
||||
if ( GetDvarInt("ai_showpaths") == 1 || SCR_CONST_ALWAYS_SHOW_VEHICLE_PATH_DEBUG )
|
||||
line(self.origin, current_node_origin, (0,0,1), 1.0, false, 4);
|
||||
#/
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
assault_vehicle_ai_aerial_pathing_turret( start_node )
|
||||
{
|
||||
Assert(node_is_aerial(start_node));
|
||||
// We're assuming here that the vehicle is already at the start_node and needs to pick a new node right away
|
||||
|
||||
current_aerial_node = start_node;
|
||||
visited_nodes = [];
|
||||
visited_nodes[current_aerial_node GetNodeNumber()] = 1;
|
||||
|
||||
while(1)
|
||||
{
|
||||
// Pick a new node to visit
|
||||
current_aerial_node = assault_vehicle_ai_pick_aerial_node( current_aerial_node, visited_nodes );
|
||||
|
||||
// Move to the current node
|
||||
assault_vehicle_ai_move_to_aerial_node(current_aerial_node);
|
||||
|
||||
// Mark the current node as visited
|
||||
current_node_number = current_aerial_node GetNodeNumber();
|
||||
if ( !IsDefined(visited_nodes[current_node_number]) )
|
||||
visited_nodes[current_node_number] = 0;
|
||||
visited_nodes[current_node_number]++;
|
||||
|
||||
// Wait some amount of time at this node
|
||||
wait(RandomFloatRange(0.05,2.0));
|
||||
}
|
||||
}
|
||||
|
||||
assault_vehicle_ai_aerial_pathing_c4()
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
node_goal = undefined;
|
||||
if ( self assault_vehicle_ai_enemy_exists_and_is_alive() )
|
||||
node_goal = self.enemy_target get_ent_closest_aerial_node();
|
||||
|
||||
if ( !IsDefined(node_goal) )
|
||||
node_goal = Random(level.aerial_pathnodes);
|
||||
|
||||
start_node = self get_ent_closest_aerial_node();
|
||||
path = find_path_between_aerial_nodes( start_node, node_goal );
|
||||
if ( IsDefined(path) )
|
||||
assault_vehicle_ai_follow_path(path, ::assault_vehicle_ai_air_movement_func, ::assault_vehicle_ai_enemy_moved_air, get_aerial_offset()[2]);
|
||||
|
||||
if ( self assault_vehicle_ai_enemy_exists_and_is_alive() )
|
||||
{
|
||||
if ( !self assault_vehicle_ai_enemy_moved_air(node_goal) || Distance2DSquared(self.origin,self.enemy_target.origin) < squared(200) )
|
||||
{
|
||||
// Descend to enemy and proceed on the ground
|
||||
ground_target_origin = self.enemy_target.origin + SCR_CONST_ENEMY_TARGET_OFFSET;
|
||||
self SetVehGoalPos( ground_target_origin, true );
|
||||
while( self assault_vehicle_ai_enemy_exists_and_is_alive() && DistanceSquared( ground_target_origin, self.origin ) > squared(SCR_CONST_REACHED_NODE_RADIUS) )
|
||||
wait(0.05);
|
||||
wait(0.8);
|
||||
|
||||
self assault_vehicle_ai_ground_movement( ::assault_vehicle_ai_air_movement_func, ::assault_vehicle_ai_enemy_moved_ground );
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
assault_vehicle_ai_pick_aerial_node( current_node, visited_nodes )
|
||||
{
|
||||
best_node = undefined;
|
||||
best_node_visits = 9999;
|
||||
neighbors_randomized = array_randomize(current_node.aerial_neighbors);
|
||||
foreach( neighbor_node in neighbors_randomized )
|
||||
{
|
||||
neighbor_node_num = neighbor_node GetNodeNumber();
|
||||
neighbor_node_visits = visited_nodes[neighbor_node_num];
|
||||
if ( !IsDefined(neighbor_node_visits) )
|
||||
return neighbor_node; // Node has never been visited, just return it
|
||||
|
||||
if ( neighbor_node_visits < best_node_visits )
|
||||
{
|
||||
best_node = neighbor_node;
|
||||
best_node_visits = neighbor_node_visits;
|
||||
}
|
||||
}
|
||||
|
||||
Assert(IsDefined(best_node)); // There should always be a node returned from this function
|
||||
return best_node;
|
||||
}
|
||||
|
||||
assault_vehicle_ai_get_nearest_node()
|
||||
{
|
||||
nearest_node_to_vehicle = GetClosestNodeInSight( self.origin, true );
|
||||
if ( !IsDefined(nearest_node_to_vehicle) )
|
||||
{
|
||||
nodes_near_vehicle_sorted = GetNodesInRadiusSorted(self.origin,1000,0);
|
||||
if ( nodes_near_vehicle_sorted.size > 0 )
|
||||
nearest_node_to_vehicle = nodes_near_vehicle_sorted[0];
|
||||
}
|
||||
|
||||
return nearest_node_to_vehicle;
|
||||
}
|
||||
|
||||
assault_vehicle_ai_ground_movement( movement_func, enemy_moved_func ) // self == Assault Vehicle
|
||||
{
|
||||
self endon("death");
|
||||
|
||||
// Test to see if there is a nearest node
|
||||
nearest_node_to_vehicle = assault_vehicle_ai_get_nearest_node();
|
||||
if ( !IsDefined(nearest_node_to_vehicle) )
|
||||
{
|
||||
// Couldn't find a nearest node to the vehicle, so movement will be impossible. Assert and return.
|
||||
AssertMsg("AI Ground Assault Vehicle unable to find nearby node near pos " + self.origin );
|
||||
return;
|
||||
}
|
||||
|
||||
while(1)
|
||||
{
|
||||
self childthread assault_vehicle_ai_ground_movement_loop( movement_func, enemy_moved_func );
|
||||
self waittill_any("enemy");
|
||||
}
|
||||
}
|
||||
|
||||
assault_vehicle_ai_ground_movement_loop( movement_func, enemy_moved_func ) // self == Assault Vehicle
|
||||
{
|
||||
self notify("assault_vehicle_ai_ground_movement_loop");
|
||||
self endon("assault_vehicle_ai_ground_movement_loop");
|
||||
|
||||
unreachable_nodes = [];
|
||||
|
||||
while(1)
|
||||
{
|
||||
randomNode = undefined;
|
||||
goal_pos = undefined;
|
||||
if ( self assault_vehicle_ai_enemy_exists_and_is_alive() )
|
||||
{
|
||||
goal_pos = self.enemy_target.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = 0;
|
||||
while( !IsDefined(randomNode) && count < 20 )
|
||||
{
|
||||
randomNode = GetRandomNodeDestination( self.origin, self.angles );
|
||||
if ( IsDefined(randomNode) )
|
||||
{
|
||||
if ( array_contains(unreachable_nodes,randomNode) )
|
||||
randomNode = undefined; // Node was already marked as unreachable, so ignore it
|
||||
else
|
||||
goal_pos = randomNode.origin;
|
||||
}
|
||||
|
||||
count++;
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( goal_pos ) )
|
||||
{
|
||||
start_node = assault_vehicle_ai_get_nearest_node();
|
||||
if ( !IsDefined(start_node) )
|
||||
{
|
||||
// Couldn't find a nearest node to the vehicle, so movement will be impossible. Assert and return.
|
||||
AssertMsg("AI Ground Assault Vehicle unable to find nearby node near pos " + self.origin );
|
||||
return;
|
||||
}
|
||||
|
||||
path = GetNodesOnPath( self.origin, goal_pos, false, start_node );
|
||||
if ( IsDefined(path) )
|
||||
assault_vehicle_ai_follow_path(path, movement_func, enemy_moved_func );
|
||||
else
|
||||
unreachable_nodes[unreachable_nodes.size] = randomNode;
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
assault_vehicle_ai_get_camera_position() // self == Assault Vehicle
|
||||
{
|
||||
camera_offset = self VehicleGet3PCameraOffset();
|
||||
camera_position = self.origin + RotateVector( camera_offset, self.angles );
|
||||
return camera_position;
|
||||
}
|
||||
|
||||
assault_vehicle_ai_threat() // self == Assault Vehicle
|
||||
{
|
||||
self endon("death");
|
||||
/#
|
||||
self childthread assault_vehicle_ai_debug_threat_lines();
|
||||
#/
|
||||
while(1)
|
||||
{
|
||||
known_enemies = [];
|
||||
wait_this_frame = false;
|
||||
|
||||
if ( IsDefined(self.enemy_target) && !IsAlive(self.enemy_target) )
|
||||
{
|
||||
last_inflictor = self.enemy_target.lastInflictor;
|
||||
if ( IsDefined(last_inflictor) )
|
||||
{
|
||||
if ( last_inflictor == self || ( IsDefined(last_inflictor.tank) && last_inflictor.tank == self ) )
|
||||
{
|
||||
self.fire_at_dead_time = GetTime() + 1000;
|
||||
wait(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach( character in level.characters )
|
||||
{
|
||||
if ( IsAlive(character) && !IsAlliedSentient(self,character) && self.owner != character )
|
||||
{
|
||||
if ( character _hasPerk( "specialty_blindeye" ) )
|
||||
continue;
|
||||
|
||||
is_position_known = false;
|
||||
|
||||
trace_start = self assault_vehicle_ai_get_camera_position();
|
||||
trace_end = character.origin + SCR_CONST_ENEMY_TARGET_OFFSET;
|
||||
|
||||
if ( self.hasARHud )
|
||||
is_position_known = true;
|
||||
if ( IsDefined(character.lastshotfiredtime) && GetTime() - character.lastshotfiredtime < SCR_CONST_WEAPON_RADAR_PING_TIME )
|
||||
is_position_known = true;
|
||||
else if ( GetTeamRadarStrength(self.team) > GetUAVStrengthLevelNeutral() )
|
||||
is_position_known = true;
|
||||
else if ( SightTracePassed( trace_start, trace_end, false, self, character ) )
|
||||
is_position_known = true;
|
||||
|
||||
if ( is_position_known && self.hasTurret )
|
||||
{
|
||||
// Don't target an enemy directly out of my turret's firing angle
|
||||
pitch_max = self VehicleGet3PPitchClamp();
|
||||
|
||||
vec_to_target = trace_end - trace_start;
|
||||
angles_to_target = VectorToAngles(vec_to_target);
|
||||
pitch = AngleClamp180(angles_to_target[0]);
|
||||
|
||||
if ( pitch > pitch_max || pitch < -1*pitch_max )
|
||||
is_position_known = false;
|
||||
}
|
||||
|
||||
if ( is_position_known )
|
||||
known_enemies[known_enemies.size] = character;
|
||||
|
||||
if ( wait_this_frame )
|
||||
wait(0.05);
|
||||
wait_this_frame = !wait_this_frame;
|
||||
}
|
||||
}
|
||||
|
||||
// Add enemy drones and sentries to the list
|
||||
if( IsDefined( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
// Add enemy drones
|
||||
foreach( drone in level.flying_attack_drones )
|
||||
{
|
||||
is_position_known = false;
|
||||
|
||||
trace_start = self assault_vehicle_ai_get_camera_position();
|
||||
trace_end = (0,0,0);
|
||||
if( IsDefined( drone.origin ) )
|
||||
{
|
||||
trace_end = drone.origin;
|
||||
if ( SightTracePassed( trace_start, trace_end, false, self, drone ) )
|
||||
is_position_known = true;
|
||||
}
|
||||
|
||||
if ( is_position_known )
|
||||
{
|
||||
// Don't target an enemy directly out of my turret's firing angle
|
||||
pitch_max = self VehicleGet3PPitchClamp();
|
||||
|
||||
vec_to_target = trace_end - trace_start;
|
||||
angles_to_target = VectorToAngles(vec_to_target);
|
||||
pitch = AngleClamp180(angles_to_target[0]);
|
||||
|
||||
if ( pitch > pitch_max || pitch < -1*pitch_max )
|
||||
is_position_known = false;
|
||||
}
|
||||
|
||||
if ( is_position_known )
|
||||
{
|
||||
known_enemies[known_enemies.size] = drone;
|
||||
}
|
||||
|
||||
// Wait so that we don't overthink who our closest target is in any given frame
|
||||
if ( wait_this_frame )
|
||||
waitframe;
|
||||
wait_this_frame = !wait_this_frame;
|
||||
}
|
||||
|
||||
// Add enemy sentries
|
||||
foreach( sentry in level.hordeSentryArray )
|
||||
{
|
||||
is_position_known = false;
|
||||
|
||||
trace_start = self assault_vehicle_ai_get_camera_position();
|
||||
trace_end = (0,0,0);
|
||||
|
||||
if( IsDefined( sentry.origin ) )
|
||||
{
|
||||
trace_end = sentry.origin + SCR_CONST_ENEMY_TARGET_OFFSET;
|
||||
if ( SightTracePassed( trace_start, trace_end, false, self, sentry ) )
|
||||
is_position_known = true;
|
||||
}
|
||||
|
||||
if ( is_position_known )
|
||||
{
|
||||
// Don't target an enemy directly out of my turret's firing angle
|
||||
pitch_max = self VehicleGet3PPitchClamp();
|
||||
|
||||
vec_to_target = trace_end - trace_start;
|
||||
angles_to_target = VectorToAngles(vec_to_target);
|
||||
pitch = AngleClamp180(angles_to_target[0]);
|
||||
|
||||
if ( pitch > pitch_max || pitch < -1*pitch_max )
|
||||
is_position_known = false;
|
||||
}
|
||||
|
||||
if ( is_position_known )
|
||||
{
|
||||
known_enemies[known_enemies.size] = sentry;
|
||||
}
|
||||
|
||||
// Wait so that we don't overthink who our closest target is in any given frame
|
||||
if ( wait_this_frame )
|
||||
waitframe;
|
||||
wait_this_frame = !wait_this_frame;
|
||||
}
|
||||
|
||||
// Remove dead entities (enemies that have been killed so no longer exist)
|
||||
removeTheseGuys = [];
|
||||
|
||||
foreach( guy in known_enemies )
|
||||
{
|
||||
if( !IsDefined( guy ) )
|
||||
{
|
||||
removeTheseGuys[ removeTheseGuys.size ] = guy;
|
||||
}
|
||||
else if( ( IsDefined( guy.ishordedrone ) && guy.ishordedrone ) )
|
||||
{
|
||||
if( !IsDefined( guy.damageTaken ) || !IsDefined( guy.maxHealth ) || ( guy.damageTaken > guy.maxHealth ) )
|
||||
{
|
||||
removeTheseGuys[ removeTheseGuys.size ] = guy;
|
||||
}
|
||||
}
|
||||
else if( IsDefined( guy.ishordeenemysentry ) && guy.ishordeenemysentry )
|
||||
{
|
||||
if( ! guy.isAlive )
|
||||
{
|
||||
removeTheseGuys[ removeTheseGuys.size ] = guy;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach( guy in removeTheseGuys )
|
||||
{
|
||||
known_enemies = array_remove( known_enemies, guy );
|
||||
}
|
||||
}
|
||||
|
||||
// Choose the closest living enemy
|
||||
if ( known_enemies.size > 0 )
|
||||
{
|
||||
known_enemies_sorted = get_array_of_closest( self.origin, known_enemies );
|
||||
old_enemy = self.enemy_target;
|
||||
self.enemy_target = known_enemies_sorted[0];
|
||||
|
||||
if ( !IsDefined(old_enemy) || old_enemy != self.enemy_target )
|
||||
self notify("enemy");
|
||||
}
|
||||
else if ( IsDefined(self.enemy_target) )
|
||||
{
|
||||
// Current enemy is not visible, so clear it out but don't change the path
|
||||
self.enemy_target = undefined;
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
/#
|
||||
assault_vehicle_ai_debug_threat_lines()
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
if ( SCR_CONST_SHOW_AI_TARGET )
|
||||
{
|
||||
if ( self assault_vehicle_ai_enemy_exists_and_is_alive() )
|
||||
{
|
||||
debug_line_color = (0,1,0);
|
||||
if ( self assault_vehicle_ai_can_see_living_enemy() )
|
||||
debug_line_color = (1,0,0);
|
||||
|
||||
if( IsDefined( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
if( ( IsDefined( self.enemy_target.ishordedrone ) && self.enemy_target.ishordedrone ) )
|
||||
line(self.origin,self.enemy_target.origin,debug_line_color,1.0,false);
|
||||
else
|
||||
line(self.origin,self.enemy_target.origin + SCR_CONST_ENEMY_TARGET_OFFSET,debug_line_color,1.0,false);
|
||||
}
|
||||
else
|
||||
line(self.origin,self.enemy_target.origin + SCR_CONST_ENEMY_TARGET_OFFSET,debug_line_color,1.0,false);
|
||||
}
|
||||
|
||||
if ( IsDefined(self.TargetEnt) )
|
||||
line(self.origin,self.TargetEnt.origin,(0,0,1),1.0,false);
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
assault_vehicle_ai_weapons( wait_till_in_air ) // self == Assault Vehicle
|
||||
{
|
||||
self endon("death");
|
||||
|
||||
if ( wait_till_in_air )
|
||||
self waittill("in_air");
|
||||
|
||||
self.last_rocket_time = 0;
|
||||
self.initial_enemy_target = true;
|
||||
c4_radius_sq = squared(maps\mp\killstreaks\_drone_assault::GetAssaultVehicleC4Radius() * 0.75);
|
||||
|
||||
while(1)
|
||||
{
|
||||
if ( IsDefined(self.TargetEnt) )
|
||||
{
|
||||
if ( self assault_vehicle_ai_enemy_exists_and_is_alive() )
|
||||
{
|
||||
if ( self assault_vehicle_ai_can_see_living_enemy() )
|
||||
{
|
||||
if( IsDefined( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
if( ( IsDefined( self.enemy_target.ishordedrone ) && self.enemy_target.ishordedrone ) )
|
||||
self.TargetEnt.origin = self.enemy_target.origin;
|
||||
else
|
||||
self.TargetEnt.origin = self.enemy_target.origin + SCR_CONST_ENEMY_TARGET_OFFSET;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( self.hasTurret )
|
||||
self.TargetEnt.origin = self.enemy_target.origin + SCR_CONST_ENEMY_TARGET_OFFSET;
|
||||
else
|
||||
self.TargetEnt.origin = self.enemy_target.origin + (AnglesToForward(self.enemy_target.angles) * 100);
|
||||
}
|
||||
|
||||
desired_angles = VectorToAngles(self.TargetEnt.origin - self.origin);
|
||||
yaw_change = desired_angles[1] - self.angles[1];
|
||||
while(yaw_change > 180)
|
||||
yaw_change -= 360;
|
||||
while(yaw_change < -180)
|
||||
yaw_change += 360;
|
||||
|
||||
yaw_change_per_frame = 10;
|
||||
if ( abs(yaw_change) < yaw_change_per_frame )
|
||||
new_yaw = desired_angles[1];
|
||||
else
|
||||
new_yaw = self.angles[1] + yaw_change_per_frame * (yaw_change/abs(yaw_change));
|
||||
self Vehicle_Teleport( self.origin, (desired_angles[0], new_yaw, self.angles[2]), true, true );
|
||||
|
||||
if ( self.initial_enemy_target )
|
||||
{
|
||||
// First time we've targeted this enemy, so allow ForceTurretAimAt to run before we continue
|
||||
wait(0.1);
|
||||
self.initial_enemy_target = false;
|
||||
if ( !self assault_vehicle_ai_can_see_living_enemy() )
|
||||
continue;
|
||||
}
|
||||
|
||||
should_use_rockets = self.hasRockets && self.RocketAmmo > 0;
|
||||
if ( self.hasTurret )
|
||||
attack_start_loc = self.mgTurret GetTagOrigin( "tag_flash" );
|
||||
else
|
||||
attack_start_loc = self.origin;
|
||||
if ( should_use_rockets )
|
||||
should_use_rockets = DistanceSquared(attack_start_loc, self.enemy_target.origin) > SCR_CONST_ENEMY_TOO_CLOSE_FOR_ROCKETS_DIST_SQ;
|
||||
|
||||
vec_to_target = (self.TargetEnt.origin - attack_start_loc);
|
||||
angles_to_target = VectorToAngles(vec_to_target);
|
||||
pitch_max = self VehicleGet3PPitchClamp();
|
||||
pitch_to_target = AngleClamp180(angles_to_target[0]);
|
||||
is_within_pitch_limitations = pitch_to_target < pitch_max && pitch_to_target > -1 * pitch_max;
|
||||
|
||||
vehicle_forward_2d = VectorNormalize(AnglesToForward(self.angles) * (1,1,0));
|
||||
vec_to_target_2d = VectorNormalize(vec_to_target * (1,1,0));
|
||||
is_in_front_of_vehicle = VectorDot(vehicle_forward_2d, vec_to_target_2d) > 0.90;
|
||||
|
||||
if ( is_within_pitch_limitations && is_in_front_of_vehicle )
|
||||
{
|
||||
if ( self.hasRockets && should_use_rockets )
|
||||
{
|
||||
if ( GetTime() > self.last_rocket_time + 1000 )
|
||||
{
|
||||
if ( self.hasMG )
|
||||
self notify("FireSecondaryWeapon");
|
||||
else
|
||||
self notify("FirePrimaryWeapon");
|
||||
self.last_rocket_time = GetTime();
|
||||
}
|
||||
}
|
||||
else if ( self.hasMG )
|
||||
{
|
||||
self.mgTurret ShootTurret();
|
||||
}
|
||||
else if ( !self.hasTurret )
|
||||
{
|
||||
if ( SightTracePassed( attack_start_loc, self.TargetEnt.origin, false, self, self.enemy_target ) )
|
||||
self notify("FirePrimaryWeapon");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.initial_enemy_target = true;
|
||||
}
|
||||
}
|
||||
else if ( IsDefined(self.enemy_target) && !IsAlive(self.enemy_target) )
|
||||
{
|
||||
if ( self.hasMG )
|
||||
{
|
||||
if ( IsDefined( self.fire_at_dead_time ) && GetTime() < self.fire_at_dead_time )
|
||||
self.mgTurret ShootTurret();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( self.hasCloak && !maps\mp\killstreaks\_drone_common::droneIsCloaked( self ) )
|
||||
{
|
||||
if ( !IsDefined(self.CloakCooldown) || self.CloakCooldown == 0 )
|
||||
self notify("Cloak");
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
assault_vehicle_ai_enemy_exists_and_is_alive()
|
||||
{
|
||||
if( IsDefined( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
if( IsDefined(self.enemy_target) && IsDefined( self.enemy_target.ishordeenemysentry ) && self.enemy_target.ishordeenemysentry )
|
||||
return self.enemy_target.isAlive;
|
||||
else
|
||||
return ( IsDefined(self.enemy_target) && IsAlive(self.enemy_target) );
|
||||
}
|
||||
else
|
||||
return ( IsDefined(self.enemy_target) && IsAlive(self.enemy_target) );
|
||||
}
|
||||
|
||||
assault_vehicle_ai_can_see_living_enemy()
|
||||
{
|
||||
if ( !self assault_vehicle_ai_enemy_exists_and_is_alive() )
|
||||
return false;
|
||||
|
||||
if ( GetTime() > self.enemy_target_last_vis_time )
|
||||
{
|
||||
self.enemy_target_last_vis_time = GetTime();
|
||||
|
||||
if( IsDefined( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
if( IsDefined( self.enemy_target.ishordedrone ) && self.enemy_target.ishordedrone )
|
||||
self.enemy_target_visible = SightTracePassed( self assault_vehicle_ai_get_camera_position(), self.enemy_target.origin, false, self, self.enemy_target );
|
||||
else
|
||||
self.enemy_target_visible = SightTracePassed( self assault_vehicle_ai_get_camera_position(), self.enemy_target.origin + SCR_CONST_ENEMY_TARGET_OFFSET, false, self, self.enemy_target );
|
||||
}
|
||||
else
|
||||
self.enemy_target_visible = SightTracePassed( self assault_vehicle_ai_get_camera_position(), self.enemy_target.origin, false, self, self.enemy_target );
|
||||
}
|
||||
|
||||
return self.enemy_target_visible;
|
||||
}
|
||||
|
||||
assault_vehicle_ai_follow_path( path, movement_func, enemy_moved_func, node_z_offset )
|
||||
{
|
||||
if ( !IsDefined(node_z_offset) )
|
||||
node_z_offset = 0;
|
||||
node_offset = (0,0,node_z_offset);
|
||||
|
||||
for( i=0; i<path.size; i++ )
|
||||
{
|
||||
next_node = path[i];
|
||||
self [[movement_func]](next_node.origin + node_offset);
|
||||
drive_time = 0;
|
||||
|
||||
while( Distance2DSquared( next_node.origin, self.origin ) > squared(SCR_CONST_REACHED_NODE_RADIUS) )
|
||||
{
|
||||
/#
|
||||
self assault_vehicle_ai_draw_debug_path(path,i,node_offset);
|
||||
#/
|
||||
drive_time += 0.05;
|
||||
needs_new_path = ( drive_time > self assault_vehicle_ai_path_timeout_time() );
|
||||
|
||||
if ( !needs_new_path && self assault_vehicle_ai_enemy_exists_and_is_alive() )
|
||||
needs_new_path = self [[enemy_moved_func]](path[path.size-1]);
|
||||
|
||||
if ( needs_new_path )
|
||||
return;
|
||||
|
||||
if ( self.hasTurret && self assault_vehicle_ai_can_see_living_enemy() )
|
||||
{
|
||||
self [[movement_func]](self.origin); // don't move when enemy is visible
|
||||
while( self assault_vehicle_ai_can_see_living_enemy() )
|
||||
wait(0.05);
|
||||
|
||||
// Can no longer see the enemy, continue on path
|
||||
self [[movement_func]](next_node.origin);
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assault_vehicle_ai_enemy_moved_air( path_end_node )
|
||||
{
|
||||
enemy_nearest_aerial_node = self.enemy_target get_ent_closest_aerial_node();
|
||||
return ( enemy_nearest_aerial_node != path_end_node );
|
||||
}
|
||||
|
||||
assault_vehicle_ai_enemy_moved_ground( path_end_node )
|
||||
{
|
||||
return DistanceSquared(path_end_node.origin, self.enemy_target.origin) > squared(SCR_CONST_ENEMY_MOVED_FAR_FROM_GOAL_DIST);
|
||||
}
|
||||
|
||||
/#
|
||||
assault_vehicle_ai_draw_debug_path(path, start_index, node_offset)
|
||||
{
|
||||
if ( GetDvarInt("ai_showpaths") == 1 || SCR_CONST_ALWAYS_SHOW_VEHICLE_PATH_DEBUG )
|
||||
{
|
||||
line(self.origin + (0,0,10), path[start_index].origin + (0,0,10) + node_offset, (0,0,1), 1.0, false, 4);
|
||||
for( j=start_index; j<path.size-1; j++ )
|
||||
{
|
||||
line(path[j].origin + (0,0,10) + node_offset, path[j+1].origin + (0,0,10) + node_offset, (0,0,1), 1.0, false, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
assault_vehicle_ai_path_timeout_time()
|
||||
{
|
||||
return SCR_CONST_PATH_TIMEOUT_TIME_AIR;
|
||||
}
|
||||
|
||||
assault_vehicle_ai_air_movement_func( dest )
|
||||
{
|
||||
self SetVehGoalPos(dest, true);
|
||||
}
|
1550
raw/maps/mp/killstreaks/_autosentry.gsc
Normal file
1550
raw/maps/mp/killstreaks/_autosentry.gsc
Normal file
File diff suppressed because it is too large
Load Diff
680
raw/maps/mp/killstreaks/_coop_util.gsc
Normal file
680
raw/maps/mp/killstreaks/_coop_util.gsc
Normal file
@ -0,0 +1,680 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
CONST_COOP_USE_BAR_TIME_MIN = 1.25;
|
||||
CONST_COOP_USE_BAR_TIME_MAX = 2.5;
|
||||
|
||||
init()
|
||||
{
|
||||
if ( !level.teamBased )
|
||||
return;
|
||||
|
||||
level.streakSupportQueueAllies = [];
|
||||
level.streakSupportQueueAxis = [];
|
||||
level.streakSupporDisabledCount = [];
|
||||
|
||||
SetDvar( "scr_coop_util_delay", "1" );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: promptForStreakSupport( <streakPlayer>, <joinText>, <needSupportVO>, <buddyJoinedVO> )"
|
||||
"Summary: Shows a hold-x-to-support prompt on all friendly players in team based modes. Returns the id of the prompt so it can be stopped."
|
||||
"Module: Utility"
|
||||
"CallOn: Level"
|
||||
"MandatoryArg: <streakTeam>: Team that owns the scorestreak, should match streakPlayer's team if defined."
|
||||
"MandatoryArg: <joinText>: Text to display for the prompt to the allies."
|
||||
"OptionalArg: <splashRef>: Reference string of the splash to show."
|
||||
"OptionalArg: <needSupportVO>: VO reference to play on characters telling them coop support is needed."
|
||||
"OptionalArg: <buddyJoinedVO>: VO reference to play on streak player telling him a buddy has joined."
|
||||
"OptionalArg: <streakPlayer>: Player that owns the scorestreak."
|
||||
"OptionalArg: <joinedVO>: VO reference to play on player telling him he has joined the streak."
|
||||
"Example: promptID = promptForStreakSupport( player, &"MP_OSP_JOIN", "orbitalsupport_splash", "osp_assist", "osp_buddy_joined", player, "osp_joined" );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
promptForStreakSupport( streakTeam, joinText, splashRef, needSupportVO, buddyJoinedVO, streakPlayer, joinedVO )
|
||||
{
|
||||
if ( !level.teamBased ) // only do coop prompts in team based modes
|
||||
return;
|
||||
|
||||
AssertEx( !IsDefined( streakPlayer ) || streakPlayer.team == streakTeam, "promptForStreakSupport: streakPlayer's team needs to match streakTeam." );
|
||||
|
||||
streakPromptOrigin = ( 0, 0, 0 );
|
||||
if ( IsDefined( streakPlayer ) )
|
||||
streakPromptOrigin = streakPlayer.origin;
|
||||
|
||||
streakPrompt = Spawn( "script_model", streakPromptOrigin );
|
||||
streakPrompt Hide();
|
||||
streakPrompt.team = streakTeam;
|
||||
streakPrompt.needSupportVO = needSupportVO;
|
||||
streakPrompt.buddyJoinedVO = buddyJoinedVO;
|
||||
streakPrompt.streakPlayer = streakPlayer;
|
||||
streakPrompt.joinedVO = joinedVO;
|
||||
streakPrompt.joinText = joinText;
|
||||
streakPrompt.splashRef = splashRef;
|
||||
streakPrompt.active = false;
|
||||
streakPrompt.promptID = getUniqueStreakPromptID();
|
||||
if ( IsDefined( streakPlayer ) )
|
||||
streakPrompt DisablePlayerUse( streakPlayer );
|
||||
|
||||
addStreakSupportPrompt( streakPrompt );
|
||||
|
||||
return streakPrompt.promptID;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: stopPromptForStreakSupport( <promptID> )"
|
||||
"Summary: Hides/removes the hold-x-to-support prompt for the prompt ID."
|
||||
"Module: Utility"
|
||||
"CallOn: Level"
|
||||
"MandatoryArg: <promptID>: Id assigned to coop prompt when started."
|
||||
"Example: stopPromptForStreakSupport( id );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
stopPromptForStreakSupport( promptID )
|
||||
{
|
||||
if ( !level.teamBased ) // only do coop prompts in team based modes
|
||||
return;
|
||||
|
||||
foreach ( streakPrompt in level.streakSupportQueueAllies )
|
||||
{
|
||||
if ( streakPrompt.promptID == promptID )
|
||||
{
|
||||
thread removeStreakSupportPrompt( streakPrompt );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( streakPrompt in level.streakSupportQueueAxis )
|
||||
{
|
||||
if ( streakPrompt.promptID == promptID )
|
||||
{
|
||||
thread removeStreakSupportPrompt( streakPrompt );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: waittillBuddyJoinedStreak( <promptID> )"
|
||||
"Summary: Will wait until someone activates the prompt and return the player."
|
||||
"Module: Utility"
|
||||
"CallOn: Level"
|
||||
"MandatoryArg: <promptID>: Id assigned to coop prompt when started."
|
||||
"Example: buddy = waittillBuddyJoinedStreak( id );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
waittillBuddyJoinedStreak( promptId )
|
||||
{
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "buddyJoinedStreak", buddy, id );
|
||||
|
||||
if ( id == promptID )
|
||||
return buddy;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: playerSetupCoopStreak()"
|
||||
"Summary: Call this during the blackout to setup the players weapon so the hud hides."
|
||||
"Module: Utility"
|
||||
"CallOn: Player"
|
||||
"Example: buddy playerSetupCoopStreak();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
playerSetupCoopStreak( delay )
|
||||
{
|
||||
self playerSetupCoopStreakInternal( delay );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: playerResetAfterCoopStreak()"
|
||||
"Summary: Will take away the laptop and reset the player."
|
||||
"Module: Utility"
|
||||
"CallOn: Player"
|
||||
"Example: buddy playerResetAfterCoopStreak();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
playerResetAfterCoopStreak()
|
||||
{
|
||||
self playerResetAfterCoopStreakInternal();
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: playerStopPromptForStreakSupport()"
|
||||
"Summary: Hides all hold-x-to-support prompts for the player."
|
||||
"Module: Utility"
|
||||
"CallOn: Level"
|
||||
"Example: player playerStopPromptForStreakSupport();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
playerStopPromptForStreakSupport()
|
||||
{
|
||||
if ( !level.teamBased ) // only do coop prompts in team based modes
|
||||
return;
|
||||
|
||||
if( !IsDefined(level.streakSupporDisabledCount[self.guid]) )
|
||||
level.streakSupporDisabledCount[self.guid] = 0;
|
||||
|
||||
level.streakSupporDisabledCount[self.guid]++;
|
||||
|
||||
if( level.streakSupporDisabledCount[self.guid] > 1 )
|
||||
return;
|
||||
|
||||
if ( self.team == "allies" )
|
||||
{
|
||||
foreach ( streakPrompt in level.streakSupportQueueAllies )
|
||||
{
|
||||
streakPrompt DisablePlayerUse( self );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ( streakPrompt in level.streakSupportQueueAxis )
|
||||
{
|
||||
streakPrompt DisablePlayerUse( self );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: playerStartPromptForStreakSupport()"
|
||||
"Summary: Shows all hold-x-to-support prompts for the player."
|
||||
"Module: Utility"
|
||||
"CallOn: Level"
|
||||
"Example: player playerStartPromptForStreakSupport();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
playerStartPromptForStreakSupport()
|
||||
{
|
||||
if ( !level.teamBased ) // only do coop prompts in team based modes
|
||||
return;
|
||||
|
||||
level.streakSupporDisabledCount[self.guid]--;
|
||||
|
||||
assert( level.streakSupporDisabledCount[self.guid] >= 0 );
|
||||
|
||||
if( level.streakSupporDisabledCount[self.guid] > 0 )
|
||||
return;
|
||||
|
||||
if ( self.team == "allies" )
|
||||
{
|
||||
foreach ( streakPrompt in level.streakSupportQueueAllies )
|
||||
{
|
||||
if ( self != streakPrompt.streakPlayer )
|
||||
streakPrompt EnablePlayerUse( self );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ( streakPrompt in level.streakSupportQueueAxis )
|
||||
{
|
||||
if ( self != streakPrompt.streakPlayer )
|
||||
streakPrompt EnablePlayerUse( self );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
// Queue management
|
||||
|
||||
addStreakSupportPrompt( streakPrompt )
|
||||
{
|
||||
if ( streakPrompt.team == "allies" )
|
||||
{
|
||||
level.streakSupportQueueAllies[ level.streakSupportQueueAllies.size ] = streakPrompt;
|
||||
|
||||
if ( level.streakSupportQueueAllies.size == 1 )
|
||||
level thread startStreakSupportPrompt( streakPrompt );
|
||||
}
|
||||
else // team == "axis"
|
||||
{
|
||||
level.streakSupportQueueAxis[ level.streakSupportQueueAxis.size ] = streakPrompt;
|
||||
|
||||
if ( level.streakSupportQueueAxis.size == 1 )
|
||||
level thread startStreakSupportPrompt( streakPrompt );
|
||||
}
|
||||
}
|
||||
|
||||
removeStreakSupportPrompt( streakPrompt )
|
||||
{
|
||||
wasActive = streakPrompt.active;
|
||||
streakPrompt.active = false;
|
||||
streakPrompt notify( "streakPromptStopped" );
|
||||
|
||||
if ( streakPrompt.team == "allies" )
|
||||
{
|
||||
level.streakSupportQueueAllies = array_remove( level.streakSupportQueueAllies, streakPrompt );
|
||||
|
||||
if ( wasActive && level.streakSupportQueueAllies.size > 0 )
|
||||
level thread startStreakSupportPrompt( level.streakSupportQueueAllies[0] );
|
||||
}
|
||||
else // team == "axis"
|
||||
{
|
||||
level.streakSupportQueueAxis = array_remove( level.streakSupportQueueAxis, streakPrompt );
|
||||
|
||||
if ( wasActive && level.streakSupportQueueAxis.size > 0 )
|
||||
level thread startStreakSupportPrompt( level.streakSupportQueueAxis[0] );
|
||||
}
|
||||
thread delayDeletePrompt( streakPrompt );
|
||||
}
|
||||
|
||||
delayDeletePrompt( streakPrompt )
|
||||
{
|
||||
wait 1;
|
||||
streakPrompt Delete();
|
||||
}
|
||||
|
||||
getUniqueStreakPromptID( team )
|
||||
{
|
||||
maxID = 0;
|
||||
|
||||
foreach ( streakPrompt in level.streakSupportQueueAllies )
|
||||
{
|
||||
if ( streakPrompt.promptID >= maxID )
|
||||
maxID = streakPrompt.promptID + 1;
|
||||
}
|
||||
|
||||
foreach ( streakPrompt in level.streakSupportQueueAxis )
|
||||
{
|
||||
if ( streakPrompt.promptID >= maxID )
|
||||
maxID = streakPrompt.promptID + 1;
|
||||
}
|
||||
|
||||
return maxID;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// Prompt logic
|
||||
|
||||
startStreakSupportPrompt( streakPrompt )
|
||||
{
|
||||
streakPrompt.active = true;
|
||||
|
||||
level thread handlePrompt( streakPrompt );
|
||||
level thread onConnectPrompt( streakPrompt );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( IsDefined( streakPrompt.streakPlayer ) && player == streakPrompt.streakPlayer )
|
||||
continue;
|
||||
|
||||
if ( isReallyAlive( player ) && player.team == streakPrompt.team )
|
||||
player thread playerSetupStreakPrompt( streakPrompt );
|
||||
|
||||
player thread playerOnSpawnPrompt( streakPrompt );
|
||||
}
|
||||
}
|
||||
|
||||
onConnectPrompt( streakPrompt ) // self == level
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
streakPrompt endon( "streakPromptStopped" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
player thread playerOnSpawnPrompt( streakPrompt );
|
||||
}
|
||||
}
|
||||
|
||||
playerOnSpawnPrompt( streakPrompt ) // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
streakPrompt endon( "streakPromptStopped" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
if ( self.team == streakPrompt.team )
|
||||
self thread playerSetupStreakPrompt( streakPrompt );
|
||||
}
|
||||
}
|
||||
|
||||
playerSetupStreakPrompt( streakPrompt ) // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
streakPrompt endon( "streakPromptStopped" );
|
||||
|
||||
while ( self isUsingRemote() || self isInRemoteTransition() )
|
||||
waitframe();
|
||||
|
||||
playerDisabledWait( streakPrompt );
|
||||
|
||||
self thread playerDisplayJoinRequest( streakPrompt );
|
||||
self thread playerTakeStreakSupportInput( streakPrompt );
|
||||
}
|
||||
|
||||
playerDisabledWait( streakPrompt )
|
||||
{
|
||||
if( !IsDefined(level.streakSupporDisabledCount[self.guid]) )
|
||||
return;
|
||||
|
||||
if( level.streakSupporDisabledCount[self.guid] > 0 )
|
||||
{
|
||||
streakPrompt DisablePlayerUse( self );
|
||||
while( level.streakSupporDisabledCount[self.guid] > 0 )
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
playerDisplayJoinRequest( streakPrompt ) //self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
streakPrompt endon( "streakPromptStopped" );
|
||||
|
||||
if ( IsDefined( streakPrompt.splashRef ) )
|
||||
self thread maps\mp\gametypes\_hud_message::coopKillstreakSplashNotify( streakPrompt.splashRef, streakPrompt.needSupportVO );
|
||||
}
|
||||
|
||||
waittillPlayerCanBeBuddy( player, streakPrompt )
|
||||
{
|
||||
if ( isInRemoteTransition() )
|
||||
player maps\mp\killstreaks\_killstreaks::playerWaittillRideKillstreakComplete();
|
||||
|
||||
waitframe();
|
||||
|
||||
if ( isUsingRemote() ) // cannot join if in a killstreak
|
||||
player waittill( "stopped_using_remote" );
|
||||
}
|
||||
|
||||
waittillPromptActivated( streakPrompt )
|
||||
{
|
||||
streakPrompt endon( "streakPromptStopped" );
|
||||
|
||||
streakPrompt waittill( "trigger" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
playerTakeStreakSupportInput( streakPrompt ) // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
waittillPlayerCanBeBuddy( self );
|
||||
|
||||
result = waittillPromptActivated( streakPrompt );
|
||||
if ( !IsDefined( result ) )
|
||||
return;
|
||||
|
||||
if ( !streakPrompt.active )
|
||||
return;
|
||||
|
||||
if ( IsDefined( self PlayerGetUseEnt() ) && self PlayerGetUseEnt() == streakPrompt && self UseButtonPressed() && self IsOnGround() )
|
||||
{
|
||||
useTime = self PlayerGetUseTime();
|
||||
result = self playerHandleJoining( streakPrompt, useTime );
|
||||
if ( result || !streakPrompt.active )
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playerGetUseTime()
|
||||
{
|
||||
if ( GetDvarInt( "scr_coop_util_delay", 1 ) == 0 )
|
||||
{
|
||||
return CONST_COOP_USE_BAR_TIME_MIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
// give players with less score a shorter use time
|
||||
minScore = self.score;
|
||||
maxScore = self.score;
|
||||
|
||||
for ( i = 1; i < level.players.size; i++ )
|
||||
{
|
||||
player = level.players[i];
|
||||
|
||||
if ( player.team != self.team )
|
||||
continue;
|
||||
|
||||
if ( player.score > maxScore )
|
||||
maxScore = player.score;
|
||||
else if ( player.score < minScore )
|
||||
minScore = player.score;
|
||||
}
|
||||
|
||||
scoreSpread = maxScore - minScore;
|
||||
if ( scoreSpread == 0 )
|
||||
return CONST_COOP_USE_BAR_TIME_MIN;
|
||||
|
||||
playerScorePercent = ( self.score - minScore ) / scoreSpread;
|
||||
|
||||
useTimeSpread = CONST_COOP_USE_BAR_TIME_MAX - CONST_COOP_USE_BAR_TIME_MIN;
|
||||
useTime = CONST_COOP_USE_BAR_TIME_MIN + ( playerScorePercent * useTimeSpread );
|
||||
|
||||
return useTime;
|
||||
}
|
||||
}
|
||||
|
||||
handlePrompt( streakPrompt )
|
||||
{
|
||||
streakPrompt makeGloballyUsableByType( "coopStreakPrompt", streakPrompt.joinText, undefined, streakPrompt.team );
|
||||
|
||||
streakPrompt waittill( "streakPromptStopped" );
|
||||
|
||||
streakPrompt makeGloballyUnusableByType();
|
||||
}
|
||||
|
||||
playerHandleJoining( streakPrompt, useTime ) // self == player
|
||||
{
|
||||
useTimeMS = useTime * 1000;
|
||||
if ( streakPrompt useHoldThink( self, useTimeMS, streakPrompt ) )
|
||||
{
|
||||
level notify( "buddyJoinedStreak", self, streakPrompt.promptID );
|
||||
|
||||
self thread maps\mp\_events::killStreakJoinEvent();
|
||||
|
||||
if ( IsDefined( streakPrompt.streakPlayer ) && IsAlive( streakPrompt.streakPlayer ) )
|
||||
{
|
||||
if ( IsDefined( streakPrompt.joinedVO ) )
|
||||
self thread leaderDialogOnPlayer( streakPrompt.joinedVO );
|
||||
|
||||
if ( IsDefined( streakPrompt.buddyJoinedVO ) )
|
||||
streakPrompt.streakPlayer thread leaderDialogOnPlayer( streakPrompt.buddyJoinedVO );
|
||||
|
||||
if( isDefined( streakPrompt.streakPlayer.currentKillStreakIndex ) )
|
||||
{
|
||||
setMatchData( "killstreaks", streakPrompt.streakPlayer.currentKillStreakIndex, "coopPlayerIndex", self.clientId );
|
||||
}
|
||||
}
|
||||
|
||||
streakPrompt notify( "streakPromptStopped" );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
useHoldThink( player, useTimeMS, streakPrompt )
|
||||
{
|
||||
Assert( IsPlayer( player ) );
|
||||
|
||||
player PlayerLinkTo( streakPrompt );
|
||||
player PlayerLinkedOffsetEnable();
|
||||
player.manuallyJoiningKillStreak = true;
|
||||
|
||||
self thread useHoldThinkCleanupOnPlayerDeath( player );
|
||||
|
||||
self.curProgress = 0;
|
||||
self.inUse = true;
|
||||
self.useRate = 0;
|
||||
self.useTime = useTimeMS;
|
||||
|
||||
if ( IsDefined( player.inWater ) )
|
||||
{
|
||||
player AllowCrouch( false );
|
||||
player AllowProne( false );
|
||||
}
|
||||
|
||||
player _giveWeapon( "killstreak_remote_turret_mp" );
|
||||
player SwitchToWeapon( "killstreak_remote_turret_mp" );
|
||||
player DisableWeaponSwitch();
|
||||
|
||||
player thread personalUseBar( self, streakPrompt );
|
||||
|
||||
result = useHoldThinkLoop( player, streakPrompt );
|
||||
|
||||
if ( !IsDefined( result ) )
|
||||
result = false;
|
||||
|
||||
if ( isAlive( player ) && !result )
|
||||
player playerResetAfterCoopStreakInternal();
|
||||
|
||||
self.inUse = false;
|
||||
self.curProgress = 0;
|
||||
if ( IsDefined( player ) )
|
||||
{
|
||||
player.manuallyJoiningKillStreak = false;
|
||||
player SetClientOmnvar( "ui_use_bar_text", 0 );
|
||||
player SetClientOmnvar( "ui_use_bar_end_time", 0 );
|
||||
player SetClientOmnvar( "ui_use_bar_start_time", 0 );
|
||||
}
|
||||
|
||||
self notify( "coopUtilUseHoldThinkComplete" );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
useHoldThinkCleanupOnPlayerDeath( player )
|
||||
{
|
||||
self endon( "coopUtilUseHoldThinkComplete" );
|
||||
|
||||
player waittill_any( "death", "disconnect" );
|
||||
|
||||
if ( IsDefined( player ) )
|
||||
{
|
||||
player playerResetAfterCoopStreakInternal();
|
||||
player.manuallyJoiningKillStreak = false;
|
||||
player SetClientOmnvar( "ui_use_bar_text", 0 );
|
||||
player SetClientOmnvar( "ui_use_bar_end_time", 0 );
|
||||
player SetClientOmnvar( "ui_use_bar_start_time", 0 );
|
||||
}
|
||||
}
|
||||
|
||||
playerResetAfterCoopStreakInternal()
|
||||
{
|
||||
self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( "killstreak_predator_missile_mp" );
|
||||
self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( "killstreak_remote_turret_mp" );
|
||||
self AllowCrouch( true );
|
||||
self AllowProne( true );
|
||||
self EnableWeaponSwitch();
|
||||
self SwitchToWeapon( self getLastWeapon() );
|
||||
self thread playerDelayControl();
|
||||
self Unlink();
|
||||
}
|
||||
|
||||
playerSetupCoopStreakInternal( delay )
|
||||
{
|
||||
if ( IsDefined( delay ) )
|
||||
wait delay;
|
||||
|
||||
self EnableWeaponSwitch();
|
||||
self _giveWeapon( "killstreak_predator_missile_mp" );
|
||||
self SwitchToWeaponImmediate( "killstreak_predator_missile_mp" );
|
||||
self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( "killstreak_remote_turret_mp" );
|
||||
self DisableWeaponSwitch();
|
||||
}
|
||||
|
||||
playerDelayControl() // self == player
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
self freezeControlsWrapper( true );
|
||||
wait( 0.5 );
|
||||
self freezeControlsWrapper( false );
|
||||
}
|
||||
|
||||
personalUseBar( object, streakPrompt )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
streakPrompt endon( "streakPromptStopped" );
|
||||
|
||||
self SetClientOmnvar( "ui_use_bar_text", 2 );
|
||||
self SetClientOmnvar( "ui_use_bar_start_time", int( GetTime() ) );
|
||||
|
||||
lastRate = -1;
|
||||
while ( isReallyAlive( self ) && isDefined( object ) && object.inUse && !level.gameEnded )
|
||||
{
|
||||
if ( lastRate != object.useRate )
|
||||
{
|
||||
if( object.curProgress > object.useTime)
|
||||
object.curProgress = object.useTime;
|
||||
if ( object.useRate > 0 )
|
||||
{
|
||||
now = GetTime();
|
||||
current = object.curProgress / object.useTime;
|
||||
endTime = now + ( 1 - current ) * ( object.useTime / object.useRate );
|
||||
self SetClientOmnvar( "ui_use_bar_end_time", int( endTime ) );
|
||||
}
|
||||
lastRate = object.useRate;
|
||||
}
|
||||
wait ( 0.05 );
|
||||
}
|
||||
|
||||
self SetClientOmnvar( "ui_use_bar_end_time", 0 );
|
||||
|
||||
}
|
||||
|
||||
useHoldThinkLoop( player, streakPrompt )
|
||||
{
|
||||
streakPrompt endon( "streakPromptStopped" );
|
||||
|
||||
while( !level.gameEnded && isDefined( self ) && isReallyAlive( player ) && player useButtonPressed() && self.curProgress < self.useTime )
|
||||
{
|
||||
self.curProgress += (50 * self.useRate);
|
||||
|
||||
if ( isDefined(self.objectiveScaler) )
|
||||
self.useRate = 1 * self.objectiveScaler;
|
||||
else
|
||||
self.useRate = 1;
|
||||
|
||||
if ( self.curProgress >= self.useTime )
|
||||
return ( isReallyAlive( player ) );
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
286
raw/maps/mp/killstreaks/_dog_killstreak.gsc
Normal file
286
raw/maps/mp/killstreaks/_dog_killstreak.gsc
Normal file
@ -0,0 +1,286 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
|
||||
//===========================================
|
||||
// constants
|
||||
//===========================================
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_GAME = 5;
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_PLAYER = 1;
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER = 2;
|
||||
|
||||
//===========================================
|
||||
// init
|
||||
//===========================================
|
||||
init()
|
||||
{
|
||||
PreCacheModel( "animal_dobernan" );
|
||||
|
||||
level.killStreakFuncs["guard_dog"] = ::tryUseDog;
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// setup_callbacks
|
||||
//===========================================
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["dog"] = level.agent_funcs["player"];
|
||||
|
||||
level.agent_funcs["dog"]["spawn"] = ::spawn_dog;
|
||||
level.agent_funcs["dog"]["on_killed"] = ::on_agent_dog_killed;
|
||||
level.agent_funcs["dog"]["on_damaged"] = maps\mp\agents\_agents::on_agent_generic_damaged;
|
||||
level.agent_funcs["dog"]["on_damaged_finished"] = ::on_damaged_finished;
|
||||
level.agent_funcs["dog"]["think"] = maps\mp\agents\dog\_dog_think::main;
|
||||
|
||||
level.killstreakWieldWeapons["agent_mp"] = "agent_mp";
|
||||
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// tryUseDog
|
||||
//===========================================
|
||||
tryUseDog( lifeId, streakName )
|
||||
{
|
||||
return useDog();
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// useDog
|
||||
//===========================================
|
||||
useDog()
|
||||
{
|
||||
// limit the number of active "dog" agents allowed per game
|
||||
if( getNumActiveAgents( "dog" ) >= CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_GAME )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_DOGS" );
|
||||
return false;
|
||||
}
|
||||
// limit the number of active "dog" agents allowed per player
|
||||
if( getNumOwnedActiveAgentsByType( self, "dog" ) >= CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_PLAYER )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_ALREADY_HAVE_DOG" );
|
||||
return false;
|
||||
}
|
||||
// limit the number of active agents allowed per player
|
||||
if( getNumOwnedActiveAgents( self ) >= CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AGENT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: we should probably do a queue system for these, so the player can call it but it'll go into a queue for when an agent dies to open up a spot
|
||||
// limit the number of active agents allowed per player
|
||||
maxagents = GetMaxAgents();
|
||||
if( getNumActiveAgents() >= maxagents )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_UNAVAILABLE" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the player is still alive before the agent trys to spawn on the player
|
||||
if( !isReallyAlive( self ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to spawn the agent on a path node near the player
|
||||
nearestPathNode = self getValidSpawnPathNodeNearPlayer( true );
|
||||
if( !IsDefined(nearestPathNode) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// find an available agent
|
||||
agent = maps\mp\agents\_agent_common::connectNewAgent( "dog" , self.team );
|
||||
if( !IsDefined( agent ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// set the agent to the player's team
|
||||
agent set_agent_team( self.team, self );
|
||||
|
||||
spawnOrigin = nearestPathNode.origin;
|
||||
spawnAngles = VectorToAngles( self.origin - nearestPathNode.origin );
|
||||
|
||||
agent thread [[ agent agentFunc("spawn") ]]( spawnOrigin, spawnAngles, self );
|
||||
|
||||
agent _setNameplateMaterial( "player_name_bg_green_dog", "player_name_bg_red_dog" );
|
||||
|
||||
if ( IsDefined( self.ballDrone ) && self.ballDrone.ballDroneType == "ball_drone_backup" )
|
||||
{
|
||||
self maps\mp\gametypes\_missions::processChallenge( "ch_twiceasdeadly" );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_agent_dog_killed
|
||||
//=======================================================
|
||||
on_agent_dog_killed( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
self.isActive = false;
|
||||
self.hasDied = false;
|
||||
|
||||
eAttacker.lastKillDogTime = GetTime();
|
||||
|
||||
if ( IsDefined( self.animCBs.OnExit[ self.aiState ] ) )
|
||||
self [[ self.animCBs.OnExit[ self.aiState ] ]] ();
|
||||
|
||||
// award XP for killing agents
|
||||
if( isPlayer( eAttacker ) && IsDefined(self.owner) && (eAttacker != self.owner) )
|
||||
{
|
||||
self.owner leaderDialogOnPlayer( "dog_killed" );
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( eAttacker, sWeapon, sMeansOfDeath, iDamage, "destroyed_guard_dog" );
|
||||
|
||||
if ( IsPlayer( eAttacker ) )
|
||||
{
|
||||
eAttacker maps\mp\gametypes\_missions::processChallenge( "ch_notsobestfriend" );
|
||||
|
||||
// assume jumping?
|
||||
if ( !self IsOnGround() )
|
||||
{
|
||||
eAttacker maps\mp\gametypes\_missions::processChallenge( "ch_hoopla" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self SetAnimState( "death" );
|
||||
animEntry = self GetAnimEntry();
|
||||
animLength = GetAnimLength( animEntry );
|
||||
|
||||
deathAnimDuration = int( animLength * 1000 ); // duration in milliseconds
|
||||
|
||||
self.body = self CloneAgent( deathAnimDuration );
|
||||
|
||||
self PlaySound( "anml_doberman_death" );
|
||||
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
|
||||
self notify( "killanimscript" );
|
||||
}
|
||||
|
||||
|
||||
on_damaged_finished( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if( !IsDefined( self.playing_pain_sound ) )
|
||||
self thread play_pain_sound( 2.5 );
|
||||
|
||||
// damage needs to be modified depending on the weapon and hit location because the head is really easy to hit when the dog is running at you
|
||||
// assuming 1.4 modifier for head shots
|
||||
damageModified = iDamage;
|
||||
if( IsDefined( sHitLoc ) && sHitLoc == "head" && level.gametype != "horde" )
|
||||
{
|
||||
damageModified = int( damageModified * 0.6 );
|
||||
if ( iDamage > 0 && damageModified <= 0 )
|
||||
damageModified = 1;
|
||||
}
|
||||
|
||||
if ( self.health - damageModified > 0 )
|
||||
{
|
||||
self maps\mp\agents\dog\_dog_think::OnDamage( eInflictor, eAttacker, damageModified, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
// attack the player that has damaged them
|
||||
if( IsPlayer( eAttacker ) )
|
||||
{
|
||||
// is the dog already attacking?
|
||||
if ( IsDefined( self.attackState ) && self.attackState != "attacking" )
|
||||
{
|
||||
// is the attacker within the dog damaged range?
|
||||
if( DistanceSquared( self.origin, eAttacker.origin ) <= self.dogDamagedRadiusSq )
|
||||
{
|
||||
self.favoriteEnemy = eAttacker;
|
||||
self.forceAttack = true;
|
||||
self thread maps\mp\agents\dog\_dog_think::watchFavoriteEnemyDeath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maps\mp\agents\_agents::agent_damage_finished( eInflictor, eAttacker, damageModified, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
play_pain_sound( delay ) // self == dog
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
self PlaySound( "anml_doberman_pain" );
|
||||
self.playing_pain_sound = true;
|
||||
wait( delay );
|
||||
self.playing_pain_sound = undefined;
|
||||
}
|
||||
|
||||
spawn_dog( optional_spawnOrigin, optional_spawnAngles, optional_owner ) // self == agent
|
||||
{
|
||||
if ( IsHairRunning() )
|
||||
self SetModel( "animal_dobernan" );
|
||||
else
|
||||
self SetModel( "animal_dobernan" );
|
||||
|
||||
self.species = "dog";
|
||||
|
||||
self.OnEnterAnimState = maps\mp\agents\dog\_dog_think::OnEnterAnimState;
|
||||
|
||||
// allow killstreaks to pass in specific spawn locations
|
||||
if( IsDefined(optional_spawnOrigin) && IsDefined(optional_spawnAngles) )
|
||||
{
|
||||
spawnOrigin = optional_spawnOrigin;
|
||||
spawnAngles = optional_spawnAngles;
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnPoint = self [[level.getSpawnPoint]]();
|
||||
spawnOrigin = spawnpoint.origin;
|
||||
spawnAngles = spawnpoint.angles;
|
||||
}
|
||||
self activateAgent();
|
||||
self.spawnTime = GetTime();
|
||||
self.lastSpawnTime = GetTime();
|
||||
|
||||
self maps\mp\agents\dog\_dog_think::init();
|
||||
|
||||
// called from code when an agent is done initializing after AddAgent is called
|
||||
// this should set up any state specific to this agent and game
|
||||
self SpawnAgent( spawnOrigin, spawnAngles, "dog_animclass", 15, 40, optional_owner );
|
||||
level notify( "spawned_agent", self );
|
||||
|
||||
self maps\mp\agents\_agent_common::set_agent_health( 250 );
|
||||
|
||||
// must set the team after SpawnAgent to fix a bug with weapon crosshairs and nametags
|
||||
if( IsDefined(optional_owner) )
|
||||
{
|
||||
self set_agent_team( optional_owner.team, optional_owner );
|
||||
}
|
||||
|
||||
self SetThreatBiasGroup( "Dogs" );
|
||||
|
||||
self TakeAllWeapons();
|
||||
|
||||
// hide the dog, let the whistle happen, then show the dog
|
||||
if( IsDefined(self.owner) )
|
||||
{
|
||||
self Hide();
|
||||
wait( 1.0 ); // not sure what to endon for this
|
||||
|
||||
// The dog could have died during the 1 second wait (for example if he spawned in a kill trigger), so if that happened,
|
||||
// don't start thinking since it will cause SREs due to him missing a self.agent_type
|
||||
if ( !IsAlive(self) )
|
||||
return;
|
||||
|
||||
self Show();
|
||||
wait( 0.1 );
|
||||
}
|
||||
|
||||
self thread [[ self agentFunc("think") ]]();
|
||||
|
||||
wait( 0.1 );
|
||||
if ( IsHairRunning() )
|
||||
PlayFXOnTag( level.furFX, self, "tag_origin" );
|
||||
}
|
1148
raw/maps/mp/killstreaks/_drone_assault.gsc
Normal file
1148
raw/maps/mp/killstreaks/_drone_assault.gsc
Normal file
File diff suppressed because it is too large
Load Diff
181
raw/maps/mp/killstreaks/_drone_carepackage.gsc
Normal file
181
raw/maps/mp/killstreaks/_drone_carepackage.gsc
Normal file
@ -0,0 +1,181 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hostmigration;
|
||||
#include maps\mp\_aerial_pathnodes;
|
||||
|
||||
init()
|
||||
{
|
||||
level.carepackageDrone = SpawnStruct();
|
||||
level.carepackageDrone.health = 999999; //keep it from dying anywhere in code
|
||||
level.carepackageDrone.maxHealth = 200; //this is what we check against for death
|
||||
level.carepackageDrone.fxId_explode = LoadFX( "vfx/explosion/tracking_drone_explosion" );
|
||||
level.carepackageDrone.sound_explode = "veh_tracking_drone_explode";
|
||||
level.carepackageDrone.releaseString = &"KILLSTREAKS_DRONE_CAREPACKAGE_RELEASE";
|
||||
|
||||
level.carepackageDrones = [];
|
||||
}
|
||||
|
||||
setupCarepackageDrone( drone, setupRelease )
|
||||
{
|
||||
// make expendable if it is non-lethal drone
|
||||
drone make_entity_sentient_mp( self.team );
|
||||
drone makevehiclenotcollidewithplayers( true );
|
||||
|
||||
drone addToCarepackageDroneList();
|
||||
drone thread removeFromCarepackageDroneListOnDeath();
|
||||
|
||||
drone.health = level.carepackageDrone.health;
|
||||
drone.maxHealth = level.carepackageDrone.maxHealth;
|
||||
drone.damageTaken = 0; // how much damage has it taken
|
||||
|
||||
drone.speed = 15;
|
||||
drone.followSpeed = 15;
|
||||
drone.owner = self;
|
||||
drone.team = self.team;
|
||||
|
||||
drone Vehicle_SetSpeed( drone.speed, 10, 10 );
|
||||
drone SetYawSpeed( 120, 90 );
|
||||
drone SetNearGoalNotifyDist( 64 );
|
||||
drone SetHoverParams( 4, 5, 5 );
|
||||
|
||||
drone.fx_tag0 = "tag_body";
|
||||
|
||||
if ( setupRelease )
|
||||
{
|
||||
drone.usableEnt = Spawn( "script_model", drone.origin + ( 0, 0, 1 ) );
|
||||
drone.usableEnt SetModel( "tag_origin" );
|
||||
drone.usableEnt.owner = self;
|
||||
drone.usableEnt makeGloballyUsableByType( "killstreakRemote", level.carepackageDrone.releaseString, self );
|
||||
}
|
||||
|
||||
maxPitch = 45;
|
||||
maxRoll = 45;
|
||||
drone SetMaxPitchRoll( maxPitch, maxRoll );
|
||||
|
||||
attract_strength = 10000;
|
||||
attract_range = 150;
|
||||
drone.attractor = Missile_CreateAttractorEnt( drone, attract_strength, attract_range );
|
||||
drone.stunned = false;
|
||||
|
||||
drone thread carepackageDrone_watchDeath();
|
||||
drone thread carepackageDrone_watchOwnerLoss();
|
||||
drone thread carePackageDrone_watchRoundEnd();
|
||||
}
|
||||
|
||||
carepackageDrone_deleteOnActivate() // self == drone
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
owner = self.owner;
|
||||
|
||||
self.usableEnt waittill( "trigger" );
|
||||
|
||||
self carepackageDrone_Delete();
|
||||
}
|
||||
|
||||
carepackageDrone_watchDeath() // self == drone
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "gone" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
if ( !IsDefined( self ) )
|
||||
return;
|
||||
|
||||
carepackageDrone_leave();
|
||||
}
|
||||
|
||||
carepackageDrone_watchOwnerLoss() // self == drone
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
|
||||
self.owner waittill_any( "disconnect", "joined_team", "joined_spectators" );
|
||||
|
||||
self notify( "owner_gone" );
|
||||
// leave
|
||||
self thread carepackageDrone_leave();
|
||||
}
|
||||
|
||||
carePackageDrone_watchRoundEnd() // self == drone
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
self.owner endon( "disconnect" );
|
||||
self endon( "owner_gone" );
|
||||
|
||||
level waittill_any( "round_end_finished", "game_ended" );
|
||||
|
||||
// leave
|
||||
self thread carepackageDrone_leave();
|
||||
}
|
||||
|
||||
carepackageDrone_leave() // self == drone
|
||||
{
|
||||
self endon( "death" );
|
||||
self notify( "leaving" );
|
||||
|
||||
carepackageDrone_explode();
|
||||
}
|
||||
|
||||
carepackageDrone_explode() // self == drone
|
||||
{
|
||||
if ( IsDefined( level.carepackageDrone.fxId_explode ) )
|
||||
{
|
||||
PlayFX( level.carepackageDrone.fxId_explode, self.origin );
|
||||
}
|
||||
|
||||
if ( IsDefined( level.carepackageDrone.sound_explode ) )
|
||||
{
|
||||
self PlaySound( level.carepackageDrone.sound_explode );
|
||||
}
|
||||
|
||||
if ( IsDefined( self.usableEnt ) )
|
||||
{
|
||||
self.usableEnt makeGloballyUnusableByType();
|
||||
self.usableEnt Delete();
|
||||
}
|
||||
|
||||
self notify( "explode" );
|
||||
|
||||
self carepackageDrone_remove();
|
||||
}
|
||||
|
||||
carepackageDrone_Delete() // self == drone
|
||||
{
|
||||
if ( IsDefined( self.usableEnt ) )
|
||||
{
|
||||
self.usableEnt makeGloballyUnusableByType();
|
||||
self.usableEnt Delete();
|
||||
}
|
||||
|
||||
self notify( "explode" );
|
||||
|
||||
self carepackageDrone_remove();
|
||||
}
|
||||
|
||||
carepackageDrone_remove() // self == drone
|
||||
{
|
||||
// decrement the faux vehicle count right before it is deleted this way we know for sure it is gone
|
||||
decrementFauxVehicleCount();
|
||||
|
||||
self Delete();
|
||||
}
|
||||
|
||||
addToCarepackageDroneList()
|
||||
{
|
||||
level.carepackageDrones[ level.carepackageDrones.size ] = self;
|
||||
}
|
||||
|
||||
removeFromCarepackageDroneListOnDeath()
|
||||
{
|
||||
entNum = self GetEntityNumber();
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
level.carepackageDrones = array_remove( level.carepackageDrones, self );
|
||||
}
|
484
raw/maps/mp/killstreaks/_drone_common.gsc
Normal file
484
raw/maps/mp/killstreaks/_drone_common.gsc
Normal file
@ -0,0 +1,484 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
CONST_cloak_duration = 10;
|
||||
CONST_cloak_cooldown_min_duration = 5;
|
||||
|
||||
droneGetSpawnPoint( eyeZOffset ) // self == player
|
||||
{
|
||||
if ( !IsDefined( eyeZOffset ) )
|
||||
eyeZOffset = 50;
|
||||
|
||||
forwardOffset = 75;
|
||||
trace_radius = 23; // Should match vehicle GDT settings
|
||||
trace_height = trace_radius * 2; // Needs to be at least twice the radius (for some reason)
|
||||
|
||||
eye = ( self.origin + ( 0, 0, eyeZOffset ) );
|
||||
angles = self getplayerangles();
|
||||
forward = anglestoforward( flat_angle( angles ) );
|
||||
|
||||
end = eye + ( forward * forwardOffset );
|
||||
|
||||
spawnOrigin = end;
|
||||
spawnAngles = self.angles;
|
||||
|
||||
placementOK = true;
|
||||
|
||||
// get the drone out of water
|
||||
offset = getSpawnInWaterOffset( spawnOrigin + ( 0, 0, -1 * 30 ) );
|
||||
if ( IsDefined( offset ) && offset > 0 )
|
||||
{
|
||||
spawnOrigin += ( 0, 0, offset );
|
||||
eye += ( 0, 0, offset );
|
||||
}
|
||||
else if ( !IsDefined( offset ) )
|
||||
{
|
||||
placementOK = false;
|
||||
}
|
||||
|
||||
// Ensure the player can see the placement position
|
||||
if ( placementOK && !SightTracePassed( eye, end, true, self ) )
|
||||
{
|
||||
placementOK = false;
|
||||
}
|
||||
|
||||
// Small test for vehicle clip & glass
|
||||
if ( placementOK )
|
||||
{
|
||||
trace = BulletTrace( eye, end, true, self, true, false, true, true, true );
|
||||
if ( trace["fraction"] < 1 )
|
||||
placementOK = false;
|
||||
}
|
||||
|
||||
// Ensure the point is not in solid
|
||||
if ( placementOK )
|
||||
{
|
||||
|
||||
start = eye + ( 0, 0, trace_height * -0.5 );
|
||||
end = end + ( 0, 0, trace_height * -0.5 );
|
||||
|
||||
trace = self AIPhysicsTrace( start, end, trace_radius, trace_height, false, true );
|
||||
if ( trace["fraction"] < 1 )
|
||||
placementOK = false;
|
||||
}
|
||||
|
||||
results = SpawnStruct();
|
||||
results.placementOK = placementOK;
|
||||
results.origin = spawnOrigin;
|
||||
results.angles = spawnAngles;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
getSpawnInWaterOffset( spawnOrigin )
|
||||
{
|
||||
triggers = getentarray( "trigger_underwater", "targetname" );
|
||||
if ( triggers.size == 0 )
|
||||
return 0;
|
||||
|
||||
MAX_UNITS_UP = 200;
|
||||
unitsUp = 0;
|
||||
testOrigin = Spawn( "script_origin", spawnOrigin );
|
||||
touchingWater = false;
|
||||
while ( unitsUp < MAX_UNITS_UP )
|
||||
{
|
||||
if ( touchingWaterTriggers( testOrigin, triggers ) )
|
||||
{
|
||||
unitsUp += 10;
|
||||
testOrigin.origin += ( 0, 0, 10 );
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
testOrigin Delete();
|
||||
|
||||
if ( unitsUp >= MAX_UNITS_UP )
|
||||
return undefined;
|
||||
else
|
||||
return unitsUp;
|
||||
}
|
||||
|
||||
touchingWaterTriggers( ent, triggers )
|
||||
{
|
||||
for ( i = 0; i < triggers.size; i++ )
|
||||
{
|
||||
trigger = triggers[i];
|
||||
if ( ent IsTouching( trigger ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
droneAddToGlobalList( entNum )
|
||||
{
|
||||
level.ugvs[entNum] = self;
|
||||
}
|
||||
|
||||
droneRemoveFromGlobalList( entNum )
|
||||
{
|
||||
level.ugvs[entNum] = undefined;
|
||||
}
|
||||
|
||||
// Cloaking
|
||||
droneInitCloakOmnvars() // self == player
|
||||
{
|
||||
// self SetClientOmnvar( "ui_drone_cloak", 0 );
|
||||
// self SetClientOmnvar( "ui_drone_cloaktext", 0 );
|
||||
// self SetClientOmnvar( "ui_drone_cloaktime", 0 );
|
||||
// self SetClientOmnvar( "ui_drone_cloakdur", 0 );
|
||||
}
|
||||
|
||||
droneSetupCloaking( vehicle, startAsCloaked ) // self == player
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
|
||||
droneInitCloakOmnvars();
|
||||
vehicle.cloakState = 0;
|
||||
vehicle.CloakCooldown = 0;
|
||||
self droneCloakingTransition( vehicle, true, true ); // Wait to cloak and then show the model
|
||||
maps\mp\killstreaks\_killstreaks::playerWaittillRideKillstreakComplete();
|
||||
if ( IsDefined( startAsCloaked ) && startAsCloaked )
|
||||
{
|
||||
self thread droneMonitorDamageWhileCloaking( vehicle );
|
||||
self SetClientOmnvar( "ui_drone_cloak", 2 );
|
||||
// Update cloak end time (for HUD meter)
|
||||
cloakMS = CONST_cloak_duration * 1000;
|
||||
cloakEndTime = GetTime() + cloakMS;
|
||||
self SetClientOmnvar( "ui_drone_cloak_time", cloakEndTime );
|
||||
|
||||
vehicle.CloakCooldown = CONST_cloak_cooldown_min_duration;
|
||||
thread CloakCooldown( vehicle );
|
||||
self thread droneCloakWaitForExit( vehicle );
|
||||
}
|
||||
else
|
||||
{
|
||||
vehicle PlaySound( "recon_drn_cloak_deactivate" );
|
||||
self droneCloakingTransition( vehicle, false ); // Decloak immediately
|
||||
}
|
||||
}
|
||||
|
||||
droneIsCloaked( vehicle ) // self == player
|
||||
{
|
||||
return ( vehicle.hasCloak && vehicle.cloakState >= 0 );
|
||||
}
|
||||
|
||||
droneCloakReady( vehicle, startAsCloaked ) // self == player
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
|
||||
if ( IsDefined( startAsCloaked ) && startAsCloaked )
|
||||
{
|
||||
thread droneCloakCooldown( vehicle );
|
||||
self waittill( "CloakCharged" );
|
||||
}
|
||||
|
||||
while( true )
|
||||
{
|
||||
self SetClientOmnvar( "ui_drone_cloak", 1 ); // Cloak Ready
|
||||
|
||||
thread droneCloakActivated( vehicle );
|
||||
thread droneCloakCooldown( vehicle );
|
||||
|
||||
if ( vehicle.CloakCooldown != 0 )
|
||||
{
|
||||
self SetClientOmnvar( "ui_drone_cloak", 3 );
|
||||
wait vehicle.CloakCooldown;
|
||||
}
|
||||
|
||||
if ( vehicle.hasCloak )
|
||||
self SetClientOmnvar( "ui_drone_cloak", 1 );
|
||||
|
||||
vehicle waittill( "Cloak" );
|
||||
|
||||
vehicle notify( "ActivateCloak" );
|
||||
|
||||
vehicle PlaySound( "recon_drn_cloak_activate" );
|
||||
|
||||
//the cloak has been used and has fully recharged.
|
||||
self waittill( "CloakCharged" );
|
||||
}
|
||||
}
|
||||
|
||||
droneCloakActivated( vehicle )
|
||||
{
|
||||
vehicle endon ( "death" );
|
||||
|
||||
vehicle waittill( "ActivateCloak" );
|
||||
|
||||
self thread droneCloakingTransition( vehicle, true );
|
||||
self thread droneMonitorDamageWhileCloaking( vehicle );
|
||||
|
||||
// Update cloak end time (for HUD meter)
|
||||
cloakMS = CONST_cloak_duration * 1000;
|
||||
cloakEndTime = GetTime() + cloakMS;
|
||||
self SetClientOmnvar( "ui_drone_cloak_time", cloakEndTime );
|
||||
|
||||
// Update the hud text (decloak)
|
||||
self SetClientOmnvar( "ui_drone_cloak", 2 );
|
||||
|
||||
vehicle.CloakCooldown = CONST_cloak_cooldown_min_duration;
|
||||
thread CloakCooldown( vehicle );
|
||||
self thread droneCloakWaitForExit( vehicle );
|
||||
}
|
||||
|
||||
droneCloakCooldown( vehicle )
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
|
||||
self waittill( "UnCloak" );
|
||||
|
||||
vehicle PlaySound( "recon_drn_cloak_deactivate" );
|
||||
self thread droneCloakingTransition( vehicle, false );
|
||||
|
||||
// Update the hud text (cloak recharging)
|
||||
self SetClientOmnvar( "ui_drone_cloak", 3 );
|
||||
|
||||
self thread droneCloakDeactivatedDialog( vehicle );
|
||||
}
|
||||
|
||||
CloakCooldown( vehicle )
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
|
||||
self waittill( "UnCloak" );
|
||||
|
||||
while( vehicle.CloakCooldown > 0 )
|
||||
{
|
||||
vehicle.CloakCooldown -= 0.5;
|
||||
wait 0.5;
|
||||
}
|
||||
|
||||
vehicle.CloakCooldown = 0;
|
||||
self notify( "CloakCharged" );
|
||||
}
|
||||
|
||||
droneCloakWaitForExit( vehicle )
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
|
||||
start = GetTime();
|
||||
self waittill_any_timeout_no_endon_death( CONST_cloak_duration, "ForceUncloak", "Cloak" );
|
||||
end = GetTime();
|
||||
cooldownDuration = max( ( end - start ), CONST_cloak_cooldown_min_duration * 1000 );
|
||||
vehicle.CloakCooldown = cooldownDuration / 1000;
|
||||
|
||||
cooldownEnd = GetTime() + cooldownDuration;
|
||||
self SetClientOmnvar( "ui_drone_cloak_cooldown", cooldownEnd );
|
||||
|
||||
self notify( "UnCloak" );
|
||||
}
|
||||
|
||||
droneCloakingTransition( vehicle, enable, init )
|
||||
{
|
||||
vehicle notify( "cloaking_transition" );
|
||||
vehicle endon( "cloaking_transition" );
|
||||
vehicle endon( "death" );
|
||||
|
||||
if ( enable )
|
||||
{
|
||||
if ( vehicle.cloakState == -2 )
|
||||
return;
|
||||
|
||||
vehicle.cloakState = -1;
|
||||
vehicle CloakingEnable();
|
||||
if ( IsDefined( vehicle.mgTurret ) )
|
||||
vehicle.mgTurret CloakingEnable();
|
||||
vehicle Vehicle_SetMinimapVisible( false );
|
||||
if ( !isdefined( init ) || !init )
|
||||
wait 2.2; // should match the code values for the transition + a small time
|
||||
else
|
||||
wait 1.5; // during init, we want to give enough time for the cloaking to start then hide, before we unhide
|
||||
vehicle Show();
|
||||
if ( IsDefined( vehicle.mgTurret ) )
|
||||
vehicle.mgTurret Show();
|
||||
vehicle.cloakState = -2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( vehicle.cloakState == 2 )
|
||||
return;
|
||||
|
||||
vehicle.cloakState = 1;
|
||||
vehicle CloakingDisable();
|
||||
vehicle Vehicle_SetMinimapVisible( true );
|
||||
if ( IsDefined( vehicle.mgTurret ) )
|
||||
vehicle.mgTurret CloakingDisable();
|
||||
wait 2.2; // should match the code values for the transition
|
||||
vehicle.cloakState = 2;
|
||||
}
|
||||
}
|
||||
|
||||
droneCloakDeactivatedDialog( vehicle )
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
self endon( "CloakCharged" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "Cloak" );
|
||||
|
||||
self playlocalsound( "recon_drn_cloak_notready" );
|
||||
|
||||
wait 1;
|
||||
}
|
||||
}
|
||||
|
||||
droneMonitorDamageWhileCloaking( vehicle )
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
self endon( "UnCloak" );
|
||||
|
||||
wait 1;
|
||||
|
||||
vehicle waittill( "damage" );
|
||||
self notify( "ForceUncloak" );
|
||||
}
|
||||
|
||||
updateShootingLocation( vehicle, effect, ignoreTurret )
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
vehicle endon( "stopShootLocationUpdate" );
|
||||
|
||||
vehicle.targetEnt = spawn( "script_model", ( 0, 0, 0 ) );
|
||||
vehicle.targetEnt setModel( "tag_origin" );
|
||||
vehicle.targetEnt.angles = ( -90, 0, 0 );
|
||||
|
||||
if ( IsDefined( vehicle.mgTurret ) && ( !IsDefined( ignoreTurret ) || !ignoreTurret ) )
|
||||
{
|
||||
vehicle.mgTurret SetTargetEntity( vehicle.targetEnt );
|
||||
vehicle.mgTurret TurretSetGroundAimEntity( vehicle.targetEnt );
|
||||
}
|
||||
else
|
||||
{
|
||||
vehicle SetOtherEnt( vehicle.targetEnt );
|
||||
}
|
||||
|
||||
thread _cleanupShootingLocationOnDeath( vehicle, effect );
|
||||
|
||||
if ( IsDefined( effect ) )
|
||||
{
|
||||
PlayFXOnTagForClients( effect, vehicle.targetEnt, "tag_origin", self );
|
||||
vehicle thread showReticleToEnemies( effect );
|
||||
}
|
||||
|
||||
if ( IsDefined( vehicle.hasAIOption ) && vehicle.hasAIOption )
|
||||
return;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
// AI version should manually manage the position of AssaultVeh.TargetEnt
|
||||
start = self GetViewOrigin();
|
||||
angles = self GetPlayerAngles();
|
||||
forward = AnglesToForward( angles );
|
||||
end = start + ( forward * 8000 );
|
||||
traceResult = BulletTrace( start, end, false, vehicle );
|
||||
vehicle.targetEnt.origin = traceResult[ "position" ];
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
showReticleToEnemies( effect )
|
||||
{
|
||||
self endon ("death");
|
||||
self endon ( "end_remote" );
|
||||
|
||||
if ( !level.hardcoreMode )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( self.owner isEnemy( player ) )
|
||||
{
|
||||
waitframe();
|
||||
PlayFXOnTagForClients( effect, self.targetEnt, "tag_origin", player );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_cleanupShootingLocationOnDeath( vehicle, effect )
|
||||
{
|
||||
vehicle waittill_any( "death", "stopShootLocationUpdate" );
|
||||
|
||||
if ( IsDefined( vehicle.targetEnt ) )
|
||||
{
|
||||
targetEnt = vehicle.targetEnt;
|
||||
if ( IsDefined( effect ) )
|
||||
StopFXOnTag( effect, targetEnt, "tag_origin" );
|
||||
waitframe();
|
||||
targetEnt delete();
|
||||
}
|
||||
}
|
||||
|
||||
playerHandleExhaustFx( vehicle, effectRef, tag, playerEndonString ) // self == player
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
if ( IsDefined( playerEndonString ) )
|
||||
self endon( playerEndonString );
|
||||
|
||||
PlayFXOnTag( getfx( effectRef ), vehicle, tag );
|
||||
|
||||
self thread playerDeleteExhaustFxOnVehicleDeath( vehicle, effectRef, tag );
|
||||
|
||||
if ( !vehicle.hasCloak )
|
||||
return;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "Cloak" );
|
||||
StopFXOnTag( getfx( effectRef ), vehicle, tag );
|
||||
waitframe();
|
||||
PlayFXOnTagForClients( getfx( effectRef ), vehicle, tag, self );
|
||||
|
||||
self waittill( "UnCloak" );
|
||||
StopFXOnTag( getfx( effectRef ), vehicle, tag );
|
||||
waitframe();
|
||||
PlayFXOnTag( getfx( effectRef ), vehicle, tag );
|
||||
}
|
||||
}
|
||||
|
||||
playerDeleteExhaustFxOnVehicleDeath( vehicle, effectRef, tag ) // self == player
|
||||
{
|
||||
vehicle waittill( "death" );
|
||||
|
||||
KillFXOnTag( getfx( effectRef ), vehicle, tag );
|
||||
}
|
||||
|
||||
setDroneVisionAndLightSetPerMap( delay, vehicle ) // self == player
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
vehicle endon( "death" );
|
||||
|
||||
wait( delay );
|
||||
|
||||
if ( IsDefined( level.droneVisionSet ) )
|
||||
{
|
||||
self SetClientTriggerVisionSet( level.droneVisionSet, 0 );
|
||||
}
|
||||
|
||||
if ( IsDefined( level.droneLightSet ) )
|
||||
{
|
||||
self LightSetForPlayer( level.droneLightSet );
|
||||
}
|
||||
}
|
||||
|
||||
removeDroneVisionAndLightSetPerMap( delay ) // self == player
|
||||
{
|
||||
self SetClientTriggerVisionSet( "", delay );
|
||||
self LightSetForPlayer( "" );
|
||||
}
|
||||
|
||||
playerWatchForDroneEMP( vehicle )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
vehicle endon( "death" );
|
||||
self endon( "assaultDroneHunterKiller" );
|
||||
|
||||
vehicle waittill( "emp_damage" );
|
||||
|
||||
vehicle notify( "death" );
|
||||
}
|
716
raw/maps/mp/killstreaks/_drone_recon.gsc
Normal file
716
raw/maps/mp/killstreaks/_drone_recon.gsc
Normal file
@ -0,0 +1,716 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\killstreaks\_drone_common;
|
||||
|
||||
//HUD Values
|
||||
CONST_Movement_hudx = 120;
|
||||
CONST_Movement_hudy = 400;
|
||||
|
||||
CONST_Steer_hudx = 420;
|
||||
CONST_Steer_hudy = 375;
|
||||
|
||||
CONST_Controls_hudx = 25;
|
||||
CONST_Controls_hudy = 380;
|
||||
|
||||
PAINT_DRAW_TIME = .5;
|
||||
|
||||
DAMAGE_FADE_TIME = 1.0;
|
||||
|
||||
//UGV Placement Values
|
||||
PLACEMENT_RADIUS = 25.0;
|
||||
PLACEMENT_HEIGHT = 25.0;
|
||||
PLACEMENT_FORWARD_DISTANCE = 100.0;
|
||||
PLACEMENT_UP_DISTANCE = 40.0;
|
||||
PLACEMENT_SWEEP_DISTANCE = 80.0;
|
||||
PLACEMENT_MIN_NORMAL = 0.7;
|
||||
|
||||
/#
|
||||
SCR_CONST_DEBUG_INFINITE = false;
|
||||
#/
|
||||
|
||||
init()
|
||||
{
|
||||
/#
|
||||
SetDvarIfUninitialized( "scr_drone_recon_infinite", 0 );
|
||||
#/
|
||||
|
||||
level._effect[ "emp_grenade" ] = LoadFX( "vfx/explosion/emp_grenade_explosion" );
|
||||
level._effect[ "antenna_light_mp" ] = loadfx( "vfx/lights/light_reconugv_antenna" );
|
||||
level._effect[ "recon_drone_marker_threat" ] = LoadFX( "vfx/ui/vfx_marker_drone_recon" );
|
||||
level._effect[ "recon_drone_marker_emp" ] = LoadFX( "vfx/ui/vfx_marker_drone_recon2" );
|
||||
level._effect[ "recond_drone_exhaust" ] = LoadFX( "vfx/vehicle/vehicle_mp_recon_drone_smoke" );
|
||||
|
||||
level.UgvMarkedArrays = [];
|
||||
|
||||
thread OnPlayerConnect();
|
||||
|
||||
level.killStreakFuncs[ "recon_ugv" ] = ::tryUseReconDrone;
|
||||
|
||||
level.killstreakWieldWeapons["recon_drone_turret_mp"] = "recon_ugv";
|
||||
level.killstreakWieldWeapons["emp_grenade_killstreak_mp"] = "recon_ugv";
|
||||
level.killstreakWieldWeapons["paint_grenade_killstreak_mp"] = "recon_ugv";
|
||||
|
||||
game["dialog"][ "ks_recdrone_destroyed" ] = "ks_recdrone_destroyed";
|
||||
}
|
||||
|
||||
getDroneSpawnPoint() // self == player
|
||||
{
|
||||
results = droneGetSpawnPoint();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
tryUseReconDrone( lifeId, modules )
|
||||
{
|
||||
if( currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount + 1 >= maxVehiclesAllowed() )
|
||||
{
|
||||
//"Too many vehicles already in the area."
|
||||
self IPrintLnBold( &"MP_TOO_MANY_VEHICLES" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn
|
||||
incrementFauxVehicleCount();
|
||||
|
||||
results = getDroneSpawnPoint();
|
||||
if ( !results.placementOK )
|
||||
{
|
||||
//"Drone Placement Invalid"
|
||||
self IPrintLnBold( &"MP_DRONE_PLACEMENT_INVALID" );
|
||||
decrementFauxVehicleCount();
|
||||
return false;
|
||||
}
|
||||
|
||||
result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak( "recon_ugv" );
|
||||
if ( result != "success" )
|
||||
{
|
||||
decrementFauxVehicleCount();
|
||||
return false;
|
||||
}
|
||||
self setUsingRemote( "recon_ugv" );
|
||||
|
||||
droneInstance = self CreateReconUav( lifeId, modules, results.origin, results.angles );
|
||||
|
||||
if( IsDefined( droneInstance ) )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "recon_ugv", self.origin );
|
||||
self thread teamPlayerCardSplash( "used_recon_ugv", self );
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// decrement the faux vehicle count since this failed to spawn
|
||||
decrementFauxVehicleCount();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
OnPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
TempMarkArrayStruct = SpawnStruct();
|
||||
TempMarkArrayStruct.MarkedPlayerArray=[];
|
||||
TempMarkArrayStruct.MarkedTurretArray=[];
|
||||
TempMarkArrayStruct.owner = self;
|
||||
TempMarkArrayStruct.MonitorMarkingThread = false;
|
||||
|
||||
level.UgvMarkedArrays = array_add(level.UgvMarkedArrays, TempMarkArrayStruct);
|
||||
}
|
||||
|
||||
////=================================================================================================================//
|
||||
//// UAV SETUP //
|
||||
////=================================================================================================================//
|
||||
|
||||
CreateReconUav( lifeId, modules, spawnOrigin, spawnAngles )
|
||||
{
|
||||
vehicle = "recon_uav_mp";
|
||||
// model = "model_tracking_drone_mp";
|
||||
// model = "vehicle_atlas_aerial_drone_01_patrol_mp_static_clr_01";
|
||||
model = "vehicle_atlas_aerial_drone_02_patrol_mp_static_75p";
|
||||
ReconUav = spawnHelicopter( self, spawnOrigin, spawnAngles, vehicle, model );
|
||||
if ( !IsDefined( ReconUav ) )
|
||||
return undefined;
|
||||
|
||||
self thread playerCommonReconVehicleSetup( ReconUav, modules, lifeId );
|
||||
|
||||
ReconUav.maxHealth = 250;
|
||||
ReconUav.vehicleType = "drone_recon";
|
||||
ReconUav.VehName = "recon_uav";
|
||||
ReconUav.MarkDistance = 1500;
|
||||
|
||||
if( ReconUav.hasIncreasedTime )
|
||||
{
|
||||
lifeSpan = 30.0 + 15.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
lifeSpan = 30.0;
|
||||
}
|
||||
ReconUav.lifeSpan = lifeSpan;
|
||||
ReconUav.endTime = GetTime() + lifespan * 1000;
|
||||
|
||||
ReconUav thread maps\mp\gametypes\_damage::setEntityDamageCallback( ReconUav.maxHealth, undefined, ::onReconDroneDeath, maps\mp\killstreaks\_aerial_utility::heli_ModifyDamage, true );
|
||||
|
||||
if( ReconUav.hasCloak )
|
||||
self thread droneCloakReady( ReconUav, ReconUav.hasCloak );
|
||||
|
||||
self StartUsingReconVehicle( ReconUav );
|
||||
|
||||
self thread MonitorUavSafeArea( ReconUav );
|
||||
self thread MonitorPlayerDisconnect( ReconUav );
|
||||
self thread MonitorPlayerSwitchTeams( ReconUav );
|
||||
self thread MonitorPlayerGameEnded( ReconUav );
|
||||
self thread ReconHandleTimeoutWarning( ReconUav );
|
||||
self thread ReconHandleTimeout( ReconUav );
|
||||
self thread ReconHandleDeath( ReconUav );
|
||||
self thread ReconHudSetup( ReconUav );
|
||||
self thread playerWatchForDroneEMP( ReconUav );
|
||||
|
||||
data = SpawnStruct();
|
||||
data.validateAccurateTouching = true;
|
||||
data.deathOverrideCallback = ::override_drone_platform_death;
|
||||
ReconUav thread maps\mp\_movers::handle_moving_platforms( data );
|
||||
|
||||
ReconUav.getStingerTargetPosFunc = ::reconuav_stinger_target_pos;
|
||||
|
||||
return ReconUav;
|
||||
}
|
||||
|
||||
reconuav_stinger_target_pos()
|
||||
{
|
||||
return self GetTagOrigin( "tag_origin" );
|
||||
}
|
||||
|
||||
override_drone_platform_death( data )
|
||||
{
|
||||
self notify( "death" );
|
||||
}
|
||||
|
||||
setupPlayerCommands( modules )
|
||||
{
|
||||
if ( IsBot( self ) )
|
||||
return;
|
||||
|
||||
self NotifyOnPlayerCommand( "recon_fire_main", "+attack" );
|
||||
self NotifyOnPlayerCommand( "recon_fire_main", "+attack_akimbo_accessible" );
|
||||
self NotifyOnPlayerCommand( "recon_fire_secondary", "+speed_throw" );
|
||||
self NotifyOnPlayerCommand( "recon_fire_secondary", "+toggleads_throw" );
|
||||
self NotifyOnPlayerCommand( "recon_fire_secondary", "+ads_akimbo_accessible" );
|
||||
|
||||
if ( array_contains( modules, "recon_ugv_cloak" ) )
|
||||
{
|
||||
self NotifyOnPlayerCommand( "Cloak", "+activate" );
|
||||
self NotifyOnPlayerCommand( "Cloak", "+usereload" );
|
||||
}
|
||||
}
|
||||
|
||||
disablePlayercommands( drone )
|
||||
{
|
||||
if ( IsBot( self ) )
|
||||
return;
|
||||
|
||||
self NotifyOnPlayerCommandRemove( "recon_fire_main", "+attack" );
|
||||
self NotifyOnPlayerCommandRemove( "recon_fire_main", "+attack_akimbo_accessible" );
|
||||
self NotifyOnPlayerCommandRemove( "recon_fire_secondary", "+speed_throw" );
|
||||
self NotifyOnPlayerCommandRemove( "recon_fire_secondary", "+toggleads_throw" );
|
||||
self NotifyOnPlayerCommandRemove( "recon_fire_secondary", "+ads_akimbo_accessible" );
|
||||
if ( IsDefined( drone ) && drone.hasCloak )
|
||||
{
|
||||
self NotifyOnPlayerCommandRemove( "Cloak", "+activate" );
|
||||
self NotifyOnPlayerCommandRemove( "Cloak", "+usereload" );
|
||||
}
|
||||
}
|
||||
|
||||
playerCommonReconVehicleSetup( vehicle, modules, lifeId ) // self == player
|
||||
{
|
||||
self endon( "reconStreakComplete" );
|
||||
vehicle endon( "death" );
|
||||
|
||||
self.using_remote_tank = false;
|
||||
|
||||
vehicle.lifeId = lifeId;
|
||||
vehicle.team = self.team;
|
||||
vehicle.owner = self;
|
||||
vehicle.damageTaken = 0;
|
||||
vehicle.destroyed = false;
|
||||
vehicle.empGrenaded = false;
|
||||
vehicle.damageFade = DAMAGE_FADE_TIME;
|
||||
vehicle.markedPlayers = [];
|
||||
|
||||
vehicle.modules = modules;
|
||||
vehicle.hasARHud = array_contains( vehicle.modules, "recon_ugv_ar_hud" );
|
||||
vehicle.hasPaintGrenade = true; // array_contains( vehicle.modules, "recon_ugv_paint_grenade" );
|
||||
vehicle.hasAssistPoints = array_contains( vehicle.modules, "recon_ugv_assist_points" );
|
||||
vehicle.hasStun = array_contains( vehicle.modules, "recon_ugv_stun" );
|
||||
vehicle.hasIncreasedTime = array_contains( vehicle.modules, "recon_ugv_increased_time" );
|
||||
vehicle.hasCloak = array_contains( vehicle.modules, "recon_ugv_cloak" );
|
||||
vehicle.hasEMPGrenade = array_contains( vehicle.modules, "recon_ugv_emp" );
|
||||
|
||||
vehicle Hide(); // Will get shown by the initial decloak
|
||||
vehicle MakeUnusable();
|
||||
vehicle MakeVehicleSolidCapsule( 23, -9, 23 );
|
||||
vehicle SetCanDamage( true );
|
||||
vehicle make_entity_sentient_mp( vehicle.team );
|
||||
|
||||
reconSpawnTurret( vehicle );
|
||||
|
||||
self thread droneSetupCloaking( vehicle, vehicle.hasCloak );
|
||||
|
||||
// Wait for the transition into the streak to finish before setting up the rest
|
||||
wait( 1.6 );
|
||||
|
||||
self setupPlayerCommands( modules );
|
||||
|
||||
self thread notify_recon_drone_on_player_command( vehicle );
|
||||
|
||||
// Spawn the target ent
|
||||
groundMarkerEffectRef = "recon_drone_marker_threat";
|
||||
if ( vehicle.hasEMPGrenade )
|
||||
groundMarkerEffectRef = "recon_drone_marker_emp";
|
||||
self thread updateShootingLocation( vehicle, getfx( groundMarkerEffectRef ), true );
|
||||
self thread playerHandleExhaustFx( vehicle, "recond_drone_exhaust", "tag_exhaust" );
|
||||
|
||||
vehicle.mgTurret SetTargetEntity( vehicle.targetEnt );
|
||||
|
||||
self thread ReconPlayerExit( vehicle );
|
||||
|
||||
}
|
||||
|
||||
reconSpawnTurret( vehicle )
|
||||
{
|
||||
// Spawn the turret
|
||||
turretWeapon = "recon_drone_turret_mp";
|
||||
turretLinkTag = "tag_turret";
|
||||
turretModelName = "vehicle_atlas_aerial_drone_02_patrol_mp_turret_75p";
|
||||
|
||||
mgTurret = SpawnTurret( "misc_turret", vehicle GetTagOrigin( turretLinkTag ), turretWeapon, false );
|
||||
mgTurret.angles = vehicle GetTagAngles( turretLinkTag );
|
||||
mgTurret SetModel( turretModelName );
|
||||
mgTurret SetDefaultDropPitch( 45.0 );
|
||||
mgTurret LinkTo( vehicle, turretLinkTag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
|
||||
mgTurret.owner = vehicle.owner;
|
||||
mgTurret.health = 99999;
|
||||
mgTurret.maxHealth = 1000;
|
||||
mgTurret.damageTaken = 0;
|
||||
mgTurret.stunned = false;
|
||||
mgTurret.stunnedTime = 0.0;
|
||||
mgTurret SetCanDamage( false );
|
||||
mgTurret SetCanRadiusDamage( false );
|
||||
mgTurret MakeUnusable();
|
||||
mgTurret.team = vehicle.team;
|
||||
mgTurret.pers["team"] = vehicle.team;
|
||||
if ( level.teamBased )
|
||||
mgTurret SetTurretTeam( vehicle.team );
|
||||
mgTurret SetMode( "sentry_manual" );
|
||||
mgTurret SetSentryOwner( vehicle.owner );
|
||||
mgTurret SetTurretMinimapVisible( false );
|
||||
mgTurret.chopper = vehicle;
|
||||
mgTurret SetContents( 0 );
|
||||
|
||||
mgTurret.fireSoundEnt = Spawn( "script_model", vehicle GetTagOrigin( turretLinkTag ) );
|
||||
mgTurret.fireSoundEnt SetModel( "tag_origin" );
|
||||
mgTurret.fireSoundEnt LinkToSynchronizedParent( vehicle, turretLinkTag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
|
||||
mgTurret.fireSoundEnt SetContents( 0 );
|
||||
|
||||
mgTurret Hide();
|
||||
|
||||
vehicle.mgTurret = mgTurret;
|
||||
|
||||
if ( vehicle.hasPaintGrenade )
|
||||
thread fireThreatGrenades( vehicle );
|
||||
if ( vehicle.hasEMPGrenade )
|
||||
thread fireEmpGrenades( vehicle );
|
||||
}
|
||||
|
||||
fireThreatGrenades( vehicle )
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
nextFlashTime = GetTime();
|
||||
shouldFlash = false;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "recon_fire_main" );
|
||||
|
||||
self notify( "ForceUncloak" );
|
||||
|
||||
startPos = vehicle.mgTurret GetTagOrigin( "tag_aim" );
|
||||
targetPos = vehicle.targetEnt.origin;
|
||||
|
||||
if ( vehicle.hasStun && GetTime() >= nextFlashTime )
|
||||
{
|
||||
nextFlashTime = GetTime() + 6000;
|
||||
shouldFlash = true;
|
||||
}
|
||||
|
||||
self maps\mp\killstreaks\_aerial_utility::playerFakeShootPaintGrenadeAtTarget( vehicle.mgTurret.fireSoundEnt, startPos, targetPos, shouldFlash, vehicle );
|
||||
|
||||
self SetClientOmnvar( "ui_recondrone_paint", 2 ); // Reloading
|
||||
|
||||
wait 2;
|
||||
|
||||
self SetClientOmnvar( "ui_recondrone_paint", 1 ); // Available
|
||||
|
||||
shouldFlash = false;
|
||||
}
|
||||
}
|
||||
|
||||
fireEMPGrenades( vehicle )
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "recon_fire_secondary" );
|
||||
|
||||
self notify( "ForceUncloak" );
|
||||
|
||||
startPos = vehicle.mgTurret GetTagOrigin( "tag_aim" );
|
||||
targetPos = vehicle.targetEnt.origin;
|
||||
|
||||
self maps\mp\killstreaks\_aerial_utility::playerFakeShootEmpGrenadeAtTarget( vehicle.mgTurret.fireSoundEnt, startPos, targetPos );
|
||||
|
||||
self SetClientOmnvar( "ui_recondrone_emp", 2 ); // Reloading
|
||||
|
||||
wait 5;
|
||||
|
||||
self SetClientOmnvar( "ui_recondrone_emp", 1 ); // Available
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
notify_recon_drone_on_player_command( vehicle ) // self == player
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
vehicle endon( "death" );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
result = self waittill_any_return( "recon_fire_main", "recon_fire_secondary", "Cloak" );
|
||||
if ( isDefined( result ) )
|
||||
{
|
||||
vehicle notify( result );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StartUsingReconVehicle( ReconVeh ) // self == tank
|
||||
{
|
||||
owner = self;
|
||||
|
||||
if ( getDvarInt( "camera_thirdPerson" ) )
|
||||
owner setThirdPersonDOF( false );
|
||||
|
||||
owner playerSaveAngles();
|
||||
|
||||
owner CameraLinkTo(ReconVeh,"tag_origin");
|
||||
owner RemoteControlVehicle( ReconVeh );
|
||||
|
||||
owner thread setDroneVisionAndLightSetPerMap( 1.5, ReconVeh );
|
||||
|
||||
owner.using_remote_tank = true;
|
||||
|
||||
if( owner isJuggernaut() )
|
||||
owner.juggernautOverlay.alpha = 0;
|
||||
}
|
||||
|
||||
////=================================================================================================================//
|
||||
//// UGV & UAV HUD //
|
||||
////=================================================================================================================//
|
||||
|
||||
ReconHudSetup( vehicle )
|
||||
{
|
||||
vehicle endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
// Ensure the omnvars have been reset
|
||||
ReconHudRemove( vehicle );
|
||||
|
||||
wait 0.5;
|
||||
|
||||
// Add the overlay
|
||||
self SetClientOmnvar( "ui_recondrone_toggle", true );
|
||||
|
||||
self maps\mp\killstreaks\_aerial_utility::playerEnableStreakStatic();
|
||||
|
||||
self SetClientOmnvar( "ui_recondrone_countdown", vehicle.endTime );
|
||||
/#
|
||||
if ( SCR_CONST_DEBUG_INFINITE || GetDvarInt( "scr_drone_recon_infinite", 0 ) )
|
||||
self SetClientOmnvar( "ui_recondrone_countdown", 0 );
|
||||
#/
|
||||
|
||||
if ( vehicle.hasCloak )
|
||||
self SetClientOmnvar( "ui_drone_cloak", 2 );
|
||||
|
||||
if ( vehicle.hasPaintGrenade )
|
||||
self SetClientOmnvar( "ui_recondrone_paint", 1 );
|
||||
|
||||
if ( vehicle.hasEMPGrenade )
|
||||
self SetClientOmnvar( "ui_recondrone_emp", 1 );
|
||||
|
||||
if ( vehicle.hasARHud )
|
||||
self ThermalVisionFOFOverlayOn();
|
||||
}
|
||||
|
||||
ReconHudRemove( vehicle )
|
||||
{
|
||||
self SetClientOmnvar( "ui_recondrone_toggle", false );
|
||||
self SetClientOmnvar( "ui_recondrone_countdown", 0 );
|
||||
self SetClientOmnvar( "ui_drone_cloak", 0 );
|
||||
self SetClientOmnvar( "ui_drone_cloak_time", 0 );
|
||||
self SetClientOmnvar( "ui_drone_cloak_cooldown", 0 );
|
||||
self SetClientOmnvar( "ui_recondrone_paint", 0 );
|
||||
self SetClientOmnvar( "ui_recondrone_emp", 0 );
|
||||
self maps\mp\killstreaks\_aerial_utility::playerDisableStreakStatic();
|
||||
}
|
||||
|
||||
////=================================================================================================================//
|
||||
//// DAMAGE AND TIMEOUT MECHANIC //
|
||||
////=================================================================================================================//
|
||||
|
||||
MonitorUavSafeArea( vehicle )
|
||||
{
|
||||
self endon( "reconStreakComplete" );
|
||||
|
||||
self thread maps\mp\killstreaks\_aerial_utility::playerHandleBoundaryStatic( vehicle, "reconStreakComplete" );
|
||||
self thread maps\mp\killstreaks\_aerial_utility::playerHandleKillVehicle( vehicle, "reconStreakComplete" );
|
||||
|
||||
vehicle waittill( "outOfBounds" );
|
||||
|
||||
wait 2;
|
||||
|
||||
vehicle notify( "death" );
|
||||
}
|
||||
|
||||
MonitorPlayerDisconnect( vehicle )
|
||||
{
|
||||
self endon( "StopWaitForDisconnect" );
|
||||
vehicle endon( "death" );
|
||||
|
||||
self waittill( "disconnect" );
|
||||
|
||||
vehicle notify( "death" );
|
||||
}
|
||||
|
||||
MonitorPlayerSwitchTeams( vehicle )
|
||||
{
|
||||
self endon( "reconStreakComplete" );
|
||||
|
||||
self waittill_any( "joined_team", "joined_spectators" );
|
||||
|
||||
vehicle notify( "death" );
|
||||
}
|
||||
|
||||
MonitorPlayerGameEnded( vehicle )
|
||||
{
|
||||
self endon( "reconStreakComplete" );
|
||||
|
||||
level waittill( "game_ended" );
|
||||
|
||||
vehicle notify( "death" );
|
||||
}
|
||||
|
||||
onReconDroneDeath( attacker, weapon, meansOfDeath, damage )
|
||||
{
|
||||
self notify( "death", attacker, meansOfDeath, weapon );
|
||||
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, meansOfDeath, damage, "recon_drone_destroyed", undefined, "callout_destroyed_drone_recon", true );
|
||||
}
|
||||
|
||||
ReconHandleTimeoutWarning( ReconVeh )
|
||||
{
|
||||
ReconVeh endon ( "death" );
|
||||
|
||||
/#
|
||||
if ( SCR_CONST_DEBUG_INFINITE || GetDvarInt( "scr_drone_recon_infinite", 0 ) )
|
||||
return;
|
||||
#/
|
||||
|
||||
timeout_warning_length = 10.0;
|
||||
warn_interval = 1.0;
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( ReconVeh.lifeSpan - timeout_warning_length );
|
||||
|
||||
while( timeout_warning_length > 0 )
|
||||
{
|
||||
/* if( ReconVeh.VehName == "recon_ugv" )
|
||||
{
|
||||
PlayFXOnTagForClients( level._effect[ "antenna_light_mp" ], ReconVeh, "jnt_antenna3", self );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayFXOnTagForClients( level._effect[ "antenna_light_mp" ], ReconVeh, "jnt_rotor_bm", self ); // TODO: different fx for UAV?
|
||||
}*/
|
||||
|
||||
ReconVeh PlaySound( "mp_warbird_outofbounds_warning" );
|
||||
timeout_warning_length -= warn_interval;
|
||||
|
||||
wait( warn_interval );
|
||||
}
|
||||
|
||||
ReconVeh notify( "death" );
|
||||
}
|
||||
|
||||
ReconHandleTimeout( ReconVeh )
|
||||
{
|
||||
ReconVeh endon ( "death" );
|
||||
|
||||
/#
|
||||
if ( SCR_CONST_DEBUG_INFINITE || GetDvarInt( "scr_drone_recon_infinite", 0 ) )
|
||||
return;
|
||||
#/
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( ReconVeh.lifeSpan );
|
||||
|
||||
ReconVeh notify( "death" );
|
||||
}
|
||||
|
||||
ReconHandleDeath( vehicle )
|
||||
{
|
||||
entNum = vehicle GetEntityNumber();
|
||||
|
||||
vehicle droneAddToGlobalList( entNum );
|
||||
|
||||
vehicle waittill ( "death", attacker );
|
||||
|
||||
if ( IsDefined( vehicle ) )
|
||||
vehicle Ghost(); // Still send over the network
|
||||
|
||||
if ( IsDefined( vehicle.mgTurret ) )
|
||||
vehicle.mgTurret Ghost(); // Still send over the network
|
||||
|
||||
if ( IsDefined( self ) )
|
||||
freezeControlsWrapper( true );
|
||||
|
||||
self notify( "reconStreakComplete" );
|
||||
self notify( "StopWaitForDisconnect" );
|
||||
|
||||
//need to have a good way to make the car not lose all momentum when it 'crashes'
|
||||
vehicle PlaySound( "assault_drn_death" );
|
||||
|
||||
vehicle droneRemoveFromGlobalList( entNum );
|
||||
|
||||
waitframe();
|
||||
|
||||
PlayFXOnTag( level._effect[ "remote_tank_explode" ], vehicle, "tag_origin" );
|
||||
|
||||
wait 1;
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
if ( IsDefined( self ) && !level.gameEnded )
|
||||
freezeControlsWrapper( false );
|
||||
|
||||
if ( IsDefined( self ) && IsDefined( attacker ) && self != attacker )
|
||||
self thread leaderDialogOnPlayer( "ks_recdrone_destroyed", undefined, undefined, self.origin );
|
||||
|
||||
// make sure the owner gets out
|
||||
// there's a bug where you can get stuck in a black screen because the player and ReconUgv die at the same time and that ends scripts that would reset the view
|
||||
if ( IsDefined( self ) && ( self.using_remote_tank || self isUsingRemote() ) )
|
||||
{
|
||||
self ReconSetInactivity( vehicle );
|
||||
self.using_remote_tank = false;
|
||||
if( self isJuggernaut() )
|
||||
self.juggernautOverlay.alpha = 1;
|
||||
}
|
||||
|
||||
// decrement the faux vehicle count right before it is deleted this way we know for sure it is gone
|
||||
decrementFauxVehicleCount();
|
||||
|
||||
if ( IsDefined( vehicle.mgTurret ) )
|
||||
{
|
||||
if ( IsDefined( vehicle.mgTurret.fireSoundEnt ) )
|
||||
vehicle.mgTurret.fireSoundEnt Delete();
|
||||
vehicle.mgTurret Delete();
|
||||
}
|
||||
|
||||
if ( IsDefined( vehicle.thing ) )
|
||||
vehicle.thing Delete();
|
||||
|
||||
vehicle delete();
|
||||
}
|
||||
|
||||
ReconSetInactivity( ReconVeh )
|
||||
{
|
||||
if( !IsDefined( ReconVeh ) )
|
||||
return;
|
||||
|
||||
owner = self;
|
||||
|
||||
if( IsDefined( owner.using_remote_tank ) && owner.using_remote_tank )
|
||||
{
|
||||
owner notify( "end_remote" );
|
||||
|
||||
owner RemoteControlVehicleOff( ReconVeh );
|
||||
owner ThermalVisionFOFOverlayOff();
|
||||
self thread removeDroneVisionAndLightSetPerMap( 1.5 );
|
||||
|
||||
owner ReconHudRemove( ReconVeh );
|
||||
|
||||
owner disablePlayercommands( ReconVeh );
|
||||
|
||||
if ( owner isUsingRemote() && !level.gameEnded )
|
||||
owner clearUsingRemote();
|
||||
|
||||
killstreakWeapon = getKillstreakWeapon( "recon_ugv" );
|
||||
owner TakeWeapon( killstreakWeapon );
|
||||
|
||||
owner EnableWeaponSwitch();
|
||||
owner SwitchToWeapon( self getLastWeapon() );
|
||||
owner playerRestoreAngles();
|
||||
|
||||
if ( getDvarInt( "camera_thirdPerson" ) )
|
||||
owner setThirdPersonDOF( true );
|
||||
|
||||
// just in case the tank is killed before you can get in it, the initRideKillstreak() does _disableUsability() and we unfortunately kill the thread on death
|
||||
if( IsDefined( owner.disabledUsability ) && owner.disabledUsability )
|
||||
owner _enableUsability();
|
||||
|
||||
owner.using_remote_tank = false;
|
||||
}
|
||||
}
|
||||
|
||||
ReconPlayerExit( vehicle )
|
||||
{
|
||||
if( !IsDefined( self ) )
|
||||
return;
|
||||
|
||||
owner = self;
|
||||
|
||||
level endon( "game_ended" );
|
||||
owner endon ( "disconnect" );
|
||||
vehicle endon ( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
timeUsed = 0;
|
||||
while( owner UseButtonPressed() )
|
||||
{
|
||||
timeUsed += 0.05;
|
||||
if( timeUsed > 0.75 )
|
||||
{
|
||||
vehicle notify( "death" );
|
||||
return;
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
975
raw/maps/mp/killstreaks/_emp.gsc
Normal file
975
raw/maps/mp/killstreaks/_emp.gsc
Normal file
@ -0,0 +1,975 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
level._effect[ "emp_third_person_sparks" ] = LoadFX( "vfx/explosion/electrical_sparks_small_emp_runner" );
|
||||
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
for( i = 0; i < level.teamNameList.size; i++ )
|
||||
{
|
||||
level.teamEMPed[level.teamNameList[i]] = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
level.teamEMPed["allies"] = false;
|
||||
level.teamEMPed["axis"] = false;
|
||||
}
|
||||
level.empOwner = undefined;
|
||||
level.empPlayer = undefined;
|
||||
level.empStreaksDisabled = false;
|
||||
level.empEquipmentDisabled = false;
|
||||
level.empAssistPoints = false;
|
||||
level.empExoDisabled = false;
|
||||
level.empTimeRemaining = 0;
|
||||
|
||||
level thread EMP_PlayerTracker();
|
||||
|
||||
level.killstreakFuncs["emp"] = ::EMP_Use;
|
||||
|
||||
level thread onPlayerConnect();
|
||||
|
||||
/#
|
||||
SetDevDvarIfUninitialized( "scr_emp_timeout", 60.0 );
|
||||
SetDevDvarIfUninitialized( "scr_emp_damage_debug", 0 );
|
||||
#/
|
||||
}
|
||||
|
||||
// last minute add to change VO based on modules for EMP
|
||||
getModuleLineEMP( modules )
|
||||
{
|
||||
empStreaksDisabled = array_contains( modules, "emp_streak_kill" );
|
||||
empEquipmentDisabled = array_contains( modules, "emp_equipment_kill" );
|
||||
empExoDisabled = array_contains( modules, "emp_exo_kill" );
|
||||
|
||||
// HUD
|
||||
if( !empStreaksDisabled && !empEquipmentDisabled && !empExoDisabled )
|
||||
return "_01";
|
||||
|
||||
// streaks
|
||||
if( empStreaksDisabled && !empEquipmentDisabled && !empExoDisabled )
|
||||
return "_02";
|
||||
|
||||
// equipment
|
||||
if( !empStreaksDisabled && empEquipmentDisabled && !empExoDisabled )
|
||||
return "_03";
|
||||
|
||||
// exo suit
|
||||
if( !empStreaksDisabled && !empEquipmentDisabled && empExoDisabled )
|
||||
return "_04";
|
||||
|
||||
// streaks and equipment
|
||||
if( empStreaksDisabled && empEquipmentDisabled && !empExoDisabled )
|
||||
return "_05";
|
||||
|
||||
// streaks and exo suit
|
||||
if( empStreaksDisabled && !empEquipmentDisabled && empExoDisabled )
|
||||
return "_06";
|
||||
|
||||
// equipment and exo suit
|
||||
if( !empStreaksDisabled && empEquipmentDisabled && empExoDisabled )
|
||||
return "_07";
|
||||
|
||||
// everything
|
||||
return "_08";
|
||||
}
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
for(;;)
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
if ( isSystemHacked() && !self _hasPerk( "specialty_empimmune" ) )
|
||||
{
|
||||
self applyEMP();
|
||||
}
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
if ( self.team == "spectator" || self isSystemHacked() )
|
||||
{
|
||||
self removeEMP();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isSystemHacked()
|
||||
{
|
||||
return ( (level.teamBased && level.teamEMPed[self.team]) || (!level.teamBased && isDefined( level.empPlayer ) && level.empPlayer != self ) );
|
||||
}
|
||||
|
||||
applyEMP( result )
|
||||
{
|
||||
HUDtext = 2;
|
||||
|
||||
// disable exo when emp'd
|
||||
if ( level.empExoDisabled )
|
||||
{
|
||||
HUDtext = 1;
|
||||
|
||||
if ( isAugmentedGameMode() )
|
||||
{
|
||||
self playerAllowHighJump(false, "emp");
|
||||
self playerAllowHighJumpDrop(false, "emp");
|
||||
self playerAllowBoostJump(false, "emp");
|
||||
self playerAllowPowerSlide(false, "emp");
|
||||
self playerAllowDodge(false, "emp");
|
||||
}
|
||||
}
|
||||
|
||||
self.empScrambleId = maps\mp\gametypes\_scrambler::playerSetHUDEmpScrambled( level.empEndTime, HUDtext, "emp" );
|
||||
self DigitalDistortSetMaterial("digital_distort_mp");
|
||||
self DigitalDistortSetParams(1.0, 1.0);
|
||||
|
||||
self.empOn = true;
|
||||
self notify( "applyEMPkillstreak" );
|
||||
self setEMPJammed( true, level.empEquipmentDisabled );
|
||||
|
||||
if( IsDefined(result) && (result == "emp_update") )
|
||||
self PlaySoundToPlayer( "emp_system_hacked", self );
|
||||
|
||||
self thread dynamicDistortion();
|
||||
self thread playerDelayStartSparksEffect();
|
||||
}
|
||||
|
||||
playerDelayStartSparksEffect()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "emp_update" );
|
||||
|
||||
if ( !IsDefined( self.costume ) )
|
||||
self waittill( "player_model_set" );
|
||||
|
||||
if ( !IsDefined( self.empFX ) )
|
||||
{
|
||||
self.empFX = SpawnLinkedFx( getfx( "emp_third_person_sparks" ), self, "j_shoulder_ri" );
|
||||
TriggerFX(self.empFX);
|
||||
SetFXKillOnDelete( self.empFX, true );
|
||||
}
|
||||
}
|
||||
|
||||
dynamicDistortion()
|
||||
{
|
||||
self notify( "dynamicDistortion" );
|
||||
|
||||
self endon ( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "dynamicDistortion" );
|
||||
|
||||
wait( 0.1 );
|
||||
|
||||
fadeTime = 0;
|
||||
maxIntensity = 0.55;
|
||||
minIntensity = 0.2;
|
||||
intensityRange = maxIntensity - minIntensity;
|
||||
increment = 0.2;
|
||||
empDuration = ( ( level.empEndTime - gettime() ) / 1000 ) - 0.2;
|
||||
|
||||
while( fadeTime < empDuration )
|
||||
{
|
||||
if( IsDefined(self.empOn) && !self.empOn )
|
||||
break;
|
||||
|
||||
currentIntensity = (empDuration - fadeTime) / (empDuration );
|
||||
self DigitalDistortSetParams((currentIntensity * intensityRange) + minIntensity, 1.0);
|
||||
|
||||
fadeTime += increment;
|
||||
wait( increment );
|
||||
}
|
||||
|
||||
self DigitalDistortSetParams( 0.0, 0.0 );
|
||||
}
|
||||
|
||||
|
||||
removeEMP( result )
|
||||
{
|
||||
if ( isAugmentedGameMode() )
|
||||
{
|
||||
self playerAllowHighJump(true, "emp");
|
||||
self playerAllowHighJumpDrop(true, "emp");
|
||||
self playerAllowBoostJump(true, "emp");
|
||||
self playerAllowPowerSlide(true, "emp");
|
||||
self playerAllowDodge(true, "emp");
|
||||
}
|
||||
|
||||
if ( IsDefined( self.empScrambleId ) )
|
||||
{
|
||||
maps\mp\gametypes\_scrambler::playerSetHUDEmpScrambledOff( self.empScrambleId );
|
||||
self.empScrambleId = undefined;
|
||||
}
|
||||
else if ( self.team == "spectator" )
|
||||
{
|
||||
self SetClientOmnvar( "ui_exo_reboot_end_time", 0 );
|
||||
self SetClientOmnvar( "ui_exo_reboot_type", 0 );
|
||||
}
|
||||
|
||||
self DigitalDistortSetParams(0.0, 0.0);
|
||||
|
||||
self.empOn = undefined;
|
||||
self notify( "removeEMPkillstreak" );
|
||||
self SetEMPJammed( false );
|
||||
|
||||
if( IsDefined(result) && (result == "emp_update") )
|
||||
self PlaySoundToPlayer( "emp_system_reboot", self );
|
||||
|
||||
if ( IsDefined( self.empFX ) )
|
||||
self.empFX Delete();
|
||||
}
|
||||
|
||||
|
||||
EMP_Use( lifeId, modules )
|
||||
{
|
||||
assert( isDefined( self ) );
|
||||
|
||||
myTeam = self.pers["team"];
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
otherTeam = level.otherTeam[myTeam];
|
||||
self thread EMP_JamTeam( otherTeam, modules );
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread EMP_JamPlayers( self, modules );
|
||||
}
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "emp", self.origin );
|
||||
self maps\mp\gametypes\_missions::processChallenge( "ch_streak_emp", 1 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EMP_GetTimeoutFromModules( modules )
|
||||
{
|
||||
timeoutVal = 20.0;
|
||||
if ( array_contains( modules, "emp_time_1" ) && array_contains( modules, "emp_time_2" ) )
|
||||
timeoutVal = 40.0;
|
||||
else if ( array_contains( modules, "emp_time_1" ) || array_contains( modules, "emp_time_2" ) )
|
||||
timeoutVal = 30.0;
|
||||
if ( isdefined ( level.isHorde ) && level.isHorde )
|
||||
return 60.0;
|
||||
|
||||
return timeoutVal;
|
||||
}
|
||||
|
||||
EMP_Artifacts( time )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
self notify( "EMP_Artifacts" );
|
||||
self endon( "EMP_Artifacts" );
|
||||
|
||||
self SetClientOmnvar( "ui_hud_static", 2 );
|
||||
|
||||
wait time;
|
||||
|
||||
|
||||
self SetClientOmnvar( "ui_hud_static", 0 );
|
||||
}
|
||||
|
||||
//jams all players on the team passed in the argument teamName
|
||||
EMP_JamTeam( teamName, modules )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
assert( teamName == "allies" || teamName == "axis" );
|
||||
|
||||
if ( !isdefined ( level.isHorde ) )
|
||||
thread teamPlayerCardSplash( "used_emp", self );
|
||||
|
||||
level notify ( "EMP_JamTeam" + teamName );
|
||||
level endon ( "EMP_JamTeam" + teamName );
|
||||
|
||||
level.empOwner = self;
|
||||
|
||||
timeoutVal = EMP_GetTimeoutFromModules( modules );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player playLocalSound( "emp_big_activate" );
|
||||
|
||||
if ( player.team != teamName )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_empimmune" ) )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_localjammer" ) )
|
||||
player SetMotionTrackerVisible( true );
|
||||
|
||||
player thread EMP_Artifacts( timeoutVal );
|
||||
}
|
||||
|
||||
VisionSetNaked( "coup_sunblind", 0.1 );
|
||||
|
||||
// Flash players
|
||||
if ( array_contains( modules, "emp_flash" ) )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( player.team != teamName || !isReallyAlive( player ) || IsDefined( player.usingRemote ) )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_empimmune" ) )
|
||||
continue;
|
||||
|
||||
player thread maps\mp\_flashgrenades::applyFlash( 2.5, 0.75 );
|
||||
}
|
||||
}
|
||||
|
||||
wait ( 0.1 );
|
||||
|
||||
// resetting the vision set to the same thing won't normally have an effect.
|
||||
// however, if the client receives the previous visionset change in the same packet as this one,
|
||||
// this will force them to lerp from the bright one to the normal one.
|
||||
VisionSetNaked( "coup_sunblind", 0 );
|
||||
if( IsDefined( level.nukeDetonated ) )
|
||||
VisionSetNaked( level.nukeVisionSet, 3.0 );
|
||||
else
|
||||
VisionSetNaked( "", 3.0 ); // go to default visionset
|
||||
|
||||
level.teamEMPed[teamName] = true;
|
||||
|
||||
level.empStreaksDisabled = array_contains( modules, "emp_streak_kill" );
|
||||
level.empEquipmentDisabled = array_contains( modules, "emp_equipment_kill" );
|
||||
level.empAssistPoints = array_contains( modules, "emp_assist" );
|
||||
level.empExoDisabled = array_contains( modules, "emp_exo_kill" );
|
||||
|
||||
level notify ( "emp_update" );
|
||||
|
||||
level.empEndTime = getTime() + int(timeoutVal * 1000);
|
||||
|
||||
// Kill active vehicles
|
||||
if ( level.empStreaksDisabled )
|
||||
{
|
||||
level destroyActiveStreakVehicles( self, teamName );
|
||||
}
|
||||
|
||||
if ( level.empEquipmentDisabled )
|
||||
{
|
||||
level destroyActiveEquipmentVehicles( self, teamName );
|
||||
}
|
||||
|
||||
level thread keepEMPTimeRemaining( timeoutVal );
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeoutVal );
|
||||
|
||||
level.teamEMPed[teamName] = false;
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team != teamName )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_localjammer" ) )
|
||||
player SetMotionTrackerVisible( false );
|
||||
}
|
||||
|
||||
level.empOwner = undefined;
|
||||
level.empStreaksDisabled = false;
|
||||
level.empEquipmentDisabled = false;
|
||||
level.empAssistPoints = false;
|
||||
level.empExoDisabled = false;
|
||||
|
||||
level notify ( "emp_update" );
|
||||
}
|
||||
|
||||
EMP_JamPlayers( owner, modules )
|
||||
{
|
||||
level notify ( "EMP_JamPlayers" );
|
||||
level endon ( "EMP_JamPlayers" );
|
||||
|
||||
assert( isDefined( owner ) );
|
||||
level.empOwner = owner;
|
||||
|
||||
timeoutVal = EMP_GetTimeoutFromModules( modules );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player playLocalSound( "emp_big_activate" );
|
||||
|
||||
if ( player == owner )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_localjammer" ) )
|
||||
player SetMotionTrackerVisible( true );
|
||||
|
||||
player thread EMP_Artifacts( timeoutVal );
|
||||
}
|
||||
|
||||
VisionSetNaked( "coup_sunblind", 0.1 );
|
||||
|
||||
// Flash players
|
||||
if ( array_contains( modules, "emp_flash" ) )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( player == owner || !isReallyAlive( player ) || IsDefined( player.usingRemote ) )
|
||||
continue;
|
||||
|
||||
player thread maps\mp\_flashgrenades::applyFlash( 2.5, 0.75 );
|
||||
}
|
||||
}
|
||||
|
||||
wait ( 0.1 );
|
||||
|
||||
// resetting the vision set to the same thing won't normally have an effect.
|
||||
// however, if the client receives the previous visionset change in the same packet as this one,
|
||||
// this will force them to lerp from the bright one to the normal one.
|
||||
VisionSetNaked( "coup_sunblind", 0 );
|
||||
if( IsDefined( level.nukeDetonated ) )
|
||||
VisionSetNaked( level.nukeVisionSet, 3.0 );
|
||||
else
|
||||
VisionSetNaked( "", 3.0 ); // go to default visionset
|
||||
|
||||
level notify ( "emp_update" );
|
||||
|
||||
level.empPlayer = owner;
|
||||
level.empPlayer thread empPlayerFFADisconnect();
|
||||
|
||||
level.empStreaksDisabled = array_contains( modules, "emp_streak_kill" );
|
||||
level.empEquipmentDisabled = array_contains( modules, "emp_equipment_kill" );
|
||||
level.empAssistPoints = array_contains( modules, "emp_assist" );
|
||||
level.empExoDisabled = array_contains( modules, "emp_exo_kill" );
|
||||
level.empEndTime = getTime() + int(timeoutVal * 1000);
|
||||
|
||||
// Kill active vehicles
|
||||
if ( level.empStreaksDisabled )
|
||||
{
|
||||
level destroyActiveStreakVehicles( owner );
|
||||
}
|
||||
|
||||
if ( level.empEquipmentDisabled )
|
||||
{
|
||||
level destroyActiveEquipmentVehicles( owner );
|
||||
}
|
||||
|
||||
level notify ( "emp_update" );
|
||||
|
||||
level thread keepEMPTimeRemaining( timeoutVal );
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeoutVal );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player == owner )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_localjammer" ) )
|
||||
player SetMotionTrackerVisible( false );
|
||||
}
|
||||
|
||||
level.empPlayer = undefined;
|
||||
level.empOwner = undefined;
|
||||
level.empStreaksDisabled = false;
|
||||
level.empEquipmentDisabled = false;
|
||||
level.empAssistPoints = false;
|
||||
level.empExoDisabled = false;
|
||||
|
||||
level notify ( "emp_update" );
|
||||
level notify ( "emp_ended" );
|
||||
}
|
||||
|
||||
keepEMPTimeRemaining( timeoutVal )
|
||||
{
|
||||
level notify( "keepEMPTimeRemaining" );
|
||||
level endon( "keepEMPTimeRemaining" );
|
||||
|
||||
level endon( "emp_ended" );
|
||||
|
||||
// we need to know how much time is left for the unavailable string
|
||||
level.empTimeRemaining = int( timeoutVal );
|
||||
while( level.empTimeRemaining )
|
||||
{
|
||||
wait( 1.0 );
|
||||
level.empTimeRemaining--;
|
||||
}
|
||||
}
|
||||
|
||||
empPlayerFFADisconnect()
|
||||
{
|
||||
level endon ( "EMP_JamPlayers" );
|
||||
level endon ( "emp_ended" );
|
||||
|
||||
self waittill( "disconnect" );
|
||||
level notify ( "emp_update" );
|
||||
}
|
||||
|
||||
EMP_PlayerTracker()
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
|
||||
result = level waittill_any_return_no_endon_death( "joined_team", "emp_update", "game_ended" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == "spectator" )
|
||||
{
|
||||
spectatingPlayer = player GetSpectatingPlayer();
|
||||
if ( !IsDefined( spectatingPlayer ) || !spectatingPlayer isSystemHacked() )
|
||||
player removeEMP( result );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( player _hasPerk( "specialty_empimmune" ) )
|
||||
continue;
|
||||
|
||||
if ( isReallyAlive( player ) && player isSystemHacked() && !level.gameEnded )
|
||||
player applyEMP( result );
|
||||
else
|
||||
player removeEMP( result );
|
||||
}
|
||||
|
||||
if ( level.gameEnded )
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveVehicles( attacker, teamEMPed )
|
||||
{
|
||||
thread destroyActiveStreakVehicles( attacker, teamEMPed );
|
||||
thread destroyActiveEquipmentVehicles( attacker, teamEMPed );
|
||||
}
|
||||
|
||||
destroyActiveStreakVehicles( attacker, teamEMPed )
|
||||
{
|
||||
// thread all of the things that need to get destroyed, this way we can put frame waits in between each destruction so we don't hit the server with a lot at one time
|
||||
thread destroyActiveHelis( attacker, teamEMPed );
|
||||
thread destroyActiveLittleBirds( attacker, teamEMPed );
|
||||
thread destroyActiveTurrets( attacker, teamEMPed );
|
||||
thread destroyActiveRockets( attacker, teamEMPed );
|
||||
thread destroyActiveUAVs( attacker, teamEMPed );
|
||||
thread destroyActiveUGVs( attacker, teamEMPed );
|
||||
thread destroyActiveOrbitalLasers( attacker, teamEMPed );
|
||||
thread destroyActiveGoliaths( attacker, teamEMPed );
|
||||
}
|
||||
|
||||
destroyActiveEquipmentVehicles( attacker, teamEMPed )
|
||||
{
|
||||
thread destroyActiveDrones( attacker, teamEMPed );
|
||||
}
|
||||
|
||||
destroyEMPObjectsInRadius( attacker, teamEMPed, empOrigin, empRadius )
|
||||
{
|
||||
// thread all of the things that need to get destroyed, this way we can put frame waits in between each destruction so we don't hit the server with a lot at one time
|
||||
thread destroyActiveHelis( attacker, teamEMPed, empOrigin, empRadius );
|
||||
thread destroyActiveLittleBirds( attacker, teamEMPed, empOrigin, empRadius );
|
||||
thread destroyActiveTurrets( attacker, teamEMPed, empOrigin, empRadius );
|
||||
thread destroyActiveRockets( attacker, teamEMPed, empOrigin, empRadius );
|
||||
thread destroyActiveUAVs( attacker, teamEMPed, empOrigin, empRadius );
|
||||
thread destroyActiveUGVs( attacker, teamEMPed, empOrigin, empRadius );
|
||||
thread destroyActiveOrbitalLasers( attacker, teamEMPed, empOrigin, empRadius );
|
||||
thread destroyActiveDrones( attacker, teamEMPed, empOrigin, empRadius );
|
||||
}
|
||||
|
||||
destroyActiveHelis( attacker, teamEMPed, empOrigin, empRadius )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
damage = 5000;
|
||||
|
||||
activeHelis = level.helis;
|
||||
|
||||
if( IsDefined(level.orbitalsupport_planemodel) )
|
||||
activeHelis[activeHelis.size] = level.orbitalsupport_planemodel;
|
||||
|
||||
foreach ( heli in activeHelis )
|
||||
{
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
if( IsDefined( heli.team ) && heli.team != teamEMPed )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( heli.owner ) && heli.owner == attacker )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( IsDefined( empOrigin ) && IsDefined( empRadius ) )
|
||||
{
|
||||
point = empOrigin;
|
||||
if ( DistanceSquared( heli.origin, empOrigin ) > ( empRadius * empRadius ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
damage = heli.maxhealth + 1;
|
||||
|
||||
heli DoDamage( damage, heli.origin, attacker, attacker, meansOfDeath, weapon );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveLittleBirds( attacker, teamEMPed, empOrigin, empRadius )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
damage = 5000;
|
||||
|
||||
thingsToKill = array_combine( level.planes, level.littleBirds );
|
||||
|
||||
// disable the drone delivery module
|
||||
foreach( drone in level.carepackageDrones)
|
||||
{
|
||||
if ( IsDefined( drone.crate ) )
|
||||
thingsToKill[thingsToKill.size] = drone.crate;
|
||||
}
|
||||
|
||||
foreach ( lb in thingsToKill )
|
||||
{
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
if( IsDefined( lb.team ) && lb.team != teamEMPed )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( lb.owner ) && lb.owner == attacker )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( IsDefined( empOrigin ) && IsDefined( empRadius ) )
|
||||
{
|
||||
point = empOrigin;
|
||||
if ( DistanceSquared( lb.origin, empOrigin ) > ( empRadius * empRadius ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
damage = lb.maxhealth + 1;
|
||||
|
||||
if ( IsDefined( lb.crateType ) )
|
||||
lb = lb.enemyModel;
|
||||
|
||||
lb DoDamage( damage, lb.origin, attacker, attacker, meansOfDeath, weapon );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveTurrets( attacker, teamEMPed, empOrigin, empRadius )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
damage = 5000;
|
||||
|
||||
foreach ( turret in level.turrets )
|
||||
{
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
if( IsDefined( turret.team ) && turret.team != teamEMPed )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( turret.owner ) && turret.owner == attacker )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( IsDefined( empOrigin ) && IsDefined( empRadius ) )
|
||||
{
|
||||
point = empOrigin;
|
||||
if ( DistanceSquared( turret.origin, empOrigin ) > ( empRadius * empRadius ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
damage = turret.maxhealth + 1;
|
||||
|
||||
turret DoDamage( damage, turret.origin, attacker, attacker, meansOfDeath, weapon );
|
||||
}
|
||||
|
||||
// Force players to cancel carrying their turrets
|
||||
if( IsDefined( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if( IsDefined( player.isCarrying ) && player.isCarrying )
|
||||
{
|
||||
player notify( "force_cancel_placement" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveRockets( attacker, teamEMPed, empOrigin, empRadius )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
damage = 5000;
|
||||
|
||||
|
||||
foreach ( rocket in level.rockets )
|
||||
{
|
||||
if( IsDefined(rocket.weaponName) && skipRocketEMP(rocket.weaponName) )
|
||||
continue;
|
||||
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
if( IsDefined( rocket.team ) && rocket.team != teamEMPed )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( rocket.owner ) && rocket.owner == attacker )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( IsDefined( empOrigin ) && IsDefined( empRadius ) )
|
||||
{
|
||||
point = empOrigin;
|
||||
if ( DistanceSquared( rocket.origin, empOrigin ) > ( empRadius * empRadius ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
if( shouldDamageRocket(rocket) )
|
||||
{
|
||||
damage = rocket.maxhealth + 1;
|
||||
rocket DoDamage( damage, rocket.origin, attacker, attacker, meansOfDeath, weapon );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayFX( level.remotemissile_fx[ "explode" ], rocket.origin );
|
||||
rocket delete();
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
shouldDamageRocket( rocket )
|
||||
{
|
||||
// only damage rockets that have damage functions
|
||||
return IsDefined(rocket.damageCallback);
|
||||
}
|
||||
|
||||
skipRocketEMP( weaponName )
|
||||
{
|
||||
// emp should not affect care package
|
||||
return ( weaponName == "orbital_carepackage_pod_mp" );
|
||||
}
|
||||
|
||||
destroyActiveUAVs( attacker, teamEMPed, empOrigin, empRadius )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
damage = 5000;
|
||||
|
||||
uavArray = level.uavModels;
|
||||
|
||||
if( level.teamBased && IsDefined( teamEMPed ) )
|
||||
uavArray = level.uavModels[ teamEMPed ];
|
||||
|
||||
foreach ( uav in uavArray )
|
||||
{
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( uav.owner ) && uav.owner == attacker )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( IsDefined( empOrigin ) && IsDefined( empRadius ) )
|
||||
{
|
||||
point = empOrigin;
|
||||
if ( DistanceSquared( uav.origin, empOrigin ) > ( empRadius * empRadius ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
damage = uav.maxhealth + 1;
|
||||
|
||||
uav DoDamage( damage, uav.origin, attacker, attacker, meansOfDeath, weapon );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveUGVs( attacker, teamEMPed, empOrigin, empRadius )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
damage = 5000;
|
||||
|
||||
foreach ( ugv in level.ugvs )
|
||||
{
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
if( IsDefined( ugv.team ) && ugv.team != teamEMPed )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( ugv.owner ) && ugv.owner == attacker )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( IsDefined( empOrigin ) && IsDefined( empRadius ) )
|
||||
{
|
||||
point = empOrigin;
|
||||
if ( DistanceSquared( ugv.origin, empOrigin ) > ( empRadius * empRadius ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
damage = ugv.maxhealth + 1;
|
||||
|
||||
ugv DoDamage( damage, ugv.origin, attacker, attacker, meansOfDeath, weapon );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveDrones( attacker, teamEMPed, empOrigin, empRadius )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
damage = 5000;
|
||||
|
||||
|
||||
droneList = array_combine( level.trackingDrones, level.explosiveDrones );
|
||||
|
||||
foreach( activeDrone in droneList )
|
||||
{
|
||||
if( level.teamBased && IsDefined(teamEMPed) )
|
||||
{
|
||||
if( IsDefined(activeDrone.team) && (activeDrone.team != teamEMPed) )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined(activeDrone.owner) && (activeDrone.owner == attacker) )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( IsDefined(empOrigin) && IsDefined(empRadius) )
|
||||
{
|
||||
point = empOrigin;
|
||||
if ( DistanceSquared( activeDrone.origin, empOrigin ) > ( empRadius * empRadius ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
damage = activeDrone.maxhealth + 1;
|
||||
|
||||
activeDrone DoDamage( damage, activeDrone.origin, attacker, attacker, meansOfDeath, weapon );
|
||||
}
|
||||
|
||||
foreach( grenade in level.grenades )
|
||||
{
|
||||
if( !IsDefined(grenade.weaponName) || !IsSubStr( grenade.weaponName, "explosive_drone_mp" ) )
|
||||
continue;
|
||||
|
||||
if( level.teamBased && IsDefined(teamEMPed) )
|
||||
{
|
||||
if( IsDefined(grenade.team) && (grenade.team != teamEMPed) )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined(grenade.owner) && (grenade.owner == attacker) )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( IsDefined(empOrigin) && IsDefined(empRadius) )
|
||||
{
|
||||
point = empOrigin;
|
||||
if ( DistanceSquared( grenade.origin, empOrigin ) > ( empRadius * empRadius ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
grenade thread maps\mp\_explosive_drone::explosiveGrenadeDeath();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
destroyActiveOrbitalLasers( attacker, teamEMPed, empOrigin, empRadius )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
|
||||
damage = 5000;
|
||||
direction_vec = ( 0, 0, 0 );
|
||||
point = ( 0, 0, 0 );
|
||||
modelName = "";
|
||||
tagName = "";
|
||||
partName = "";
|
||||
iDFlags = undefined;
|
||||
|
||||
foreach ( laser in level.orbital_lasers )
|
||||
{
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
if( IsDefined( laser.team ) && laser.team != teamEMPed )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( laser.owner ) && laser.owner == attacker )
|
||||
continue;
|
||||
}
|
||||
|
||||
laser notify( "death", attacker, meansOfDeath, weapon );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
destroyActiveGoliaths( attacker, teamEMPed )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player isJuggernaut() )
|
||||
{
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
if ( IsDefined( player.team ) && player.team != teamEMPed )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isdefined ( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
player maps\mp\_snd_common_mp::snd_message( "goliath_self_destruct" );
|
||||
PlayFX( getfx( "goliath_self_destruct" ), player.origin, AnglesToUp(player.angles) );
|
||||
player thread [[level.hordeHandleJuggDeath]]();
|
||||
}
|
||||
else
|
||||
player thread maps\mp\killstreaks\_juggernaut::playerKillHeavyExo( player.origin, attacker, "MOD_EXPLOSIVE", "killstreak_goliathsd_mp" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/#
|
||||
drawEMPDamageOrigin( pos, ang, radius )
|
||||
{
|
||||
while( GetDvarInt( "scr_emp_damage_debug" ) )
|
||||
{
|
||||
Line( pos, pos + ( AnglesToForward( ang ) * radius ), ( 1, 0, 0 ) );
|
||||
Line( pos, pos + ( AnglesToRight( ang ) * radius ), ( 0, 1, 0 ) );
|
||||
Line( pos, pos + ( AnglesToUp( ang ) * radius ), ( 0, 0, 1 ) );
|
||||
|
||||
Line( pos, pos - ( AnglesToForward( ang ) * radius ), ( 1, 0, 0 ) );
|
||||
Line( pos, pos - ( AnglesToRight( ang ) * radius ), ( 0, 1, 0 ) );
|
||||
Line( pos, pos - ( AnglesToUp( ang ) * radius ), ( 0, 0, 1 ) );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
#/
|
3455
raw/maps/mp/killstreaks/_juggernaut.gsc
Normal file
3455
raw/maps/mp/killstreaks/_juggernaut.gsc
Normal file
File diff suppressed because it is too large
Load Diff
2524
raw/maps/mp/killstreaks/_killstreaks.gsc
Normal file
2524
raw/maps/mp/killstreaks/_killstreaks.gsc
Normal file
File diff suppressed because it is too large
Load Diff
125
raw/maps/mp/killstreaks/_killstreaks_init.gsc
Normal file
125
raw/maps/mp/killstreaks/_killstreaks_init.gsc
Normal file
@ -0,0 +1,125 @@
|
||||
#include maps\mp\killstreaks\_killstreaks;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
level.KILLSTREAK_STRING_TABLE = "mp/killstreakTable.csv";
|
||||
level.KILLSTREAK_GIMME_SLOT = 0;
|
||||
level.KILLSTREAK_SLOT_1 = 1;
|
||||
level.KILLSTREAK_SLOT_2 = 2;
|
||||
level.KILLSTREAK_SLOT_3 = 3;
|
||||
level.KILLSTREAK_SLOT_4 = 4;
|
||||
level.KILLSTREAK_STACKING_START_SLOT = 5;
|
||||
|
||||
// modules
|
||||
level.KS_MODULES_TABLE = "mp/killstreakModules.csv";
|
||||
level.KS_MODULE_REF_COLUMN = 1;
|
||||
/# level.KS_MODULE_NAME_COLUMN = 2; #/
|
||||
level.KS_MODULE_KILLSTREAK_REF_COLUMN = 4;
|
||||
level.KS_MODULE_ADDED_POINTS_COLUMN = 5;
|
||||
level.KS_MODULE_SLOT_COLUMN = 6;
|
||||
level.KS_MODULE_SUPPORT_COLUMN = 7;
|
||||
|
||||
// default modules table for killstreaks out of a care package
|
||||
// level.KS_COMBINATIONS_TABLE = "mp/killstreakCombinations.csv";
|
||||
// level.KS_COMBINATIONS_REF_COLUMN = 0;
|
||||
// level.KS_COMBINATIONS_MOD1_COLUMN = 1;
|
||||
// level.KS_COMBINATIONS_MOD2_COLUMN = 2;
|
||||
// level.KS_COMBINATIONS_MOD3_COLUMN = 3;
|
||||
// level.KS_COMBINATIONS_VALID_COLUMN = 4;
|
||||
|
||||
level.killstreakRoundDelay = getIntProperty( "scr_game_killstreakdelay", 10 );
|
||||
level.killstreakFuncs = [];
|
||||
level.killstreakSetupFuncs = [];
|
||||
level.killstreakWieldWeapons = [];
|
||||
|
||||
initKillstreakData();
|
||||
|
||||
level thread onPlayerConnect();
|
||||
|
||||
if ( IsDefined( level.isZombieGame ) && level.isZombieGame )
|
||||
return;
|
||||
|
||||
// killstreak utility files
|
||||
level thread maps\mp\killstreaks\_aerial_utility::init();
|
||||
level thread maps\mp\killstreaks\_coop_util::init();
|
||||
|
||||
// map specific kill streaks
|
||||
if( IsDefined(level.mapCustomKillstreakFunc) )
|
||||
[[ level.mapCustomKillstreakFunc ]]();
|
||||
|
||||
// active killstreaks
|
||||
level thread maps\mp\killstreaks\_uav::init();
|
||||
level thread maps\mp\killstreaks\_airdrop::init();
|
||||
level thread maps\mp\killstreaks\_remoteturret::init();
|
||||
level thread maps\mp\killstreaks\_rippedturret::init();
|
||||
level thread maps\mp\killstreaks\_emp::init();
|
||||
level thread maps\mp\killstreaks\_nuke::init();
|
||||
level thread maps\mp\killstreaks\_juggernaut::init();
|
||||
level thread maps\mp\killstreaks\_orbital_strike::init();
|
||||
level thread maps\mp\killstreaks\_missile_strike::init();
|
||||
level thread maps\mp\killstreaks\_orbital_carepackage::init();
|
||||
level thread maps\mp\killstreaks\_warbird::init();
|
||||
level thread maps\mp\killstreaks\_drone_assault::init();
|
||||
level thread maps\mp\killstreaks\_drone_recon::init();
|
||||
level thread maps\mp\killstreaks\_orbitalsupport::init();
|
||||
level thread maps\mp\killstreaks\_airstrike::init();
|
||||
// level thread maps\mp\killstreaks\_autosentry::init();
|
||||
level thread maps\mp\killstreaks\_drone_carepackage::init();
|
||||
level thread maps\mp\killstreaks\_orbital_util::initStart();
|
||||
}
|
||||
|
||||
|
||||
initKillstreakData()
|
||||
{
|
||||
for ( i = 0; true; i++ )
|
||||
{
|
||||
streakRef = TableLookupByRow( level.KILLSTREAK_STRING_TABLE, i, 1);
|
||||
if ( !IsDefined( streakRef ) || streakRef == "" )
|
||||
break;
|
||||
|
||||
if ( streakRef == "b1" || streakRef == "none" )
|
||||
continue;
|
||||
|
||||
streakUseHint = TableLookupIStringByRow( level.KILLSTREAK_STRING_TABLE, i, 5 );
|
||||
assert( streakUseHint != &"" );
|
||||
|
||||
streakEarnDialog = TableLookupByRow( level.KILLSTREAK_STRING_TABLE, i, 7 );
|
||||
assert( streakEarnDialog != "" );
|
||||
game["dialog"][ streakRef ] = streakEarnDialog;
|
||||
|
||||
streakAlliesUseDialog = TableLookupByRow( level.KILLSTREAK_STRING_TABLE, i, 8 );
|
||||
assert( streakAlliesUseDialog != "" );
|
||||
game["dialog"][ "allies_friendly_" + streakRef + "_inbound" ] = "ks_" + streakAlliesUseDialog + "_allyuse";
|
||||
game["dialog"][ "allies_enemy_" + streakRef + "_inbound" ] = "ks_" + streakAlliesUseDialog + "_enemyuse";
|
||||
|
||||
streakAxisUseDialog = TableLookupByRow( level.KILLSTREAK_STRING_TABLE, i, 9 );
|
||||
assert( streakAxisUseDialog != "" );
|
||||
game["dialog"][ "axis_friendly_" + streakRef + "_inbound" ] = "ks_" + streakAxisUseDialog + "_allyuse";
|
||||
game["dialog"][ "axis_enemy_" + streakRef + "_inbound" ] = "ks_" + streakAxisUseDialog + "_enemyuse";
|
||||
|
||||
streakPoints = int( TableLookupByRow( level.KILLSTREAK_STRING_TABLE, i, 12 ) );
|
||||
maps\mp\gametypes\_rank::registerXPEventInfo( streakRef + "_earned", streakPoints );
|
||||
}
|
||||
|
||||
additionalVO();
|
||||
}
|
||||
|
||||
additionalVO()
|
||||
{
|
||||
// last minute add to change VO based on modules for EMP
|
||||
baseStringAlliesFriendly = "allies_friendly_emp_inbound";
|
||||
baseStringAlliesEnemy = "allies_enemy_emp_inbound";
|
||||
baseStringAxisFriendly = "axis_friendly_emp_inbound";
|
||||
baseStringAxisEnemy = "axis_enemy_emp_inbound";
|
||||
|
||||
for( i = 1; i < 9; i++ )
|
||||
{
|
||||
appendString = "_0" + i;
|
||||
game["dialog"][ baseStringAlliesFriendly + appendString ] = game["dialog"][ baseStringAlliesFriendly] + appendString;
|
||||
game["dialog"][ baseStringAlliesEnemy + appendString ] = game["dialog"][ baseStringAlliesEnemy] + appendString;
|
||||
game["dialog"][ baseStringAxisFriendly + appendString ] = game["dialog"][ baseStringAxisFriendly] + appendString;
|
||||
game["dialog"][ baseStringAxisEnemy + appendString ] = game["dialog"][ baseStringAxisEnemy] + appendString;
|
||||
}
|
||||
}
|
10
raw/maps/mp/killstreaks/_marking_util.gsc
Normal file
10
raw/maps/mp/killstreaks/_marking_util.gsc
Normal file
@ -0,0 +1,10 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
playerProcessTaggedAssist( victim )
|
||||
{
|
||||
if( level.teamBased && isDefined( victim ) )
|
||||
{
|
||||
self thread maps\mp\_events::processAssistEvent( victim );
|
||||
}
|
||||
}
|
1657
raw/maps/mp/killstreaks/_missile_strike.gsc
Normal file
1657
raw/maps/mp/killstreaks/_missile_strike.gsc
Normal file
File diff suppressed because it is too large
Load Diff
520
raw/maps/mp/killstreaks/_nuke.gsc
Normal file
520
raw/maps/mp/killstreaks/_nuke.gsc
Normal file
@ -0,0 +1,520 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
// the nuke will kill the other team and emp them for 60 seconds, it will also change the visionset for the level
|
||||
|
||||
init()
|
||||
{
|
||||
//level.nukeVisionSet = "aftermath";
|
||||
|
||||
level._effect[ "nuke_flash" ] = loadfx( "vfx/explosion/dna_bomb_flash_mp" );
|
||||
level._effect[ "nuke_aftermath" ] = loadfx( "vfx/dust/nuke_aftermath_mp" );
|
||||
level._effect[ "dna_bomb_body_gas" ] = loadfx( "vfx/explosion/dna_bomb_body_gas" );
|
||||
|
||||
game["strings"]["nuclear_strike"] = &"KILLSTREAKS_TACTICAL_NUKE";
|
||||
|
||||
level.killstreakFuncs["nuke"] = ::tryUseNuke;
|
||||
level.killstreakWieldWeapons["nuke_mp"] = "nuke";
|
||||
|
||||
SetDvarIfUninitialized( "scr_nukeTimer", 10 );
|
||||
SetDvarIfUninitialized( "scr_nukeCancelMode", 0 );
|
||||
|
||||
level.nukeTimer = getDvarInt( "scr_nukeTimer" );
|
||||
level.cancelMode = getDvarInt( "scr_nukeCancelMode" );
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// if( level.multiTeamBased )
|
||||
// {
|
||||
// for( i = 0; i < level.teamNameList.size; i++ )
|
||||
// {
|
||||
// level.teamNukeEMPed[level.teamNameList[i]] = false;
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// level.teamNukeEMPed["allies"] = false;
|
||||
// level.teamNukeEMPed["axis"] = false;
|
||||
// }
|
||||
|
||||
level.nukeEmpTimeout = 60.0;
|
||||
level.nukeEmpTimeRemaining = int( level.nukeEmpTimeout );
|
||||
level.nukeInfo = spawnStruct();
|
||||
level.nukeInfo.xpScalar = 2;
|
||||
level.nukeDetonated = undefined;
|
||||
|
||||
//level thread nuke_EMPTeamTracker();
|
||||
|
||||
level thread onPlayerConnect();
|
||||
|
||||
/#
|
||||
//SetDevDvarIfUninitialized( "scr_nuke_empTimeout", 60.0 );
|
||||
SetDevDvarIfUninitialized( "scr_nukeDistance", 5000 );
|
||||
SetDevDvarIfUninitialized( "scr_nukeEndsGame", true );
|
||||
SetDevDvarIfUninitialized( "scr_nukeDebugPosition", false );
|
||||
#/
|
||||
}
|
||||
|
||||
tryUseNuke( lifeId, modules, allowCancel )
|
||||
{
|
||||
if( isDefined( level.nukeIncoming ) )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_NUKE_ALREADY_INBOUND" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isUsingRemote() )
|
||||
return false;
|
||||
|
||||
if ( !isDefined( allowCancel ) )
|
||||
allowCancel = true;
|
||||
|
||||
self thread doNuke( allowCancel );
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "nuke", self.origin );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
delaythread_nuke( delay, func )
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( delay );
|
||||
|
||||
thread [[ func ]]();
|
||||
}
|
||||
|
||||
doNuke( allowCancel )
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
level.nukeInfo.player = self;
|
||||
level.nukeInfo.team = self.pers["team"];
|
||||
|
||||
level.nukeIncoming = true;
|
||||
|
||||
SetOmnvar( "ui_bomb_timer", 4 ); // Nuke sets '4' to avoid briefcase icon showing
|
||||
|
||||
if( level.teambased )
|
||||
{
|
||||
thread teamPlayerCardSplash( "used_nuke", self, self.team );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !level.hardcoreMode )
|
||||
self IPrintLnBold( &"MP_FRIENDLY_TACTICAL_NUKE" );
|
||||
}
|
||||
|
||||
level thread delaythread_nuke( (level.nukeTimer - 3.3), ::nukeSoundIncoming );
|
||||
level thread delaythread_nuke( level.nukeTimer, ::nukeSoundExplosion );
|
||||
level thread delaythread_nuke( level.nukeTimer, ::nukeSlowMo );
|
||||
level thread delaythread_nuke( (level.nukeTimer - 0.32 ), ::nukeEffects );
|
||||
level thread delaythread_nuke( (level.nukeTimer - 0.1), ::nukeVision );
|
||||
level thread delaythread_nuke( (level.nukeTimer + 0.5), ::nukeDeath );
|
||||
level thread delaythread_nuke( (level.nukeTimer + 1.5), ::nukeEarthquake );
|
||||
level thread nukeAftermathEffect();
|
||||
level thread update_ui_timers();
|
||||
|
||||
if ( level.cancelMode && allowCancel )
|
||||
level thread cancelNukeOnDeath( self );
|
||||
|
||||
// need objects to play sound off of, i'm keeping them on level so we don't spawn them more than once if multiple nukes get called in a match
|
||||
if( !IsDefined( level.nuke_clockObject ) )
|
||||
{
|
||||
level.nuke_clockObject = Spawn( "script_origin", (0,0,0) );
|
||||
level.nuke_clockObject hide();
|
||||
}
|
||||
if( !IsDefined( level.nuke_soundObject ) )
|
||||
{
|
||||
level.nuke_soundObject = Spawn( "script_origin", (0,0,1) );
|
||||
level.nuke_soundObject hide();
|
||||
}
|
||||
|
||||
nukeTimer = level.nukeTimer;
|
||||
while( nukeTimer > 0 )
|
||||
{
|
||||
// TODO: get a new sound for this so we don't remind people of the old nuke
|
||||
level.nuke_clockObject playSound( "ks_dna_warn_timer" );
|
||||
wait( 1.0 );
|
||||
nukeTimer--;
|
||||
}
|
||||
}
|
||||
|
||||
cancelNukeOnDeath( player )
|
||||
{
|
||||
player waittill_any( "death", "disconnect" );
|
||||
|
||||
// if ( isDefined( player ) && level.cancelMode == 2 )
|
||||
// player thread maps\mp\killstreaks\_emp::EMP_Use( 0, 0 );
|
||||
|
||||
SetOmnvar( "ui_bomb_timer", 0 ); // Nuke sets '4' to avoid briefcase icon showing
|
||||
level.nukeIncoming = undefined;
|
||||
|
||||
level notify ( "nuke_cancelled" );
|
||||
}
|
||||
|
||||
nukeSoundIncoming()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
if( IsDefined( level.nuke_soundObject ) )
|
||||
level.nuke_soundObject PlaySound( "ks_dna_incoming" );
|
||||
}
|
||||
|
||||
nukeSoundExplosion()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
if( IsDefined( level.nuke_soundObject ) )
|
||||
{
|
||||
level.nuke_soundObject PlaySound( "ks_dna_explosion" );
|
||||
level.nuke_soundObject PlaySound( "ks_dna_wave" );
|
||||
}
|
||||
}
|
||||
|
||||
nukeEffects()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
//SetOmnvar( "ui_bomb_timer", 0 );
|
||||
|
||||
//level.nukeDetonated = true;
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
playerForward = anglestoforward( player.angles );
|
||||
playerForward = ( playerForward[0], playerForward[1], 0 );
|
||||
playerForward = VectorNormalize( playerForward );
|
||||
|
||||
nukeDistance = 300;
|
||||
// /# nukeDistance = getDvarInt( "scr_nukeDistance" ); #/
|
||||
|
||||
nukeEnt = Spawn( "script_model", player.origin + ( playerForward * nukeDistance ) );
|
||||
nukeEnt setModel( "tag_origin" );
|
||||
nukeEnt.angles = ( 0, (player.angles[1] + 180), 90 );
|
||||
|
||||
/#
|
||||
if ( getDvarInt( "scr_nukeDebugPosition" ) )
|
||||
{
|
||||
lineTop = ( nukeEnt.origin[0], nukeEnt.origin[1], (nukeEnt.origin[2] + 500) );
|
||||
thread draw_line_for_time( nukeEnt.origin, lineTop, 1, 0, 0, 10 );
|
||||
}
|
||||
#/
|
||||
|
||||
nukeEnt thread nukeEffect( player );
|
||||
}
|
||||
}
|
||||
|
||||
nukeEffect( player )
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
player endon( "disconnect" );
|
||||
|
||||
waitframe();
|
||||
PlayFXOnTagForClients( level._effect[ "nuke_flash" ], self , "tag_origin", player );
|
||||
}
|
||||
|
||||
nukeAftermathEffect()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
level waittill ( "spawning_intermission" );
|
||||
|
||||
afermathEnt = getEntArray( "mp_global_intermission", "classname" );
|
||||
afermathEnt = afermathEnt[0];
|
||||
up = anglestoup( afermathEnt.angles );
|
||||
right = anglestoright( afermathEnt.angles );
|
||||
|
||||
PlayFX( level._effect[ "nuke_aftermath" ], afermathEnt.origin, up, right );
|
||||
}
|
||||
|
||||
nukeSlowMo()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
SetOmnvar( "ui_bomb_timer", 0 );
|
||||
|
||||
SetSlowMotion( 1.0, 0.25, 0.5 );
|
||||
level waittill( "nuke_death" );
|
||||
SetSlowMotion( 0.25, 1, 2.0 );
|
||||
}
|
||||
|
||||
nukeVision()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
level.nukeVisionInProgress = true;
|
||||
foreach (player in level.players)
|
||||
{
|
||||
player SetClientTriggerVisionSet("dna_bomb", 0.5 );
|
||||
player thread maps\mp\_flashgrenades::applyFlash( 1.6, 0.35 );
|
||||
}
|
||||
|
||||
level waittill( "nuke_death" );
|
||||
|
||||
//time for fog to linger
|
||||
wait 3.0;
|
||||
foreach (player in level.players)
|
||||
player SetClientTriggerVisionSet("", 10 );
|
||||
// turn off nuke vision set
|
||||
level.nukeVisionInProgress = undefined;
|
||||
|
||||
|
||||
}
|
||||
|
||||
nukeDeath()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
level notify( "nuke_death" );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
AmbientStop(1);
|
||||
|
||||
counter = 0;
|
||||
foreach( player in level.players )
|
||||
{
|
||||
// don't kill teammates
|
||||
if( level.teambased )
|
||||
{
|
||||
if( IsDefined( level.nukeInfo.team ) && player.team == level.nukeInfo.team )
|
||||
continue;
|
||||
}
|
||||
// ffa, don't kill the player who called it
|
||||
else
|
||||
{
|
||||
if( IsDefined( level.nukeInfo.player ) && player == level.nukeInfo.player )
|
||||
continue;
|
||||
}
|
||||
|
||||
player.nuked = true;
|
||||
if ( isAlive( player ) )
|
||||
{
|
||||
player thread maps\mp\gametypes\_damage::finishPlayerDamageWrapper( level.nukeInfo.player, level.nukeInfo.player, 999999, 0, "MOD_EXPLOSIVE", "nuke_mp", player.origin, player.origin, "none", 0, 0 );
|
||||
|
||||
if ( IsDefined( player.isJuggernaut ) && player.isJuggernaut == true )
|
||||
{
|
||||
player DoDamage( 1, player.origin, level.nukeInfo.player, level.nukeInfo.player, "MOD_EXPLOSIVE", "nuke_mp" );
|
||||
}
|
||||
|
||||
//play vfx on corpses, use counter delay so all players don't get called on same frame
|
||||
delayThread( counter + 1, ::bodyGasFX, player.body );
|
||||
counter += 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
// emp jam them after death, if we do before then the timing is off
|
||||
level thread nuke_EMPJam();
|
||||
|
||||
// since the nuke death happened, the nuke is no longer incoming
|
||||
level.nukeIncoming = undefined;
|
||||
}
|
||||
|
||||
bodyGasFX( ent )
|
||||
{
|
||||
if ( isDefined(ent) )
|
||||
playfxontag( getfx( "dna_bomb_body_gas" ), ent, "J_SPINELOWER" );
|
||||
}
|
||||
|
||||
nukeEarthquake()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
level waittill( "nuke_death" );
|
||||
|
||||
// TODO: need to get a different position to call this on
|
||||
//earthquake( 0.6, 10, nukepos, 100000 );
|
||||
|
||||
//foreach( player in level.players )
|
||||
//player PlayRumbleOnEntity( "damage_heavy" );
|
||||
}
|
||||
|
||||
|
||||
nuke_EMPJam()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
// since nukes do emp damage, might as well emp jam for a little while also
|
||||
|
||||
// set this up to end itself if called again
|
||||
level notify( "nuke_EMPJam" );
|
||||
level endon( "nuke_EMPJam" );
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
//nuke all other teams
|
||||
for( i = 0; i < level.teamNameList.size; i++ )
|
||||
{
|
||||
if( level.nukeInfo.team != level.teamNameList[i] )
|
||||
{
|
||||
level maps\mp\killstreaks\_emp::destroyActiveVehicles( level.nukeInfo.player, level.teamNameList[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( level.teambased )
|
||||
{
|
||||
level maps\mp\killstreaks\_emp::destroyActiveVehicles( level.nukeInfo.player, getOtherTeam( level.nukeInfo.team ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
level maps\mp\killstreaks\_emp::destroyActiveVehicles( level.nukeInfo.player, getOtherTeam( level.nukeInfo.team ) );
|
||||
}
|
||||
|
||||
level notify( "nuke_emp_update" );
|
||||
|
||||
/#
|
||||
level.nukeEmpTimeout = GetDvarFloat( "scr_nuke_empTimeout" );
|
||||
#/
|
||||
/*level thread keepNukeEMPTimeRemaining();
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( level.nukeEmpTimeout );
|
||||
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
//nuke all other teams
|
||||
for( i = 0; i < level.teamNameList.size; i++ )
|
||||
{
|
||||
if( level.nukeInfo.team != level.teamNameList[i] )
|
||||
{
|
||||
level.teamNukeEMPed[level.teamNameList[i]] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( level.teambased )
|
||||
{
|
||||
level.teamNukeEMPed[ getOtherTeam( level.nukeInfo.team ) ] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
level.teamNukeEMPed[ level.nukeInfo.team ] = false;
|
||||
level.teamNukeEMPed[ getOtherTeam( level.nukeInfo.team ) ] = false;
|
||||
}
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if( level.teambased && player.team == level.nukeInfo.team )
|
||||
continue;
|
||||
|
||||
player.nuked = undefined;
|
||||
}
|
||||
|
||||
// we want the nuke vision to last the rest of this match, leaving here in case we change our minds :)
|
||||
level.nukeVisionInProgress = undefined;
|
||||
VisionSetNaked( "", 5.0 ); // go to default visionset*/
|
||||
|
||||
level notify( "nuke_emp_update" );
|
||||
level notify ( "nuke_emp_ended" );
|
||||
}
|
||||
|
||||
keepNukeEMPTimeRemaining()
|
||||
{
|
||||
level notify( "keepNukeEMPTimeRemaining" );
|
||||
level endon( "keepNukeEMPTimeRemaining" );
|
||||
|
||||
level endon( "nuke_emp_ended" );
|
||||
|
||||
// we need to know how much time is left for the unavailable string
|
||||
level.nukeEmpTimeRemaining = int( level.nukeEmpTimeout );
|
||||
while( level.nukeEmpTimeRemaining )
|
||||
{
|
||||
wait( 1.0 );
|
||||
level.nukeEmpTimeRemaining--;
|
||||
}
|
||||
}
|
||||
|
||||
nuke_EMPTeamTracker()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill_either ( "joined_team", "nuke_emp_update" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == "spectator" )
|
||||
continue;
|
||||
|
||||
if( level.teambased )
|
||||
{
|
||||
if( IsDefined( level.nukeInfo.team ) && player.team == level.nukeInfo.team )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( level.nukeInfo.player ) && player == level.nukeInfo.player )
|
||||
continue;
|
||||
}
|
||||
|
||||
// if this nuke emp is over, let's do an extra check to make sure we shouldn't be regular emped, isEmped() does the extra emp check
|
||||
if( !level.teamNukeEMPed[ player.team ] && !player isEMPed() )
|
||||
player SetEMPJammed( false );
|
||||
else
|
||||
player SetEMPJammed( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
for(;;)
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
if( isDefined ( level.nukeVisionInProgress ) )
|
||||
{
|
||||
self SetClientTriggerVisionSet("dna_bomb" );
|
||||
waitframe();
|
||||
self SetClientTriggerVisionSet("", 10 );
|
||||
}
|
||||
/*if( level.teamNukeEMPed[ self.team ] )
|
||||
{
|
||||
if( level.teambased )
|
||||
self SetEMPJammed( true );
|
||||
else
|
||||
{
|
||||
if( !IsDefined( level.nukeInfo.player ) || ( IsDefined( level.nukeInfo.player ) && self != level.nukeInfo.player ) )
|
||||
self SetEMPJammed( true );
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the vision set stays on between deaths
|
||||
if( IsDefined( level.nukeDetonated ) )
|
||||
self VisionSetNakedForPlayer( level.nukeVisionSet, 0 );
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
update_ui_timers()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
level endon ( "disconnect" );
|
||||
level endon ( "nuke_cancelled" );
|
||||
level endon ( "nuke_death" );
|
||||
|
||||
nukeEndMilliseconds = (level.nukeTimer * 1000) + gettime();
|
||||
SetOmnvar( "ui_nuke_end_milliseconds", nukeEndMilliseconds );
|
||||
|
||||
level waittill( "host_migration_begin" );
|
||||
|
||||
timePassed = maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
if ( timePassed > 0 )
|
||||
{
|
||||
SetOmnvar( "ui_nuke_end_milliseconds", nukeEndMilliseconds + timePassed );
|
||||
}
|
||||
}
|
984
raw/maps/mp/killstreaks/_orbital_carepackage.gsc
Normal file
984
raw/maps/mp/killstreaks/_orbital_carepackage.gsc
Normal file
@ -0,0 +1,984 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\killstreaks\_orbital_util;
|
||||
|
||||
NODE_OFFSET = ( 0, 0, 70 );
|
||||
FLY_NODE_OFFSET = ( 0, 0, 40 );
|
||||
DROPPOINT_DISTANCE_SQ = 22500; // 150 * 150
|
||||
|
||||
init()
|
||||
{
|
||||
level._orbital_care_pod = [];
|
||||
level.orbitalDropMarkers = [];
|
||||
level._effect[ "ocp_death" ] = LoadFX( "vfx/explosion/exo_droppod_explosion");
|
||||
level._effect[ "ocp_midair" ] = LoadFX( "vfx/explosion/exo_droppod_split" );
|
||||
level._effect[ "ocp_ground_marker" ] = LoadFX( "vfx/unique/vfx_marker_killstreak_guide_carepackage" );
|
||||
level._effect[ "ocp_ground_marker_bad" ]= LoadFX( "vfx/unique/vfx_marker_killstreak_guide_carepackage_fizzle" );
|
||||
level._effect[ "ocp_exhaust" ] = LoadFX( "vfx/vehicle/vehicle_ocp_exhaust" );
|
||||
level._effect[ "ocp_thruster_small" ] = LoadFX( "vfx/vehicle/vehicle_ocp_thrusters_small" );
|
||||
level._effect[ "vfx_ocp_steam" ] = LoadFX( "vfx/steam/vfx_ocp_steam" );
|
||||
level._effect[ "vfx_ocp_steam2" ] = LoadFX( "vfx/steam/vfx_ocp_steam2" );
|
||||
level._effect[ "ocp_glow" ] = LoadFX( "vfx/unique/orbital_carepackage_glow" );
|
||||
|
||||
level.killStreakFuncs["orbital_carepackage"] = ::tryUseDefaultOrbitalCarePackage;
|
||||
level.killstreakWieldWeapons["orbital_carepackage_pod_mp"] = "orbital_carepackage";
|
||||
level.killstreakFuncs["orbital_carepackage_juggernaut_exosuit"] = ::tryUseOrbitalJuggernautExosuit;
|
||||
|
||||
PrecacheMpAnim("orbital_care_package_open");
|
||||
PrecacheMpAnim("orbital_care_package_fan_spin");
|
||||
|
||||
/#
|
||||
level thread debugDestroyCarepackages();
|
||||
#/
|
||||
}
|
||||
|
||||
tryUseDefaultOrbitalCarePackage( lifeId, modules )
|
||||
{
|
||||
return tryUseOrbitalCarePackage( lifeId, "orbital_carepackage", modules );
|
||||
}
|
||||
|
||||
tryUseOrbitalJuggernautExosuit( lifeId, modules )
|
||||
{
|
||||
return tryUseOrbitalCarePackage( lifeId, "orbital_carepackage_juggernaut_exosuit", modules );
|
||||
}
|
||||
|
||||
tryUseOrbitalCarePackage( lifeId, streakName, modules )
|
||||
{
|
||||
if ( array_contains( modules, "orbital_carepackage_drone" ) && currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount + 1 >= maxVehiclesAllowed() )
|
||||
{
|
||||
//"Too many vehicles already in the area."
|
||||
self IPrintLnBold( &"MP_TOO_MANY_VEHICLES" );
|
||||
return false;
|
||||
}
|
||||
|
||||
result = self playerLaunchCarepackage( streakName, modules );
|
||||
|
||||
if ( !isDefined( result ) || !result )
|
||||
return false;
|
||||
|
||||
if( streakName == "orbital_carepackage" )
|
||||
self maps\mp\gametypes\_missions::processChallenge( "ch_streak_orbitalcare", 1 );
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
playerLaunchCarepackage( streakName, modules )
|
||||
{
|
||||
outsideNode = self playerGetOutsideNode( "carepackage" );
|
||||
outsidePosition = undefined;
|
||||
|
||||
if ( IsDefined( outsideNode ) )
|
||||
{
|
||||
outsidePosition = outsideNode.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( isdefined ( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
outsideNode = [[level.hordeGetOutsidePosition]]();
|
||||
outsidePosition = outsideNode.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread playerPlayInvalidPositionEffect( getfx( "ocp_ground_marker_bad" ) );
|
||||
self SetClientOmnvar( "ui_invalid_orbital_care_package", 1 );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
drone = undefined;
|
||||
if ( array_contains( modules, "orbital_carepackage_drone" ) )
|
||||
{
|
||||
drone = SpawnHelicopter( self, outsidePosition + ( 0, 0, 200 ), ( 0, 0, 0 ), "orbital_carepackage_drone_mp", "orbital_carepackage_pod_01_vehicle" );
|
||||
if ( !IsDefined( drone ) )
|
||||
return false;
|
||||
drone Hide();
|
||||
}
|
||||
|
||||
result = FirePod( "orbital_carepackage_pod_mp", self, outsideNode, streakName, modules, drone, undefined, undefined, true );
|
||||
|
||||
return IsDefined( result );
|
||||
}
|
||||
|
||||
FirePod( weaponType, player, node, streakName, modules, drone, startPos, excludedCrateTypes, giveBackCarepackage )
|
||||
{
|
||||
if ( !IsDefined( startPos ) )
|
||||
startPos = player playerGetOrbitalStartPos( node, "carepackage" );
|
||||
targetPos = node.origin;
|
||||
|
||||
if ( !IsDefined( excludedCrateTypes ) )
|
||||
excludedCrateTypes = [];
|
||||
|
||||
/# debugPlacementLine( startPos, targetPos, ( 0, 1, 0 ) ); #/
|
||||
podrocket = MagicBullet( weaponType, startPos, targetPos, player );
|
||||
|
||||
if ( !IsDefined( podrocket ) )
|
||||
return;
|
||||
|
||||
podrocket thread SetMissileSpecialClipmaskDelayed( 3.0 ); //Only need to check item clip near the ground
|
||||
podrocket thread trajectory_kill( player );
|
||||
|
||||
DropPod = player createPlayerDropPod(podrocket);
|
||||
DropPod.streakName = streakName;
|
||||
DropPod.modules = modules;
|
||||
DropPod.dropPoint = node.origin;
|
||||
DropPod.drone = drone;
|
||||
DropPod.giveBackCarepackage = giveBackCarepackage;
|
||||
|
||||
podrocket.team = player.team;
|
||||
podrocket.owner = player;
|
||||
podrocket.type = "remote";
|
||||
|
||||
return monitorDrop( player, podrocket, DropPod, streakName, excludedCrateTypes, weaponType );
|
||||
}
|
||||
|
||||
trajectory_kill( player )
|
||||
{
|
||||
self endon("death");
|
||||
|
||||
lastOrigin = self.origin;
|
||||
|
||||
while( IsDefined( self ) )
|
||||
{
|
||||
if ( !level.teamBased )
|
||||
capsule_damage( 10000, self.origin, lastOrigin, 30, undefined, player );
|
||||
else
|
||||
capsule_damage( 10000, self.origin, lastOrigin, 30, level.otherTeam[ player.team ], player );
|
||||
lastOrigin = self.origin;
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
capsule_damage( damage, origin1, origin2, radius, killTeam, attacker )
|
||||
{
|
||||
assert( radius > 0 );
|
||||
|
||||
delta = origin2 - origin1;
|
||||
deltaNml = VectorNormalize( delta );
|
||||
dist = Length( delta );
|
||||
radiusSq = radius * radius;
|
||||
|
||||
foreach ( enemy in level.characters )
|
||||
{
|
||||
if ( !IsAlive( enemy ) )
|
||||
continue;
|
||||
|
||||
if ( enemy != attacker && IsDefined( killTeam ) && IsDefined( enemy.team ) && enemy.team != killTeam )
|
||||
continue;
|
||||
|
||||
deltaEnemy = enemy.origin - origin1;
|
||||
dot = VectorDot( deltaEnemy, deltaNml );
|
||||
|
||||
if ( dot > (radius * -1) && dot < dist + radius )
|
||||
{
|
||||
closestPoint = origin1 + ( deltaNml * dot );
|
||||
distEnemySq = DistanceSquared( closestPoint, enemy.origin );
|
||||
if ( distEnemySq <= radiusSq )
|
||||
enemy DoDamage( damage, closestPoint, attacker, self, "MOD_EXPLOSIVE", "orbital_carepackage_pod_mp" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetMissileSpecialClipmaskDelayed( delayTime )
|
||||
{
|
||||
self endon("death");
|
||||
wait delayTime;
|
||||
self SetMissileSpecialClipmask( true ); // Causes the rocket to collide with item clip, which is necessary as it will become a physics object on impact
|
||||
}
|
||||
|
||||
createPlayerDropPod(podrocket)
|
||||
{
|
||||
PodNum = 0;
|
||||
|
||||
if(!IsDefined(level._orbital_care_pod))
|
||||
{
|
||||
level._orbital_care_pod = [];
|
||||
}
|
||||
else //if (IsDefined(self._orbital_care_pod))
|
||||
{
|
||||
|
||||
level._orbital_care_pod = cleanArray(level._orbital_care_pod);
|
||||
PodNum = level._orbital_care_pod.size;
|
||||
}
|
||||
level._orbital_care_pod[PodNum] = SpawnStruct();
|
||||
level._orbital_care_pod[PodNum].HasLeftCam = false;
|
||||
level._orbital_care_pod[PodNum].podrocket = podrocket;
|
||||
level._orbital_care_pod[PodNum].podrocket.maxhealth = 100;
|
||||
level._orbital_care_pod[PodNum].podrocket.health = 100;
|
||||
level._orbital_care_pod[PodNum].podrocket.damageTaken = 0;
|
||||
level._orbital_care_pod[PodNum].podrocket.isPodRocket = true;
|
||||
level._orbital_care_pod[PodNum].owner = self;
|
||||
level._orbital_care_pod[PodNum].alive = true;
|
||||
return level._orbital_care_pod[PodNum];
|
||||
|
||||
}
|
||||
|
||||
Rocket_CleanupOnDeath()
|
||||
{
|
||||
entityNumber = self getEntityNumber();
|
||||
level.rockets[ entityNumber ] = self;
|
||||
self waittill( "death" );
|
||||
|
||||
level.rockets[ entityNumber ] = undefined;
|
||||
|
||||
if(IsDefined(self.killCamEnt))
|
||||
{
|
||||
self.killCamEnt Unlink();
|
||||
self.killCamEnt.origin += (0,0,300);
|
||||
}
|
||||
}
|
||||
|
||||
getDropTypeFromStreakName( streakName )
|
||||
{
|
||||
switch ( streakName )
|
||||
{
|
||||
case "orbital_carepackage_juggernaut_exosuit":
|
||||
return "orbital_carepackage_juggernaut_exosuit";
|
||||
case "airdrop_reinforcement_common":
|
||||
return "airdrop_reinforcement_common";
|
||||
case "airdrop_reinforcement_uncommon":
|
||||
return "airdrop_reinforcement_uncommon";
|
||||
case "airdrop_reinforcement_rare":
|
||||
return "airdrop_reinforcement_rare";
|
||||
case "airdrop_reinforcement_practice":
|
||||
return "airdrop_reinforcement_practice";
|
||||
case "horde_support_drop":
|
||||
return "horde_support_drop";
|
||||
default:
|
||||
return "airdrop_assault";
|
||||
}
|
||||
}
|
||||
|
||||
allowDroneDelivery( player )
|
||||
{
|
||||
if ( !IsDefined( player ) )
|
||||
return false;
|
||||
|
||||
if( level.teamBased && level.teamEMPed[player.team] )
|
||||
return false;
|
||||
|
||||
if( !level.teamBased && IsDefined(level.empPlayer) && level.empPlayer != player )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
monitorDrop( player, podrocket, DropPod, streakName, excludedCrateTypes, weaponType )
|
||||
{
|
||||
droptype = getDropTypeFromStreakName( streakName );
|
||||
if ( droptype == "airdrop_assault" && array_contains( DropPod.modules, "orbital_carepackage_odds" ) )
|
||||
droptype = "airdrop_assault_odds";
|
||||
if ( IsDefined( level.getCrateForDropType ) )
|
||||
crateType = [[ level.getCrateForDropType ]]( dropType );
|
||||
else
|
||||
crateType = maps\mp\killstreaks\_airdrop::getCrateTypeForDropType( droptype );
|
||||
|
||||
thread monitorDropInternal( player, podrocket, DropPod, droptype, crateType, weaponType );
|
||||
|
||||
return crateType;
|
||||
}
|
||||
|
||||
monitorDropInternal( player, podrocket, DropPod, droptype, crateType, weaponType )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
podrocket thread Rocket_CleanupOnDeath();
|
||||
|
||||
hasTrap = array_contains( DropPod.modules, "orbital_carepackage_trap" );
|
||||
dropCrate = player maps\mp\killstreaks\_airdrop::createAirDropCrate( player, droptype, crateType, podrocket.origin, undefined, hasTrap, false );
|
||||
dropCrate.moduleTrap = array_contains( DropPod.modules, "orbital_carepackage_trap" );
|
||||
dropCrate.moduleHide = array_contains( DropPod.modules, "orbital_carepackage_hide" );
|
||||
dropCrate.moduleRoll = array_contains( DropPod.modules, "orbital_carepackage_roll" );
|
||||
dropCrate.modulePickup = array_contains( DropPod.modules, "orbital_carepackage_fast_pickup" );
|
||||
dropCrate.angles = (0,0,0);
|
||||
|
||||
podrocket.killCamEnt = dropCrate.killCamEnt;
|
||||
|
||||
if ( weaponType == "orbital_carepackage_pod_plane_mp" )
|
||||
podrocket.killCamEnt.killCamStartTime = GetTime();
|
||||
|
||||
droneDelivery = array_contains(DropPod.modules, "orbital_carepackage_drone");
|
||||
|
||||
marker = Spawn( "script_model", DropPod.dropPoint + ( 0, 0, 5 ) );
|
||||
marker.angles = ( -90, 0, 0 );
|
||||
marker SetModel( "tag_origin" );
|
||||
marker Hide();
|
||||
marker ShowToPlayer( player );
|
||||
PlayFXOnTag( getfx( "ocp_ground_marker" ), marker, "tag_origin" );
|
||||
marker thread carePackageSetupMinimap( DropPod.modules, player );
|
||||
|
||||
maps\mp\killstreaks\_orbital_util::addDropMarker( marker );
|
||||
|
||||
if ( droneDelivery )
|
||||
player thread playerMonitorForDroneDelivery( podrocket, DropPod, marker, dropCrate );
|
||||
|
||||
dropCrate LinkTo( podrocket, "tag_origin", ( 0, 0, 0 ), ( -90, 0, 0 ) );
|
||||
|
||||
podrocket waittill( "death", attacker_ent, means_of_death, weapon_name );
|
||||
|
||||
if ( IsDefined( podrocket ) && !droneDelivery && podrocket.origin[2] > DropPod.dropPoint[2] && DistanceSquared( podrocket.origin, DropPod.dropPoint ) > DROPPOINT_DISTANCE_SQ )
|
||||
{
|
||||
if ( DropPod.giveBackCarepackage )
|
||||
{
|
||||
if ( isdefined ( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
outsidePosition = [[level.hordeGetOutsidePosition]]();
|
||||
FirePod( "orbital_carepackage_pod_mp", self, outsidePosition, "horde_support_drop", DropPod.modules, false, undefined, undefined, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IsDefined( player ) )
|
||||
player playerGiveBackCarepackage( DropPod );
|
||||
}
|
||||
}
|
||||
level thread cleanupCarepackage( DropPod, dropCrate, marker );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( droneDelivery && allowDroneDelivery(player) && IsDefined( DropPod.drone ) )
|
||||
{
|
||||
DropPod.drone Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
Earthquake( 0.4, 1, DropPod.dropPoint, 800 );
|
||||
PlayRumbleOnPosition( "artillery_rumble", DropPod.dropPoint );
|
||||
}
|
||||
|
||||
KillFXOnTag( getfx( "ocp_ground_marker" ), marker, "tag_origin" );
|
||||
|
||||
DropPod.alive = false;
|
||||
|
||||
if ( droneDelivery && allowDroneDelivery(player) && IsDefined( DropPod.drone ) )
|
||||
{
|
||||
DropPod.drone waittill( "delivered" );
|
||||
dropCrate SetContents( dropCrate.oldContents );
|
||||
dropCrate.oldContents = undefined;
|
||||
}
|
||||
|
||||
marker thread carepackageCleanup( dropCrate );
|
||||
|
||||
dropCrate CloneBrushmodelToScriptmodel( level.airDropCrateCollision );
|
||||
|
||||
// Below adds physics to crate
|
||||
dropCrate.droppingToGround = true;
|
||||
|
||||
dropCrate Unlink();
|
||||
dropCrate PhysicsLaunchServer( (0,0,0) );
|
||||
dropCrate thread crateDetectStopPhysics();
|
||||
dropCrate thread orbitalPhysicsWaiter( droptype, crateType, player );
|
||||
|
||||
level thread RemovePod(dropCrate, DropPod);
|
||||
}
|
||||
|
||||
crateImpactCleanup( player ) // self = dropCrate
|
||||
{
|
||||
if ( !IsDefined( player ) )
|
||||
return;
|
||||
|
||||
nearestNodes = GetNodesInRadiusSorted( self.origin, 300, 0, 300 );
|
||||
|
||||
foreach ( character in level.characters )
|
||||
{
|
||||
if ( !IsAlive( character ) )
|
||||
continue;
|
||||
|
||||
if ( IsAlliedSentient( character, player ) )
|
||||
{
|
||||
// Any allies that are touching the clip should be teleported out of its way
|
||||
if ( character IsTouching( self ) )
|
||||
{
|
||||
foreach ( node in nearestNodes )
|
||||
{
|
||||
if ( DistanceSquared( node.origin, self.origin ) > 100 * 100 )
|
||||
{
|
||||
character SetOrigin( node.origin, true );
|
||||
nearestNodes = array_remove( nearestNodes, node );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crateDetectStopPhysics()
|
||||
{
|
||||
self endon( "physics_finished" );
|
||||
self endon( "death" );
|
||||
|
||||
SECONDS_TO_STOP_PHYSICS = 4;
|
||||
NUM_FRAMES_TO_STOP_PHYSICS = SECONDS_TO_STOP_PHYSICS / 0.05;
|
||||
DIST_NOT_MOVING_SQ = 5 * 5;
|
||||
|
||||
numFrames = 0;
|
||||
lastOrigin = self.origin;
|
||||
while ( true )
|
||||
{
|
||||
waitframe();
|
||||
|
||||
distSq = DistanceSquared( lastOrigin, self.origin );
|
||||
if ( distSq < DIST_NOT_MOVING_SQ )
|
||||
numFrames++;
|
||||
else
|
||||
numFrames = 0;
|
||||
|
||||
lastOrigin = self.origin;
|
||||
|
||||
if ( numFrames >= NUM_FRAMES_TO_STOP_PHYSICS )
|
||||
{
|
||||
self PhysicsStop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playerGiveBackCarepackage( DropPod ) // self == player
|
||||
{
|
||||
streakVal = self maps\mp\killstreaks\_killstreaks::getStreakCost( "orbital_carepackage" );
|
||||
slotIndex = self maps\mp\killstreaks\_killstreaks::getNextKillstreakSlotIndex( "orbital_carepackage", false );
|
||||
self thread maps\mp\gametypes\_hud_message::killstreakSplashNotify( "orbital_carepackage", streakVal, undefined, DropPod.modules, slotIndex );
|
||||
self thread maps\mp\killstreaks\_killstreaks::giveKillstreak( "orbital_carepackage", false, false, self, DropPod.modules );
|
||||
}
|
||||
|
||||
cleanupCarepackage( DropPod, dropCrate, marker )
|
||||
{
|
||||
if ( IsDefined( dropCrate ) )
|
||||
{
|
||||
self thread RemovePod(dropCrate, DropPod);
|
||||
dropCrate Delete();
|
||||
}
|
||||
|
||||
if ( IsDefined( DropPod.drone ) )
|
||||
DropPod.drone maps\mp\killstreaks\_drone_carepackage::carepackageDrone_remove();
|
||||
|
||||
if ( IsDefined( marker ) )
|
||||
{
|
||||
if ( IsDefined( marker.objIdFriendly ) )
|
||||
_objective_delete( marker.objIdFriendly );
|
||||
if ( IsDefined( marker.objIdEnemy ) )
|
||||
_objective_delete( marker.objIdEnemy );
|
||||
KillFXOnTag( getfx( "ocp_ground_marker" ), marker, "tag_origin" );
|
||||
waitframe();
|
||||
marker Delete();
|
||||
}
|
||||
}
|
||||
|
||||
orbitalPhysicsWaiter( droptype, crateType, player )
|
||||
{
|
||||
self endon("death");
|
||||
self maps\mp\killstreaks\_airdrop::physicsWaiter( droptype, crateType );
|
||||
|
||||
self PlaySound("orbital_pkg_panel");
|
||||
|
||||
if(IsDefined(self.enemymodel))
|
||||
{
|
||||
self.enemymodel thread orbitalAnimate();
|
||||
self.enemyModel Solid();
|
||||
}
|
||||
if(IsDefined(self.friendlymodel))
|
||||
{
|
||||
self.friendlymodel thread orbitalAnimate();
|
||||
self.friendlymodel Solid();
|
||||
}
|
||||
|
||||
self thread crateImpactCleanup( player );
|
||||
}
|
||||
|
||||
orbitalAnimate( alreadyOpen )
|
||||
{
|
||||
self endon("death");
|
||||
|
||||
if ( !IsDefined( alreadyOpen ) || !alreadyOpen )
|
||||
wait .75;
|
||||
|
||||
if ( IsDefined( alreadyOpen ) && alreadyOpen )
|
||||
self ScriptModelPlayAnim("orbital_care_package_open_loop");
|
||||
else
|
||||
self ScriptModelPlayAnim("orbital_care_package_open");
|
||||
|
||||
PlayFXOnTag( getfx("ocp_glow"), self, "TAG_ORIGIN");
|
||||
|
||||
if ( !IsDefined( alreadyOpen ) || !alreadyOpen )
|
||||
{
|
||||
waitframe();
|
||||
|
||||
PlayFXOnTag( getfx("vfx_ocp_steam2"), self, "TAG_FX_PANEL_F");
|
||||
PlayFXOnTag( getfx("vfx_ocp_steam2"), self, "TAG_FX_PANEL_K");
|
||||
|
||||
waitframe();
|
||||
|
||||
PlayFXOnTag( getfx("vfx_ocp_steam"), self, "TAG_FX_PANEL_FR");
|
||||
PlayFXOnTag( getfx("vfx_ocp_steam"), self, "TAG_FX_PANEL_KL");
|
||||
|
||||
waitframe();
|
||||
|
||||
PlayFXOnTag( getfx("vfx_ocp_steam"), self, "TAG_FX_PANEL_FL");
|
||||
PlayFXOnTag( getfx("vfx_ocp_steam"), self, "TAG_FX_PANEL_KR");
|
||||
}
|
||||
}
|
||||
|
||||
delayCleanupDropPod( dropPod )
|
||||
{
|
||||
wait 5;
|
||||
dropPod Delete();
|
||||
}
|
||||
|
||||
RemovePod(Crate, DropPod)
|
||||
{
|
||||
/////// In theory removes all left over meshes and and removes structs from the array on the player...
|
||||
|
||||
Crate waittill("death");
|
||||
wait(15);
|
||||
for(i = 0; i < level._orbital_care_pod.size; i++)
|
||||
{
|
||||
if(IsDefined(level._orbital_care_pod[i]) && level._orbital_care_pod[i] == DropPod)
|
||||
{
|
||||
if(level._orbital_care_pod[i].alive == false)
|
||||
{
|
||||
level._orbital_care_pod[i] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(IsDefined(DropPod))
|
||||
{
|
||||
DropPod = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup of drop pod in the air
|
||||
carePackageSetupMinimap( modules, carrier ) // self == fake care package
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
if ( array_contains( modules, "orbital_carepackage_hide" ) )
|
||||
return;
|
||||
|
||||
//setup owner team
|
||||
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
Objective_Add( curObjID, "invisible", ( 0, 0, 0 ) );
|
||||
Objective_Position( curObjID, self.origin );
|
||||
Objective_State( curObjID, "active" );
|
||||
|
||||
shaderName = "compass_objpoint_ammo_friendly";
|
||||
Objective_Icon( curObjID, shaderName );
|
||||
|
||||
if ( !level.teamBased )
|
||||
Objective_PlayerTeam( curObjId, carrier GetEntityNumber() );
|
||||
else
|
||||
Objective_Team( curObjID, carrier.team );
|
||||
|
||||
self.objIdFriendly = curObjID;
|
||||
|
||||
// setup enemy team
|
||||
if( ! ( IsDefined ( level.isHorde ) && level.isHorde ) )
|
||||
{
|
||||
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
Objective_Add( curObjID, "invisible", ( 0, 0, 0 ) );
|
||||
Objective_Position( curObjID, self.origin );
|
||||
Objective_State( curObjID, "active" );
|
||||
Objective_Icon( curObjID, "compass_objpoint_ammo_enemy" );
|
||||
|
||||
if ( !level.teamBased )
|
||||
Objective_PlayerEnemyTeam( curObjId, carrier GetEntityNumber() );
|
||||
else
|
||||
Objective_Team( curObjID, level.otherTeam[ carrier.team ] );
|
||||
|
||||
self.objIdEnemy = curObjID;
|
||||
}
|
||||
|
||||
if ( array_contains( modules, "orbital_carepackage_drone" ) )
|
||||
{
|
||||
self waittill( "linkedToDrone" );
|
||||
|
||||
Objective_OnEntity( self.objIdFriendly, self );
|
||||
if ( IsDefined( self.objIdEnemy ) )
|
||||
{
|
||||
Objective_OnEntity( self.objIdEnemy, self );
|
||||
self Show(); // show to all now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
carepackageCleanup( dropCrate ) // self == fake care package
|
||||
{
|
||||
dropCrate waittill_any( "physics_finished", "death" );
|
||||
|
||||
if ( IsDefined( self.objIdFriendly ) )
|
||||
_objective_delete( self.objIdFriendly );
|
||||
|
||||
if ( IsDefined( self.objIdEnemy ) )
|
||||
_objective_delete( self.objIdEnemy );
|
||||
|
||||
KillFXOnTag( getfx("ocp_glow"), self, "TAG_ORIGIN");
|
||||
|
||||
if( IsDefined ( level.isHorde ) && level.isHorde )
|
||||
dropCrate notify ( "drop_pod_cleared" );
|
||||
|
||||
waitframe();
|
||||
|
||||
self Delete();
|
||||
}
|
||||
|
||||
setupDamageCallback( crate )
|
||||
{
|
||||
crate.health = 500;
|
||||
crate.maxhealth = crate.health;
|
||||
crate.readyToDie = false;
|
||||
setupDamageCallbackInternal( crate.friendlyModel );
|
||||
setupDamageCallbackInternal( crate.enemyModel );
|
||||
}
|
||||
|
||||
setupDamageCallbackInternal( crate )
|
||||
{
|
||||
crate thread maps\mp\gametypes\_damage::setEntityDamageCallback( 9999, undefined, undefined, ::crateHandleDamageCallback, true );
|
||||
}
|
||||
|
||||
disableDamageCallback( crate )
|
||||
{
|
||||
disableDamageCallbackInternal( crate.friendlyModel );
|
||||
disableDamageCallbackInternal( crate.enemyModel );
|
||||
}
|
||||
|
||||
disableDamageCallbackInternal( crate )
|
||||
{
|
||||
crate.damageCallback = undefined;
|
||||
crate SetCanDamage( false );
|
||||
crate SetDamageCallbackOn( false );
|
||||
}
|
||||
|
||||
crateHandleDamageCallback( attacker, weapon, type, damage )
|
||||
{
|
||||
parentCrate = self;
|
||||
if ( IsDefined( self.parentCrate ) )
|
||||
parentCrate = self.parentCrate;
|
||||
|
||||
finalDamage = self maps\mp\gametypes\_damage::modifyDamage( attacker, weapon, type, damage );
|
||||
|
||||
parentCrate.health -= finalDamage;
|
||||
|
||||
if ( parentCrate.health <= 0 )
|
||||
{
|
||||
// don't actually let code destroy it
|
||||
disableDamageCallback( parentCrate );
|
||||
parentCrate notify( "disabled" );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
playerMonitorForDroneDelivery( podRocket, dropPod, marker, dropCrate )
|
||||
{
|
||||
self endon( "disconenct" );;
|
||||
self endon( "joined_team" );
|
||||
self endon( "joined_spectators" );
|
||||
|
||||
SCR_CONST_REACHED_NODE_RADIUS_SQ = 24 * 24;
|
||||
DIST_TO_GROUND_SQ = 500 * 500;
|
||||
targetPos = dropPod.dropPoint;
|
||||
curPos = podRocket.origin;
|
||||
distToGroundSq = DistanceSquared( curPos, targetPos );
|
||||
|
||||
setupDamageCallback( dropCrate );
|
||||
drone = dropPod.drone;
|
||||
drone thread carepackageDroneWatchCrateDeath( dropCrate );
|
||||
dropCrate.oldContents = dropCrate SetContents( 0 );
|
||||
dropCrate.friendlyModel Solid();
|
||||
dropCrate.enemyModel Solid();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( !IsDefined( podRocket ) )
|
||||
break;
|
||||
|
||||
curPos = podRocket.origin;
|
||||
distToGroundSq = DistanceSquared( curPos, targetPos );
|
||||
|
||||
if ( distToGroundSq <= DIST_TO_GROUND_SQ )
|
||||
break;
|
||||
|
||||
waitframe();
|
||||
}
|
||||
|
||||
if ( distToGroundSq > DIST_TO_GROUND_SQ )
|
||||
{
|
||||
if ( dropPod.giveBackCarepackage && allowDroneDelivery( self ) )
|
||||
self playerGiveBackCarepackage( dropPod );
|
||||
level thread cleanupCarepackage( dropPod, dropCrate, marker );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !IsDefined( self ) )
|
||||
{
|
||||
level thread cleanupCarepackage( dropPod, dropCrate, marker );
|
||||
return;
|
||||
}
|
||||
|
||||
// if EMP'd abort on drone delivery
|
||||
if ( !allowDroneDelivery( self ) )
|
||||
{
|
||||
level thread cleanupCarepackage( dropCrate, undefined, undefined );
|
||||
return;
|
||||
}
|
||||
|
||||
drone thread carepackageDroneWatchDeath();
|
||||
drone endon( "death" );
|
||||
|
||||
drone Vehicle_Teleport( dropCrate.origin, dropCrate.angles, false, false );
|
||||
dropCrate LinkTo( drone, "tag_origin", ( 0, 0, 0 ), ( 0, 0, 0 ) );
|
||||
dropCrate.friendlyModel ScriptModelPlayAnim( "orbital_care_package_fan_spin", "nothing" );
|
||||
dropCrate.enemyModel ScriptModelPlayAnim( "orbital_care_package_fan_spin", "nothing" );
|
||||
maps\mp\killstreaks\_drone_carepackage::setupCarepackageDrone( drone, true );
|
||||
drone.crate = dropCrate;
|
||||
if ( IsDefined( podRocket ) )
|
||||
{
|
||||
curPos = podRocket.origin;
|
||||
podRocket notify( "death" );
|
||||
podRocket Delete();
|
||||
}
|
||||
playSoundAtPos( curPos, "orbital_pkg_pod_midair_exp" );
|
||||
PlayFX( getfx( "ocp_midair" ), curPos, GetDvarVector("scr_ocp_forward", ( 0, 0, -1 ) ) );
|
||||
drone thread drone_thrusterFX();
|
||||
|
||||
goalPos = DropPod.dropPoint + NODE_OFFSET;
|
||||
|
||||
drone SetVehGoalPos( goalPos, true );
|
||||
drone Vehicle_SetSpeedImmediate( GetDvarFloat( "scr_ocp_dropspeed", 30 ), GetDvarFloat( "scr_ocp_dropa", 20 ), GetDvarFloat( "scr_ocp_dropd", 1 ) );
|
||||
drone SetHoverParams( 30, 5, 5 );
|
||||
drone SetMaxPitchRoll( 15, 15 );
|
||||
|
||||
while ( DistanceSquared( drone.origin, goalPos ) > SCR_CONST_REACHED_NODE_RADIUS_SQ && dropCrate.health > 0 )
|
||||
waitframe();
|
||||
|
||||
if ( dropCrate.health > 0 )
|
||||
wait 1;
|
||||
|
||||
if ( dropCrate.health > 0 )
|
||||
{
|
||||
marker LinkTo( drone, "tag_origin" );
|
||||
marker notify( "linkedToDrone" );
|
||||
|
||||
drone thread maps\mp\killstreaks\_drone_carepackage::carepackageDrone_deleteOnActivate();
|
||||
drone carepackageDroneFindOwner();
|
||||
}
|
||||
|
||||
disableDamageCallback( dropCrate );
|
||||
dropCrate PlaySoundOnMovingEnt( "orbital_pkg_drone_jets_off" );
|
||||
|
||||
if ( IsDefined( drone ) )
|
||||
drone drone_stopThrusterEffects();
|
||||
dropCrate.friendlyModel ScriptModelClearAnim( "orbital_care_package_fan_spin", "nothing" );
|
||||
dropCrate.enemyModel ScriptModelClearAnim( "orbital_care_package_fan_spin", "nothing" );
|
||||
|
||||
waitframe();
|
||||
|
||||
if ( IsDefined( drone ) )
|
||||
drone maps\mp\killstreaks\_drone_carepackage::carepackageDrone_Delete();
|
||||
}
|
||||
|
||||
carepackageDroneWatchDeath() // self == drone
|
||||
{
|
||||
self endon( "delivered" );
|
||||
|
||||
self waittill( "death" );
|
||||
self notify( "delivered" );
|
||||
}
|
||||
|
||||
carepackageDroneWatchCrateDeath( crate ) // self == drone
|
||||
{
|
||||
self endon( "delivered" );
|
||||
|
||||
crate waittill( "disabled" );
|
||||
self notify( "delivered" );
|
||||
}
|
||||
|
||||
/#
|
||||
carepackageDroneDebugPathing() // self == drone
|
||||
{
|
||||
owner = self.owner;
|
||||
owner endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "delivered" );
|
||||
|
||||
toOwner = true;
|
||||
goalClient = self.owner;
|
||||
|
||||
SetDvarIfUninitialized( "scr_ocp_debugDeliveryNext", "0" );
|
||||
|
||||
DIST_BEFORE_DELIVERY_SQ = ( 150 * 150 );
|
||||
|
||||
restartPathing = true;
|
||||
nextGoalTime = GetTime();
|
||||
|
||||
while ( GetDvar( "scr_ocp_debugDelivery", "0" ) != "0" )
|
||||
{
|
||||
while ( true )
|
||||
{
|
||||
alive = isReallyAlive( goalClient );
|
||||
if ( !alive )
|
||||
{
|
||||
restartPathing = true;
|
||||
waitframe();
|
||||
}
|
||||
|
||||
if ( nextGoalTime < GetTime() || restartPathing )
|
||||
{
|
||||
restartPathing = false;
|
||||
self SetDroneGoalPos( goalClient, FLY_NODE_OFFSET + ( 0, -100, 0 ) );
|
||||
nextGoalTime = GetTime() + 1000;
|
||||
}
|
||||
|
||||
distSq = DistanceSquared( self.origin, goalClient.origin + FLY_NODE_OFFSET );
|
||||
|
||||
if ( distSq < DIST_BEFORE_DELIVERY_SQ )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
|
||||
while ( GetDvar( "scr_ocp_debugDeliveryNext" ) == "0" )
|
||||
waitframe();
|
||||
|
||||
SetDvar( "scr_ocp_debugDeliveryNext", "0" );
|
||||
|
||||
toOwner = !toOwner;
|
||||
|
||||
if ( !toOwner && IsDefined( level.players[1] ) )
|
||||
{
|
||||
goalClient = level.players[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
goalClient = owner;
|
||||
toOwner = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
carepackageDroneFindOwner() // self == drone
|
||||
{
|
||||
owner = self.owner;
|
||||
owner endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "delivered" );
|
||||
|
||||
/#
|
||||
if ( GetDvar( "scr_ocp_debugDelivery", "0" ) != "0" )
|
||||
{
|
||||
self carepackageDroneDebugPathing();
|
||||
}
|
||||
#/
|
||||
|
||||
DIST_BEFORE_DELIVERY_SQ = ( 150 * 150 );
|
||||
|
||||
restartPathing = true;
|
||||
nextGoalTime = GetTime();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
ownerAlive = isReallyAlive( owner );
|
||||
if ( !ownerAlive )
|
||||
{
|
||||
restartPathing = true;
|
||||
waitframe();
|
||||
}
|
||||
|
||||
if ( nextGoalTime < GetTime() || restartPathing )
|
||||
{
|
||||
restartPathing = false;
|
||||
self SetDroneGoalPos( owner, FLY_NODE_OFFSET + ( 0, -100, 0 ) );
|
||||
nextGoalTime = GetTime() + 1000;
|
||||
}
|
||||
|
||||
distSq = DistanceSquared( self.origin, owner.origin + FLY_NODE_OFFSET );
|
||||
|
||||
if ( distSq < DIST_BEFORE_DELIVERY_SQ )
|
||||
{
|
||||
wait GetDvarFloat( "scr_ocp_waitDeliver", 1 );
|
||||
self notify( "delivered" );
|
||||
return;
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
drone_thrusterFX()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
PlayFXOnTag( getfx( "ocp_thruster_small" ), self, "j_thruster_fl" );
|
||||
PlayFXOnTag( getfx( "ocp_thruster_small" ), self, "j_thruster_fr" );
|
||||
PlayFXOnTag( getfx( "ocp_thruster_small" ), self, "j_thruster_kl" );
|
||||
PlayFXOnTag( getfx( "ocp_thruster_small" ), self, "j_thruster_kr" );
|
||||
waitframe();
|
||||
waitframe();
|
||||
if ( IsDefined( self ) )
|
||||
PlayFXOnTag( getfx( "ocp_exhaust" ), self, "tag_fx" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
self thread drone_thrusterPlayerConnected( player );
|
||||
}
|
||||
}
|
||||
|
||||
drone_thrusterPlayerConnected( player )
|
||||
{
|
||||
player endon( "disconnect" );
|
||||
|
||||
player waittill( "spawned_player" );
|
||||
|
||||
if ( IsDefined( player ) && IsDefined( self ) )
|
||||
self drone_thrusterPlayer( player );
|
||||
}
|
||||
|
||||
drone_thrusterPlayer( player ) // self == drone
|
||||
{
|
||||
player endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
|
||||
PlayFXOnTagForClients( getfx( "ocp_thruster_small" ), self, "j_thruster_fl", player );
|
||||
PlayFXOnTagForClients( getfx( "ocp_thruster_small" ), self, "j_thruster_fr", player );
|
||||
PlayFXOnTagForClients( getfx( "ocp_thruster_small" ), self, "j_thruster_kl", player );
|
||||
PlayFXOnTagForClients( getfx( "ocp_thruster_small" ), self, "j_thruster_kr", player );
|
||||
waitframe();
|
||||
waitframe();
|
||||
if ( IsDefined( self ) )
|
||||
PlayFXOnTagForClients( getfx( "ocp_exhaust" ), self, "tag_fx", player );
|
||||
}
|
||||
|
||||
drone_stopThrusterEffects() // self == drone
|
||||
{
|
||||
KillFXOnTag( getfx( "ocp_thruster_small" ), self, "j_thruster_fl" );
|
||||
KillFXOnTag( getfx( "ocp_thruster_small" ), self, "j_thruster_fr" );
|
||||
KillFXOnTag( getfx( "ocp_thruster_small" ), self, "j_thruster_kl" );
|
||||
KillFXOnTag( getfx( "ocp_thruster_small" ), self, "j_thruster_kr" );
|
||||
waitframe();
|
||||
waitframe();
|
||||
if ( IsDefined( self ) )
|
||||
KillFXOnTag( getfx( "ocp_exhaust" ), self, "tag_fx" );
|
||||
}
|
||||
|
||||
/#
|
||||
debugDestroyCarepackages()
|
||||
{
|
||||
while ( true )
|
||||
{
|
||||
if ( GetDvar( "scr_ocp_destroyall", "0" ) != "0" )
|
||||
{
|
||||
SetDvar( "scr_ocp_destroyall", "0" );
|
||||
foreach ( crate in level.carePackages )
|
||||
crate maps\mp\killstreaks\_airdrop::deleteCrate();
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
#/
|
1632
raw/maps/mp/killstreaks/_orbital_strike.gsc
Normal file
1632
raw/maps/mp/killstreaks/_orbital_strike.gsc
Normal file
File diff suppressed because it is too large
Load Diff
945
raw/maps/mp/killstreaks/_orbital_util.gsc
Normal file
945
raw/maps/mp/killstreaks/_orbital_util.gsc
Normal file
@ -0,0 +1,945 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
ORBITAL_BULLETTRACE_MAX_PER_FRAME = 5;
|
||||
ORBITAL_TRACE_DIST = 24000;
|
||||
ORBITAL_TRACE_CP_RADIUS = 26;
|
||||
ORBITAL_TRACE_G_RADIUS = 41;
|
||||
ORBITAL_TRACE_GROUND_OFFSET = ( 0, 0, 6 );
|
||||
ORBITAL_DIST_FROM_PLAYER = 500;
|
||||
ORBITAL_FIND_NODE_MAX_LINKS = 6;
|
||||
|
||||
initStart()
|
||||
{
|
||||
level.orbital_util_remote_traces_frame = 0;
|
||||
level.orbital_util_remote_traces = ORBITAL_BULLETTRACE_MAX_PER_FRAME;
|
||||
|
||||
level.orbital_util_capsule_traces_frame = 0;
|
||||
level.orbital_util_capsule_traces = ORBITAL_BULLETTRACE_MAX_PER_FRAME;
|
||||
|
||||
level.orbital_util_last_trace = 0;
|
||||
|
||||
level thread deleteMapRemoteMissileClip();
|
||||
|
||||
level.orbital_util_covered_volumes = GetEntArray( "orbital_node_covered", "targetname" );
|
||||
/#
|
||||
nodes = GetAllNodes();
|
||||
if ( !NodeHasRemoteMissileSet( nodes[0] ) )
|
||||
PrintLn( "WARNING: remoteMissileSpawn entities are either missing or do not have script_noteworthy set to 0, 1, 2, 3, or 4." );
|
||||
#/
|
||||
}
|
||||
|
||||
deleteMapRemoteMissileClip()
|
||||
{
|
||||
clipbrushes = GetEntArray( "carepackage_clip", "targetname" );
|
||||
foreach ( brush in clipbrushes )
|
||||
brush Delete();
|
||||
}
|
||||
|
||||
playerGetOutsideNode( type ) // self == player
|
||||
{
|
||||
if ( !IsDefined( type ) )
|
||||
type = "goliath";
|
||||
|
||||
node = self playerGetNodeLookingAt( type );
|
||||
if ( !IsDefined( node ) )
|
||||
return;
|
||||
|
||||
self.lastNodeLookingAtTrace = undefined;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
playerGetOrbitalStartPos( node, type ) // self == player
|
||||
{
|
||||
if ( !IsDefined( type ) )
|
||||
type = "goliath";
|
||||
|
||||
remoteMissileSpawns = maps\mp\killstreaks\_aerial_utility::getEntOrStructArray( "remoteMissileSpawn" , "targetname" );
|
||||
org = nodeGetRemoteMissileOrigin( node, remoteMissileSpawns, type );
|
||||
if ( IsDefined( org ) )
|
||||
return org;
|
||||
else
|
||||
return nodeGetRemoteMissileOrgFromAbove( node );
|
||||
}
|
||||
|
||||
getStartPositionAbove( node )
|
||||
{
|
||||
return ( node.origin + ( 0, 0, ORBITAL_TRACE_DIST ) );
|
||||
}
|
||||
|
||||
addDropMarker( markerEnt, type )
|
||||
{
|
||||
if ( !IsDefined( type ) )
|
||||
type = "goliath";
|
||||
|
||||
markerEnt.orbitalType = type;
|
||||
|
||||
level.orbitalDropMarkers[level.orbitalDropMarkers.size] = markerEnt;
|
||||
|
||||
thread _addDropMarkerInternal( markerEnt );
|
||||
}
|
||||
|
||||
playerPlayInvalidPositionEffect( effectRef ) // self == player
|
||||
{
|
||||
trace = self.lastNodeLookingAtTrace;
|
||||
nearestNode = self.lastNearestNode;
|
||||
if ( !IsDefined( trace ) )
|
||||
{
|
||||
playerDir = AnglesToForward( self GetPlayerAngles() );
|
||||
start = self GetEye();
|
||||
end = start + ( playerDir * ORBITAL_DIST_FROM_PLAYER );
|
||||
trace = BulletTrace( start, end, false, self, true, false, false, false, false );
|
||||
}
|
||||
self.lastNodeLookingAtTrace = undefined;
|
||||
self.lastNearestNode = undefined;
|
||||
|
||||
effectOrg = trace["position"];
|
||||
|
||||
// might be looking at a wall so determine whether to use the trace position or this node
|
||||
if ( IsDefined( nearestNode ) )
|
||||
{
|
||||
normal = trace[ "normal" ];
|
||||
isGroundFlat = normal[2] > 0.8;
|
||||
if ( !isGroundFlat )
|
||||
effectOrg = nearestNode.origin;
|
||||
}
|
||||
|
||||
marker = Spawn( "script_model", effectOrg + ( 0, 0, 5 ) );
|
||||
marker.angles = ( -90, 0, 0 );
|
||||
marker SetModel( "tag_origin" );
|
||||
marker Hide();
|
||||
marker ShowToPlayer( self );
|
||||
PlayFXOnTag( effectRef, marker, "tag_origin" );
|
||||
|
||||
wait 5;
|
||||
|
||||
marker Delete();
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Internal
|
||||
|
||||
playerGetNodeLookingAt( type ) // self == player
|
||||
{
|
||||
// Test if player is looking at something
|
||||
playerDir = AnglesToForward( self GetPlayerAngles() );
|
||||
start = self GetEye();
|
||||
end = start + ( playerDir * ORBITAL_DIST_FROM_PLAYER );
|
||||
trace = BulletTrace( start, end, false, self, true, false, false, false, false );
|
||||
self.lastNodeLookingAtTrace = trace;
|
||||
lookingAway = ( trace[ "fraction" ] == 1 );
|
||||
/# debugPlacementLine( start, start + ( playerDir * ORBITAL_DIST_FROM_PLAYER * trace[ "fraction" ] ), ( 0, 1, 0 ), ( 1, 0, 0 ), lookingAway ); #/
|
||||
if ( lookingAway )
|
||||
return playerGetNearestNode( undefined, type );
|
||||
|
||||
// Test if position at has nodes nearby (is in playspace)
|
||||
position = trace[ "position" ];
|
||||
nodes = GetNodesInRadius( position, 128, 0, 60 );
|
||||
outOfPlaySpace = ( nodes.size == 0 );
|
||||
if ( outOfPlaySpace ) // any nodes within the players use radius
|
||||
return playerGetNearestNode( undefined, type );
|
||||
|
||||
// Test if position is on the ground
|
||||
normal = trace[ "normal" ];
|
||||
isGroundFlat = normal[2] > 0.8;
|
||||
/# debugPlacementLine( position, position + ( normal * 20 ), ( 0, 1, 0 ), ( 1, 0, 0 ), !isGroundFlat ); #/
|
||||
if ( !isGroundFlat )
|
||||
return playerGetNearestNode( position, type );
|
||||
|
||||
// Test if the position is in a 'covered' area
|
||||
if ( IsDefined( level.orbital_util_covered_volumes ) && level.orbital_util_covered_volumes.size > 0 )
|
||||
{
|
||||
pointInVolume = false;
|
||||
foreach ( volume in level.orbital_util_covered_volumes )
|
||||
{
|
||||
pointInVolume = IsPointInVolume( position, volume );
|
||||
if ( pointInVolume )
|
||||
break;
|
||||
}
|
||||
/# debugPlacementSphere( position, 5.0, ( 0, 1, 0 ), ( 1, 0, 0 ), pointInVolume ); #/
|
||||
if ( pointInVolume )
|
||||
return playerGetNearestNode( position, type );
|
||||
}
|
||||
|
||||
// Adding an additional test for Goliath drop pods. We want to restrict landing zones with trigger volumes with targetname, goliath_bad_landing_volume.
|
||||
if ( type == "goliath" )
|
||||
{
|
||||
if ( goliathBadLandingCheck( position ) )
|
||||
{
|
||||
return playerGetNearestNode( position, type );
|
||||
}
|
||||
}
|
||||
|
||||
// Test if the carepackage will fit here
|
||||
tracePassed = carepackageTrace( position, self, type );
|
||||
/#
|
||||
radius = ORBITAL_TRACE_G_RADIUS;
|
||||
if ( type == "carepackage" )
|
||||
radius = ORBITAL_TRACE_CP_RADIUS;
|
||||
debugPlacementSphere( position, radius, ( 0, 1, 0 ), ( 1, 0, 0 ), !tracePassed );
|
||||
#/
|
||||
if ( !tracePassed )
|
||||
return playerGetNearestNode( position, type );
|
||||
|
||||
// Test if one of 4 corners of the carepackage/pod is hanging off of the edge of a roof. Reject this case because the projectile could miss the edge of a roof.
|
||||
if ( groundPositionOffEdge( position, type ) )
|
||||
return playerGetNearestNode( position, type );
|
||||
|
||||
// Test if the carepackage can reach this position from a remoteMissileSpawn
|
||||
node = SpawnStruct();
|
||||
node.origin = position;
|
||||
remoteMissileSpawns = maps\mp\killstreaks\_aerial_utility::getEntOrStructArray( "remoteMissileSpawn" , "targetname" );
|
||||
org = nodeGetRemoteMissileOrigin( node, remoteMissileSpawns, type );
|
||||
if ( !IsDefined( org ) )
|
||||
return playerGetNearestNode( position, type );
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
groundPositionOffEdge( position, type )
|
||||
{
|
||||
if ( type == "goliath" )
|
||||
radius = ORBITAL_TRACE_G_RADIUS;
|
||||
else
|
||||
radius = ORBITAL_TRACE_CP_RADIUS;
|
||||
|
||||
xPos = ( radius, 0, 0 );
|
||||
xNeg = -1*xPos;
|
||||
yPos = ( 0, radius, 0 );
|
||||
yNeg = -1*yPos;
|
||||
|
||||
endOffset = ( 0, 0, -10 );
|
||||
|
||||
testDirections = [xPos, xNeg, yPos, yNeg];
|
||||
foreach(dir in testDirections)
|
||||
{
|
||||
start = position + dir;
|
||||
end = position + dir + endOffset;
|
||||
tracePassed = BulletTracePassed( start, end, false, undefined );
|
||||
if( tracePassed )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_nodeFindNewRemoteMissileOrg( node, remoteMissileSpawns, type )
|
||||
{
|
||||
ent = nodeFindRemoteMissleEnt( node, remoteMissileSpawns, type );
|
||||
if ( IsDefined( ent ) )
|
||||
{
|
||||
/# debugNodeFindNewRemoteMissileEnt( node, ent.script_noteworthy ); #/
|
||||
return nodeGetRemoteMissleEntOrg( node, remoteMissileSpawns );
|
||||
}
|
||||
|
||||
org = nodeTestFireFromAbove( node, type );
|
||||
if ( IsDefined( org ) )
|
||||
{
|
||||
/# debugNodeFindNewRemoteMissileEnt( node, "up" ); #/
|
||||
return nodeGetRemoteMissileOrgFromAbove( node );
|
||||
}
|
||||
else
|
||||
{
|
||||
/# debugNodeFindNewRemoteMissileEnt( node, "none" ); #/
|
||||
}
|
||||
}
|
||||
|
||||
nodeGetRemoteMissileOrigin( node, remoteMissileSpawns, type )
|
||||
{
|
||||
/#
|
||||
if ( GetDvarInt( "scr_remoteMissile_redo_node", 0 ) != 0 )
|
||||
{
|
||||
if ( nodeHasRemoteMissileDataSet( node ) )
|
||||
return _nodeFindNewRemoteMissileOrg( node, remoteMissileSpawns, type );
|
||||
|
||||
return;
|
||||
}
|
||||
#/
|
||||
if ( nodeHasRemoteMissileDataSet( node ) )
|
||||
{
|
||||
if ( !nodeIsRemoteMissileFromAbove( node ) )
|
||||
return nodeGetRemoteMissleEntOrg( node, remoteMissileSpawns );
|
||||
else
|
||||
return nodeGetRemoteMissileOrgFromAbove( node );
|
||||
}
|
||||
else
|
||||
{
|
||||
return _nodeFindNewRemoteMissileOrg( node, remoteMissileSpawns, type );
|
||||
}
|
||||
}
|
||||
|
||||
nodeIsPathnode( node )
|
||||
{
|
||||
return ( IsDefined( node.type ) );
|
||||
}
|
||||
|
||||
nodeIsRemoteMissileFromAbove( node )
|
||||
{
|
||||
return ( ( nodeIsPathnode( node ) && NodeHasRemoteMissileSet( node ) && NodeGetRemoteMissileName( node ) == "up" ) || IsDefined( node.bestMissileSpawnAbove ) );
|
||||
}
|
||||
|
||||
nodeHasRemoteMissileDataSet( node )
|
||||
{
|
||||
return ( ( nodeIsPathnode( node ) && NodeHasRemoteMissileSet( node ) ) || ( IsDefined( node.bestMissileSpawnAbove ) || IsDefined( node.bestMissileSpawn ) ) );
|
||||
}
|
||||
|
||||
nodeGetRemoteMissileOrgFromAbove( node )
|
||||
{
|
||||
return getStartPositionAbove( node );
|
||||
}
|
||||
|
||||
nodeTestFireFromAbove( node, type )
|
||||
{
|
||||
org = getStartPositionAbove( node );
|
||||
|
||||
passed = remoteMissileEntTraceToOriginPassedWrapper( org, node.origin, type );
|
||||
/# debugPlacementLine( org, node.origin, ( 0, 1, 0 ), ( 1, 0, 0 ), !passed ); #/
|
||||
if ( passed )
|
||||
{
|
||||
node.bestMissileSpawnAbove = org;
|
||||
return org;
|
||||
}
|
||||
}
|
||||
|
||||
nodeGetRemoteMissleEntOrg( node, remoteMissileSpawns )
|
||||
{
|
||||
remoteMissileEnt = undefined;
|
||||
if ( nodeIsPathnode( node ) && NodeHasRemoteMissileSet( node ) )
|
||||
{
|
||||
name = NodeGetRemoteMissileName( node );
|
||||
foreach ( ent in remoteMissileSpawns )
|
||||
{
|
||||
if ( IsDefined( ent.script_noteworthy ) && ent.script_noteworthy == name )
|
||||
remoteMissileEnt = ent;
|
||||
}
|
||||
}
|
||||
else if ( IsDefined( node.bestMissileSpawn ) )
|
||||
{
|
||||
remoteMissileEnt = node.bestMissileSpawn;
|
||||
}
|
||||
|
||||
Assert( IsDefined( remoteMissileEnt ) );
|
||||
|
||||
dir = VectorNormalize( remoteMissileEnt.origin - node.origin );
|
||||
return ( node.origin + ( dir * ORBITAL_TRACE_DIST ) );
|
||||
}
|
||||
|
||||
nodeFindRemoteMissleEnt( node, remoteMissileSpawns, type )
|
||||
{
|
||||
remoteMissileSpawns = SortByDistance( remoteMissileSpawns, node.origin );
|
||||
foreach ( ent in remoteMissileSpawns )
|
||||
{
|
||||
passed = remoteMissileEntTraceToOriginPassedWrapper( ent.origin, node.origin, type );
|
||||
/# debugPlacementLine( ent.origin, node.origin, ( 0, 1, 0 ), ( 1, 0, 0 ), !passed ); #/
|
||||
if ( passed )
|
||||
{
|
||||
node.bestMissileSpawn = ent;
|
||||
return ent;
|
||||
}
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
remoteMissileEntTraceToOriginPassedWrapper( remoteMissileSpawnOrigin, groundOrigin, type )
|
||||
{
|
||||
if ( level.orbital_util_remote_traces_frame != GetTime() )
|
||||
{
|
||||
level.orbital_util_remote_traces_frame = GetTime();
|
||||
level.orbital_util_remote_traces = ORBITAL_BULLETTRACE_MAX_PER_FRAME;
|
||||
}
|
||||
|
||||
if ( level.orbital_util_remote_traces <= 0 )
|
||||
{
|
||||
if ( level.orbital_util_last_trace != GetTime() )
|
||||
{
|
||||
waitframe();
|
||||
level.orbital_util_last_trace = GetTime();
|
||||
}
|
||||
level.orbital_util_remote_traces = ORBITAL_BULLETTRACE_MAX_PER_FRAME;
|
||||
}
|
||||
|
||||
level.orbital_util_remote_traces--;
|
||||
|
||||
radius = ORBITAL_TRACE_CP_RADIUS;
|
||||
if ( type == "goliath" )
|
||||
radius = ORBITAL_TRACE_G_RADIUS;
|
||||
|
||||
// expensive
|
||||
return RemoteMissileEntTraceToOriginPassed( remoteMissileSpawnOrigin, groundOrigin, radius, true );
|
||||
}
|
||||
|
||||
nodeCanHitGround( node, type )
|
||||
{
|
||||
if ( IsDefined( type ) && type == "goliath" )
|
||||
{
|
||||
if ( goliathBadLandingCheck( node.origin ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ( NodeHasRemoteMissileSet( node ) )
|
||||
return ( NodeGetRemoteMissileName( node ) != "none" );
|
||||
else
|
||||
return NodeExposedToSky( node, true );
|
||||
}
|
||||
|
||||
carepackageTrace( position, player, type )
|
||||
{
|
||||
HEIGHT = 100;
|
||||
if ( type == "goliath" )
|
||||
radius = ORBITAL_TRACE_G_RADIUS;
|
||||
else
|
||||
radius = ORBITAL_TRACE_CP_RADIUS;
|
||||
|
||||
foreach ( marker in level.orbitalDropMarkers )
|
||||
{
|
||||
dist = radius;
|
||||
if ( marker.orbitalType == "goliath" )
|
||||
dist += ORBITAL_TRACE_G_RADIUS;
|
||||
else
|
||||
dist += ORBITAL_TRACE_CP_RADIUS;
|
||||
distSqMin = dist * dist;
|
||||
distSq = Distance2DSquared( marker.origin, position );
|
||||
if ( distSq < distSqMin )
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( level.orbital_util_capsule_traces_frame != GetTime() )
|
||||
{
|
||||
level.orbital_util_capsule_traces_frame = GetTime();
|
||||
level.orbital_util_capsule_traces = ORBITAL_BULLETTRACE_MAX_PER_FRAME;
|
||||
}
|
||||
|
||||
if ( level.orbital_util_capsule_traces <= 0 )
|
||||
{
|
||||
if ( level.orbital_util_last_trace != GetTime() )
|
||||
{
|
||||
waitframe();
|
||||
level.orbital_util_last_trace = GetTime();
|
||||
}
|
||||
level.orbital_util_capsule_traces = ORBITAL_BULLETTRACE_MAX_PER_FRAME;
|
||||
}
|
||||
|
||||
level.orbital_util_capsule_traces--;
|
||||
|
||||
return CapsuleTracePassed( position + ORBITAL_TRACE_GROUND_OFFSET, radius, radius * 2, player, false );
|
||||
}
|
||||
|
||||
playerGetNearestNode( point, type )
|
||||
{
|
||||
if ( !IsDefined( point ) )
|
||||
{
|
||||
DIST = 300;
|
||||
start = self GetEye();
|
||||
dir = AnglesToForward( self.angles );
|
||||
end = start + ( dir * DIST );
|
||||
trace = BulletTrace( start, end, false, self );
|
||||
|
||||
point = end;
|
||||
if ( trace[ "fraction" ] < 1 )
|
||||
point = start + ( dir * DIST * trace[ "fraction" ] );
|
||||
}
|
||||
|
||||
nearestNode = GetClosestNodeInSight( point, true );
|
||||
nearestNodeValid = IsDefined( nearestNode );
|
||||
if ( nearestNodeValid )
|
||||
{
|
||||
nearestNodeValid = nodeCanHitGround( nearestNode, type ) && carepackageTrace( nearestNode.origin, self, type );
|
||||
/#debugPlacementBox( nearestNode.origin + ( 0, 0, 20 ), ( 0, 1, 0 ), ( 1, 0, 0 ), !nearestNodeValid );#/
|
||||
}
|
||||
|
||||
if ( nearestNodeValid )
|
||||
return nearestNode;
|
||||
|
||||
|
||||
// try some traces to find a node the player can see
|
||||
result = SpawnStruct();
|
||||
result.maxTracesPerFrame = 5;
|
||||
result.maxNodes = 20;
|
||||
result.numTraces = 5;
|
||||
self playerFindNodeInFront( point, type, result );
|
||||
bestNode = result.nearestNode;
|
||||
if ( IsDefined( bestNode ) )
|
||||
return bestNode;
|
||||
|
||||
if ( !IsDefined( nearestNode ) )
|
||||
{
|
||||
nearestNode = self playerGetClosestNode( 500, 100, self.origin, false, true, type );
|
||||
if ( !IsDefined( nearestNode ) )
|
||||
nearestNode = self playerGetClosestNode( 500, 0, self.origin, false, false, type );
|
||||
if ( !IsDefined( nearestNode ) )
|
||||
nearestNode = self GetNearestNode();
|
||||
}
|
||||
|
||||
self.lastNearestNode = nearestNode;
|
||||
|
||||
// player can't see a close node, so find a nearby aerial node, possibly out of fov
|
||||
if ( IsDefined( nearestNode ) )
|
||||
return playerFindAltNode( nearestNode, type );
|
||||
}
|
||||
|
||||
goliathBadLandingCheck( pos ) // returns true if pos is in a bad landing volume for Goliaths.
|
||||
{
|
||||
if ( IsDefined( level.goliath_bad_landing_volumes ) )
|
||||
{
|
||||
foreach ( trig_volume in level.goliath_bad_landing_volumes )
|
||||
{
|
||||
if ( IsPointInVolume( pos, trig_volume ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
playerFindNodeInFront( point, type, result )
|
||||
{
|
||||
maxDistSearch = ORBITAL_DIST_FROM_PLAYER;
|
||||
minDistSearch = 100;
|
||||
|
||||
nodeFound = playerFindNodeInFrontInternal( point, minDistSearch, maxDistSearch, type, result );
|
||||
|
||||
if ( !IsDefined( nodeFound ) && result.maxNodes > 0 )
|
||||
{
|
||||
minDistSearch = 0;
|
||||
|
||||
nodeFound = playerFindNodeInFrontInternal( point, minDistSearch, maxDistSearch, type, result );
|
||||
}
|
||||
|
||||
result.nearestNode = nodeFound;
|
||||
}
|
||||
|
||||
playerFindNodeInFrontInternal( point, minDistSearch, maxDistSearch, type, result )
|
||||
{
|
||||
while ( minDistSearch < maxDistSearch && result.maxNodes > 0 )
|
||||
{
|
||||
nextNode = self playerGetClosestNode( maxDistSearch, minDistSearch, point, true, true, type );
|
||||
|
||||
if ( result.numTraces <= 0 && !traceDoneRecently() )
|
||||
{
|
||||
waitframe();
|
||||
result.numTraces = result.maxTracesPerFrame;
|
||||
}
|
||||
|
||||
if ( IsDefined( nextNode ) )
|
||||
{
|
||||
result.numTraces--;
|
||||
result.maxNodes--;
|
||||
start = self GetEye();
|
||||
end = nextNode.origin + ORBITAL_TRACE_GROUND_OFFSET;
|
||||
trace = BulletTrace( start, end, false, self );
|
||||
|
||||
nextNodeValid = ( trace[ "fraction" ] == 1 ) && carepackageTrace( nextNode.origin, self, type );
|
||||
/#debugPlacementBox( nextNode.origin + ( 0, 0, 20 ), ( 0, 1, 0 ), ( 1, 0, 0 ), !nextNodeValid );#/
|
||||
if ( nextNodeValid )
|
||||
return nextNode;
|
||||
|
||||
minDistSearch = Distance( point, nextNode.origin ) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
minDistSearch = maxDistSearch + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playerFindAltNode( nearestNode, type )
|
||||
{
|
||||
// prof_begin( "playerFindAltNodeOrigin" );
|
||||
bestNode = checkNodeStart( nearestNode, self, type );
|
||||
// prof_end( "playerFindAltNodeOrigin" );
|
||||
|
||||
if ( IsDefined( bestNode ) )
|
||||
{
|
||||
if ( type == "goliath" )
|
||||
{
|
||||
if ( goliathBadLandingCheck( bestNode.origin ) )
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return bestNode;
|
||||
}
|
||||
}
|
||||
|
||||
traceDoneRecently()
|
||||
{
|
||||
return ( level.orbital_util_last_trace == GetTime() );
|
||||
}
|
||||
|
||||
checkNodeStart( startNode, player, type )
|
||||
{
|
||||
MAX_DIST_SQ = ORBITAL_DIST_FROM_PLAYER * ORBITAL_DIST_FROM_PLAYER;
|
||||
NODES_PER_FRAME = 20;
|
||||
|
||||
startNode.linkDistance = 0;
|
||||
startNode.nodeChecked = true;
|
||||
nodeContainer = SpawnStruct();
|
||||
nodeContainer.nodesToCheck = [];
|
||||
nodeContainer.nodesChecked = [];
|
||||
nodeContainer.nodesChecked["" + startNode GetNodeNumber()] = startNode;
|
||||
nodeContainer.nextNodes = GetLinkedNodes( startNode, true);
|
||||
addNodesToBeChecked( nodeContainer, 1, startNode, MAX_DIST_SQ, player, type );
|
||||
numNodes = 0;
|
||||
while ( true )
|
||||
{
|
||||
nextNode = getNextNode( nodeContainer );
|
||||
if ( IsDefined( nextNode ) )
|
||||
{
|
||||
numNodes++;
|
||||
if ( !carepackageTrace( nextNode.origin, player, type ) )
|
||||
{
|
||||
nextNode.nodeChecked = true;
|
||||
nodeContainer.nodesToCheck["" + nextNode GetNodeNumber()] = undefined;
|
||||
nodeContainer.nodesChecked["" + nextNode GetNodeNumber()] = nextNode;
|
||||
/# drawBadPathDebug( nextNode ); #/
|
||||
nextLinkDistance = nextNode.linkDistance + 1;
|
||||
|
||||
if ( nextLinkDistance <= ORBITAL_FIND_NODE_MAX_LINKS )
|
||||
{
|
||||
nodeContainer.nextNodes = GetLinkedNodes( nextNode, true );
|
||||
addNodesToBeChecked( nodeContainer, nextLinkDistance, nextNode, MAX_DIST_SQ, player, type );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cleanupNodeFields( nodeContainer );
|
||||
/# drawParentPathDebug( nextNode ); #/
|
||||
/# removeNodeParents( nodeContainer ); #/
|
||||
return nextNode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
cleanupNodeFields( nodeContainer );
|
||||
//PrintLn( "NUMNODES: " + numNodes );
|
||||
/# removeNodeParents( nodeContainer ); #/
|
||||
return;
|
||||
}
|
||||
|
||||
if ( numNodes >= NODES_PER_FRAME )
|
||||
{
|
||||
if ( !traceDoneRecently() )
|
||||
waitframe();
|
||||
numNodes = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanupNodeFields( nodeContainer )
|
||||
{
|
||||
foreach ( node in nodeContainer.nodesToCheck )
|
||||
{
|
||||
node.linkDistance = undefined;
|
||||
node.nodeChecked = undefined;
|
||||
}
|
||||
foreach ( node in nodeContainer.nodesChecked )
|
||||
{
|
||||
node.linkDistance = undefined;
|
||||
node.nodeChecked = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
getNextNode( nodeContainer )
|
||||
{
|
||||
if ( nodeContainer.nodesToCheck.size == 0 )
|
||||
return;
|
||||
|
||||
bestNode = undefined;
|
||||
bestLinkDistance = undefined;
|
||||
keys = GetArrayKeys( nodeContainer.nodesToCheck );
|
||||
for ( i = 0; i < keys.size; i++ )
|
||||
{
|
||||
node = nodeContainer.nodesToCheck[keys[i]];
|
||||
if ( !IsDefined( bestNode ) || node.linkDistance < bestLinkDistance )
|
||||
{
|
||||
bestNode = node;
|
||||
bestLinkDistance = node.linkDistance;
|
||||
}
|
||||
}
|
||||
|
||||
return bestNode;
|
||||
}
|
||||
|
||||
addNodesToBeChecked( nodeContainer, linkDistance, parentNode, maxDistSq, player, type )
|
||||
{
|
||||
for ( i = 0; i < nodeContainer.nextNodes.size; i++ )
|
||||
{
|
||||
node = nodeContainer.nextNodes[i];
|
||||
if ( !IsDefined( node.nodeChecked ) )
|
||||
{
|
||||
validNode = nodeCanHitGround( node, type );
|
||||
|
||||
if ( validNode )
|
||||
{
|
||||
distSq = DistanceSquared( node.origin, player.origin );
|
||||
validNode = distSq < maxDistSq;
|
||||
}
|
||||
|
||||
if ( !validNode )
|
||||
{
|
||||
node.nodeChecked = true;
|
||||
nodeContainer.nodesChecked["" + node GetNodeNumber()] = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !IsDefined( node.linkDistance ) )
|
||||
{
|
||||
node.linkDistance = linkDistance;
|
||||
nodeContainer.nodesToCheck["" + node GetNodeNumber()] = node;
|
||||
/# addNodeParent( node, parentNode ); #/
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( node.linkDistance > linkDistance )
|
||||
{
|
||||
node.linkDistance = linkDistance;
|
||||
/# addNodeParent( node, parentNode ); #/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playerGetClosestNode( maxRadius, minRadius, point, checkExposed, checkPlayerFov, type )
|
||||
{
|
||||
if ( !IsDefined( maxRadius ) )
|
||||
maxRadius = 1500;
|
||||
if ( !IsDefined( minRadius ) )
|
||||
minRadius = 0;
|
||||
if ( !IsDefined( point ) )
|
||||
point = self.origin;
|
||||
|
||||
radiusIncrement = 100;
|
||||
|
||||
// want to be sure to get the closest node because if you make the radius too large and get too many nodes then
|
||||
// native code may skip the close ones so limit to a radius of 100 at a time
|
||||
nextMinRadius = minRadius;
|
||||
nextMaxRadius = minRadius + radiusIncrement;
|
||||
|
||||
if ( nextMaxRadius > maxRadius )
|
||||
nextMaxRadius = maxRadius;
|
||||
|
||||
while ( nextMaxRadius <= maxRadius && nextMinRadius < maxRadius )
|
||||
{
|
||||
node = self playerGetClosestNodeInternal( nextMaxRadius, nextMinRadius, point, checkExposed, checkPlayerFov, type );
|
||||
|
||||
if ( IsDefined( node ) )
|
||||
return node;
|
||||
|
||||
nextMinRadius += radiusIncrement;
|
||||
nextMaxRadius += radiusIncrement;
|
||||
|
||||
if ( nextMaxRadius > maxRadius )
|
||||
nextMaxRadius = maxRadius;
|
||||
}
|
||||
}
|
||||
|
||||
playerGetClosestNodeInternal( maxRadius, minRadius, point, checkExposed, checkPlayerFov, type )
|
||||
{
|
||||
valid = true;
|
||||
|
||||
nodes = GetNodesInRadiusSorted( point, maxRadius, minRadius, 120, "path" );
|
||||
for ( i = 0; i < nodes.size; i++ )
|
||||
{
|
||||
if ( checkExposed )
|
||||
valid = valid & nodeCanHitGround( nodes[ i ], type );
|
||||
if ( checkPlayerFov )
|
||||
valid = valid & self playerWithinFOV2D( nodes[ i ].origin );
|
||||
|
||||
///#
|
||||
// if ( !valid )
|
||||
// debugPlacementBox( nodes[i].origin, ( 1, 0.6, 0 ), ( 1, 1, 0 ), !withinFov );
|
||||
//#/
|
||||
|
||||
if ( valid )
|
||||
return nodes[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
playerWithinFov2D( origin )
|
||||
{
|
||||
fov = cos( 60 );
|
||||
normal = VectorNormalize( ( origin[ 0 ], origin[ 1 ], 0 ) - ( self.origin[ 0 ], self.origin[ 1 ], 0 ) );
|
||||
forward = AnglesToForward( ( 0, self.angles[ 1 ], 0 ) );
|
||||
return VectorDot( forward, normal ) >= fov;
|
||||
}
|
||||
|
||||
_addDropMarkerInternal( markerEnt )
|
||||
{
|
||||
markerEnt waittill( "death" );
|
||||
|
||||
level.orbitalDropMarkers = array_remove( level.orbitalDropMarkers, markerEnt );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: nodeSetRemoteMissileNameWrapper( <origin> , <name> )"
|
||||
"Summary: Changes the remote missile entity of this node to use in a patch situation. Turn on the following dvars: \ai_shownodes 16, \set scr_remoteMissile_redo_node 1. Give yourself a goliath. Point at a suspect node and call it down. If the number changes then check the output for the script change."
|
||||
"CallOn: level"
|
||||
"MandatoryArg: <origin>: the origin of the pathnode"
|
||||
"MandatoryArg: <name>: Remote missile ent name. Valid names are "0", "1", "2", "3", "4", "up", "none""
|
||||
"Example: maps\mp\killstreaks\_orbital_util::nodeSetRemoteMissileNameWrapper( (-1249.3, 828.9, 2202.63), "1" );"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
nodeSetRemoteMissileNameWrapper( origin, name )
|
||||
{
|
||||
AssertEx( name == "0" || name == "1" || name == "2" || name == "3" || name == "4" || name == "up" || name == "none" );
|
||||
|
||||
nodes = GetNodesInRadiusSorted( origin , 24, 0 );
|
||||
|
||||
if ( nodes.size > 0 )
|
||||
{
|
||||
node = nodes[0];
|
||||
|
||||
NodeSetRemoteMissileName( node, name );
|
||||
}
|
||||
else
|
||||
{
|
||||
/# PrintLn( "Error: nodeSetRemoteMissileNameWrapper() could not find a node at origin: " + origin ); #/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// DEBUG
|
||||
|
||||
/#
|
||||
addNodeParent( node, parent )
|
||||
{
|
||||
if ( GetDvar( "scr_orbital_path_debug", "0" ) != "0" )
|
||||
{
|
||||
node.parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
removeNodeParents( nodeContainer )
|
||||
{
|
||||
if ( GetDvar( "scr_orbital_path_debug", "0" ) != "0" )
|
||||
{
|
||||
foreach ( node in nodeContainer.nodesToCheck )
|
||||
node.parent = undefined;
|
||||
foreach ( node in nodeContainer.nodesChecked )
|
||||
node.parent = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
drawBadPathDebug( node )
|
||||
{
|
||||
if ( GetDvar( "scr_orbital_path_debug", "0" ) != "0" )
|
||||
{
|
||||
OFFSET = ( 0, 0, 2 );
|
||||
if ( IsDefined( node.parent ) )
|
||||
Line( node.origin + OFFSET, node.parent.origin + OFFSET, ( 1, 0, 0 ), 1, 0, GetDvarInt( "scr_orbital_path_debug_frames", 1000 ) );
|
||||
Sphere( node.origin + OFFSET, 4.0, ( 0, 0, 1 ), 0, GetDvarInt( "scr_orbital_path_debug_frames", 1000 ) );
|
||||
}
|
||||
}
|
||||
|
||||
drawParentPathDebug( node )
|
||||
{
|
||||
if ( GetDvar( "scr_orbital_path_debug", "0" ) != "0" )
|
||||
{
|
||||
OFFSET = ( 0, 0, 10 );
|
||||
numLinks = 0;
|
||||
previousNode = node;
|
||||
nextNode = node.parent;
|
||||
Sphere( previousNode.origin + OFFSET, 5.0, ( 0, 1, 1 ), 0, GetDvarInt( "scr_orbital_path_debug_frames", 1000 ) );
|
||||
Print( "PATH DEBUG: Nodes = " + previousNode GetNodeNumber() );
|
||||
while ( IsDefined( nextNode ) && numLinks <= ORBITAL_FIND_NODE_MAX_LINKS )
|
||||
{
|
||||
numLinks++;
|
||||
Line( previousNode.origin + OFFSET, nextNode.origin + OFFSET, ( 0, 1, 0 ), 1, 0, GetDvarInt( "scr_orbital_path_debug_frames", 1000 ) );
|
||||
Sphere( nextNode.origin + OFFSET, 5.0, ( 0, 1, 1 ), 0, GetDvarInt( "scr_orbital_path_debug_frames", 1000 ) );
|
||||
Print( " <- " + nextNode GetNodeNumber() );
|
||||
previousNode = nextNode;
|
||||
nextNode = nextNode.parent;
|
||||
}
|
||||
PrintLn( "" );
|
||||
PrintLn( "PATH DEBUG: num links: " + numLinks );
|
||||
}
|
||||
}
|
||||
|
||||
debugBestNode( node )
|
||||
{
|
||||
if ( !IsDefined( node ) )
|
||||
return;
|
||||
|
||||
if ( GetDvar( "scr_goliath_debug_placement", "0" ) != "0" )
|
||||
{
|
||||
debugPlacementBox( node.origin + ( 0, 0, 20 ), ( 0, 1, 0 ) );
|
||||
nextNode = node.parentNode;
|
||||
prevNode = node;
|
||||
while ( IsDefined( nextNode ) )
|
||||
{
|
||||
debugPlacementBox( nextNode.origin + ( 0, 0, 20 ), ( 0, 0, 1 ) );
|
||||
if ( IsDefined( prevNode ) )
|
||||
debugPlacementLine( nextNode.origin + ( 0, 0, 20 ), prevNode.origin + ( 0, 0, 20 ), ( 0, 1, 1 ) );
|
||||
|
||||
prevNode = nextNode;
|
||||
nextNode = nextNode.parentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debugPlacementLine( pos1, pos2, color1, color2, colorCondition )
|
||||
{
|
||||
if ( !IsDefined( colorCondition ) )
|
||||
colorCondition = false;
|
||||
|
||||
if ( GetDvar( "scr_goliath_debug_placement", "0" ) != "0" )
|
||||
{
|
||||
color = color1;
|
||||
if ( colorCondition )
|
||||
color = color2;
|
||||
Line( pos1, pos2, color, 1, 0, GetDvarInt( "scr_goliath_debug_placement_frames", 1000 ) );
|
||||
}
|
||||
}
|
||||
|
||||
debugPlacementSphere( pos, radius, color1, color2, colorCondition )
|
||||
{
|
||||
if ( !IsDefined( colorCondition ) )
|
||||
colorCondition = false;
|
||||
|
||||
if ( GetDvar( "scr_goliath_debug_placement", "0" ) != "0" )
|
||||
{
|
||||
color = color1;
|
||||
if ( colorCondition )
|
||||
color = color2;
|
||||
Sphere( pos, radius, color, 0, GetDvarInt( "scr_goliath_debug_placement_frames", 1000 ) );
|
||||
}
|
||||
}
|
||||
|
||||
debugPlacementBox( pos, color1, color2, colorCondition )
|
||||
{
|
||||
if ( !IsDefined( colorCondition ) )
|
||||
colorCondition = false;
|
||||
|
||||
if ( GetDvar( "scr_goliath_debug_placement", "0" ) != "0" )
|
||||
{
|
||||
color = color1;
|
||||
if ( colorCondition )
|
||||
color = color2;
|
||||
Box( pos, 0, color, 0, GetDvarInt( "scr_goliath_debug_placement_frames", 1000 ) );
|
||||
}
|
||||
}
|
||||
|
||||
debugNodeFindNewRemoteMissileEnt( node, remoteMissileEntName )
|
||||
{
|
||||
if ( GetDvarInt( "scr_remoteMissile_redo_node", 0 ) != 0 )
|
||||
{
|
||||
PrintLn( "*********************************************************************" );
|
||||
PrintLn( "To change this nodes remote missile ent add this to your level gsc: " );
|
||||
Println( "maps\\mp\\killstreaks\\_orbital_util::nodeSetRemoteMissileNameWrapper( " + node.origin + ", \"" + remoteMissileEntName + "\" );" );
|
||||
PrintLn( "*********************************************************************" );
|
||||
|
||||
NodeSetRemoteMissileName( node, remoteMissileEntName );
|
||||
}
|
||||
}
|
||||
#/
|
2426
raw/maps/mp/killstreaks/_orbitalsupport.gsc
Normal file
2426
raw/maps/mp/killstreaks/_orbitalsupport.gsc
Normal file
File diff suppressed because it is too large
Load Diff
700
raw/maps/mp/killstreaks/_placeable.gsc
Normal file
700
raw/maps/mp/killstreaks/_placeable.gsc
Normal file
@ -0,0 +1,700 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
if ( !IsDefined( level.placeableConfigs ) )
|
||||
{
|
||||
level.placeableConfigs = [];
|
||||
}
|
||||
}
|
||||
|
||||
givePlaceable( streakName ) // self == player
|
||||
{
|
||||
placeable = self createPlaceable( streakName );
|
||||
|
||||
// returning from this streak activation seems to strip this?
|
||||
// manually removing and restoring
|
||||
self removePerks();
|
||||
|
||||
self.carriedItem = placeable;
|
||||
|
||||
result = self onBeginCarrying( streakName, placeable, true );
|
||||
|
||||
self.carriedItem = undefined;
|
||||
|
||||
self restorePerks();
|
||||
|
||||
// if the placeable exist, then it was placed
|
||||
return ( IsDefined( placeable ) );
|
||||
}
|
||||
|
||||
createPlaceable( streakName )
|
||||
{
|
||||
if( IsDefined( self.isCarrying ) && self.isCarrying )
|
||||
return;
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
obj = Spawn( "script_model", self.origin );
|
||||
obj SetModel( config.modelBase );
|
||||
obj.angles = self.angles;
|
||||
obj.owner = self;
|
||||
obj.team = self.team;
|
||||
obj.config = config;
|
||||
obj.firstPlacement = true;
|
||||
|
||||
/*
|
||||
obj = SpawnTurret( "misc_turret", self.origin + ( 0, 0, 25 ), "sentry_minigun_mp" );
|
||||
|
||||
obj.angles = self.angles;
|
||||
obj.owner = self;
|
||||
|
||||
obj SetModel( config.modelBase );
|
||||
|
||||
obj MakeTurretInoperable();
|
||||
obj SetTurretModeChangeWait( true );
|
||||
obj SetMode( "sentry_offline" );
|
||||
obj MakeUnusable();
|
||||
obj SetSentryOwner( self );
|
||||
*/
|
||||
|
||||
// inits happen here
|
||||
if ( IsDefined( config.onCreateDelegate ) )
|
||||
{
|
||||
obj [[ config.onCreateDelegate ]]( streakName );
|
||||
}
|
||||
|
||||
obj deactivate( streakName );
|
||||
|
||||
obj thread timeOut( streakName );
|
||||
obj thread handleUse( streakName );
|
||||
|
||||
obj thread onKillstreakDisowned( streakName );
|
||||
obj thread onGameEnded( streakName );
|
||||
|
||||
obj thread createBombSquadModel( streakName );
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
handleUse( streakName ) // self == placeable
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill ( "trigger", player );
|
||||
|
||||
assert( player == self.owner );
|
||||
assert( !IsDefined( self.carriedBy ) );
|
||||
|
||||
if ( !isReallyAlive( player ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( self GetLinkedParent() ) )
|
||||
{
|
||||
self Unlink();
|
||||
}
|
||||
|
||||
// why does the IMS create a second one?
|
||||
|
||||
player onBeginCarrying( streakName, self, false );
|
||||
}
|
||||
}
|
||||
|
||||
// setCarrying
|
||||
onBeginCarrying( streakName, placeable, allowCancel ) // self == player
|
||||
{
|
||||
self endon ( "death" );
|
||||
self endon ( "disconnect" );
|
||||
|
||||
assert( isReallyAlive( self ) );
|
||||
|
||||
placeable thread onCarried( streakName, self );
|
||||
|
||||
self _disableWeapon();
|
||||
|
||||
if ( !IsAI(self) ) // Bots handle these internally
|
||||
{
|
||||
self notifyOnPlayerCommand( "placePlaceable", "+attack" );
|
||||
self notifyOnPlayerCommand( "placePlaceable", "+attack_akimbo_accessible" ); // support accessibility control scheme
|
||||
self notifyOnPlayerCommand( "cancelPlaceable", "+actionslot 4" );
|
||||
if( !level.console )
|
||||
{
|
||||
self notifyOnPlayerCommand( "cancelPlaceable", "+actionslot 5" );
|
||||
self notifyOnPlayerCommand( "cancelPlaceable", "+actionslot 6" );
|
||||
self notifyOnPlayerCommand( "cancelPlaceable", "+actionslot 7" );
|
||||
self notifyOnPlayerCommand( "cancelPlaceable", "+actionslot 8" );
|
||||
}
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
result = waittill_any_return( "placePlaceable", "cancelPlaceable", "force_cancel_placement" );
|
||||
|
||||
// object was deleted
|
||||
if ( !IsDefined( placeable ) )
|
||||
{
|
||||
self _enableWeapon();
|
||||
return true;
|
||||
}
|
||||
// !!! 2013-08-08 wallace: this force cancel problem is really ugly
|
||||
// it's also being used to indicate player wading in water, which we should use the "bad placement" model instead of canceling outright. But it's too late to fix now.
|
||||
else if ( (result == "cancelPlaceable" && allowCancel)
|
||||
|| result == "force_cancel_placement" )
|
||||
{
|
||||
placeable onCancel( streakName, result == "force_cancel_placement" && !IsDefined( placeable.firstPlacement ) );
|
||||
return false;
|
||||
}
|
||||
else if ( placeable.canBePlaced )
|
||||
{
|
||||
placeable thread onPlaced( streakName );
|
||||
self _enableWeapon();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onCancel( streakName, playDestroyVfx ) // self == placeable
|
||||
{
|
||||
if( IsDefined( self.carriedBy ) )
|
||||
{
|
||||
owner = self.carriedBy;
|
||||
owner ForceUseHintOff();
|
||||
owner.isCarrying = undefined;
|
||||
|
||||
owner.carriedItem = undefined;
|
||||
|
||||
owner _enableWeapon();
|
||||
}
|
||||
|
||||
if( IsDefined( self.bombSquadModel ) )
|
||||
{
|
||||
self.bombSquadModel Delete();
|
||||
}
|
||||
|
||||
if ( IsDefined( self.carriedObj ) )
|
||||
{
|
||||
self.carriedObj Delete();
|
||||
}
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
if ( IsDefined( config.onCancelDelegate ) )
|
||||
{
|
||||
self [[ config.onCancelDelegate ]]( streakName );
|
||||
}
|
||||
|
||||
if ( IsDefined( playDestroyVfx ) && playDestroyVfx )
|
||||
{
|
||||
self maps\mp\gametypes\_weapons::equipmentDeleteVfx();
|
||||
}
|
||||
|
||||
self Delete();
|
||||
}
|
||||
|
||||
onPlaced( streakName ) // self == placeable
|
||||
{
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
self.origin = self.placementOrigin;
|
||||
self.angles = self.carriedObj.angles;
|
||||
|
||||
self PlaySound( config.placedSfx );
|
||||
|
||||
self showPlacedModel( streakName );
|
||||
|
||||
if ( IsDefined( config.onPlacedDelegate ) )
|
||||
{
|
||||
self [[ config.onPlacedDelegate ]]( streakName );
|
||||
}
|
||||
|
||||
self setCursorHint( "HINT_NOICON" );
|
||||
self setHintString( config.hintString );
|
||||
|
||||
owner = self.owner;
|
||||
owner ForceUseHintOff();
|
||||
owner.isCarrying = undefined;
|
||||
self.carriedBy = undefined;
|
||||
self.isPlaced = true;
|
||||
self.firstPlacement = undefined;
|
||||
|
||||
if ( IsDefined( config.headIconHeight ) )
|
||||
{
|
||||
if ( level.teamBased )
|
||||
{
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( self.team, (0,0,config.headIconHeight) );
|
||||
}
|
||||
else
|
||||
{
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( owner, (0,0,config.headIconHeight) );
|
||||
}
|
||||
}
|
||||
|
||||
self thread handleDamage( streakName );
|
||||
self thread handleDeath( streakName );
|
||||
|
||||
self MakeUsable();
|
||||
|
||||
self make_entity_sentient_mp( self.owner.team );
|
||||
if ( IsSentient( self ) )
|
||||
{
|
||||
self SetThreatBiasGroup( "DogsDontAttack" );
|
||||
}
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if( player == owner )
|
||||
self EnablePlayerUse( player );
|
||||
else
|
||||
self DisablePlayerUse( player );
|
||||
}
|
||||
|
||||
if( IsDefined( self.shouldSplash ) )
|
||||
{
|
||||
level thread teamPlayerCardSplash( config.splashName, owner );
|
||||
self.shouldSplash = false;
|
||||
}
|
||||
|
||||
// Moving platforms.
|
||||
data = SpawnStruct();
|
||||
data.linkParent = self.moving_platform;
|
||||
data.playDeathFx = true;
|
||||
data.endonString = "carried";
|
||||
if ( IsDefined( config.onMovingPlatformCollision ) )
|
||||
{
|
||||
data.deathOverrideCallback = config.onMovingPlatformCollision;
|
||||
}
|
||||
self thread maps\mp\_movers::handle_moving_platforms( data );
|
||||
|
||||
self thread watchPlayerConnected();
|
||||
|
||||
self notify ( "placed" );
|
||||
|
||||
self.carriedObj Delete();
|
||||
self.carriedObj = undefined;
|
||||
}
|
||||
|
||||
onCarried( streakName, carrier ) // self == placeable
|
||||
{
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
assert( isPlayer( carrier ) );
|
||||
assertEx( carrier == self.owner, "_placeable::onCarried: specified carrier does not own this ims" );
|
||||
|
||||
self.carriedObj = carrier createCarriedObject( streakName );
|
||||
|
||||
// self SetModel( config.modelPlacement );
|
||||
|
||||
self.isPlaced = undefined;
|
||||
self.carriedBy = carrier;
|
||||
carrier.isCarrying = true;
|
||||
|
||||
self deactivate( streakName );
|
||||
|
||||
self hidePlacedModel( streakName );
|
||||
|
||||
if ( IsDefined( config.onCarriedDelegate ) )
|
||||
{
|
||||
self [[ config.onCarriedDelegate ]]( streakName );
|
||||
}
|
||||
|
||||
self thread updatePlacement( streakName, carrier );
|
||||
|
||||
self thread onCarrierDeath( streakName, carrier );
|
||||
|
||||
self notify ( "carried" );
|
||||
}
|
||||
|
||||
updatePlacement( streakName, carrier ) // self == placeable
|
||||
{
|
||||
carrier endon ( "death" );
|
||||
carrier endon ( "disconnect" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
self endon ( "placed" );
|
||||
self endon ( "death" );
|
||||
|
||||
self.canBePlaced = true;
|
||||
prevCanBePlaced = -1; // force initial update
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
// allow the visuals be raised up slightly (e.g. iw6 Sat Com, so that player can see it)
|
||||
placementOffset = (0 ,0, 0);
|
||||
if ( IsDefined( config.placementOffsetZ ) )
|
||||
{
|
||||
placementOffset = (0 ,0 ,config.placementOffsetZ);
|
||||
}
|
||||
|
||||
carriedObj = self.carriedObj;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
placement = carrier CanPlayerPlaceSentry( true, config.placementRadius );
|
||||
|
||||
// NOTE TO SELF: Talk to Simon C about how to get vertical offset / additional rotation working with client prediction
|
||||
self.placementOrigin = placement[ "origin" ];
|
||||
carriedObj.origin = self.placementOrigin + placementOffset;
|
||||
carriedObj.angles = placement[ "angles" ];
|
||||
|
||||
self.canBePlaced = carrier IsOnGround()
|
||||
&& placement[ "result" ]
|
||||
&& ( abs(self.placementOrigin[2] - carrier.origin[2]) < config.placementHeightTolerance );
|
||||
|
||||
if ( isdefined( placement[ "entity" ] ) )
|
||||
{
|
||||
self.moving_platform = placement[ "entity" ];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.moving_platform = undefined;
|
||||
}
|
||||
|
||||
if ( self.canBePlaced != prevCanBePlaced )
|
||||
{
|
||||
if ( self.canBePlaced )
|
||||
{
|
||||
carriedObj SetModel( config.modelPlacement );
|
||||
carrier ForceUseHintOn( config.placeString );
|
||||
}
|
||||
else
|
||||
{
|
||||
carriedObj SetModel( config.modelPlacementFailed );
|
||||
carrier ForceUseHintOn( config.cannotPlaceString );
|
||||
}
|
||||
}
|
||||
|
||||
prevCanBePlaced = self.canBePlaced;
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
deactivate( streakName ) // self == placeable
|
||||
{
|
||||
self MakeUnusable();
|
||||
|
||||
self hideHeadIcons();
|
||||
|
||||
self FreeEntitySentient();
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
if ( IsDefined( config.onDeactiveDelegate ) )
|
||||
{
|
||||
self [[ config.onDeactiveDelegate ]]( streakName );
|
||||
}
|
||||
}
|
||||
|
||||
hideHeadIcons()
|
||||
{
|
||||
if ( level.teamBased )
|
||||
{
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( "none", ( 0, 0, 0 ) );
|
||||
}
|
||||
else if ( IsDefined( self.owner ) )
|
||||
{
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( undefined, ( 0, 0, 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
// important callbacks:
|
||||
// onDamagedDelegate - filter out or amplify damage based on specifics
|
||||
// onDestroyedDelegate - any extra handling when the object is killed
|
||||
handleDamage( streakName ) // self == placeable
|
||||
{
|
||||
self endon( "carried" );
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
self maps\mp\gametypes\_damage::setEntityDamageCallback(
|
||||
config.maxHealth,
|
||||
config.damageFeedback,
|
||||
::handleDeathDamage,
|
||||
::modifyDamage,
|
||||
true // isKillstreak
|
||||
);
|
||||
}
|
||||
|
||||
modifyDamage( attacker, weapon, type, damage )
|
||||
{
|
||||
modifiedDamage = damage;
|
||||
|
||||
config = self.config;
|
||||
if ( IsDefined( config.allowMeleeDamage ) && config.allowMeleeDamage )
|
||||
{
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMeleeDamage( weapon, type, modifiedDamage );
|
||||
}
|
||||
|
||||
if ( IsDefined( config.allowEmpDamage ) && config.allowEmpDamage )
|
||||
{
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleEmpDamage( weapon, type, modifiedDamage, attacker );
|
||||
}
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMissileDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleGrenadeDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleAPDamage( weapon, type, modifiedDamage, attacker );
|
||||
|
||||
if ( IsDefined( config.modifyDamage ) )
|
||||
{
|
||||
modifiedDamage = self [[ config.modifyDamage ]]( weapon, type, modifiedDamage );
|
||||
}
|
||||
|
||||
return modifiedDamage;
|
||||
}
|
||||
|
||||
handleDeathDamage( attacker, weapon, type, damage )
|
||||
{
|
||||
config = self.config;
|
||||
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, type, damage, config.xpPopup, config.destroyedVO );
|
||||
}
|
||||
|
||||
handleDeath( streakName )
|
||||
{
|
||||
self endon( "carried" );
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
// this handles cases of deletion
|
||||
if ( IsDefined( self ) )
|
||||
{
|
||||
// play sound
|
||||
|
||||
self deactivate( streakName );
|
||||
|
||||
// set destroyed model
|
||||
if ( IsDefined( config.modelDestroyed ) )
|
||||
{
|
||||
self SetModel( config.modelDestroyed );
|
||||
}
|
||||
|
||||
// or do it in the callbacks?
|
||||
|
||||
if ( IsDefined( config.onDeathDelegate ) )
|
||||
{
|
||||
self [[ config.onDeathDelegate ]]( streakName );
|
||||
}
|
||||
|
||||
self Delete();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
onCarrierDeath( streakName, carrier ) // self == placeable
|
||||
{
|
||||
self endon ( "placed" );
|
||||
self endon ( "death" );
|
||||
carrier endon( "disconnect" );
|
||||
|
||||
carrier waittill ( "death" );
|
||||
|
||||
if ( self.canBePlaced )
|
||||
{
|
||||
self thread onPlaced( streakName );
|
||||
}
|
||||
else
|
||||
{
|
||||
self onCancel( streakName );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onKillstreakDisowned( streakName ) // self == placeable
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
self.owner waittill ( "killstreak_disowned" );
|
||||
|
||||
self cleanup( streakName );
|
||||
}
|
||||
|
||||
onGameEnded( streakName ) // self == placeable
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
level waittill ( "game_ended" );
|
||||
|
||||
self cleanup( streakName );
|
||||
}
|
||||
|
||||
cleanup( streakName ) // self == placeable
|
||||
{
|
||||
if ( IsDefined( self.isPlaced ) )
|
||||
{
|
||||
self notify( "death" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self onCancel( streakName );
|
||||
}
|
||||
}
|
||||
|
||||
watchPlayerConnected() // self == ims
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
// when new players connect they need to not be able to use the planted ims
|
||||
level waittill( "connected", player );
|
||||
self thread onPlayerConnected( player );
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerConnected( owner ) // self == placeable
|
||||
{
|
||||
self endon( "death" );
|
||||
owner endon( "disconnect" );
|
||||
|
||||
owner waittill( "spawned_player" );
|
||||
|
||||
// this can't possibly be the owner because the ims is destroyed if the owner leaves the game, so disable use for this player
|
||||
self DisablePlayerUse( owner );
|
||||
}
|
||||
|
||||
timeOut( streakName )
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
lifeSpan = config.lifeSpan;
|
||||
|
||||
while ( lifeSpan > 0.0 )
|
||||
{
|
||||
wait ( 1.0 );
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
if ( !IsDefined( self.carriedBy ) )
|
||||
{
|
||||
lifeSpan -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( self.owner ) && IsDefined( config.goneVO ) )
|
||||
{
|
||||
self.owner thread leaderDialogOnPlayer( config.goneVO );
|
||||
}
|
||||
|
||||
self notify ( "death" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
removeWeapons()
|
||||
{
|
||||
if ( self HasWeapon( "iw6_riotshield_mp" ) )
|
||||
{
|
||||
self.restoreWeapon = "iw6_riotshield_mp";
|
||||
self takeWeapon( "iw6_riotshield_mp" );
|
||||
}
|
||||
}
|
||||
|
||||
removePerks()
|
||||
{
|
||||
if ( self _hasPerk( "specialty_explosivebullets" ) )
|
||||
{
|
||||
self.restorePerk = "specialty_explosivebullets";
|
||||
self _unsetPerk( "specialty_explosivebullets" );
|
||||
}
|
||||
}
|
||||
|
||||
restoreWeapons()
|
||||
{
|
||||
if ( IsDefined( self.restoreWeapon ) )
|
||||
{
|
||||
self _giveWeapon( self.restoreWeapon );
|
||||
self.restoreWeapon = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
restorePerks()
|
||||
{
|
||||
if ( IsDefined( self.restorePerk ) )
|
||||
{
|
||||
self givePerk( self.restorePerk, false );
|
||||
self.restorePerk = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
createBombSquadModel( streakName ) // self == box
|
||||
{
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
if ( IsDefined( config.modelBombSquad ) )
|
||||
{
|
||||
bombSquadModel = Spawn( "script_model", self.origin );
|
||||
bombSquadModel.angles = self.angles;
|
||||
bombSquadModel Hide();
|
||||
|
||||
bombSquadModel thread maps\mp\gametypes\_weapons::bombSquadVisibilityUpdater( self.owner );
|
||||
bombSquadModel SetModel( config.modelBombSquad );
|
||||
bombSquadModel LinkTo( self );
|
||||
bombSquadModel SetContents( 0 );
|
||||
self.bombSquadModel = bombSquadModel;
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
// Could have been deleted when the player was carrying the ims and died
|
||||
if ( IsDefined( bombSquadModel ) )
|
||||
{
|
||||
bombSquadModel delete();
|
||||
self.bombSquadModel = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showPlacedModel( streakname )
|
||||
{
|
||||
self Show();
|
||||
|
||||
if ( IsDefined( self.bombSquadModel ) )
|
||||
{
|
||||
self.bombSquadModel Show();
|
||||
level notify( "update_bombsquad" );
|
||||
}
|
||||
}
|
||||
|
||||
hidePlacedModel( streakName )
|
||||
{
|
||||
self Hide();
|
||||
|
||||
if ( IsDefined( self.bombSquadModel ) )
|
||||
{
|
||||
self.bombSquadModel Hide();
|
||||
}
|
||||
}
|
||||
|
||||
createCarriedObject( streakName )
|
||||
{
|
||||
assertEx( IsDefined( self ), "createIMSForPlayer() called without owner specified" );
|
||||
|
||||
// need to make sure we aren't already carrying, this fixes a bug where you could start to pull a new one out as you picked the old one up
|
||||
// this resulted in you being able to plant one and then pull your gun out while having another one attached to you like you're carrying it
|
||||
if( IsDefined( self.isCarrying ) && self.isCarrying )
|
||||
return;
|
||||
|
||||
carriedObj = SpawnTurret( "misc_turret", self.origin + ( 0, 0, 25 ), "sentry_minigun_mp" );
|
||||
|
||||
carriedObj.angles = self.angles;
|
||||
carriedObj.owner = self;
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
carriedObj SetModel( config.modelBase );
|
||||
|
||||
carriedObj MakeTurretInoperable();
|
||||
carriedObj SetTurretModeChangeWait( true );
|
||||
carriedObj SetMode( "sentry_offline" );
|
||||
carriedObj MakeUnusable();
|
||||
carriedObj SetSentryOwner( self );
|
||||
carriedObj SetSentryCarrier( self );
|
||||
|
||||
carriedObj SetCanDamage( false );
|
||||
carriedObj SetContents( 0 );
|
||||
|
||||
return carriedObj;
|
||||
}
|
2294
raw/maps/mp/killstreaks/_remoteturret.gsc
Normal file
2294
raw/maps/mp/killstreaks/_remoteturret.gsc
Normal file
File diff suppressed because it is too large
Load Diff
322
raw/maps/mp/killstreaks/_rippedturret.gsc
Normal file
322
raw/maps/mp/killstreaks/_rippedturret.gsc
Normal file
@ -0,0 +1,322 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
CONST_REMOTE_TURRET_SENTRY_RIPPABLE_AMMO_MG = 100;
|
||||
CONST_REMOTE_TURRET_SENTRY_RIPPABLE_AMMO_ROCKET = 6;
|
||||
CONST_REMOTE_TURRET_SENTRY_RIPPABLE_AMMO_ENERGY_SECONDS = 10;
|
||||
|
||||
init()
|
||||
{
|
||||
level.killStreakFuncs[ "ripped_turret" ] = ::tryUseRippedTurret;
|
||||
|
||||
level.killstreakWieldWeapons["turretheadmg_mp"] = "ripped_turret";
|
||||
level.killstreakWieldWeapons["turretheadenergy_mp"] = "ripped_turret";
|
||||
level.killstreakWieldWeapons["turretheadrocket_mp"] = "ripped_turret";
|
||||
|
||||
level thread onPlayerConnect();
|
||||
}
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
level thread onPlayerSpawned( player );
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerSpawned( player )
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
player waittill( "killstreakUseWaiter" );
|
||||
level thread updateAmmo( player );
|
||||
}
|
||||
}
|
||||
|
||||
updateAmmo( player )
|
||||
{
|
||||
player SetClientOmnvar( "ui_energy_ammo", 1 );
|
||||
|
||||
if( !IsDefined( player.pers[ "rippableSentry" ] ) )
|
||||
return;
|
||||
|
||||
weapon = undefined;
|
||||
|
||||
if( player HasWeapon( "turretheadmg_mp" ) )
|
||||
{
|
||||
weapon = "turretheadmg_mp";
|
||||
}
|
||||
else if( player HasWeapon( "turretheadenergy_mp" ) )
|
||||
{
|
||||
weapon = "turretheadenergy_mp";
|
||||
}
|
||||
else if( player HasWeapon( "turretheadrocket_mp" ) )
|
||||
{
|
||||
weapon = "turretheadrocket_mp";
|
||||
}
|
||||
|
||||
if( !IsDefined(weapon) )
|
||||
return;
|
||||
|
||||
ammo = player playerGetRippableAmmo();
|
||||
|
||||
if( isTurretEnergyWeapon( weapon ) )
|
||||
{
|
||||
fullEnergy = getAmmoForTurretWeaponType( weapon );
|
||||
percent = ammo / fullEnergy;
|
||||
player SetClientOmnvar( "ui_energy_ammo", percent );
|
||||
}
|
||||
else
|
||||
{
|
||||
player SetWeaponAmmoClip( weapon, ammo );
|
||||
}
|
||||
}
|
||||
|
||||
tryUseRippedTurret( lifeId, modules )
|
||||
{
|
||||
result = tryUseRippedTurretInternal( modules );
|
||||
|
||||
if( result )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "ripped_turret", self.origin );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
tryUseRippedTurretInternal( modules )
|
||||
{
|
||||
if ( self isUsingRemote() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Assert( self playerModulesHaveRippedTurret( modules ) && self playerHasRippableTurretInfo() );
|
||||
|
||||
result = self playerSetupRecordedTurretHead( modules );
|
||||
return result;
|
||||
}
|
||||
|
||||
playerGiveTurretHead( weapon )
|
||||
{
|
||||
self maps\mp\killstreaks\_killstreaks::giveKillstreak( "ripped_turret", false, false, self, [ weapon ] );
|
||||
|
||||
// Ammo is persistent to handle round switching. If this is
|
||||
// the first use init the self.per[ "aaLauncherAmmo" ] struct
|
||||
if ( !IsDefined( self.pers[ "rippableSentry" ] ) )
|
||||
{
|
||||
self.pers[ "rippableSentry" ] = SpawnStruct();
|
||||
}
|
||||
|
||||
ammo = getAmmoForTurretWeaponType( weapon );
|
||||
self playerRecordRippableAmmo( ammo );
|
||||
|
||||
if( !self is_player_gamepad_enabled() )
|
||||
{
|
||||
self notify( "streakUsed1" );
|
||||
waittillframeend;
|
||||
}
|
||||
|
||||
self SwitchToWeapon( weapon );
|
||||
}
|
||||
|
||||
playerModulesHaveRippedTurret( modules )
|
||||
{
|
||||
foreach ( module in modules )
|
||||
{
|
||||
if ( module == "turretheadenergy_mp" ||
|
||||
module == "turretheadrocket_mp" ||
|
||||
module == "turretheadmg_mp" )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
playerSetupRecordedTurretHead( modules )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
ammo = self playerGetRippableAmmo();
|
||||
weapon = modules[0];
|
||||
|
||||
if ( !isTurretEnergyWeapon( weapon ) )
|
||||
self SetWeaponAmmoClip( weapon, ammo );
|
||||
self SetWeaponAmmoStock( weapon, 0 );
|
||||
|
||||
self thread playerMonitorWeaponSwitch( weapon );
|
||||
|
||||
if ( isTurretEnergyWeapon( weapon ) )
|
||||
self thread playerSetupTurretEnergyBar( weapon, ammo );
|
||||
else
|
||||
self thread playerTrackTurretAmmo( weapon );
|
||||
|
||||
self waittill_any_return( "death", "rippable_complete", "rippable_switch" );
|
||||
|
||||
if ( !IsDefined( self ) )
|
||||
return false;
|
||||
|
||||
if ( isTurretEnergyWeapon( weapon ) )
|
||||
{
|
||||
self NotifyOnPlayerCommandRemove( "fire_turret_weapon", "+attack" );
|
||||
self NotifyOnPlayerCommandRemove( "fire_turret_weapon", "+attack_akimbo_accessible" );
|
||||
}
|
||||
|
||||
result = !playerHasRippableTurretInfo();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
playerMonitorWeaponSwitch( weapon )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "rippable_complete" );
|
||||
|
||||
currentWeapon = self GetCurrentWeapon();
|
||||
|
||||
while ( currentWeapon == weapon || isBombSiteWeapon( currentWeapon ) )
|
||||
{
|
||||
self waittill( "weapon_change", currentWeapon );
|
||||
}
|
||||
|
||||
if ( isKillstreakWeapon( currentWeapon ) )
|
||||
self.justSwitchedToKillstreakWeapon = currentWeapon;
|
||||
|
||||
self notify( "rippable_switch" );
|
||||
}
|
||||
|
||||
playerTrackTurretAmmo( weapon )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "rippable_switch" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
ammo = self GetWeaponAmmoClip( weapon );
|
||||
self playerRecordRippableAmmo( ammo );
|
||||
if ( ammo == 0 )
|
||||
{
|
||||
self playerClearRippableTurretInfo();
|
||||
self notify( "rippable_complete" );
|
||||
return;
|
||||
}
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
playerHasTurretHeadWeapon()
|
||||
{
|
||||
if ( self playerHasRippableTurretInfo() )
|
||||
return true;
|
||||
|
||||
weapons = self GetWeaponsListPrimaries();
|
||||
foreach ( weapon in weapons )
|
||||
{
|
||||
if ( ( weapon == "turretheadenergy_mp" ) ||
|
||||
( weapon == "turretheadrocket_mp" ) ||
|
||||
( weapon == "turretheadmg_mp" ) )
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getAmmoForTurretWeaponType( weapon )
|
||||
{
|
||||
if ( weapon == "turretheadmg_mp" )
|
||||
return CONST_REMOTE_TURRET_SENTRY_RIPPABLE_AMMO_MG;
|
||||
else if ( weapon == "turretheadrocket_mp" )
|
||||
return CONST_REMOTE_TURRET_SENTRY_RIPPABLE_AMMO_ROCKET;
|
||||
else
|
||||
return getFullEnergy();
|
||||
}
|
||||
|
||||
isTurretEnergyWeapon( weapon )
|
||||
{
|
||||
return ( weapon == "turretheadenergy_mp" );
|
||||
}
|
||||
|
||||
playerSetupTurretEnergyBar( weapon, ammo )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "rippable_switch" );
|
||||
|
||||
fullEnergy = getFullEnergy();
|
||||
self NotifyOnPlayerCommand( "fire_turret_weapon", "+attack" );
|
||||
self NotifyOnPlayerCommand( "fire_turret_weapon", "+attack_akimbo_accessible" );
|
||||
|
||||
ammo = self playerGetRippableAmmo();
|
||||
percent = ammo / fullEnergy;
|
||||
self SetClientOmnvar( "ui_energy_ammo", percent );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( !self AttackButtonPressed() )
|
||||
self waittill( "fire_turret_weapon" );
|
||||
|
||||
if ( self IsSwitchingWeapon() || self GetCurrentWeapon() != "turretheadenergy_mp" || !self IsFiring() || self IsUsingOffhand() )
|
||||
{
|
||||
waitframe();
|
||||
continue;
|
||||
}
|
||||
|
||||
ammo = self playerGetRippableAmmo();
|
||||
percent = ammo / fullEnergy;
|
||||
self SetClientOmnvar( "ui_energy_ammo", percent );
|
||||
|
||||
if ( ammo <= 0 )
|
||||
{
|
||||
weapons = self GetWeaponsListPrimaries();
|
||||
if ( weapons.size > 0 )
|
||||
self SwitchToWeapon( weapons[0] );
|
||||
else // shouldn't happen
|
||||
self TakeWeapon( weapon );
|
||||
self playerClearRippableTurretInfo();
|
||||
self notify( "rippable_complete" );
|
||||
return;
|
||||
}
|
||||
|
||||
waitframe();
|
||||
|
||||
self playerRecordRippableAmmo( ammo - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
getFullEnergy()
|
||||
{
|
||||
return ( CONST_REMOTE_TURRET_SENTRY_RIPPABLE_AMMO_ENERGY_SECONDS / 0.05 );
|
||||
}
|
||||
|
||||
playerGetRippableAmmo()
|
||||
{
|
||||
AssertEx( IsDefined( self.pers[ "rippableSentry" ] ), "playerGetRippableAmmo() called on player with no \"playerGetRippableAmmo\" array key." );
|
||||
|
||||
return self.pers[ "rippableSentry" ].ammo;
|
||||
}
|
||||
|
||||
playerRecordRippableAmmo( ammo )
|
||||
{
|
||||
AssertEx( IsDefined( self.pers[ "rippableSentry" ] ), "playerGetRippableAmmo() called on player with no \"playerGetRippableAmmo\" array key." );
|
||||
|
||||
// Rippable turret info is persistent to handle round switching. If this is
|
||||
self.pers[ "rippableSentry" ].ammo = ammo;
|
||||
}
|
||||
|
||||
playerHasRippableTurretInfo()
|
||||
{
|
||||
return ( IsDefined( self.pers[ "rippableSentry" ] ) && self playerGetRippableAmmo() > 0 );
|
||||
}
|
||||
|
||||
playerClearRippableTurretInfo()
|
||||
{
|
||||
self.pers[ "rippableSentry" ] = undefined;
|
||||
}
|
61
raw/maps/mp/killstreaks/_teamammorefill.gsc
Normal file
61
raw/maps/mp/killstreaks/_teamammorefill.gsc
Normal file
@ -0,0 +1,61 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
/*
|
||||
Team Ammo Refill Killstreak: when the player uses this, everyone on their team gets an ammo refill
|
||||
*/
|
||||
|
||||
init()
|
||||
{
|
||||
level.killStreakFuncs[ "team_ammo_refill" ] = ::tryUseTeamAmmoRefill;
|
||||
}
|
||||
|
||||
tryUseTeamAmmoRefill( lifeId, modules )
|
||||
{
|
||||
result = self giveTeamAmmoRefill();
|
||||
if ( result )
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "team_ammo_refill", self.origin );
|
||||
|
||||
return ( result );
|
||||
}
|
||||
|
||||
giveTeamAmmoRefill()
|
||||
{
|
||||
if( level.teambased )
|
||||
{
|
||||
foreach( teammate in level.players )
|
||||
{
|
||||
if( teammate.team == self.team )
|
||||
{
|
||||
teammate refillAmmo( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self refillAmmo( true );
|
||||
}
|
||||
|
||||
level thread teamPlayerCardSplash( "used_team_ammo_refill", self );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
refillAmmo( refillEquipment )
|
||||
{
|
||||
weaponList = self GetWeaponsListAll();
|
||||
|
||||
foreach ( weaponName in weaponList )
|
||||
{
|
||||
if ( isSubStr( weaponName, "grenade" ) || ( GetSubStr( weaponName, 0, 2 ) == "gl" ) )
|
||||
{
|
||||
if ( !refillEquipment || self getAmmoCount( weaponName ) >= 1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
self giveMaxAmmo( weaponName );
|
||||
}
|
||||
|
||||
self playLocalSound( "ammo_crate_use" );
|
||||
|
||||
}
|
872
raw/maps/mp/killstreaks/_uav.gsc
Normal file
872
raw/maps/mp/killstreaks/_uav.gsc
Normal file
@ -0,0 +1,872 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// Streak Data
|
||||
CONST_UAV_BASE_TIME = 30;
|
||||
CONST_UAV_MOD_INCREASED_TIME = 15;
|
||||
CONST_THREAT_DRAW_TIME = 10;
|
||||
|
||||
init()
|
||||
{
|
||||
assert( CONST_UAV_BASE_TIME > 7 );
|
||||
|
||||
level._effect[ "uav_explode" ] = LoadFX( "vfx/explosion/vehicle_uav_explosion" );
|
||||
level._effect[ "uav_exit" ] = LoadFX( "vfx/trail/smoketrail_uav" );
|
||||
level._effect[ "uav_trail" ] = LoadFX( "vfx/trail/smoketrail_uav" );
|
||||
|
||||
level.killStreakFuncs["uav"] = ::tryUseUAV;
|
||||
level.killStreakFuncs["uav_support"] = ::tryUseUAV;
|
||||
level.killStreakFuncs["counter_uav"] = ::tryUseUAV;
|
||||
|
||||
minimapOrigins = getEntArray( "minimap_corner", "targetname" );
|
||||
if ( miniMapOrigins.size )
|
||||
uavOrigin = maps\mp\gametypes\_spawnlogic::findBoxCenter( miniMapOrigins[0].origin, miniMapOrigins[1].origin );
|
||||
else
|
||||
uavOrigin = (0,0,0);
|
||||
|
||||
level.UAVRig = spawn( "script_model", uavOrigin );
|
||||
level.UAVRig setModel( "c130_zoomrig" );
|
||||
level.UAVRig.angles = (0,115,0);
|
||||
level.UAVRig hide();
|
||||
|
||||
level.UAVRig.targetname = "uavrig_script_model"; // used for debug printing
|
||||
|
||||
level.UAVRig thread rotateUAVRig();
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
level.radarMode["allies"] = "normal_radar";
|
||||
level.radarMode["axis"] = "normal_radar";
|
||||
level.activeUAVs["allies"] = 0;
|
||||
level.activeUAVs["axis"] = 0;
|
||||
level.activeCounterUAVs["allies"] = 0;
|
||||
level.activeCounterUAVs["axis"] = 0;
|
||||
level.uavModels["allies"] = [];
|
||||
level.uavModels["axis"] = [];
|
||||
}
|
||||
else
|
||||
{
|
||||
level.radarMode = [];
|
||||
level.activeUAVs = [];
|
||||
level.activeCounterUAVs = [];
|
||||
level.uavModels = [];
|
||||
}
|
||||
|
||||
level thread onPlayerConnect();
|
||||
level thread UAVTracker();
|
||||
|
||||
/#
|
||||
SetDvarIfUninitialized( "scr_uav_timeout", "0" );
|
||||
SetDvarIfUninitialized( "scr_uav_norandom", "0" );
|
||||
SetDvarIfUninitialized( "scr_uav_alwaysshow", "0" );
|
||||
#/
|
||||
}
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
if ( !level.teamBased )
|
||||
{
|
||||
level.activeUAVs[ player.guid ] = 0;
|
||||
level.activeUAVs[ player.guid + "_radarStrength" ] = 0;
|
||||
level.activeCounterUAVs[ player.guid ] = 0;
|
||||
|
||||
level.radarMode[ player.guid ] = "normal_radar";
|
||||
}
|
||||
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
level notify ( "uav_update" );
|
||||
}
|
||||
}
|
||||
|
||||
rotateUAVRig()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
self rotateyaw( -360, 60 );
|
||||
wait ( 60 );
|
||||
}
|
||||
}
|
||||
|
||||
/#
|
||||
debugLocation()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
Print3d( self.origin, "UAV", ( 1, 0, 0 ) );
|
||||
Print3d( self.origin, "UAV origin: " + self.origin[0] + ", " + self.origin[1] + ", " + self.origin[2], ( 1, 0, 0 ) );
|
||||
|
||||
Print3d( level.UAVRig.origin, "UAV Rig", ( 1, 0, 0 ) );
|
||||
Print3d( level.UAVRig.origin, "UAV Rig origin: " + level.UAVRig.origin[0] + ", " + level.UAVRig.origin[1] + ", " + level.UAVRig.origin[2], ( 1, 0, 0 ) );
|
||||
|
||||
Print3d( level.UAVRig.origin - ( 0, 0, 50), "Distance: " + Distance( level.UAVRig.origin, self.origin ), ( 1, 0, 0 ) );
|
||||
|
||||
Line( level.UAVRig.origin, self.origin, ( 0, 0, 1 ) );
|
||||
|
||||
anglesForward = AnglesToForward( level.players[0].angles );
|
||||
scalar = (anglesForward[0] * 200, anglesForward[1] * 200, anglesForward[2] );
|
||||
Print3d( level.players[0].origin + scalar, "Distance: " + Distance( level.players[0].origin, self.origin ), ( 1, 0, 0 ) );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
debugTrace()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
result = BulletTrace( level.players[0].origin, self.origin, false, undefined );
|
||||
if( IsDefined( result ) && IsDefined( result[ "surfacetype" ] ) )
|
||||
{
|
||||
PrintLn( "UAV debugTrace: " + result[ "surfacetype" ] );
|
||||
}
|
||||
wait( 1.0 );
|
||||
}
|
||||
}
|
||||
#/
|
||||
playTrailFX()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
PlayFXOnTag( level._effect["uav_trail"],self,"tag_origin");
|
||||
|
||||
}
|
||||
launchUAV( owner, team, uavType, modules )
|
||||
{
|
||||
UAVModel = spawn( "script_model", level.UAVRig getTagOrigin( "tag_origin" ) );
|
||||
UAVModel.modules = modules;
|
||||
|
||||
/#
|
||||
if( GetDvarInt( "scr_debuguav", 0 ) )
|
||||
{
|
||||
UAVModel thread debugLocation();
|
||||
UAVModel thread debugTrace();
|
||||
}
|
||||
#/
|
||||
UAVModel.value = 1;
|
||||
|
||||
if ( array_contains( UAVModel.modules, "uav_advanced_updates" ) ) // streak customization
|
||||
UAVModel.value = 2;
|
||||
if ( array_contains( UAVModel.modules, "uav_enemy_direction" ) ) // streak customization
|
||||
UAVModel.value = 3;
|
||||
|
||||
if ( array_contains( UAVModel.modules, "uav_scrambler" ) ) // streak customization
|
||||
isScrambler = true;
|
||||
else
|
||||
isScrambler = false;
|
||||
|
||||
UAVModel setModel( "uav_drone_static" );
|
||||
UAVModel thread playTrailFX();
|
||||
|
||||
UAVModel thread maps\mp\gametypes\_damage::setEntityDamageCallback( 1000, undefined, ::uavOnDeath, undefined, true );
|
||||
|
||||
UAVModel.team = team;
|
||||
UAVModel.owner = owner;
|
||||
UAVModel.timeToAdd = 0;
|
||||
UAVModel.orbit = array_contains( UAVModel.modules, "uav_orbit" ); // streak customization
|
||||
UAVModel.paintOutline = array_contains( UAVModel.modules, "uav_paint_outline" ); // streak customization
|
||||
UAVModel.assistPoints = array_contains( UAVModel.modules, "uav_assist_points" ); // streak customization
|
||||
UAVModel make_entity_sentient_mp( team );
|
||||
|
||||
UAVModel thread handleIncomingStinger();
|
||||
|
||||
UAVModel.StreakCustomization = owner.StreakCustomization;
|
||||
|
||||
UAVModel addUAVModel();
|
||||
|
||||
thread flyIn( UAVModel );
|
||||
UAVModel thread updateUAVModelVisibility();
|
||||
UAVModel thread maps\mp\killstreaks\_killstreaks::updateAerialKillStreakMarker();
|
||||
|
||||
UAVModel addActiveUAV();
|
||||
|
||||
if ( isScrambler )
|
||||
UAVModel addActiveCounterUAV();
|
||||
|
||||
//this adds 5 seconds of time to all active UAV's of the same type.
|
||||
if ( isDefined( level.activeUAVs[team] ) )
|
||||
{
|
||||
foreach ( uav in level.UAVModels[team] )
|
||||
{
|
||||
if (uav == UAVModel)
|
||||
continue;
|
||||
|
||||
if ( isScrambler )
|
||||
uav.timeToAdd += 5;
|
||||
else if ( !isScrambler )
|
||||
uav.timeToAdd += 5;
|
||||
}
|
||||
}
|
||||
|
||||
waitframe();
|
||||
level notify ( "uav_update" );
|
||||
|
||||
duration = CONST_UAV_BASE_TIME;
|
||||
if ( array_contains( UAVModel.modules, "uav_increased_time" ) ) // streak customization
|
||||
duration += CONST_UAV_MOD_INCREASED_TIME;
|
||||
|
||||
/#
|
||||
if ( GetDvarInt( "scr_uav_timeout", 0 ) != 0 )
|
||||
duration = GetDvarInt( "scr_uav_timeout" );
|
||||
#/
|
||||
|
||||
UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", duration );
|
||||
|
||||
if ( UAVModel.damageTaken < UAVModel.maxHealth )
|
||||
{
|
||||
UAVModel unlink();
|
||||
|
||||
destPoint = UAVModel.origin + ( AnglesToForward( UAVModel.angles ) * 20000 );
|
||||
UAVModel moveTo( destPoint, 60 );
|
||||
PlayFXOnTag( getfx( "uav_exit" ) , UAVModel, "tag_origin" );
|
||||
|
||||
UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", 3 );
|
||||
|
||||
if ( UAVModel.damageTaken < UAVModel.maxHealth )
|
||||
{
|
||||
UAVModel notify( "leaving" );
|
||||
UAVModel.isLeaving = true;
|
||||
UAVModel moveTo( destPoint, 4, 4, 0.0 );
|
||||
}
|
||||
|
||||
UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", 4 + UAVModel.timeToAdd );
|
||||
}
|
||||
|
||||
if ( isScrambler )
|
||||
UAVModel removeActiveCounterUAV();
|
||||
|
||||
UAVModel removeActiveUAV();
|
||||
|
||||
UAVModel delete();
|
||||
UAVModel removeUAVModel();
|
||||
|
||||
level notify ( "uav_update" );
|
||||
}
|
||||
|
||||
flyIn( UAVModel )
|
||||
{
|
||||
UAVModel Hide();
|
||||
|
||||
zOffset = RandomIntRange( 3000, 5000 );
|
||||
|
||||
// we need to make sure the uav doesn't go higher than 8100 units because bullets die at 8192
|
||||
if ( IsDefined( level.spawnpoints ) )
|
||||
spawns = level.spawnPoints;
|
||||
else
|
||||
spawns = level.startSpawnPoints;
|
||||
|
||||
lowestSpawn = spawns[ 0 ];
|
||||
foreach ( Spawn in spawns )
|
||||
{
|
||||
if ( Spawn.origin[ 2 ] < lowestSpawn.origin[ 2 ] )
|
||||
lowestSpawn = Spawn;
|
||||
}
|
||||
lowestZ = lowestSpawn.origin [ 2 ];
|
||||
UAVRigZ = level.UAVRig.origin[ 2 ];
|
||||
if ( lowestZ < 0 )
|
||||
{
|
||||
UAVRigZ += lowestZ * -1;
|
||||
lowestZ = 0;
|
||||
}
|
||||
diffZ = UAVRigZ - lowestZ;
|
||||
AssertEx( diffZ < 8100.0, "The lowest spawn and the UAV node are more than 8100 z units apart, please notify MP Design." );
|
||||
if ( diffZ + zOffset > 8100.0 )
|
||||
{
|
||||
zOffset -= ( ( diffZ + zOffset ) - 8100.0 );
|
||||
}
|
||||
|
||||
angle = RandomInt( 360 );
|
||||
radiusOffset = RandomInt( 2000 ) + 5000;
|
||||
|
||||
/#
|
||||
if ( GetDvarInt( "scr_uav_norandom", 0 ) != 0 )
|
||||
{
|
||||
angle = 0;
|
||||
radiusOffset = 5000;
|
||||
}
|
||||
#/
|
||||
|
||||
xOffset = Cos( angle ) * radiusOffset;
|
||||
yOffset = Sin( angle ) * radiusOffset;
|
||||
|
||||
angleVector = VectorNormalize( ( xOffset, yOffset, zOffset ) );
|
||||
angleVector = ( angleVector * RandomIntRange( 6000, 7000 ) );
|
||||
|
||||
UAVModel LinkTo( level.UAVRig, "tag_origin", angleVector, ( 0, angle - 90, 135 ) );
|
||||
|
||||
waitframe();
|
||||
|
||||
destination = UAVModel.origin;
|
||||
UAVModel Unlink();
|
||||
|
||||
UAVModel.origin = destination + ( AnglesToForward( UAVModel.angles ) * -20000 );
|
||||
UAVModel MoveTo( destination, 4, 0, 2 );
|
||||
wait 4;
|
||||
|
||||
if ( IsDefined( UAVModel ) )
|
||||
UAVModel LinkTo( level.UAVRig, "tag_origin" );
|
||||
}
|
||||
|
||||
waittill_notify_or_timeout_hostmigration_pause( msg, timer )
|
||||
{
|
||||
self endon( msg );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timer );
|
||||
}
|
||||
|
||||
|
||||
updateUAVModelVisibility()
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
/#
|
||||
if ( GetDvarInt( "scr_uav_alwaysshow", 0 ) != 0 )
|
||||
{
|
||||
waitframe();
|
||||
self Show();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill_either ( "joined_team", "uav_update" );
|
||||
|
||||
self hide();
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( level.teamBased )
|
||||
{
|
||||
if ( (player.team != self.team) && !self.orbit )
|
||||
self showToPlayer( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( (isDefined( self.owner ) && player == self.owner) || self.orbit )
|
||||
continue;
|
||||
|
||||
self showToPlayer( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uavOnDeath( attacker, weapon, meansOfDeath, damage )
|
||||
{
|
||||
self Hide();
|
||||
self notify( "death" );
|
||||
|
||||
forward = ( AnglesToRight( self.angles ) * 200 );
|
||||
playFx ( getfx( "uav_explode" ), self.origin, forward );
|
||||
playSoundAtPos( self.origin, "uav_air_death" );
|
||||
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, meansOfDeath, damage, "uav_destroyed", undefined, "callout_destroyed_uav", true );
|
||||
}
|
||||
|
||||
|
||||
tryUseUAV( lifeId, modules )
|
||||
{
|
||||
if(IsDefined(self.pers["killstreaks"][self.killstreakIndexWeapon].streakName))
|
||||
{
|
||||
StreakName = self.pers["killstreaks"][self.killstreakIndexWeapon].streakName;
|
||||
}
|
||||
else
|
||||
{
|
||||
StreakName = "uav_support";
|
||||
}
|
||||
|
||||
if ( isdefined ( level.isHorde ) && level.isHorde && self.killstreakIndexWeapon == 1 )
|
||||
self notify ( "used_horde_uav" );
|
||||
|
||||
return useUAV( streakname, modules );
|
||||
}
|
||||
|
||||
useUAV( uavType, modules )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( uavType, self.origin );
|
||||
|
||||
team = self.pers["team"];
|
||||
|
||||
level thread launchUAV( self, team, uavType, modules );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UAVTracker()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "uav_update" );
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
updateTeamUAVStatus( "allies" );
|
||||
updateTeamUAVStatus( "axis" );
|
||||
}
|
||||
else
|
||||
{
|
||||
updatePlayersUAVStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_getRadarStrength( team, fastSweep, enemyDirection )
|
||||
{
|
||||
activeUAVs = 0;
|
||||
activeCounterUAVs = 0;
|
||||
|
||||
foreach ( uav in level.UAVModels[team] )
|
||||
{
|
||||
activeUAVs += uav.value;
|
||||
}
|
||||
|
||||
foreach ( uav in level.UAVModels[level.otherTeam[team] ] )
|
||||
{
|
||||
if ( uav.uavType != "counter" )
|
||||
continue;
|
||||
|
||||
activeCounterUAVs += uav.value;
|
||||
}
|
||||
|
||||
if( activeCounterUAVs > 0 )
|
||||
radarStrength = -3;
|
||||
else
|
||||
radarStrength = activeUAVs;
|
||||
|
||||
|
||||
strengthMin = GetUAVStrengthMin();
|
||||
strengthMax = GetUAVStrengthMax();
|
||||
|
||||
//clamp between min/max
|
||||
if( radarStrength <= strengthMin )
|
||||
{
|
||||
radarStrength = strengthMin;
|
||||
}
|
||||
else if( radarStrength >= strengthMax )
|
||||
{
|
||||
radarStrength = strengthMax;
|
||||
}
|
||||
|
||||
return radarStrength;
|
||||
}
|
||||
|
||||
_getTeamPaintOutline( team )
|
||||
{
|
||||
paintOutlineUavs = 0;
|
||||
paintOutline = false;
|
||||
|
||||
foreach ( uav in level.UAVModels[team] )
|
||||
{
|
||||
if ( uav.paintOutline ) // streak customization
|
||||
paintOutlineUavs += uav.value;
|
||||
}
|
||||
|
||||
if( paintOutlineUavs > 0 )
|
||||
paintOutline = true;
|
||||
else
|
||||
paintOutline = false;
|
||||
|
||||
return paintOutline;
|
||||
}
|
||||
|
||||
_getPaintOutline( player )
|
||||
{
|
||||
paintOutlineUavs = 0;
|
||||
paintOutline = false;
|
||||
|
||||
foreach ( uav in level.UAVModels )
|
||||
{
|
||||
if ( IsDefined( uav.owner ) && uav.owner == player && uav.paintOutline ) // streak customization
|
||||
paintOutlineUavs += uav.value;
|
||||
}
|
||||
|
||||
if( paintOutlineUavs > 0 )
|
||||
paintOutline = true;
|
||||
else
|
||||
paintOutline = false;
|
||||
|
||||
return paintOutline;
|
||||
}
|
||||
|
||||
|
||||
updateTeamUAVStatus( team )
|
||||
{
|
||||
radarStrength = _getRadarStrength( team );
|
||||
hasPaintOutline = _getTeamPaintOutline( team );
|
||||
|
||||
updateTeamPaintOutline( team, hasPaintOutline );
|
||||
|
||||
setTeamRadarStrength( team, radarStrength );
|
||||
|
||||
if ( radarStrength >= GetUAVStrengthLevelNeutral() ) //note: exception for counter uavs
|
||||
{
|
||||
updateTeamRadarBlocked( team, false );
|
||||
unblockTeamRadar( team );
|
||||
}
|
||||
else
|
||||
{
|
||||
updateTeamRadarBlocked( team, true );
|
||||
blockTeamRadar( team );
|
||||
}
|
||||
|
||||
if ( radarStrength <= GetUAVStrengthLevelNeutral() )
|
||||
{
|
||||
setTeamRadarWrapper( team, 0 );
|
||||
updateTeamUAVType( team );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( radarStrength >= GetUAVStrengthLevelShowEnemyFastSweep() )
|
||||
level.radarMode[team] = "fast_radar";
|
||||
else
|
||||
level.radarMode[team] = "normal_radar";
|
||||
|
||||
updateTeamUAVType( team );
|
||||
setTeamRadarWrapper( team, 1 );
|
||||
}
|
||||
|
||||
//for FFA
|
||||
updatePlayersUAVStatus()
|
||||
{
|
||||
strengthMin = GetUAVStrengthMin();
|
||||
strengthMax = GetUAVStrengthMax();
|
||||
strengthDirectional = GetUAVStrengthLevelShowEnemyDirectional();
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
radarStrength = level.activeUAVs[ player.guid + "_radarStrength" ];
|
||||
hasPaintOutline = _getPaintOutline( player );
|
||||
|
||||
updatePaintOutline( player, hasPaintOutline );
|
||||
|
||||
// if there are any counters up that aren't this player's then they are blocked
|
||||
foreach( enemyPlayer in level.players )
|
||||
{
|
||||
if( enemyPlayer == player )
|
||||
continue;
|
||||
|
||||
activeCounterUAVs = level.activeCounterUAVs[ enemyPlayer.guid ];
|
||||
if( activeCounterUAVs > 0 )
|
||||
{
|
||||
radarStrength = -3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//clamp between min/max
|
||||
if( radarStrength <= strengthMin )
|
||||
{
|
||||
radarStrength = strengthMin;
|
||||
}
|
||||
else if( radarStrength >= strengthMax )
|
||||
{
|
||||
radarStrength = strengthMax;
|
||||
}
|
||||
|
||||
player.radarstrength = radarStrength;
|
||||
|
||||
if ( radarStrength >= GetUAVStrengthLevelNeutral() )
|
||||
{
|
||||
updatePlayerRadarBlocked( player, false );
|
||||
player.isRadarBlocked = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
updatePlayerRadarBlocked( player, true );
|
||||
player.isRadarBlocked = true;
|
||||
}
|
||||
|
||||
|
||||
if ( radarStrength <= GetUAVStrengthLevelNeutral() )
|
||||
{
|
||||
player.hasRadar = false;
|
||||
player.radarShowEnemyDirection = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( radarStrength >= GetUAVStrengthLevelShowEnemyFastSweep())
|
||||
player.radarMode = "fast_radar";
|
||||
else
|
||||
player.radarMode = "normal_radar";
|
||||
|
||||
//set directional status
|
||||
player.radarShowEnemyDirection = radarStrength >= strengthDirectional;
|
||||
|
||||
player.hasRadar = true;
|
||||
}
|
||||
}
|
||||
|
||||
updateTeamUAVType( team, ShowEnemyDirection )
|
||||
{
|
||||
shouldBeDirectional = _getRadarStrength( team ) >= GetUAVStrengthLevelShowEnemyDirectional();
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == "spectator" )
|
||||
continue;
|
||||
|
||||
enemyTeam = maps\mp\gametypes\_gameobjects::getEnemyTeam( player.team );
|
||||
player.radarMode = level.radarMode[player.team];
|
||||
player.enemyRadarMode = level.radarMode[enemyTeam];
|
||||
|
||||
//use direction based on uav signal strength
|
||||
if( player.team == team )
|
||||
{
|
||||
player.radarShowEnemyDirection = shouldBeDirectional;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateTeamPaintOutline( team, on )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( IsDefined( player ) && player.team == team )
|
||||
player playerSetupUAVPaintOutline( on );
|
||||
}
|
||||
}
|
||||
|
||||
updatePaintOutline( player, on )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
player playerSetupUAVPaintOutline( on );
|
||||
}
|
||||
|
||||
playerSetupUAVPaintOutline( on ) // self == player
|
||||
{
|
||||
if ( on )
|
||||
{
|
||||
if ( !IsDefined( self.uav_paint_effect ) )
|
||||
{
|
||||
self.uav_paint_effect = maps\mp\_threatdetection::detection_highlight_hud_effect_on( self, -1 );
|
||||
}
|
||||
self SetPerk( "specialty_uav_paint", true, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IsDefined( self.uav_paint_effect ) )
|
||||
{
|
||||
maps\mp\_threatdetection::detection_highlight_hud_effect_off( self.uav_paint_effect );
|
||||
self.uav_paint_effect = undefined;
|
||||
}
|
||||
self UnSetPerk( "specialty_uav_paint", true );
|
||||
}
|
||||
}
|
||||
|
||||
updateTeamRadarBlocked( team, isBlocked )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( IsDefined( player ) && player.team == team )
|
||||
updatePlayerRadarBlocked( player, isBlocked );
|
||||
}
|
||||
}
|
||||
|
||||
updatePlayerRadarBlocked( player, isBlocked )
|
||||
{
|
||||
if ( !isBlocked || !player _hasPerk( "specialty_class_hardwired" ) )
|
||||
player SetClientOmnvar( "ui_uav_scrambler_on", isBlocked );
|
||||
}
|
||||
|
||||
setTeamRadarWrapper( team, value )
|
||||
{
|
||||
setTeamRadar( team, value );
|
||||
level notify( "radar_status_change", team );
|
||||
}
|
||||
|
||||
handleIncomingStinger()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "stinger_fired", player, missiles );
|
||||
|
||||
if ( !maps\mp\_stingerm7::anyStingerMissileLockedOn( missiles, self ) )
|
||||
continue;
|
||||
|
||||
foreach ( missile in missiles )
|
||||
{
|
||||
if ( !IsDefined( missile ) )
|
||||
continue;
|
||||
|
||||
missile thread stingerProximityDetonate( missile.lockedStingerTarget, player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stingerProximityDetonate( targetEnt, player )
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
minDist = distance( self.origin, targetEnt GetPointInBounds( 0, 0, 0 ) );
|
||||
lastCenter = targetEnt GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
// UAV already destroyed
|
||||
if ( !isDefined( targetEnt ) )
|
||||
center = lastCenter;
|
||||
else
|
||||
center = targetEnt GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
lastCenter = center;
|
||||
|
||||
curDist = distance( self.origin, center );
|
||||
|
||||
if ( curDist < minDist )
|
||||
minDist = curDist;
|
||||
|
||||
if ( curDist > minDist )
|
||||
{
|
||||
if ( curDist > 1536 )
|
||||
return;
|
||||
|
||||
RadiusDamage( self.origin, 1536, 600, 600, player, "MOD_EXPLOSIVE", "stinger_mp" );
|
||||
playFx( level.stingerFXid, self.origin );
|
||||
|
||||
//self playSound( "remotemissile_explode" );
|
||||
self hide();
|
||||
|
||||
self notify("deleted");
|
||||
wait ( 0.05 );
|
||||
self delete();
|
||||
player notify( "killstreak_destroyed" );
|
||||
}
|
||||
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addUAVModel() // self == uavmodel
|
||||
{
|
||||
if ( level.teamBased )
|
||||
level.UAVModels[ self.team ][level.UAVModels[ self.team ].size] = self;
|
||||
else
|
||||
level.UAVModels[ self.owner.guid + "_" + getTime() ] = self;
|
||||
}
|
||||
|
||||
|
||||
removeUAVModel() // self == uavmodel
|
||||
{
|
||||
UAVModels = [];
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
team = self.team;
|
||||
|
||||
foreach ( uavModel in level.UAVModels[team] )
|
||||
{
|
||||
if ( !isDefined( uavModel ) )
|
||||
continue;
|
||||
|
||||
UAVModels[UAVModels.size] = uavModel;
|
||||
}
|
||||
|
||||
level.UAVModels[team] = UAVModels;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ( uavModel in level.UAVModels )
|
||||
{
|
||||
if ( !isDefined( uavModel ) )
|
||||
continue;
|
||||
|
||||
UAVModels[UAVModels.size] = uavModel;
|
||||
}
|
||||
|
||||
level.UAVModels = UAVModels;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addActiveUAV() // self == uav model
|
||||
{
|
||||
self.uavType = "standard";
|
||||
|
||||
if ( level.teamBased )
|
||||
level.activeUAVs[self.team]++;
|
||||
else
|
||||
{
|
||||
level.activeUAVs[ self.owner.guid ]++;
|
||||
level.activeUAVs[ self.owner.guid + "_radarStrength" ] += self.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addActiveCounterUAV()
|
||||
{
|
||||
self.uavType = "counter";
|
||||
|
||||
if ( level.teamBased )
|
||||
level.activeCounterUAVs[self.team]++;
|
||||
else
|
||||
level.activeCounterUAVs[self.owner.guid]++;
|
||||
}
|
||||
|
||||
|
||||
removeActiveUAV() // self == uav model
|
||||
{
|
||||
if ( level.teamBased )
|
||||
{
|
||||
level.activeUAVs[self.team]--;
|
||||
|
||||
if ( !level.activeUAVs[self.team] )
|
||||
{
|
||||
//printOnTeam( &"MP_WAR_RADAR_EXPIRED", self.team );
|
||||
//printOnTeam( &"MP_WAR_RADAR_EXPIRED_ENEMY", level.otherTeam[self.team] );
|
||||
}
|
||||
}
|
||||
else if ( isDefined( self.owner ) )
|
||||
{
|
||||
level.activeUAVs[ self.owner.guid ]--;
|
||||
level.activeUAVs[ self.owner.guid + "_radarStrength" ] -= self.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
removeActiveCounterUAV()
|
||||
{
|
||||
if ( level.teamBased )
|
||||
{
|
||||
level.activeCounterUAVs[self.team]--;
|
||||
|
||||
if ( !level.activeCounterUAVs[self.team] )
|
||||
{
|
||||
//printOnTeam( &"MP_WAR_COUNTER_RADAR_EXPIRED", self.team );
|
||||
//printOnTeam( &"MP_WAR_COUNTER_RADAR_EXPIRED_ENEMY", level.otherTeam[self.team] );
|
||||
}
|
||||
}
|
||||
else if ( isDefined( self.owner ) )
|
||||
{
|
||||
level.activeCounterUAVs[self.owner.guid]--;
|
||||
}
|
||||
}
|
2288
raw/maps/mp/killstreaks/_warbird.gsc
Normal file
2288
raw/maps/mp/killstreaks/_warbird.gsc
Normal file
File diff suppressed because it is too large
Load Diff
343
raw/maps/mp/killstreaks/streak_mp_comeback.gsc
Normal file
343
raw/maps/mp/killstreaks/streak_mp_comeback.gsc
Normal file
@ -0,0 +1,343 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
level.killstreakWieldWeapons["killstreak_comeback_mp"] = "mp_comeback";
|
||||
|
||||
level.killstreakFuncs["mp_comeback"] = ::tryUseKillstreak;
|
||||
|
||||
level.mapKillStreak = "mp_comeback";
|
||||
level.mapKillstreakPickupString = &"MP_COMEBACK_MAP_KILLSTREAK_PICKUP";
|
||||
|
||||
level.mapCustomBotKillstreakFunc = ::setupBotsForMapKillstreak;
|
||||
|
||||
level.killstreak_tank_groups = [];
|
||||
level.killstreak_tanks = GetEntArray("walker_tank", "targetname");
|
||||
array_thread(level.killstreak_tanks, ::init_tank);
|
||||
|
||||
level.killstreak_tank_groups = array_randomize(level.killstreak_tank_groups);
|
||||
|
||||
//level.stingerLockOnEntsFunc = ::stinger_lock_on_list;
|
||||
|
||||
level.sky_nodes = [];
|
||||
allNodes = GetAllNodes();
|
||||
foreach(node in allNodes)
|
||||
{
|
||||
if( NodeExposedToSky(node, true) )
|
||||
level.sky_nodes[level.sky_nodes.size] = node;
|
||||
}
|
||||
|
||||
level.missile_start_offset = 20;
|
||||
|
||||
PrecacheMpAnim("mp_comeback_spider_tank_idle");
|
||||
PrecacheMpAnim("mp_comeback_spider_tank_fire");
|
||||
}
|
||||
|
||||
setupBotsForMapKillstreak()
|
||||
{
|
||||
level thread maps\mp\bots\_bots_ks::bot_register_killstreak_func( "mp_comeback", maps\mp\bots\_bots_ks::bot_killstreak_simple_use );
|
||||
}
|
||||
|
||||
tryUseKillstreak(lifeId,modules)
|
||||
{
|
||||
killstreak_group = undefined;
|
||||
foreach(group in level.killstreak_tank_groups)
|
||||
{
|
||||
isOlder = !IsDefined(killstreak_group) || (group.last_run_time < killstreak_group.last_run_time);
|
||||
|
||||
if(!group.active && isOlder)
|
||||
{
|
||||
killstreak_group = group;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!IsDefined(killstreak_group))
|
||||
{
|
||||
IPrintLnBold(&"MP_COMEBACK_MAP_KILLSTREAK_NOT_AVAILABLE");
|
||||
return false;
|
||||
}
|
||||
|
||||
killstreak_group thread group_run(self);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
init_tank()
|
||||
{
|
||||
self.start_origin = self.origin;
|
||||
self.start_angles = self.angles;
|
||||
|
||||
//self.getStingerTargetPosFunc = ::tank_stinger_target_pos;
|
||||
|
||||
self.objIds = [];
|
||||
teams = ["allies","axis"];
|
||||
foreach(team in teams)
|
||||
{
|
||||
self.objIds[team] = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
objective_add( self.objIds[team], "invisible", (0,0,0) );
|
||||
objective_icon( self.objIds[team], ter_op(team=="allies", "compass_objpoint_tank_friendly", "compass_objpoint_tank_enemy") );
|
||||
}
|
||||
|
||||
self.group = self.script_noteworthy;
|
||||
if(!IsDefined(self.group))
|
||||
self.group = "default";
|
||||
|
||||
if(!IsDefined(level.killstreak_tank_groups[self.group]))
|
||||
level.killstreak_tank_groups[self.group] = init_new_tank_group();
|
||||
|
||||
group_tank_count = level.killstreak_tank_groups[self.group].tanks.size;
|
||||
level.killstreak_tank_groups[self.group].tanks[group_tank_count] = self;
|
||||
|
||||
tank_idle(self);
|
||||
}
|
||||
|
||||
init_new_tank_group()
|
||||
{
|
||||
group = SpawnStruct();
|
||||
group.active = false;
|
||||
group.tanks = [];
|
||||
group.last_run_time = 0;
|
||||
return group;
|
||||
}
|
||||
|
||||
group_run(owner)
|
||||
{
|
||||
self.active = true;
|
||||
self.owner_team = owner.team;
|
||||
self.owner = owner;
|
||||
|
||||
self.last_run_time = GetTime();
|
||||
|
||||
self.tank_count = self.tanks.size;
|
||||
foreach(tank in self.tanks)
|
||||
{
|
||||
self thread tank_run(tank);
|
||||
}
|
||||
|
||||
self waittill("all_tanks_done");
|
||||
|
||||
self.active = false;
|
||||
}
|
||||
|
||||
tank_run(tank, owner)
|
||||
{
|
||||
tank_show_icon(tank);
|
||||
|
||||
//self thread tank_damage_watch(tank);
|
||||
|
||||
tank_attack(tank);
|
||||
|
||||
tank_idle( tank );
|
||||
|
||||
tank_end(tank);
|
||||
}
|
||||
|
||||
tank_show_icon(tank)
|
||||
{
|
||||
foreach(team, objId in tank.objIds)
|
||||
{
|
||||
objective_state( objId, "active" );
|
||||
if ( team == "allies" )
|
||||
Objective_PlayerTeam( objId, self.owner GetEntityNumber());
|
||||
else
|
||||
Objective_PlayerEnemyTeam( objId, self.owner GetEntityNumber());
|
||||
Objective_OnEntityWithRotation( objId, tank );
|
||||
}
|
||||
}
|
||||
|
||||
tank_hide_icon(tank)
|
||||
{
|
||||
foreach(team, objId in tank.objIds)
|
||||
{
|
||||
objective_state( objId, "invisible" );
|
||||
}
|
||||
}
|
||||
|
||||
tank_idle(tank)
|
||||
{
|
||||
tank ScriptModelPlayAnimDeltaMotion("mp_comeback_spider_tank_idle");
|
||||
}
|
||||
|
||||
tank_attack(tank)
|
||||
{
|
||||
self.owner endon("disconnect");
|
||||
tank endon("tank_destroyed");
|
||||
|
||||
tank PlaySound("walker_start");
|
||||
tank ScriptModelPlayAnimDeltaMotion("mp_comeback_spider_tank_fire", "comeback_tank");
|
||||
|
||||
right_count = 0;
|
||||
left_count = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
tank waittill("comeback_tank", notetrack);
|
||||
switch(notetrack)
|
||||
{
|
||||
case "fire_right":
|
||||
right_count++;
|
||||
tag = "tag_missile_"+right_count+"_r";
|
||||
self tank_fire_missile( tank, tag );
|
||||
break;
|
||||
case "fire_left":
|
||||
left_count++;
|
||||
tag = "tag_missile_"+left_count+"_l";
|
||||
self tank_fire_missile( tank, tag );
|
||||
break;
|
||||
case "end":
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tank_fire_missile(tank, tag)
|
||||
{
|
||||
origin = tank GetTagOrigin(tag);
|
||||
tag_angles = tank GetTagAngles(tag);
|
||||
dir = AnglesToForward(tag_angles);
|
||||
|
||||
start = origin+(dir*level.missile_start_offset);
|
||||
end = start + dir;
|
||||
missile = MagicBullet("killstreak_comeback_mp", start, end, self.owner);
|
||||
self thread tank_missile_set_target(missile);
|
||||
}
|
||||
|
||||
tank_missile_set_target(missile)
|
||||
{
|
||||
missile endon("death");
|
||||
|
||||
wait RandomFloatRange(0.2, 0.4);
|
||||
|
||||
target = undefined;
|
||||
|
||||
find_target_time = RandomFloatRange(.5, 1.0);
|
||||
find_end_time = GetTime()+(find_target_time*1000);
|
||||
|
||||
other_team = getOtherTeam(self.owner_team);
|
||||
while(GetTime()<find_end_time && !IsDefined(target))
|
||||
{
|
||||
players = array_randomize(level.players);
|
||||
foreach(player in players)
|
||||
{
|
||||
if(player.team != other_team)
|
||||
continue;
|
||||
|
||||
if(!isReallyAlive(player))
|
||||
continue;
|
||||
|
||||
if(IsDefined(player.tank_no_target_time) && player.tank_no_target_time>GetTime())
|
||||
continue;
|
||||
|
||||
if(IsDefined(player.spawnTime) && (player.spawnTime+3000) > GetTime())
|
||||
continue;
|
||||
|
||||
if( SightTracePassed(missile.origin, player.origin + (0,0,40), false, missile, player, false) )
|
||||
{
|
||||
target = player;
|
||||
break;
|
||||
}
|
||||
}
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
if(IsDefined(target))
|
||||
{
|
||||
target.tank_no_target_time = GetTime() + 3000;
|
||||
missile Missile_SetTargetEnt(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
min_owner_dist = 250;
|
||||
min_owner_dist_sqr = min_owner_dist*min_owner_dist;
|
||||
random_node = random(level.sky_nodes);
|
||||
//try not to shoot the owner
|
||||
if(IsDefined(self.owner))
|
||||
{
|
||||
for(i=0; i<10 && DistanceSquared(random_node.origin, self.owner.origin)<min_owner_dist_sqr; i++)
|
||||
{
|
||||
random_node = random(level.sky_nodes);
|
||||
}
|
||||
}
|
||||
|
||||
missile Missile_SetTargetPos(random_node.origin);
|
||||
}
|
||||
}
|
||||
|
||||
tank_end(tank)
|
||||
{
|
||||
tank_hide_icon(tank);
|
||||
|
||||
self.tank_count--;
|
||||
if(self.tank_count==0)
|
||||
self notify("all_tanks_done");
|
||||
}
|
||||
|
||||
//tank_damage_watch(tank)
|
||||
//{
|
||||
// self endon("all_tanks_done");
|
||||
//
|
||||
// tank SetCanDamage(true);
|
||||
// tank.health = 1000;
|
||||
//
|
||||
// while(1)
|
||||
// {
|
||||
// tank waittill("damage", amount, attacker, direction_vec, point, type);
|
||||
//
|
||||
// if(IsDefined(attacker))
|
||||
// {
|
||||
// if(attacker.team == self.owner_team)
|
||||
// {
|
||||
// tank.health += amount;
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "comeback_tank" );
|
||||
// }
|
||||
//
|
||||
// if ( tank.health<0 )
|
||||
// {
|
||||
// tank SetModel("vehicle_walker_tank_dstrypv");
|
||||
//
|
||||
// gravity = -800;
|
||||
// up_impulse = 400;
|
||||
// time = 2.0*(-1*up_impulse)/gravity;
|
||||
// tank MoveGravity( (0,0,up_impulse), time);
|
||||
// PlayFX(level._effect[ "vehicle_walker_tank_explosion" ], tank.origin);
|
||||
//
|
||||
// tank notify("tank_destroyed");
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//stinger_lock_on_list(player)
|
||||
//{
|
||||
// tanks = [];
|
||||
// foreach(group in level.killstreak_tank_groups)
|
||||
// {
|
||||
// if(group.active && group.owner_team != player.team)
|
||||
// {
|
||||
// foreach(tank in group.tanks)
|
||||
// {
|
||||
// if(isAlive(tank))
|
||||
// tanks[tanks.size] = tank;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return tanks;
|
||||
//}
|
||||
|
||||
//tank_stinger_target_pos()
|
||||
//{
|
||||
// return self.origin + (0,0,120);
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
609
raw/maps/mp/killstreaks/streak_mp_dam.gsc
Normal file
609
raw/maps/mp/killstreaks/streak_mp_dam.gsc
Normal file
@ -0,0 +1,609 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
level.killstreakFuncs[ "mp_dam" ] = ::tryUseDamKillstreak;
|
||||
level.mapKillStreak = "mp_dam";
|
||||
|
||||
level.dam_killstreak_duration = 30;
|
||||
/#
|
||||
//Used for tuning purposes. Please do not delete.
|
||||
// SetDvarIfUninitialized( "scr_dam_killstreak_duration", 30 );
|
||||
#/
|
||||
|
||||
precacheshader("s1_railgun_hud_reticle_center");
|
||||
precacheshader("s1_railgun_hud_reticle_meter_circ");
|
||||
precacheshader("s1_railgun_hud_inner_frame_edge");
|
||||
precacheshader("s1_railgun_hud_inner_frame_edge_right");
|
||||
|
||||
PreCacheItem( "killstreak_dam_mp" );
|
||||
|
||||
PreCacheModel( "mp_dam_large_caliber_turret" );
|
||||
|
||||
level.HUDItem = [];
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: tryUseDamKillstreak( <lifeId> )"
|
||||
"Summary: the killstreak function added to level.killstreakFuncs[]."
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"MandatoryArg: <lifeId>: "
|
||||
"Example: "
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
tryUseDamKillstreak( lifeId, modules )
|
||||
{
|
||||
if ( isDefined( level.mp_dam_player ) )
|
||||
{
|
||||
self iPrintLnBold( &"MP_DAM_IN_USE" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isUsingRemote() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isAirDenied() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isEMPed() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(IsDefined(self.ControllingWarbird)&&self.ControllingWarbird==true)
|
||||
{
|
||||
self iprintLnBold( &"MP_WARBIRD_ACTIVE" );
|
||||
return false;
|
||||
}
|
||||
|
||||
result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak();
|
||||
if ( result != "success" )
|
||||
return false;
|
||||
self setUsingRemote( "mp_dam" );
|
||||
|
||||
result = setMPDamPlayer( self );
|
||||
|
||||
// this needs to get set after we say the player is using it because this could get set to true and then they leave the game
|
||||
// this fixes a bug where a player calls it, leaves before getting fully in it and then no one else can call it because it thinks it's being used
|
||||
if( IsDefined( result ) && result )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "mp_dam", self.origin );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( isUsingRemote() )
|
||||
self clearUsingRemote();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: setMPDamPlayer( <player> )"
|
||||
"Summary: kicks off most of the script that controls mp_dam map killstreak functionality."
|
||||
"Module: Entity"
|
||||
"CallOn: NA"
|
||||
"MandatoryArg: <player>: the player who is using the mp_dam map killstreak."
|
||||
"Example: result = setMPDamPlayer( self );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
setMPDamPlayer( player )
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
|
||||
if( IsDefined( level.mp_dam_player ) )
|
||||
return false;
|
||||
|
||||
level.mp_dam_player = player;
|
||||
|
||||
player NotifyOnPlayerCommand( "SwitchTurret", "weapnext" );
|
||||
player NotifyOnPlayerCommand( "SwitchVisionMode", "+actionslot 1" );
|
||||
|
||||
//player openMenu( "ac130timer" );
|
||||
|
||||
thread teamPlayerCardSplash( "used_mp_dam", player );
|
||||
|
||||
// with the way we do visionsets we need to wait for the clearRideIntro() is done before we set thermal
|
||||
player thread waitSetThermal( 1.0 );
|
||||
|
||||
//player thread overlay( player );
|
||||
|
||||
if ( getDvarInt( "camera_thirdPerson" ) )
|
||||
player setThirdPersonDOF( false );
|
||||
|
||||
/#
|
||||
// level.dam_killstreak_duration = GetDvarInt( "scr_dam_killstreak_duration", 30 );
|
||||
#/
|
||||
|
||||
player SetClientOmnvar( "ui_damturret_countdown", (level.dam_killstreak_duration*1000) + GetTime() ); //UI countdowns automatically subtract the current match time, so we have to add it back here with GetTime().
|
||||
|
||||
player thread CycleTurretControl();
|
||||
player thread removeMPDamPlayerAfterTime( level.dam_killstreak_duration );
|
||||
player thread removeMPDamPlayerOnDisconnect();
|
||||
player thread removeMPDamPlayerOnChangeTeams();
|
||||
player thread removeMPDamPlayerOnSpectate();
|
||||
player thread removeMPDamPlayerOnGameCleanup();
|
||||
player thread removeMPDamPlayerOnCommand();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CycleTurretControl()
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
|
||||
player = self;
|
||||
|
||||
if(IsDefined(level.DamTurrets))
|
||||
{
|
||||
player EnableSlowAim(.2,.2);
|
||||
while(1)
|
||||
{
|
||||
for( i=0;i<level.DamTurrets.size;i++)
|
||||
{
|
||||
turret = level.DamTurrets[i];
|
||||
|
||||
turret.owner = player;
|
||||
turret.team = player.team;
|
||||
turret.pers["team"] = player.team;
|
||||
if ( level.teamBased )
|
||||
{
|
||||
turret SetTurretTeam( player.team );
|
||||
}
|
||||
player.turret = turret;
|
||||
|
||||
turret setmode( "sentry_manual" );
|
||||
turret SetSentryOwner( player );
|
||||
player thread shotFired( turret );
|
||||
|
||||
if(i==0)
|
||||
player PlayerLinkWeaponViewToDelta(turret, "tag_player", 0, 60, 30, 5, 58, false );
|
||||
else
|
||||
player PlayerLinkWeaponViewToDelta(turret, "tag_player", 0, 40, 50, 5, 58, false );
|
||||
|
||||
player PlayerLinkedSetUseBaseAngleForViewClamp( true );
|
||||
|
||||
player RemoteControlTurret(turret,40);
|
||||
//player thread movementAudio( turret );
|
||||
|
||||
wait .5;
|
||||
|
||||
if(IsDefined(self.DamThermal) && self.DamThermal == true)
|
||||
self ThermalVisionOn();
|
||||
|
||||
self SetClientOmnvar( "ui_damturret_toggle", true );
|
||||
|
||||
self waittill( "SwitchTurret" );
|
||||
|
||||
if(IsDefined(self.DamThermal) && self.DamThermal == true)
|
||||
self ThermalVisionOff();
|
||||
|
||||
self SetClientOmnvar( "ui_damturret_toggle", false );
|
||||
|
||||
player TransitionTurret();
|
||||
|
||||
player Unlink();
|
||||
player RemoteControlTurretOff(turret);
|
||||
|
||||
//reset default turret mode
|
||||
turret SetMode( "manual" );
|
||||
turret SetTargetEntity( level.DamDefaultAimEnt );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TransitionTurret()
|
||||
{
|
||||
self VisionSetNakedForPlayer( "black_bw", 0.75 );
|
||||
wait .8;
|
||||
self revertVisionSetForTurretPlayer( .5 ); // go to default visionset
|
||||
}
|
||||
|
||||
revertVisionSetForTurretPlayer( time )
|
||||
{
|
||||
AssertEx( isPlayer( self ), "revertVisionSetForPlayer() called on a non player entity" );
|
||||
|
||||
if( !isDefined( time ) )
|
||||
time = 1;
|
||||
if ( IsDefined( level.nukeDetonated ) && isDefined( level.nukeVisionSet ) )
|
||||
self VisionSetNakedForPlayer( level.nukeVisionSet, time ); // go to nuke visionset
|
||||
else if( isDefined( self.usingRemote ) && isDefined( self.rideVisionSet ) )
|
||||
self VisionSetNakedForPlayer( self.rideVisionSet, time ); // go to ride visionset
|
||||
else
|
||||
self VisionSetNakedForPlayer( "", time ); // go to default visionset
|
||||
}
|
||||
|
||||
|
||||
waitSetThermal( delay )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
level endon( "mp_dam_player_removed" );
|
||||
|
||||
wait( delay );
|
||||
|
||||
self VisionSetThermalForPlayer( game["thermal_vision"], 0 );
|
||||
self ThermalVisionFOFOverlayOn();
|
||||
self SetBlurForPlayer( 1.1, 0 );
|
||||
|
||||
//self thread thermalVision();
|
||||
}
|
||||
|
||||
thermalVision()
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
// self endon ( "SwitchTurret" );
|
||||
|
||||
// self VisionSetThermalForPlayer( "paris_ac130_thermal", 0.25 );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self.DamThermal = false;
|
||||
self waittill( "SwitchVisionMode" );
|
||||
self ThermalVisionOn();
|
||||
|
||||
self.DamThermal = true;
|
||||
self waittill( "SwitchVisionMode" );
|
||||
self ThermalVisionOff();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: createMPDamKillstreakClock()"
|
||||
"Summary: HUD clock for mp_dam map-based killstreak duration."
|
||||
"Module: Entity"
|
||||
"CallOn: the player using the mp_dam map-based killstreak."
|
||||
"Example: player thread createMPDamKillstreakClock()"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
createMPDamKillstreakClock()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
|
||||
self.dam_clock = maps\mp\gametypes\_hud_util::createTimer( "hudsmall", 0.9 );
|
||||
self.dam_clock maps\mp\gametypes\_hud_util::setPoint( "CENTER", "CENTER", 0, -145 );
|
||||
/#
|
||||
// level.dam_killstreak_duration = GetDvarFloat( "scr_dam_killstreak_duration", 30.0 );
|
||||
#/
|
||||
self.dam_clock setTimer( level.dam_killstreak_duration );
|
||||
self.dam_clock.color = ( 1.0, 1.0, 1.0 );
|
||||
self.dam_clock.archived = false;
|
||||
self.dam_clock.foreground = true;
|
||||
|
||||
self thread destroyMPDamKillstreakClock();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: destroyMPDamKillstreakClock()"
|
||||
"Summary: cleans up the timer for the mp_dam map-based killstreak HUD."
|
||||
"Module: Entity"
|
||||
"CallOn: the player using the mp_dam map-based killstreak."
|
||||
"Example: player thread destroyMPDamKillstreakClock()"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
destroyMPDamKillstreakClock()
|
||||
{
|
||||
self waittill( "mp_dam_player_removed" );
|
||||
|
||||
if ( IsDefined( self.dam_clock ) )
|
||||
{
|
||||
self.dam_clock Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: removeMPDamPlayerOnCommand()"
|
||||
"Summary: removes the dam killstreak player if the player holds down the exit button"
|
||||
"Module: Entity"
|
||||
"CallOn: the player controlling the dam map killstreak"
|
||||
"Example: player thread removeMPDamPlayerOnCommand()"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
removeMPDamPlayerOnCommand()
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
button_hold_time = 0;
|
||||
while ( self UseButtonPressed() )
|
||||
{
|
||||
button_hold_time += 0.05;
|
||||
if ( button_hold_time > 0.75 )
|
||||
{
|
||||
level thread removeMPDamPlayer( self, false );
|
||||
return;
|
||||
}
|
||||
wait(0.05);
|
||||
}
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
removeMPDamPlayerOnGameCleanup()
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
|
||||
level waittill ( "game_cleanup" );
|
||||
|
||||
level thread removeMPDamPlayer( self, false );
|
||||
}
|
||||
|
||||
|
||||
removeMPDamPlayerOnDeath()
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
level thread removeMPDamPlayer( self, false );
|
||||
}
|
||||
|
||||
|
||||
removeMPDamPlayerOnDisconnect()
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
|
||||
self waittill ( "disconnect" );
|
||||
|
||||
level thread removeMPDamPlayer( self, true );
|
||||
}
|
||||
|
||||
|
||||
removeMPDamPlayerOnChangeTeams()
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
|
||||
self waittill ( "joined_team" );
|
||||
|
||||
level thread removeMPDamPlayer( self, false);
|
||||
}
|
||||
|
||||
|
||||
removeMPDamPlayerOnSpectate()
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
|
||||
self waittill_any ( "joined_spectators", "spawned" );
|
||||
|
||||
level thread removeMPDamPlayer( self, false);
|
||||
}
|
||||
|
||||
|
||||
removeMPDamPlayerAfterTime( removeDelay )
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
|
||||
if ( self _hasPerk( "specialty_blackbox" ) && IsDefined( self.specialty_blackbox_bonus ) )
|
||||
{
|
||||
removeDelay *= self.specialty_blackbox_bonus;
|
||||
}
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( removeDelay );
|
||||
|
||||
level thread removeMPDamPlayer( self, false );
|
||||
}
|
||||
|
||||
|
||||
removeMPDamPlayer( player, disconnected )
|
||||
{
|
||||
player notify ( "mp_dam_player_removed" );
|
||||
level notify ( "mp_dam_player_removed" );
|
||||
|
||||
waittillframeend;
|
||||
|
||||
if ( !disconnected )
|
||||
{
|
||||
player SetClientOmnvar( "ui_damturret_toggle", false );
|
||||
player SetBlurForPlayer( 0, 0 );
|
||||
player ThermalVisionOff();
|
||||
player ThermalVisionFOFOverlayOff();
|
||||
player RemoteControlTurretOff( player.turret );
|
||||
player Unlink();
|
||||
player clearUsingRemote();
|
||||
player revertVisionSetForPlayer( .5 ); // go to default visionset
|
||||
player DisableSlowAim();
|
||||
|
||||
if ( getDvarInt( "camera_thirdPerson" ) )
|
||||
player setThirdPersonDOF( true );
|
||||
|
||||
if ( isDefined( player.darkScreenOverlay ) )
|
||||
player.darkScreenOverlay destroy();
|
||||
|
||||
foreach(turret in level.DamTurrets)
|
||||
{
|
||||
turret SetMode( "manual" );
|
||||
turret SetTargetEntity( level.DamDefaultAimEnt );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: this might already be undefined if the player disconnected... need a better solution.
|
||||
// we could set it to "true" or something... but we'll have to check places it is used for potential issues with that.
|
||||
level.mp_dam_player = undefined;
|
||||
}
|
||||
|
||||
|
||||
overlay( player )
|
||||
{
|
||||
level.HUDItem[ "thermal_vision" ] = NewClientHudElem( player );
|
||||
level.HUDItem[ "thermal_vision" ].x = 200;
|
||||
level.HUDItem[ "thermal_vision" ].y = 0;
|
||||
level.HUDItem[ "thermal_vision" ].alignX = "left";
|
||||
level.HUDItem[ "thermal_vision" ].alignY = "top";
|
||||
level.HUDItem[ "thermal_vision" ].horzAlign = "left";
|
||||
level.HUDItem[ "thermal_vision" ].vertAlign = "top";
|
||||
level.HUDItem[ "thermal_vision" ].fontScale = 2.5;
|
||||
level.HUDItem[ "thermal_vision" ] SetText( &"AC130_HUD_FLIR" );
|
||||
level.HUDItem[ "thermal_vision" ].alpha = 1.0;
|
||||
|
||||
level.HUDItem[ "enhanced_vision" ] = NewClientHudElem( player );
|
||||
level.HUDItem[ "enhanced_vision" ].x = -200;
|
||||
level.HUDItem[ "enhanced_vision" ].y = 0;
|
||||
level.HUDItem[ "enhanced_vision" ].alignX = "right";
|
||||
level.HUDItem[ "enhanced_vision" ].alignY = "top";
|
||||
level.HUDItem[ "enhanced_vision" ].horzAlign = "right";
|
||||
level.HUDItem[ "enhanced_vision" ].vertAlign = "top";
|
||||
level.HUDItem[ "enhanced_vision" ].fontScale = 2.5;
|
||||
level.HUDItem[ "enhanced_vision" ] SetText( &"AC130_HUD_OPTICS" );
|
||||
level.HUDItem[ "enhanced_vision" ].alpha = 1.0;
|
||||
|
||||
player setBlurForPlayer( 1.2, 0 );
|
||||
}
|
||||
|
||||
shotFired( turret )
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
self endon ( "SwitchTurret" );
|
||||
for (;;)
|
||||
{
|
||||
turret waittill( "turret_fire" );
|
||||
|
||||
earthquake( 0.25, 1.0, turret.origin, 3500 );
|
||||
PlayRumbleOnPosition( "artillery_rumble", turret.origin);
|
||||
self thread shotFiredDarkScreenOverlay();
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
shotFiredDarkScreenOverlay()
|
||||
{
|
||||
self endon( "mp_dam_player_removed" );
|
||||
self notify( "darkScreenOverlay" );
|
||||
self endon( "darkScreenOverlay" );
|
||||
|
||||
if ( !isdefined( self.darkScreenOverlay ) )
|
||||
{
|
||||
self.darkScreenOverlay = NewClientHudElem( self );
|
||||
self.darkScreenOverlay.x = 0;
|
||||
self.darkScreenOverlay.y = 0;
|
||||
self.darkScreenOverlay.alignX = "left";
|
||||
self.darkScreenOverlay.alignY = "top";
|
||||
self.darkScreenOverlay.horzAlign = "fullscreen";
|
||||
self.darkScreenOverlay.vertAlign = "fullscreen";
|
||||
self.darkScreenOverlay setshader ( "black", 640, 480 );
|
||||
self.darkScreenOverlay.sort = -10;
|
||||
self.darkScreenOverlay.alpha = 0.0;
|
||||
}
|
||||
|
||||
self.darkScreenOverlay.alpha = 0.0;
|
||||
self.darkScreenOverlay fadeOverTime( 0.05 );
|
||||
self.darkScreenOverlay.alpha = 0.2;
|
||||
wait 0.4;
|
||||
self.darkScreenOverlay fadeOverTime( 0.8 );
|
||||
self.darkScreenOverlay.alpha = 0.0;
|
||||
}
|
||||
|
||||
movementAudio( turret )
|
||||
{
|
||||
self endon ( "mp_dam_player_removed" );
|
||||
self endon ( "SwitchTurret" );
|
||||
|
||||
if ( !IsDefined( level.aud ) )
|
||||
{
|
||||
level.aud = spawnstruct();
|
||||
}
|
||||
|
||||
self thread movementAudioCleanup();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
yaw_rate = turret GetTurretYawRate();
|
||||
yaw_rate = abs( yaw_rate );
|
||||
|
||||
pitch_rate = turret GetTurretPitchRate();
|
||||
pitch_rate = abs( pitch_rate );
|
||||
|
||||
if ( yaw_rate > 0.1 )
|
||||
{
|
||||
if ( !IsDefined( level.aud.turretYawLp ) )
|
||||
{
|
||||
level.aud.turretYawLp = spawn( "script_origin", turret.origin );
|
||||
level.aud.turretYawLp PlayLoopSound("wpn_railgun_dam_lat_move_lp");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IsDefined( level.aud.turretYawLp ) )
|
||||
{
|
||||
level.aud.turretYawLp StopLoopSound();
|
||||
level.aud.turretYawLp Delete();
|
||||
level.aud.turretYawLp = undefined;
|
||||
turret PlaySound("wpn_railgun_dam_lat_stop");
|
||||
}
|
||||
}
|
||||
|
||||
if ( pitch_rate > 0.1 )
|
||||
{
|
||||
if ( !IsDefined( level.aud.turretPitchLp ) )
|
||||
{
|
||||
level.aud.turretPitchLp = spawn( "script_origin", turret.origin );
|
||||
level.aud.turretPitchLp PlayLoopSound("wpn_railgun_dam_vert_move_lp");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IsDefined( level.aud.turretPitchLp ) )
|
||||
{
|
||||
level.aud.turretPitchLp StopLoopSound();
|
||||
level.aud.turretPitchLp Delete();
|
||||
level.aud.turretPitchLp = undefined;
|
||||
turret PlaySound("wpn_railgun_dam_vert_stop");
|
||||
}
|
||||
}
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
movementAudioCleanup()
|
||||
{
|
||||
self waittill( "mp_dam_player_removed" );
|
||||
|
||||
if ( IsDefined( level.aud.turretYawLp ) )
|
||||
{
|
||||
level.aud.turretYawLp StopLoopSound();
|
||||
level.aud.turretYawLp Delete();
|
||||
level.aud.turretYawLp = undefined;
|
||||
}
|
||||
|
||||
if ( IsDefined( level.aud.turretPitchLp ) )
|
||||
{
|
||||
level.aud.turretPitchLp StopLoopSound();
|
||||
level.aud.turretPitchLp Delete();
|
||||
level.aud.turretPitchLp = undefined;
|
||||
}
|
||||
}
|
323
raw/maps/mp/killstreaks/streak_mp_detroit.gsc
Normal file
323
raw/maps/mp/killstreaks/streak_mp_detroit.gsc
Normal file
@ -0,0 +1,323 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\mp_detroit_events;
|
||||
|
||||
init()
|
||||
{
|
||||
level.killstreakWieldWeapons["detroit_tram_turret_mp"] = "mp_detroit";
|
||||
|
||||
level.killstreakFuncs["mp_detroit"] = ::tryUseKillstreak;
|
||||
|
||||
level.mapKillStreak = "mp_detroit";
|
||||
level.mapKillstreakPickupString = &"MP_DETROIT_MAP_KILLSTREAK_PICKUP";
|
||||
|
||||
//level.mapCustomBotKillstreakFunc = ::setupBotsForMapKillstreak;
|
||||
|
||||
level.getAerialKillstreakArray = ::tramLockOnEntsForTeam;
|
||||
|
||||
level.streak_trams = GetEntArray("streak_tram", "targetname");
|
||||
array_thread(level.streak_trams, ::tram_init);
|
||||
array_thread(level.streak_trams, ::tram_killstreak_init);
|
||||
/#
|
||||
array_thread(level.streak_trams, ::tram_spline_debug);
|
||||
#/
|
||||
|
||||
|
||||
level.detroitTramObjIds = [];
|
||||
teams = ["allies","axis"];
|
||||
foreach(team in teams)
|
||||
{
|
||||
level.detroitTramObjIds[team] = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
objective_add( level.detroitTramObjIds[team], "invisible", (0,0,0) );
|
||||
objective_icon( level.detroitTramObjIds[team], ter_op(team=="allies", "compass_objpoint_tram_turret_friendly", "compass_objpoint_tram_turret_enemy") );
|
||||
}
|
||||
}
|
||||
|
||||
setupBotsForMapKillstreak()
|
||||
{
|
||||
level thread maps\mp\bots\_bots_ks::bot_register_killstreak_func( "mp_detroit", maps\mp\bots\_bots_ks::bot_killstreak_simple_use );
|
||||
}
|
||||
|
||||
tram_killstreak_init()
|
||||
{
|
||||
self.getStingerTargetPosFunc = ::tram_stinger_target_pos;
|
||||
}
|
||||
|
||||
tram_stinger_target_pos()
|
||||
{
|
||||
return self GetTagOrigin("tag_turret");
|
||||
}
|
||||
|
||||
tramLockOnEntsForTeam( team )
|
||||
{
|
||||
lockOnEnts = [];
|
||||
foreach(tram in level.streak_trams)
|
||||
{
|
||||
if( tram.active && IsDefined(tram.owner) && (!level.teamBased || tram.owner.team == team) )
|
||||
{
|
||||
lockOnEnts[lockOnEnts.size] = tram;
|
||||
}
|
||||
}
|
||||
|
||||
return lockOnEnts;
|
||||
}
|
||||
|
||||
tryUseKillstreak(lifeId,modules)
|
||||
{
|
||||
if ( level.fauxVehicleCount + 1 >= maxVehiclesAllowed() )
|
||||
{
|
||||
self IPrintLnBold( &"MP_TOO_MANY_VEHICLES" );
|
||||
return false;
|
||||
}
|
||||
|
||||
killstreak_active = false;
|
||||
foreach(tram in level.streak_trams)
|
||||
{
|
||||
if( tram.active )
|
||||
{
|
||||
killstreak_active = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
tram maps\mp\mp_detroit_events::tram_reset();
|
||||
}
|
||||
}
|
||||
|
||||
killstreak_tram = undefined;
|
||||
if ( !killstreak_active )
|
||||
{
|
||||
level.streak_trams = SortByDistance(level.streak_trams, self.origin);
|
||||
killstreak_tram = level.streak_trams[0];
|
||||
}
|
||||
|
||||
if( IsDefined(killstreak_tram) )
|
||||
{
|
||||
result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak( "mp_detroit_tram" );
|
||||
if ( result != "success" )
|
||||
return false;
|
||||
|
||||
incrementFauxVehicleCount();
|
||||
self thread run_tram_killstreak(killstreak_tram);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
self IPrintLnBold(&"MP_DETROIT_MAP_KILLSTREAK_NOT_AVAILABLE");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
run_tram_killstreak(tram)
|
||||
{
|
||||
time = 35; //+5, Give the streak time to drive into the playspace
|
||||
tram maps\mp\mp_detroit_events::tram_reset();
|
||||
|
||||
tram.owner = self;
|
||||
tram.team = self.team;
|
||||
tram.turret = tram spawn_tram_turret();
|
||||
tram.isLeaving = false;
|
||||
tram.stopDamageFunc = false;
|
||||
|
||||
tram tram_show_icon();
|
||||
tram thread maps\mp\gametypes\_damage::setEntityDamageCallback( 1000, undefined, ::tramOnDeath, undefined, true );
|
||||
|
||||
self NotifyOnPlayerCommand( "CancelTramStart" , "+usereload" );
|
||||
self NotifyOnPlayerCommand( "CancelTramEnd", "-usereload" );
|
||||
|
||||
self setUsingRemote( "mp_detroit_tram" );
|
||||
|
||||
self RemoteControlTurret( tram.turret, 30, tram.angles[1]-90 );
|
||||
tram thread tram_update_shooting_location();
|
||||
|
||||
tram make_entity_sentient_mp( tram.team );
|
||||
|
||||
self PlayerLinkWeaponViewToDelta( tram, "tag_player", 0, 180, 180, 0, 90, false );
|
||||
self PlayerLinkedSetViewZNear( false );
|
||||
|
||||
self ThermalVisionFOFOverlayON();
|
||||
self SetClientOmnvar("ui_detroit_tram_turret", true);
|
||||
|
||||
tram thread tram_killstreak_team_change_watch();
|
||||
tram thread tram_killstreak_cancel_watch();
|
||||
tram thread tram_killstreak_exit_watch();
|
||||
tram thread tram_killstreak_move(time);
|
||||
tram thread tram_killstreak_match_ended();
|
||||
tram thread maps\mp\killstreaks\_killstreaks::updateAerialKillStreakMarker();
|
||||
}
|
||||
|
||||
tram_update_shooting_location()
|
||||
{
|
||||
player = self.owner;
|
||||
|
||||
self endon("player_exit");
|
||||
player endon("disconnect");
|
||||
self.turret endon( "death" );
|
||||
|
||||
self.target_ent = Spawn( "script_model", ( 0, 0, 0 ) );
|
||||
self.target_ent SetModel( "tag_origin" );
|
||||
self.turret TurretSetGroundAimEntity( self.target_ent );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
startpoint = self.turret GetTagOrigin( "tag_player" );
|
||||
endpoint = self.turret GetTagOrigin( "tag_player" ) + AnglesToForward( self.turret GetTagAngles( "tag_player" ) ) * 20000;
|
||||
trace = BulletTrace( startpoint, endpoint, false, self.turret );
|
||||
|
||||
point = trace[ "position" ];
|
||||
|
||||
self.target_ent.origin = point;
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
tram_killstreak_match_ended()
|
||||
{
|
||||
self endon("player_exit");
|
||||
level waittill( "game_ended" );
|
||||
self notify("player_exit");
|
||||
}
|
||||
|
||||
tram_show_icon()
|
||||
{
|
||||
foreach(team, objId in level.detroitTramObjIds)
|
||||
{
|
||||
objective_state( objId, "active" );
|
||||
if ( team == "allies" )
|
||||
Objective_PlayerTeam( objId, self.owner GetEntityNumber());
|
||||
else
|
||||
Objective_PlayerEnemyTeam( objId, self.owner GetEntityNumber());
|
||||
Objective_OnEntityWithRotation( objId, self.turret.obj_ent );
|
||||
}
|
||||
}
|
||||
|
||||
tram_hide_icon()
|
||||
{
|
||||
foreach(team, objId in level.detroitTramObjIds)
|
||||
{
|
||||
objective_state( objId, "invisible" );
|
||||
}
|
||||
}
|
||||
|
||||
tramOnDeath( attacker, weapon, meansOfDeath, damage )
|
||||
{
|
||||
self notify("player_exit");
|
||||
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, meansOfDeath, damage, "map_killstreak_destroyed", undefined, "callout_destroyed_tram_turet", true );
|
||||
|
||||
waitframe();
|
||||
|
||||
PlayFXOnTag( getfx("vehicle_pdrone_explosion"), self, "tag_turret" );
|
||||
|
||||
if(IsDefined(self.turret))
|
||||
self.turret Delete();
|
||||
if(IsDefined(self.target_ent))
|
||||
self.target_ent Delete();
|
||||
}
|
||||
|
||||
tram_killstreak_cancel_watch()
|
||||
{
|
||||
player = self.owner;
|
||||
|
||||
self endon("player_exit");
|
||||
player endon("disconnect");
|
||||
|
||||
while(1)
|
||||
{
|
||||
player waittill("CancelTramStart");
|
||||
|
||||
result = player waittill_any_timeout(1, "CancelTramEnd");
|
||||
|
||||
if( result == "timeout" )
|
||||
{
|
||||
self notify("player_exit");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tram_killstreak_team_change_watch()
|
||||
{
|
||||
player = self.owner;
|
||||
|
||||
self endon("player_exit");
|
||||
player endon("disconnect");
|
||||
|
||||
player waittill_any( "joined_team", "joined_spectators" );
|
||||
|
||||
self notify("player_exit");
|
||||
}
|
||||
|
||||
|
||||
tram_killstreak_move(time)
|
||||
{
|
||||
self maps\mp\mp_detroit_events::tram_spline_move(time);
|
||||
|
||||
self notify("player_exit");
|
||||
|
||||
self waittill("trackEnd");
|
||||
|
||||
if(IsDefined(self.turret))
|
||||
self.turret Delete();
|
||||
if(IsDefined(self.target_ent))
|
||||
self.target_ent Delete();
|
||||
}
|
||||
|
||||
tram_killstreak_exit_watch()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
self waittill("player_exit");
|
||||
|
||||
tram_hide_icon();
|
||||
|
||||
self.owner SetClientOmnvar("ui_detroit_tram_turret", false);
|
||||
self.owner ThermalVisionFOFOverlayOff();
|
||||
|
||||
self.owner unlink();
|
||||
self.owner RemoteControlTurretOff( self.turret );
|
||||
self FreeEntitySentient();
|
||||
self.owner clearUsingRemote();
|
||||
self.owner = undefined;
|
||||
self thread maps\mp\killstreaks\_killstreaks::updateAerialKillStreakMarker();
|
||||
self notify("leaving");
|
||||
self.isLeaving = true;
|
||||
}
|
||||
|
||||
spawn_tram_turret()
|
||||
{
|
||||
linktotag = "tag_turret";
|
||||
|
||||
spawned_turret = SpawnTurret( "misc_turret", self.origin, "detroit_tram_turret_mp", false );
|
||||
spawned_turret.angles = (0, 0, 0);
|
||||
spawned_turret SetModel( "vehicle_xh9_warbird_turret_detroit_mp" );
|
||||
spawned_turret SetDefaultDropPitch( 45.0 );
|
||||
spawned_turret LinkTo( self, linktotag, (0,0,0), (0,0,0) );
|
||||
spawned_turret.owner = self.owner;
|
||||
spawned_turret.health = 99999;
|
||||
spawned_turret.maxHealth = 1000;
|
||||
spawned_turret.damageTaken = 0;
|
||||
spawned_turret.stunned = false;
|
||||
spawned_turret.stunnedTime = 0.0;
|
||||
spawned_turret SetCanDamage( false );
|
||||
spawned_turret SetCanRadiusDamage( false );
|
||||
spawned_turret.team = self.team;
|
||||
spawned_turret.pers["team"] = self.team;
|
||||
if ( level.teamBased )
|
||||
{
|
||||
spawned_turret SetTurretTeam( self.team );
|
||||
}
|
||||
spawned_turret SetMode( "sentry_manual" );
|
||||
spawned_turret SetSentryOwner( self.owner );
|
||||
spawned_turret SetTurretMinimapVisible( false );
|
||||
spawned_turret.chopper = self;
|
||||
|
||||
obj_ent = Spawn("script_model", self.origin);
|
||||
obj_ent Linkto(spawned_turret, "tag_aim_pivot", (0,0,0), (0,0,0));
|
||||
obj_ent SetContents(0);
|
||||
spawned_turret thread delete_on_death(obj_ent);
|
||||
spawned_turret.obj_ent = obj_ent;
|
||||
|
||||
return spawned_turret;
|
||||
}
|
326
raw/maps/mp/killstreaks/streak_mp_instinct.gsc
Normal file
326
raw/maps/mp/killstreaks/streak_mp_instinct.gsc
Normal file
@ -0,0 +1,326 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
|
||||
|
||||
//===========================================
|
||||
// constants
|
||||
//===========================================
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_GAME = 3;
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_PLAYER = 3;
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER = 3;
|
||||
CONST_DOG_KILL_COUNT_LIMIT = 2;
|
||||
CONST_STREAK_TIMEOUT = 60;
|
||||
CONST_STREAK_RETREAT_TIMEOUT=20;
|
||||
|
||||
init()
|
||||
{
|
||||
level.killstreakFuncs["mp_instinct"] = ::tryUseMPInstinct;
|
||||
level.mapKillStreak = "mp_instinct";
|
||||
|
||||
level.InstinctDogs = [];
|
||||
|
||||
//PreCacheModel( "sb_pig_table_noguts" );
|
||||
PreCacheModel( "animal_iw6_dog_a" );
|
||||
|
||||
level.killstreakWieldWeapons[ "killstreak_instinct_mp" ] = "mp_instinct";
|
||||
|
||||
level.InstinctDogSpawnPoints =[];
|
||||
|
||||
SpawnPoints = GetNodeArray( "InstinctDogSpawnPoint", "targetname" );
|
||||
|
||||
foreach( point in SpawnPoints)
|
||||
{
|
||||
tempPoint = SpawnStruct();
|
||||
tempPoint.SpawnPoint = point;
|
||||
tempPoint.Weight = 0;
|
||||
|
||||
level.InstinctDogSpawnPoints = add_to_array(level.InstinctDogSpawnPoints, tempPoint);
|
||||
}
|
||||
|
||||
OnPlayerConnect();
|
||||
}
|
||||
|
||||
tryUseMPInstinct(lifeId, streakName)
|
||||
{
|
||||
return spawnInstinctDogs();
|
||||
}
|
||||
|
||||
|
||||
setMPInstinctPlayer( player )
|
||||
{
|
||||
level.mp_instinct_owner = player;
|
||||
|
||||
thread teamPlayerCardSplash( "used_mp_instinct", player );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
OnPlayerConnect()
|
||||
{
|
||||
connectCount =0;
|
||||
for(;;)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
|
||||
if(connectCount==0)
|
||||
{
|
||||
if ( !IsDefined(level.isHorde) || !level.isHorde )
|
||||
{
|
||||
level.agent_funcs["dog"]["think"] = maps\mp\agents\dog\_instinct_dog_think::main;
|
||||
level.agent_funcs["dog"]["on_killed"] = ::on_agent_dog_killed;
|
||||
}
|
||||
}
|
||||
|
||||
connectCount++;
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
self thread MonitorKills();
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// spawnInstinctDogs //spawn three from smart spawn locations around the map.
|
||||
//===========================================
|
||||
spawnInstinctDogs()
|
||||
{
|
||||
// limit the number of active "dog" agents allowed per game
|
||||
if( getNumActiveAgents( "dog" ) >= CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_GAME )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_DOGS" );
|
||||
return false;
|
||||
}
|
||||
// limit the number of active "dog" agents allowed per player
|
||||
if( getNumOwnedActiveAgentsByType( self, "dog" ) >= CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_PLAYER )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_ALREADY_HAVE_DOG" );
|
||||
return false;
|
||||
}
|
||||
// limit the number of active agents allowed per player
|
||||
if( getNumOwnedActiveAgents( self ) >= CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AGENT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: we should probably do a queue system for these, so the player can call it but it'll go into a queue for when an agent dies to open up a spot
|
||||
// limit the number of active agents allowed per player
|
||||
maxagents = GetMaxAgents();
|
||||
if( getNumActiveAgents() >= maxagents )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_UNAVAILABLE" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the player is still alive before the agent trys to spawn on the player
|
||||
if( !isReallyAlive( self ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ConnectAndSpawnInstinctDogPack();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SortInstinctDogSpawnPoints()
|
||||
{
|
||||
foreach(point in level.InstinctDogSpawnPoints)
|
||||
{
|
||||
point.Weight=0;
|
||||
//rate points by distance from owner
|
||||
tempDistance = Distance(point.SpawnPoint.origin,self.origin);
|
||||
if(tempDistance<1500)
|
||||
point.Weight=1;
|
||||
else if(tempDistance>1500 && tempDistance<2000)
|
||||
point.Weight=2;
|
||||
else if(tempDistance>2000 && tempDistance<2500)
|
||||
point.Weight=3;
|
||||
else if(tempDistance>2500)
|
||||
point.Weight=4;
|
||||
|
||||
//rate points by los and distance to other players
|
||||
foreach(player in level.players)
|
||||
{
|
||||
start_pos = point.SpawnPoint.origin;
|
||||
target_pos = player.origin;
|
||||
LineOfSightCheck = SightTracePassed(start_pos ,target_pos , false, player);
|
||||
|
||||
if( LineOfSightCheck )
|
||||
point.Weight--;
|
||||
|
||||
tempDistance = Distance(point.SpawnPoint.origin,player.origin);
|
||||
if(tempDistance<256)
|
||||
point.Weight--;
|
||||
}
|
||||
}
|
||||
|
||||
level.InstinctDogSpawnPoints = array_sort_with_func(level.InstinctDogSpawnPoints, ::isPointAScoredHigherThanB);
|
||||
}
|
||||
|
||||
PickInstinctDogSpawnPoint()
|
||||
{
|
||||
SpawnPoint = level.InstinctDogSpawnPoints[0];
|
||||
|
||||
level.InstinctDogSpawnPoints = array_remove(level.InstinctDogSpawnPoints, level.InstinctDogSpawnPoints[0]);
|
||||
|
||||
return SpawnPoint;
|
||||
}
|
||||
|
||||
isPointAScoredHigherThanB( a, b )
|
||||
{
|
||||
return ( a.Weight > b.Weight );
|
||||
}
|
||||
|
||||
WaitForTimeout()
|
||||
{
|
||||
self waittill_any_timeout( CONST_STREAK_TIMEOUT, "KillCountMet" );
|
||||
self notify("retreat");
|
||||
DeleteNodes = GetNodeArray("DeletePoint", "targetname");
|
||||
Node = getClosest(self.origin, DeleteNodes);
|
||||
self notify("timeoutRetreat");
|
||||
self ScrAgentSetGoalPos( Node.origin );
|
||||
|
||||
//waits till dog hits retreat node or a timeout for dogs who get stuck...
|
||||
self waittill_any_timeout(CONST_STREAK_RETREAT_TIMEOUT, "stop_soon" );
|
||||
|
||||
//TODO kill the dog gracefully...
|
||||
self Suicide();
|
||||
self notify("death");
|
||||
}
|
||||
|
||||
ConnectAndSpawnInstinctDogPack()
|
||||
{
|
||||
SortInstinctDogSpawnPoints();
|
||||
for(i=0;i<3;i++)
|
||||
{
|
||||
// try to spawn the agent on a path node near the player
|
||||
nearestPathNode = self PickInstinctDogSpawnPoint( );
|
||||
|
||||
if( !IsDefined(nearestPathNode) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// find an available agent
|
||||
agent = maps\mp\agents\_agent_common::connectNewAgent( "dog" , self.team );
|
||||
if( !IsDefined( agent ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
oldHealth = self.health;
|
||||
newHealth = 25;
|
||||
self.agenthealth = newHealth;
|
||||
self.health = newHealth;
|
||||
self.maxhealth = newHealth;
|
||||
|
||||
level.InstinctDogs = array_add(level.InstinctDogs,agent);
|
||||
|
||||
agent thread WaitForDeath( nearestPathNode );
|
||||
|
||||
// set the agent to the player's team
|
||||
agent set_agent_team( self.team, self );
|
||||
|
||||
spawnOrigin = nearestPathNode.SpawnPoint.origin;
|
||||
spawnAngles = VectorToAngles( self.origin - nearestPathNode.SpawnPoint.origin );
|
||||
|
||||
agent thread [[ agent agentFunc("spawn") ]]( spawnOrigin, spawnAngles, self );
|
||||
|
||||
//agent _setNameplateMaterial( "player_name_bg_green_dog", "player_name_bg_red_dog" );
|
||||
|
||||
if ( IsDefined( self.ballDrone ) && self.ballDrone.ballDroneType == "ball_drone_backup" )
|
||||
{
|
||||
self maps\mp\gametypes\_missions::processChallenge( "ch_twiceasdeadly" );
|
||||
}
|
||||
|
||||
agent thread WaitForTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
WaitForDeath( PathNode )
|
||||
{
|
||||
self waittill_any( "death", "retreat" );
|
||||
level.InstinctDogSpawnPoints = array_add(level.InstinctDogSpawnPoints, PathNode );
|
||||
level.InstinctDogs =array_remove(level.InstinctDogs, self);
|
||||
}
|
||||
|
||||
MonitorKills()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
self waittill("death",instigator);
|
||||
|
||||
foreach(dog in level.InstinctDogs)
|
||||
{
|
||||
if(instigator == dog)
|
||||
{
|
||||
if(!IsDefined(dog.EnemyKills))
|
||||
dog.EnemyKills =0;
|
||||
|
||||
dog.EnemyKills++;
|
||||
|
||||
if(dog.EnemyKills>=CONST_DOG_KILL_COUNT_LIMIT)
|
||||
dog notify("KillCountMet");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// on_agent_dog_killed
|
||||
//=======================================================
|
||||
on_agent_dog_killed( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
self.isActive = false;
|
||||
self.hasDied = false;
|
||||
|
||||
eAttacker.lastKillDogTime = GetTime();
|
||||
|
||||
if ( IsDefined( self.animCBs.OnExit[ self.aiState ] ) )
|
||||
self [[ self.animCBs.OnExit[ self.aiState ] ]] ();
|
||||
|
||||
// award XP for killing agents
|
||||
if( isPlayer( eAttacker ) && IsDefined(self.owner) && (eAttacker != self.owner) )
|
||||
{
|
||||
self.owner leaderDialogOnPlayer( "dog_kia_mp_instinct" );
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( eAttacker, sWeapon, sMeansOfDeath, iDamage, "destroyed_guard_dog" );
|
||||
|
||||
if ( IsPlayer( eAttacker ) )
|
||||
{
|
||||
eAttacker maps\mp\gametypes\_missions::processChallenge( "ch_notsobestfriend" );
|
||||
|
||||
// assume jumping?
|
||||
if ( !self IsOnGround() )
|
||||
{
|
||||
eAttacker maps\mp\gametypes\_missions::processChallenge( "ch_hoopla" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self SetAnimState( "death" );
|
||||
animEntry = self GetAnimEntry();
|
||||
animLength = GetAnimLength( animEntry );
|
||||
|
||||
deathAnimDuration = int( animLength * 1000 ); // duration in milliseconds
|
||||
|
||||
self.body = self CloneAgent( deathAnimDuration );
|
||||
|
||||
self PlaySound( "anml_dog_shot_death" );
|
||||
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
|
||||
self notify( "killanimscript" );
|
||||
}
|
1763
raw/maps/mp/killstreaks/streak_mp_laser2.gsc
Normal file
1763
raw/maps/mp/killstreaks/streak_mp_laser2.gsc
Normal file
File diff suppressed because it is too large
Load Diff
994
raw/maps/mp/killstreaks/streak_mp_prison.gsc
Normal file
994
raw/maps/mp/killstreaks/streak_mp_prison.gsc
Normal file
@ -0,0 +1,994 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
level.mp_prison_killstreak_duration = 25;
|
||||
/#
|
||||
//Used for tuning purposes. Please do not delete.
|
||||
// SetDvarIfUninitialized( "mp_prison_killstreak_duration", 25 );
|
||||
#/
|
||||
|
||||
precacheLocationSelector( "map_artillery_selector" );
|
||||
precacheString( &"KILLSTREAKS_MP_PRISON" );
|
||||
|
||||
PreCacheItem( "prison_turret_mp" );
|
||||
|
||||
PreCacheLaser( "prison_laser" );
|
||||
|
||||
level.mp_prison_InUse = false;
|
||||
level.prison_turrets_alive = 0;
|
||||
level.prison_turret_alarm_sfx = "mp_prison_ks_alarm"; //TODO: request audio.
|
||||
level.prison_turret_burn_sfx = "orbital_laser"; //TODO: request audio.
|
||||
level.prison_turret_warning_light_friendly = LoadFX( "vfx/lights/light_tracking_prison_blink_friendly" );
|
||||
level.prison_turret_warning_light_enemy = LoadFX( "vfx/lights/light_tracking_prison_blink_enemy" );
|
||||
level.prison_turret_laser_glow = LoadFX( "vfx/lights/prison_tracking_laser_blue" );
|
||||
|
||||
level.killStreakFuncs[ "mp_prison" ] = ::tryUseMpPrison;
|
||||
level.mapKillStreak = "mp_prison";
|
||||
|
||||
if ( !IsDefined( level.sentrySettings ) )
|
||||
level.sentrySettings = [];
|
||||
|
||||
level.sentrySettings[ "prison_turret" ] = spawnStruct();
|
||||
level.sentrySettings[ "prison_turret" ].health = 999999; // keep it from dying anywhere in code
|
||||
level.sentrySettings[ "prison_turret" ].maxHealth = 1000; // this is the health we'll check
|
||||
level.sentrySettings[ "prison_turret" ].burstMin = 20;
|
||||
level.sentrySettings[ "prison_turret" ].burstMax = 120;
|
||||
level.sentrySettings[ "prison_turret" ].pauseMin = 0.15;
|
||||
level.sentrySettings[ "prison_turret" ].pauseMax = 0.35;
|
||||
level.sentrySettings[ "prison_turret" ].sentryModeOn = "sentry";
|
||||
level.sentrySettings[ "prison_turret" ].sentryModeOff = "sentry_offline";
|
||||
level.sentrySettings[ "prison_turret" ].timeOut = 90.0;
|
||||
level.sentrySettings[ "prison_turret" ].spinupTime = 0.05;
|
||||
level.sentrySettings[ "prison_turret" ].overheatTime = 8.0;
|
||||
level.sentrySettings[ "prison_turret" ].cooldownTime = 0.1;
|
||||
level.sentrySettings[ "prison_turret" ].fxTime = 0.3;
|
||||
level.sentrySettings[ "prison_turret" ].streakName = "sentry";
|
||||
level.sentrySettings[ "prison_turret" ].weaponInfo = "prison_turret_mp";
|
||||
level.sentrySettings[ "prison_turret" ].modelBase = "prison_security_laser";
|
||||
level.sentrySettings[ "prison_turret" ].modelPlacement = "sentry_minigun_weak_obj";
|
||||
level.sentrySettings[ "prison_turret" ].modelPlacementFailed = "sentry_minigun_weak_obj_red";
|
||||
level.sentrySettings[ "prison_turret" ].modelDestroyed = "sentry_minigun_weak_destroyed";
|
||||
level.sentrySettings[ "prison_turret" ].hintString = &"MP_PRISON_SENSOR_PICKUP";
|
||||
level.sentrySettings[ "prison_turret" ].headIcon = true;
|
||||
level.sentrySettings[ "prison_turret" ].teamSplash = "used_sentry";
|
||||
level.sentrySettings[ "prison_turret" ].shouldSplash = false;
|
||||
level.sentrySettings[ "prison_turret" ].voDestroyed = "sentry_destroyed";
|
||||
|
||||
level.mapKillstreakPickupString = level.sentrySettings[ "prison_turret" ].hintString;
|
||||
|
||||
level thread onPrisonPlayerConnect();
|
||||
|
||||
level.prison_turrets = setupPrisonTurrets();
|
||||
}
|
||||
|
||||
tryUseMpPrison( lifeId, modules )
|
||||
{
|
||||
if ( level.mp_prison_InUse )
|
||||
{
|
||||
self iPrintLnBold( &"MP_PRISON_IN_USE" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isUsingRemote() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isAirDenied() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isEMPed() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = setPrisonTurretPlayer( self );
|
||||
|
||||
if ( IsDefined( result ) && result )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "mp_prison", self.origin );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
setupPrisonTurrets()
|
||||
{
|
||||
turrets = getentarray( "prison_turret", "targetname" );
|
||||
|
||||
sentryType = "prison_turret";
|
||||
|
||||
for ( i = 0; i < turrets.size; i++ )
|
||||
{
|
||||
turrets[i].spawned_turret = SpawnTurret( "misc_turret", turrets[i].origin, level.sentrySettings[ sentryType ].weaponInfo, false );
|
||||
turrets[i].spawned_turret makeTurretSolid(); //We need to make turrets solid for them to be damageable...
|
||||
turrets[i].spawned_turret sentry_initSentry( sentryType );
|
||||
turrets[i].spawned_turret.angles = turrets[i].angles;
|
||||
|
||||
turrets[i].spawned_turret.alarm_on = false;
|
||||
turrets[i].spawned_turret.burn_on = false;
|
||||
turrets[i].spawned_turret.proxy_alarm_on = false;
|
||||
turrets[i].spawned_turret.prison_turret_active = false;
|
||||
}
|
||||
|
||||
return turrets;
|
||||
}
|
||||
|
||||
|
||||
sentry_initSentry( sentryType )
|
||||
{
|
||||
self.sentryType = sentryType;
|
||||
self.canBePlaced = true;
|
||||
|
||||
self setModel( level.sentrySettings[ self.sentryType ].modelBase );
|
||||
self makeTurretInoperable();
|
||||
self SetDefaultDropPitch( 0.0 ); // setting this mainly prevents Turret_RestoreDefaultDropPitch() from running
|
||||
|
||||
self setTurretModeChangeWait( true );
|
||||
self maps\mp\killstreaks\_autosentry::sentry_setInactive();
|
||||
|
||||
self thread maps\mp\killstreaks\_autosentry::sentry_handleUse();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: setPrisonTurretPlayer( <player> )"
|
||||
"Summary: sets the owner of the Prison turrets when a player uses the map-based killstreak."
|
||||
"Module: Entity"
|
||||
"CallOn: N/A"
|
||||
"MandatoryArg: <player>: the player that used the killstreak"
|
||||
"Example: result = setPrisonTurretPlayer( self );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
setPrisonTurretPlayer( player )
|
||||
{
|
||||
if( level.mp_prison_InUse )
|
||||
return false;
|
||||
|
||||
level.mp_prison_InUse = true;
|
||||
|
||||
//thread teamPlayerCardSplash( "mp_prison", player );
|
||||
|
||||
for ( i = 0; i < level.prison_turrets.size; i++ )
|
||||
{
|
||||
level.prison_turrets_alive++;
|
||||
Assert( IsDefined( level.prison_turrets[i].spawned_turret ) );
|
||||
level.prison_turrets[i].spawned_turret sentry_setOwner( player );
|
||||
level.prison_turrets[i].spawned_turret.shouldSplash = false;
|
||||
level.prison_turrets[i].spawned_turret.carriedBy = player;
|
||||
level.prison_turrets[i].spawned_turret sentry_setPlaced();
|
||||
// level.prison_turrets[i].spawned_turret LaserOn(); //Was previously used to activate the laser attached to the turret when the turret stunned enemies.
|
||||
level.prison_turrets[i].spawned_turret setCanDamage( true );
|
||||
level.prison_turrets[i].spawned_turret setCanRadiusDamage( true );
|
||||
level.prison_turrets[i].spawned_turret thread sentry_handleDamage();
|
||||
level.prison_turrets[i].spawned_turret thread sentry_handleDeath();
|
||||
|
||||
level.prison_turrets[i].spawned_turret.alarm_on = false;
|
||||
level.prison_turrets[i].spawned_turret.burn_on = false;
|
||||
level.prison_turrets[i].spawned_turret.proxy_alarm_on = false;
|
||||
level.prison_turrets[i].spawned_turret.shocking_target = false;
|
||||
level.prison_turrets[i].spawned_turret.prison_turret_active = true;
|
||||
level.prison_turrets[i].spawned_turret.numNearbyPlayers = 0;
|
||||
|
||||
// level.prison_turrets[i].spawned_turret thread inflictPrisonTurretPressure();
|
||||
//level.prison_turrets[i].spawned_turret thread handlePrisonTurretLights(); //new model doesn't support this visually or technically -katz
|
||||
level.prison_turrets[i].spawned_turret thread prisonTurretPortableRadar();
|
||||
level.prison_turrets[i].spawned_turret thread repeatOneShotPrisonAlarm();
|
||||
level.prison_turrets[i].spawned_turret thread aud_play_announcer_warning();
|
||||
}
|
||||
|
||||
level thread prisonTurretTimer();
|
||||
level thread monitorPrisonKillstreakOwnership();
|
||||
level thread applyPrisonTurretRadarArrow();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: prisonTurretTimer()"
|
||||
"Summary: notifies after mp_prison_killstreak_duration is up."
|
||||
"Module: Entity"
|
||||
"CallOn: the level"
|
||||
"Example: level thread prisonTurretTimer();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
prisonTurretTimer()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
/#
|
||||
// level.mp_prison_killstreak_duration = GetDvarInt( "mp_prison_killstreak_duration", 25 );
|
||||
#/
|
||||
wait_time = level.mp_prison_killstreak_duration;
|
||||
|
||||
while ( wait_time > 0 )
|
||||
{
|
||||
wait( 1 );
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
wait_time--;
|
||||
|
||||
//End this thread if the killstreak is no longer in use. For example, end it if all the turrets are killed.
|
||||
if ( level.mp_prison_InUse == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//If the killstreak ended because of time, some turrets might still be alive. Notify death for all turrets to deactivate them.
|
||||
for ( i = 0; i < level.prison_turrets.size; i++ )
|
||||
{
|
||||
level.prison_turrets[i].spawned_turret notify( "fake_prison_death" ); //Using "fake_prison_death" instead of "death" because "death" would detrimentally end some functionality taken from _autosentry.gsc.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: sentry_setOwner( <owner> )"
|
||||
"Summary: sets the owner of the spawned turret."
|
||||
"Module: Entity"
|
||||
"CallOn: a turret spawned by SpawnTurret()."
|
||||
"MandatoryArg: <owner>: the player that used the prison killstreak"
|
||||
"Example: level.prison_turrets[i]["spawned_turret"] sentry_setOwner( player );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
sentry_setOwner( owner )
|
||||
{
|
||||
self.owner = owner;
|
||||
|
||||
self SetSentryOwner( self.owner );
|
||||
// self SetTurretMinimapVisible( true, self.sentryType ); //Don't want to show minimap icons for mp_prison killstreak.
|
||||
|
||||
if ( level.teamBased && IsDefined( owner ) )
|
||||
{
|
||||
self.team = self.owner.team;
|
||||
self setTurretTeam( self.team );
|
||||
}
|
||||
|
||||
self thread sentry_handleOwnerDisconnect();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: sentry_setPlaced()"
|
||||
"Summary: among other things, sets the sentry turret down and puts it into play."
|
||||
"Module: Entity"
|
||||
"CallOn: a turret"
|
||||
"Example: prison_turret["spawned_turret"] maps\mp\killstreaks\_autosentry::sentry_setPlaced();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
sentry_setPlaced()
|
||||
{
|
||||
self setSentryCarrier( undefined );
|
||||
|
||||
self.carriedBy forceUseHintOff();
|
||||
self.carriedBy = undefined;
|
||||
|
||||
if( IsDefined( self.owner ) )
|
||||
self.owner.isCarrying = false;
|
||||
|
||||
self sentry_setActive();
|
||||
|
||||
self playSound( "sentry_gun_plant" );
|
||||
|
||||
self notify ( "placed" );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: sentry_handleDamage()"
|
||||
"Summary: handles the death of a turret spawned by SpawnTurret()"
|
||||
"Module: Entity"
|
||||
"CallOn: a turret spawned by SpawnTurret()."
|
||||
"Example: self thread sentry_handleDamage();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
sentry_handleDamage()
|
||||
{
|
||||
self endon( "fake_prison_death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self.health = level.sentrySettings[ self.sentryType ].health;
|
||||
self.maxHealth = level.sentrySettings[ self.sentryType ].maxHealth;
|
||||
self.damageTaken = 0; // how much damage has it taken
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
||||
|
||||
// don't allow people to destroy equipment on their team if FF is off
|
||||
if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
|
||||
self.wasDamagedFromBulletPenetration = true;
|
||||
|
||||
modifiedDamage = 0;
|
||||
|
||||
if( IsDefined( weapon ) )
|
||||
{
|
||||
shortWeapon = maps\mp\_utility::strip_suffix( weapon, "_lefthand" );
|
||||
|
||||
switch( shortWeapon )
|
||||
{
|
||||
case "emp_grenade_mp":
|
||||
case "emp_grenade_var_mp":
|
||||
self.largeProjectileDamage = false;
|
||||
modifiedDamage = self.maxHealth + 1;
|
||||
if ( isPlayer( attacker ) )
|
||||
{
|
||||
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "sentry" );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ( meansOfDeath == "MOD_RIFLE_BULLET" || meansOfDeath == "MOD_PISTOL_BULLET" )
|
||||
{
|
||||
modifiedDamage = damage * 3.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
modifiedDamage = 0;
|
||||
}
|
||||
if ( IsPlayer( attacker ) )
|
||||
{
|
||||
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "sentry" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
|
||||
}
|
||||
|
||||
self.damageTaken += modifiedDamage;
|
||||
|
||||
if ( self.damageTaken >= self.maxHealth )
|
||||
{
|
||||
thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath, weapon );
|
||||
|
||||
if ( isPlayer( attacker ) && (!IsDefined(self.owner) || attacker != self.owner) )
|
||||
{
|
||||
level thread maps\mp\gametypes\_rank::awardGameEvent( "map_killstreak_destroyed", attacker, weapon, undefined, meansOfDeath );
|
||||
}
|
||||
|
||||
if ( IsDefined( self.owner ) )
|
||||
self.owner thread leaderDialogOnPlayer( level.sentrySettings[ self.sentryType ].voDestroyed, undefined, undefined, self.origin );
|
||||
|
||||
self notify( "fake_prison_death" );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sentry_handleDeath()
|
||||
{
|
||||
self waittill ( "fake_prison_death" );
|
||||
|
||||
// this handles cases of deletion
|
||||
if ( !IsDefined( self ) )
|
||||
return;
|
||||
|
||||
self maps\mp\killstreaks\_autosentry::sentry_setInactive();
|
||||
self SetSentryOwner( undefined );
|
||||
self SetTurretMinimapVisible( false );
|
||||
|
||||
//Turning off the turrets' head icons. This is added functionality for the mp_prison turrets.
|
||||
if( level.sentrySettings[ self.sentryType ].headIcon )
|
||||
{
|
||||
if ( level.teamBased )
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( "none", (0,0,0) );
|
||||
else
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( "none", (0,0,0) );
|
||||
}
|
||||
|
||||
level.prison_turrets_alive--;
|
||||
|
||||
self setCanDamage( false );
|
||||
self setCanRadiusDamage( false );
|
||||
|
||||
// self LaserOff(); //Used the laser attached to the turret when the turret stunned enemies.
|
||||
|
||||
|
||||
//Cleaning up sound and damage stuff for the turret./////////
|
||||
if ( self.alarm_on == true )
|
||||
{
|
||||
// self thread stop_loop_sound_on_entity( level.prison_turret_alarm_sfx );
|
||||
self.alarm_on = false;
|
||||
}
|
||||
if ( self.burn_on == true )
|
||||
{
|
||||
// self thread stop_loop_sound_on_entity( level.prison_turret_burn_sfx );
|
||||
self.burn_on = false;
|
||||
// StopFXOnTag( level.prison_turret_beam, self, "tag_weapon" );
|
||||
}
|
||||
if ( IsDefined( self.previous_turret_target ) )
|
||||
{
|
||||
self notify( "lost_or_changed_target" );
|
||||
self.previous_turret_target = undefined;
|
||||
}
|
||||
self.shocking_target = false;
|
||||
self.turret_on_target = undefined;
|
||||
|
||||
if ( self.proxy_alarm_on == true )
|
||||
{
|
||||
// self thread stop_loop_sound_on_entity( level.prison_turret_alarm_sfx );
|
||||
self.proxy_alarm_on = false;
|
||||
}
|
||||
self.prison_turret_active = false;
|
||||
/////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: sentry_setActive()"
|
||||
"Summary: activates the sentry turret"
|
||||
"Module: Entity"
|
||||
"CallOn: a turret"
|
||||
"Example: self sentry_setActive();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
sentry_setActive()
|
||||
{
|
||||
self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOn );
|
||||
|
||||
if( level.sentrySettings[ self.sentryType ].headIcon )
|
||||
{
|
||||
if ( level.teamBased )
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( self.team, (0,0,25) );
|
||||
else
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, (0,0,25) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: sentry_handleOwnerDisconnect()"
|
||||
"Summary: notifies death for this turret (self) if its owner disconnects."
|
||||
"Module: Entity"
|
||||
"CallOn: a turret spawned by SpawnTurret()."
|
||||
"Example: self thread sentry_handleOwnerDisconnect();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
sentry_handleOwnerDisconnect()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon( "fake_prison_death" ); //Ending this thread if the turret (self) dies by other means (damage, time).
|
||||
|
||||
self.owner waittill_any( "disconnect", "joined_team", "joined_spectators" );
|
||||
|
||||
self notify( "fake_prison_death" );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: monitorPrisonKillstreakOwnership()"
|
||||
"Summary: frees up the prison killstreak when the killstreak time is up or all turrets are disabled."
|
||||
"Module: Entity"
|
||||
"CallOn: the level"
|
||||
"Example: level thread monitorPrisonKillstreakOwnership()"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
monitorPrisonKillstreakOwnership()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
//If any turrets are alive or any turrets are up... wait!
|
||||
while ( level.prison_turrets_alive > 0 )
|
||||
{
|
||||
wait( 0.05 );
|
||||
}
|
||||
|
||||
level.mp_prison_InUse = false;
|
||||
|
||||
//Wrap up a bunch of stuff now that the killstreak is over.
|
||||
for ( i = 0; i < level.prison_turrets.size; i++ )
|
||||
{
|
||||
if ( level.prison_turrets[i].spawned_turret.proxy_alarm_on == true )
|
||||
{
|
||||
// level.prison_turrets[i].spawned_turret thread stop_loop_sound_on_entity( level.prison_turret_alarm_sfx );
|
||||
level.prison_turrets[i].spawned_turret.proxy_alarm_on = false;
|
||||
}
|
||||
|
||||
for ( j = 0; j < level.players.size; j++ )
|
||||
{
|
||||
level.players[j].laser_tag_array[i] LaserOff();
|
||||
stopfxontag(level.prison_turret_laser_glow, level.players[j].laser_tag_array[i], "tag_laser");
|
||||
level.players[j].laser_tag_array[i] ClearLookAtTarget();
|
||||
level.players[j].laser_tag_array[i].laserFXActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
for ( k = 0; k < level.players.size; k++ )
|
||||
{
|
||||
level.players[k].numNearbyPrisonTurrets = 0;
|
||||
|
||||
if ( level.players[k] HasPerk( "specialty_radararrow", true ) )
|
||||
{
|
||||
level.players[k] unsetPerk( "specialty_radararrow", true );
|
||||
}
|
||||
|
||||
level.players[k] notify( "player_not_tracked" );
|
||||
//level.players[k] endPrisonTrackingOverlay();
|
||||
level.players[k].is_being_tracked = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: handlePrisonTurretLights()"
|
||||
"Summary: turns on/off the prison turrets lights."
|
||||
"Module: Entity"
|
||||
"CallOn: a prison turret"
|
||||
"Example: turret thread handlePrisonTurretLights();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
handlePrisonTurretLights()
|
||||
{
|
||||
PlayFXOnTag( level.prison_turret_warning_light_friendly, self, "tag_fx" );
|
||||
|
||||
self waittill( "fake_prison_death" );
|
||||
|
||||
StopFXOnTag( level.prison_turret_warning_light_friendly, self, "tag_fx" );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: prisonTurretPortableRadar()"
|
||||
"Summary: creates a portable radar entity for this turret."
|
||||
"Module: Entity"
|
||||
"CallOn: a prison turret"
|
||||
"Example: turret thread prisonTurretPortableRadar();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
prisonTurretPortableRadar()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
self.portable_radar = spawn( "script_model", self.origin );
|
||||
self.portable_radar.team = self.team;
|
||||
/#
|
||||
self.portable_radar.targetname = "portable_radar"; // best practices to track bandwidth hogs
|
||||
#/
|
||||
|
||||
self.portable_radar makePortableRadar( self.owner );
|
||||
|
||||
self waittill( "fake_prison_death" );
|
||||
|
||||
level maps\mp\gametypes\_portable_radar::deletePortableRadar( self.portable_radar );
|
||||
self.portable_radar = undefined;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: applyPrisonTurretRadarArrow()"
|
||||
"Summary: loops through all prison turrets and all players to determine if players should be marked on the minimap"
|
||||
"Module: Entity"
|
||||
"CallOn: level"
|
||||
"Example: level thread applyPrisonTurretRadarArrow"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
applyPrisonTurretRadarArrow()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( level.mp_prison_InUse )
|
||||
{
|
||||
if ( IsDefined( level.prison_turrets ) && IsDefined( level.players ) )
|
||||
{
|
||||
for ( i = 0; i < level.prison_turrets.size; i++ )
|
||||
{
|
||||
if ( level.prison_turrets[i].spawned_turret.prison_turret_active != true )
|
||||
{
|
||||
for ( j = 0; j < level.players.size; j++ )
|
||||
{
|
||||
if ( level.players[j].laser_tag_array[i].laserFXActive == true )
|
||||
{
|
||||
level.players[j].laser_tag_array[i] LaserOff();
|
||||
level.players[j].laser_tag_array[i] ClearLookAtTarget();
|
||||
level.players[j].laser_tag_array[i].laserFXActive = false;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
level.prison_turrets[i].spawned_turret.numNearbyPlayers = 0;
|
||||
|
||||
for ( j = 0; j < level.players.size; j++ )
|
||||
{
|
||||
setLaserToOn = false;
|
||||
|
||||
randomRange = 10;
|
||||
randomOffset = ( randomFloat( randomRange ), randomFloat( randomRange ), randomFloat( randomRange ) ) - ( 5, 5, 5 );
|
||||
if ( level.players[j] GetStance() == "stand" )
|
||||
{
|
||||
player_origin_offset = ( 0, 0, 50 ) + randomOffset;
|
||||
}
|
||||
else if ( level.players[j] GetStance() == "crouch" )
|
||||
{
|
||||
player_origin_offset = ( 0, 0, 35 ) + randomOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
player_origin_offset = ( 0, 0, 10 ) + randomOffset;
|
||||
}
|
||||
|
||||
//TODO: get this working in free for all.
|
||||
if ( IsDefined( level.players[j] ) && IsAlive( level.players[j] ) &&
|
||||
( ( level.teamBased && level.players[j].team != level.prison_turrets[i].spawned_turret.team ) ||
|
||||
( !level.teamBased && level.players[j] != level.prison_turrets[i].spawned_turret.owner ) ) )
|
||||
{
|
||||
turret_to_player_distance = DistanceSquared( level.players[j].origin, level.prison_turrets[i].spawned_turret.origin );
|
||||
|
||||
if ( turret_to_player_distance < 3610000 )
|
||||
{
|
||||
if ( SightTracePassed( level.prison_turrets[i].spawned_turret.origin, level.players[j].origin + player_origin_offset, false, undefined ) )
|
||||
{
|
||||
setLaserToOn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( setLaserToOn )
|
||||
{
|
||||
// do not update laser_tag angles ! they are synchronized automatically thanks to the call to SetLookAtTarget()
|
||||
//level.players[j].laser_tag_array[i].angles = VectorToAngles( ( level.players[j].origin + player_origin_offset ) - level.prison_turrets[i].spawned_turret.origin );
|
||||
if ( level.players[j].laser_tag_array[i].laserFXActive == false )
|
||||
{
|
||||
level.players[j].laser_tag_array[i].laserFXActive = true;
|
||||
level.players[j].laser_tag_array[i] LaserOn( "prison_laser" ); //The i index corresponds directly to one of the level.prison_turret turrets.
|
||||
playfxontag(level.prison_turret_laser_glow, level.players[j].laser_tag_array[i], "tag_laser");
|
||||
level.players[j].laser_tag_array[i] SetLookAtTarget( level.players[j], "bone", "tag_eye", "randomoffset" );
|
||||
}
|
||||
|
||||
level.players[j].numNearbyPrisonTurrets++;
|
||||
level.prison_turrets[i].spawned_turret.numNearbyPlayers++;
|
||||
}
|
||||
else if ( level.players[j].laser_tag_array[i].laserFXActive == true )
|
||||
{
|
||||
level.players[j].laser_tag_array[i].laserFXActive = false;
|
||||
level.players[j].laser_tag_array[i] LaserOff();
|
||||
stopfxontag(level.prison_turret_laser_glow, level.players[j].laser_tag_array[i], "tag_laser");
|
||||
level.players[j].laser_tag_array[i] ClearLookAtTarget();
|
||||
}
|
||||
}
|
||||
|
||||
if ( level.prison_turrets[i].spawned_turret.numNearbyPlayers > 0 )
|
||||
{
|
||||
if ( level.prison_turrets[i].spawned_turret.proxy_alarm_on == false )
|
||||
{
|
||||
// level.prison_turrets[i].spawned_turret thread play_loop_sound_on_entity( level.prison_turret_alarm_sfx );
|
||||
level.prison_turrets[i].spawned_turret.proxy_alarm_on = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( level.prison_turrets[i].spawned_turret.proxy_alarm_on == true )
|
||||
{
|
||||
// level.prison_turrets[i].spawned_turret thread stop_loop_sound_on_entity( level.prison_turret_alarm_sfx );
|
||||
level.prison_turrets[i].spawned_turret.proxy_alarm_on = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//For each player, give it the specialty_radararrow perk if it is near at least one turret.
|
||||
for ( k = 0; k < level.players.size; k++ )
|
||||
{
|
||||
if ( level.players[k].numNearbyPrisonTurrets > 0 )
|
||||
{
|
||||
level.players[k] setPerk( "specialty_radararrow", true, false );
|
||||
|
||||
if ( level.players[k].is_being_tracked == false )
|
||||
{
|
||||
//level.players[k] thread fadeInOutPrisonTrackingOverlay();
|
||||
level.players[k].is_being_tracked = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( level.players[k] HasPerk( "specialty_radararrow", true ) )
|
||||
{
|
||||
level.players[k] unsetPerk( "specialty_radararrow", true );
|
||||
}
|
||||
|
||||
level.players[k] notify( "player_not_tracked" );
|
||||
//level.players[k] endPrisonTrackingOverlay();
|
||||
level.players[k].is_being_tracked = false;
|
||||
}
|
||||
|
||||
level.players[k].numNearbyPrisonTurrets = 0;
|
||||
}
|
||||
}
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: createLaserTagArray()"
|
||||
"Summary: creates an array of tag_laser script_models for a player."
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"Example: level.players[j] createLaserTagArray();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
createLaserTagArray()
|
||||
{
|
||||
if ( !IsDefined( self.laser_tag_array ) )
|
||||
{
|
||||
self.laser_tag_array = [];
|
||||
for ( i = 0; i < level.prison_turrets.size; i++ )
|
||||
{
|
||||
//laserOrigin = level.prison_turrets[i].spawned_turret GetTagOrigin( "tag_laser");
|
||||
laserOrigin = level.prison_turrets[i].spawned_turret.origin;
|
||||
self.laser_tag_array[i] = Spawn( "script_model", laserOrigin );
|
||||
self.laser_tag_array[i] SetModel( "tag_laser" );
|
||||
self.laser_tag_array[i].laserFXActive = false;
|
||||
/#
|
||||
self.laser_tag_array[i].targetname = "tag_laser"; // best practices to track bandwidth hogs
|
||||
#/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: deleteLaserTagArray()"
|
||||
"Summary: deletes the array of script_models created for a player when playing in mp_prison."
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"Example: player deleteLaserTagArray();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
deleteLaserTagArray()
|
||||
{
|
||||
if ( IsDefined( self.laser_tag_array ) )
|
||||
{
|
||||
for ( i = 0; i < level.prison_turrets.size; i++ )
|
||||
{
|
||||
self.laser_tag_array[i] ClearLookAtTarget();
|
||||
self.laser_tag_array[i] Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: onPrisonPlayerConnect()"
|
||||
"Summary: does stuff to a player for the mp_prison killstreak when a player connects."
|
||||
"Module: Entity"
|
||||
"CallOn: level"
|
||||
"Example: level thread onPrisonPlayerConnect();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
onPrisonPlayerConnect()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
//player createPrisonTurretTrackingOverlay();
|
||||
|
||||
player.is_being_tracked = false;
|
||||
|
||||
player createLaserTagArray();
|
||||
|
||||
player.numNearbyPrisonTurrets = 0;
|
||||
|
||||
player thread onPrisonPlayerDisconnect();
|
||||
|
||||
//TODO: set more things to zero here
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: onPrisonPlayerDisconnect()"
|
||||
"Summary: does stuff to a player for the mp_prison killstreak when a player disconnects."
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"Example: player thread onPrisonPlayerDisconnect();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
onPrisonPlayerDisconnect()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
self waittill( "disconnect" );
|
||||
|
||||
self deleteLaserTagArray();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: createPrisonTurretTrackingOverlay()"
|
||||
"Summary: create a HUD element for this player that will be used for the prison turret tracking effect."
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"Example: player createPrisonTurretTrackingOverlay();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
createPrisonTurretTrackingOverlay()
|
||||
{
|
||||
if ( !IsDefined( self.prisonTurretTrackingOverlay ) )
|
||||
{
|
||||
self.prisonTurretTrackingOverlay = newClientHudElem( self );
|
||||
self.prisonTurretTrackingOverlay.x = -80;
|
||||
self.prisonTurretTrackingOverlay.y = -60;
|
||||
self.prisonTurretTrackingOverlay setshader( "tracking_drone_targeted_overlay", 800, 600 );
|
||||
self.prisonTurretTrackingOverlay.alignX = "left";
|
||||
self.prisonTurretTrackingOverlay.alignY = "top";
|
||||
self.prisonTurretTrackingOverlay.horzAlign = "fullscreen";
|
||||
self.prisonTurretTrackingOverlay.vertAlign = "fullscreen";
|
||||
self.prisonTurretTrackingOverlay.alpha = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: fadeInOutPrisonTrackingOverlay()"
|
||||
"Summary: fades in/out the overlay used to denote prison turret tracking."
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"Example: level.players[k] thread fadeInOutPrisonTrackingOverlay();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
fadeInOutPrisonTrackingOverlay()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "player_not_tracked" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "joined_spectators" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if(IsDefined(self.TurretTrackingOverlay))
|
||||
{
|
||||
brightness = randomFloatRange(0.25, 1.0);
|
||||
self.prisonTurretTrackingOverlay FadeOverTime( 0.1 );
|
||||
self.prisonTurretTrackingOverlay.color = ( brightness, brightness, brightness );
|
||||
self.prisonTurretTrackingOverlay.alpha = 1;
|
||||
wait 0.1;
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: endPrisonTrackingOverlay()"
|
||||
"Summary: quickly fades out the prison turret tracking overlay on a player."
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"Example: level.players[k] endPrisonTrackingOverlay();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
endPrisonTrackingOverlay()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "joined_spectators" );
|
||||
|
||||
self.prisonTurretTrackingOverlay FadeOverTime( 0.2 );
|
||||
self.prisonTurretTrackingOverlay.alpha = 0.0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: repeatOneShotPrisonAlarm()"
|
||||
"Summary: repeatedly plays the prison turret alarm on a prison turret."
|
||||
"Module: Entity"
|
||||
"CallOn: a prison turret"
|
||||
"Example: level.prison_turrets[i].spawned_turret thread repeatOneShotPrisonAlarm();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
repeatOneShotPrisonAlarm()
|
||||
{
|
||||
self endon( "fake_prison_death" );
|
||||
|
||||
while ( level.mp_prison_InUse )
|
||||
{
|
||||
if ( self.proxy_alarm_on == true )
|
||||
{
|
||||
playSoundAtPos( self.origin, level.prison_turret_alarm_sfx );
|
||||
}
|
||||
|
||||
wait( 4 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
aud_play_announcer_warning()
|
||||
{
|
||||
wait(2.5);
|
||||
playSoundAtPos( ( 0,0,0 ), "mp_prison_anouncer_ext" );
|
||||
}
|
621
raw/maps/mp/killstreaks/streak_mp_recovery.gsc
Normal file
621
raw/maps/mp/killstreaks/streak_mp_recovery.gsc
Normal file
@ -0,0 +1,621 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
|
||||
level.mp_recovery_killstreak = SpawnStruct();
|
||||
level.mp_recovery_killstreak.killstreak_InUse = false;
|
||||
level.mp_recovery_killstreak.killstreak_duration = 25;
|
||||
level.mp_recovery_killstreak.speed_scale = 1.25;
|
||||
level.mp_recovery_killstreak.health_scale = 1.75;
|
||||
level.mp_recovery_killstreak.max_health_amplify_object = 1500;
|
||||
level.mp_recovery_killstreak.exo_super_vfx = LoadFX( "vfx/lights/air_light_exosuper_yellow" );
|
||||
level.mp_recovery_killstreak.amplify_vfx = LoadFX( "vfx/lights/air_light_amplifymachine_yellow" );
|
||||
|
||||
amplify_ring_side_01 = getent("damage_ring_01","targetname");
|
||||
amplify_ring_side_02 = getent("damage_ring_02","targetname");
|
||||
level.mp_recovery_killstreak.DamageRingsArray = [amplify_ring_side_01, amplify_ring_side_02];
|
||||
|
||||
foreach( damage_ring in level.mp_recovery_killstreak.DamageRingsArray )
|
||||
{
|
||||
damage_ring HudOutlineEnable(1, true);
|
||||
damage_ring SetCanDamage(true);
|
||||
damage_ring SetCanRadiusDamage(true);
|
||||
damage_ring.Max_fake_health = level.mp_recovery_killstreak.max_health_amplify_object;
|
||||
damage_ring.health = damage_ring.Max_fake_health;
|
||||
damage_ring.maxhealth = damage_ring.Max_fake_health;
|
||||
damage_ring.fakehealth = damage_ring.Max_fake_health;
|
||||
|
||||
VFX_points = getstructarray(damage_ring.target, "targetname");
|
||||
damage_ring.tag_array = [];
|
||||
foreach(VFX_point in VFX_points)
|
||||
{
|
||||
tag = spawn_tag_origin();
|
||||
tag.origin = VFX_point.origin;
|
||||
tag show();
|
||||
damage_ring.tag_array[damage_ring.tag_array.size] = tag;
|
||||
}
|
||||
}
|
||||
|
||||
/#
|
||||
//Used for tuning purposes. Please do not delete.
|
||||
// SetDvarIfUninitialized( "killstreak_duration", 25 );
|
||||
#/
|
||||
|
||||
precacheString( &"KILLSTREAKS_MP_RECOVERY" );
|
||||
|
||||
|
||||
|
||||
|
||||
level.killStreakFuncs[ "mp_recovery" ] = ::tryUseMpRecovery;
|
||||
level.mapKillStreak = "mp_recovery";
|
||||
|
||||
|
||||
level thread onRecoveryPlayerConnect();
|
||||
|
||||
}
|
||||
|
||||
tryUseMpRecovery( lifeId, modules )
|
||||
{
|
||||
if ( level.mp_recovery_killstreak.killstreak_InUse )
|
||||
{
|
||||
self iPrintLnBold( &"MP_RECOVERY_IN_USE" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isUsingRemote() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isAirDenied() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isEMPed() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = exoTeamBuffSetup( self );
|
||||
|
||||
if ( IsDefined( result ) && result )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "mp_recovery", self.origin );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
exoTeamBuffSetup(owner)
|
||||
{
|
||||
if(IsDefined(owner))
|
||||
{
|
||||
level.mp_recovery_killstreak.killstreak_InUse = true;
|
||||
level.mp_recovery_killstreak.owner = owner;
|
||||
level.mp_recovery_killstreak.killstreak_team = owner.team;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
killstreakTeam = owner.team;
|
||||
|
||||
thread StartExoRecoveryKillstreak(owner, killstreakTeam);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StartExoRecoveryKillstreak(owner, killstreakTeam)
|
||||
{
|
||||
SetupAmplifierDamageMonitor(owner, killstreakTeam);
|
||||
// TurnOnPlayerHUDOutline( owner, killstreakTeam)
|
||||
SortPlayersAndGivePowers(owner, killstreakTeam);
|
||||
thread SetMapKillstreakTimer();
|
||||
|
||||
level waittill_any("time_up", "amplifier_destroyed");
|
||||
|
||||
|
||||
ShutOffAmplifyObjectVFX();
|
||||
ShutOffAllPlayersExoBuffs();
|
||||
|
||||
level notify("recovery_streak_over");
|
||||
|
||||
wait(0.25);
|
||||
|
||||
|
||||
level.mp_recovery_killstreak.killstreak_InUse = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
SetMapKillstreakTimer()
|
||||
{
|
||||
level endon("recovery_streak_over");
|
||||
|
||||
wait(level.mp_recovery_killstreak.killstreak_duration);
|
||||
|
||||
level notify("time_up");
|
||||
}
|
||||
|
||||
SetupAmplifierDamageMonitor(owner, killstreakTeam)
|
||||
{
|
||||
|
||||
faction = "atlas";
|
||||
teamToShow = "axis";
|
||||
|
||||
if(killstreakTeam == "axis")
|
||||
{
|
||||
faction = "atlas";
|
||||
teamToShow = "allies";
|
||||
}
|
||||
else if(killstreakTeam == "allies")
|
||||
{
|
||||
faction = "sentinel";
|
||||
teamToShow = "axis";
|
||||
}
|
||||
AmplifyIconArt = "faction_128_" + faction;
|
||||
|
||||
|
||||
foreach( damage_ring in level.mp_recovery_killstreak.DamageRingsArray )
|
||||
{
|
||||
damage_ring SetCanDamage(true);
|
||||
damage_ring SetCanRadiusDamage(true);
|
||||
damage_ring.health = damage_ring.Max_fake_health;
|
||||
damage_ring.maxhealth = damage_ring.Max_fake_health;
|
||||
damage_ring.fakehealth = damage_ring.Max_fake_health;
|
||||
|
||||
damage_ring thread StartAmplifyObjectVFX();
|
||||
damage_ring thread MonitorAmplifierDamage(owner, killstreakTeam);
|
||||
|
||||
|
||||
|
||||
/////// don't put icons up for side you can't get to
|
||||
if(level.DynamicEventStatus == "before" && damage_ring.targetname == "damage_ring_02")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if(level.DynamicEventStatus == "after" && damage_ring.targetname == "damage_ring_01")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(level.teamBased == false)
|
||||
{
|
||||
foreach(player in level.players)
|
||||
{
|
||||
if(player != owner)
|
||||
{
|
||||
damage_ring maps\mp\_entityheadIcons::setHeadIcon(player, AmplifyIconArt, (0,0,0), 18, 18, undefined, undefined, undefined, true, false, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(level.teamBased == true)
|
||||
{
|
||||
damage_ring maps\mp\_entityheadIcons::setHeadIcon(teamToShow, AmplifyIconArt, (0,0,0), 18, 18, undefined, undefined, undefined, true, false, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MonitorAmplifierDamage(owner, killstreakTeam)
|
||||
{
|
||||
level endon( "recovery_streak_over" );
|
||||
|
||||
while(level.mp_recovery_killstreak.killstreak_InUse == true)
|
||||
{
|
||||
self waittill("damage", amount, attacker, direction, point, means_of_death, model, tag, part_name, damage_flags, weapon_name);
|
||||
|
||||
if(!IsValidStreakPlayer(attacker, owner, killstreakTeam))
|
||||
{
|
||||
self.fakehealth += (amount * -1);
|
||||
if(self.fakehealth <= 0)
|
||||
{
|
||||
level notify("amplifier_destroyed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StartAmplifyObjectVFX()
|
||||
{
|
||||
foreach( damage_ring in level.mp_recovery_killstreak.DamageRingsArray )
|
||||
{
|
||||
foreach(tag in damage_ring.tag_array)
|
||||
{
|
||||
PlayFXOnTag( level.mp_recovery_killstreak.amplify_vfx, tag, "tag_origin" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShutOffAmplifyObjectVFX()
|
||||
{
|
||||
ShutOffPlayerHUDOutline();
|
||||
foreach( damage_ring in level.mp_recovery_killstreak.DamageRingsArray )
|
||||
{
|
||||
damage_ring destroyPlayerIcons();
|
||||
foreach(tag in damage_ring.tag_array)
|
||||
{
|
||||
StopFXOnTag( level.mp_recovery_killstreak.amplify_vfx, tag, "tag_origin" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
destroyPlayerIcons() //Borrowed some of this script from _entityheadicons.gsc::setHeadIcon()
|
||||
{
|
||||
if ( IsDefined (self.entityHeadIcons) )
|
||||
{
|
||||
if ( isDefined( self.entityHeadIcons[ "allies" ] ) )
|
||||
{
|
||||
self.entityHeadIcons[ "allies" ] destroy();
|
||||
self.entityHeadIcons[ "allies" ] = undefined;
|
||||
}
|
||||
if ( isDefined( self.entityHeadIcons[ "axis" ] ) )
|
||||
{
|
||||
self.entityHeadIcons[ "axis" ] destroy();
|
||||
self.entityHeadIcons[ "axis" ] = undefined;
|
||||
}
|
||||
foreach(player in level.players)
|
||||
{
|
||||
if ( isDefined( self.entityHeadIcons[ player.guid ] ) )
|
||||
{
|
||||
self.entityHeadIcons[ player.guid ] destroy();
|
||||
self.entityHeadIcons[ player.guid ] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShutOffPlayerHUDOutline()
|
||||
{
|
||||
foreach(player in level.players)
|
||||
{
|
||||
foreach( damage_ring in level.mp_recovery_killstreak.DamageRingsArray )
|
||||
{
|
||||
damage_ring HudOutlineDisableForClient(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TurnOnPlayerHUDOutline( owner, killstreakTeam)
|
||||
{
|
||||
foreach(player in level.players)
|
||||
{
|
||||
if(!IsValidStreakPlayer(player, owner, killstreakTeam))
|
||||
{
|
||||
foreach( damage_ring in level.mp_recovery_killstreak.DamageRingsArray )
|
||||
{
|
||||
damage_ring HudOutlineEnableForClient(player, 1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SortPlayersAndGivePowers(owner, killstreakTeam)
|
||||
{
|
||||
|
||||
foreach(player in level.players)
|
||||
{
|
||||
if(IsValidStreakPlayer(player, owner, killstreakTeam) == true)
|
||||
{
|
||||
if( isReallyAlive(player))
|
||||
{
|
||||
player SetupSuperExo();
|
||||
player thread GiveSuperExo();
|
||||
}
|
||||
}
|
||||
|
||||
player thread MonitorSpawnDurringStreak(owner, killstreakTeam);
|
||||
}
|
||||
|
||||
thread MonitorConnectedDuringStreak(owner, killstreakTeam);
|
||||
}
|
||||
|
||||
ShutOffAllPlayersExoBuffs()
|
||||
{
|
||||
// TODO make it play with exo abilities overclock and health gen etc
|
||||
|
||||
|
||||
foreach(player in level.players)
|
||||
{
|
||||
player ShutOffExoBuffs();
|
||||
}
|
||||
}
|
||||
|
||||
ShutOffExoBuffs()
|
||||
{
|
||||
if(isdefined(self.SuperExoSettings) && isdefined(self.SuperExoSettings.IsActive))
|
||||
{
|
||||
self.SuperExoSettings.IsActive = false;
|
||||
}
|
||||
self ShutOffSpeed();
|
||||
self ShutOffFX();
|
||||
self ShutOffHealth();
|
||||
self ShutOffSlam();
|
||||
}
|
||||
|
||||
|
||||
|
||||
ShutOffSlam()
|
||||
{
|
||||
if(IsDefined(self.CAC_has_slam) && self.CAC_has_slam == true)
|
||||
{
|
||||
// do nothing for now
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( self _hasPerk( "specialty_exo_slamboots" ) )
|
||||
{
|
||||
// UnSetPerk
|
||||
_unsetPerk("specialty_exo_slamboots");
|
||||
}
|
||||
}
|
||||
self.CAC_has_slam = undefined;
|
||||
}
|
||||
|
||||
ShutOffSpeed()
|
||||
{
|
||||
self.moveSpeedScaler = level.basePlayerMoveScale;
|
||||
if ( self _hasPerk( "specialty_lightweight" ) )
|
||||
{
|
||||
self.moveSpeedScaler = lightWeightScalar();
|
||||
}
|
||||
self maps\mp\gametypes\_weapons::updateMoveSpeedScale();
|
||||
}
|
||||
|
||||
ShutOffHealth()
|
||||
{
|
||||
|
||||
self.maxhealth = int( self.maxhealth / level.mp_recovery_killstreak.health_scale );
|
||||
|
||||
if( self.health > self.maxhealth )
|
||||
{
|
||||
self.health = self.maxhealth;
|
||||
}
|
||||
|
||||
self.healthRegenLevel = undefined;
|
||||
}
|
||||
|
||||
ShutOffFX()
|
||||
{
|
||||
if( Isdefined(self.SuperExoSettings) && IsDefined( self.SuperExoSettings.overlay ) )
|
||||
{
|
||||
self.SuperExoSettings.overlay Destroy();
|
||||
}
|
||||
|
||||
if ( IsDefined( level.mp_recovery_killstreak.exo_super_vfx ) )
|
||||
{
|
||||
if(isReallyAlive(self))
|
||||
{
|
||||
StopFXOnTag( level.mp_recovery_killstreak.exo_super_vfx, self, "tag_shield_back" );
|
||||
StopFXOnTag( level.mp_recovery_killstreak.exo_super_vfx, self, "j_knee_le" );
|
||||
StopFXOnTag( level.mp_recovery_killstreak.exo_super_vfx, self, "j_knee_ri" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GiveSuperExo()
|
||||
{
|
||||
// TODO make it play with exo abilities overclock and health gen etc
|
||||
|
||||
self SetupSuperExo();
|
||||
self.SuperExoSettings.IsActive = true;
|
||||
self GiveSuperSpeed();
|
||||
self GiveSuperHealth();
|
||||
self GiveSuperStomp();
|
||||
self GiveSuperPunch();
|
||||
self GiveSuperRepulse();
|
||||
self TurnOnSuperFX();
|
||||
|
||||
self thread WatchForDeath();
|
||||
}
|
||||
|
||||
WatchForDeath()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
level endon( "recovery_streak_over" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
|
||||
self waittill("death");
|
||||
if(level.mp_recovery_killstreak.killstreak_InUse == true)
|
||||
{
|
||||
self ShutOffExoBuffs();
|
||||
}
|
||||
}
|
||||
|
||||
GiveSuperSpeed()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
self.moveSpeedScaler = level.mp_recovery_killstreak.speed_scale;
|
||||
self maps\mp\gametypes\_weapons::updateMoveSpeedScale();
|
||||
}
|
||||
|
||||
|
||||
GiveSuperHealth()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
|
||||
self.maxhealth = int( self.maxhealth * level.mp_recovery_killstreak.health_scale );
|
||||
self.ignoreRegenDelay = true;
|
||||
self.healthRegenLevel = .99;
|
||||
self notify( "damage" );
|
||||
|
||||
}
|
||||
GiveSuperStomp()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
self.CAC_has_slam = undefined;
|
||||
|
||||
if( self _hasPerk( "specialty_exo_slamboots" ) )
|
||||
{
|
||||
self.CAC_has_slam = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
self givePerk( "specialty_exo_slamboots", false );
|
||||
self.CAC_has_slam = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GiveSuperPunch()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
}
|
||||
|
||||
GiveSuperRepulse()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
self thread maps\mp\_exo_repulsor::do_exo_repulsor();
|
||||
}
|
||||
|
||||
TurnOnSuperFX()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
|
||||
//temp hud overlay
|
||||
if(!isdefined(self.SuperExoSettings.overlay))
|
||||
{
|
||||
self.SuperExoSettings.overlay = NewClientHudElem( self );
|
||||
self.SuperExoSettings.overlay.x = 0;
|
||||
self.SuperExoSettings.overlay.y = 0;
|
||||
self.SuperExoSettings.overlay.horzAlign = "fullscreen";
|
||||
self.SuperExoSettings.overlay.vertAlign = "fullscreen";
|
||||
self.SuperExoSettings.overlay SetShader( "exo_hud_cloak_overlay", 640, 480 );
|
||||
self.SuperExoSettings.overlay.archive = true;
|
||||
self.SuperExoSettings.overlay.alpha = 1.0;
|
||||
}
|
||||
|
||||
// temp vfx
|
||||
if ( IsDefined( level.mp_recovery_killstreak.exo_super_vfx ) )
|
||||
{
|
||||
PlayFXOnTag( level.mp_recovery_killstreak.exo_super_vfx, self, "tag_shield_back" );
|
||||
PlayFXOnTag( level.mp_recovery_killstreak.exo_super_vfx, self, "j_knee_le" );
|
||||
PlayFXOnTag( level.mp_recovery_killstreak.exo_super_vfx, self, "j_knee_ri" );
|
||||
}
|
||||
|
||||
//temp activation sound
|
||||
// self playLocalSound( "earn_superbonus" );
|
||||
}
|
||||
|
||||
SetupSuperExo()
|
||||
{
|
||||
if(!isdefined(self.SuperExoSettings))
|
||||
{
|
||||
self.SuperExoSettings = SpawnStruct();
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.mp_recovery_killstreak.exo_super_vfx ) )
|
||||
{
|
||||
level.mp_recovery_killstreak.exo_super_vfx = LoadFX( "vfx/lights/air_light_exosuper_yellow" );
|
||||
}
|
||||
self.SuperExoSettings.IsActive = false;
|
||||
}
|
||||
|
||||
IsValidStreakPlayer(player, owner, killstreakTeam)
|
||||
{
|
||||
if(level.teamBased == false && Isdefined(owner) && player == owner)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(level.teamBased == true && player.team == killstreakTeam)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MonitorSpawnDurringStreak(owner, killstreakTeam)
|
||||
{
|
||||
self endon("disconnect");
|
||||
level endon( "game_ended" );
|
||||
level endon( "recovery_streak_over" );
|
||||
|
||||
while(level.mp_recovery_killstreak.killstreak_InUse == true)
|
||||
{
|
||||
self waittill("spawned_player");
|
||||
|
||||
if(IsValidStreakPlayer(self, owner, killstreakTeam) == true)
|
||||
{
|
||||
wait(0.25); // because of an overlay bug being destroyed then created on spawn
|
||||
if(level.mp_recovery_killstreak.killstreak_InUse == true)
|
||||
{
|
||||
self SetupSuperExo();
|
||||
self thread GiveSuperExo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MonitorConnectedDuringStreak(owner, killstreakTeam)
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
level endon( "recovery_streak_over" );
|
||||
|
||||
while(level.mp_recovery_killstreak.killstreak_InUse == true)
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
player MonitorSpawnDurringStreak(owner, killstreakTeam);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onRecoveryPlayerConnect()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
foreach( damage_ring in level.mp_recovery_killstreak.DamageRingsArray )
|
||||
{
|
||||
damage_ring HudOutlineDisableForClient(player);
|
||||
}
|
||||
player thread onRecoveryPlayerDisconnect();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onRecoveryPlayerDisconnect()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
self waittill( "disconnect" );
|
||||
}
|
751
raw/maps/mp/killstreaks/streak_mp_refraction.gsc
Normal file
751
raw/maps/mp/killstreaks/streak_mp_refraction.gsc
Normal file
@ -0,0 +1,751 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_audio;
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
level.mp_refraction_killstreak_duration = 25;
|
||||
/#
|
||||
//Used for tuning purposes. Please do not delete.
|
||||
// SetDvarIfUninitialized( "mp_refraction_killstreak_duration", 25 );
|
||||
#/
|
||||
|
||||
level.mp_refraction_InUse = false;
|
||||
level.refraction_turrets_alive = 0;
|
||||
level.refraction_turrets_moved_down = 0;
|
||||
level.mp_refraction_owner = undefined;
|
||||
|
||||
level.killstreakFuncs["mp_refraction"] = ::tryUseMPRefraction;
|
||||
level.mapKillStreak = "mp_refraction";
|
||||
level.mapKillstreakPickupString = &"MP_REFRACTION_MAP_KILLSTREAK_PICKUP";
|
||||
level.killstreakWieldWeapons[ "refraction_turret_mp" ] = "refraction_turret_mp";
|
||||
|
||||
if ( !IsDefined( level.sentrySettings ) )
|
||||
level.sentrySettings = [];
|
||||
|
||||
level.sentrySettings[ "refraction_turret" ] = spawnStruct();
|
||||
level.sentrySettings[ "refraction_turret" ].health = 999999; // keep it from dying anywhere in code
|
||||
level.sentrySettings[ "refraction_turret" ].maxHealth = 1000; // this is the health we'll check
|
||||
level.sentrySettings[ "refraction_turret" ].burstMin = 20;
|
||||
level.sentrySettings[ "refraction_turret" ].burstMax = 120;
|
||||
level.sentrySettings[ "refraction_turret" ].pauseMin = 0.15;
|
||||
level.sentrySettings[ "refraction_turret" ].pauseMax = 0.35;
|
||||
level.sentrySettings[ "refraction_turret" ].sentryModeOn = "sentry";
|
||||
level.sentrySettings[ "refraction_turret" ].sentryModeOff = "sentry_offline";
|
||||
level.sentrySettings[ "refraction_turret" ].timeOut = 90.0;
|
||||
level.sentrySettings[ "refraction_turret" ].spinupTime = 0.05;
|
||||
level.sentrySettings[ "refraction_turret" ].overheatTime = 8.0;
|
||||
level.sentrySettings[ "refraction_turret" ].cooldownTime = 0.1;
|
||||
level.sentrySettings[ "refraction_turret" ].fxTime = 0.3;
|
||||
level.sentrySettings[ "refraction_turret" ].streakName = "sentry";
|
||||
level.sentrySettings[ "refraction_turret" ].weaponInfo = "refraction_turret_mp";
|
||||
level.sentrySettings[ "refraction_turret" ].modelBase = "ref_turret_01";
|
||||
level.sentrySettings[ "refraction_turret" ].sentryType = "refraction_turret";
|
||||
level.sentrySettings[ "refraction_turret" ].modelPlacement = "sentry_minigun_weak_obj";
|
||||
level.sentrySettings[ "refraction_turret" ].modelPlacementFailed = "sentry_minigun_weak_obj_red";
|
||||
level.sentrySettings[ "refraction_turret" ].modelDestroyed = "sentry_minigun_weak_destroyed";
|
||||
level.sentrySettings[ "refraction_turret" ].hintString = &"SENTRY_PICKUP";
|
||||
level.sentrySettings[ "refraction_turret" ].headIcon = true;
|
||||
level.sentrySettings[ "refraction_turret" ].teamSplash = "used_sentry";
|
||||
level.sentrySettings[ "refraction_turret" ].shouldSplash = false;
|
||||
level.sentrySettings[ "refraction_turret" ].voDestroyed = "sentry_destroyed";
|
||||
|
||||
level.refraction_turrets = turret_setup();
|
||||
|
||||
level.turret_movement_sound = "mp_refraction_turret_movement1";
|
||||
level.turret_movement2_sound = "mp_refraction_turret_movement2";
|
||||
level.turret_movement3_sound = "mp_refraction_turret_movement3";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: tryUseMPRefraction( <lifeId> )"
|
||||
"Summary: function called when a player tries to use the mp_refraction map-based killstreak"
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"MandatoryArg: <lifeId>: "
|
||||
"Example: level.killstreakFuncs["mp_refraction"] = ::tryUseMPRefraction;"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
tryUseMPRefraction( lifeId, modules )
|
||||
{
|
||||
if ( isDefined( level.mp_refraction_owner ) || level.mp_refraction_InUse )
|
||||
{
|
||||
self iPrintLnBold( &"MP_REFRACTION_IN_USE" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isUsingRemote() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isAirDenied() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isEMPed() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = setRefractionTurretPlayer( self );
|
||||
|
||||
if ( IsDefined( result ) && result )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "mp_refraction", self.origin );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: refractionTurretTimer()"
|
||||
"Summary: notifies after mp_refraction_killstreak_duration is up."
|
||||
"Module: Entity"
|
||||
"CallOn: the level"
|
||||
"Example: level thread refractionTurretTimer();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
refractionTurretTimer()
|
||||
{
|
||||
self endon( "game_ended" );
|
||||
|
||||
/#
|
||||
// level.mp_refraction_killstreak_duration = GetDvarInt( "mp_refraction_killstreak_duration", 25 );
|
||||
#/
|
||||
wait_time = level.mp_refraction_killstreak_duration;
|
||||
|
||||
if ( level.mp_refraction_owner _hasPerk( "specialty_blackbox" ) && IsDefined( level.mp_refraction_owner.specialty_blackbox_bonus ) )
|
||||
{
|
||||
wait_time *= level.mp_refraction_owner.specialty_blackbox_bonus;
|
||||
}
|
||||
|
||||
while ( wait_time > 0 )
|
||||
{
|
||||
wait( 1 );
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
wait_time -= 1.0;
|
||||
|
||||
//End this thread if the killstreak is no longer in use. For example, end it if all the turrets are killed.
|
||||
if ( level.mp_refraction_InUse == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//If the killstreak ended because of time, some turrets might still be alive. Notify death for all turrets to deactivate them.
|
||||
for ( i = 0; i < level.refraction_turrets.size; i++ )
|
||||
{
|
||||
level.refraction_turrets[i] notify( "fake_refraction_death" ); //Using "fake_refraction_death" instead of "death" because "death" would detrimentally end some functionality taken from _autosentry.gsc.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: monitorRefractionKillstreakOwnership()"
|
||||
"Summary: frees up the refraction killstreak when the killstreak time is up or all turrets are disabled."
|
||||
"Module: Entity"
|
||||
"CallOn: the level"
|
||||
"Example: level thread monitorRefractionKillstreakOwnership()"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
monitorRefractionKillstreakOwnership()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
//If any turrets are alive or any turrets are up... wait!
|
||||
while ( level.refraction_turrets_alive > 0 || level.refraction_turrets_moved_down < level.refraction_turrets.size )
|
||||
{
|
||||
wait( 0.05 );
|
||||
}
|
||||
|
||||
unsetRefractionTurretPlayer();
|
||||
}
|
||||
|
||||
setRefractionTurretPlayer( player )
|
||||
{
|
||||
if( IsDefined( level.mp_refraction_owner ) )
|
||||
return false;
|
||||
|
||||
level.mp_refraction_InUse = true;
|
||||
level.mp_refraction_owner = player;
|
||||
|
||||
thread teamPlayerCardSplash( "used_mp_refraction", player );
|
||||
|
||||
sentryType = "refraction_turret";
|
||||
|
||||
//IPrintLnBold("Bleed Time");
|
||||
|
||||
for ( i = 0; i < level.refraction_turrets.size; i++ )
|
||||
{
|
||||
level.refraction_turrets_alive++;
|
||||
level.refraction_turrets_moved_down = 0; //All the turrets will move up, so we're setting this value to zero. It will be incremented back up to 4 when all the turrets move back down, either via time or death.
|
||||
Assert( IsDefined( level.refraction_turrets[i] ) );
|
||||
level.refraction_turrets[i] sentry_setOwner( player );
|
||||
level.refraction_turrets[i] SetLeftArc( 45 );
|
||||
level.refraction_turrets[i] SetRightArc( 45 );
|
||||
level.refraction_turrets[i] SetTopArc( 10 );
|
||||
level.refraction_turrets[i].shouldSplash = false;
|
||||
level.refraction_turrets[i].carriedBy = player;
|
||||
level.refraction_turrets[i] sentry_setPlaced();
|
||||
level.refraction_turrets[i] thread sentry_handleDamage();
|
||||
level.refraction_turrets[i] thread sentry_handleDeath();
|
||||
level.refraction_turrets[i] thread sentry_laserMark();
|
||||
level.refraction_turrets[i] thread aud_turrets_activate();
|
||||
}
|
||||
|
||||
level thread refractionTurretTimer();
|
||||
level thread monitorRefractionKillstreakOwnership();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
aud_turrets_activate()
|
||||
{
|
||||
thread snd_play_in_space( "turret_cover_explode", self.origin );
|
||||
thread snd_play_in_space( "turret_rise_start", self.origin );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: unsetRefractionTurretPlayer()"
|
||||
"Summary: unsets the Refraction Turrets' owner - frees them up for someone else."
|
||||
"Module: Entity"
|
||||
"CallOn: N/A"
|
||||
"Example: unsetRefractionTurretPlayer();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
unsetRefractionTurretPlayer()
|
||||
{
|
||||
level.mp_refraction_owner = undefined;
|
||||
level.mp_refraction_InUse = false;
|
||||
}
|
||||
|
||||
|
||||
turret_setup() //GET ALL FOUR TURRETS
|
||||
{
|
||||
turrets = getentarray( "turret_killer", "targetname" );
|
||||
|
||||
assertEx( isDefined( turrets), "no entities found with targetname of turret killer" );
|
||||
|
||||
foreach( turret in turrets )
|
||||
{
|
||||
parts = undefined;
|
||||
if( isDefined( turret.target ) )
|
||||
parts = getentarray( turret.target, "targetname" );
|
||||
|
||||
assertEx( isDefined( parts), "no entities found with targetname of " + turret.target );
|
||||
|
||||
foreach( part in parts )
|
||||
{
|
||||
if( isDefined( part.script_noteworthy ) && part.script_noteworthy == "turret_lifter" )
|
||||
{
|
||||
turret.lifter = part;
|
||||
continue;
|
||||
}
|
||||
else if( isDefined( part.script_noteworthy ) && part.script_noteworthy == "hatch" )
|
||||
{
|
||||
turret.hatch = part;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
assertMsg( "no parts with targetname turret_lifter or hatch were found" );
|
||||
}
|
||||
|
||||
assertEx( isDefined( turret.lifter), "no lifter entity found for turret" );
|
||||
assertEx( isDefined( turret.hatch), "no hatch entity found for turret" );
|
||||
|
||||
turret.lifter.animUp = "ref_turret_gun_raise";
|
||||
turret.lifter.animDown = "ref_turret_gun_lower";
|
||||
turret.lifter.IdleUp = "ref_turret_gun_idle_up";
|
||||
turret.lifter.IdleDown = "ref_turret_gun_idle_down";
|
||||
|
||||
turret.hatch.animUp = "ref_turret_hatch_raise";
|
||||
turret.hatch.animDown = "ref_turret_hatch_lower";
|
||||
turret.hatch.IdleUp = "ref_turret_hatch_idle_up";
|
||||
turret.hatch.IdleDown = "ref_turret_hatch_idle_down";
|
||||
|
||||
turret.collision = SpawnStruct();
|
||||
collision = undefined;
|
||||
if( isDefined( turret.lifter.target ) )
|
||||
collision = getentarray( turret.lifter.target, "targetname" );
|
||||
|
||||
assertEx( isDefined( collision), "no collision entities found with targetname of " + turret.target );
|
||||
|
||||
foreach( collision_part in collision )
|
||||
{
|
||||
if( isDefined( collision_part.script_noteworthy ) )
|
||||
{
|
||||
switch( collision_part.script_noteworthy )
|
||||
{
|
||||
case "ref_turret_body_col":
|
||||
turret.collision.col_body = collision_part;
|
||||
break;
|
||||
case "ref_turret_head_col":
|
||||
turret.collision.col_head = collision_part;
|
||||
break;
|
||||
case "ref_turret_leg_r_col":
|
||||
turret.collision.col_leg_r = collision_part;
|
||||
break;
|
||||
case "ref_turret_leg_l_col":
|
||||
turret.collision.col_leg_l = collision_part;
|
||||
break;
|
||||
case "ref_turret_gun_col":
|
||||
turret.collision.col_gun = collision_part;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
turret.sound_tag = spawn_tag_origin();
|
||||
turret.sound_tag.origin = turret.origin + (0,0,24);
|
||||
turret SetDefaultDropPitch( 0 );
|
||||
sentryType = level.sentrySettings[ "refraction_turret" ].sentryType;
|
||||
turret sentry_initSentry( sentryType ); //HACK: I'm calling sentry_initSentry() to do initial setup for the turrets. The turrets don't have an owner yet.
|
||||
turret makeTurretSolid(); //We need to make turrets solid for them to be damageable...
|
||||
turret hide();
|
||||
|
||||
turret.laser_tag = Spawn( "script_model", turret.origin );
|
||||
turret.laser_tag SetModel( "tag_laser" );
|
||||
}
|
||||
|
||||
return turrets;
|
||||
}
|
||||
|
||||
LinkCollisionToTurret(turret, ShouldLink)
|
||||
{
|
||||
if(ShouldLink == false)
|
||||
{
|
||||
if( isDefined( turret.collision.col_body ) )
|
||||
turret.collision.col_body unlink();
|
||||
if( isDefined( turret.collision.col_head ) )
|
||||
turret.collision.col_head unlink();
|
||||
if( isDefined( turret.collision.col_leg_r ) )
|
||||
turret.collision.col_leg_r unlink();
|
||||
if( isDefined( turret.collision.col_leg_l ) )
|
||||
turret.collision.col_leg_l unlink();
|
||||
if( isDefined( turret.collision.col_gun ) )
|
||||
turret.collision.col_gun unlink();
|
||||
}
|
||||
else if(ShouldLink == true)
|
||||
{
|
||||
if( isDefined( turret.collision.col_body ) )
|
||||
turret.collision.col_body linkto( self, "tag_origin" );
|
||||
if( isDefined( turret.collision.col_head ) )
|
||||
turret.collision.col_head linkto( self, "tag_aim_animated" );
|
||||
if( isDefined( turret.collision.col_leg_r ) )
|
||||
turret.collision.col_leg_r linkto( self, "arm_r" );
|
||||
if( isDefined( turret.collision.col_leg_l ) )
|
||||
turret.collision.col_leg_l linkto( self, "arm_l" );
|
||||
if( isDefined( turret.collision.col_gun ) )
|
||||
turret.collision.col_gun linkto( self, "tag_barrel" );
|
||||
}
|
||||
}
|
||||
|
||||
sentry_laserMark() //Monitor laser marking for each individual turret
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
self waittill( "refraction_turret_moved_up" );
|
||||
|
||||
self.laser_tag LaserOn();
|
||||
self.laser_tag.origin = self GetTagOrigin( "tag_flash" );
|
||||
self.laser_tag.angles = self GetTagAngles( "tag_flash" );
|
||||
self.laser_tag LinkTo( self, "tag_flash" );
|
||||
|
||||
self waittill( "fake_refraction_death" );
|
||||
|
||||
self.laser_tag LaserOff( );
|
||||
}
|
||||
|
||||
turret_moveUP() //TURRETS MOVE UP FROM UNDERGROUND
|
||||
{
|
||||
wait( RandomFloatRange( 1, 1.5 ) ); //make them not look too in sync
|
||||
|
||||
self.killCamEnt = Spawn( "script_model", self GetTagOrigin( "tag_player" ) );
|
||||
self.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
|
||||
self.lifter LinkCollisionToTurret(self, true);
|
||||
|
||||
alias_array = [];
|
||||
|
||||
alias_array[ "ref_turret_raise_doors_start" ] = "ref_turret_raise_doors_start";
|
||||
alias_array[ "ref_turret_raise_doors_end" ] = "ref_turret_raise_doors_end";
|
||||
alias_array[ "ref_turret_down_start" ] = "ref_turret_down_start";
|
||||
alias_array[ "ref_turret_down_end" ] = "ref_turret_down_end";
|
||||
alias_array[ "ref_turret_doors_close_start" ] = "ref_turret_doors_close_start";
|
||||
alias_array[ "ref_turret_doors_close_end" ] = "ref_turret_doors_close_end";
|
||||
alias_array[ "ref_turret_barrell_ext_start" ] = "ref_turret_barrell_ext_start";
|
||||
alias_array[ "ref_turret_barrell_ext_end" ] = "ref_turret_barrell_ext_end";
|
||||
|
||||
self.hatch ScriptModelPlayAnimDeltaMotion( self.hatch.animUp );
|
||||
self.lifter ScriptModelPlayAnimDeltaMotion( self.lifter.animUp, "ref_turret_raise_doors_start" );
|
||||
self.lifter thread snd_play_on_notetrack( alias_array, "ref_turret_raise_doors_start");
|
||||
// AUDIO
|
||||
|
||||
self thread PlayFxTurretMoveUp();//TODO: These are temp
|
||||
self thread playAudioTurretMoveUp();//TODO: These are temp
|
||||
|
||||
wait( 4.17 ); //HACK FOR ANIM LENGTH
|
||||
|
||||
self.lifter LinkCollisionToTurret(self, false);
|
||||
self show();
|
||||
//self LinkCollisionToTurret(self, true); //taking this out so collision doesn't kill dudes who stand against the turret while it's on
|
||||
self.lifter hide();
|
||||
|
||||
self notify( "refraction_turret_moved_up" );
|
||||
}
|
||||
|
||||
playFxTurretMoveUp()
|
||||
{
|
||||
level thread common_scripts\_exploder::activate_clientside_exploder(1); //smoke fx
|
||||
level thread common_scripts\_exploder::activate_clientside_exploder(2);
|
||||
level thread common_scripts\_exploder::activate_clientside_exploder(3);
|
||||
level thread common_scripts\_exploder::activate_clientside_exploder(4);
|
||||
level thread common_scripts\_exploder::activate_clientside_exploder(5);
|
||||
level thread common_scripts\_exploder::activate_clientside_exploder(6);
|
||||
level thread common_scripts\_exploder::activate_clientside_exploder(7);
|
||||
level thread common_scripts\_exploder::activate_clientside_exploder(8);
|
||||
}
|
||||
|
||||
playAudioTurretMoveUp()
|
||||
{
|
||||
self.sound_tag thread Play_Sound_on_Tag( level.turret_movement_sound, "tag_origin" );
|
||||
|
||||
wait(1);
|
||||
|
||||
self.sound_tag thread Play_Sound_on_Tag(level.turret_movement2_sound,"tag_origin");
|
||||
|
||||
wait(1);
|
||||
|
||||
self.sound_tag thread Play_Sound_on_Tag(level.turret_movement3_sound,"tag_origin");
|
||||
}
|
||||
|
||||
|
||||
turret_moveDOWN(turret)
|
||||
{
|
||||
wait( RandomFloatRange( 1, 1.5 ) ); //make them not look too in sync
|
||||
|
||||
//self LinkCollisionToTurret(self, false); //taking this out so collision doesn't kill dudes who stand against the turret while it's on
|
||||
self.lifter show();
|
||||
self.lifter LinkCollisionToTurret(self, true);
|
||||
self hide();
|
||||
|
||||
alias_array = [];
|
||||
|
||||
alias_array[ "ref_turret_raise_doors_start" ] = "ref_turret_raise_doors_start";
|
||||
alias_array[ "ref_turret_raise_doors_end" ] = "ref_turret_raise_doors_end";
|
||||
alias_array[ "ref_turret_down_start" ] = "ref_turret_down_start";
|
||||
alias_array[ "ref_turret_down_end" ] = "ref_turret_down_end";
|
||||
alias_array[ "ref_turret_barrell_close_start" ] = "ref_turret_barrell_close_start";
|
||||
alias_array[ "ref_turret_barrell_close_end" ] = "ref_turret_barrell_close_end";
|
||||
alias_array[ "ref_turret_doors_close_start" ] = "ref_turret_doors_close_start";
|
||||
alias_array[ "ref_turret_doors_close_end" ] = "ref_turret_doors_close_end";
|
||||
alias_array[ "ref_turret_doors_lock_start" ] = "ref_turret_doors_lock_start";
|
||||
alias_array[ "ref_turret_doors_lock_end" ] = "ref_turret_doors_lock_end";
|
||||
|
||||
//steam vfx when turret moves down
|
||||
fx_angles = self.hatch.angles + ( -90, 0, 0 );
|
||||
noself_delayCall ( 4.1, ::PlayFx, getfx( "mp_ref_turret_steam_off" ), self.hatch.origin, AnglesToForward (fx_angles), AnglesToUp (fx_angles) ); //steam burst vfx on shut off
|
||||
self.hatch ScriptModelPlayAnimDeltaMotion( self.hatch.animDown );
|
||||
self.lifter ScriptModelPlayAnimDeltaMotion( self.lifter.animDown, "ref_turret_down_end" );
|
||||
|
||||
self.lifter thread snd_play_on_notetrack( alias_array, "ref_turret_down_end");
|
||||
|
||||
wait( 4.64 ); //HACK FOR ANIM LENGTH
|
||||
waittillframeend; //just to make sure the anim is done
|
||||
self.lifter LinkCollisionToTurret(self, false);
|
||||
level.refraction_turrets_moved_down++;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
//STUFF TAKEN FROM _autosentry.gsc
|
||||
////////////////////////////////////
|
||||
|
||||
sentry_setPlaced()
|
||||
{
|
||||
self setSentryCarrier( undefined );
|
||||
|
||||
self.carriedBy forceUseHintOff();
|
||||
self.carriedBy = undefined;
|
||||
|
||||
if( IsDefined( self.owner ) )
|
||||
self.owner.isCarrying = false;
|
||||
|
||||
self thread sentry_setActive();
|
||||
|
||||
self playSound( "sentry_gun_plant" );
|
||||
|
||||
self notify ( "placed" );
|
||||
}
|
||||
|
||||
sentry_setActive()
|
||||
{
|
||||
self turret_moveUP();
|
||||
|
||||
self setCanDamage( true ); //Make the turrets damageable AFTER they have finished moving up.
|
||||
self setCanRadiusDamage( true );
|
||||
|
||||
self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOn );
|
||||
|
||||
if( level.sentrySettings[ self.sentryType ].headIcon )
|
||||
{
|
||||
if ( level.teamBased )
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( self.team, (0,0,95) );
|
||||
else
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, (0,0,95) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//HACK: I removed all references to the owner argument. This is because the sentry doesn't have an owner when it is first created.
|
||||
sentry_initSentry( sentryType ) // self == sentry, turret, sam
|
||||
{
|
||||
self.sentryType = sentryType;
|
||||
self.canBePlaced = true;
|
||||
|
||||
self setModel( level.sentrySettings[ self.sentryType ].modelBase );
|
||||
self makeTurretInoperable();
|
||||
self SetDefaultDropPitch( 0.0 ); // setting this mainly prevents Turret_RestoreDefaultDropPitch() from running
|
||||
|
||||
self setTurretModeChangeWait( true );
|
||||
|
||||
self maps\mp\killstreaks\_autosentry::sentry_setInactive();
|
||||
|
||||
self thread maps\mp\killstreaks\_autosentry::sentry_handleUse();
|
||||
self thread maps\mp\killstreaks\_autosentry::sentry_attackTargets();
|
||||
}
|
||||
|
||||
|
||||
sentry_handleDeath()
|
||||
{
|
||||
self waittill ( "fake_refraction_death" );
|
||||
|
||||
// this handles cases of deletion
|
||||
if ( !IsDefined( self ) )
|
||||
return;
|
||||
|
||||
self maps\mp\killstreaks\_autosentry::sentry_setInactive();
|
||||
self SetSentryOwner( undefined );
|
||||
self SetTurretMinimapVisible( false );
|
||||
|
||||
// //Turning off the turrets' head icons. This is added functionality for the mp_refraction turrets.
|
||||
// if( level.sentrySettings[ self.sentryType ].headIcon )
|
||||
// {
|
||||
// if ( level.teamBased )
|
||||
// self maps\mp\_entityheadicons::setTeamHeadIcon( "none", (0,0,0) );
|
||||
// else
|
||||
// self maps\mp\_entityheadicons::setPlayerHeadIcon( "none", (0,0,0) );
|
||||
// }
|
||||
|
||||
self setCanDamage( false ); //Make the turrets non-damageable when they fake die.
|
||||
self setCanRadiusDamage( false );
|
||||
|
||||
self.laser_tag LaserOff( );
|
||||
|
||||
self turret_moveDOWN();
|
||||
|
||||
level.refraction_turrets_alive--;
|
||||
|
||||
if ( IsDefined( self.killCamEnt ) )
|
||||
self.killCamEnt Delete();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: sentry_setOwner( <owner> )"
|
||||
"Summary: sets the owner of the spawned turret."
|
||||
"Module: Entity"
|
||||
"CallOn: a turret spawned by SpawnTurret()."
|
||||
"MandatoryArg: <owner>: the player that used the refraction killstreak"
|
||||
"Example: level.refraction_turrets[i]["spawned_turret"] sentry_setOwner( player );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
sentry_setOwner( owner )
|
||||
{
|
||||
self.owner = owner;
|
||||
|
||||
self SetSentryOwner( self.owner );
|
||||
self SetTurretMinimapVisible( true, self.sentryType );
|
||||
|
||||
if ( level.teamBased && IsDefined( owner ) )
|
||||
{
|
||||
self.team = self.owner.team;
|
||||
self setTurretTeam( self.team );
|
||||
}
|
||||
|
||||
self thread sentry_handleOwnerDisconnect();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: sentry_handleOwnerDisconnect()"
|
||||
"Summary: notifies death for this turret (self) if its owner disconnects."
|
||||
"Module: Entity"
|
||||
"CallOn: a turret spawned by SpawnTurret()."
|
||||
"Example: self thread sentry_handleOwnerDisconnect();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
sentry_handleOwnerDisconnect()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon( "fake_refraction_death" ); //Ending this thread if the turret (self) dies by other means (damage, time).
|
||||
|
||||
self.owner waittill_any( "disconnect", "joined_team", "joined_spectators" );
|
||||
|
||||
self notify( "fake_refraction_death" );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: sentry_handleDamage()"
|
||||
"Summary: handles the death of a turret spawned by SpawnTurret()"
|
||||
"Module: Entity"
|
||||
"CallOn: a turret spawned by SpawnTurret()."
|
||||
"Example: self thread sentry_handleDamage();"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
sentry_handleDamage()
|
||||
{
|
||||
self endon( "fake_refraction_death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self.health = level.sentrySettings[ self.sentryType ].health;
|
||||
self.maxHealth = level.sentrySettings[ self.sentryType ].maxHealth;
|
||||
self.damageTaken = 0; // how much damage has it taken
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
||||
|
||||
// don't allow people to destroy equipment on their team if FF is off
|
||||
if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
|
||||
self.wasDamagedFromBulletPenetration = true;
|
||||
|
||||
// up the damage for airstrikes, stealth bombs, and bomb sites //Don't need this from original in _autosentry.gsc.
|
||||
// switch( weapon )
|
||||
// {
|
||||
// case "artillery_mp":
|
||||
// case "stealth_bomb_mp":
|
||||
// damage *= 4;
|
||||
// break;
|
||||
// case "bomb_site_mp":
|
||||
// damage = self.maxHealth;
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// if ( meansOfDeath == "MOD_MELEE" )
|
||||
// self.damageTaken += self.maxHealth;
|
||||
//
|
||||
// modifiedDamage = damage;
|
||||
// if ( isPlayer( attacker ) )
|
||||
// {
|
||||
// attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "sentry" );
|
||||
//
|
||||
// if ( attacker _hasPerk( "specialty_armorpiercing" ) )
|
||||
// {
|
||||
// modifiedDamage = damage * level.armorPiercingMod;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // in case we are shooting from a remote position, like being in the osprey gunner shooting this
|
||||
// if( IsDefined( attacker.owner ) && IsPlayer( attacker.owner ) )
|
||||
// {
|
||||
// attacker.owner maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "sentry" );
|
||||
// }
|
||||
|
||||
modifiedDamage = 0;
|
||||
|
||||
if( IsDefined( weapon ) )
|
||||
{
|
||||
shortWeapon = maps\mp\_utility::strip_suffix( weapon, "_lefthand" );
|
||||
|
||||
switch( shortWeapon )
|
||||
{
|
||||
// case "ac130_105mm_mp":
|
||||
// case "ac130_40mm_mp":
|
||||
// case "stinger_mp":
|
||||
// case "remotemissile_projectile_mp":
|
||||
// self.largeProjectileDamage = true;
|
||||
// modifiedDamage = self.maxHealth + 1;
|
||||
// break;
|
||||
//
|
||||
// case "artillery_mp":
|
||||
// case "stealth_bomb_mp":
|
||||
// self.largeProjectileDamage = false;
|
||||
// modifiedDamage += ( damage * 4 );
|
||||
// break;
|
||||
//
|
||||
// case "bomb_site_mp":
|
||||
case "emp_grenade_mp":
|
||||
case "emp_grenade_var_mp":
|
||||
self.largeProjectileDamage = false;
|
||||
modifiedDamage = self.maxHealth + 1;
|
||||
if ( isPlayer( attacker ) )
|
||||
{
|
||||
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "sentry" );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
modifiedDamage = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
|
||||
}
|
||||
|
||||
self.damageTaken += modifiedDamage;
|
||||
|
||||
if ( self.damageTaken >= self.maxHealth )
|
||||
{
|
||||
thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath, weapon );
|
||||
|
||||
if ( isPlayer( attacker ) && (!IsDefined(self.owner) || attacker != self.owner) )
|
||||
{
|
||||
level thread maps\mp\gametypes\_rank::awardGameEvent( "kill", attacker, weapon, undefined, meansOfDeath );
|
||||
}
|
||||
|
||||
if ( IsDefined( self.owner ) )
|
||||
self.owner thread leaderDialogOnPlayer( level.sentrySettings[ self.sentryType ].voDestroyed, undefined, undefined, self.origin );
|
||||
|
||||
self notify( "fake_refraction_death" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
629
raw/maps/mp/killstreaks/streak_mp_solar.gsc
Normal file
629
raw/maps/mp/killstreaks/streak_mp_solar.gsc
Normal file
@ -0,0 +1,629 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// Killstreak Data
|
||||
CONST_DAMAGE_RADIUS = 128;
|
||||
CONST_DAMAGE_MIN = 2;
|
||||
CONST_DAMAGE_MAX = 8;
|
||||
CONST_FIRE_TIME = 5;
|
||||
CONST_FIRE_RADIUS_SQ = 70 * 70;
|
||||
CONST_FIRE_DIST_RADIUS_SQ = 50 * 50;
|
||||
CONST_FIRE_HEIGHT = 80;
|
||||
|
||||
init()
|
||||
{
|
||||
level.solar_killstreak_duration = 30;
|
||||
|
||||
//FX
|
||||
level.solar_fire_fx = LoadFX( "vfx/fire/fire_xsglow_runner_5s" );
|
||||
|
||||
//Sounds
|
||||
level.solar_reflector_sfx = "mp_solar_array_player";
|
||||
level.solar_reflector_target_sfx = "mp_solar_array_target";
|
||||
|
||||
|
||||
level.killstreakFuncs["mp_solar"] = ::tryUseSolarReflector;
|
||||
level.mapKillStreak = "mp_solar";
|
||||
level.mapKillstreakPickupString = &"MP_SOLAR_MAP_KILLSTREAK_PICKUP";
|
||||
level.mapKillStreakDamageFeedbackSound = ::handleDamageFeedbackSound;
|
||||
|
||||
level.mapCustomBotKillstreakFunc = ::setupBotsForMapKillstreak;
|
||||
|
||||
level.killstreakWieldWeapons["killstreak_solar_mp"] = "mp_solar";
|
||||
}
|
||||
|
||||
setupBotsForMapKillstreak()
|
||||
{
|
||||
level thread maps\mp\bots\_bots_ks::bot_register_killstreak_func( "mp_solar", maps\mp\bots\_bots_ks::bot_killstreak_never_use, maps\mp\bots\_bots_ks::bot_killstreak_do_not_use );
|
||||
}
|
||||
|
||||
tryUseSolarReflector( lifeId, modules )
|
||||
{
|
||||
if ( isDefined( level.solar_reflector_player ) )
|
||||
{
|
||||
self iPrintLnBold( &"MP_SOLAR_REFLECTOR_IN_USE" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isUsingRemote() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isAirDenied() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isEMPed() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak();
|
||||
if ( result != "success" )
|
||||
return false;
|
||||
self setUsingRemote( "mp_solar" );
|
||||
|
||||
result = setSolarReflectorPlayer( self );
|
||||
|
||||
// this needs to get set after we say the player is using it because this could get set to true and then they leave the game
|
||||
// this fixes a bug where a player calls it, leaves before getting fully in it and then no one else can call it because it thinks it's being used
|
||||
if( IsDefined( result ) && result )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "mp_solar", self.origin ); //TODO: set this up for the Solar Reflector.
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( self isUsingRemote() )
|
||||
self clearUsingRemote();
|
||||
}
|
||||
|
||||
return ( IsDefined( result ) && result );
|
||||
}
|
||||
|
||||
|
||||
setSolarReflectorPlayer( player )
|
||||
{
|
||||
self endon ( "solar_reflector_player_removed" );
|
||||
|
||||
level.solar_reflector_player = player;
|
||||
|
||||
thread teamPlayerCardSplash( "used_mp_solar", player );
|
||||
|
||||
thread onPlayerConnect();
|
||||
thread setupPlayerDeath();
|
||||
|
||||
player thread overlay();
|
||||
|
||||
player thread runBeam();
|
||||
|
||||
player thread removeSolarReflectorPlayerAfterTime( level.solar_killstreak_duration );
|
||||
player thread removeSolarReflectorPlayerWatch();
|
||||
player thread removeSolarReflectorLevelWatch();
|
||||
player thread removeSolarReflectorPlayerOnCommand();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
beamMinimap(ground_ent)
|
||||
{
|
||||
solar_reflector_friendShader = "compassping_orbitallaser_friendly";
|
||||
solar_reflector_foeShader = "compassping_orbitallaser_hostile";
|
||||
|
||||
currentObj = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
objective_add( currentObj, "invisible", (0,0,0) );
|
||||
objective_OnEntity( currentObj, ground_ent );
|
||||
objective_state( currentObj, "active" );
|
||||
if(level.teambased)
|
||||
objective_team( currentObj, self.team );
|
||||
else
|
||||
objective_player( currentObj, self GetEntityNumber() );
|
||||
objective_icon( currentObj, solar_reflector_friendShader );
|
||||
friendlyObjID = currentObj;
|
||||
|
||||
currentObj = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
objective_add( currentObj, "invisible", (0,0,0) );
|
||||
objective_OnEntity( currentObj, ground_ent );
|
||||
objective_state( currentObj, "active" );
|
||||
if(level.teamBased)
|
||||
objective_team( currentObj, level.otherTeam[ self.team ] );
|
||||
else
|
||||
objective_playerenemyteam( currentObj, self GetEntityNumber() );
|
||||
objective_icon( currentObj, solar_reflector_foeShader );
|
||||
enemyObjID = currentObj;
|
||||
|
||||
level waittill( "solar_reflector_player_removed" );
|
||||
|
||||
_objective_delete( friendlyObjID );
|
||||
_objective_delete( enemyObjID );
|
||||
}
|
||||
|
||||
beamSounds(cam_ent, ground_ent)
|
||||
{
|
||||
waitframe();
|
||||
ground_ent PlayLoopSound( level.solar_reflector_target_sfx );
|
||||
cam_ent PlayLoopSound( level.solar_reflector_sfx );
|
||||
|
||||
PlaySoundAtPos( cam_ent.origin, "array_beam_start");
|
||||
|
||||
level waittill( "solar_reflector_player_removed" );
|
||||
|
||||
PlaySoundAtPos( cam_ent.origin, "array_beam_stop");
|
||||
ground_ent StopLoopSound();
|
||||
cam_ent StopLoopSound();
|
||||
}
|
||||
|
||||
runBeam()
|
||||
{
|
||||
cam_pos = GetStruct("solar_cam_pos", "targetname");
|
||||
beam_pos = GetStruct("solar_beam_pos", "targetname");
|
||||
ground_pos = GetStruct("solar_ground_pos", "targetname");
|
||||
|
||||
ground_ent = getGroundEnt(ground_pos);
|
||||
|
||||
cam_ent = getCameraEnt(cam_pos, ground_pos);
|
||||
self thread playerSetCamera(cam_ent);
|
||||
|
||||
beam_ent = getBeamEnt(beam_pos, ground_pos);
|
||||
|
||||
self thread beamMinimap(ground_ent);
|
||||
self thread beamSounds(cam_ent, ground_ent);
|
||||
//self thread beamStartFires(ground_ent);
|
||||
//self thread beamGroundFx(ground_ent); //Moved to laser gdt
|
||||
runBeamUpdate(beam_ent, cam_ent, ground_ent);
|
||||
|
||||
beam_ent.killCamEnt Delete();
|
||||
beam_ent Delete();
|
||||
cam_ent Delete();
|
||||
ground_ent Delete();
|
||||
}
|
||||
|
||||
getCameraEnt(cam_pos, ground_pos)
|
||||
{
|
||||
cam_ent = Spawn("script_model", cam_pos.origin );
|
||||
cam_ent.angles = VectorToAngles(ground_pos.origin - cam_pos.origin);
|
||||
cam_ent SetModel("tag_player");
|
||||
|
||||
return cam_ent;
|
||||
}
|
||||
|
||||
getGroundEnt(ground_pos)
|
||||
{
|
||||
ground_ent = Spawn("script_model", ground_pos.origin);
|
||||
ground_ent.angles = (0,0,0);
|
||||
ground_ent SetModel("tag_origin");
|
||||
return ground_ent;
|
||||
}
|
||||
|
||||
playerSetCamera(cam_ent)
|
||||
{
|
||||
cam_ent endon("death");
|
||||
/#
|
||||
if ( GetDvarInt( "test_mp_solar_killstreak_death", 0 ) != 0 )
|
||||
return;
|
||||
#/
|
||||
|
||||
self PlayerLinkWeaponViewToDelta( cam_ent, "tag_player", 1.0, 40, 40, 12, 10 );
|
||||
self SetPlayerAngles( cam_ent GetTagAngles( "tag_player" ) );
|
||||
|
||||
self SetClientOmnvar( "fov_scale", 0.2 );
|
||||
self ThermalVisionFOFOverlayOn();
|
||||
|
||||
while(1)
|
||||
{
|
||||
self EnableSlowAim( 0.05, 0.05 );
|
||||
|
||||
level waittill( "host_migration_begin" );
|
||||
waitframe();
|
||||
self SetClientOmnvar( "fov_scale", 0.2 );
|
||||
self ThermalVisionFOFOverlayOn();
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
}
|
||||
}
|
||||
|
||||
getBeamEnt(beam_pos, ground_pos)
|
||||
{
|
||||
beam_ent = Spawn("script_model", beam_pos.origin );
|
||||
beam_ent.angles = VectorToAngles(ground_pos.origin - beam_pos.origin);
|
||||
beam_ent SetModel("tag_laser");
|
||||
beam_ent LaserOn( "solar_laser" );
|
||||
|
||||
offset = (AnglesToForward(beam_ent.angles) * 5000) - (0,0,100);
|
||||
killCamEnt = Spawn("script_model", beam_pos.origin + offset );
|
||||
killCamEnt.angles = beam_ent.angles;
|
||||
killCamEnt LinkTo(beam_ent);
|
||||
beam_ent.killCamEnt = killCamEnt;
|
||||
|
||||
return beam_ent;
|
||||
}
|
||||
|
||||
beamGroundFx(ground_ent)
|
||||
{
|
||||
ground_ent endon ( "death" );
|
||||
|
||||
lastFX = undefined;
|
||||
while(1)
|
||||
{
|
||||
waitframe();
|
||||
|
||||
if ( !isDefined(ground_ent.surfacetype) )
|
||||
continue;
|
||||
|
||||
nextFX = beamGetGroundFX(ground_ent.surfacetype);
|
||||
if(!IsDefined(lastFX) || lastFX != nextFX)
|
||||
{
|
||||
|
||||
if(IsDefined(lastFX))
|
||||
StopFXOnTag(lastFX, ground_ent, "tag_origin");
|
||||
|
||||
PlayFXOnTag(nextFX, ground_ent, "tag_origin");
|
||||
|
||||
lastFX = nextFX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beamGetGroundFX(surfacetype)
|
||||
{
|
||||
switch(surfacetype)
|
||||
{
|
||||
case "water":
|
||||
case "water_waist":
|
||||
return getfx("steam_column_rising");
|
||||
default:
|
||||
return getfx("fx_flare_solar");
|
||||
}
|
||||
}
|
||||
|
||||
runBeamUpdate(beam_ent, cam_ent, ground_ent)
|
||||
{
|
||||
self endon ( "solar_reflector_player_removed" );
|
||||
|
||||
speed = 300;
|
||||
trace_dist = 20000;
|
||||
|
||||
beam_ent_pos = SpawnStruct();
|
||||
beam_ent_pos.origin = ground_ent.origin;
|
||||
|
||||
test_dvar = 0;
|
||||
/#
|
||||
test_dvar = GetDvarInt( "test_mp_solar_killstreak_death", 0 );
|
||||
if ( test_dvar )
|
||||
level.test_start_angles = cam_ent.angles;
|
||||
#/
|
||||
|
||||
while(1)
|
||||
{
|
||||
goal_angles = self GetPlayerAngles();
|
||||
|
||||
/#
|
||||
if( test_dvar == 2 )
|
||||
{
|
||||
offset = Sin((GetTime()/1000) * 3.14 * 10) * 10;
|
||||
goal_angles = level.test_start_angles + (0,offset,0);
|
||||
}
|
||||
else if ( test_dvar != 0 )
|
||||
{
|
||||
goal_angles = level.test_start_angles;
|
||||
}
|
||||
#/
|
||||
|
||||
|
||||
goal_angles = (goal_angles[0], goal_angles[1], 0); //GetPlayerAngles is is returning with a very small roll
|
||||
goal_dir = AnglesToForward(goal_angles);
|
||||
|
||||
goal_dist = abs((cam_ent.origin[2] - beam_ent_pos.origin[2])/goal_dir[2]);
|
||||
goal_point = cam_ent.origin + goal_dir*goal_dist; //Push the point down the the same plane as the ground;
|
||||
|
||||
move_dist = Distance2D(goal_point, beam_ent_pos.origin);
|
||||
|
||||
if( move_dist <= speed*0.05 )
|
||||
{
|
||||
beam_ent_pos.origin = goal_point;
|
||||
}
|
||||
else
|
||||
{
|
||||
move_dir = goal_point - beam_ent_pos.origin;
|
||||
move_dir = VectorNormalize(move_dir);
|
||||
|
||||
beam_ent_pos.origin += move_dir*speed*0.05;
|
||||
}
|
||||
|
||||
beam_dir = VectorNormalize(beam_ent_pos.origin-beam_ent.origin);
|
||||
|
||||
beam_ent RotateTo(VectorToAngles(beam_dir), 0.1);
|
||||
//beam_ent.angles = anglessnaptocompressedangles( beam_ent.angles );
|
||||
|
||||
start = beam_ent.origin;
|
||||
end = start + beam_dir*trace_dist;
|
||||
results = BulletTrace(start, end, false);
|
||||
|
||||
Assert(results["fraction"]< 1.0);
|
||||
ground_ent MoveTo(results["position"], .1);
|
||||
ground_ent.surfacetype = results["surfacetype"];
|
||||
ground_ent.killCamEnt = beam_ent.killCamEnt;
|
||||
ground_ent RadiusDamage( ground_ent.origin, CONST_DAMAGE_RADIUS, CONST_DAMAGE_MAX, CONST_DAMAGE_MIN, self, "MOD_EXPLOSIVE", "killstreak_solar_mp" );
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
handleDamageFeedbackSound()
|
||||
{
|
||||
self.shouldloopdamagefeedback = true;
|
||||
self.damagefeedbacktimer = 10;
|
||||
|
||||
self PlayLocalSound( "MP_solar_hit_alert" );
|
||||
|
||||
self PlayRumbleLoopOnEntity("damage_light");
|
||||
|
||||
while ( self.damagefeedbacktimer > 0 )
|
||||
{
|
||||
self.damagefeedbacktimer--;
|
||||
wait( 0.05 );
|
||||
}
|
||||
|
||||
self StopRumble("damage_light");
|
||||
self StopLocalSound( "MP_solar_hit_alert" );
|
||||
|
||||
self.shouldloopdamagefeedback = undefined;
|
||||
}
|
||||
|
||||
|
||||
removeSolarReflectorPlayerOnCommand()
|
||||
{
|
||||
self endon ( "solar_reflector_player_removed" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
button_hold_time = 0;
|
||||
while ( self UseButtonPressed() )
|
||||
{
|
||||
button_hold_time += 0.05;
|
||||
if ( button_hold_time > 0.75 )
|
||||
{
|
||||
level thread removeSolarReflectorPlayer( self );
|
||||
return;
|
||||
}
|
||||
waitframe();
|
||||
}
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
removeSolarReflectorPlayerWatch()
|
||||
{
|
||||
self endon ( "solar_reflector_player_removed" );
|
||||
|
||||
self waittill_any("disconnect", "joined_team", "joined_spectators", "spawned", "killstreak_exit");
|
||||
|
||||
level thread removeSolarReflectorPlayer( self );
|
||||
}
|
||||
|
||||
|
||||
removeSolarReflectorLevelWatch()
|
||||
{
|
||||
self endon ( "solar_reflector_player_removed" );
|
||||
|
||||
level waittill ( "game_cleanup" );
|
||||
|
||||
level thread removeSolarReflectorPlayer( self );
|
||||
}
|
||||
|
||||
|
||||
removeSolarReflectorPlayerAfterTime( removeDelay )
|
||||
{
|
||||
self endon ( "solar_reflector_player_removed" );
|
||||
|
||||
wait 1; //Don't start timer till we fade up from black
|
||||
|
||||
if ( self _hasPerk( "specialty_blackbox" ) && IsDefined( self.specialty_blackbox_bonus ) )
|
||||
{
|
||||
removeDelay *= self.specialty_blackbox_bonus;
|
||||
}
|
||||
|
||||
self thread solarRelectorTimer(removeDelay);
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( removeDelay );
|
||||
|
||||
/#
|
||||
while ( GetDvarInt( "test_mp_solar_killstreak_death", 0 ) != 0 )
|
||||
waitframe();
|
||||
#/
|
||||
|
||||
|
||||
level thread removeSolarReflectorPlayer( self );
|
||||
}
|
||||
|
||||
|
||||
solarRelectorTimer( removeDelay )
|
||||
{
|
||||
self endon ( "solar_reflector_player_removed" );
|
||||
|
||||
endTime = GetTime() + removeDelay*1000;
|
||||
|
||||
while(1)
|
||||
{
|
||||
self SetClientOmnvar("ui_solar_beam_timer", endTime);
|
||||
|
||||
level waittill( "host_migration_begin" );
|
||||
|
||||
timePassed = maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
endTime += timePassed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
removeSolarReflectorPlayer( player )
|
||||
{
|
||||
player notify ( "solar_reflector_player_removed" );
|
||||
level notify ( "solar_reflector_player_removed" );
|
||||
|
||||
waittillframeend;
|
||||
|
||||
if ( IsDefined(player) )
|
||||
{
|
||||
player clearUsingRemote();
|
||||
|
||||
player show();
|
||||
player unlink();
|
||||
|
||||
player ThermalVisionFOFOverlayOff();
|
||||
player setBlurForPlayer( 0, 0 );
|
||||
player SetClientOmnvar("ui_solar_beam", 0);
|
||||
player DisableSlowAim();
|
||||
player SetClientOmnvar( "fov_scale", 1 );
|
||||
}
|
||||
|
||||
level.solar_reflector_player = undefined;
|
||||
}
|
||||
|
||||
|
||||
overlay()
|
||||
{
|
||||
self endon("disconnect");
|
||||
level endon( "solar_reflector_player_removed" );
|
||||
|
||||
wait 1; // Don't show hud till we fade up from black
|
||||
self setBlurForPlayer( 1.2, 0 );
|
||||
|
||||
self SetClientOmnvar("ui_solar_beam", 1);
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
level notify( "solarOnPlayerConnect" );
|
||||
level endon( "solarOnPlayerConnect" );
|
||||
|
||||
level endon( "solar_reflector_player_removed" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
player.preKilledFunc = ::playerPreKilled;
|
||||
player thread onPlayerSpawned();
|
||||
player thread playerImmuneToFire();
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
level notify( "solarOnPlayerSpawned" );
|
||||
level endon( "solarOnPlayerSpawned" );
|
||||
|
||||
level endon( "solar_reflector_player_removed" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "player_spawned" );
|
||||
|
||||
self.hideOnDeath = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
setupPlayerDeath()
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( !IsDefined( player ) || player == level.solar_reflector_player )
|
||||
continue;
|
||||
|
||||
player.preKilledFunc = ::playerPreKilled;
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
playerPlayVaporizeFX()
|
||||
{
|
||||
self.hideOnDeath = true;
|
||||
offset = ( 0, 0, 30 ); // stand;
|
||||
|
||||
stance = self GetStance();
|
||||
if ( stance == "crouch" )
|
||||
offset = ( 0, 0, 20 );
|
||||
else if ( stance == "prone" )
|
||||
offset = ( 0, 0, 10 );
|
||||
|
||||
PlayFX( getfx( "solar_killstreak_death" ), self.origin + offset );
|
||||
}
|
||||
|
||||
playerPreKilled( eInflictor, attacker, victim, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, isFauxDeath )
|
||||
{
|
||||
if ( sWeapon == "killstreak_solar_mp" )
|
||||
self playerPlayVaporizeFX();
|
||||
}
|
||||
|
||||
playerImmuneToFire()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
self.solarImmuneFire = true;
|
||||
|
||||
wait CONST_FIRE_TIME;
|
||||
|
||||
self.solarImmuneFire = undefined;
|
||||
}
|
||||
|
||||
|
||||
beamStartFires(ground_ent)
|
||||
{
|
||||
level endon( "solar_reflector_player_removed" );
|
||||
|
||||
fireFxEnt = SpawnFx( level.solar_fire_fx, ( 0, 0, 0 ) );
|
||||
|
||||
lastFirePos = ( 0, 0, 0 );
|
||||
lastFireTime = GetTime();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
waitframe();
|
||||
|
||||
distSq = Distance2DSquared( ground_ent.origin, lastFirePos );
|
||||
duration = ( lastFireTime - GetTime() ) / 1000;
|
||||
if ( distSq > CONST_FIRE_DIST_RADIUS_SQ || duration > CONST_FIRE_TIME )
|
||||
{
|
||||
lastFirePos = ground_ent.origin;
|
||||
if ( !IsDefined(ground_ent.surfacetype) || !isStrStart( ground_ent.surfacetype, "water_") )
|
||||
{
|
||||
level thread fireAtPosition( lastFirePos, self );
|
||||
}
|
||||
|
||||
lastFireTime = GetTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fireAtPosition( fireOrigin, killstreakPlayer )
|
||||
{
|
||||
PlayFX( level.solar_fire_fx, fireOrigin );
|
||||
|
||||
endTime = GetTime() + ( CONST_FIRE_TIME * 1000 );
|
||||
|
||||
while ( GetTime() < endTime )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
// just connected
|
||||
if ( IsDefined( player.solarImmuneFire ) )
|
||||
continue;
|
||||
|
||||
// below the fire
|
||||
if ( player.origin[2] < ( fireOrigin[2] - 5 ) )
|
||||
continue;
|
||||
|
||||
// above the fire
|
||||
if ( player.origin[2] > ( fireOrigin[2] + CONST_FIRE_HEIGHT ) )
|
||||
continue;
|
||||
|
||||
distSq = Distance2DSquared( player.origin, fireOrigin );
|
||||
|
||||
if ( distSq < CONST_FIRE_RADIUS_SQ )
|
||||
player DoDamage( 4, fireOrigin, killstreakPlayer, killstreakPlayer, "MOD_EXPLOSIVE", "killstreak_solar_mp" );
|
||||
}
|
||||
|
||||
wait 0.1;
|
||||
}
|
||||
}
|
19
raw/maps/mp/killstreaks/streak_mp_terrace.gsc
Normal file
19
raw/maps/mp/killstreaks/streak_mp_terrace.gsc
Normal file
@ -0,0 +1,19 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
level.killstreakWieldWeapons["killstreak_terrace_mp"] = "mp_terrace";
|
||||
|
||||
level.killstreakFuncs["mp_terrace"] = ::tryUseKillstreak;
|
||||
|
||||
level.mapKillStreak = "mp_terrace";
|
||||
level.mapKillstreakPickupString = &"MP_TERRACE_MAP_KILLSTREAK_PICKUP";
|
||||
}
|
||||
|
||||
tryUseKillstreak(lifeId,modules)
|
||||
{
|
||||
modules = ["mp_terrace"];
|
||||
return maps\mp\killstreaks\_drone_assault::tryUseAssaultDrone( lifeId, modules );
|
||||
}
|
Reference in New Issue
Block a user